diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5c0f48a..4c1dc8528 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Added +- Refactored `account_create_transaction_create_with_alias.py` example by splitting monolithic function into modular functions: `generate_main_and_alias_keys()`, `create_account_with_ecdsa_alias()`, `fetch_account_info()`, `print_account_summary()` (#1016) - 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. +- 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. - Added unit tests for `crypto_utils` (#993) - Added a github template for good first issues diff --git a/examples/account/account_create_transaction_create_with_alias.py b/examples/account/account_create_transaction_create_with_alias.py index 9d8e6cade..21370e8cd 100644 --- a/examples/account/account_create_transaction_create_with_alias.py +++ b/examples/account/account_create_transaction_create_with_alias.py @@ -23,6 +23,7 @@ Client, PrivateKey, AccountCreateTransaction, + AccountInfo, AccountInfoQuery, Network, AccountId, @@ -49,83 +50,132 @@ 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.""" - try: - print("\nSTEP 1: Generating main account key and separate ECDSA alias key...") - - # Main account key (can be any key type, here ed25519) - main_private_key = PrivateKey.generate() - main_public_key = main_private_key.public_key() - - # Separate ECDSA key used only for the EVM alias - alias_private_key = PrivateKey.generate("ecdsa") - alias_public_key = alias_private_key.public_key() - 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) - - 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}") - - 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 - transaction = ( - AccountCreateTransaction( - initial_balance=Hbar(5), - memo="Account with separate ECDSA alias", - ) - .set_key_with_alias(main_private_key, alias_public_key) - ) - # Freeze and sign: - # - operator key signs as payer (via client) - # - alias private key MUST sign to authorize the alias - transaction = ( - transaction.freeze_with(client) - .sign(alias_private_key) - ) +def generate_main_and_alias_keys() -> tuple[PrivateKey, PrivateKey]: + """Generate the main account key and a separate ECDSA alias key. + + Returns: + tuple: (main_private_key, alias_private_key) + """ + print("\nSTEP 1: Generating main account key and separate ECDSA alias key...") + + # Main account key (can be any key type, here ed25519) + main_private_key = PrivateKey.generate() + main_public_key = main_private_key.public_key() + + # Separate ECDSA key used only for the EVM alias + alias_private_key = PrivateKey.generate("ecdsa") + alias_public_key = alias_private_key.public_key() + 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) - response = transaction.execute(client) - new_account_id = response.account_id + 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}") - if new_account_id is None: - raise RuntimeError( - "AccountID not found in receipt. Account may not have been created." - ) + return main_private_key, alias_private_key - print(f"✅ Account created with ID: {new_account_id}\n") - account_info = ( - AccountInfoQuery() - .set_account_id(new_account_id) - .execute(client) +def create_account_with_ecdsa_alias( + client: Client, main_private_key: PrivateKey, alias_private_key: PrivateKey +) -> AccountId: + """Create an account with a separate ECDSA key as the EVM alias. + + Args: + client: The Hedera client. + main_private_key: The main account private key. + alias_private_key: The ECDSA private key for the EVM alias. + + Returns: + AccountId: The newly created account ID. + """ + print("\nSTEP 2: Creating the account with the EVM alias from the ECDSA key...") + + alias_public_key = alias_private_key.public_key() + + # Use the helper that accepts both the main key and the ECDSA alias key + transaction = ( + AccountCreateTransaction( + initial_balance=Hbar(5), + memo="Account with separate ECDSA alias", + ) + .set_key_with_alias(main_private_key, alias_public_key) + ) + + # Freeze and sign: + # - operator key signs as payer (via client) + # - alias private key MUST sign to authorize the alias + transaction = ( + transaction.freeze_with(client) + .sign(alias_private_key) + ) + + 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." ) - out = info_to_dict(account_info) - print("Account Info:") - print(json.dumps(out, indent=2) + "\n") + print(f"✅ Account created with ID: {new_account_id}\n") + return new_account_id - if account_info.contract_account_id is not None: - print( - f"✅ Contract Account ID (EVM alias on-chain): " - f"{account_info.contract_account_id}" - ) - else: - print("❌ Error: Contract Account ID (alias) does not exist.") - except Exception as error: - print(f"❌ Error: {error}") - sys.exit(1) +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 (EVM alias on-chain): " + f"{account_info.contract_account_id}" + ) + else: + print("❌ Error: Contract Account ID (alias) does not exist.") + def main(): """Main entry point.""" - client = setup_client() - create_account_with_separate_ecdsa_alias(client) + try: + client = setup_client() + main_private_key, alias_private_key = generate_main_and_alias_keys() + account_id = create_account_with_ecdsa_alias( + client, main_private_key, alias_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__":