Skip to content

Commit

Permalink
TokenFreezeTransaction
Browse files Browse the repository at this point in the history
rough notes token freeze

local tests passed

test tuple

freeze account ids

stub naming

freezing from recipient
  • Loading branch information
exploreriii committed Jan 12, 2025
1 parent d2f2ce3 commit d70dc55
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 8 deletions.
32 changes: 31 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, Transfer, Freeze & Delete
- Manage Consensus Transactions like Topic Create, Update, Delete
- Submit Topic Messages
- Query Account Balance, Transaction Receipts, Topic Infos and Messages
Expand All @@ -19,6 +19,7 @@ This is a Python SDK for interacting with the Hedera Hashgraph platform. It allo
- [Associating a Token](#associating-a-token)
- [Transferring Tokens](#transferring-tokens)
- [Deleting a Token](#deleting-a-token)
- [Freezing a Token](#freezing-a-token)
- [Transferring HBAR](#transferring-hbar)
- [Creating a Topic](#creating-a-topic)
- [Submitting a Topic Message](#submitting-a-topic-message)
Expand Down Expand Up @@ -77,9 +78,11 @@ 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...
FREEZE_KEY=302a300306072b65700321009308ecfdf...
RECIPIENT_ID=0.0.789xx
TOKEN_ID=0.0.100xx
TOPIC_ID=0.0.200xx
FREEZE_ACCOUNT_ID=0.0.100
NETWORK=testnet
```

Expand Down Expand Up @@ -108,6 +111,7 @@ New Account Public Key: 8f444e36e8926def492adxxx...
Token creation successful. Token ID: 0.0.5025xxx
Token association successful.
Token transfer successful.
Token freeze successful.
Token deletion successful.
Topic creation successful.
Topic Message submitted.
Expand Down Expand Up @@ -284,6 +288,32 @@ transaction.execute(client)
transaction.execute(client)
```

### Freezing a Token

#### Pythonic Syntax:
```
transaction = TokenFreezeTransaction(
token_id=token_id
account_id=account_id
).freeze_with(client)
transaction.sign(freeze_key) # Freeze key must have been set during token creation.
transaction.execute(client)
```
#### Method Chaining:
```
transaction = (
TokenFreezeTransaction()
.set_token_id(token_id)
.set_account_id(account_id)
.freeze_with(client)
)
transaction.sign(freeze_key) # Freeze key must also have been set in Token Create
transaction.execute(client)
```


### Transferring HBAR

#### Pythonic Syntax:
Expand Down
5 changes: 4 additions & 1 deletion examples/token_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,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'))

freeze_key = PrivateKey.from_string(os.getenv('FREEZE_KEY'))

client.set_operator(operator_id, operator_key)

transaction = (
Expand All @@ -29,9 +30,11 @@ def create_token():
.set_initial_supply(10)
.set_treasury_account_id(operator_id)
.set_admin_key(admin_key)
.set_freeze_key(freeze_key)
.freeze_with(client)
.sign(operator_key)
.sign(admin_key)

)

try:
Expand Down
46 changes: 46 additions & 0 deletions examples/token_freeze.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import sys
from dotenv import load_dotenv

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 TokenFreezeTransaction
from hedera_sdk_python.client.network import Network
from hedera_sdk_python.tokens.token_id import TokenId

load_dotenv()

def freeze_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'))
freeze_key = PrivateKey.from_string(os.getenv('FREEZE_KEY'))
token_id = TokenId.from_string(os.getenv('TOKEN_ID'))
account_id = AccountId.from_string(os.getenv('FREEZE_ACCOUNT_ID'))

client.set_operator(operator_id, operator_key)

transaction = (
TokenFreezeTransaction()
.set_token_id(token_id)
.set_account(account_id)
.freeze_with(client)
.sign(freeze_key)
)

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

if __name__ == "__main__":
freeze_token()
19 changes: 17 additions & 2 deletions src/hedera_sdk_python/tokens/token_create_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class TokenCreateTransaction(Transaction):
"""

def __init__(self, token_name=None, token_symbol=None, decimals=None, initial_supply=None,
treasury_account_id=None, admin_key=None):
treasury_account_id=None, admin_key=None, freeze_key=None):
"""
Initializes a new TokenCreateTransaction instance with optional keyword arguments.
Expand All @@ -26,6 +26,7 @@ def __init__(self, token_name=None, token_symbol=None, decimals=None, initial_su
initial_supply (int, optional): The initial supply of the token.
treasury_account_id (AccountId, optional): The treasury account ID.
admin_key (PrivateKey, optional): The admin key for the token.
freeze_key (PrivateKey, optional): The freeze key for the token.
"""
super().__init__()
self.token_name = token_name
Expand All @@ -34,6 +35,7 @@ def __init__(self, token_name=None, token_symbol=None, decimals=None, initial_su
self.initial_supply = initial_supply
self.treasury_account_id = treasury_account_id
self.admin_key = admin_key
self.freeze_key = freeze_key

self._default_transaction_fee = 3_000_000_000

Expand Down Expand Up @@ -67,6 +69,12 @@ def set_admin_key(self, admin_key):
self.admin_key = admin_key
return self

def set_freeze_key(self, freeze_key):
self._require_not_frozen()
self.freeze_key = freeze_key
return self


def build_transaction_body(self):
"""
Builds and returns the protobuf transaction body for token creation.
Expand All @@ -91,13 +99,20 @@ def build_transaction_body(self):
admin_public_key_bytes = self.admin_key.public_key().to_bytes_raw()
admin_key_proto = basic_types_pb2.Key(ed25519=admin_public_key_bytes)

freeze_key_proto = None
if self.freeze_key:
freeze_public_key_bytes = self.freeze_key.public_key().to_bytes_raw()
freeze_key_proto = basic_types_pb2.Key(ed25519=freeze_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(),
adminKey=admin_key_proto
adminKey=admin_key_proto,
freezeKey=freeze_key_proto

)

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

class TokenFreezeTransaction(Transaction):
"""
Represents a token freeze transaction on the Hedera network.
This transaction freezes a specified token for a given account.
Inherits from the base Transaction class and implements the required methods
to build and execute a token freeze transaction.
"""

def __init__(self, token_id=None, account_id=None):
"""
Initializes a new TokenDeleteTransaction instance with optional token_id.
Args:
token_id (TokenId, optional): The ID of the token to be frozen.
account_id (AccountId, optional): The ID of the account to have their token frozen.
"""
super().__init__()
self.token_id = token_id
self.account_id = account_id
self._default_transaction_fee = 3_000_000_000

def set_token_id(self, token_id):
"""
Sets the ID of the token to be frozen.
Args:
token_id (TokenId): The ID of the token to be frozen.
Returns:
TokenFreezeTransaction: Returns self for method chaining.
"""
self._require_not_frozen()
self.token_id = token_id
return self

def set_account_id(self, account_id):
"""
Sets the ID of the account to be frozen.
Args:
account_id (AccountId): The ID of the account to have their token frozen.
Returns:
TokenFreezeTransaction: Returns self for method chaining.
"""
self._require_not_frozen()
self.account_id = account_id
return self

def build_transaction_body(self):
"""
Builds and returns the protobuf transaction body for token freeze.
Returns:
TransactionBody: The protobuf transaction body containing the token freeze details.
Raises:
ValueError: If the token ID is missing.
ValueError: If the account ID is missing.
"""

if not self.token_id:
raise ValueError("Missing required TokenID.")

if not self.account_id:
raise ValueError("Missing required AccountID.")

token_freeze_body = token_freeze_account_pb2.TokenFreezeAccountTransactionBody(
token=self.token_id.to_proto(),
account=self.account_id.to_proto()
)

transaction_body = self.build_base_transaction_body()
transaction_body.tokenFreeze.CopyFrom(token_freeze_body)

return transaction_body

def _execute_transaction(self, client, transaction_proto):
"""
Executes the token freeze 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.freezeTokenAccount(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
27 changes: 24 additions & 3 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from hedera_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
from hedera_sdk_python.transaction.transfer_transaction import TransferTransaction
from hedera_sdk_python.tokens.token_delete_transaction import TokenDeleteTransaction
from hedera_sdk_python.tokens.token_freeze_transaction import TokenFreezeTransaction
from hedera_sdk_python.response_code import ResponseCode
from hedera_sdk_python.consensus.topic_create_transaction import TopicCreateTransaction
from hedera_sdk_python.consensus.topic_message_submit_transaction import TopicMessageSubmitTransaction
Expand Down Expand Up @@ -63,14 +64,15 @@ def query_balance(client, account_id):
print(f"Account {account_id} balance: {balance.hbars}")
return balance

def create_token(client, operator_id, admin_key):
def create_token(client, operator_id, admin_key, freeze_key):
transaction = TokenCreateTransaction(
token_name="ExampleToken",
token_symbol="EXT",
decimals=2,
initial_supply=1000,
treasury_account_id=operator_id,
admin_key=admin_key
admin_key=admin_key,
freeze_key=freeze_key
)
transaction.freeze_with(client)
transaction.sign(client.operator_private_key)
Expand Down Expand Up @@ -146,6 +148,22 @@ def delete_token(client, token_id, admin_key):
print(f"Token deletion failed: {str(e)}")
sys.exit(1)

def freeze_token(client, token_id, account_id, freeze_key):
transaction = TokenFreezeTransaction(token_id=token_id, account_id=account_id)
transaction.freeze_with(client)
transaction.sign(client.operator_private_key)
transaction.sign(freeze_key)

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

def create_topic(client):
key = client.operator_private_key
transaction = TopicCreateTransaction(
Expand Down Expand Up @@ -239,6 +257,7 @@ def query_topic_info(client, topic_id):
def main():
operator_id, operator_key = load_operator_credentials()
admin_key = PrivateKey.generate()
freeze_key = PrivateKey.generate()

network_type = os.getenv('NETWORK')
network = Network(network=network_type)
Expand All @@ -248,9 +267,11 @@ 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)
token_id = create_token(client, operator_id, admin_key, freeze_key)

associate_token(client, recipient_id, recipient_private_key, token_id)
transfer_token(client, recipient_id, token_id)
freeze_token(client, token_id, recipient_id, freeze_key)
delete_token(client, token_id, admin_key)

topic_id = create_topic(client)
Expand Down
Loading

0 comments on commit d70dc55

Please sign in to comment.