Skip to content

Commit df9ad8a

Browse files
committed
TokenDissociateTransaction
TokenDissociateTransaction Test token association transfer out operator
1 parent bdaffef commit df9ad8a

File tree

5 files changed

+339
-9
lines changed

5 files changed

+339
-9
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ submitting messages.
1313
- [Creating an Account](#creating-an-account)
1414
- [Creating a Token](#creating-a-token)
1515
- [Associating a Token](#associating-a-token)
16+
- [Dissociating a Token](#dissociating-a-token)
1617
- [Transferring Tokens](#transferring-tokens)
1718
- [Deleting a Token](#deleting-a-token)
1819
- [Transferring HBAR](#transferring-hbar)
@@ -97,13 +98,14 @@ New Account Private Key: 228a06c363b0eb328434d51xxx...
9798
New Account Public Key: 8f444e36e8926def492adxxx...
9899
Token creation successful. Token ID: 0.0.5025xxx
99100
Token association successful.
101+
Token dissociation successful.
100102
Token transfer successful.
101103
Token deletion successful.
102104
```
103105

104106
## Usage
105107

106-
Below are examples of how to use the SDK for creating tokens, associating them with accounts, and transferring or deleting tokens (also see 'examples' directiory)
108+
Below are examples of how to use the SDK for creating tokens, associating them with accounts, dissociating them from accounts, and transferring or deleting tokens (also see 'examples' directiory)
107109

108110
### Creating an Account
109111

@@ -153,6 +155,20 @@ transaction = (
153155
transaction.execute(client)
154156
```
155157

158+
### Dissociating a Token
159+
160+
```
161+
transaction = (
162+
TokenDissociateTransaction()
163+
.set_account_id(recipient_id)
164+
.add_token_id(token_id)
165+
.freeze_with(client)
166+
.sign(recipient_key)
167+
)
168+
169+
transaction.execute(client)
170+
```
171+
156172
### Transfering a Token
157173

158174
```

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 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):
17+
"""
18+
Initializes a new TokenDissociateTransaction instance with default values.
19+
"""
20+
super().__init__()
21+
self.account_id = None
22+
self.token_ids = []
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: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
from hedera_sdk_python.crypto.public_key import PublicKey
1010
from hedera_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
1111
from hedera_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
12+
from hedera_sdk_python.tokens.token_dissociate_transaction import TokenDissociateTransaction
1213
from hedera_sdk_python.transaction.transfer_transaction import TransferTransaction
1314
from hedera_sdk_python.response_code import ResponseCode
1415
from hedera_sdk_python.consensus.topic_create_transaction import TopicCreateTransaction
1516
from hedera_sdk_python.tokens.token_delete_transaction import TokenDeleteTransaction
1617
from cryptography.hazmat.primitives import serialization
17-
1818
load_dotenv()
1919

2020
def load_operator_credentials():
@@ -74,7 +74,6 @@ def create_token(client, operator_id, admin_key):
7474
transaction.sign(client.operator_private_key)
7575
transaction.sign(admin_key)
7676

77-
7877
try:
7978
receipt = transaction.execute(client)
8079
except Exception as e:
@@ -111,15 +110,36 @@ def associate_token(client, recipient_id, recipient_private_key, token_id):
111110
print(f"Token association failed: {str(e)}")
112111
sys.exit(1)
113112

114-
def transfer_token(client, recipient_id, token_id):
113+
def dissociate_token(client, recipient_id, recipient_private_key, token_id):
114+
"""Dissociate the specified token with the recipient account."""
115+
transaction = (
116+
TokenDissociateTransaction()
117+
.set_account_id(recipient_id)
118+
.add_token_id(token_id)
119+
.freeze_with(client)
120+
)
121+
transaction.sign(client.operator_private_key) # sign with operator's key (payer)
122+
transaction.sign(recipient_private_key) # sign with newly created account's key (recipient)
123+
124+
try:
125+
receipt = transaction.execute(client)
126+
if receipt.status != ResponseCode.SUCCESS:
127+
status_message = ResponseCode.get_name(receipt.status)
128+
raise Exception(f"Token dissociation failed with status: {status_message}")
129+
print("Token dissociation successful.")
130+
except Exception as e:
131+
print(f"Token dissociation failed: {str(e)}")
132+
sys.exit(1)
133+
134+
def transfer_token(client, source_id, source_private_key, recipient_id, token_id):
115135
"""Transfer the specified token to the recipient account."""
116136
transaction = (
117137
TransferTransaction()
118-
.add_token_transfer(token_id, client.operator_account_id, -1)
138+
.add_token_transfer(token_id, source_id, -1)
119139
.add_token_transfer(token_id, recipient_id, 1)
120140
.freeze_with(client)
121141
)
122-
transaction.sign(client.operator_private_key)
142+
transaction.sign(source_private_key)
123143

124144
try:
125145
receipt = transaction.execute(client)
@@ -155,7 +175,6 @@ def create_topic(client):
155175

156176
return topic_id
157177

158-
159178
def delete_token(client, token_id, admin_key):
160179
"""Deletes the specified token on the Hedera network."""
161180
transaction = (
@@ -194,9 +213,19 @@ def main():
194213

195214
recipient_id, recipient_private_key = create_new_account(client)
196215
token_id = create_token(client, operator_id, admin_key)
216+
217+
associate_token(client, recipient_id, recipient_private_key, token_id)
218+
print(f"Token {token_id} successfully associated.")
219+
transfer_token(client, operator_id, operator_key, recipient_id, token_id)
220+
print(f"Token {token_id} successfully transferred in.")
221+
transfer_token(client, recipient_id, recipient_private_key, operator_id, token_id)
222+
print(f"Token {token_id} successfully transferred out.")
223+
dissociate_token(client, recipient_id, recipient_private_key, token_id)
224+
print(f"Token {token_id} successfully dissociated.")
197225
associate_token(client, recipient_id, recipient_private_key, token_id)
198-
transfer_token(client, recipient_id, token_id)
199-
delete_token(client, token_id, admin_key)
226+
print(f"Token {token_id} successfully associated.")
227+
delete_token(client, token_id, admin_key)
228+
print(f"Token {token_id} successfully deleted.")
200229

201230
topic_id = create_topic(client)
202231

0 commit comments

Comments
 (0)