Skip to content

Commit bca1a44

Browse files
committed
TokenDeleteTransaction
token_delete.py token_delete_transaction.py test_token_delete_transaction.py test.py account_id naming in unit test readme delete token typo successful hardcoding to env file token_id string token_id readme admin_key integration admin_key token_create_transaction admin_key signing example admin_key test.py creating token readme/example with admin key test enable solo key basic types bytes defining bytes in private key sign admin as is set typo admin_key generate distinct public key test call admin_public_key_bytes test admin_key signing admin_key private public user-friendly and docs transaction body layer Documentation admin key paths uv lock
1 parent d172e1a commit bca1a44

File tree

11 files changed

+399
-16
lines changed

11 files changed

+399
-16
lines changed

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ submitting messages.
1414
- [Creating a Token](#creating-a-token)
1515
- [Associating a Token](#associating-a-token)
1616
- [Transferring Tokens](#transferring-tokens)
17+
- [Deleting a Token](#deleting-a-token)
1718
- [Transferring HBAR](#transferring-hbar)
1819
- [Creating a Topic](#creating-a-topic)
1920
- [Contributing](#contributing)
@@ -66,6 +67,7 @@ Create a .env file in the root of your project with the following (replace with
6667
```
6768
OPERATOR_ID=0.0.1234xx
6869
OPERATOR_KEY=302e020100300506032b657004220420...
70+
ADMIN_KEY=302a300506032b65700321009308ecfdf...
6971
RECIPIENT_ID=0.0.789xx
7072
TOKEN_ID=0.0.100xx
7173
NETWORK=testnet
@@ -96,11 +98,12 @@ New Account Public Key: 8f444e36e8926def492adxxx...
9698
Token creation successful. Token ID: 0.0.5025xxx
9799
Token association successful.
98100
Token transfer successful.
101+
Token deletion successful.
99102
```
100103

101104
## Usage
102105

103-
Below are examples of how to use the SDK for creating tokens, associating them with accounts, and transferring tokens (also see 'examples' directiory)
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)
104107

105108
### Creating an Account
106109

@@ -127,9 +130,11 @@ transaction = (
127130
.set_decimals(2)
128131
.set_initial_supply(1000)
129132
.set_treasury_account_id(operator_id)
133+
.set_admin_key(admin_key) # Optional to create a token. Necessary for Token Delete or Update.
130134
.freeze_with(client)
131135
)
132136
137+
transaction.sign(admin_key) # If admin key exists.
133138
transaction.sign(operator_key)
134139
transaction.execute(client)
135140
```
@@ -162,6 +167,20 @@ transaction = (
162167
transaction.execute(client)
163168
```
164169

170+
### Deleting a Token
171+
172+
```
173+
transaction = (
174+
TokenDeleteTransaction()
175+
.set_token_id(token_id)
176+
.freeze_with(client)
177+
)
178+
179+
transaction.sign(admin_key) #Admin key must also have been set in Token Create
180+
transaction.sign(operator_key)
181+
transaction.execute(client)
182+
```
183+
165184
### Transfering a HBAR
166185

167186
```

examples/token_create.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from hedera_sdk_python.crypto.private_key import PrivateKey
1111
from hedera_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
1212
from hedera_sdk_python.client.network import Network
13+
from cryptography.hazmat.primitives import serialization
1314

1415
load_dotenv()
1516

@@ -19,7 +20,8 @@ def create_token():
1920

2021
operator_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
2122
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
22-
23+
admin_key = PrivateKey.from_string(os.getenv('ADMIN_KEY'))
24+
2325
client.set_operator(operator_id, operator_key)
2426

2527
transaction = (
@@ -29,10 +31,12 @@ def create_token():
2931
.set_decimals(2)
3032
.set_initial_supply(10)
3133
.set_treasury_account_id(operator_id)
34+
.set_admin_key(admin_key)
3235
.freeze_with(client)
3336
.sign(operator_key)
37+
.sign(admin_key)
3438
)
35-
39+
3640
try:
3741
receipt = transaction.execute(client)
3842
if receipt and receipt.tokenId:

examples/token_delete.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.tokens.token_delete_transaction import TokenDeleteTransaction
12+
from hedera_sdk_python.client.network import Network
13+
from hedera_sdk_python.tokens.token_id import TokenId
14+
15+
load_dotenv()
16+
17+
def delete_token():
18+
network = Network(network='testnet')
19+
client = Client(network)
20+
21+
operator_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
22+
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
23+
admin_key = PrivateKey.from_string(os.getenv('ADMIN_KEY'))
24+
token_id = TokenId.from_string(os.getenv('TOKEN_ID'))
25+
26+
client.set_operator(operator_id, operator_key)
27+
28+
transaction = (
29+
TokenDeleteTransaction()
30+
.set_token_id(token_id)
31+
.freeze_with(client)
32+
.sign(operator_key)
33+
.sign(admin_key)
34+
)
35+
36+
try:
37+
receipt = transaction.execute(client)
38+
if receipt is not None and receipt.status == 'SUCCESS':
39+
print(f"Token deletion successful")
40+
else:
41+
print(f"Token deletion failed.")
42+
sys.exit(1)
43+
except Exception as e:
44+
print(f"Token deletion failed: {str(e)}")
45+
sys.exit(1)
46+
47+
if __name__ == "__main__":
48+
delete_token()

src/hedera_sdk_python/crypto/private_key.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,18 @@ def to_string(self):
7676
encryption_algorithm=serialization.NoEncryption()
7777
)
7878
return private_bytes.hex()
79+
80+
81+
def to_bytes(self):
82+
"""
83+
Returns the private key as bytes.
84+
85+
Returns:
86+
bytes: The private key.
87+
"""
88+
private_bytes = self._private_key.private_bytes(
89+
encoding=serialization.Encoding.Raw,
90+
format=serialization.PrivateFormat.Raw,
91+
encryption_algorithm=serialization.NoEncryption()
92+
)
93+
return private_bytes

src/hedera_sdk_python/tokens/token_create_transaction.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from hedera_sdk_python.transaction.transaction import Transaction
2-
from hedera_sdk_python.hapi import token_create_pb2
2+
from hedera_sdk_python.hapi import token_create_pb2, basic_types_pb2
33
from hedera_sdk_python.response_code import ResponseCode
4+
from cryptography.hazmat.primitives import serialization
45

56
class TokenCreateTransaction(Transaction):
67
"""
@@ -23,6 +24,7 @@ def __init__(self):
2324
self.decimals = None
2425
self.initial_supply = None
2526
self.treasury_account_id = None
27+
self.admin_key = None
2628

2729
self._default_transaction_fee = 3_000_000_000
2830

@@ -50,7 +52,12 @@ def set_treasury_account_id(self, account_id):
5052
self._require_not_frozen()
5153
self.treasury_account_id = account_id
5254
return self
53-
55+
56+
def set_admin_key(self, admin_key):
57+
self._require_not_frozen()
58+
self.admin_key = admin_key
59+
return self
60+
5461
def build_transaction_body(self):
5562
"""
5663
Builds and returns the protobuf transaction body for token creation.
@@ -70,12 +77,21 @@ def build_transaction_body(self):
7077
]):
7178
raise ValueError("Missing required fields")
7279

80+
admin_key_proto = None
81+
if self.admin_key:
82+
admin_public_key_bytes = self.admin_key.public_key().public_bytes(
83+
encoding=serialization.Encoding.Raw,
84+
format=serialization.PublicFormat.Raw
85+
)
86+
admin_key_proto = basic_types_pb2.Key(ed25519=admin_public_key_bytes)
87+
7388
token_create_body = token_create_pb2.TokenCreateTransactionBody(
7489
name=self.token_name,
7590
symbol=self.token_symbol,
7691
decimals=self.decimals,
7792
initialSupply=self.initial_supply,
78-
treasury=self.treasury_account_id.to_proto()
93+
treasury=self.treasury_account_id.to_proto(),
94+
adminKey=admin_key_proto
7995
)
8096

8197
transaction_body = self.build_base_transaction_body()
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from hedera_sdk_python.transaction.transaction import Transaction
2+
from hedera_sdk_python.hapi import token_delete_pb2
3+
from hedera_sdk_python.response_code import ResponseCode
4+
5+
class TokenDeleteTransaction(Transaction):
6+
"""
7+
Represents a token deletion transaction on the Hedera network.
8+
9+
This transaction deletes a specified token, rendering it inactive.
10+
11+
Inherits from the base Transaction class and implements the required methods
12+
to build and execute a token deletion transaction.
13+
14+
"""
15+
16+
def __init__(self):
17+
"""
18+
Initializes a new TokenDeleteTransaction instance with default values.
19+
"""
20+
super().__init__()
21+
self.token_id = None
22+
self._default_transaction_fee = 3_000_000_000
23+
24+
def set_token_id(self, token_id):
25+
self._require_not_frozen()
26+
self.token_id = token_id
27+
return self
28+
29+
def build_transaction_body(self):
30+
"""
31+
Builds and returns the protobuf transaction body for token deletion.
32+
33+
Returns:
34+
TransactionBody: The protobuf transaction body containing the token deletion details.
35+
36+
Raises:
37+
ValueError: If the token ID is missing.
38+
"""
39+
if not self.token_id:
40+
raise ValueError("Missing required TokenID.")
41+
42+
token_delete_body = token_delete_pb2.TokenDeleteTransactionBody(
43+
token=self.token_id.to_proto()
44+
)
45+
46+
transaction_body = self.build_base_transaction_body()
47+
transaction_body.tokenDeletion.CopyFrom(token_delete_body)
48+
49+
return transaction_body
50+
51+
def _execute_transaction(self, client, transaction_proto):
52+
"""
53+
Executes the token deletion transaction using the provided client.
54+
55+
Args:
56+
client (Client): The client instance to use for execution.
57+
transaction_proto (Transaction): The protobuf Transaction message.
58+
59+
Returns:
60+
TransactionReceipt: The receipt from the network after transaction execution.
61+
62+
Raises:
63+
Exception: If the transaction submission fails or receives an error response.
64+
"""
65+
response = client.token_stub.deleteToken(transaction_proto)
66+
67+
if response.nodeTransactionPrecheckCode != ResponseCode.OK:
68+
error_code = response.nodeTransactionPrecheckCode
69+
error_message = ResponseCode.get_name(error_code)
70+
raise Exception(f"Error during transaction submission: {error_code} ({error_message})")
71+
72+
receipt = self.get_receipt(client)
73+
return receipt
74+
75+
def get_receipt(self, client, timeout=60):
76+
"""
77+
Retrieves the receipt for the transaction.
78+
79+
Args:
80+
client (Client): The client instance.
81+
timeout (int): Maximum time in seconds to wait for the receipt.
82+
83+
Returns:
84+
TransactionReceipt: The transaction receipt from the network.
85+
86+
Raises:
87+
Exception: If the transaction ID is not set or if receipt retrieval fails.
88+
"""
89+
if self.transaction_id is None:
90+
raise Exception("Transaction ID is not set.")
91+
92+
receipt = client.get_transaction_receipt(self.transaction_id, timeout)
93+
return receipt

test.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
from hedera_sdk_python.account.account_id import AccountId
77
from hedera_sdk_python.account.account_create_transaction import AccountCreateTransaction
88
from hedera_sdk_python.crypto.private_key import PrivateKey
9+
from hedera_sdk_python.crypto.public_key import PublicKey
910
from hedera_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
1011
from hedera_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
1112
from hedera_sdk_python.transaction.transfer_transaction import TransferTransaction
1213
from hedera_sdk_python.response_code import ResponseCode
1314
from hedera_sdk_python.consensus.topic_create_transaction import TopicCreateTransaction
15+
from hedera_sdk_python.token_delete_transaction import TokenDeleteTransaction
16+
from cryptography.hazmat.primitives import serialization
1417

1518
load_dotenv()
1619

@@ -55,7 +58,7 @@ def create_new_account(client, initial_balance=100000000):
5558

5659
return new_account_id, new_account_private_key
5760

58-
def create_token(client, operator_id):
61+
def create_token(client, operator_id, admin_key):
5962
"""Create a new token and return its TokenId instance."""
6063

6164
transaction = (
@@ -65,10 +68,11 @@ def create_token(client, operator_id):
6568
.set_decimals(2)
6669
.set_initial_supply(1000)
6770
.set_treasury_account_id(operator_id)
71+
.set_admin_key(admin_key)
6872
.freeze_with(client)
6973
)
70-
7174
transaction.sign(client.operator_private_key)
75+
7276

7377
try:
7478
receipt = transaction.execute(client)
@@ -151,8 +155,35 @@ def create_topic(client):
151155
return topic_id
152156

153157

158+
def delete_token(client, token_id, admin_key):
159+
"""Deletes the specified token on the Hedera network."""
160+
transaction = (
161+
TokenDeleteTransaction()
162+
.set_token_id(token_id)
163+
.freeze_with(client)
164+
)
165+
166+
transaction.sign(client.operator_private_key)
167+
transaction.sign(admin_key)
168+
169+
try:
170+
receipt = transaction.execute(client)
171+
if receipt.status != ResponseCode.SUCCESS:
172+
status_message = ResponseCode.get_name(receipt.status)
173+
raise Exception(f"Token deletion failed with status: {status_message}")
174+
print("Token deletion successful.")
175+
except Exception as e:
176+
print(f"Token deletion failed: {str(e)}")
177+
sys.exit(1)
178+
154179
def main():
155180
operator_id, operator_key = load_operator_credentials()
181+
admin_key = PrivateKey.generate()
182+
admin_public_key = admin_key.public_key()
183+
admin_public_key_bytes = admin_public_key.public_bytes(
184+
encoding=serialization.Encoding.Raw,
185+
format=serialization.PublicFormat.Raw
186+
)
156187

157188
network_type = os.getenv('NETWORK')
158189
network = Network(network=network_type)
@@ -161,9 +192,10 @@ def main():
161192
client.set_operator(operator_id, operator_key)
162193

163194
recipient_id, recipient_private_key = create_new_account(client)
164-
token_id = create_token(client, operator_id)
195+
token_id = create_token(client, operator_id, admin_key)
165196
associate_token(client, recipient_id, recipient_private_key, token_id)
166197
transfer_token(client, recipient_id, token_id)
198+
delete_token(client, token_id, admin_key)
167199

168200
topic_id = create_topic(client)
169201

0 commit comments

Comments
 (0)