diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5c0f48a..d6356ea8b 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 +- Refactored `account_create_transaction_evm_alias.py` example by splitting monolithic function into modular functions: `generate_alias_key()`, `create_account_with_alias()`, `fetch_account_info()`, and `print_account_summary()` (#1017) - Modularized `transfer_transaction_fungible` example by introducing `account_balance_query()` & `transfer_transaction()`.Renamed `transfer_tokens()` → `main()` - Phase 2 of the inactivity-unassign bot:Automatically detects stale open pull requests (no commit activity for 21+ days), comments with a helpful InactivityBot message, closes the stale PR, and unassigns the contributor from the linked issue. - Added `__str__()` to CustomFixedFee and updated examples and tests accordingly. diff --git a/examples/account/account_create_transaction_create_with_alias.py b/examples/account/account_create_transaction_create_with_alias.py index 9d8e6cade..e86a27f5d 100644 --- a/examples/account/account_create_transaction_create_with_alias.py +++ b/examples/account/account_create_transaction_create_with_alias.py @@ -15,6 +15,7 @@ import os import sys import json +from typing import Tuple, Optional from dotenv import load_dotenv from examples.utils import info_to_dict @@ -22,6 +23,7 @@ from hiero_sdk_python import ( Client, PrivateKey, + PublicKey, AccountCreateTransaction, AccountInfoQuery, Network, @@ -33,7 +35,7 @@ network_name = os.getenv("NETWORK", "testnet").lower() -def setup_client(): +def setup_client() -> Client: """Setup Client.""" network = Network(network_name) print(f"Connecting to Hedera {network_name} network!") @@ -49,8 +51,9 @@ def setup_client(): print("Error: Please check OPERATOR_ID and OPERATOR_KEY in your .env file.") sys.exit(1) -def create_account_with_separate_ecdsa_alias(client: Client) -> None: - """Create an account whose alias comes from a separate ECDSA key.""" + +def generate_main_and_alias_keys() -> Tuple[PrivateKey, PublicKey, PrivateKey]: + """Generate the main key and the ECDSA alias key.""" try: print("\nSTEP 1: Generating main account key and separate ECDSA alias key...") @@ -64,13 +67,27 @@ def create_account_with_separate_ecdsa_alias(client: Client) -> None: alias_evm_address = alias_public_key.to_evm_address() if alias_evm_address is None: - print("❌ Error: Failed to generate EVM address from alias ECDSA key.") - sys.exit(1) + raise RuntimeError("Failed to generate EVM address from alias ECDSA key.") print(f"✅ Main account public key: {main_public_key}") print(f"✅ Alias ECDSA public key: {alias_public_key}") print(f"✅ Alias EVM address: {alias_evm_address}") + return main_private_key, alias_public_key, alias_private_key + + except Exception as error: + print(f"❌ Error in key generation: {error}") + sys.exit(1) + + +def create_account( + client: Client, + main_private_key: PrivateKey, + alias_public_key: PublicKey, + alias_private_key: PrivateKey, +) -> AccountId: + """Create the account with the EVM alias from the ECDSA key.""" + try: print("\nSTEP 2: Creating the account with the EVM alias from the ECDSA key...") # Use the helper that accepts both the main key and the ECDSA alias key @@ -98,11 +115,22 @@ def create_account_with_separate_ecdsa_alias(client: Client) -> None: "AccountID not found in receipt. Account may not have been created." ) - print(f"✅ Account created with ID: {new_account_id}\n") + print(f"✅ Account created with ID: {new_account_id}") + return new_account_id + + except Exception as error: + print(f"❌ Error in account creation: {error}") + sys.exit(1) + +def print_account_info(client: Client, account_id: AccountId) -> None: + """Print account information.""" + try: + print("\nSTEP 3: Verifying account info...") + account_info = ( AccountInfoQuery() - .set_account_id(new_account_id) + .set_account_id(account_id) .execute(client) ) @@ -119,13 +147,24 @@ def create_account_with_separate_ecdsa_alias(client: Client) -> None: print("❌ Error: Contract Account ID (alias) does not exist.") except Exception as error: - print(f"❌ Error: {error}") + print(f"❌ Error fetching account info: {error}") sys.exit(1) + def main(): """Main entry point.""" client = setup_client() - create_account_with_separate_ecdsa_alias(client) + + # Step 1: Generate keys + main_private_key, alias_public_key, alias_private_key = generate_keys() + + # Step 2: Create account + new_account_id = create_account( + client, main_private_key, alias_public_key, alias_private_key + ) + + # Step 3: Print account info + print_account_info(client, new_account_id) if __name__ == "__main__": diff --git a/examples/account/account_create_transaction_evm_alias.py b/examples/account/account_create_transaction_evm_alias.py index b5bf4d1f8..bda40d3c8 100644 --- a/examples/account/account_create_transaction_evm_alias.py +++ b/examples/account/account_create_transaction_evm_alias.py @@ -15,6 +15,7 @@ Client, PrivateKey, AccountCreateTransaction, + AccountInfo, AccountInfoQuery, Network, AccountId, @@ -24,8 +25,9 @@ load_dotenv() network_name = os.getenv('NETWORK', 'testnet').lower() + def setup_client(): - """Setup Client """ + """Setup Client.""" network = Network(network_name) print(f"Connecting to Hedera {network_name} network!") client = Client(network) @@ -42,59 +44,115 @@ def setup_client(): print("Error: Please check OPERATOR_ID and OPERATOR_KEY in your .env file.") sys.exit(1) -def create_account_with_alias(client): - """Create an account with an alias transaction.""" - try: - print("\nSTEP 1: Generating a new ECDSA key pair for the account alias...") - private_key = PrivateKey.generate('ecdsa') - public_key = private_key.public_key() - evm_address = public_key.to_evm_address() - if evm_address is None: - print("❌ Error: Failed to generate EVM address from public key.") - sys.exit(1) - print(f"✅ Generated new ECDSA key pair. EVM Address (alias): {evm_address}") - # Create the account with the alias - print("\nSTEP 2: Creating the account with the EVM address alias...") - transaction = ( - AccountCreateTransaction() - .set_key(public_key) - .set_initial_balance(Hbar(5)) - .set_alias(evm_address) - ) - # Sign the transaction with both the new key and the operator key - transaction = transaction.freeze_with(client) \ - .sign(private_key) \ - .sign(client.operator_private_key) - - # Execute the transaction - response = transaction.execute(client) - new_account_id = response.account_id - print(f"✅ Account created with ID: {new_account_id}\n") - # Fetch and display account info - account_info = AccountInfoQuery().set_account_id(new_account_id).execute(client) - # Print the account info - out = info_to_dict(account_info) - print("🧾 Account Info:") - print(json.dumps(out, indent=2) + "\n") - if account_info.contract_account_id is not None: - print(f"✅ Contract Account ID (alias): {account_info.contract_account_id}") - else: - print("❌ Error: Contract Account ID (alias) does not exist.") +def generate_alias_key() -> PrivateKey: + """Generate an ECDSA key pair for the account alias. - except Exception as error: - print(f"❌ Error: {error}") + Returns: + PrivateKey: The generated ECDSA private key. + """ + print("\nSTEP 1: Generating a new ECDSA key pair for the account alias...") + private_key = PrivateKey.generate('ecdsa') + public_key = private_key.public_key() + evm_address = public_key.to_evm_address() + + if evm_address is None: + print("❌ Error: Failed to generate EVM address from public key.") sys.exit(1) + print(f"✅ Generated new ECDSA key pair. EVM Address (alias): {evm_address}") + return private_key + + +def create_account_with_alias(client: Client, private_key: PrivateKey) -> AccountId: + """Create an account with an EVM address alias. + + Args: + client: The Hedera client. + private_key: The ECDSA private key for the account. + + Returns: + AccountId: The newly created account ID. + """ + print("\nSTEP 2: Creating the account with the EVM address alias...") + + public_key = private_key.public_key() + evm_address = public_key.to_evm_address() + + transaction = ( + AccountCreateTransaction() + .set_key(public_key) + .set_initial_balance(Hbar(5)) + .set_alias(evm_address) + ) + + # Sign the transaction with both the new key and the operator key + transaction = transaction.freeze_with(client) \ + .sign(private_key) \ + .sign(client.operator_private_key) + + # Execute the transaction + response = transaction.execute(client) + new_account_id = response.account_id + + if new_account_id is None: + raise RuntimeError( + "AccountID not found in receipt. Account may not have been created." + ) + + print(f"✅ Account created with ID: {new_account_id}\n") + return new_account_id + + +def fetch_account_info(client: Client, account_id: AccountId) -> AccountInfo: + """Fetch account information from the network. + + Args: + client: The Hedera client. + account_id: The account ID to query. + + Returns: + AccountInfo: The account info object. + """ + print("\nSTEP 3: Fetching account information...") + account_info = AccountInfoQuery().set_account_id(account_id).execute(client) + return account_info + + +def print_account_summary(account_info: AccountInfo) -> None: + """Print a summary of the account information. + + Args: + account_info: The account info object to display. + """ + out = info_to_dict(account_info) + print("🧾 Account Info:") + print(json.dumps(out, indent=2) + "\n") + + if account_info.contract_account_id is not None: + print(f"✅ Contract Account ID (alias): {account_info.contract_account_id}") + else: + print("❌ Error: Contract Account ID (alias) does not exist.") + + def main(): """Main function to create an account with an alias transaction. + 1- Setup client 2- Generate ECDSA key pair and EVM address alias 3- Create account with alias - 4- Print account info + 4- Fetch account information + 5- Print account summary """ - client = setup_client() - create_account_with_alias(client) + try: + client = setup_client() + private_key = generate_alias_key() + account_id = create_account_with_alias(client, private_key) + account_info = fetch_account_info(client, account_id) + print_account_summary(account_info) + except Exception as error: + print(f"❌ Error: {error}") + sys.exit(1) if __name__ == "__main__":