Day 4

Spend 交易

中级阶段 · 预计学习时间 3-4 小时

学习目标
  • 理解交易生命周期
  • 使用 TTLNoncer 管理 Nonce
  • 创建、签名、广播 SpendTx
交易生命周期
步骤说明
1. 构建创建交易结构(发送方、接收方、金额等)
2. 签名使用发送方私钥签名交易哈希
3. 广播将签名后的交易发送到节点
4. 确认等待交易被打包进区块
TTLNoncer 辅助工具

创建交易需要有效的 Nonce(账户序号)和 TTL(有效期)。SDK 提供 TTLNoncer 自动从节点获取这些值。

import "github.com/aeternity/aepp-sdk-go/v9/transactions"

// 创建 TTLNoncer
ttlnoncer := transactions.NewTTLNoncer(node)
创建 Spend 交易
package main

import (
    "fmt"
    "log"
    "math/big"

    "github.com/aeternity/aepp-sdk-go/v9/account"
    "github.com/aeternity/aepp-sdk-go/v9/config"
    "github.com/aeternity/aepp-sdk-go/v9/naet"
    "github.com/aeternity/aepp-sdk-go/v9/transactions"
)

func main() {
    // 1. 设置节点和账户
    config.Node.NetworkID = "ae_uat"
    node := naet.NewNode("https://testnet.aeternity.io", false)
    
    // 加载发送方账户
    alice, err := account.LoadFromKeyStoreFile("alice.json", "password")
    if err != nil {
        log.Fatal(err)
    }
    
    // 接收方地址
    bobAddress := "ak_..." // 替换为实际地址

    // 2. 创建 TTLNoncer
    ttlnoncer := transactions.NewTTLNoncer(node)

    // 3. 创建 Spend 交易
    amount := big.NewInt(1000000000000000000) // 1 AE (18位小数)
    payload := []byte("Payment for coffee")
    
    tx, err := transactions.NewSpendTx(
        alice.Address,  // 发送方
        bobAddress,     // 接收方
        amount,         // 金额
        payload,        // 附加数据
        ttlnoncer,      // TTLNoncer
    )
    if err != nil {
        log.Fatal("创建交易失败:", err)
    }

    fmt.Printf("交易已创建, Fee: %s\n", tx.Fee)
}
签名和广播
    // 4. 签名交易
    // 需要 Network ID 防止重放攻击
    networkID := config.Node.NetworkID
    
    signedTx, txHash, _, err := transactions.SignHashTx(alice, tx, networkID)
    if err != nil {
        log.Fatal("签名失败:", err)
    }

    // 5. 序列化交易
    txStr, err := transactions.SerializeTx(signedTx)
    if err != nil {
        log.Fatal("序列化失败:", err)
    }

    // 6. 广播到网络
    err = node.PostTransaction(txStr, txHash)
    if err != nil {
        log.Fatal("广播失败:", err)
    }

    fmt.Printf("✅ 交易已发送!\n")
    fmt.Printf("Hash: %s\n", txHash)
自定义费用

SDK 自动计算最低费用,但你可以手动设置更高的费用以加快确认。

// 创建交易后,修改费用
customFee := big.NewInt(20000000000000) // 自定义费用
tx.SetFee(customFee)

// 然后继续签名和广播...
完整转账流程
func sendAE(node *naet.Node, sender *account.Account, 
           recipient string, amountAE float64, message string) (string, error) {
    
    // 转换为 aettos
    aettos := new(big.Float).SetFloat64(amountAE)
    aettos.Mul(aettos, big.NewFloat(1e18))
    amount, _ := aettos.Int(nil)
    
    // 创建交易
    ttlnoncer := transactions.NewTTLNoncer(node)
    tx, err := transactions.NewSpendTx(sender.Address, recipient, amount, 
                                        []byte(message), ttlnoncer)
    if err != nil {
        return "", err
    }
    
    // 签名
    signedTx, txHash, _, err := transactions.SignHashTx(sender, tx, config.Node.NetworkID)
    if err != nil {
        return "", err
    }
    
    // 广播
    txStr, _ := transactions.SerializeTx(signedTx)
    err = node.PostTransaction(txStr, txHash)
    
    return txHash, err
}

// 使用
hash, err := sendAE(node, alice, bobAddress, 1.5, "Coffee payment")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("交易哈希: %s\n", hash)
知识检查点