1+ """
2+ This example demonstrates the Wipe Key privileges for token management using Hiero SDK Python.
3+
4+ It shows:
5+ 1. Creating a FUNGIBLE token WITHOUT a wipe key.
6+ 2. Attempting to wipe tokens (which fails because no wipe key exists).
7+ 3. Creating a FUNGIBLE token WITH a wipe key.
8+ 4. Associating and transferring tokens to a user account.
9+ 5. Wiping tokens from that user's account using the wipe key.
10+ 6. Verifying the total supply has decreased (effectively burning).
11+ 7. Creating an NFT (Non-Fungible) token with a wipe key.
12+ 8. Wiping a specific NFT Serial Number from a user account.
13+
14+ Required environment variables:
15+ - OPERATOR_ID, OPERATOR_KEY
16+
17+ Usage:
18+ uv run examples/token_create_transaction_wipe_key.py
19+ """
20+
21+ import os
22+ import sys
23+ from dotenv import load_dotenv
24+
25+ from hiero_sdk_python import (
26+ Client ,
27+ AccountId ,
28+ PrivateKey ,
29+ Network ,
30+ TokenCreateTransaction ,
31+ TokenWipeTransaction ,
32+ TokenAssociateTransaction ,
33+ TransferTransaction ,
34+ AccountCreateTransaction ,
35+ TokenInfoQuery ,
36+ TokenMintTransaction ,
37+ Hbar ,
38+ NftId # <--- FIX 1: Added NftId import
39+ )
40+
41+ from hiero_sdk_python .response_code import ResponseCode
42+ from hiero_sdk_python .tokens .token_type import TokenType
43+ from hiero_sdk_python .tokens .supply_type import SupplyType
44+
45+ load_dotenv ()
46+ network_name = os .getenv ('NETWORK' , 'testnet' ).lower ()
47+
48+ def setup_client ():
49+ """
50+ Initialise and return a Hiero SDK client based on environment variables.
51+ """
52+ network = Network (network_name )
53+ print (f"Connecting to Hedera { network_name } network" )
54+ client = Client (network )
55+
56+ try :
57+ operator_id = AccountId .from_string (os .getenv ('OPERATOR_ID' , '' ))
58+ operator_key = PrivateKey .from_string (os .getenv ('OPERATOR_KEY' , '' ))
59+ client .set_operator (operator_id , operator_key )
60+ print (f"Client set-up with operator id { client .operator_account_id } ." )
61+ return client , operator_id , operator_key
62+
63+ except (TypeError , ValueError ):
64+ print ("Error: please check OPERATOR_ID and OPERATOR_KEY in you environment file." )
65+ sys .exit (1 )
66+
67+ def create_recipient_account (client ):
68+ """
69+ Helper: Create a new account to hold tokens(wiped ones)
70+ """
71+ private_key = PrivateKey .generate_ed25519 ()
72+ tx = (
73+ AccountCreateTransaction ()
74+ .set_key (private_key .public_key ())
75+ .set_initial_balance (Hbar (2 ))
76+ )
77+ receipt = tx .execute (client )
78+ if receipt .status != ResponseCode .SUCCESS :
79+ print (f"❌ Account creation failed with status: { ResponseCode (receipt .status ).name } " )
80+ sys .exit (1 )
81+
82+ print (f"✅ Account created: { receipt .account_id } " )
83+ return receipt .account_id , private_key
84+
85+ def associate_and_transfer (client , token_id , recipient_id , recipient_key , amount ):
86+ """Helper: Associate token to recipient and transfer tokens to them"""
87+
88+ associate_tx = (
89+ TokenAssociateTransaction ()
90+ .set_account_id (recipient_id )
91+ .add_token_id (token_id )
92+ .freeze_with (client )
93+ .sign (recipient_key )
94+ )
95+ receipt_associate = associate_tx .execute (client )
96+
97+ if receipt_associate .status != ResponseCode .SUCCESS :
98+ print (f"❌ Token association failed with status: { ResponseCode (receipt_associate .status ).name } " )
99+ sys .exit (1 )
100+ print (f" --> Associated token { token_id } to account { recipient_id } ." )
101+
102+ transfer_tx = (
103+ TransferTransaction ()
104+ .add_token_transfer (token_id , client .operator_account_id , - amount )
105+ .add_token_transfer (token_id , recipient_id , amount )
106+ )
107+
108+ receipt_transfer = transfer_tx .execute (client )
109+
110+ if receipt_transfer .status != ResponseCode .SUCCESS :
111+ print (f"❌ Token transfer failed with status: { ResponseCode (receipt_transfer .status ).name } " )
112+ sys .exit (1 )
113+ print (f" --> Transferred { amount } tokens to account { recipient_id } ." )
114+
115+
116+ def create_token_no_wipe_key (client , operator_id , operator_key ):
117+ """Create a token WITHOUT a wipe key."""
118+ print ("\n --- Scenario 1: Token WITHOUT Wipe Key ---" )
119+ print ("Creating token WITHOUT a wipe key..." )
120+
121+ transaction = (
122+ TokenCreateTransaction ()
123+ .set_token_name ("No Wipe Token" )
124+ .set_token_symbol ("NWT" )
125+ .set_decimals (0 )
126+ .set_initial_supply (1000 )
127+ .set_treasury_account_id (operator_id )
128+ # No wipe key set here
129+ .freeze_with (client )
130+ )
131+ transaction .sign (operator_key )
132+
133+ try :
134+ receipt = transaction .execute (client )
135+ if receipt .status != ResponseCode .SUCCESS :
136+ print (f"❌ Token creation failed with status: { ResponseCode (receipt .status ).name } " )
137+ sys .exit (1 )
138+
139+ print (f"✅ Token created: { receipt .token_id } " )
140+ return receipt .token_id
141+
142+ except Exception as e :
143+ print (f"❌ Token creation failed with error: { e } " )
144+ sys .exit (1 )
145+
146+
147+ def demonstrate_wipe_fail (client , token_id , target_account_id ):
148+ """Attempt to wipe tokens when no wipe key exists. Should FAIL."""
149+ print (f"Attempting to wipe tokens from { target_account_id } (Should FAIL)..." )
150+
151+ transaction = (
152+ TokenWipeTransaction ()
153+ .set_token_id (token_id )
154+ .set_account_id (target_account_id )
155+ .set_amount (10 )
156+ .freeze_with (client )
157+ )
158+
159+ try :
160+ # Since no wipe key exists on the token, Hiero will reject this.
161+ receipt = transaction .execute (client )
162+ if receipt .status == ResponseCode .TOKEN_HAS_NO_WIPE_KEY :
163+ print (f"✅ Wipe failed as expected! Token has no wipe key with status: { ResponseCode (receipt .status ).name } ." )
164+ else :
165+ print (f"❌ Wipe unexpectedly succeeded or failed with status: { ResponseCode (receipt .status ).name } " )
166+
167+ except Exception as e :
168+ print (f"✅ Wipe failed as expected with error: { e } " )
169+
170+
171+ def create_token_with_wipe_key (client , operator_id , operator_key ):
172+ """Create a token WITH a wipe key."""
173+ print ("\n --- Scenario 2: Token WITH Wipe Key ---" )
174+ print ("Creating token WITH a wipe key..." )
175+ wipe_key = PrivateKey .generate_ed25519 ()
176+
177+ transaction = (
178+ TokenCreateTransaction ()
179+ .set_token_name ("With Wipe Token" )
180+ .set_token_symbol ("WWT" )
181+ .set_decimals (0 )
182+ .set_initial_supply (1000 )
183+ .set_treasury_account_id (operator_id )
184+ .set_wipe_key (wipe_key ) # Setting the wipe key
185+ .freeze_with (client )
186+ )
187+ transaction .sign (operator_key )
188+
189+ try :
190+ receipt = transaction .execute (client )
191+ if receipt .status != ResponseCode .SUCCESS :
192+ print (f"❌ Token creation failed with status: { ResponseCode (receipt .status ).name } " )
193+ sys .exit (1 )
194+
195+ print (f"✅ Token created: { receipt .token_id } " )
196+ return receipt .token_id , wipe_key
197+
198+ except Exception as e :
199+ print (f"❌ Token creation failed with error: { e } " )
200+ sys .exit (1 )
201+
202+ def demonstrate_wipe_success (client , token_id , target_account_id , wipe_key ):
203+ """Wipe tokens using the valid wipe key."""
204+ print (f"Wiping 10 tokens from { target_account_id } using Wipe Key..." )
205+
206+ transaction = (
207+ TokenWipeTransaction ()
208+ .set_token_id (token_id )
209+ .set_account_id (target_account_id )
210+ .set_amount (10 )
211+ .freeze_with (client )
212+ )
213+
214+ # Critical: Sign with the wipe key
215+ transaction .sign (wipe_key )
216+
217+ receipt = transaction .execute (client )
218+ if receipt .status != ResponseCode .SUCCESS :
219+ print (f"❌ Wipe failed with status: { ResponseCode (receipt .status ).name } " )
220+ return False
221+
222+ print (f"✅ Wipe Successful!" )
223+ return True
224+
225+
226+ def verify_supply (client , token_id ):
227+ """Check the total supply to verify burn occurred."""
228+ info = TokenInfoQuery ().set_token_id (token_id ).execute (client )
229+ print (f" -> New Total Supply: { info .total_supply } (Should be 990)" )
230+
231+
232+ def demonstrate_nft_wipe_scenario (client , operator_id , operator_key , user_id , user_key ):
233+ """
234+ Scenario 3: Create an NFT, Mint it, Transfer it, and then Wipe it.
235+ This demonstrates that Wipe Key works for NON_FUNGIBLE_UNIQUE tokens as well.
236+ """
237+ print ("\n --- Scenario 3: NFT Wipe with Wipe Key ---" )
238+ print ("Creating an NFT Collection with a Wipe Key..." )
239+
240+ wipe_key = PrivateKey .generate_ed25519 ()
241+ supply_key = PrivateKey .generate_ed25519 () # Needed to mint the NFT first
242+
243+ # 1. Create the NFT Token
244+ transaction = (
245+ TokenCreateTransaction ()
246+ .set_token_name ("Wipeable NFT" )
247+ .set_token_symbol ("W-NFT" )
248+ .set_token_type (TokenType .NON_FUNGIBLE_UNIQUE )
249+ .set_initial_supply (0 )
250+ .set_treasury_account_id (operator_id )
251+ .set_admin_key (operator_key )
252+ .set_supply_key (supply_key ) # Required to mint
253+ .set_wipe_key (wipe_key ) # Required to wipe
254+ .freeze_with (client )
255+ )
256+ transaction .sign (operator_key )
257+
258+ receipt = transaction .execute (client )
259+ nft_token_id = receipt .token_id
260+ print (f"✅ NFT Token created: { nft_token_id } " )
261+
262+ # 2. Mint an NFT (Serial #1)
263+ print ("Minting NFT Serial #1..." )
264+ mint_tx = (
265+ TokenMintTransaction ()
266+ .set_token_id (nft_token_id )
267+ .set_metadata ([b"Metadata for NFT 1" ])
268+ .freeze_with (client )
269+ )
270+ mint_tx .sign (supply_key )
271+ mint_receipt = mint_tx .execute (client )
272+ serial_number = mint_receipt .serial_numbers [0 ]
273+ print (f"✅ Minted NFT Serial: { serial_number } " )
274+
275+ # 3. Associate User and Transfer NFT to them
276+ print (f"Transferring NFT #{ serial_number } to user { user_id } ..." )
277+
278+ # Associate
279+ associate_tx = (
280+ TokenAssociateTransaction ()
281+ .set_account_id (user_id )
282+ .add_token_id (nft_token_id )
283+ .freeze_with (client )
284+ .sign (user_key )
285+ ).execute (client )
286+
287+ # Transfer
288+ # FIX 2: Use NftId(token_id, serial_number)
289+ transfer_tx = (
290+ TransferTransaction ()
291+ .add_nft_transfer (NftId (nft_token_id , serial_number ), operator_id , user_id )
292+ .execute (client )
293+ )
294+ print (f"✅ Transfer complete." )
295+
296+ # 4. Wipe the NFT from the User
297+ print (f"Attempting to WIPE NFT #{ serial_number } from user { user_id } ..." )
298+
299+ wipe_tx = (
300+ TokenWipeTransaction ()
301+ .set_token_id (nft_token_id )
302+ .set_account_id (user_id )
303+ .set_serial ([serial_number ])
304+ .freeze_with (client )
305+ )
306+ wipe_tx .sign (wipe_key ) # Sign with Wipe Key
307+
308+ wipe_receipt = wipe_tx .execute (client )
309+
310+ if wipe_receipt .status == ResponseCode .SUCCESS :
311+ print ("✅ NFT Wipe Successful! The NFT has been effectively burned from the user's account." )
312+ else :
313+ print (f"❌ NFT Wipe Failed: { ResponseCode (wipe_receipt .status ).name } " )
314+
315+
316+ def main ():
317+ """
318+ Main execution flow:
319+ 1. Setup Client
320+ 2. Create a recipient account
321+ 3. Scenario 1: Fail to wipe without key (Fungible)
322+ 4. Scenario 2: Successfully wipe with key (Fungible)
323+ 5. Scenario 3: Successfully wipe with key (NFT)
324+ """
325+ client , operator_id , operator_key = setup_client ()
326+
327+ # Create a generic user to hold tokens
328+ print ("\n Creating a user account to hold tokens..." )
329+ user_id , user_key = create_recipient_account (client )
330+ print (f"User created: { user_id } " )
331+
332+ # --- Scenario 1: No Wipe Key (Fungible) ---
333+ token_id_no_key = create_token_no_wipe_key (client , operator_id , operator_key )
334+ associate_and_transfer (client , token_id_no_key , user_id , user_key , 50 )
335+ demonstrate_wipe_fail (client , token_id_no_key , user_id )
336+
337+ # --- Scenario 2: With Wipe Key (Fungible) ---
338+ token_id_with_key , wipe_key = create_token_with_wipe_key (client , operator_id , operator_key )
339+ associate_and_transfer (client , token_id_with_key , user_id , user_key , 50 )
340+
341+ if demonstrate_wipe_success (client , token_id_with_key , user_id , wipe_key ):
342+ verify_supply (client , token_id_with_key )
343+
344+ # --- Scenario 3: NFT Wipe (Non-Fungible) ---
345+ demonstrate_nft_wipe_scenario (client , operator_id , operator_key , user_id , user_key )
346+
347+ print ("\n 🎉 Wipe key demonstration completed!" )
348+
349+ if __name__ == "__main__" :
350+ main ()
0 commit comments