Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 16 additions & 28 deletions src/hiero_sdk_python/account/account_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from hiero_sdk_python.account.account_id import AccountId
from hiero_sdk_python.crypto.public_key import PublicKey
from hiero_sdk_python.Duration import Duration
from hiero_sdk_python.hapi.services.basic_types_pb2 import StakingInfo
from hiero_sdk_python.staking_info import StakingInfo
from hiero_sdk_python.hapi.services.crypto_get_info_pb2 import CryptoGetInfoResponse
from hiero_sdk_python.hbar import Hbar
from hiero_sdk_python.timestamp import Timestamp
Expand Down Expand Up @@ -56,9 +56,8 @@ class AccountInfo:
account_memo: Optional[str] = None
owned_nfts: Optional[int] = None
max_automatic_token_associations: Optional[int] = None
staked_account_id: Optional[AccountId] = None
staked_node_id: Optional[int] = None
decline_staking_reward: Optional[bool] = None
staking_info: Optional[StakingInfo] = None


@classmethod
def _from_proto(cls, proto: CryptoGetInfoResponse.AccountInfo) -> "AccountInfo":
Expand Down Expand Up @@ -100,20 +99,14 @@ def _from_proto(cls, proto: CryptoGetInfoResponse.AccountInfo) -> "AccountInfo":
account_memo=proto.memo,
owned_nfts=proto.ownedNfts,
max_automatic_token_associations=proto.max_automatic_token_associations,
staking_info=(
StakingInfo._from_proto(proto.staking_info)
if proto.HasField("staking_info")
else None
)
)

staking_info = proto.staking_info if proto.HasField('staking_info') else None

if staking_info:
account_info.staked_account_id = (
AccountId._from_proto(staking_info.staked_account_id)
if staking_info.HasField('staked_account_id') else None
)
account_info.staked_node_id = (
staking_info.staked_node_id
if staking_info.HasField('staked_node_id') else None
)
account_info.decline_staking_reward = staking_info.decline_reward

Comment thread
exploreriii marked this conversation as resolved.

return account_info

Expand Down Expand Up @@ -147,11 +140,11 @@ def _to_proto(self) -> CryptoGetInfoResponse.AccountInfo:
memo=self.account_memo,
ownedNfts=self.owned_nfts,
max_automatic_token_associations=self.max_automatic_token_associations,
staking_info=StakingInfo(
staked_account_id=self.staked_account_id._to_proto() if self.staked_account_id else None,
staked_node_id=self.staked_node_id if self.staked_node_id else None,
decline_reward=self.decline_staking_reward
),
staking_info=(
self.staking_info._to_proto()
if self.staking_info is not None
else None
),
)
Comment on lines +143 to 148

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Inconsistent indentation in _to_proto.

Line 147 uses 6-space indentation which breaks consistency with the rest of the method.

🔧 Proposed fix
             staking_info=(
                 self.staking_info._to_proto()
                 if self.staking_info is not None
                 else 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
staking_info=(
self.staking_info._to_proto()
if self.staking_info is not None
else None
),
)
staking_info=(
self.staking_info._to_proto()
if self.staking_info is not None
else None
),
)


def __str__(self) -> str:
Expand All @@ -166,8 +159,7 @@ def __str__(self) -> str:
(self.account_memo, "Memo"),
(self.owned_nfts, "Owned NFTs"),
(self.max_automatic_token_associations, "Max Automatic Token Associations"),
(self.staked_account_id, "Staked Account ID"),
(self.staked_node_id, "Staked Node ID"),
(self.staking_info, "Staked Info"),
(self.proxy_received, "Proxy Received"),
(self.expiration_time, "Expiration Time"),
(self.auto_renew_period, "Auto Renew Period"),
Expand All @@ -182,9 +174,6 @@ def __str__(self) -> str:

if self.receiver_signature_required is not None:
lines.append(f"Receiver Signature Required: {self.receiver_signature_required}")

if self.decline_staking_reward is not None:
lines.append(f"Decline Staking Reward: {self.decline_staking_reward}")

if self.token_relationships:
lines.append(f"Token Relationships: {len(self.token_relationships)}")
Expand All @@ -202,7 +191,6 @@ def __repr__(self) -> str:
f"receiver_signature_required={self.receiver_signature_required!r}, "
f"owned_nfts={self.owned_nfts!r}, "
f"account_memo={self.account_memo!r}, "
f"staked_node_id={self.staked_node_id!r}, "
f"staked_account_id={self.staked_account_id!r}"
f"staked_info={self.staking_info!r}, "
f")"
Comment on lines +194 to 195

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent field name in __repr__: staked_info should be staking_info.

The field is named staking_info (line 59), but __repr__ uses staked_info. This inconsistency could confuse developers during debugging.

🔧 Proposed fix
-            f"staked_info={self.staking_info!r}, "
+            f"staking_info={self.staking_info!r}, "
📝 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
f"staked_info={self.staking_info!r}, "
f")"
f"staking_info={self.staking_info!r}, "
f")"

)
57 changes: 57 additions & 0 deletions tests/unit/account_info_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from hiero_sdk_python.tokens.token_relationship import TokenRelationship
from hiero_sdk_python.tokens.token_id import TokenId
from hiero_sdk_python.hapi.services.crypto_get_info_pb2 import CryptoGetInfoResponse
from hiero_sdk_python.staking_info import StakingInfo

pytestmark = pytest.mark.unit

Expand All @@ -28,6 +29,8 @@ def account_info():
token_relationships=[],
account_memo="Test account memo",
owned_nfts=5,
max_automatic_token_associations=10,
staking_info=None
)


Expand All @@ -47,6 +50,8 @@ def proto_account_info():
tokenRelationships=[],
memo="Test account memo",
ownedNfts=5,
max_automatic_token_associations=10,
staking_info=None
)
return proto

Expand All @@ -65,6 +70,32 @@ def test_account_info_initialization(account_info):
assert account_info.token_relationships == []
assert account_info.account_memo == "Test account memo"
assert account_info.owned_nfts == 5
assert account_info.max_automatic_token_associations == 10
assert account_info.staking_info is None

def test_from_proto_with_staking_info():
"""Test the from_proto method of the AccountInfo class with staking info"""
public_key = PrivateKey.generate_ed25519().public_key()

staking_info={
"decline_reward": True,
"staked_node_id": 3,
"staked_account_id": None
}

proto = CryptoGetInfoResponse.AccountInfo(
accountID=AccountId(0, 0, 100)._to_proto(),
key=public_key._to_proto(),
balance=5000000,


)

account_info = AccountInfo._from_proto(proto)

assert account_info.staking_info is not None
assert account_info.staking_info.decline_reward is True
assert account_info.staking_info.staked_node_id == 3
Comment on lines +76 to +98

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Test is broken: staking_info is never set on the proto.

The test creates a staking_info dict (lines 80-84) but never uses it. The proto (lines 86-92) doesn't have staking_info set, so AccountInfo._from_proto(proto) will return staking_info=None. The assertions on lines 96-98 will fail because they expect staking_info to be populated.

Static analysis also flagged the unused variable on line 80.

🐛 Proposed fix - set staking_info on the proto
 def test_from_proto_with_staking_info():
     """Test the from_proto method of the AccountInfo class with staking info"""
     public_key = PrivateKey.generate_ed25519().public_key()
     
-    staking_info={
-            "decline_reward": True,
-            "staked_node_id": 3,
-            "staked_account_id": None 
-        }
+    # Create StakingInfo and convert to proto
+    staking_info = StakingInfo(
+        decline_reward=True,
+        staked_node_id=3,
+        staked_account_id=None
+    )
     
     proto = CryptoGetInfoResponse.AccountInfo(
         accountID=AccountId(0, 0, 100)._to_proto(),
         key=public_key._to_proto(),
         balance=5000000,
-        
-        
+        staking_info=staking_info._to_proto(),
     )
 
     account_info = AccountInfo._from_proto(proto)
     
     assert account_info.staking_info is not None
     assert account_info.staking_info.decline_reward is True
     assert account_info.staking_info.staked_node_id == 3
📝 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
def test_from_proto_with_staking_info():
"""Test the from_proto method of the AccountInfo class with staking info"""
public_key = PrivateKey.generate_ed25519().public_key()
staking_info={
"decline_reward": True,
"staked_node_id": 3,
"staked_account_id": None
}
proto = CryptoGetInfoResponse.AccountInfo(
accountID=AccountId(0, 0, 100)._to_proto(),
key=public_key._to_proto(),
balance=5000000,
)
account_info = AccountInfo._from_proto(proto)
assert account_info.staking_info is not None
assert account_info.staking_info.decline_reward is True
assert account_info.staking_info.staked_node_id == 3
def test_from_proto_with_staking_info():
"""Test the from_proto method of the AccountInfo class with staking info"""
public_key = PrivateKey.generate_ed25519().public_key()
# Create StakingInfo and convert to proto
staking_info = StakingInfo(
decline_reward=True,
staked_node_id=3,
staked_account_id=None
)
proto = CryptoGetInfoResponse.AccountInfo(
accountID=AccountId(0, 0, 100)._to_proto(),
key=public_key._to_proto(),
balance=5000000,
staking_info=staking_info._to_proto(),
)
account_info = AccountInfo._from_proto(proto)
assert account_info.staking_info is not None
assert account_info.staking_info.decline_reward is True
assert account_info.staking_info.staked_node_id == 3
🧰 Tools
🪛 Ruff (0.14.14)

[error] 80-80: Local variable staking_info is assigned to but never used

Remove assignment to unused variable staking_info

(F841)



def test_account_info_default_initialization():
Expand All @@ -82,7 +113,25 @@ def test_account_info_default_initialization():
assert account_info.token_relationships == []
assert account_info.account_memo is None
assert account_info.owned_nfts is None
assert account_info.max_automatic_token_associations is None
assert account_info.staking_info is None

def test_staking_info_persistence(account_info):
"""Ensure staking info is preserved through proto conversion"""

account_info.staking_info = StakingInfo(
decline_reward=True,
staked_node_id=5,
staked_account_id=None
)

proto = account_info._to_proto()
converted_info = AccountInfo._from_proto(proto)

assert converted_info.staking_info is not None
assert converted_info.staking_info.decline_reward is True
assert converted_info.staking_info.staked_node_id == 5
assert converted_info.staking_info.staked_account_id is None

def test_from_proto(proto_account_info):
"""Test the from_proto method of the AccountInfo class"""
Expand All @@ -100,6 +149,8 @@ def test_from_proto(proto_account_info):
assert account_info.token_relationships == []
assert account_info.account_memo == "Test account memo"
assert account_info.owned_nfts == 5
assert account_info.max_automatic_token_associations == 10
assert account_info.staking_info == None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use is None instead of == None for None comparison.

Per PEP 8, comparisons to singletons like None should use is or is not, not equality operators.

🔧 Proposed fix
-    assert account_info.staking_info == None
+    assert account_info.staking_info is 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
assert account_info.staking_info == None
assert account_info.staking_info is None
🧰 Tools
🪛 Ruff (0.14.14)

[error] 153-153: Comparison to None should be cond is None

Replace with cond is None

(E711)



def test_from_proto_with_token_relationships():
Expand Down Expand Up @@ -141,6 +192,11 @@ def test_to_proto(account_info):
assert proto.tokenRelationships == []
assert proto.memo == "Test account memo"
assert proto.ownedNfts == 5
assert proto.max_automatic_token_associations == 10
assert not proto.HasField("staking_info")





def test_to_proto_with_none_values():
Expand Down Expand Up @@ -192,6 +248,7 @@ def test_proto_conversion(account_info):
)
assert converted_account_info.account_memo == account_info.account_memo
assert converted_account_info.owned_nfts == account_info.owned_nfts
assert converted_account_info.staking_info == account_info.staking_info


def test_str_and_repr(account_info):
Expand Down
5 changes: 3 additions & 2 deletions tests/unit/topic_info_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def test_repr_and_str(topic_info):
assert "memo='Test topic memo'" in repr_output
assert "sequence_number=42" in repr_output
assert "running_hash=0x0102030405060708" in repr_output
assert "expiration_time=2021-07-01 00:00:00" in repr_output
assert "expiration_time=2021-07-01" in str_output
assert "auto_renew_period=7776000" in repr_output

def test_str_formatting(topic_info):
Expand All @@ -264,7 +264,8 @@ def test_str_formatting(topic_info):
assert "memo='Test topic memo'" in str_output
assert "running_hash=0x0102030405060708" in str_output
assert "sequence_number=42" in str_output
assert "expiration_time=2021-07-01 00:00:00" in str_output
# Instead of the full date-time string
assert "expiration_time=2021-07-01" in str_output
assert "admin_key=ed25519(" in str_output
assert "submit_key=ed25519(" in str_output
assert "auto_renew_period=7776000" in str_output
Expand Down
Loading