Skip to content

Commit

Permalink
TokenDissociateTransaction
Browse files Browse the repository at this point in the history
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
  • Loading branch information
exploreriii committed Jan 6, 2025
1 parent d2f2ce3 commit a2ff10e
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 18 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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

- Manage Token Transactions like Create, Associate, Transfer & Delete
- Manage Token Transactions like Create, Associate, Dissociate, Transfer & Delete
- Manage Consensus Transactions like Topic Create, Update, Delete
- Submit Topic Messages
- Query Account Balance, Transaction Receipts, Topic Infos and Messages
Expand All @@ -17,6 +17,7 @@ This is a Python SDK for interacting with the Hedera Hashgraph platform. It allo
- [Querying Account Balance](#querying-account-balance)
- [Creating a Token](#creating-a-token)
- [Associating a Token](#associating-a-token)
- [Dissociating a Token](#dissociating-a-token)
- [Transferring Tokens](#transferring-tokens)
- [Deleting a Token](#deleting-a-token)
- [Transferring HBAR](#transferring-hbar)
Expand Down Expand Up @@ -107,6 +108,7 @@ New Account Private Key: 228a06c363b0eb328434d51xxx...
New Account Public Key: 8f444e36e8926def492adxxx...
Token creation successful. Token ID: 0.0.5025xxx
Token association successful.
Token dissociation successful.
Token transfer successful.
Token deletion successful.
Topic creation successful.
Expand Down Expand Up @@ -229,6 +231,31 @@ transaction = (
transaction.execute(client)
```

### Dissociating a Token

#### Pythonic Syntax:
```
transaction = TokenDissociateTransaction(
account_id=recipient_id,
token_ids=[token_id]
).freeze_with(client)
transaction.sign(recipient_key)
transaction.execute(client)
```
#### Method Chaining:
```
transaction = (
TokenDissociateTransaction()
.set_account_id(recipient_id)
.add_token_id(token_id)
.freeze_with(client)
.sign(recipient_key)
)
transaction.execute(client)
```

### Transferring Tokens

#### Pythonic Syntax:
Expand Down
75 changes: 75 additions & 0 deletions examples/token_dissociate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import os
import sys
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.client.client import Client
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
from hedera_sdk_python.client.network import Network
from hedera_sdk_python.tokens.token_id import TokenId
from hedera_sdk_python.tokens.token_dissociate_transaction import TokenDissociateTransaction

load_dotenv()

def dissociate_token(): #Single token
network = Network(network='testnet')
client = Client(network)

recipient_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
recipient_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
token_id = TokenId.from_string('TOKEN_ID')

client.set_operator(recipient_id, recipient_key)

transaction = (
TokenDissociateTransaction()
.set_account_id(recipient_id)
.add_token_id(token_id)
.freeze_with(client)
.sign(recipient_key)
)

try:
receipt = transaction.execute(client)
print("Token dissociation successful.")
except Exception as e:
print(f"Token dissociation failed: {str(e)}")
sys.exit(1)

def dissociate_tokens(): # Multiple tokens
network = Network(network='testnet')
client = Client(network)

recipient_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
recipient_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
token_ids = [TokenId.from_string('TOKEN_ID_1'), TokenId.from_string('TOKEN_ID_2')]

client.set_operator(recipient_id, recipient_key)

transaction = (
TokenDissociateTransaction()
.set_account_id(recipient_id)
)

for token_id in token_ids:
transaction.add_token_id(token_id)

transaction = (
transaction
.freeze_with(client)
.sign(recipient_key)
)

try:
receipt = transaction.execute(client)
print("Token dissociations successful.")
except Exception as e:
print(f"Token dissociations failed: {str(e)}")
sys.exit(1)

if __name__ == "__main__":
dissociate_token() # For single token dissociation
# dissociate_tokens() # For multiple token dissociation
101 changes: 101 additions & 0 deletions src/hedera_sdk_python/tokens/token_dissociate_transaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from hedera_sdk_python.transaction.transaction import Transaction
from hedera_sdk_python.hapi.services import token_dissociate_pb2
from hedera_sdk_python.response_code import ResponseCode

class TokenDissociateTransaction(Transaction):
"""
Represents a token dissociate transaction on the Hedera network.
This transaction dissociates the specified tokens with an account,
meaning the account can no longer hold or transact with those tokens.
Inherits from the base Transaction class and implements the required methods
to build and execute a token dissociate transaction.
"""

def __init__(self, account_id=None, token_ids=None):
"""
Initializes a new TokenDissociateTransaction instance with default values.
"""
super().__init__()
self.account_id = account_id
self.token_ids = token_ids or []

self._default_transaction_fee = 500_000_000

def set_account_id(self, account_id):
self._require_not_frozen()
self.account_id = account_id
return self

def add_token_id(self, token_id):
self._require_not_frozen()
self.token_ids.append(token_id)
return self

def build_transaction_body(self):
"""
Builds and returns the protobuf transaction body for token dissociation.
Returns:
TransactionBody: The protobuf transaction body containing the token dissociation details.
Raises:
ValueError: If account ID or token IDs are not set.
"""
if not self.account_id or not self.token_ids:
raise ValueError("Account ID and token IDs must be set.")

token_dissociate_body = token_dissociate_pb2.TokenDissociateTransactionBody(
account=self.account_id.to_proto(),
tokens=[token_id.to_proto() for token_id in self.token_ids]
)

transaction_body = self.build_base_transaction_body()
transaction_body.tokenDissociate.CopyFrom(token_dissociate_body)

return transaction_body

def _execute_transaction(self, client, transaction_proto):
"""
Executes the token dissociation transaction using the provided client.
Args:
client (Client): The client instance to use for execution.
transaction_proto (Transaction): The protobuf Transaction message.
Returns:
TransactionReceipt: The receipt from the network after transaction execution.
Raises:
Exception: If the transaction submission fails or receives an error response.
"""
response = client.token_stub.dissociateTokens(transaction_proto)

if response.nodeTransactionPrecheckCode != ResponseCode.OK:
error_code = response.nodeTransactionPrecheckCode
error_message = ResponseCode.get_name(error_code)
raise Exception(f"Error during transaction submission: {error_code} ({error_message})")

receipt = self.get_receipt(client)
return receipt

def get_receipt(self, client, timeout=60):
"""
Retrieves the receipt for the transaction.
Args:
client (Client): The client instance.
timeout (int): Maximum time in seconds to wait for the receipt.
Returns:
TransactionReceipt: The transaction receipt from the network.
Raises:
Exception: If the transaction ID is not set or if receipt retrieval fails.
"""
if self.transaction_id is None:
raise Exception("Transaction ID is not set.")

receipt = client.get_transaction_receipt(self.transaction_id, timeout)
return receipt
57 changes: 40 additions & 17 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from hedera_sdk_python.crypto.private_key import PrivateKey
from hedera_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
from hedera_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
from hedera_sdk_python.tokens.token_dissociate_transaction import TokenDissociateTransaction
from hedera_sdk_python.transaction.transfer_transaction import TransferTransaction
from hedera_sdk_python.tokens.token_delete_transaction import TokenDeleteTransaction
from hedera_sdk_python.response_code import ResponseCode
Expand All @@ -18,7 +19,6 @@
from hedera_sdk_python.consensus.topic_id import TopicId
from hedera_sdk_python.query.topic_info_query import TopicInfoQuery
from hedera_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery

load_dotenv()

def load_operator_credentials():
Expand Down Expand Up @@ -90,10 +90,10 @@ def create_token(client, operator_id, admin_key):
print(f"Token creation successful. Token ID: {token_id}")
return token_id

def associate_token(client, recipient_id, recipient_private_key, token_id):
def associate_token(client, recipient_id, recipient_private_key, token_ids):
transaction = TokenAssociateTransaction(
account_id=recipient_id,
token_ids=[token_id]
token_ids=token_ids
)
transaction.freeze_with(client)
transaction.sign(client.operator_private_key)
Expand All @@ -109,16 +109,34 @@ def associate_token(client, recipient_id, recipient_private_key, token_id):
print(f"Token association failed: {str(e)}")
sys.exit(1)

def transfer_token(client, recipient_id, token_id):
transaction = TransferTransaction(
token_transfers={
token_id: {
client.operator_account_id: -1,
recipient_id: 1,
}
}
).freeze_with(client)
transaction.sign(client.operator_private_key)
def dissociate_token(client, recipient_id, recipient_private_key, token_id):
"""Dissociate the specified token with the recipient account."""
transaction = TokenDissociateTransaction(
account_id = recipient_id,
token_ids = token_id)
transaction.freeze_with(client)
transaction.sign(client.operator_private_key)
transaction.sign(recipient_private_key)

try:
receipt = transaction.execute(client)
if receipt.status != ResponseCode.SUCCESS:
status_message = ResponseCode.get_name(receipt.status)
raise Exception(f"Token dissociation failed with status: {status_message}")
print("Token dissociation successful.")
except Exception as e:
print(f"Token dissociation failed: {str(e)}")
sys.exit(1)

def transfer_token(client, source_id, source_private_key, recipient_id, token_id):
"""Transfer the specified token to the recipient account."""
transaction = (
TransferTransaction()
.add_token_transfer(token_id, source_id, -1)
.add_token_transfer(token_id, recipient_id, 1)
.freeze_with(client)
)
transaction.sign(source_private_key)

try:
receipt = transaction.execute(client)
Expand Down Expand Up @@ -248,10 +266,15 @@ def main():
recipient_id, recipient_private_key = create_new_account(client)
query_balance(client, recipient_id)

token_id = create_token(client, operator_id, admin_key)
associate_token(client, recipient_id, recipient_private_key, token_id)
transfer_token(client, recipient_id, token_id)
delete_token(client, token_id, admin_key)
token_id_1 = create_token(client, operator_id, admin_key)
token_id_2 = create_token(client, operator_id, admin_key)

associate_token(client, recipient_id, recipient_private_key, [token_id_1, token_id_2])
transfer_token(client, operator_id, operator_key, recipient_id, token_id_1)
transfer_token(client, recipient_id, recipient_private_key, operator_id, token_id_1)
dissociate_token(client, recipient_id, recipient_private_key, [token_id_1, token_id_2])
associate_token(client, recipient_id, recipient_private_key, [token_id_1])
delete_token(client, token_id_1, admin_key)

topic_id = create_topic(client)
submit_message(client, topic_id)
Expand Down
Loading

0 comments on commit a2ff10e

Please sign in to comment.