Go语言实现以太坊转账,从环境搭建到代码实战全指南

投稿 2026-02-24 17:57 点击数: 26

以太坊作为全球第二大公链,其原生代币ETH的转账操作是区块链开发中最基础也最核心的功能之一,本文将详细介绍如何使用Go语言结合以太坊官方go-ethereum库,实现从环境搭建、账户管理到交易发送的完整转账流程,帮助开发者快速掌握Go与以太坊交互的实战技巧。

前置准备:环境与依赖安装

在开始编码前,需确保以下环境已配置完成:

  1. Go环境:建议安装Go 1.16及以上版本,可通过go version验证安装。
  2. 以太坊节点:可选择连接公共节点(如Infura、Alchemy)或本地运行节点(如Geth),本文以公共节点为例,需注册获取节点URL(如https://mainnet.infura.io/v3/YOUR_PROJECT_ID)。
  3. 依赖库:安装go-ethereum库,这是Go语言与以太坊交互的核心工具包:
    go get -u github.com/ethereum/go-ethereum
    go get -u github.com/ethereum/go-ethereum/crypto
    go get -u github.com/ethereum/go-ethereum/common
    go get -u github.com/ethereum/go-ethereum/accounts/abi/bind

核心概念与账户准备

以太坊转账本质上是发送一笔包含value(转账金额)和data(可选数据)的交易,需调用eth_sendRawTransaction接口,实现转账需明确以下要素:

  1. 发送方账户:需拥有ETH的账户,并掌握其私钥(用于签名交易)和地址

    • 账户可通过随机配图

de>geth命令行创建,或使用go-ethereumcrypto包生成:

import "github.com/ethereum/go-ethereum/crypto"
privateKey, err := crypto.GenerateKey()
if err != nil {
    log.Fatal(err)
}
address := crypto.PubkeyToAddress(privateKey.PublicKey).Hex()
fmt.Println("生成的地址:", address)
  • 注意:私钥是账户的唯一凭证,严禁硬编码在代码中,建议通过环境变量或加密文件存储。

  • 接收方地址:需验证地址格式是否正确(以0x开头,42位十六进制字符)。

  • Go语言实现以太坊转账步骤

    以下是完整的转账代码实现,分为连接节点、构建交易、签名交易、发送交易四个核心环节。

    初始化客户端,连接以太坊节点

    使用go-ethereumethclient包连接节点:

    package main
    import (
        "context"
        "crypto/ecdsa"
        "fmt"
        "log"
        "math/big"
        "github.com/ethereum/go-ethereum/common"
        "github.com/ethereum/go-ethereum/core/types"
        "github.com/ethereum/go-ethereum/crypto"
        "github.com/ethereum/go-ethereum/ethclient"
    )
    func main() {
        // 替换为你的Infura节点URL
        client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")
        if err != nil {
            log.Fatal(err)
        }
        defer client.Close()
        fmt.Println("成功连接到以太坊节点")
    }

    加载发送方私钥与地址

    假设已有发送方私钥(需从安全来源加载,如环境变量):

    // 从环境变量或配置文件加载私钥(示例中直接使用字符串,实际需注意安全)
    privateKeyHex := "YOUR_PRIVATE_KEY" // 替换为真实的私钥(以0x开头)
    privateKey, err := crypto.HexToECDSA(privateKeyHex[2:]) // 去掉0x前缀
    if err != nil {
        log.Fatal("私钥解析失败:", err)
    }
    senderAddress := crypto.PubkeyToAddress(privateKey.PublicKey)
    fmt.Println("发送方地址:", senderAddress.Hex())

    获取当前nonce与gas参数

    交易发送前需获取发送方的nonce(账户交易序列号,防止交易重放)和预估的gas费用(包括gasLimitgasPrice):

    // 获取nonce
    nonce, err := client.PendingNonceAt(context.Background(), senderAddress)
    if err != nil {
        log.Fatal("获取nonce失败:", err)
    }
    // 设置gasPrice(单位:Gwei,1 ETH = 1e9 Gwei)
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal("获取gasPrice失败:", err)
    }
    // 设置gasLimit(普通转账建议21000)
    gasLimit := uint64(21000)
    // 接收方地址(示例中为以太坊官方测试地址)
    receiverAddress := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f8e524")

    构建交易数据

    以太坊交易的核心数据包括:接收方地址、转账金额(value)、noncegasPricegasLimit,转账金额需以wei为单位(1 ETH = 1e18 wei):

    // 转账金额(示例:0.01 ETH)
    value := big.NewInt(0)
    value.Mul(value, big.NewInt(1e18)) // 0.01 ETH = 1e16 wei
    // 构建交易
    tx := types.NewTransaction(nonce, receiverAddress, value, gasLimit, gasPrice, nil)

    签名交易

    使用发送方私钥对交易进行签名,确保交易的有效性:

    // 获取链ID(用于签名,防止跨链交易重放)
    chainID, err := client.NetworkID(context.Background())
    if err != nil {
        log.Fatal("获取链ID失败:", err)
    }
    // 签名交易
    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal("签名交易失败:", err)
    }

    发送交易并等待上链

    将签名后的交易发送到以太坊网络,并可通过交易哈希查询状态:

    // 发送交易
    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal("发送交易失败:", err)
    }
    fmt.Printf("交易发送成功!交易哈希: %s\n", signedTx.Hash().Hex())
    // 等待交易上链(可选)
    receipt, err := client.TransactionReceipt(context.Background(), signedTx.Hash())
    if err != nil {
        log.Fatal("获取交易回执失败:", err)
    }
    if receipt.Status == 1 {
        fmt.Println("交易上链成功!区块号:", receipt.BlockNumber.String())
    } else {
        fmt.Println("交易上链失败!")
    }

    完整代码与注意事项

    将上述步骤整合后,完整代码如下(需替换私钥、节点URL等参数):

    package main
    import (
        "context"
        "crypto/ecdsa"
        "fmt"
        "log"
        "math/big"
        "github.com/ethereum/go-ethereum/common"
        "github.com/ethereum/go-ethereum/core/types"
        "github.com/ethereum/go-ethereum/crypto"
        "github.com/ethereum/go-ethereum/ethclient"
    )
    func main() {
        // 1. 连接节点
        client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")
        if err != nil {
            log.Fatal(err)
        }
        defer client.Close()
        // 2. 加载私钥与地址
        privateKeyHex := "YOUR_PRIVATE_KEY"
        privateKey, err := crypto.HexToECDSA(privateKeyHex[2:])
        if err != nil {
            log.Fatal(err)
        }
        senderAddress := crypto.PubkeyToAddress(privateKey.PublicKey)
        // 3. 获取nonce与gas参数
        nonce, err := client.PendingNonceAt(context.Background(), senderAddress)
        if err != nil {
            log.Fatal(err)
        }
        gasPrice, err := client.SuggestGasPrice(context.Background())
        if err != nil {
            log.Fatal(err)
        }
        gasLimit := uint64(21000)
        // 4. 构建交易
        receiverAddress := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f8e524")
        value := big.NewInt(1e16) // 0.01 ETH
        tx := types.NewTransaction(nonce, receiverAddress, value, gasLimit, gasPrice, nil)
        // 5. 签名交易
        chainID, err := client.NetworkID(context.Background())
        if err != nil {
            log.Fatal(err)
        }
        signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
        if err != nil {
            log.Fatal(err)
        }
        // 6. 发