在以太坊开发中,转 ETH 轻而易举,然而 ERC-20 代币转账 却因为多了“智能合约调用”这一步,常让 Go 开发者们犯难。本文将带你从 0 开始,用 Go 语言完成一次链上代币转账,并穿插关键词优化、避坑提醒与扩展阅读,确保即使是第一次接触的你,也能理解 区块链转账、智能合约交互、gas 估算 等概念,顺畅地把 1000 枚代币发出去。
前置准备
1. 环境与依赖
- 安装 Go 1.20+
- 引入以太坊官方 Go 版本 go-ethereum
- 已配置好的 Rinkeby 节点(或任何测试网皆可)
2. 准备好 Go 项目骨架
确保已:
- 连接 Ethereum 客户端
- 导入私钥并解锁账户
- 设置默认 gasPrice
👉 体验一键为 Rinkeby 获取免费 ETH 的方法 — 全网最简单新手指南。
创建测试代币
在正式编写 代币转账 逻辑前,必须有一个可以调用的 ERC-20 合约。最常见的方式是使用 Token Factory:
- 打开浏览器访问 Token Factory
- 填写代币名称、符号、供应量
- 部署到 Rinkeby,记录代币合约地址
示例地址:0x28b149020d2152179873ec60bed6bf7cd705775d
(HelloToken HTN)
提示:创建完成后,把少量代币先转到自己的钱包地址,才能在后续步骤中真正转出。
核心:转账流程拆解
第一步:确定交易字段
ETH 金额为 0
value := big.NewInt(0) // 不转 ETH,只触合约
接收地址
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
听得懂却写不出?常见问题在文末 FAQ 集中解答。
第二步:构造 data 字段
区块链上的转账要“跟合约对话”,就得把对话内容塞进 交易 data。具体做法如下:
找到函数签名
transferFnSignature := []byte("transfer(address,uint256)")
计算方法 ID(前 4 字节 Keccak256 哈希)
methodID := sha3.NewLegacyKeccak256().Sum(nil)[:4]
对齐地址与金额(32 字节)
paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32) paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
拼接
data := append(methodID, append(paddedAddress, paddedAmount...)...)
第三步:估算 gasLimit
随意给一个 gasLimit 易导致交易失败。推荐用 EstimateGas 让节点根据最新区块状态自动测算:
callMsg := ethereum.CallMsg{
To: &tokenAddress,
Data: data,
}
gasLimit, err := client.EstimateGas(ctx, callMsg)
省心且省币!如果估算值反复失败,可手动 +20% 作为冗余。
第四步:构建、签名、广播
接着,即可“照搬”转 ETH 代码模板,只需三处改动:
- to 用代币合约地址
- value 填 0
- data 用上一步拼好的字节数组
tx := types.NewTransaction(nonce, tokenAddress, big.NewInt(0), gasLimit, gasPrice, data)
signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
client.SendTransaction(ctx, signedTx)
FAQ:常见疑问速查
Q1:为什么合约地址是收款对象?
A:ERC-20 的 transfer
函数是写在合约里的。把整个调用“包”进交易,发到合约地址即可。
Q2:amount 前端显示 1000,代码却要乘 1e18?
A:大多数代币 decimals=18。合约层面只认最小单位,必须在代码里乘 10^decimals。
Q3:EstimateGas 超限怎么办?
A:优先检查 data 拼错、地址填错;若确认无误,再手动提高 gasLimit 即可。
Q4:链是 Rinkeby,可否直接用 mainnet?
A:流程一致,只需替换节点 RPC 与链 ID。强烈建议测试通过后再迁主网。
Q5:怎么验证转成功没?
A:复制交易哈希到区块浏览器,例如 Rinkeby Etherscan,状态 “Success” 即到账。
Q6:如何动态加载代币 ABI?
A:可用 accounts/abi/bind
包自动生成绑定代码,下一篇「与 智能合约 高级交互」章节会细讲。
完整可直接跑的源码
以下文件保存为 transfer_tokens.go
:
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"golang.org/x/crypto/sha3"
)
func main() {
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY") // 替换为自己的私钥
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA := publicKey.(*ecdsa.PublicKey)
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
gasPrice, _ := client.SuggestGasPrice(context.Background())
to := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
tokenAddress := common.HexToAddress("0x28b149020d2152179873ec60bed6bf7cd705775d")
transferFnSignature := []byte("transfer(address,uint256)")
hash := sha3.NewLegacyKeccak256()
hash.Write(transferFnSignature)
methodID := hash.Sum(nil)[:4]
paddedAddress := common.LeftPadBytes(to.Bytes(), 32)
amount := new(big.Int)
amount.SetString("1000000000000000000000", 10) // 1000 枚带 18 位小数的代币
paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
var data []byte
data = append(append(data, methodID...), append(paddedAddress, paddedAmount...)...)
gasLimit, _ := client.EstimateGas(context.Background(), ethereum.CallMsg{
To: &tokenAddress,
Data: data,
})
tx := types.NewTransaction(nonce, tokenAddress, big.NewInt(0), gasLimit, gasPrice, data)
chainID, _ := client.NetworkID(context.Background())
signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("交易哈希:%s\n", signedTx.Hash().Hex())
}
拓展阅读
- EIP-20 官方规范 — 理解 decimals、totalSupply、balanceOf、transferFrom 等函数
- 深入了解 区块链开发 可以阅读官方 go-ethereum 源码
- 想一步到“主网实战”,务必提前装好 硬件钱包 与冷备份方案