diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c57b685e..ee9121de1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ## [Unreleased] ### Added -- - +- Add examples/tokens/token_create_transaction_pause_key.py example demonstrating token pause/unpause behavior and pause key usage (#833) ### Changed - diff --git a/examples/tokens/token_create_transaction_pause_key.py b/examples/tokens/token_create_transaction_pause_key.py new file mode 100644 index 000000000..026040c42 --- /dev/null +++ b/examples/tokens/token_create_transaction_pause_key.py @@ -0,0 +1,262 @@ +""" +This example demonstrates the pause key capabilities for token management using the Hiero Python SDK. + +It shows: +1. Creating a token *without* a pause key +2. Attempting to pause it — expected failure +3. Creating a token *with* a pause key +4. Successfully pausing and unpausing the token +5. Demonstrating that transfers fail while the token is paused + +Required environment variables: +- OPERATOR_ID +- OPERATOR_KEY + +Usage: +uv run examples/token_create_transaction_pause_key.py +""" + +import os +import sys +from dotenv import load_dotenv + +from hiero_sdk_python import ( + Client, + AccountId, + PrivateKey, + Network, + TokenCreateTransaction, + TokenPauseTransaction, + TokenUnpauseTransaction, + TokenUpdateTransaction, + TokenInfoQuery, + TransferTransaction, + AccountCreateTransaction, + Hbar, +) + +from hiero_sdk_python.response_code import ResponseCode +from hiero_sdk_python.tokens.token_type import TokenType +from hiero_sdk_python.tokens.supply_type import SupplyType + +load_dotenv() +network_name = os.getenv("NETWORK", "testnet").lower() + + +# ------------------------------------------------------- +# CLIENT SETUP +# ------------------------------------------------------- +def setup_client(): + """Create client from environment variables""" + network = Network(network_name) + print(f"Connecting to Hedera {network_name} network...") + client = Client(network) + + try: + operator_id = AccountId.from_string(os.getenv("OPERATOR_ID", "")) + operator_key = PrivateKey.from_string(os.getenv("OPERATOR_KEY", "")) + client.set_operator(operator_id, operator_key) + print(f"Client ready — Operator: {client.operator_account_id}\n") + return client, operator_id, operator_key + + except Exception: + print("āŒ ERROR: Invalid OPERATOR_ID or OPERATOR_KEY in .env") + sys.exit(1) + + +# ------------------------------------------------------- +# TOKEN CREATION (NO PAUSE KEY) +# ------------------------------------------------------- +def create_token_without_pause_key(client, operator_id, operator_key): + print("šŸ”¹ Creating token WITHOUT pause key...") + + tx = ( + TokenCreateTransaction() + .set_token_name("PauseKeyMissing") + .set_token_symbol("NOPAUSE") + .set_decimals(0) + .set_initial_supply(100) + .set_treasury_account_id(operator_id) + .set_token_type(TokenType.FUNGIBLE_COMMON) + .set_supply_type(SupplyType.INFINITE) + .freeze_with(client) + .sign(operator_key) + ) + + receipt = tx.execute(client) + if receipt.status != ResponseCode.SUCCESS: + print("āŒ Token creation failed") + sys.exit(1) + + token_id = receipt.token_id + print(f"āœ… Token created WITHOUT pause key → {token_id}\n") + return token_id + + +def attempt_pause_should_fail(client, token_id, operator_key): + print("šŸ”¹ Attempting to pause token WITHOUT a pause key... (expected failure)") + + tx = ( + TokenPauseTransaction() + .set_token_id(token_id) + .freeze_with(client) + .sign(operator_key) + ) + + receipt = tx.execute(client) + + if receipt.status == ResponseCode.TOKEN_HAS_NO_PAUSE_KEY: + print("āœ… Expected failure: token cannot be paused because no pause key exists.\n") + else: + print(f"āŒ Unexpected status: {ResponseCode(receipt.status).name}\n") + + +# ------------------------------------------------------- +# TOKEN CREATION WITH PAUSE KEY +# ------------------------------------------------------- +def create_token_with_pause_key(client, operator_id, operator_key, pause_key): + print("šŸ”¹ Creating token WITH pause key...") + + tx = ( + TokenCreateTransaction() + .set_token_name("PauseKeyDemo") + .set_token_symbol("PAUSE") + .set_decimals(0) + .set_initial_supply(100) + .set_treasury_account_id(operator_id) + .set_token_type(TokenType.FUNGIBLE_COMMON) + .set_supply_type(SupplyType.INFINITE) + .set_pause_key(pause_key) # NEW + .freeze_with(client) + ) + + tx.sign(operator_key) + tx.sign(pause_key) + + receipt = tx.execute(client) + if receipt.status != ResponseCode.SUCCESS: + print("āŒ Token creation failed") + sys.exit(1) + + token_id = receipt.token_id + print(f"āœ… Token created WITH pause key → {token_id}\n") + return token_id + + +# ------------------------------------------------------- +# PAUSE / UNPAUSE DEMO +# ------------------------------------------------------- +def pause_token(client, token_id, pause_key): + print("šŸ”¹ Pausing token...") + + tx = ( + TokenPauseTransaction() + .set_token_id(token_id) + .freeze_with(client) + .sign(pause_key) + ) + + receipt = tx.execute(client) + if receipt.status == ResponseCode.SUCCESS: + print("āœ… Token paused successfully!\n") + else: + print(f"āŒ Pause failed: {ResponseCode(receipt.status).name}") + + +def unpause_token(client, token_id, pause_key): + print("šŸ”¹ Unpausing token...") + + tx = ( + TokenUnpauseTransaction() + .set_token_id(token_id) + .freeze_with(client) + .sign(pause_key) + ) + + receipt = tx.execute(client) + if receipt.status == ResponseCode.SUCCESS: + print("āœ… Token unpaused successfully!\n") + else: + print(f"āŒ Unpause failed: {ResponseCode(receipt.status).name}") + + +# ------------------------------------------------------- +# TRANSFERS WHILE PAUSED SHOULD FAIL +# ------------------------------------------------------- +def create_temp_account(client, operator_key): + """Creates a small account for transfer testing.""" + new_key = PrivateKey.generate_ed25519() + pub_key = new_key.public_key() + + print("šŸ”¹ Creating a temporary recipient account...") + + tx = ( + AccountCreateTransaction() + .set_key(pub_key) # MUST use public key + .set_initial_balance(Hbar.from_tinybars(1000)) + .freeze_with(client) + .sign(operator_key) + ) + + receipt = tx.execute(client) + + if receipt.status != ResponseCode.SUCCESS: + print(f"āŒ Failed to create temp account: {ResponseCode(receipt.status).name}") + sys.exit(1) + + account_id = receipt.account_id + print(f"āœ… Temp account created: {account_id}\n") + + return account_id, new_key + + + +def test_transfer_while_paused(client, operator_id, operator_key, recipient_id, token_id): + print("šŸ”¹ Attempting transfer WHILE token is paused (expected failure)...") + + tx = ( + TransferTransaction() + .add_token_transfer(token_id, operator_id, -10) + .add_token_transfer(token_id, recipient_id, 10) + .freeze_with(client) + .sign(operator_key) + ) + + receipt = tx.execute(client) + + if receipt.status == ResponseCode.TOKEN_IS_PAUSED: + print("āœ… Transfer failed as expected: TOKEN_IS_PAUSED\n") + else: + print(f"āš ļø Unexpected status: {ResponseCode(receipt.status).name}\n") + +# ------------------------------------------------------- +# MAIN +# ------------------------------------------------------- +def main(): + client, operator_id, operator_key = setup_client() + + print("\n==================== PART 1 — NO PAUSE KEY ====================\n") + token_no_pause = create_token_without_pause_key(client, operator_id, operator_key) + attempt_pause_should_fail(client, token_no_pause, operator_key) + + print("\n==================== PART 2 — WITH PAUSE KEY ====================\n") + pause_key = PrivateKey.generate_ed25519() + + token_with_pause = create_token_with_pause_key( + client, operator_id, operator_key, pause_key + ) + + pause_token(client, token_with_pause, pause_key) + + recipient_id, _ = create_temp_account(client, operator_key) + test_transfer_while_paused( + client, operator_id, operator_key, recipient_id, token_with_pause + ) + + unpause_token(client, token_with_pause, pause_key) + + print("\nšŸŽ‰ Pause key demonstration completed!") + + +if __name__ == "__main__": + main()