Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TokenDissociateTransaction #32

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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
Loading