Go-Ethereum测试Solidity ERC20合约 - Go-Ethereum调用区块链方法
1. Remix编写、编译、部署、测试Solidity ERC20合约 - 基础篇
2. Remix编写、编译、部署、测试Solidity ERC20合约 - 进阶篇
3. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 基础篇
4. JSON-RPC调用区块链方法
5. JSON-RPC调用合约方法
6. JSON-RPC给合约账户同时转以太币和代币
7. web3.js调用区块链方法
8. web3.js调用合约方法
9. web3.js给合约账户同时转以太币和代币
10. Go-Ethereum调用区块链方法
11. Go-Ethereum调用合约方法
12. sendTransaction和sendRawTransaction区别
13. Metamask导入代币,转账ETH,转账代币 - 界面操作
14. Metamask连接hardhat本地节点
15. DAPP react界面-web3.js库-Metemask调用和显示-调用合约方法
16. web3js结合sepolia测试网
17. 总结
1. 环境配置和合约代码
参考文章12. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - Metamask导入代币,转账ETH,转账代币
GoLand 2025.2.4版本
go 1.25.4
2. 编写调试代码
可连接hardhat node本地网络和sepolia测试网络
为了突出业务逻辑,省略了错误处理。

2.1 hardhat node同以太坊主网、sepolia测试网络区别
hardhat node可以使用sendTransaction方法通信,会初始化10个持有私钥的账户。
以太坊主网、sepolia测试网络等不持有账户私钥,不支持sendTransaction方法通信。
hardhat node、以太坊主网、sepolia测试网络等都支持sendRawTransaction方法通信。

2.2 sendTransaction和sendRawTransaction区别
2.2.1 sendTransaction

sendTransaction方式。
发送原始JSON-RPC结构,以太坊接收节点持有私钥,对原始JSON-RPC结构进行签名。然后将签名后的数据广播到其他节点。
func (this *Geth) sendTransaction() {
// 获取底层的 rpc.Client
rpcClient := this.ethe.Client()
// 发送交易
tx := map[string]interface{}{
"from": fromAddress,
"to": toAddress,
"value": valueETH, // 0.1 ETH
}
var txHash common.Hash
_ = rpcClient.Call(&txHash, "eth_sendTransaction", tx)
}
调试代码,看到Go-Ethereum框架底层构建JSON-RPC结构,params字段是原始交易对象

2.2.2 sendRawTransaction

sendRawTransaction方式
发送私钥签名后的数据,以太坊节点收到后,将签名后的数据广播到其他节点。
// 区块链交易。hardhat node或sepolia,交易结构中的参数都需要,底层调用eth_sendRawTransaction,需要Nonce,需要私钥签名,不需要from。从私钥推导出账户地址
// web3.js web3.eth.sendTransaction底层调用eth_sendTransaction,节点自动填充Nonce和from。hardhat node不验证私钥签名,用默认第一个账户。但sepolia需要私钥签名,从私钥推导出账户地址
func (this *Geth) sendRawTransaction(toAddress common.Address, valueETH *big.Int, privateKey string) {
// 解析私钥
privateKey_, _ := crypto.HexToECDSA(privateKey[2:])
// 获取公钥
publicKey := privateKey_.Public()
// 账户地址
fromAddress := crypto.PubkeyToAddress(*publicKey.(*ecdsa.PublicKey))
nonce, _ := this.ethe.PendingNonceAt(context.Background(), fromAddress)
gasPrice, _ := this.ethe.SuggestGasPrice(context.Background())
// 创建交易
tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: &toAddress,
Value: valueETH,
GasPrice: gasPrice,
Gas: 21000,
})
chainID, _ := this.ethe.NetworkID(context.Background())
// 签名
signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey_)
// 发送交易
_ = this.ethe.SendTransaction(context.Background(), signedTx)
// 带超时的上下文
waitCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// 等待挖矿成功
_, _ = bind.WaitMined(waitCtx, this.ethe, signedTx)
}
调试代码,看到Go-Ethereum框架底层构建JSON-RPC结构,params字段是私钥签名后的数据

需要传入转账接收地址toAddress,转账金额valueETH,私钥privateKey。其中privateKey可以获得v,r,s。其他参数可以通过接口获得。
{
Nonce: nonce,
To: &toAddress,
Value: valueETH,
GasPrice: gasPrice,
Gas: 21000,
}
{
to: toAddress,
value: valueETH,
v,r,s: privateKey计算签名,
nonce: 可内部获取,
gas: 可内部获取,
gasPrice:可内部获取,
data: 不需要
}
接收端根据签名获得发送账户地址
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"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"
)
var privateKey = "你自己的私钥"
var fromAddress = common.HexToAddress("0xc312c07abEb8246510412a7ce87A295E0ceC5D48")
var toAddress = common.HexToAddress("0xE78Ff27498c9a6Fd8BC3ff8170Ecf9a13ECBE49e")
var contractAddress = common.HexToAddress("0x5FbDB2315678afecb367f032d93F642f64180aa3")
var valueETH = big.NewInt(1e18)
var valueMTK = big.NewInt(1e18)
var isHardhatNode = true
var url = ""
type Geth struct {
// 区块链调用
ethe *ethclient.Client
}
func (this *Geth) init() {
// hardhat 本地节点
if isHardhatNode {
// hardhat 默认账户地址1私钥
privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
// hardhat 默认账户地址1
fromAddress = common.HexToAddress("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")
// hardhat 默认账户地址2、合约地址、Metamask 账户地址
toAddress = common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
valueETH = big.NewInt(1e18) // 1ETH
url = "http://localhost:8545"
} else { // sepolia 测试网络节点
// Metamask 账户地址1私钥
privateKey = "你自己的私钥"
// Metamask 账户地址1
fromAddress = common.HexToAddress("0xc312c07abEb8246510412a7ce87A295E0ceC5D48")
// Metamask 账户地址2
toAddress = common.HexToAddress("0xE78Ff27498c9a6Fd8BC3ff8170Ecf9a13ECBE49e")
contractAddress = common.HexToAddress("0x451Dc02Cee616361815253C858Df0a3028c42901")
valueETH = big.NewInt(1e15) // 10 Gwei
url = "https://ethereum-sepolia-rpc.publicnode.com"
}
}
func (this *Geth) NewClient() *Geth {
this.init()
// 连接网络
this.ethe, _ = ethclient.Dial(url)
return this
}
// 区块链操作
func (this *Geth) BlockChain() {
id, _ := this.ethe.ChainID(context.Background())
fmt.Println(id)
balance, _ := this.ethe.BalanceAt(context.Background(), fromAddress, nil)
fmt.Println("发送前", this.weiToETH(balance))
balance, _ = this.ethe.BalanceAt(context.Background(), toAddress, nil)
fmt.Println("发送前", this.weiToETH(balance))
/*
{
to: toAddress,
value: valueETH,
v,r,s: privateKey计算签名,
nonce: 可内部获取,
gas: 可内部获取,
gasPrice:可内部获取,
data: 不需要
}
接收端根据签名获得发送账户地址
*/
//this.transaction(toAddress, valueETH, privateKey)
this.sendRawTransaction()
balance, _ = this.ethe.BalanceAt(context.Background(), fromAddress, nil)
fmt.Println("发送后", this.weiToETH(balance))
balance, _ = this.ethe.BalanceAt(context.Background(), toAddress, nil)
fmt.Println("发送后", this.weiToETH(balance))
}
// 区块链交易。hardhat node或sepolia,交易结构中的参数都需要,底层调用eth_sendRawTransaction,需要Nonce,需要私钥签名,不需要from。从私钥推导出账户地址
// web3.js web3.eth.sendTransaction底层调用eth_sendTransaction,节点自动填充Nonce和from。hardhat node不验证私钥签名,用默认第一个账户。但sepolia需要私钥签名,从私钥推导出账户地址
func (this *Geth) transaction(toAddress common.Address, valueETH *big.Int, privateKey string) {
// 解析私钥
privateKey_, _ := crypto.HexToECDSA(privateKey[2:])
// 获取公钥
publicKey := privateKey_.Public()
// 账户地址
fromAddress := crypto.PubkeyToAddress(*publicKey.(*ecdsa.PublicKey))
nonce, _ := this.ethe.PendingNonceAt(context.Background(), fromAddress)
gasPrice, _ := this.ethe.SuggestGasPrice(context.Background())
// 创建交易
tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: &toAddress,
Value: valueETH,
GasPrice: gasPrice,
Gas: 21000,
})
chainID, _ := this.ethe.NetworkID(context.Background())
// 签名
signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey_)
// 发送交易
_ = this.ethe.SendTransaction(context.Background(), signedTx)
// 带超时的上下文
waitCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// 等待挖矿成功
_, _ = bind.WaitMined(waitCtx, this.ethe, signedTx)
}
// wei转ETH
func (this *Geth) weiToETH(wei *big.Int) *big.Float {
return new(big.Float).Quo(new(big.Float).SetInt(wei), big.NewFloat(1e18))
}
// ETH转wei
func (this *Geth) EthToWei(eth float64) *big.Int {
wei, _ := new(big.Int).SetString(new(big.Float).Mul(big.NewFloat(eth), big.NewFloat(1e18)).Text('f', 0), 10)
return wei
}
func (this *Geth) sendRawTransaction() {
// 获取底层的 rpc.Client
rpcClient := this.ethe.Client()
// 发送交易
tx := map[string]interface{}{
"from": fromAddress,
"to": toAddress,
"value": valueETH, // 0.1 ETH
}
var txHash common.Hash
_ = rpcClient.Call(&txHash, "eth_sendTransaction", tx)
}
3. 合约部署到Solidity
hardhat配置文件
require("@nomicfoundation/hardhat-toolbox");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.28",
networks: {
hardhat: {
},
localhost: {
url: "http://localhost:8545"
},
sepolia: {
url: "https://ethereum-sepolia-rpc.publicnode.com",
accounts: ["你自己的私钥"],
chainId: 11155111,
}
}
};
hh run ignition\modules\Mytoken.js --network localhost

连接sepolia


5020

被折叠的 条评论
为什么被折叠?



