Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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.
- Added `__str__()` to CustomFixedFee and updated examples and tests accordingly.
Expand Down
179 changes: 114 additions & 65 deletions examples/account/account_create_transaction_create_with_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,83 +49,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):
"""Fetch account information from the network.

Args:
client: The Hedera client.
account_id: The account ID to query.

Returns:
The account info object.
"""
print("STEP 3: Fetching account information...")
account_info = (
AccountInfoQuery()
.set_account_id(account_id)
.execute(client)
)
return account_info


def print_account_summary(account_info) -> 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__":
Expand Down