From e269f5749d4034f2ee5f60c04e5a83a12bd0a517 Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Thu, 20 Nov 2025 01:08:58 +0530 Subject: [PATCH 01/12] feat(examples): add pause key example for token create Signed-off-by: undefinedIsMyLife --- .../token_create_transaction_pause_key.py | 319 ++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 examples/token_create_transaction_pause_key.py diff --git a/examples/token_create_transaction_pause_key.py b/examples/token_create_transaction_pause_key.py new file mode 100644 index 000000000..4e5413102 --- /dev/null +++ b/examples/token_create_transaction_pause_key.py @@ -0,0 +1,319 @@ +""" +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 +6. (Bonus) Removing the pause key while the token is paused → permanently 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)") + try: + tx = ( + TokenPauseTransaction() + .set_token_id(token_id) + .freeze_with(client) + .sign(operator_key) + ) + receipt = tx.execute(client) + + print("āš ļø Unexpected success! Pause should NOT be possible.") + print(f"Status: {ResponseCode(receipt.status).name}\n") + + except Exception as e: + print(f"āœ… Expected failure occurred: {e}\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) + 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") + + + +# ------------------------------------------------------- +# BONUS: REMOVE PAUSE KEY WHILE PAUSED → PERMANENT +# ------------------------------------------------------- +def remove_pause_key(client, token_id, pause_key): + print("šŸ”¹ Removing pause key while token is UNPAUSED...") + + tx = ( + TokenUpdateTransaction() + .set_token_id(token_id) + .set_pause_key(None) + .freeze_with(client) + .sign(pause_key) + ) + + receipt = tx.execute(client) + + if receipt.status == ResponseCode.SUCCESS: + print("āœ… Pause key removed successfully.\n") + return True + else: + print(f"āŒ Failed to remove pause key: {ResponseCode(receipt.status).name}\n") + return False + + +def attempt_unpause_after_removal_should_fail(client, token_id): + print("šŸ”¹ Attempting to unpause a token with NO pause key (should fail)...") + + try: + tx = ( + TokenUnpauseTransaction() + .set_token_id(token_id) + .freeze_with(client) + ) + + # Do NOT sign — because pause key no longer exists + receipt = tx.execute(client) + + print(f"āš ļø Unexpected status: {ResponseCode(receipt.status).name}\n") + + except Exception as e: + print(f"āœ… Permanent unpause failure occurred as expected: {e}\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==================== BONUS — PERMANENT PAUSE ====================\n") + # 1. Pause + pause_token(client, token_with_pause, pause_key) + + # 2. Unpause (required to modify keys) + unpause_token(client, token_with_pause, pause_key) + + # 3. Remove pause key + if remove_pause_key(client, token_with_pause, pause_key): + + # 4. Pause again + pause_token(client, token_with_pause, pause_key) + + # 5. Unpause should now fail permanently + attempt_unpause_after_removal_should_fail(client, token_with_pause) + +print("\nšŸŽ‰ Pause key demonstration completed!") + + +if __name__ == "__main__": + main() From 9b46d8966f6d32aed19a4901d84b03512437af22 Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Sun, 23 Nov 2025 23:44:29 +0530 Subject: [PATCH 02/12] fix(example): remove unsupported bonus pause key removal logic Signed-off-by: undefinedIsMyLife --- .../token_create_transaction_pause_key.py | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/examples/token_create_transaction_pause_key.py b/examples/token_create_transaction_pause_key.py index 4e5413102..26b18889b 100644 --- a/examples/token_create_transaction_pause_key.py +++ b/examples/token_create_transaction_pause_key.py @@ -224,52 +224,6 @@ def test_transfer_while_paused(client, operator_id, operator_key, recipient_id, else: print(f"āš ļø Unexpected status: {ResponseCode(receipt.status).name}\n") - - -# ------------------------------------------------------- -# BONUS: REMOVE PAUSE KEY WHILE PAUSED → PERMANENT -# ------------------------------------------------------- -def remove_pause_key(client, token_id, pause_key): - print("šŸ”¹ Removing pause key while token is UNPAUSED...") - - tx = ( - TokenUpdateTransaction() - .set_token_id(token_id) - .set_pause_key(None) - .freeze_with(client) - .sign(pause_key) - ) - - receipt = tx.execute(client) - - if receipt.status == ResponseCode.SUCCESS: - print("āœ… Pause key removed successfully.\n") - return True - else: - print(f"āŒ Failed to remove pause key: {ResponseCode(receipt.status).name}\n") - return False - - -def attempt_unpause_after_removal_should_fail(client, token_id): - print("šŸ”¹ Attempting to unpause a token with NO pause key (should fail)...") - - try: - tx = ( - TokenUnpauseTransaction() - .set_token_id(token_id) - .freeze_with(client) - ) - - # Do NOT sign — because pause key no longer exists - receipt = tx.execute(client) - - print(f"āš ļø Unexpected status: {ResponseCode(receipt.status).name}\n") - - except Exception as e: - print(f"āœ… Permanent unpause failure occurred as expected: {e}\n") - - - # ------------------------------------------------------- # MAIN # ------------------------------------------------------- @@ -296,22 +250,6 @@ def main(): unpause_token(client, token_with_pause, pause_key) - print("\n==================== BONUS — PERMANENT PAUSE ====================\n") - # 1. Pause - pause_token(client, token_with_pause, pause_key) - - # 2. Unpause (required to modify keys) - unpause_token(client, token_with_pause, pause_key) - - # 3. Remove pause key - if remove_pause_key(client, token_with_pause, pause_key): - - # 4. Pause again - pause_token(client, token_with_pause, pause_key) - - # 5. Unpause should now fail permanently - attempt_unpause_after_removal_should_fail(client, token_with_pause) - print("\nšŸŽ‰ Pause key demonstration completed!") From 809d038f73d914017559c4c33a49224a6b4c0595 Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Mon, 24 Nov 2025 12:18:38 +0530 Subject: [PATCH 03/12] chore(example): remove unsupported bonus pause-key section Signed-off-by: undefinedIsMyLife --- examples/token_create_transaction_pause_key.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/token_create_transaction_pause_key.py b/examples/token_create_transaction_pause_key.py index 26b18889b..1ac21455b 100644 --- a/examples/token_create_transaction_pause_key.py +++ b/examples/token_create_transaction_pause_key.py @@ -7,7 +7,7 @@ 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 -6. (Bonus) Removing the pause key while the token is paused → permanently paused + Required environment variables: - OPERATOR_ID From 15bf91b3e0fa7dced14de0ff2f4e1ad28e8781cb Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Mon, 24 Nov 2025 19:58:26 +0530 Subject: [PATCH 04/12] fix(example): improve receipt handling per review feedback Signed-off-by: undefinedIsMyLife --- .../token_create_transaction_pause_key.py | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/token_create_transaction_pause_key.py b/examples/token_create_transaction_pause_key.py index 1ac21455b..a90f50a5c 100644 --- a/examples/token_create_transaction_pause_key.py +++ b/examples/token_create_transaction_pause_key.py @@ -96,20 +96,20 @@ def create_token_without_pause_key(client, operator_id, operator_key): def attempt_pause_should_fail(client, token_id, operator_key): print("šŸ”¹ Attempting to pause token WITHOUT a pause key... (expected failure)") - try: - tx = ( - TokenPauseTransaction() - .set_token_id(token_id) - .freeze_with(client) - .sign(operator_key) - ) - receipt = tx.execute(client) - print("āš ļø Unexpected success! Pause should NOT be possible.") - print(f"Status: {ResponseCode(receipt.status).name}\n") + tx = ( + TokenPauseTransaction() + .set_token_id(token_id) + .freeze_with(client) + .sign(operator_key) + ) - except Exception as e: - print(f"āœ… Expected failure occurred: {e}\n") + 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") # ------------------------------------------------------- @@ -200,8 +200,14 @@ def create_temp_account(client, 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 From f409d91b1b58a468da6b4f8e69d6bb23379689df Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Wed, 26 Nov 2025 22:20:08 +0530 Subject: [PATCH 05/12] fix(example): receipt checks and cleanup Signed-off-by: undefinedIsMyLife --- examples/token_create_transaction_pause_key.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/token_create_transaction_pause_key.py b/examples/token_create_transaction_pause_key.py index a90f50a5c..ba2c2d485 100644 --- a/examples/token_create_transaction_pause_key.py +++ b/examples/token_create_transaction_pause_key.py @@ -8,7 +8,6 @@ 4. Successfully pausing and unpausing the token 5. Demonstrating that transfers fail while the token is paused - Required environment variables: - OPERATOR_ID - OPERATOR_KEY From 6b3188b655bf7af2d256c1993a3af6e5e9e251ee Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Thu, 27 Nov 2025 14:57:26 +0530 Subject: [PATCH 06/12] fix(changelog): restore removed lines and update entry Signed-off-by: undefinedIsMyLife --- examples/{ => tokens}/token_create_transaction_pause_key.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{ => tokens}/token_create_transaction_pause_key.py (100%) diff --git a/examples/token_create_transaction_pause_key.py b/examples/tokens/token_create_transaction_pause_key.py similarity index 100% rename from examples/token_create_transaction_pause_key.py rename to examples/tokens/token_create_transaction_pause_key.py From e90bbb11e92a0a9b0601358598b93b84bda1475c Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Fri, 28 Nov 2025 22:47:41 +0530 Subject: [PATCH 07/12] fix(example): final reviewer adjustments Signed-off-by: undefinedIsMyLife --- examples/tokens/token_create_transaction_pause_key.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tokens/token_create_transaction_pause_key.py b/examples/tokens/token_create_transaction_pause_key.py index ba2c2d485..026040c42 100644 --- a/examples/tokens/token_create_transaction_pause_key.py +++ b/examples/tokens/token_create_transaction_pause_key.py @@ -255,7 +255,7 @@ def main(): unpause_token(client, token_with_pause, pause_key) -print("\nšŸŽ‰ Pause key demonstration completed!") + print("\nšŸŽ‰ Pause key demonstration completed!") if __name__ == "__main__": From 58f61882288a4d882e10c05879981854a3cacf03 Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Fri, 28 Nov 2025 23:06:28 +0530 Subject: [PATCH 08/12] fix(changelog): place pause-key example correctly under Unreleased Signed-off-by: undefinedIsMyLife --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c5fdc2f..adbb19df5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Added +- Add examples/token_create_transaction_pause_key.py example demonstrating token pause/unpause behavior and pause key usage (#820) - Add example demonstrating usage of `CustomFeeLimit` in `examples/transaction/custom_fee_limit.py` - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased] From 82c3a303bb0209f1be26abbf24108715812acd6e Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Fri, 28 Nov 2025 23:33:12 +0530 Subject: [PATCH 09/12] fix(changelog): correct path and PR number for pause key example (#833) Signed-off-by: undefinedIsMyLife --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adbb19df5..cdc02e32f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Added -- Add examples/token_create_transaction_pause_key.py example demonstrating token pause/unpause behavior and pause key usage (#820) +- Add examples/tokens/token_create_transaction_pause_key.py example demonstrating token pause/unpause behavior and pause key usage (#833) - Add example demonstrating usage of `CustomFeeLimit` in `examples/transaction/custom_fee_limit.py` - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased] From 119add0d50b5a47604bf95857fe216d45f27a708 Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Fri, 28 Nov 2025 23:39:22 +0530 Subject: [PATCH 10/12] chore(changelog): temporarily remove pause-key entry Signed-off-by: undefinedIsMyLife --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc02e32f..41c5fdc2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Added -- Add examples/tokens/token_create_transaction_pause_key.py example demonstrating token pause/unpause behavior and pause key usage (#833) - Add example demonstrating usage of `CustomFeeLimit` in `examples/transaction/custom_fee_limit.py` - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased] From fd80cc53bc2b48cc4c11277d6d668a7100efddb9 Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Fri, 28 Nov 2025 23:40:38 +0530 Subject: [PATCH 11/12] chore(changelog): restore pause-key example entry (#833) Signed-off-by: undefinedIsMyLife --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c5fdc2f..4c680c699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Added +- Add `examples/tokens/token_create_transaction_pause_key.py` example demonstrating token pause/unpause behavior and pause key usage (#833) - Add example demonstrating usage of `CustomFeeLimit` in `examples/transaction/custom_fee_limit.py` - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased] From e5d5b2ba1a2bde551419a1fc7e2a3a2ed947793f Mon Sep 17 00:00:00 2001 From: undefinedIsMyLife Date: Sat, 29 Nov 2025 00:05:12 +0530 Subject: [PATCH 12/12] chore(changelog): remove pause-key line as requested Signed-off-by: undefinedIsMyLife --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c680c699..41c5fdc2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Added -- Add `examples/tokens/token_create_transaction_pause_key.py` example demonstrating token pause/unpause behavior and pause key usage (#833) - Add example demonstrating usage of `CustomFeeLimit` in `examples/transaction/custom_fee_limit.py` - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased]