Skip to content
Closed

Test #68

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
93 changes: 93 additions & 0 deletions src/hiero_sdk_python/tokens/token_association.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Module defining the TokenAssociation class.

This class provides a lightweight, immutable representation of a token ↔ account association
as reported by the Hedera network — specifically for associations that the network creates
**automatically** during transaction execution.

These appear in:
TransactionRecord.automatic_token_associations
(a list of TokenAssociation objects)

Common triggers include:
- Transfers to accounts with available auto-association slots
(configured via max_automatic_token_associations on the account)
- Payer-sponsored transfers to non-existent aliases (HIP-542: auto-account creation + association)
- Frictionless token airdrops (HIP-904: automatic association on transfer when allowed)

**Important: Informational / read-only only**
Creating an instance of this object has no effect on ledger state.
Instances of TokenAssociation cannot be submitted in any transaction.

To create an association explicitly, submit a TokenAssociateTransaction.
"""

from dataclasses import dataclass
from typing import Optional
Comment on lines +25 to +26
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

Use X | None instead of Optional[X] for consistency with codebase style.

The rest of the codebase (including the companion transaction_record.py in this PR) uses the X | None union syntax. New code should follow the same convention.

Proposed fix
 from dataclasses import dataclass
-from typing import Optional
 
 from hiero_sdk_python.account.account_id import AccountId
-    token_id: Optional[TokenId] = None
+    token_id: TokenId | None = None
     """The identifier of the token being associated."""
 
-    account_id: Optional[AccountId] = None
+    account_id: AccountId | None = None
     """The identifier of the account receiving the association."""

As per coding guidelines, "The codebase uses X | None union syntax (Python 3.10+). Flag any use of Optional[X] from typing in newly added or modified code, as it is inconsistent with the established style."

Also applies to: 50-50, 53-53


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
Comment on lines +28 to +30
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
# Check if the modules exist and verify their paths

echo "=== Checking AccountId file ==="
find . -name "account_id.py" -type f 2>/dev/null | head -5

echo "=== Checking TokenId file ==="
find . -name "token_id.py" -type f 2>/dev/null | head -5

echo "=== Checking basic_types_pb2 ==="
find . -name "basic_types_pb2.py" -type f 2>/dev/null | head -5

echo "=== Verifying AccountId class exists ==="
rg "class AccountId" --type=py -A 2

echo "=== Verifying TokenId class exists ==="
rg "class TokenId" --type=py -A 2

echo "=== Checking for TokenAssociation in basic_types_pb2 ==="
rg "class TokenAssociation" src/hiero_sdk_python/hapi/ --type=py

Repository: MonaaEid/hiero-sdk-python

Length of output: 915


🏁 Script executed:

#!/bin/bash
# Search for protobuf files in the codebase
echo "=== Finding all *_pb2.py files ==="
find . -name "*_pb2.py" -type f 2>/dev/null | head -20

echo "=== Searching for basic_types_pb2 anywhere ==="
find . -name "*basic_types*" -type f 2>/dev/null

echo "=== Listing src/hiero_sdk_python structure ==="
ls -la src/hiero_sdk_python/ 2>/dev/null | head -20

echo "=== Checking if hapi or similar protobuf module exists ==="
find src/hiero_sdk_python -type d -maxdepth 1 2>/dev/null

Repository: MonaaEid/hiero-sdk-python

Length of output: 1878


🏁 Script executed:

#!/bin/bash
# Read the actual token_association.py file
echo "=== Contents of token_association.py ==="
cat src/hiero_sdk_python/tokens/token_association.py

Repository: MonaaEid/hiero-sdk-python

Length of output: 3276


Remove or correct the import of non-existent hiero_sdk_python.hapi.services.basic_types_pb2.

The hiero_sdk_python.hapi module does not exist in the codebase. The AccountId and TokenId imports are valid, but the line importing TokenAssociation as TokenAssociationProto from hiero_sdk_python.hapi.services.basic_types_pb2 references a module path that is not present. This will cause an ImportError at runtime.

Verify whether:

  • Protobuf files should be generated at build time (and are currently missing)
  • The protobuf dependency is configured correctly
  • An alternative import path should be used



@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
"""The identifier of the account receiving the association."""

@classmethod
def _from_proto(cls, proto: TokenAssociationProto) -> "TokenAssociation":
"""
Construct a TokenAssociation object from the protobuf representation.
"""
return cls(
token_id=(
TokenId._from_proto(proto.token_id)
if proto.HasField("token_id")
else None
),
account_id=(
AccountId._from_proto(proto.account_id)
if proto.HasField("account_id")
else None
),
)

def _to_proto(self) -> TokenAssociationProto:
"""
Convert this TokenAssociation back into a protobuf message.
"""
proto = TokenAssociationProto()

if self.token_id is not None:
proto.token_id.CopyFrom(self.token_id._to_proto())

if self.account_id is not None:
proto.account_id.CopyFrom(self.account_id._to_proto())

return proto

def __str__(self) -> str:
return f"TokenAssociation(token_id={self.token_id}, account_id={self.account_id})"

def __repr__(self) -> str:
return self.__str__()

Loading
Loading