Skip to content

Commit a2ff10e

Browse files
committed
TokenDissociateTransaction
TokenDissociateTransaction TokenDissociateTransaction TokenDissociateTransaction TokenDissociateTransaction Test token association transfer out operator TokenDissociate readme pythonic excess logs to bytes comments multiple token test path list TokenDissociateTransaction TokenDissociateTransaction Test token association transfer out operator readme pythonic to bytes comments multiple token test path list token_ids not as extra list [] duplicates
1 parent d2f2ce3 commit a2ff10e

File tree

5 files changed

+353
-18
lines changed

5 files changed

+353
-18
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This is a Python SDK for interacting with the Hedera Hashgraph platform. It allows developers to:
44

5-
- Manage Token Transactions like Create, Associate, Transfer & Delete
5+
- Manage Token Transactions like Create, Associate, Dissociate, Transfer & Delete
66
- Manage Consensus Transactions like Topic Create, Update, Delete
77
- Submit Topic Messages
88
- Query Account Balance, Transaction Receipts, Topic Infos and Messages
@@ -17,6 +17,7 @@ This is a Python SDK for interacting with the Hedera Hashgraph platform. It allo
1717
- [Querying Account Balance](#querying-account-balance)
1818
- [Creating a Token](#creating-a-token)
1919
- [Associating a Token](#associating-a-token)
20+
- [Dissociating a Token](#dissociating-a-token)
2021
- [Transferring Tokens](#transferring-tokens)
2122
- [Deleting a Token](#deleting-a-token)
2223
- [Transferring HBAR](#transferring-hbar)
@@ -107,6 +108,7 @@ New Account Private Key: 228a06c363b0eb328434d51xxx...
107108
New Account Public Key: 8f444e36e8926def492adxxx...
108109
Token creation successful. Token ID: 0.0.5025xxx
109110
Token association successful.
111+
Token dissociation successful.
110112
Token transfer successful.
111113
Token deletion successful.
112114
Topic creation successful.
@@ -229,6 +231,31 @@ transaction = (
229231
transaction.execute(client)
230232
```
231233

234+
### Dissociating a Token
235+
236+
#### Pythonic Syntax:
237+
```
238+
transaction = TokenDissociateTransaction(
239+
account_id=recipient_id,
240+
token_ids=[token_id]
241+
).freeze_with(client)
242+
243+
transaction.sign(recipient_key)
244+
transaction.execute(client)
245+
```
246+
#### Method Chaining:
247+
```
248+
transaction = (
249+
TokenDissociateTransaction()
250+
.set_account_id(recipient_id)
251+
.add_token_id(token_id)
252+
.freeze_with(client)
253+
.sign(recipient_key)
254+
)
255+
256+
transaction.execute(client)
257+
```
258+
232259
### Transferring Tokens
233260

234261
#### Pythonic Syntax:

examples/token_dissociate.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import os
2+
import sys
3+
from dotenv import load_dotenv
4+
5+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
6+
sys.path.insert(0, project_root)
7+
8+
from hedera_sdk_python.client.client import Client
9+
from hedera_sdk_python.account.account_id import AccountId
10+
from hedera_sdk_python.crypto.private_key import PrivateKey
11+
from hedera_sdk_python.client.network import Network
12+
from hedera_sdk_python.tokens.token_id import TokenId
13+
from hedera_sdk_python.tokens.token_dissociate_transaction import TokenDissociateTransaction
14+
15+
load_dotenv()
16+
17+
def dissociate_token(): #Single token
18+
network = Network(network='testnet')
19+
client = Client(network)
20+
21+
recipient_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
22+
recipient_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
23+
token_id = TokenId.from_string('TOKEN_ID')
24+
25+
client.set_operator(recipient_id, recipient_key)
26+
27+
transaction = (
28+
TokenDissociateTransaction()
29+
.set_account_id(recipient_id)
30+
.add_token_id(token_id)
31+
.freeze_with(client)
32+
.sign(recipient_key)
33+
)
34+
35+
try:
36+
receipt = transaction.execute(client)
37+
print("Token dissociation successful.")
38+
except Exception as e:
39+
print(f"Token dissociation failed: {str(e)}")
40+
sys.exit(1)
41+
42+
def dissociate_tokens(): # Multiple tokens
43+
network = Network(network='testnet')
44+
client = Client(network)
45+
46+
recipient_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
47+
recipient_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
48+
token_ids = [TokenId.from_string('TOKEN_ID_1'), TokenId.from_string('TOKEN_ID_2')]
49+
50+
client.set_operator(recipient_id, recipient_key)
51+
52+
transaction = (
53+
TokenDissociateTransaction()
54+
.set_account_id(recipient_id)
55+
)
56+
57+
for token_id in token_ids:
58+
transaction.add_token_id(token_id)
59+
60+
transaction = (
61+
transaction
62+
.freeze_with(client)
63+
.sign(recipient_key)
64+
)
65+
66+
try:
67+
receipt = transaction.execute(client)
68+
print("Token dissociations successful.")
69+
except Exception as e:
70+
print(f"Token dissociations failed: {str(e)}")
71+
sys.exit(1)
72+
73+
if __name__ == "__main__":
74+
dissociate_token() # For single token dissociation
75+
# dissociate_tokens() # For multiple token dissociation
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from hedera_sdk_python.transaction.transaction import Transaction
2+
from hedera_sdk_python.hapi.services import token_dissociate_pb2
3+
from hedera_sdk_python.response_code import ResponseCode
4+
5+
class TokenDissociateTransaction(Transaction):
6+
"""
7+
Represents a token dissociate transaction on the Hedera network.
8+
9+
This transaction dissociates the specified tokens with an account,
10+
meaning the account can no longer hold or transact with those tokens.
11+
12+
Inherits from the base Transaction class and implements the required methods
13+
to build and execute a token dissociate transaction.
14+
"""
15+
16+
def __init__(self, account_id=None, token_ids=None):
17+
"""
18+
Initializes a new TokenDissociateTransaction instance with default values.
19+
"""
20+
super().__init__()
21+
self.account_id = account_id
22+
self.token_ids = token_ids or []
23+
24+
self._default_transaction_fee = 500_000_000
25+
26+
def set_account_id(self, account_id):
27+
self._require_not_frozen()
28+
self.account_id = account_id
29+
return self
30+
31+
def add_token_id(self, token_id):
32+
self._require_not_frozen()
33+
self.token_ids.append(token_id)
34+
return self
35+
36+
def build_transaction_body(self):
37+
"""
38+
Builds and returns the protobuf transaction body for token dissociation.
39+
40+
Returns:
41+
TransactionBody: The protobuf transaction body containing the token dissociation details.
42+
43+
Raises:
44+
ValueError: If account ID or token IDs are not set.
45+
"""
46+
if not self.account_id or not self.token_ids:
47+
raise ValueError("Account ID and token IDs must be set.")
48+
49+
token_dissociate_body = token_dissociate_pb2.TokenDissociateTransactionBody(
50+
account=self.account_id.to_proto(),
51+
tokens=[token_id.to_proto() for token_id in self.token_ids]
52+
)
53+
54+
transaction_body = self.build_base_transaction_body()
55+
transaction_body.tokenDissociate.CopyFrom(token_dissociate_body)
56+
57+
return transaction_body
58+
59+
def _execute_transaction(self, client, transaction_proto):
60+
"""
61+
Executes the token dissociation transaction using the provided client.
62+
63+
Args:
64+
client (Client): The client instance to use for execution.
65+
transaction_proto (Transaction): The protobuf Transaction message.
66+
67+
Returns:
68+
TransactionReceipt: The receipt from the network after transaction execution.
69+
70+
Raises:
71+
Exception: If the transaction submission fails or receives an error response.
72+
"""
73+
response = client.token_stub.dissociateTokens(transaction_proto)
74+
75+
if response.nodeTransactionPrecheckCode != ResponseCode.OK:
76+
error_code = response.nodeTransactionPrecheckCode
77+
error_message = ResponseCode.get_name(error_code)
78+
raise Exception(f"Error during transaction submission: {error_code} ({error_message})")
79+
80+
receipt = self.get_receipt(client)
81+
return receipt
82+
83+
def get_receipt(self, client, timeout=60):
84+
"""
85+
Retrieves the receipt for the transaction.
86+
87+
Args:
88+
client (Client): The client instance.
89+
timeout (int): Maximum time in seconds to wait for the receipt.
90+
91+
Returns:
92+
TransactionReceipt: The transaction receipt from the network.
93+
94+
Raises:
95+
Exception: If the transaction ID is not set or if receipt retrieval fails.
96+
"""
97+
if self.transaction_id is None:
98+
raise Exception("Transaction ID is not set.")
99+
100+
receipt = client.get_transaction_receipt(self.transaction_id, timeout)
101+
return receipt

test.py

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from hedera_sdk_python.crypto.private_key import PrivateKey
99
from hedera_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
1010
from hedera_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
11+
from hedera_sdk_python.tokens.token_dissociate_transaction import TokenDissociateTransaction
1112
from hedera_sdk_python.transaction.transfer_transaction import TransferTransaction
1213
from hedera_sdk_python.tokens.token_delete_transaction import TokenDeleteTransaction
1314
from hedera_sdk_python.response_code import ResponseCode
@@ -18,7 +19,6 @@
1819
from hedera_sdk_python.consensus.topic_id import TopicId
1920
from hedera_sdk_python.query.topic_info_query import TopicInfoQuery
2021
from hedera_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery
21-
2222
load_dotenv()
2323

2424
def load_operator_credentials():
@@ -90,10 +90,10 @@ def create_token(client, operator_id, admin_key):
9090
print(f"Token creation successful. Token ID: {token_id}")
9191
return token_id
9292

93-
def associate_token(client, recipient_id, recipient_private_key, token_id):
93+
def associate_token(client, recipient_id, recipient_private_key, token_ids):
9494
transaction = TokenAssociateTransaction(
9595
account_id=recipient_id,
96-
token_ids=[token_id]
96+
token_ids=token_ids
9797
)
9898
transaction.freeze_with(client)
9999
transaction.sign(client.operator_private_key)
@@ -109,16 +109,34 @@ def associate_token(client, recipient_id, recipient_private_key, token_id):
109109
print(f"Token association failed: {str(e)}")
110110
sys.exit(1)
111111

112-
def transfer_token(client, recipient_id, token_id):
113-
transaction = TransferTransaction(
114-
token_transfers={
115-
token_id: {
116-
client.operator_account_id: -1,
117-
recipient_id: 1,
118-
}
119-
}
120-
).freeze_with(client)
121-
transaction.sign(client.operator_private_key)
112+
def dissociate_token(client, recipient_id, recipient_private_key, token_id):
113+
"""Dissociate the specified token with the recipient account."""
114+
transaction = TokenDissociateTransaction(
115+
account_id = recipient_id,
116+
token_ids = token_id)
117+
transaction.freeze_with(client)
118+
transaction.sign(client.operator_private_key)
119+
transaction.sign(recipient_private_key)
120+
121+
try:
122+
receipt = transaction.execute(client)
123+
if receipt.status != ResponseCode.SUCCESS:
124+
status_message = ResponseCode.get_name(receipt.status)
125+
raise Exception(f"Token dissociation failed with status: {status_message}")
126+
print("Token dissociation successful.")
127+
except Exception as e:
128+
print(f"Token dissociation failed: {str(e)}")
129+
sys.exit(1)
130+
131+
def transfer_token(client, source_id, source_private_key, recipient_id, token_id):
132+
"""Transfer the specified token to the recipient account."""
133+
transaction = (
134+
TransferTransaction()
135+
.add_token_transfer(token_id, source_id, -1)
136+
.add_token_transfer(token_id, recipient_id, 1)
137+
.freeze_with(client)
138+
)
139+
transaction.sign(source_private_key)
122140

123141
try:
124142
receipt = transaction.execute(client)
@@ -248,10 +266,15 @@ def main():
248266
recipient_id, recipient_private_key = create_new_account(client)
249267
query_balance(client, recipient_id)
250268

251-
token_id = create_token(client, operator_id, admin_key)
252-
associate_token(client, recipient_id, recipient_private_key, token_id)
253-
transfer_token(client, recipient_id, token_id)
254-
delete_token(client, token_id, admin_key)
269+
token_id_1 = create_token(client, operator_id, admin_key)
270+
token_id_2 = create_token(client, operator_id, admin_key)
271+
272+
associate_token(client, recipient_id, recipient_private_key, [token_id_1, token_id_2])
273+
transfer_token(client, operator_id, operator_key, recipient_id, token_id_1)
274+
transfer_token(client, recipient_id, recipient_private_key, operator_id, token_id_1)
275+
dissociate_token(client, recipient_id, recipient_private_key, [token_id_1, token_id_2])
276+
associate_token(client, recipient_id, recipient_private_key, [token_id_1])
277+
delete_token(client, token_id_1, admin_key)
255278

256279
topic_id = create_topic(client)
257280
submit_message(client, topic_id)

0 commit comments

Comments
 (0)