Day 3 交易与余额查询
掌握节点客户端使用和基本转账操作。
学习目标
- 掌握节点客户端的使用
- 理解交易的生命周期
- 查询账户余额和 Nonce
- 完成基本的转账操作
1. 节点 API 概述
| API 类型 | 端口 | 用途 | 权限 |
|---|---|---|---|
| External | 3013 | 公开查询 | 只读 |
| Internal | 3113 | 管理操作 | 需授权 |
| WebSocket | 3014 | 实时推送 | 订阅 |
2. 交易生命周期
┌─────────────────────────────────────────────────────────────┐
│ 交易生命周期 │
├─────────────────────────────────────────────────────────────┤
│ 1. 构建交易 (TxBuilder) │
│ │ │
│ ▼ │
│ 2. 签名交易 (Account.sign) │
│ │ │
│ ▼ │
│ 3. 广播交易 (client.broadcast_transaction) │
│ │ │
│ ▼ │
│ 4. 进入内存池 (Pending) │
│ │ │
│ ▼ │
│ 5. 打包进微块 (Included) │
│ │ │
│ ▼ │
│ 6. 确认 (Confirmed, 3+ 个关键块) │
└─────────────────────────────────────────────────────────────┘
2.1 交易类型
| 类型 | Tag | 说明 |
|---|---|---|
| SpendTx | 12 | 转账交易 |
| ContractCreateTx | 42 | 创建合约 |
| ContractCallTx | 43 | 调用合约 |
| NamePreclaimTx | 33 | 域名预注册 |
| NameClaimTx | 32 | 域名注册 |
| OracleRegisterTx | 22 | 注册预言机 |
2.2 费用计算
# 基础费用公式
fee = base_gas * gas_price + tx_size * gas_per_byte * gas_price
# 默认值
base_gas = 15000
gas_per_byte = 20
gas_price = 1000000000 # 1 Gwei
# 最小费用约 16000 * 1e9 = 0.000016 AE
3. 实践任务
任务 3.1: 查询账户余额
from aeternity.node import NodeClient, Config
from aeternity.utils import format_amount
client = NodeClient(Config(external_url='https://testnet.aeternity.io'))
# 使用测试网上已知有余额的账户
test_address = "ak_2swhLkgBPeeADxVTAVCJnZLY5NZtCFiM93JxsEaMuC59euuFRQ"
print(f"=== 查询账户: {test_address[:20]}... ===\n")
try:
# 方法 1: 使用 get_account
account = client.get_account(test_address)
print(f"账户信息:")
print(f" 地址: {account.address}")
print(f" 余额: {account.balance} aettos")
print(f" 余额: {format_amount(account.balance)}")
print(f" Nonce: {account.nonce}")
print(f" 类型: {account.kind}")
# 方法 2: 使用 get_balance(简化版)
balance = client.get_balance(test_address)
print(f"\nget_balance 返回: {balance}")
except Exception as e:
print(f"查询失败: {e}")
print("提示: 新账户在链上没有记录时返回 404")
任务 3.2: 获取 Nonce
from aeternity.node import NodeClient, Config
from aeternity.signing import Account
client = NodeClient(Config(external_url='https://testnet.aeternity.io'))
# 生成新账户(链上无记录)
new_account = Account.generate()
print(f"新账户: {new_account.get_address()}")
# 获取 Nonce
nonce = client.get_next_nonce(new_account.get_address())
print(f"下一个 Nonce: {nonce}")
# Nonce 说明
print(f"""
=== Nonce 说明 ===
- Nonce 是账户的交易计数器
- 每次交易后 Nonce +1
- 新账户的第一笔交易 Nonce = 1
- 交易必须按 Nonce 顺序执行
- 重复 Nonce 的交易会被拒绝
""")
任务 3.3: 构建转账交易
from aeternity.node import NodeClient, Config
from aeternity.signing import Account
from aeternity.transactions import TxBuilder
client = NodeClient(Config(external_url='https://testnet.aeternity.io'))
# 准备账户
sender = Account.generate()
recipient = Account.generate()
print(f"发送方: {sender.get_address()}")
print(f"接收方: {recipient.get_address()}")
# 获取交易构建器
tx_builder = client.tx_builder
# 获取 Nonce 和 TTL
nonce = client.get_next_nonce(sender.get_address())
ttl = client.compute_absolute_ttl(100) # 100 个区块后过期
print(f"\n交易参数:")
print(f" Nonce: {nonce}")
print(f" TTL: {ttl.absolute_ttl}")
# 构建 Spend 交易
tx = tx_builder.tx_spend(
sender_id=sender.get_address(),
recipient_id=recipient.get_address(),
amount=1000000000000000000, # 1 AE
payload="Hello from Python SDK!",
fee=0, # 0 = 自动计算
ttl=ttl.absolute_ttl,
nonce=nonce
)
print(f"\n交易信息:")
print(f" 类型: {tx.data.tag}")
print(f" 哈希: {tx.hash}")
print(f" 费用: {tx.data.fee} aettos")
# 注意: 这个交易不会成功,因为发送方没有余额
# 实际转账需要先从水龙头获取测试币
任务 3.4: 发送转账交易
# 注意: 此示例需要有余额的账户
# 可以从 https://faucet.aepps.com 获取测试币
from aeternity.node import NodeClient, Config
from aeternity.signing import Account
from aeternity.utils import format_amount
client = NodeClient(Config(
external_url='https://testnet.aeternity.io',
blocking_mode=True # 等待交易确认
))
# 转账流程(需要有余额的账户)
"""
# 1. 加载发送方账户
sender = Account.from_keystore("wallet.json", "password")
# 2. 检查余额
balance = client.get_balance(sender.get_address())
print(f"余额: {format_amount(balance)}")
# 3. 发送转账
tx = client.spend(
account=sender,
recipient_id="ak_recipient...",
amount="0.1AE", # 支持字符串格式
payload="转账备注"
)
# 4. 获取交易结果
print(f"交易哈希: {tx.hash}")
print(f"实际费用: {tx.data.fee}")
# 5. 等待确认(blocking_mode=True 时自动等待)
block_height = client.wait_for_transaction(tx.hash)
print(f"确认区块: {block_height}")
"""
任务 3.5: 金额格式转换
from aeternity.utils import amount_to_aettos, format_amount
print("=== 金额转换 ===\n")
# aettos 是 AE 的最小单位
# 1 AE = 10^18 aettos
# 转换为 aettos
test_values = [
1, # 1 aetto
1.5, # 1.5 AE
"1AE", # 1 AE
"1.5ae", # 1.5 AE
"100", # 100 aettos
0.001, # 0.001 AE
]
print("转换为 aettos:")
for val in test_values:
try:
aettos = amount_to_aettos(val)
print(f" {repr(val):15} → {aettos:>25} aettos")
except Exception as e:
print(f" {repr(val):15} → 错误: {e}")
# 格式化显示
print("\n格式化为 AE:")
test_aettos = [
1000000000000000000, # 1 AE
1500000000000000000, # 1.5 AE
100000000000000000, # 0.1 AE
]
for val in test_aettos:
formatted = format_amount(val)
print(f" {val:>25} → {formatted}")
4. 常见问题
- 检查账户余额是否足够支付金额 + 手续费
- 使用
get_balance确认当前余额 - 测试网可从 水龙头 获取测试币
# 获取正确的 Nonce
nonce = client.get_next_nonce(account.get_address())
print(f"应使用 Nonce: {nonce}")
# 使用相对 TTL,让 SDK 自动计算
ttl = client.compute_absolute_ttl(100) # 100 个区块后过期
知识检查点
- 配置和使用节点客户端
- 查询账户余额和 Nonce
- 理解交易生命周期
- 构建和发送 Spend 交易
- 等待交易确认
本页目录
预计: 3-4 小时