Skip to content

Test protobuf packag two#67

Closed
MonaaEid wants to merge 30 commits intomainfrom
test-protobuf-packagTWO
Closed

Test protobuf packag two#67
MonaaEid wants to merge 30 commits intomainfrom
test-protobuf-packagTWO

Conversation

@MonaaEid
Copy link
Owner

No description provided.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
This script posts a review comment on release PRs for CodeRabbit, ensuring it only runs for maintainer-authored PRs and avoids duplicate comments.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Refactor error handling and improve prompt loading in release PR script.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Remove message_data parameter from the constructor.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Removed tools configuration and added knowledge_base and web_search options.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Removed tools section and added web_search option under knowledge_base.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
@github-actions
Copy link

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

Please resolve these conflicts locally and push the changes.

Quick Fix for CHANGELOG.md Conflicts

If your conflict is only in CHANGELOG.md, you can resolve it easily using the GitHub web editor:

  1. Click on the "Resolve conflicts" button in the PR
  2. Accept both changes (keep both changelog entries)
  3. Click "Mark as resolved"
  4. Commit the merge

For all other merge conflicts, please read:

Thank you for contributing!

@github-actions
Copy link

Hi @MonaaEid, this is **LinkBot** 👋

Linking pull requests to issues helps us significantly with reviewing pull requests and keeping the repository healthy.

🚨 This pull request does not have an issue linked.

Please link an issue using the following format:

📖 Guide:
docs/sdk_developers/how_to_link_issues.md

If no issue exists yet, please create one:
docs/sdk_developers/creating_issues.md

Thanks!

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

Warning

Ignoring CodeRabbit configuration file changes. For security, only the configuration from the base branch is applied for open source repositories.

Walkthrough

Removed chunking and multi-step flows from TopicMessageSubmitTransaction and simplified its topic ID typing; narrowed TopicInfo fields and proto parsing; changed TopicMessage.transaction_id to a formatted string; refactored TopicCreateTransaction to direct proto construction and synchronous execution; added TokenAssociation and expanded TransactionRecord to richer fields; updated review rules in .coderabbit.yaml.

Changes

Cohort / File(s) Summary
TopicMessageSubmitTransaction
src/hiero_sdk_python/consensus/topic_message_submit_transaction.py
Eliminated chunking internals, validation, and multi-chunk lifecycle methods (freeze/execute/sign). Topic ID type changed to basic_types_pb2.TopicID. _build_proto_body now builds a single-message ConsensusSubmitMessageTransactionBody; imports and docstrings updated.
TopicCreateTransaction
src/hiero_sdk_python/consensus/topic_create_transaction.py
Constructor simplified (fewer params), removed custom-fee helpers, consolidated proto construction, added _execute_transaction(self, client, transaction_proto) that calls client.topic_stub.createTopic and handles precheck/receipt logic; removed _get_method and legacy helpers.
TopicInfo
src/hiero_sdk_python/consensus/topic_info.py
Constructor and public attributes reduced to core fields; _from_proto renamed to from_proto; representation simplified to compact __repr__.
TopicMessage
src/hiero_sdk_python/consensus/topic_message.py
transaction_id type changed from TransactionId to str; proto-derived initialTransactionID is formatted to a string; removed TransactionId import and updated local types.
TransactionRecord
src/hiero_sdk_python/transaction/transaction_record.py
Data model expanded with scheduling, consensus timestamps, assessed custom fees, automatic token associations, alias/ethereum/evm fields, paid staking rewards, contract_create_result; _from_proto/_to_proto updated to parse/emit new fields; __repr__ reworked.
TokenAssociation (new)
src/hiero_sdk_python/tokens/token_association.py
Added frozen dataclass TokenAssociation with _from_proto and _to_proto helpers and string representations for read-only automatic-association data.
Review Configuration
.coderabbit.yaml
Added knowledge_base.web_search.enabled: true, large schedule_review_instructions block, and wired schedule/consensus review guidance into path-specific and global review rules.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant TxBuilder as TopicCreateTransaction
    participant Node as TopicService (gRPC)
    participant Ledger as Receipt/Consensus

    Client->>TxBuilder: build transaction proto
    TxBuilder->>Node: submit via client.topic_stub.createTopic
    Node-->>TxBuilder: nodeTransactionPrecheckCode
    alt precheck OK
        TxBuilder->>Node: request/get receipt
        Node-->>Ledger: produce receipt
        Ledger-->>TxBuilder: return receipt
        TxBuilder-->>Client: return receipt/result
    else precheck FAIL
        Node-->>TxBuilder: return error code
        TxBuilder-->>Client: raise exception with code/name
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Test protobuf packag two' does not clearly describe the actual changes in this PR, which involve significant updates to TransactionRecord, TokenAssociation, TopicMessage, and other consensus-related classes. Use a descriptive title that reflects the main changes, such as 'Refactor consensus classes and enhance TransactionRecord with new fields' or 'Update transaction record and token association models'.
Description check ⚠️ Warning No pull request description was provided by the author, making it impossible to understand the intent and scope of the changes. Add a detailed pull request description explaining the motivation, changes made, and impact of this refactoring across multiple consensus and transaction classes.
Docstring Coverage ⚠️ Warning Docstring coverage is 51.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test-protobuf-packagTWO

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: 6

Comment on lines 26 to 51
def __init__(
self,
topic_id: Optional[TopicId] = None,
topic_id: Optional[basic_types_pb2.TopicID] = None,
message: Optional[str] = None,
chunk_size: Optional[int] = None,
max_chunks: Optional[int] = None
) -> None:
"""
Initializes a new TopicMessageSubmitTransaction instance.
Args:
topic_id (Optional[TopicId]): The ID of the topic.
topic_id (Optional[TopicID]): The ID of the topic.
message (Optional[str]): The message to submit.
chunk_size (Optional[int]): The maximum chunk size in bytes, Default: 1024.
max_chunks (Optional[int]): The maximum number of chunks allowed, Default: 20.
"""
super().__init__()
self.topic_id: Optional[TopicId] = topic_id
self.topic_id: Optional[basic_types_pb2.TopicID] = topic_id
self.message: Optional[str] = message
self.chunk_size: int = chunk_size or 1024
self.max_chunks: int = max_chunks or 20

self._current_index = 0
self._total_chunks = self.get_required_chunks()
self._initial_transaction_id: Optional[TransactionId] = None
self._transaction_ids: List[TransactionId] = []
self._signing_keys: List["PrivateKey"] = []

def get_required_chunks(self) -> int:
"""
Returns the number of chunks required for the current message.

Returns:
int: Number of chunks required.
"""
if not self.message:
return 1

content = self.message.encode("utf-8")
return math.ceil(len(content) / self.chunk_size)

def set_topic_id(
self, topic_id: TopicId
self, topic_id: basic_types_pb2.TopicID
) -> "TopicMessageSubmitTransaction":
"""
Sets the topic ID for the message submission.

Args:
topic_id (TopicId): The ID of the topic to which the message is submitted.
topic_id (TopicID): The ID of the topic to which the message is submitted.

Returns:
TopicMessageSubmitTransaction: This transaction instance (for chaining).
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Docstrings should clarify the type is the protobuf basic_types_pb2.TopicID.

The docstrings at lines 34 and 48 reference TopicID without qualifying it. Since the SDK also has its own TopicId wrapper class, this is ambiguous and could mislead callers. Specify the fully-qualified protobuf type to match the type hints.

Comment on lines +120 to 123
return consensus_submit_message_pb2.ConsensusSubmitMessageTransactionBody(
topicID=self.topic_id._to_proto(),
message=chunk_content
message=bytes(self.message, "utf-8"),
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's locate and examine the file in question
echo "=== Locating the file ==="
find . -name "topic_message_submit_transaction.py" -type f

echo ""
echo "=== Checking file size ==="
wc -l ./src/hiero_sdk_python/consensus/topic_message_submit_transaction.py 2>/dev/null || echo "File not found with that exact path"

Repository: MonaaEid/hiero-sdk-python

Length of output: 321


🏁 Script executed:

#!/bin/bash
# Let's search for any TopicId-related files and the actual file
echo "=== Searching for TopicId class definitions ==="
rg -l "class TopicId" --type py

echo ""
echo "=== Searching for topic_message_submit_transaction file ==="
git ls-files | grep -i "topic_message"

Repository: MonaaEid/hiero-sdk-python

Length of output: 781


🏁 Script executed:

#!/bin/bash
# Read the actual file content around lines 120-123
if [ -f "src/hiero_sdk_python/consensus/topic_message_submit_transaction.py" ]; then
    echo "=== Content around lines 115-130 ==="
    sed -n '115,130p' "src/hiero_sdk_python/consensus/topic_message_submit_transaction.py" | cat -n
else
    echo "File not found at expected path. Searching for it..."
    find . -name "*topic_message*" -type f
fi

Repository: MonaaEid/hiero-sdk-python

Length of output: 906


🏁 Script executed:

#!/bin/bash
# Search for _to_proto method definitions
echo "=== Searching for _to_proto method definitions in TopicId ==="
rg -A 5 "class TopicId" --type py

echo ""
echo "=== Searching for _to_proto method in consensus modules ==="
rg -B 2 -A 5 "_to_proto" "src/hiero_sdk_python/consensus/" --type py | head -100

Repository: MonaaEid/hiero-sdk-python

Length of output: 10164


🏁 Script executed:

#!/bin/bash
echo "=== Full content of topic_message_submit_transaction.py ==="
head -50 "src/hiero_sdk_python/consensus/topic_message_submit_transaction.py" | cat -n

Repository: MonaaEid/hiero-sdk-python

Length of output: 2278


🏁 Script executed:

#!/bin/bash
echo "=== Checking TopicId class and its _to_proto method ==="
sed -n '1,100p' "src/hiero_sdk_python/consensus/topic_id.py" | cat -n

Repository: MonaaEid/hiero-sdk-python

Length of output: 4053


Type annotation mismatch: self.topic_id must be TopicId, not basic_types_pb2.TopicID.

Line 121 calls self.topic_id._to_proto(), but the type annotations (lines 28, 38) declare self.topic_id as basic_types_pb2.TopicID (a protobuf generated message). Protobuf messages do not have _to_proto() methods—this method exists only on the SDK's TopicId wrapper class. This mismatch will raise AttributeError: '_to_proto' at runtime.

Update the type annotations to use TopicId instead of basic_types_pb2.TopicID:

  • Import TopicId from hiero_sdk_python.consensus.topic_id
  • Change lines 28 and 38 from basic_types_pb2.TopicID to TopicId
  • Update the set_topic_id method signature (line 42) similarly

This aligns with other consensus transaction classes (topic_create_transaction.py, topic_update_transaction.py, topic_delete_transaction.py), which all follow the same pattern of calling ._to_proto() on self.topic_id.

return consensus_submit_message_pb2.ConsensusSubmitMessageTransactionBody(
topicID=self.topic_id._to_proto(),
message=chunk_content
message=bytes(self.message, "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

Prefer str.encode() over bytes() constructor for encoding strings.

self.message.encode("utf-8") is more idiomatic Python than bytes(self.message, "utf-8").

♻️ Proposed fix
-            message=bytes(self.message, "utf-8"),
+            message=self.message.encode("utf-8"),

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
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

Comment on lines +1 to +3
from hedera_sdk_python.hapi.basic_types_pb2 import Key, AccountID
from hedera_sdk_python.hapi.timestamp_pb2 import Timestamp
from hedera_sdk_python.hapi.duration_pb2 import Duration
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's check the actual imports in the file
cat -n src/hiero_sdk_python/consensus/topic_info.py | head -20

Repository: MonaaEid/hiero-sdk-python

Length of output: 815


🏁 Script executed:

# Check the structure of the hapi package
fd -t f "\.py$" src/hiero_sdk_python/hapi/ | head -30

Repository: MonaaEid/hiero-sdk-python

Length of output: 182


🏁 Script executed:

# Look for basic_types_pb2 and similar files
fd "basic_types_pb2|timestamp_pb2|duration_pb2" src/hiero_sdk_python/

Repository: MonaaEid/hiero-sdk-python

Length of output: 51


🏁 Script executed:

# Check if there's a services subdirectory under hapi
find src/hiero_sdk_python/hapi -type d | head -20

Repository: MonaaEid/hiero-sdk-python

Length of output: 129


🌐 Web query:

ConsensusTopicInfo protobuf message fields hedera-protobufs github

💡 Result:

ConsensusTopicInfo (in consensus_topic_info.proto in hashgraph/hedera-protobufs) has these fields (name → type → field #):

  • memostring1
  • runningHashbytes2
  • sequenceNumberuint643
  • expirationTimeTimestamp4
  • adminKeyKey5
  • submitKeyKey6
  • autoRenewPeriodDuration7
  • autoRenewAccountAccountID8
  • ledger_idbytes9 (added via HIP-33 / network/ledger ID in info responses) [1][2]

Sources: Protocol docs for consensus_topic_info.proto [1], HIP-33 excerpt showing the message definition and field numbering [2].


Fix incorrect package name in imports.

The imports reference hedera_sdk_python.hapi but should reference hiero_sdk_python.hapi to match the file's location under src/hiero_sdk_python/. Update imports to:

from hiero_sdk_python.hapi.basic_types_pb2 import Key, AccountID
from hiero_sdk_python.hapi.timestamp_pb2 import Timestamp
from hiero_sdk_python.hapi.duration_pb2 import Duration
🧰 Tools
🪛 GitHub Actions: Code Coverage

[error] ModuleNotFoundError: No module named 'hedera_sdk_python' during import. Dependency may be missing in environment.


⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check hapi imports in the consensus directory
echo "=== HAPI imports in consensus directory ==="
rg -n "from.*hapi.*import" src/hiero_sdk_python/consensus/

# Check imports in other SDK directories to understand the pattern
echo -e "\n=== HAPI imports in other directories ==="
rg -n "from.*hapi.*import" src/hiero_sdk_python/ | head -20

# Check if hedera_sdk_python exists anywhere in imports (it shouldn't)
echo -e "\n=== Any hedera_sdk_python imports (should be none) ==="
rg -n "from hedera_sdk_python" src/hiero_sdk_python/

Repository: MonaaEid/hiero-sdk-python

Length of output: 5155


🏁 Script executed:

#!/bin/bash
# Check how timestamp.py imports and uses Timestamp (similar to topic_info.py)
echo "=== timestamp.py imports and usage ==="
head -20 src/hiero_sdk_python/timestamp.py

# Check the structure of topic_info.py to see the full context
echo -e "\n=== topic_info.py complete import section ==="
head -30 src/hiero_sdk_python/consensus/topic_info.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 1616


🏁 Script executed:

#!/bin/bash
# Verify the proto structure for ConsensusTopicInfo
echo "=== Checking proto field alignment ==="

# Look at how other files handle the type annotations - check if they use SDK wrappers or proto types
echo -e "\n=== Check topic_create_transaction.py for comparison ==="
grep -A 20 "class TopicCreateTransaction" src/hiero_sdk_python/consensus/topic_create_transaction.py | head -30

# Verify if there are SDK wrapper classes for Key, AccountID, Duration
echo -e "\n=== Check if SDK wrapper classes exist for these types ==="
fd -t f -e py "key\.py|account.*\.py|duration\.py" src/hiero_sdk_python/ | grep -v __pycache__
</parameter>

Repository: MonaaEid/hiero-sdk-python

Length of output: 1888


🏁 Script executed:

#!/bin/bash
# Look at the SDK wrapper classes to understand the expected types
echo "=== SDK wrapper: Duration.py ==="
head -30 src/hiero_sdk_python/Duration.py

echo -e "\n=== SDK wrapper: account_id.py ==="
head -30 src/hiero_sdk_python/account/account_id.py

# Check what key wrapper exists
echo -e "\n=== Check for Key wrapper ==="
find src/hiero_sdk_python -name "*key*.py" -type f | grep -v __pycache__ | head -10
</parameter>

Repository: MonaaEid/hiero-sdk-python

Length of output: 2395


🏁 Script executed:

#!/bin/bash
# Check what topic_create_transaction.py imports for Key, Duration, AccountId
echo "=== topic_create_transaction.py imports ==="
head -30 src/hiero_sdk_python/consensus/topic_create_transaction.py

echo -e "\n=== Check if there's a Key wrapper class ==="
rg -n "^class Key" src/hiero_sdk_python/ | head -5

echo -e "\n=== Check if PublicKey is used as Key wrapper ==="
head -50 src/hiero_sdk_python/crypto/public_key.py | grep -A 20 "^class PublicKey"

Repository: MonaaEid/hiero-sdk-python

Length of output: 2068


🏁 Script executed:

#!/bin/bash
# Check the Timestamp wrapper class
echo "=== Timestamp wrapper class ==="
head -30 src/hiero_sdk_python/timestamp.py

# Verify what AccountId looks like (already shown it exists)
echo -e "\n=== Verify from_proto in topic_info.py to see what types it actually returns ==="
sed -n '45,65p' src/hiero_sdk_python/consensus/topic_info.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 2129


🏁 Script executed:

#!/bin/bash
# Verify the complete from_proto method to understand the scope
echo "=== Complete from_proto method ==="
sed -n '36,61p' src/hiero_sdk_python/consensus/topic_info.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 1247


Fix imports to use hiero_sdk_python package with correct paths and SDK wrapper types.

Lines 1–3 have multiple critical issues:

  1. Wrong package name: hedera_sdk_python should be hiero_sdk_python

  2. Missing path component: Imports omit .services — correct pattern is hiero_sdk_python.hapi.services.<module>_pb2

  3. Wrong types: Importing raw protobuf types instead of SDK wrapper classes that are used throughout the codebase:

    • Timestamp should come from hiero_sdk_python.timestamp (not timestamp_pb2)
    • Duration should come from hiero_sdk_python.Duration (not duration_pb2)
    • AccountID should be AccountId from hiero_sdk_python.account.account_id (not basic_types_pb2)
    • Key should come from hiero_sdk_python.utils.key_utils (not basic_types_pb2)
  4. Type hints incomplete: Lines 13–21 don't mark admin_key, submit_key, auto_renew_period, auto_renew_account as Optional[...] even though from_proto() can set them to None.

  5. Missing proto conversions: The from_proto() method (lines 45–61) passes raw protobuf objects directly without converting to SDK wrapper types. Use Timestamp._from_proto(), Duration._from_proto(), etc.

Compare with topic_create_transaction.py which correctly imports and uses SDK wrappers.

🧰 Tools
🪛 GitHub Actions: Code Coverage

[error] ModuleNotFoundError: No module named 'hedera_sdk_python' during import. Dependency may be missing in environment.

Comment on lines 10 to +30
def __init__(
self,
memo: str,
running_hash: bytes,
sequence_number: int,
expiration_time: Optional[Timestamp],
admin_key: Optional[Key],
submit_key: Optional[Key],
auto_renew_period: Optional[Duration],
auto_renew_account: Optional[AccountID],
ledger_id: Optional[bytes],
fee_schedule_key: Optional[PublicKey],
fee_exempt_keys: Optional[List[PublicKey]],
custom_fees: Optional[List[CustomFixedFee]],
) -> None:
"""
Initializes a new instance of the TopicInfo class.
Args:
memo (str): The memo associated with the topic.
running_hash (bytes): The current running hash of the topic.
sequence_number (int): The sequence number of the topic.
expiration_time (Optional[Timestamp]): The expiration time of the topic.
admin_key (Optional[Key]): The admin key for the topic.
submit_key (Optional[Key]): The submit key for the topic.
auto_renew_period (Optional[Duration]): The auto-renew period for the topic.
auto_renew_account (Optional[AccountID]): The account ID for auto-renewal.
ledger_id (Optional[bytes]): The ledger ID associated with the topic.
fee_schedule_key (PublicKey): The fee schedule key for the topic.
fee_exempt_keys (List[PublicKey]): The fee exempt keys for the topic.
custom_fees (List[CustomFixedFee]): The custom fees for the topic.
"""
self.memo: str = memo
self.running_hash: bytes = running_hash
self.sequence_number: int = sequence_number
self.expiration_time: Optional[Timestamp] = expiration_time
self.admin_key: Optional[Key] = admin_key
self.submit_key: Optional[Key] = submit_key
self.auto_renew_period: Optional[Duration] = auto_renew_period
self.auto_renew_account: Optional[AccountID] = auto_renew_account
self.ledger_id: Optional[bytes] = ledger_id
self.fee_schedule_key: PublicKey = fee_schedule_key
self.fee_exempt_keys: List[PublicKey] = (
list(fee_exempt_keys) if fee_exempt_keys is not None else []
)
self.custom_fees: List[CustomFixedFee] = (
list(custom_fees) if custom_fees is not None else []
)
self,
memo: str,
running_hash: bytes,
sequence_number: int,
expiration_time: Timestamp,
admin_key: Key,
submit_key: Key,
auto_renew_period: Duration,
auto_renew_account: AccountID,
ledger_id: bytes,
):
self.memo = memo
self.running_hash = running_hash
self.sequence_number = sequence_number
self.expiration_time = expiration_time
self.admin_key = admin_key
self.submit_key = submit_key
self.auto_renew_period = auto_renew_period
self.auto_renew_account = auto_renew_account
self.ledger_id = ledger_id
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Type annotations don't allow None for optional fields.

from_proto (lines 48–55) passes None for admin_key, submit_key, auto_renew_period, and auto_renew_account when the proto field is absent, but the __init__ signature declares them as non-optional (Key, Duration, AccountID). This will mislead type checkers and callers.

🐛 Proposed fix — add Optional annotations
+from typing import Optional
+
 class TopicInfo:
     ...
     def __init__(
         self,
         memo: str,
         running_hash: bytes,
         sequence_number: int,
         expiration_time: Timestamp,
-        admin_key: Key,
-        submit_key: Key,
-        auto_renew_period: Duration,
-        auto_renew_account: AccountID,
+        admin_key: Optional[Key],
+        submit_key: Optional[Key],
+        auto_renew_period: Optional[Duration],
+        auto_renew_account: Optional[AccountID],
         ledger_id: bytes,
     ):
🧰 Tools
🪛 GitHub Actions: Code Coverage

[error] ModuleNotFoundError: No module named 'hedera_sdk_python' during import. Dependency may be missing in environment.

🪛 Ruff (0.15.1)

[warning] 10-10: Missing return type annotation for special method __init__

Add return type annotation: None

(ANN204)

cls,
topic_info_proto: consensus_topic_info_pb2.ConsensusTopicInfo
) -> "TopicInfo":
def from_proto(cls, topic_info_proto):
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 return type annotations.

Static analysis (Ruff ANN204/ANN206) flags missing return types on from_proto and __repr__. Minor fix for consistency.

✏️ Proposed fix
     `@classmethod`
-    def from_proto(cls, topic_info_proto):
+    def from_proto(cls, topic_info_proto) -> "TopicInfo":
-    def __repr__(self):
+    def __repr__(self) -> str:

Also applies to: 59-59

🧰 Tools
🪛 GitHub Actions: Code Coverage

[error] ModuleNotFoundError: No module named 'hedera_sdk_python' during import. Dependency may be missing in environment.

🪛 Ruff (0.15.1)

[warning] 33-33: Missing return type annotation for classmethod from_proto

(ANN206)

Comment on lines 43 to 57
return cls(
memo=topic_info_proto.memo,
running_hash=topic_info_proto.runningHash,
sequence_number=topic_info_proto.sequenceNumber,
expiration_time=(
topic_info_proto.expirationTime
if topic_info_proto.HasField("expirationTime") else None
),
admin_key=(
topic_info_proto.adminKey
if topic_info_proto.HasField("adminKey") else None
),
submit_key=(
topic_info_proto.submitKey
if topic_info_proto.HasField("submitKey") else None
),
auto_renew_period=(
Duration._from_proto(proto=topic_info_proto.autoRenewPeriod)
if topic_info_proto.HasField("autoRenewPeriod") else None
),
auto_renew_account=(
topic_info_proto.autoRenewAccount
if topic_info_proto.HasField("autoRenewAccount") else None
),
ledger_id=getattr(topic_info_proto, "ledger_id", None),
fee_schedule_key=(
PublicKey._from_proto(topic_info_proto.fee_schedule_key)
if topic_info_proto.HasField("fee_schedule_key") else None
),
fee_exempt_keys=[PublicKey._from_proto(key) for key in topic_info_proto.fee_exempt_key_list],
custom_fees=[CustomFixedFee._from_proto(fee) for fee in topic_info_proto.custom_fees],
expiration_time=topic_info_proto.expirationTime,
admin_key=topic_info_proto.adminKey if topic_info_proto.HasField("adminKey") else None,
submit_key=topic_info_proto.submitKey if topic_info_proto.HasField("submitKey") else None,
auto_renew_period=topic_info_proto.autoRenewPeriod
if topic_info_proto.HasField("autoRenewPeriod")
else None,
auto_renew_account=topic_info_proto.autoRenewAccount
if topic_info_proto.HasField("autoRenewAccount")
else None,
ledger_id=topic_info_proto.ledger_id,
)
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

🧩 Analysis chain

🏁 Script executed:

# First, let's see the full file structure and imports
head -50 src/hiero_sdk_python/consensus/topic_info.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 1920


🏁 Script executed:

# Read the complete file
cat -n src/hiero_sdk_python/consensus/topic_info.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 3145


🏁 Script executed:

# Search for proto files or references to ConsensusTopicInfo
find . -name "*.proto" -type f 2>/dev/null | head -20

Repository: MonaaEid/hiero-sdk-python

Length of output: 51


🌐 Web query:

hedera-protobufs ConsensusTopicInfo expirationTime field definition

💡 Result:

In hedera-protobufs (services/consensus_topic_info.proto), the ConsensusTopicInfo field is defined as:

/** * An expiration time for this topic, in seconds since the epoch. *
 * For this purpose, `epoch` SHALL be the UNIX epoch
 * with 0 at `1970-01-01T00:00:00.000Z`. */
Timestamp expirationTime = 4;

So, expirationTime is a Timestamp field with protobuf field number 4. [1]

Sources:
[1] (raw.githubusercontent.com)

Citations:


🌐 Web query:

ConsensusTopicInfo full proto definition adminKey submitKey autoRenewPeriod autoRenewAccount expirationTime

💡 Result:

Here is the full ConsensusTopicInfo proto (from services/consensus_topic_info.proto), including the fields you called out: adminKey, submitKey, autoRenewPeriod, autoRenewAccount, expirationTime. [1]

syntax = "proto3";
package proto;

option java_package = "com.hederahashgraph.api.proto.java";
option java_multiple_files = true;

import "basic_types.proto";
import "custom_fees.proto";
import "duration.proto";
import "timestamp.proto";

/**
 * A query response describing the current state of a topic for the Hedera
 * Consensus Service (HCS).
 */
message ConsensusTopicInfo {
  string memo = 1;

  bytes runningHash = 2;

  uint64 sequenceNumber = 3;

  Timestamp expirationTime = 4;

  Key adminKey = 5;

  Key submitKey = 6;

  Duration autoRenewPeriod = 7;

  AccountID autoRenewAccount = 8;

  bytes ledger_id = 9;

  Key fee_schedule_key = 10;

  repeated Key fee_exempt_key_list = 11;

  repeated FixedCustomFee custom_fees = 12;
}

Source: [1]


expiration_time is not guarded with HasField, unlike the other message-type fields.

In the proto definition, expirationTime is a Timestamp message field (field 4), and in proto3 all fields are implicitly optional. Lines 48–55 correctly guard adminKey, submitKey, autoRenewPeriod, and autoRenewAccount with HasField to return None when absent. However, line 47 assigns expirationTime unconditionally, which means if the proto field was not set, it silently stores the default Timestamp(seconds=0, nanos=0) instead of None. This inconsistency with how other message-type fields are handled could mask missing data or lead to subtle bugs. Guard expirationTime the same way as the other optional message fields.

🧰 Tools
🪛 GitHub Actions: Code Coverage

[error] ModuleNotFoundError: No module named 'hedera_sdk_python' during import. Dependency may be missing in environment.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
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: 6

Comment on lines 121 to 125
def from_proto(
cls,
response_or_responses: Union[mirror_proto.ConsensusTopicResponse, List[mirror_proto.ConsensusTopicResponse]],
chunking_enabled: bool = False
) -> "TopicMessage":
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

🧩 Analysis chain

🏁 Script executed:

# First, find the file and check its structure
ls -la src/hiero_sdk_python/consensus/topic_message.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 165


🏁 Script executed:

# Find all callers of _from_proto and from_proto across the codebase
rg -n --type py "(_from_proto|from_proto)" --glob '!src/hiero_sdk_python/consensus/topic_message.py'

Repository: MonaaEid/hiero-sdk-python

Length of output: 50381


🏁 Script executed:

# Read the actual implementation at lines 121-148
sed -n '121,148p' src/hiero_sdk_python/consensus/topic_message.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 1375


🏁 Script executed:

# Check for hapi imports in the file (per coding guidelines)
head -50 src/hiero_sdk_python/consensus/topic_message.py | grep -n "from.*hapi"

Repository: MonaaEid/hiero-sdk-python

Length of output: 149


🏁 Script executed:

# Check if there are any integration tests or examples using TopicMessage.from_proto
grep -r "TopicMessage\." tests/ examples/ 2>/dev/null | grep -E "(from_proto|_from_proto)"

Repository: MonaaEid/hiero-sdk-python

Length of output: 51


🏁 Script executed:

# Check if TopicMessage is exported in any __init__ files
find src/hiero_sdk_python -name "__init__.py" -exec grep -l "TopicMessage" {} \;

Repository: MonaaEid/hiero-sdk-python

Length of output: 101


🏁 Script executed:

# Check how TopicMessage is exported in the main __init__.py
grep -A 5 -B 5 "TopicMessage" src/hiero_sdk_python/__init__.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 1309


🏁 Script executed:

# Check if there's any changelog or migration guide mentioning this rename
find . -name "CHANGELOG*" -o -name "MIGRATION*" -o -name "*.md" | xargs grep -l "TopicMessage\|from_proto" 2>/dev/null | head -5

Repository: MonaaEid/hiero-sdk-python

Length of output: 276


🏁 Script executed:

# Check CHANGELOG for TopicMessage or from_proto changes
grep -A 10 -B 2 "TopicMessage\|from_proto" CHANGELOG.md | head -50

Repository: MonaaEid/hiero-sdk-python

Length of output: 4128


🏁 Script executed:

# Check if TopicMessageQuery uses TopicMessage or TopicMessage.from_proto
grep -A 20 "class TopicMessageQuery" src/hiero_sdk_python/query/topic_message_query.py | grep -E "(from_proto|TopicMessage)"

Repository: MonaaEid/hiero-sdk-python

Length of output: 134


🏁 Script executed:

# Verify TopicMessage is not exported by checking if it's in the consensus module's __init__.py
cat src/hiero_sdk_python/consensus/__init__.py 2>/dev/null || echo "No consensus __init__.py found"

Repository: MonaaEid/hiero-sdk-python

Length of output: 51


🏁 Script executed:

# Check the topic_message.py file for the old _from_proto method to see if it was truly renamed or both exist
grep -n "def.*from_proto" src/hiero_sdk_python/consensus/topic_message.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 92


Make chunking_enabled parameter keyword-only to comply with FBT001/FBT002.

The boolean positional argument chunking_enabled: bool = False allows ambiguous call sites. Add * to make it keyword-only:

🛠️ Suggested fix
     `@classmethod`
     def from_proto(
         cls,
         response_or_responses: Union[mirror_proto.ConsensusTopicResponse, List[mirror_proto.ConsensusTopicResponse]],
+        *,
         chunking_enabled: bool = False
     ) -> "TopicMessage":
🧰 Tools
🪛 Ruff (0.15.1)

[warning] 123-123: Use X | Y for type annotations

Convert to X | Y

(UP007)


[error] 124-124: Boolean-typed positional argument in function definition

(FBT001)


[error] 124-124: Boolean default positional argument in function definition

(FBT002)

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Updated knowledge base settings and added schedule review instructions.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
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: 1

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
@MonaaEid
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Refactor TopicCreateTransaction class by removing unused methods and comments, updating constructor parameters, and modifying transaction fee.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
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

Caution

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

⚠️ Outside diff range comments (1)
src/hiero_sdk_python/consensus/topic_message.py (1)

1-196: ⚠️ Potential issue | 🟠 Major

No tests for TopicMessage.of_single / of_many covering the new string-based transaction_id.

The transaction ID construction logic changed from a TransactionId object to a hand-formatted string, yet no tests assert on the produced string format, correctness of field access, or edge cases (e.g., missing chunkInfo). Per coding guidelines, missing critical tests for proto-dependent logic is MAJOR.

As per coding guidelines, "Prefer tests that assert on produced protobufs (fields set, oneofs, ids). Cover: valid submit, missing topic id, empty message… Missing critical tests is MAJOR."

self.memo = memo or ""
self.admin_key = admin_key
self.submit_key = submit_key
self.auto_renew_period: Duration = auto_renew_period or Duration(7890000)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "auto_renew|7890000|7776000" --type py -A 2 -B 2 | head -100

Repository: MonaaEid/hiero-sdk-python

Length of output: 8896


🏁 Script executed:

cat -n src/hiero_sdk_python/consensus/topic_create_transaction.py | head -50

Repository: MonaaEid/hiero-sdk-python

Length of output: 2151


🏁 Script executed:

rg -n "7890000|7776000" --type py src/hiero_sdk_python/consensus/ src/hiero_sdk_python/account/ src/hiero_sdk_python/contract/

Repository: MonaaEid/hiero-sdk-python

Length of output: 599


🏁 Script executed:

find . -name "*test*" -type f -name "*.py" | xargs rg -l "auto_renew.*7890000|auto_renew.*7776000|auto_renew.*period" | head -5

Repository: MonaaEid/hiero-sdk-python

Length of output: 270


🏁 Script executed:

cat -n ./tests/unit/topic_create_transaction_test.py 2>/dev/null || cat -n ./tests/unit/topic_update_transaction_test.py | grep -A 5 -B 5 "7890000\|7776000\|auto_renew" | head -30

Repository: MonaaEid/hiero-sdk-python

Length of output: 23963


🏁 Script executed:

find . -type f \( -name "*.proto" -o -name "*.md" -o -name "README*" \) | xargs rg -l "7776000|7890000|auto.*renew" 2>/dev/null | head -5

Repository: MonaaEid/hiero-sdk-python

Length of output: 120


🏁 Script executed:

grep -n "7776000\|7890000\|auto.renew" ./CHANGELOG.md | head -20

Repository: MonaaEid/hiero-sdk-python

Length of output: 310


🏁 Script executed:

rg -n "90.*day|7776000|renew.*period" --type py -B 3 -A 3 | grep -A 5 -B 5 "duration\|Duration\|default" | head -50

Repository: MonaaEid/hiero-sdk-python

Length of output: 4021


🏁 Script executed:

web_search
What is the Hedera standard auto-renew period default in seconds for entities like topics and tokens?

Repository: MonaaEid/hiero-sdk-python

Length of output: 162


🌐 Web query:

Hedera auto-renew period default 90 days seconds

💡 Result:

Hedera’s default auto-renew period is 90 days, which is 7,776,000 seconds. [1]


Fix auto-renew period default to match Hedera standard (7,776,000 seconds = 90 days).

The SDK currently defaults to 7,890,000 seconds (91.3 days), which diverges from Hedera's standard 90-day auto-renew period of 7,776,000 seconds. This inconsistency is replicated across topic, account, and token transaction types and contradicts the example in examples/topic_info.py (line 99), which uses the correct value. Update all occurrences:

  • src/hiero_sdk_python/consensus/topic_create_transaction.py (line 12)
  • src/hiero_sdk_python/consensus/topic_update_transaction.py (line 36)
  • src/hiero_sdk_python/account/account_create_transaction.py (line 23)
  • src/hiero_sdk_python/account/account_update_transaction.py (line 23)
  • src/hiero_sdk_python/tokens/token_create_transaction.py (line 32)

Reference: Hedera standard auto-renew period is 7,776,000 seconds (exactly 90 days).

Comment on lines +83 to +86
if response.nodeTransactionPrecheckCode != ResponseCode.OK:
error_code = response.nodeTransactionPrecheckCode
error_message = ResponseCode.get_name(error_code)
raise Exception(f"Error during transaction submission: {error_code} ({error_message})")
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

Uses deprecated ResponseCode.get_name() — will emit FutureWarning.

ResponseCode.get_name() is deprecated (scheduled for removal in v0.1.4 per the source). Replace with the recommended ResponseCode(code).name.

Also, raising bare Exception is not ideal (Ruff TRY002/TRY003). Consider a dedicated SDK exception type if one exists.

♻️ Proposed fix
         if response.nodeTransactionPrecheckCode != ResponseCode.OK:
             error_code = response.nodeTransactionPrecheckCode
-            error_message = ResponseCode.get_name(error_code)
-            raise Exception(f"Error during transaction submission: {error_code} ({error_message})")
+            error_name = ResponseCode(error_code).name
+            raise Exception(f"Error during transaction submission: {error_code} ({error_name})")
🧰 Tools
🪛 Ruff (0.15.1)

[warning] 86-86: Create your own exception

(TRY002)


[warning] 86-86: Avoid specifying long messages outside the exception class

(TRY003)

Comment on lines +75 to +81
transaction_id: Optional[str] = None
if response.HasField("chunkInfo") and response.chunkInfo.HasField("initialTransactionID"):
transaction_id = TransactionId._from_proto(response.chunkInfo.initialTransactionID)
tx_id = response.chunkInfo.initialTransactionID
transaction_id = (
f"{tx_id.shardNum}.{tx_id.realmNum}.{tx_id.accountNum}-"
f"{tx_id.transactionValidStart.seconds}.{tx_id.transactionValidStart.nanos}"
)
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

Extract duplicated transaction ID formatting into a helper method.

The transaction ID string construction logic is duplicated verbatim in both of_single (lines 77–81) and of_many (lines 117–121). Extract to a @staticmethod to ensure consistency and single point of maintenance.

♻️ Proposed refactor
# Add as a static method on TopicMessage:
`@staticmethod`
def _format_transaction_id(tx_id) -> str:
    """Format a protobuf TransactionID as a canonical string."""
    acct = tx_id.accountID
    return (
        f"{acct.shardNum}.{acct.realmNum}.{acct.accountNum}@"
        f"{tx_id.transactionValidStart.seconds}.{tx_id.transactionValidStart.nanos:09d}"
    )

Then replace both occurrences with:

transaction_id = cls._format_transaction_id(tx_id)

Also applies to: 117-121

@github-actions
Copy link

Hi there! I'm the LinkedIssueBot.
This pull request has been automatically closed due to the following reason(s):

Thank you,
From Python SDK team

@github-actions github-actions bot closed this Feb 23, 2026
@MonaaEid MonaaEid reopened this Feb 25, 2026
MonaaEid and others added 2 commits February 26, 2026 00:55
Add detailed review instructions for Protobuf alignment in Python files.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
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: 4

♻️ Duplicate comments (1)
.coderabbit.yaml (1)

21-23: ⚠️ Potential issue | 🟠 Major

Move knowledge_base to the top level; current placement is schema-invalid.

Line 21 nests knowledge_base under reviews, but the schema defines knowledge_base as a top-level key. This was previously flagged and appears reintroduced.

🛠️ Proposed fix
 reviews:
   profile: "assertive"
   ...
-  knowledge_base:
-    web_search:
-      enabled: true
+  
+knowledge_base:
+  web_search:
+    enabled: true
#!/bin/bash
set -euo pipefail

echo "Nested knowledge_base under reviews (should be absent):"
rg -n '^\s{2}knowledge_base:\s*$' .coderabbit.yaml || true

echo "Top-level knowledge_base (should be present):"
rg -n '^knowledge_base:\s*$' .coderabbit.yaml || true

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 744dee6 and 2718e96.

📒 Files selected for processing (2)
  • .coderabbit.yaml
  • src/hiero_sdk_python/consensus/topic_create_transaction.py

Comment on lines +46 to 65
def build_transaction_body(self):
"""
Builds and returns the protobuf transaction body for topic creation.

Returns:
TransactionBody: The protobuf transaction body containing the topic creation details.

Raises:
ValueError: If required fields are missing.
"""
consensus_create_body = self._build_proto_body()
transaction_body = self.build_base_transaction_body()
transaction_body.consensusCreateTopic.CopyFrom(consensus_create_body)
transaction_body.consensusCreateTopic.CopyFrom(consensus_create_topic_pb2.ConsensusCreateTopicTransactionBody(
adminKey=self.admin_key._to_proto() if self.admin_key is not None else None,
submitKey=self.submit_key._to_proto() if self.submit_key is not None else None,
autoRenewPeriod=self.auto_renew_period._to_proto() if self.auto_renew_period is not None else None,
autoRenewAccount=self.auto_renew_account._to_proto() if self.auto_renew_account is not None else None,
memo=self.memo
))

return transaction_body
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the actual SDK file to see its current state
cat -n src/hiero_sdk_python/consensus/topic_create_transaction.py | head -80

Repository: MonaaEid/hiero-sdk-python

Length of output: 3747


🏁 Script executed:

# Search for HIP-991 or custom_fees references in the codebase
rg -i "hip.991|custom_fees|fee_schedule_key|fee_exempt" --type py src/hiero_sdk_python/consensus/

Repository: MonaaEid/hiero-sdk-python

Length of output: 3422


🏁 Script executed:

# Check if there's a local proto file or schema reference
fd "consensus.*proto|topic.*proto" --type f

Repository: MonaaEid/hiero-sdk-python

Length of output: 51


🏁 Script executed:

# Look for the proto imports at the top of the topic_create_transaction.py file
head -20 src/hiero_sdk_python/consensus/topic_create_transaction.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 896


🏁 Script executed:

# Look for the proto definition or any documentation
fd "consensus.*pb2" --type f src/hiero_sdk_python/

Repository: MonaaEid/hiero-sdk-python

Length of output: 51


🏁 Script executed:

# Check if there's any README or documentation about HIP-991
rg -i "HIP.991|custom.*fee" README.md CHANGELOG.md docs/ 2>/dev/null | head -20

Repository: MonaaEid/hiero-sdk-python

Length of output: 2605


🏁 Script executed:

# Look at the topic_update_transaction to see the full implementation pattern
cat -n src/hiero_sdk_python/consensus/topic_update_transaction.py | head -100

Repository: MonaaEid/hiero-sdk-python

Length of output: 4816


🏁 Script executed:

# Search for any comment or issue about TopicCreateTransaction not supporting custom fees
rg -i "TopicCreateTransaction|consensusCreateTopic" --type py -A 3 -B 3 | head -50

Repository: MonaaEid/hiero-sdk-python

Length of output: 4259


🌐 Web query:

hedera-protobufs ConsensusCreateTopicTransactionBody custom_fees fee_schedule_key proto schema

💡 Result:

In hedera-protobufs, the proto3 schema for ConsensusCreateTopicTransactionBody (HCS topic create) defines fee_schedule_key and custom_fees like this (from services/consensus_create_topic.proto):

  • Key fee_schedule_key = 8;
    • “Access control for update or delete of custom fees.” If unset at create, it cannot be set later via update; if set at create, it may be changed via update. (raw.githubusercontent.com)
  • repeated Key fee_exempt_key_list = 9; (related) (raw.githubusercontent.com)
  • repeated FixedCustomFee custom_fees = 10;
    • Custom fixed fees assessed for each ConsensusSubmitMessage to the topic; bounded by MAX_CUSTOM_FEE_ENTRIES_FOR_TOPICS. (raw.githubusercontent.com)

Imports / types used

  • The file imports basic_types.proto (for Key, AccountID), custom_fees.proto (for FixedCustomFee), and duration.proto. (raw.githubusercontent.com)

Spec context

  • HIP-991 describes adding topic-level fee_schedule_key and custom_fees (and exempt keys) to consensus topics. (hips.hedera.com)

Citations:


BLOCKER (Protobuf schema alignment): HIP-991 fields missing from ConsensusCreateTopicTransactionBody construction.

The proto schema at https://github.com/hashgraph/hedera-protobufs/blob/main/services/consensus_create_topic.proto defines three non-deprecated fields for ConsensusCreateTopicTransactionBody:

  • custom_fees (field 10, repeated FixedCustomFee)
  • fee_schedule_key (field 8, Key)
  • fee_exempt_key_list (field 9, repeated Key)

TopicCreateTransaction does not expose setters or constructor parameters for these fields, nor does it set them in build_transaction_body(). This violates the Protobuf Schema Alignment guideline: every non-deprecated proto field must have a corresponding SDK attribute.

Compare with TopicUpdateTransaction, which correctly implements all three fields with setters and includes them in the protobuf construction. The existing unit tests also expect these fields to be populated in the protobuf message.

Add custom_fees, fee_schedule_key, and fee_exempt_keys as constructor parameters and attributes, along with corresponding setters, and populate them in build_transaction_body() using the same pattern as TopicUpdateTransaction.

Updated the TransactionRecord class to include additional attributes and improved documentation. Refactored type hints and enhanced the __repr__ method for better readability.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
This module defines the TokenAssociation class, which represents a token-account association as reported by the Hedera network. It includes methods for conversion to and from protobuf representation.

Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
Signed-off-by: MontyPokemon <59332150+MonaaEid@users.noreply.github.com>
@MonaaEid
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 2

♻️ Duplicate comments (2)
.coderabbit.yaml (2)

285-294: ⚠️ Potential issue | 🟠 Major

schedule_review_instructions is an invalid key under reviews — move to document root.

The reviews object in CodeRabbit's schema sets "additionalProperties": false, so custom keys like schedule_review_instructions will fail schema validation. While the YAML anchor mechanism still works, CodeRabbit may reject or ignore this configuration.

Move the anchor definition to the document root level (outside reviews:), where it remains accessible for aliasing in path_instructions:

🛠️ Proposed fix
 language: "en-US"
+
+schedule_review_instructions: &schedule_review_instructions |
+  You are acting as a senior maintainer reviewing schedule-related code
+  ...
+
 knowledge_base:
   web_search:
     enabled: true
 reviews:
   profile: "assertive"
   ...
-  # SCHEDULE REVIEW INSTRUCTIONS
-  schedule_review_instructions: &schedule_review_instructions |
-    You are acting as a senior maintainer reviewing schedule-related code
-    ...

,


455-469: ⚠️ Potential issue | 🟠 Major

contract_review_instructions is also an invalid key under reviews.

Same schema violation as schedule_review_instructions. Move this anchor definition to the document root level alongside the schedule instructions fix.

,


ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2718e96 and a39c90d.

📒 Files selected for processing (3)
  • .coderabbit.yaml
  • src/hiero_sdk_python/tokens/token_association.py
  • src/hiero_sdk_python/transaction/transaction_record.py

Comment on lines +26 to +53
from typing import Optional

from hiero_sdk_python.account.account_id import AccountId
from hiero_sdk_python.tokens.token_id import TokenId
from hiero_sdk_python.hapi.services.basic_types_pb2 import TokenAssociation as TokenAssociationProto


@dataclass(frozen=True)
class TokenAssociation:
"""
A lightweight immutable representation of a token ↔ account association.

Used in:
- TransactionRecord.automatic_token_associations (list of associations
created automatically during transaction execution)

Attributes:
token_id: The identifier of the token being associated.
account_id: The identifier of the account receiving the association.

See usage examples in:
examples/tokens/token_association.py
"""

token_id: Optional[TokenId] = None
"""The identifier of the token being associated."""

account_id: Optional[AccountId] = None
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# First, verify the file exists and read the relevant lines
echo "=== Reading token_association.py (lines 1-60) ==="
head -60 src/hiero_sdk_python/tokens/token_association.py | cat -n

echo ""
echo "=== Checking file length ==="
wc -l src/hiero_sdk_python/tokens/token_association.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 2821


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check codebase style: find usage of Optional vs | None syntax
echo "=== Optional usage in tokens directory ==="
rg 'from typing import.*Optional' src/hiero_sdk_python/tokens/ | head -20

echo ""
echo "=== Union syntax (X | None) usage in tokens directory ==="
rg ': \w+ \| None' src/hiero_sdk_python/tokens/ | head -20

echo ""
echo "=== Check broader SDK codebase style ==="
echo "Optional imports in whole SDK:"
rg 'from typing import.*Optional' src/hiero_sdk_python/ --type py | wc -l
echo "Union syntax (X | None) in SDK:"
rg ': \w+ \| None' src/hiero_sdk_python/ --type py | wc -l

Repository: MonaaEid/hiero-sdk-python

Length of output: 2118


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify the proto schema for TokenAssociation
echo "=== Checking for hedera-protobufs reference or local proto definitions ==="
# Look for the canonical proto file or references
fd -e proto -e py | xargs grep -l "TokenAssociation" 2>/dev/null | head -10

echo ""
echo "=== Checking imports in token_association.py ==="
grep -n "import" src/hiero_sdk_python/tokens/token_association.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 586


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the complete token_association.py file
echo "=== Complete token_association.py ==="
cat -n src/hiero_sdk_python/tokens/token_association.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 3924


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the coding guidelines more closely for context on union syntax
# Look for Python version requirement or style guide references
echo "=== Checking for Python version requirement ==="
fd -e toml -e cfg -e txt | xargs grep -i "python.*3\." 2>/dev/null | head -10

echo ""
echo "=== Checking for any style or lint config ==="
fd -e pylintrc -e pyproject.toml -e setup.cfg | head -5

echo ""
echo "=== Check if token_id.py gives context ==="
cat -n src/hiero_sdk_python/tokens/token_id.py | head -80

Repository: MonaaEid/hiero-sdk-python

Length of output: 3672


Replace Optional[...] with X | None union syntax per codebase style guidelines.

Lines 26, 50, and 53 use Optional[TokenId] and Optional[AccountId] annotations.

Per the established codebase convention for Python 3.10+, use the union syntax TokenId | None and AccountId | None instead.

Proposed fix
-from typing import Optional
@@
-    token_id: Optional[TokenId] = None
+    token_id: TokenId | None = None
@@
-    account_id: Optional[AccountId] = None
+    account_id: AccountId | None = None
📝 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
from typing import Optional
from hiero_sdk_python.account.account_id import AccountId
from hiero_sdk_python.tokens.token_id import TokenId
from hiero_sdk_python.hapi.services.basic_types_pb2 import TokenAssociation as TokenAssociationProto
@dataclass(frozen=True)
class TokenAssociation:
"""
A lightweight immutable representation of a tokenaccount association.
Used in:
- TransactionRecord.automatic_token_associations (list of associations
created automatically during transaction execution)
Attributes:
token_id: The identifier of the token being associated.
account_id: The identifier of the account receiving the association.
See usage examples in:
examples/tokens/token_association.py
"""
token_id: Optional[TokenId] = None
"""The identifier of the token being associated."""
account_id: Optional[AccountId] = None
from hiero_sdk_python.account.account_id import AccountId
from hiero_sdk_python.tokens.token_id import TokenId
from hiero_sdk_python.hapi.services.basic_types_pb2 import TokenAssociation as TokenAssociationProto
`@dataclass`(frozen=True)
class TokenAssociation:
"""
A lightweight immutable representation of a tokenaccount association.
Used in:
- TransactionRecord.automatic_token_associations (list of associations
created automatically during transaction execution)
Attributes:
token_id: The identifier of the token being associated.
account_id: The identifier of the account receiving the association.
See usage examples in:
examples/tokens/token_association.py
"""
token_id: TokenId | None = None
"""The identifier of the token being associated."""
account_id: AccountId | None = None

Comment on lines +69 to +84
transaction_id: TransactionId | None = None
transaction_hash: bytes | None = None
transaction_memo: str | None = None
transaction_fee: int | None = None
receipt: TransactionReceipt | None = None
call_result: ContractFunctionResult | None = None
token_transfers: defaultdict[
TokenId, defaultdict[AccountId, int]
] = field(default_factory=lambda: defaultdict(lambda: defaultdict(int)))
nft_transfers: defaultdict[
TokenId, list[TokenNftTransfer]
] = field(default_factory=lambda: defaultdict(list))
transfers: defaultdict[AccountId, int] = field(default_factory=lambda: defaultdict(int))
new_pending_airdrops: list[PendingAirdropRecord] = field(default_factory=list)

prng_number: Optional[int] = None
prng_bytes: Optional[bytes] = None
prng_number: int | None = None
prng_bytes: bytes | None = None
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if file exists and get its current content
if [ ! -f src/hiero_sdk_python/transaction/transaction_record.py ]; then
  echo "File not found"
  exit 1
fi

# Verify current SDK handling of entropy/bytes presence.
echo "=== Checking entropy/bytes handling in SDK ==="
rg -n 'transaction_hash=|prng_number=|prng_bytes=|WhichOneof\("entropy"\)|HasField\("prng_' src/hiero_sdk_python/transaction/transaction_record.py || true

echo -e "\n=== Checking _from_proto around line 248, 257-258 ==="
sed -n '245,265p' src/hiero_sdk_python/transaction/transaction_record.py

echo -e "\n=== Checking _to_proto around line 365-382 ==="
sed -n '360,390p' src/hiero_sdk_python/transaction/transaction_record.py

echo -e "\n=== Checking oneof entropy constraint in proto (field 19, 20) ==="

Repository: MonaaEid/hiero-sdk-python

Length of output: 2973


🌐 Web query:

hashgraph hedera-protobufs transaction_record.proto oneof entropy

💡 Result:

In hashgraph/hedera-protobufs’s services/transaction_record.proto, TransactionRecord defines a oneof entropy for the UtilPrng transaction output, meaning exactly one of these fields is set in any given record: prng_bytes (384-bit bytes) or prng_number (32-bit int). prng_bytes is used when the PRNG transaction has no positive range, and prng_number is used when a positive range is provided. [1][2]

Where it is in the proto (field numbers):

  • oneof entropy { bytes prng_bytes = 19; int32 prng_number = 20; } [1]

How it maps to the request (UtilPrngTransactionBody):

  • range > 0 ⇒ record returns a 32-bit number (prng_number)
  • range <= 0/unset ⇒ record returns 384-bit bytes (prng_bytes) [2]

Sources

  1. services/transaction_record.proto (raw) [1]
  2. services/util_prng.proto (raw) [2]

Fix entropy oneof handling and transactionHash bytes normalization in round-trip mapping.

Lines 248, 257–258, and 380–381 currently violate protobuf presence semantics:

  • Proto fields: transactionHash (2, bytes), prng_bytes (19), prng_number (20)
  • Issue type: oneof violation | Bytes not normalized

_from_proto must detect which entropy branch is active using WhichOneof("entropy") instead of reading both fields. _to_proto must prevent setting both entropy fields together with a ValueError guard (consistent with the existing body oneof guard). Also normalize transactionHash to None when empty to match the bytes | None type.

Proposed fix
@@
-        return cls(
+        entropy_field = proto.WhichOneof("entropy")
+
+        return cls(
             transaction_id=tx_id,
-            transaction_hash=proto.transactionHash,
+            transaction_hash=proto.transactionHash or None,
             transaction_memo=proto.memo,
             transaction_fee=proto.transactionFee,
@@
-            prng_number=proto.prng_number,
-            prng_bytes=proto.prng_bytes,
+            prng_number=proto.prng_number if entropy_field == "prng_number" else None,
+            prng_bytes=proto.prng_bytes if entropy_field == "prng_bytes" else None,
@@
+        # Defensive guard for protobuf oneof entropy
+        if self.prng_number is not None and self.prng_bytes is not None:
+            raise ValueError(
+                "prng_number and prng_bytes are mutually exclusive "
+                "(protobuf oneof entropy)"
+            )
+
         record_proto = transaction_record_pb2.TransactionRecord(
             transactionHash=self.transaction_hash,
             memo=self.transaction_memo,
             transactionFee=self.transaction_fee,
             receipt=self.receipt._to_proto() if self.receipt else None,
             contractCallResult=(
                 self.call_result._to_proto() if self.call_result else None
             ),
-            prng_number=self.prng_number,
-            prng_bytes=self.prng_bytes,
+            prng_number=self.prng_number if self.prng_number is not None else 0,
+            prng_bytes=self.prng_bytes if self.prng_bytes is not None else b"",

@github-actions
Copy link

Hi there! I'm the LinkedIssueBot.
This pull request has been automatically closed due to the following reason(s):

Thank you,
From Python SDK team

@github-actions github-actions bot closed this Feb 26, 2026
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.

1 participant