Skip to content

Commit db74c9e

Browse files
authored
chore: Refactor TokenDissociateTransaction protobuf body creation (hiero-ledger#2454)
Signed-off-by: Manish Dait <daitmanish88@gmail.com>
1 parent 832a5e9 commit db74c9e

3 files changed

Lines changed: 142 additions & 39 deletions

File tree

src/hiero_sdk_python/tokens/token_dissociate_transaction.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,15 @@ def _build_proto_body(self) -> token_dissociate_pb2.TokenDissociateTransactionBo
9898
9999
Returns:
100100
TokenDissociateTransactionBody: The protobuf body for this transaction.
101-
102-
Raises:
103-
ValueError: If account ID or token IDs are not set.
104101
"""
105-
if not self.account_id or not self.token_ids:
106-
raise ValueError("Account ID and token IDs must be set.")
102+
transaction_body = token_dissociate_pb2.TokenDissociateTransactionBody()
103+
104+
if self.account_id is not None:
105+
transaction_body.account.CopyFrom(self.account_id._to_proto())
106+
if self.token_ids is not None:
107+
transaction_body.tokens.extend(token_id._to_proto() for token_id in self.token_ids if token_id is not None)
107108

108-
return token_dissociate_pb2.TokenDissociateTransactionBody(
109-
account=self.account_id._to_proto(), tokens=[token_id._to_proto() for token_id in self.token_ids]
110-
)
109+
return transaction_body
111110

112111
def build_transaction_body(self) -> transaction_pb2.TransactionBody:
113112
"""

tests/integration/token_dissociate_transaction_e2e_test.py

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,81 @@
44

55
from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction
66
from hiero_sdk_python.crypto.private_key import PrivateKey
7+
from hiero_sdk_python.exceptions import PrecheckError
78
from hiero_sdk_python.hbar import Hbar
89
from hiero_sdk_python.response_code import ResponseCode
910
from hiero_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
1011
from hiero_sdk_python.tokens.token_dissociate_transaction import TokenDissociateTransaction
11-
from tests.integration.utils import IntegrationTestEnv, create_fungible_token
12+
from tests.integration.utils import create_fungible_token
13+
14+
15+
def _create_associated_account(env):
16+
"""Creates an account and associates it with a fungible token."""
17+
private_key = PrivateKey.generate()
18+
19+
receipt = (
20+
AccountCreateTransaction()
21+
.set_key_without_alias(private_key)
22+
.set_initial_balance(Hbar(2))
23+
.set_account_memo("Recipient Account")
24+
.freeze_with(env.client)
25+
.execute(env.client)
26+
)
27+
28+
account_id = receipt.account_id
29+
token_id = create_fungible_token(env)
30+
31+
associate_tx = TokenAssociateTransaction().set_account_id(account_id).set_token_ids([token_id])
32+
associate_tx.freeze_with(env.client)
33+
associate_tx.sign(private_key)
34+
35+
receipt = associate_tx.execute(env.client)
36+
37+
assert receipt.status == ResponseCode.SUCCESS, (
38+
f"Token association failed with status: {ResponseCode(receipt.status).name}"
39+
)
40+
41+
return account_id, private_key, token_id
1242

1343

1444
@pytest.mark.integration
15-
def test_integration_token_dissociate_transaction_can_execute():
16-
env = IntegrationTestEnv()
45+
def test_integration_token_dissociate_transaction_can_execute(env):
46+
"""Test token dissociate transaction can be executed successfully."""
47+
account_id, account_private_key, token_id = _create_associated_account(env)
1748

18-
try:
19-
new_account_private_key = PrivateKey.generate()
20-
new_account_public_key = new_account_private_key.public_key()
49+
dissociate_transaction = TokenDissociateTransaction(account_id=account_id, token_ids=[token_id])
50+
dissociate_transaction.freeze_with(env.client)
51+
dissociate_transaction.sign(account_private_key)
2152

22-
initial_balance = Hbar(2)
53+
receipt = dissociate_transaction.execute(env.client)
2354

24-
assert initial_balance.to_tinybars() == 200000000
55+
assert receipt.status == ResponseCode.SUCCESS, (
56+
f"Token dissociation failed with status: {ResponseCode(receipt.status).name}"
57+
)
2558

26-
account_transaction = AccountCreateTransaction(
27-
key=new_account_public_key, initial_balance=initial_balance, memo="Recipient Account"
28-
)
2959

30-
account_transaction.freeze_with(env.client)
31-
account_receipt = account_transaction.execute(env.client)
32-
new_account_id = account_receipt.account_id
60+
def test_token_dissociate_transaction_can_execute_with_no_tokensId(env):
61+
"""Test token dissociate transaction can be executed without setting the tokenIds."""
62+
account_id, account_private_key, _ = _create_associated_account(env)
3363

34-
token_id = create_fungible_token(env)
64+
dissociate_transaction = TokenDissociateTransaction(account_id=account_id)
65+
dissociate_transaction.freeze_with(env.client)
66+
dissociate_transaction.sign(account_private_key)
3567

36-
associate_transaction = TokenAssociateTransaction(account_id=new_account_id, token_ids=[token_id])
37-
associate_transaction.freeze_with(env.client)
38-
associate_transaction.sign(new_account_private_key)
68+
receipt = dissociate_transaction.execute(env.client)
3969

40-
receipt = associate_transaction.execute(env.client)
70+
assert receipt.status == ResponseCode.SUCCESS, (
71+
f"Token dissociation failed with status: {ResponseCode(receipt.status).name}"
72+
)
4173

42-
assert receipt.status == ResponseCode.SUCCESS, (
43-
f"Token association failed with status: {ResponseCode(receipt.status).name}"
44-
)
4574

46-
dissociate_transaction = TokenDissociateTransaction(account_id=new_account_id, token_ids=[token_id])
47-
dissociate_transaction.freeze_with(env.client)
48-
dissociate_transaction.sign(new_account_private_key)
75+
def test_token_dissociate_transaction_raise_error_if_account_id_not_set(env):
76+
"""Test token dissociate transaction raises PrecheckError if accountId not set."""
77+
a_, account_private_key, token_id = _create_associated_account(env)
4978

50-
receipt = dissociate_transaction.execute(env.client)
79+
dissociate_transaction = TokenDissociateTransaction(token_ids=[token_id])
80+
dissociate_transaction.freeze_with(env.client)
81+
dissociate_transaction.sign(account_private_key)
5182

52-
assert receipt.status == ResponseCode.SUCCESS, (
53-
f"Token dissociation failed with status: {ResponseCode(receipt.status).name}"
54-
)
55-
finally:
56-
env.close()
83+
with pytest.raises(PrecheckError):
84+
dissociate_transaction.execute(env.client)

tests/unit/token_dissociate_transaction_test.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,79 @@ def test_build_scheduled_body(mock_account_ids):
191191
assert len(schedulable_body.tokenDissociate.tokens) == len(token_ids)
192192
for i, token_id in enumerate(token_ids):
193193
assert schedulable_body.tokenDissociate.tokens[i] == token_id._to_proto()
194+
195+
196+
def test_build_protobuf_body(mock_account_ids):
197+
"""Test build protobuf body for a token dissociate transaction."""
198+
account_id, _, _, token_id_1, token_id_2 = mock_account_ids
199+
token_ids = [token_id_1, token_id_2]
200+
201+
tx = TokenDissociateTransaction().set_account_id(account_id).set_token_ids(token_ids)
202+
203+
body = tx._build_proto_body()
204+
205+
assert body is not None
206+
assert body.HasField("account")
207+
assert body.account == account_id._to_proto()
208+
209+
assert len(body.tokens) == len(token_ids)
210+
assert list(body.tokens) == [token._to_proto() for token in token_ids]
211+
212+
213+
def test_build_protobuf_body_ignore_none_tokens(mock_account_ids):
214+
"""Test build protobuf body for a token dissociate transaction ignores none tokenId in list."""
215+
account_id, _, _, token_id, _ = mock_account_ids
216+
token_ids = [token_id, None]
217+
218+
tx = TokenDissociateTransaction().set_account_id(account_id).set_token_ids(token_ids)
219+
220+
body = tx._build_proto_body()
221+
222+
assert body is not None
223+
assert body.HasField("account")
224+
assert body.account == account_id._to_proto()
225+
226+
assert len(body.tokens) == 1
227+
assert list(body.tokens) == [token_id._to_proto()]
228+
229+
230+
def test_build_protobuf_body_without_account_id(mock_account_ids):
231+
"""Test build protobuf body for a token dissociate transaction without accountId."""
232+
_, _, _, token_id_1, token_id_2 = mock_account_ids
233+
token_ids = [token_id_1, token_id_2]
234+
235+
tx = TokenDissociateTransaction().set_token_ids(token_ids)
236+
237+
body = tx._build_proto_body()
238+
239+
assert body is not None
240+
assert not body.HasField("account")
241+
242+
assert len(body.tokens) == len(token_ids)
243+
assert list(body.tokens) == [token._to_proto() for token in token_ids]
244+
245+
246+
def test_build_protobuf_body_without_token_ids(mock_account_ids):
247+
"""Test build protobuf body for a token dissociate transaction without tokenIds."""
248+
account_id, _, _, _, _ = mock_account_ids
249+
250+
tx = TokenDissociateTransaction().set_account_id(account_id)
251+
252+
body = tx._build_proto_body()
253+
254+
assert body is not None
255+
assert body.HasField("account")
256+
assert body.account == account_id._to_proto()
257+
258+
assert len(body.tokens) == 0
259+
260+
261+
def test_build_protobuf_body_missing_both_account_and_token_ids():
262+
"""Test build protobuf body for a token dissociate transaction without accountId and tokenIds."""
263+
tx = TokenDissociateTransaction()
264+
265+
body = tx._build_proto_body()
266+
267+
assert body is not None
268+
assert not body.HasField("account")
269+
assert len(body.tokens) == 0

0 commit comments

Comments
 (0)