Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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_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.
Expand Down
57 changes: 48 additions & 9 deletions examples/account/account_create_transaction_create_with_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
import os
import sys
import json
from typing import Tuple, Optional
from dotenv import load_dotenv

from examples.utils import info_to_dict

from hiero_sdk_python import (
Client,
PrivateKey,
PublicKey,
AccountCreateTransaction,
AccountInfoQuery,
Network,
Expand All @@ -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!")
Expand All @@ -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...")

Expand All @@ -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
Expand Down Expand Up @@ -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)
)

Expand All @@ -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__":
Expand Down
146 changes: 102 additions & 44 deletions examples/account/account_create_transaction_evm_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Client,
PrivateKey,
AccountCreateTransaction,
AccountInfo,
AccountInfoQuery,
Network,
AccountId,
Expand All @@ -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)
Expand All @@ -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__":
Expand Down