15. Go-Ethereum测试Solidity ERC20合约 - Go-Ethereum调用合约方法

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

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

系列文章
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

下载geth-alltools-windows-amd64-1.16.7-b9f3a3d9

2. 编写调试代码

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

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

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

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

将合约编译后的abi内容编译成go语言类型,将Contract.go放入项目中调用

clike 复制代码
 abigen.exe --abi .\Contract.abi --pkg Ethe --type Contract --out Contract.go 
clike 复制代码
需要传入转账接收地址toAddress,转账金额valueMTK,私钥privateKey。其中privateKey可以获得v,r,s。其他参数可以通过接口获得。
{
	to: 合约地址,
	data: function(toAddress, valueMTK),
	v,r,s: privateKey计算签名,
	nonce: 可内部获取,
	gas: 可内部获取,
	gasPrice:可内部获取,
	value: 不需要
}
接收端根据签名获得发送账户地址
框架的交易对象中有from属性,只是为了便于构建和签名交易的元数据,不用来构造JSON-RPC结构
type TransactOpts struct {
  From       common.Address  // ❌ 从私钥推导,不会直接放入交易,为了便于构建和签名交易的元数据
   Nonce      *big.Int        // 交易的一部分
   Signer     SignerFn        // 签名函数
   Value      *big.Int        // 交易的一部分
   GasPrice   *big.Int        // 交易的一部分
   // ... 其他字段
}
clike 复制代码
package main

import (
	"es/geth"
)

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

import (
	"context"
	"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/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
	// 合约调用
	contract *contract.Contract
}

func (this *Geth) init() {
	// hardhat 本地节点
	if isHardhatNode {
		// hardhat 默认账户地址1私钥
		privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
		// hardhat 默认账户地址1
		fromAddress = common.HexToAddress("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")
		// hardhat 默认账户地址2、合约地址、Metamask 账户地址
		toAddress = common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
		contractAddress = common.HexToAddress("0x5FbDB2315678afecb367f032d93F642f64180aa3")
		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)
	// 合约初始化
	this.contract, _ = contract.NewContract(contractAddress, this.ethe)

	return this
}

// 合约操作
func (this *Geth) Contract() {

	name, _ := this.contract.Name(nil)
	fmt.Println("name:", name)

	symbol, _ := this.contract.Symbol(nil)
	fmt.Println("symbol:", symbol)

	balance, _ := this.contract.BalanceOf(nil, fromAddress)
	fmt.Println("发送前", this.weiToETH(balance))
	balance, _ = this.contract.BalanceOf(nil, toAddress)
	fmt.Println("发送前", this.weiToETH(balance))

	/*
		{
			to: 合约地址,
			data: function(toAddress, valueMTK),
			v,r,s: privateKey计算签名,
			nonce: 可内部获取,
			gas: 可内部获取,
			gasPrice:可内部获取,
			value: 不需要
		}
		接收端根据签名获得发送账户地址
	*/
	tx, _ := this.contract.Transfer(this.transfer(privateKey), toAddress, valueMTK)
	waitCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	// 等待挖矿成功
	_, _ = bind.WaitMined(waitCtx, this.ethe, tx)

	balance, _ = this.contract.BalanceOf(nil, fromAddress)
	fmt.Println("发送后", this.weiToETH(balance))
	balance, _ = this.contract.BalanceOf(nil, toAddress)
	fmt.Println("发送后", this.weiToETH(balance))

}

// wei转ETH
func (this *Geth) weiToETH(wei *big.Int) *big.Float {
	return new(big.Float).Quo(new(big.Float).SetInt(wei), big.NewFloat(1e18))
}

// 私钥签名
func (this *Geth) transfer(privateKey string) *bind.TransactOpts {
	chainId, _ := this.ethe.ChainID(context.Background())
	privateKey_, _ := crypto.HexToECDSA(privateKey[2:])

	/*
		type TransactOpts struct {
		    From       common.Address  // ❌ 从私钥推导,不会直接放入交易,为了便于构建和签名交易的元数据
		    Nonce      *big.Int        // 交易的一部分
		    Signer     SignerFn        // 签名函数
		    Value      *big.Int        // 交易的一部分
		    GasPrice   *big.Int        // 交易的一部分
		    // ... 其他字段
		}
	*/
	opts, _ := bind.NewKeyedTransactorWithChainID(privateKey_, chainId)

	return opts
}

hh run ignition\modules\Mytoken.js --network localhost

连接sepolia

相关推荐
一条咸鱼_SaltyFish5 分钟前
远程鉴权中心设计:HTTP 与 gRPC 的技术决策与实践
开发语言·网络·网络协议·程序人生·http·开源软件·个人开发
我即将远走丶或许也能高飞17 分钟前
vuex 和 pinia 的学习使用
开发语言·前端·javascript
沐知全栈开发24 分钟前
SQL LEN() 函数详解
开发语言
钟离墨笺38 分钟前
Go语言--2go基础-->基本数据类型
开发语言·前端·后端·golang
shhpeng1 小时前
go gtk 开发入门
golang
小郭团队1 小时前
1_7_五段式SVPWM (传统算法反正切+DPWM3)算法理论与 MATLAB 实现详解
开发语言·嵌入式硬件·算法·matlab·dsp开发
C+-C资深大佬1 小时前
C++风格的命名转换
开发语言·c++
No0d1es1 小时前
2025年粤港澳青少年信息学创新大赛 C++小学组复赛真题
开发语言·c++
点云SLAM2 小时前
C++内存泄漏检测之手动记录法(Manual Memory Tracking)
开发语言·c++·策略模式·内存泄漏检测·c++实战·new / delete
码上成长2 小时前
JavaScript 数组合并性能优化:扩展运算符 vs concat vs 循环 push
开发语言·javascript·ecmascript