Skip to content

Commit

Permalink
TokenDeleteTransaction
Browse files Browse the repository at this point in the history
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

path

admin key sign

client
  • Loading branch information
exploreriii committed Dec 30, 2024
1 parent d172e1a commit 150dd26
Show file tree
Hide file tree
Showing 11 changed files with 400 additions and 16 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ submitting messages.
- [Creating a Token](#creating-a-token)
- [Associating a Token](#associating-a-token)
- [Transferring Tokens](#transferring-tokens)
- [Deleting a Token](#deleting-a-token)
- [Transferring HBAR](#transferring-hbar)
- [Creating a Topic](#creating-a-topic)
- [Contributing](#contributing)
Expand Down Expand Up @@ -66,6 +67,7 @@ Create a .env file in the root of your project with the following (replace with
```
OPERATOR_ID=0.0.1234xx
OPERATOR_KEY=302e020100300506032b657004220420...
ADMIN_KEY=302a300506032b65700321009308ecfdf...
RECIPIENT_ID=0.0.789xx
TOKEN_ID=0.0.100xx
NETWORK=testnet
Expand Down Expand Up @@ -96,11 +98,12 @@ New Account Public Key: 8f444e36e8926def492adxxx...
Token creation successful. Token ID: 0.0.5025xxx
Token association successful.
Token transfer successful.
Token deletion successful.
```

## Usage

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

### Creating an Account

Expand All @@ -127,9 +130,11 @@ transaction = (
.set_decimals(2)
.set_initial_supply(1000)
.set_treasury_account_id(operator_id)
.set_admin_key(admin_key) # Optional to create a token. Necessary for Token Delete or Update.
.freeze_with(client)
)
transaction.sign(admin_key) # If admin key exists.
transaction.sign(operator_key)
transaction.execute(client)
```
Expand Down Expand Up @@ -162,6 +167,20 @@ transaction = (
transaction.execute(client)
```

### Deleting a Token

```
transaction = (
TokenDeleteTransaction()
.set_token_id(token_id)
.freeze_with(client)
)
transaction.sign(admin_key) #Admin key must also have been set in Token Create
transaction.sign(operator_key)
transaction.execute(client)
```

### Transfering a HBAR

```
Expand Down
8 changes: 6 additions & 2 deletions examples/token_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from hedera_sdk_python.crypto.private_key import PrivateKey
from hedera_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
from hedera_sdk_python.client.network import Network
from cryptography.hazmat.primitives import serialization

load_dotenv()

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

operator_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))

admin_key = PrivateKey.from_string(os.getenv('ADMIN_KEY'))

client.set_operator(operator_id, operator_key)

transaction = (
Expand All @@ -29,10 +31,12 @@ def create_token():
.set_decimals(2)
.set_initial_supply(10)
.set_treasury_account_id(operator_id)
.set_admin_key(admin_key)
.freeze_with(client)
.sign(operator_key)
.sign(admin_key)
)

try:
receipt = transaction.execute(client)
if receipt and receipt.tokenId:
Expand Down
48 changes: 48 additions & 0 deletions examples/token_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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.tokens.token_delete_transaction import TokenDeleteTransaction
from hedera_sdk_python.client.network import Network
from hedera_sdk_python.tokens.token_id import TokenId

load_dotenv()

def delete_token():
network = Network(network='testnet')
client = Client(network)

operator_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
admin_key = PrivateKey.from_string(os.getenv('ADMIN_KEY'))
token_id = TokenId.from_string(os.getenv('TOKEN_ID'))

client.set_operator(operator_id, operator_key)

transaction = (
TokenDeleteTransaction()
.set_token_id(token_id)
.freeze_with(client)
.sign(operator_key)
.sign(admin_key)
)

try:
receipt = transaction.execute(client)
if receipt is not None and receipt.status == 'SUCCESS':
print(f"Token deletion successful")
else:
print(f"Token deletion failed.")
sys.exit(1)
except Exception as e:
print(f"Token deletion failed: {str(e)}")
sys.exit(1)

if __name__ == "__main__":
delete_token()
15 changes: 15 additions & 0 deletions src/hedera_sdk_python/crypto/private_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,18 @@ def to_string(self):
encryption_algorithm=serialization.NoEncryption()
)
return private_bytes.hex()


def to_bytes(self):
"""
Returns the private key as bytes.
Returns:
bytes: The private key.
"""
private_bytes = self._private_key.private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption()
)
return private_bytes
22 changes: 19 additions & 3 deletions src/hedera_sdk_python/tokens/token_create_transaction.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from hedera_sdk_python.transaction.transaction import Transaction
from hedera_sdk_python.hapi import token_create_pb2
from hedera_sdk_python.hapi import token_create_pb2, basic_types_pb2
from hedera_sdk_python.response_code import ResponseCode
from cryptography.hazmat.primitives import serialization

class TokenCreateTransaction(Transaction):
"""
Expand All @@ -23,6 +24,7 @@ def __init__(self):
self.decimals = None
self.initial_supply = None
self.treasury_account_id = None
self.admin_key = None

self._default_transaction_fee = 3_000_000_000

Expand Down Expand Up @@ -50,7 +52,12 @@ def set_treasury_account_id(self, account_id):
self._require_not_frozen()
self.treasury_account_id = account_id
return self


def set_admin_key(self, admin_key):
self._require_not_frozen()
self.admin_key = admin_key
return self

def build_transaction_body(self):
"""
Builds and returns the protobuf transaction body for token creation.
Expand All @@ -70,12 +77,21 @@ def build_transaction_body(self):
]):
raise ValueError("Missing required fields")

admin_key_proto = None
if self.admin_key:
admin_public_key_bytes = self.admin_key.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
admin_key_proto = basic_types_pb2.Key(ed25519=admin_public_key_bytes)

token_create_body = token_create_pb2.TokenCreateTransactionBody(
name=self.token_name,
symbol=self.token_symbol,
decimals=self.decimals,
initialSupply=self.initial_supply,
treasury=self.treasury_account_id.to_proto()
treasury=self.treasury_account_id.to_proto(),
adminKey=admin_key_proto
)

transaction_body = self.build_base_transaction_body()
Expand Down
93 changes: 93 additions & 0 deletions src/hedera_sdk_python/tokens/token_delete_transaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from hedera_sdk_python.transaction.transaction import Transaction
from hedera_sdk_python.hapi import token_delete_pb2
from hedera_sdk_python.response_code import ResponseCode

class TokenDeleteTransaction(Transaction):
"""
Represents a token deletion transaction on the Hedera network.
This transaction deletes a specified token, rendering it inactive.
Inherits from the base Transaction class and implements the required methods
to build and execute a token deletion transaction.
"""

def __init__(self):
"""
Initializes a new TokenDeleteTransaction instance with default values.
"""
super().__init__()
self.token_id = None
self._default_transaction_fee = 3_000_000_000

def set_token_id(self, token_id):
self._require_not_frozen()
self.token_id = token_id
return self

def build_transaction_body(self):
"""
Builds and returns the protobuf transaction body for token deletion.
Returns:
TransactionBody: The protobuf transaction body containing the token deletion details.
Raises:
ValueError: If the token ID is missing.
"""
if not self.token_id:
raise ValueError("Missing required TokenID.")

token_delete_body = token_delete_pb2.TokenDeleteTransactionBody(
token=self.token_id.to_proto()
)

transaction_body = self.build_base_transaction_body()
transaction_body.tokenDeletion.CopyFrom(token_delete_body)

return transaction_body

def _execute_transaction(self, client, transaction_proto):
"""
Executes the token deletion 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.deleteToken(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
39 changes: 36 additions & 3 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.account.account_create_transaction import AccountCreateTransaction
from hedera_sdk_python.crypto.private_key import PrivateKey
from hedera_sdk_python.crypto.public_key import PublicKey
from hedera_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
from hedera_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
from hedera_sdk_python.transaction.transfer_transaction import TransferTransaction
from hedera_sdk_python.response_code import ResponseCode
from hedera_sdk_python.consensus.topic_create_transaction import TopicCreateTransaction
from hedera_sdk_python.tokens.token_delete_transaction import TokenDeleteTransaction
from cryptography.hazmat.primitives import serialization

load_dotenv()

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

return new_account_id, new_account_private_key

def create_token(client, operator_id):
def create_token(client, operator_id, admin_key):
"""Create a new token and return its TokenId instance."""

transaction = (
Expand All @@ -65,10 +68,12 @@ def create_token(client, operator_id):
.set_decimals(2)
.set_initial_supply(1000)
.set_treasury_account_id(operator_id)
.set_admin_key(admin_key)
.freeze_with(client)
)

transaction.sign(client.operator_private_key)
transaction.sign(admin_key)


try:
receipt = transaction.execute(client)
Expand Down Expand Up @@ -151,8 +156,35 @@ def create_topic(client):
return topic_id


def delete_token(client, token_id, admin_key):
"""Deletes the specified token on the Hedera network."""
transaction = (
TokenDeleteTransaction()
.set_token_id(token_id)
.freeze_with(client)
)

transaction.sign(client.operator_private_key)
transaction.sign(admin_key)

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

def main():
operator_id, operator_key = load_operator_credentials()
admin_key = PrivateKey.generate()
admin_public_key = admin_key.public_key()
admin_public_key_bytes = admin_public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)

network_type = os.getenv('NETWORK')
network = Network(network=network_type)
Expand All @@ -161,9 +193,10 @@ def main():
client.set_operator(operator_id, operator_key)

recipient_id, recipient_private_key = create_new_account(client)
token_id = create_token(client, operator_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)

topic_id = create_topic(client)

Expand Down
Loading

0 comments on commit 150dd26

Please sign in to comment.