14. Go-Ethereum测试Solidity ERC20合约 - Go-Ethereum调用区块链方法

Go-Ethereum测试Solidity ERC20合约 - Go-Ethereum调用区块链方法

  • [1. 环境配置和合约代码](#1. 环境配置和合约代码)
  • [2. 编写调试代码](#2. 编写调试代码)
  • [3. 合约部署到Solidity](#3. 合约部署到Solidity)

系列文章
1. Remix编写、编译、部署、测试Solidity ERC20合约 - 基础篇
2. Remix编写、编译、部署、测试Solidity ERC20合约 - 进阶篇
3. Metamask导入代币,转账ETH,转账代币
4. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 基础篇
5. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - web3.js调用合约方法
6. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - web3.js调用区块链方法
7. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - JSON-RPC调用合约方法
8. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - JSON-RPC调用区块链方法
9. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 总结

1. 环境配置和合约代码

参考文章12. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - Metamask导入代币,转账ETH,转账代币

GoLand 2025.2.4版本

go 1.25.4

2. 编写调试代码

可连接hardhat node本地网络和sepolia测试网络

为了突出业务逻辑,省略了错误处理。

对区块链的读操作比较简单,同web3.js一样,底层构造JSON-RPC结构。

对区块链的写操作,比web3.js复杂,需要构造交易的JSON-RPC结构。web3.js接口内部实现了组装细节。

clike 复制代码
需要传入转账接收地址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: 不需要
}
接收端根据签名获得发送账户地址
clike 复制代码
package main

import (
	"es/geth"
)

func main() {
	gethClient := (&geth.Geth{}).NewClient()
	gethClient.BlockChain()
}
clike 复制代码
package geth

import (
	"context"
	"crypto/ecdsa"
	"es/contract"
	"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(1e15)
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)

	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
}

3. 合约部署到Solidity

hardhat配置文件

clike 复制代码
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

相关推荐
wjs20242 小时前
C 标准库 - `<assert.h>`
开发语言
小明的小名叫小明2 小时前
Solidity入门(15)- Foundry框架
区块链·foundry
k***92162 小时前
Linux网络编程】应用层协议HTTP(实现一个简单的http服务)
java·开发语言·学习
风送雨2 小时前
八周Python强化计划(六)
开发语言·python
zpjing~.~2 小时前
iframe和父页面消息通信
开发语言·前端·javascript
老华带你飞2 小时前
电影购票|基于java+ vue电影购票系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
老华带你飞2 小时前
宠物管理|基于java+ vue宠物管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·宠物
Larry_Yanan3 小时前
Qt多进程(六)共享内存和信号量
开发语言·qt
BD_Marathon3 小时前
bean基础配置
java·开发语言