1+ import os
2+ import sys
3+
4+ from dotenv import load_dotenv
5+
6+ from hiero_sdk_python import (
7+ AccountId ,
8+ Client ,
9+ Network ,
10+ PrivateKey ,
11+ AccountCreateTransaction ,
12+ CryptoGetAccountBalanceQuery ,
13+ ResponseCode ,
14+ TokenCreateTransaction ,
15+ TokenFreezeTransaction ,
16+ TokenType ,
17+ TokenUnfreezeTransaction ,
18+ BatchTransaction ,
19+ TransferTransaction
20+ )
21+
22+ load_dotenv ()
23+
24+ def get_balance (client , account_id , token_id ):
25+ tokens_balance = (
26+ CryptoGetAccountBalanceQuery (account_id = account_id )
27+ .execute (client )
28+ .token_balances
29+ )
30+
31+ print (f"Account: { account_id } : { tokens_balance [token_id ] if tokens_balance else 0 } " )
32+
33+ def setup_client ():
34+ """
35+ Set up and configure a Hedera client for testnet operations.
36+ """
37+ network_name = os .getenv ('NETWORK' , 'testnet' ).lower ()
38+
39+ print (f"Connecting to Hedera { network_name } network!" )
40+
41+ try :
42+ network = Network (network_name )
43+ client = Client (network )
44+
45+ operator_id = AccountId .from_string (os .getenv ('OPERATOR_ID' ,'' ))
46+ operator_key = PrivateKey .from_string (os .getenv ('OPERATOR_KEY' ,'' ))
47+
48+ client .set_operator (operator_id , operator_key )
49+ print (f"Client initialized with operator: { operator_id } " )
50+ return client
51+ except Exception as e :
52+ print (f"Failed to set up client: { e } " )
53+ sys .exit (1 )
54+
55+ def create_account (client ):
56+ """
57+ Create a new recipient account.
58+ """
59+ print ("\n Creating new recipient account..." )
60+ try :
61+ key = PrivateKey .generate ()
62+ tx = (
63+ AccountCreateTransaction ()
64+ .set_key_without_alias (key .public_key ())
65+ .set_max_automatic_token_associations (2 ) # to transfer token without associating it
66+ .set_initial_balance (1 )
67+ )
68+
69+ receipt = tx .freeze_with (client ).execute (client )
70+ recipient_id = receipt .account_id
71+
72+ print (f"New account created: { receipt .account_id } " )
73+ return recipient_id
74+ except Exception as e :
75+ print (f"Error creating new account: { e } " )
76+ sys .exit (1 )
77+
78+ def create_fungible_token (client , freeze_key ):
79+ """
80+ Create a fungible token with freeze_key.
81+ """
82+ print ("\n Creating fungible token..." )
83+ try :
84+ tx = (
85+ TokenCreateTransaction ()
86+ .set_token_name ("FiniteFungibleToken" )
87+ .set_token_symbol ("FFT" )
88+ .set_initial_supply (2 )
89+ .set_treasury_account_id (client .operator_account_id )
90+ .set_token_type (TokenType .FUNGIBLE_COMMON )
91+ .set_freeze_key (freeze_key )
92+ .freeze_with (client )
93+ .sign (client .operator_private_key )
94+ .sign (freeze_key )
95+ )
96+ receipt = tx .execute (client )
97+ token_id = receipt .token_id
98+
99+ print (f"Token created: { receipt .token_id } " )
100+
101+ return token_id
102+ except Exception as e :
103+ print (f"Error creating token: { e } " )
104+ sys .exit (1 )
105+
106+ def freeze_token (client , account_id , token_id , freeze_key ):
107+ """
108+ Freeze token for an account.
109+ """
110+ print (f"\n Freezing token for account { account_id } " )
111+ try :
112+ tx = (
113+ TokenFreezeTransaction ()
114+ .set_account_id (account_id )
115+ .set_token_id (token_id )
116+ .freeze_with (client )
117+ .sign (freeze_key )
118+ )
119+
120+ receipt = tx .execute (client )
121+
122+ if receipt .status != ResponseCode .SUCCESS :
123+ print (f"Freeze failed: { ResponseCode (receipt .status ).name } )" )
124+ sys .exit (1 )
125+
126+ print ("Token freeze successful!" )
127+ except Exception as e :
128+ print (f"Error freezing token for account: { e } " )
129+ sys .exit (1 )
130+
131+ def transfer_token (client , sender , recipient , token_id ):
132+ """
133+ Perform a token trasfer transaction.
134+ """
135+ print (f"\n Transferring token { token_id } from { sender } → { recipient } " )
136+ try :
137+ tx = (
138+ TransferTransaction ()
139+ .add_token_transfer (token_id = token_id , account_id = sender , amount = - 1 )
140+ .add_token_transfer (token_id = token_id , account_id = recipient , amount = 1 )
141+ )
142+
143+ receipt = tx .execute (client )
144+
145+ return receipt
146+ except Exception as e :
147+ print (f"Error transfering token: { e } " )
148+ sys .exit (1 )
149+
150+ def perform_batch_tx (client , sender , recipient , token_id , freeze_key ):
151+ """
152+ Perform a batch transaction.
153+ """
154+ print ("\n Performing batch transaction (unfreeze → transfer → freeze)..." )
155+ batch_key = PrivateKey .generate ()
156+
157+ unfreeze_tx = (
158+ TokenUnfreezeTransaction ()
159+ .set_account_id (sender )
160+ .set_token_id (token_id )
161+ .batchify (client , batch_key )
162+ .sign (freeze_key )
163+ )
164+
165+ transfer_tx = (
166+ TransferTransaction ()
167+ .add_token_transfer (token_id , sender , - 1 )
168+ .add_token_transfer (token_id , recipient , 1 )
169+ .batchify (client , batch_key )
170+ )
171+
172+ freeze_tx = (
173+ TokenFreezeTransaction ()
174+ .set_account_id (sender )
175+ .set_token_id (token_id )
176+ .batchify (client , batch_key )
177+ .sign (freeze_key )
178+ )
179+
180+ # 50 is the maximum limit for internal transaction inside a BatchTransaction
181+ batch = (
182+ BatchTransaction ()
183+ .add_inner_transaction (unfreeze_tx )
184+ .add_inner_transaction (transfer_tx )
185+ .add_inner_transaction (freeze_tx )
186+ .freeze_with (client )
187+ .sign (batch_key )
188+ )
189+
190+ receipt = batch .execute (client )
191+ print (f"Batch transaction status: { ResponseCode (receipt .status ).name } " )
192+
193+ def main ():
194+ client = setup_client ()
195+ freeze_key = PrivateKey .generate ()
196+
197+ recipient_id = create_account (client )
198+ token_id = create_fungible_token (client , freeze_key )
199+
200+ # Freeze operator for token
201+ freeze_token (client , client .operator_account_id , token_id , freeze_key )
202+
203+ # Confirm transfer fails
204+ receipt = transfer_token (client , client .operator_account_id , recipient_id , token_id )
205+ if receipt .status == ResponseCode .ACCOUNT_FROZEN_FOR_TOKEN :
206+ print ("\n Correct: Account is frozen for token transfers." )
207+ else :
208+ print ("\n Expected freeze to block transfer!" )
209+ sys .exit (1 )
210+
211+ # Show balances
212+ print ("\n Balances before batch:" )
213+ get_balance (client , client .operator_account_id , token_id )
214+ get_balance (client , recipient_id , token_id )
215+
216+ # Batch unfreeze → transfer → freeze
217+ perform_batch_tx (client , client .operator_account_id , recipient_id , token_id , freeze_key )
218+
219+ print ("\n Balances after batch:" )
220+ get_balance (client , client .operator_account_id , token_id )
221+ get_balance (client , recipient_id ,token_id )
222+
223+ # Should fail again Verify that token is again freeze for account
224+ receipt = transfer_token (client , client .operator_account_id , recipient_id , token_id )
225+ if receipt .status == ResponseCode .ACCOUNT_FROZEN_FOR_TOKEN :
226+ print ("\n Correct: Account is frozen again" )
227+ else :
228+ print ("\n Account should be frozen again!" )
229+ sys .exit (1 )
230+
231+
232+ if __name__ == "__main__" :
233+ main ()
0 commit comments