Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.


### Changed
- Refactored token-related example scripts (`token_delete.py`, `token_dissociate.py`, etc.) for improved readability and modularity. [#370]


### Fixed
Expand Down
70 changes: 45 additions & 25 deletions examples/token_delete.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""
uv run examples/token_delete.py
python examples/token_delete.py
# uv run examples/token_delete.py
# python examples/token_delete.py

"""
A full example that creates a token and then immediately deletes it.
"""

import os
import sys
from dotenv import load_dotenv
Expand All @@ -18,40 +20,41 @@

# Load environment variables from .env file
load_dotenv()

network_name = os.getenv('NETWORK', 'testnet').lower()

def create_and_delete_token():
"""
A full example that creates a token and then immediately deletes it.
"""
# 1. Setup Client
# =================================================================
def setup_client():
"""Setup Client """
network = Network(network_name)
print(f"Connecting to Hedera {network_name} network!")
client = Client(network)

# Get the operator account from the .env file
try:
operator_id = AccountId.from_string(os.getenv('OPERATOR_ID', ''))
# NOTE: Assumes your operator key is a raw Ed25519 key
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY', ''))
# Set the operator (payer) account for the client
client.set_operator(operator_id, operator_key)
print(f"Client set up with operator id {client.operator_account_id}")
return client, operator_id, operator_key
except (TypeError, ValueError):
print("Error: Please check OPERATOR_ID and OPERATOR_KEY in your .env file.")
sys.exit(1)


client.set_operator(operator_id, operator_key)
print(f"Client set up with operator id {client.operator_account_id}")
def generate_admin_key():
"""Generate a new admin key within the script:
This key will be used to create the token with admin privileges
"""

# 2. Generate a new admin key within the script
# =================================================================
print("\nGenerating a new admin key for the token...")
admin_key = PrivateKey.generate_ed25519()
admin_key = PrivateKey.generate(os.getenv('KEY_TYPE', 'ed25519'))
print("Admin key generated successfully.")
return admin_key

def create_new_token(client, operator_id, operator_key, admin_key):
""" Create the Token"""
token_id_to_delete = None

# 3. Create the Token
# =================================================================
try:
print("\nSTEP 1: Creating a new token...")
create_tx = (
Expand All @@ -69,14 +72,18 @@ def create_and_delete_token():
create_receipt = create_tx.execute(client)
token_id_to_delete = create_receipt.token_id
print(f"✅ Success! Created token with ID: {token_id_to_delete}")
return token_id_to_delete

except Exception as e:
except (ValueError, TypeError) as e:
print(f"❌ Error creating token: {e}")
sys.exit(1)


# 4. Delete the Token
# =================================================================
def delete_token(admin_key, token_id_to_delete, client, operator_key):
"""
Delete the Token we just created
"""

try:
print(f"\nSTEP 2: Deleting token {token_id_to_delete}...")
delete_tx = (
Expand All @@ -87,13 +94,26 @@ def create_and_delete_token():
.sign(admin_key) # Sign with the same admin key used to create it
)

delete_receipt = delete_tx.execute(client)
print(f"✅ Success! Token deleted.")
delete_tx.execute(client)
print("✅ Success! Token deleted.")

except Exception as e:
except (ValueError, TypeError) as e:
print(f"❌ Error deleting token: {e}")
sys.exit(1)

def main():
"""
1. Call create_new_token() to create a new token and get its admin key, token ID, client, and operator key.
2. Build a TokenDeleteTransaction using the token ID.
3. Freeze the transaction with the client.
4. Sign the transaction with both the operator key and the admin key.
5. Execute the transaction to delete the token.
6. Print the result or handle any errors.
"""
client, operator_id, operator_key = setup_client()
admin_key = generate_admin_key()
token_id_to_delete = create_new_token(client, operator_id, operator_key, admin_key)
delete_token(admin_key, token_id_to_delete, client, operator_key)

if __name__ == "__main__":
create_and_delete_token()
main()
155 changes: 110 additions & 45 deletions examples/token_dissociate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# uv run examples/token_dissociate.py
# python examples/token_dissociate.py
"""
uv run examples/token_dissociate.py
python examples/token_dissociate.py

A full example that creates an account, two tokens, associates them,
and finally dissociates them.
"""
import os
import sys
Expand All @@ -17,19 +18,17 @@
TokenCreateTransaction,
TokenAssociateTransaction,
TokenDissociateTransaction,
TokenType,
AccountInfoQuery,
ResponseCode,
)

# Load environment variables from .env file
load_dotenv()

network_name = os.getenv('NETWORK', 'testnet').lower()
def token_dissociate():
"""
A full example that creates an account, two tokens, associates them,
and finally dissociates them.
"""
# 1. Setup Client
# =================================================================

def setup_client():
"""Setup Client"""
network = Network(network_name)
print(f"Connecting to Hedera {network_name} network!")
client = Client(network)
Expand All @@ -38,16 +37,19 @@ def token_dissociate():
operator_id = AccountId.from_string(os.getenv('OPERATOR_ID', ''))
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY', ''))
client.set_operator(operator_id, operator_key)
print(f"Client set up with operator id {client.operator_account_id}")
return client, operator_id, operator_key

except (TypeError, ValueError):
print("❌ Error: Please check OPERATOR_ID and OPERATOR_KEY in your .env file.")
sys.exit(1)

print(f"Client set up with operator id {client.operator_account_id}")

# 2. Create a new account to associate/dissociate with
# =================================================================
def create_new_account(client, operator_id, operator_key):
"""Create a new account to associate/dissociate with tokens"""
print("\nSTEP 1: Creating a new account...")
recipient_key = PrivateKey.generate("ed25519")
recipient_key = PrivateKey.generate(os.getenv('KEY_TYPE', 'ed25519'))

try:
# Build the transaction
tx = (
Expand All @@ -60,65 +62,128 @@ def token_dissociate():
receipt = tx.freeze_with(client).sign(operator_key).execute(client)
recipient_id = receipt.account_id
print(f"✅ Success! Created new account with ID: {recipient_id}")
except Exception as e:
return client, operator_key, recipient_id, recipient_key, operator_id

except (ValueError, RuntimeError) as e:
print(f"❌ Error creating new account: {e}")
sys.exit(1)

# 3. Create two new tokens
# =================================================================
def create_token(client, operator_key, recipient_id, recipient_key, operator_id):
"""Create two new tokens (one NFT and one fungible) for demonstration purposes. """
print("\nSTEP 2: Creating two new tokens...")
try:
# Create First Token
tx1 = TokenCreateTransaction().set_token_name("First Token").set_token_symbol("TKA").set_initial_supply(1).set_treasury_account_id(operator_id)
receipt1 = tx1.freeze_with(client).sign(operator_key).execute(client)
token_id_1 = receipt1.token_id

# Create Second Token
tx2 = TokenCreateTransaction().set_token_name("Second Token").set_token_symbol("TKB").set_initial_supply(1).set_treasury_account_id(operator_id)
receipt2 = tx2.freeze_with(client).sign(operator_key).execute(client)
token_id_2 = receipt2.token_id

print(f"✅ Success! Created tokens: {token_id_1} and {token_id_2}")
except Exception as e:
# Generate supply key for NFT
supply_key = PrivateKey.generate_ed25519()
# Create NFT Token
nft_tx = (
TokenCreateTransaction()
.set_token_name("NFT Token")
.set_token_symbol("NFTK")
.set_token_type(TokenType.NON_FUNGIBLE_UNIQUE)
.set_initial_supply(0)
.set_treasury_account_id(operator_id)
.set_supply_key(supply_key)
)
nft_receipt = nft_tx.freeze_with(client).sign(operator_key).execute(client)
nft_token_id = nft_receipt.token_id

# Create Fungible Token
fungible_tx = (
TokenCreateTransaction()
.set_token_name("Fungible Token")
.set_token_symbol("FTK")
.set_initial_supply(1)
.set_treasury_account_id(operator_id)
)
fungible_receipt = fungible_tx.freeze_with(client).sign(operator_key).execute(client)
fungible_token_id = fungible_receipt.token_id

print(f"✅ Success! Created NFT token: {nft_token_id} and fungible token: {fungible_token_id}")
return client, nft_token_id, fungible_token_id, recipient_id, recipient_key

except (ValueError, RuntimeError) as e:
print(f"❌ Error creating tokens: {e}")
sys.exit(1)

# 4. Associate the tokens with the new account
# =================================================================
print(f"\nSTEP 3: Associating tokens with account {recipient_id}...")
def token_associate(client, nft_token_id, fungible_token_id, recipient_id, recipient_key):
"""
Associate the tokens with the new account.

Note: Tokens must be associated with an account before they can be used or dissociated.
Association is a prerequisite for holding, transferring, or later dissociating tokens.
"""

print(f"\nSTEP 3: Associating NFT and fungible tokens with account {recipient_id}...")
print("Note: Tokens must be associated with an account before they can be used or dissociated.")
try:
receipt = (
TokenAssociateTransaction()
.set_account_id(recipient_id)
.add_token_id(token_id_1)
.add_token_id(token_id_2)
.add_token_id(nft_token_id)
.add_token_id(fungible_token_id)
.freeze_with(client)
.sign(recipient_key) # Recipient must sign to approve
.execute(client)
)
print(f"✅ Success! Token association complete. Status: {receipt.status}")
except Exception as e:
return client, nft_token_id, fungible_token_id, recipient_id, recipient_key
except (ValueError, RuntimeError) as e:
print(f"❌ Error associating tokens: {e}")
sys.exit(1)

# 5. Dissociate the tokens from the new account
# =================================================================
print(f"\nSTEP 4: Dissociating tokens from account {recipient_id}...")
def verify_dissociation(client, nft_token_id, fungible_token_id, recipient_id):
"""Verify that the specified tokens are dissociated from the account."""
print("\nVerifying token dissociation...")
info = AccountInfoQuery().set_account_id(recipient_id).execute(client)
associated_tokens = [rel.token_id for rel in getattr(info, 'token_relationships', [])]
if nft_token_id not in associated_tokens and fungible_token_id not in associated_tokens:
print("✅ Verified: Both tokens are dissociated from the account.")
else:
print("❌ Verification failed: Some tokens are still associated.")

def token_dissociate(client, nft_token_id, fungible_token_id, recipient_id, recipient_key):
"""
Dissociate the tokens from the new account.

Why dissociate?
- To remove unwanted tokens from your account
- To reduce account costs (some tokens may incur fees)
- For security or privacy reasons
- To comply with business or regulatory requirements
"""

print(f"\nSTEP 4: Dissociating NFT and fungible tokens from account {recipient_id}...")
try:
receipt = (
TokenDissociateTransaction()
.set_account_id(recipient_id)
.add_token_id(token_id_1)
.add_token_id(token_id_2)
.add_token_id(nft_token_id)
.add_token_id(fungible_token_id)
.freeze_with(client)
.sign(recipient_key) # Recipient must sign to approve
.execute(client)
)
print(f"✅ Success! Token dissociation complete.")
except Exception as e:
print(f"✅ Success! Token dissociation complete for both NFT and fungible tokens, Status: {ResponseCode(receipt.status).name}")

except (ValueError, RuntimeError) as e:
print(f"❌ Error dissociating tokens: {e}")
sys.exit(1)

def main():
"""
1-create new account
2-create two tokens
3-associate the tokens with the new account
4-dissociate the tokens from the new account
5-verify dissociation
"""
client, operator_id, operator_key = setup_client()
client, operator_key, recipient_id, recipient_key, operator_id =create_new_account(client, operator_id, operator_key)
client, nft_token_id, fungible_token_id, recipient_id, recipient_key = create_token(client, operator_key, recipient_id, recipient_key, operator_id)
token_associate(client, nft_token_id, fungible_token_id, recipient_id, recipient_key)
token_dissociate(client, nft_token_id, fungible_token_id, recipient_id, recipient_key)
# Optional: Verify dissociation
verify_dissociation(client, nft_token_id, fungible_token_id, recipient_id)


if __name__ == "__main__":
token_dissociate()
main()
Loading