赞
踩
前两篇文章,我们讲解了go如何通过函数选择器、abi调用的方式与智能合约交互,那么有没有一种更加便捷的方式(就好像调用预先定义好的方法一样)与智能合约交互呢?答案是有的,本章我们就来学习一下如何使用abigen生成合约go文件并进行调用
本系列课程:
首先我们要安装 go-ethereum,本篇文章是在windows系统下操作,其他系统操作步骤基本类似,可自行参考安装
下载完成之后 直接默认/自定义目录安装即可,安装完成后我们打开安装目录,可以看到里面有很多.exe后缀的可执行文件
右键我的电脑-》高级系统设置-》环境变量-》系统变量,找到Path这个变量,然后点编辑按钮,在里面添加我们刚刚的安装目录,如下:
然后我们打开 命令行终端,快捷键:windows+R,输入cmd,然后回车
能正确输出安装版本号证明我们环境变量已经配置成功
(本文以标准ERC20合约作为示例,不知道怎么生成的可关注公众号私信我)
- [
- {
- "inputs": [],
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "spender",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- ……
- ]
abigen --abi abi.json --pkg main --type Token --out Token.go
--abi abi.json 为我们要通过abigen生成go文件的 源文件路径
--pkg main 为我们要生成go文件里面的包名(生成的go文件第一行代码 如:package main)
--type Token 为我们生成go文件里可操作对象/方法/变量的组成部分,比如我这里写的是Token,那么生成的go文件里面的代码格式大概如下:
可以看到,里面无论是结构体、方法、变量,均会以该名字进行命名
--out Token.go 为生成go文件的名称 (ps:如果省略此命令,生成的go文件内容会打印在命令行终端上)
我们执行此命令后,可以看到当前目录下多了一个Token.go的文件,我们打开看一下:
- // Code generated - DO NOT EDIT.
- // This file is a generated binding and any manual changes will be lost.
-
- package main
-
- import (
- "errors"
- "math/big"
- "strings"
-
- ethereum "github.com/ethereum/go-ethereum"
- "github.com/ethereum/go-ethereum/accounts/abi"
- "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/event"
- )
-
- // Reference imports to suppress errors if they are not otherwise used.
- var (
- _ = errors.New
- _ = big.NewInt
- _ = strings.NewReader
- _ = ethereum.NotFound
- _ = bind.Bind
- _ = common.Big1
- _ = types.BloomLookup
- _ = event.NewSubscription
- _ = abi.ConvertType
- )
-
- // TokenMetaData contains all meta data concerning the Token contract.
- var TokenMetaData = &bind.MetaData{
- ABI: "……"
- }
- var TokenABI = TokenMetaData.ABI
-
- // Token is an auto generated Go binding around an Ethereum contract.
- type Token struct {
- TokenCaller // Read-only binding to the contract
- TokenTransactor // Write-only binding to the contract
- TokenFilterer // Log filterer for contract events
- }
- func (_Token *TokenSession) BalanceOf(account common.Address) (*big.Int, error) {
- return _Token.Contract.BalanceOf(&_Token.CallOpts, account)
- }
- func (_Token *TokenTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
- return _Token.Contract.contract.Transfer(opts)
- }
- …………
可以看到,里面定义了很多结构体以及我们在合约里定义的方法在go文件里都有具体的实现,其实就是abigen帮我们把我们合约里定义的方法转成了go可以直接调用的方法,从而简化了我们的操作
go get -u github.com/ethereum/go-ethereum
- "context"
- "fmt"
- "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"
- "math/big"
- "os"
- const (
- privateKey = "你的钱包私钥"
- contractAddress = "调用合约地址"
- toAddress = "接收转账地址" //这里我使用transfer方法作为案例,所以需要一个接收转账地址
- )
func main() {}
- client, err := ethclient.Dial("https://goerli.infura.io/v3/3214cac49d354e48ad196cdfcefae1f8")
- if err != nil {
- fmt.Println("ethclient.Dial error : ", err)
- os.Exit(0)
- }
这里的NewToken其实就是我们生成的Token.go文件里的一个方法,通过传递合约地址、ethclient对象,他便会帮我们创建一个合约对象,我们通过这个合约对象就可以很方便调用合约里定义好的各种方法
- token, err := NewToken(common.HexToAddress(contractAddress), client)
- if err != nil {
- fmt.Println("NewToken error : ", err)
- }
call方法主要用来调用智能合约的查询方法(即不修改链上数据,不花费gas)
这里,我们调用的是智能合约的TotalSupply方法,它会返回该智能合约的总供应量
- totalSupply, err := token.TotalSupply(nil)
- if err != nil {
- fmt.Println("token.TotalSupply error : ", err)
- }
- fmt.Println("totalSupply is : ", totalSupply)
可以看到,控制台成功打印出返回信息
send方法,主要用来调用智能合约里会修改链上数据的方法(花费gas)
我们以Transfer方法为例,进行一笔账号之间的转账操作:
- // 获取当前区块链的ChainID
- chainID, err := client.ChainID(context.Background())
- if err != nil {
- fmt.Println("获取ChainID失败:", err)
- return
- }
- privateKeyECDSA, err := crypto.HexToECDSA(privateKey)
- if err != nil {
- fmt.Println("crypto.HexToECDSA error ,", err)
- return
- }
-
- gasTipCap, _ := client.SuggestGasTipCap(context.Background())
-
- //构建参数对象
- opts, err := bind.NewKeyedTransactorWithChainID(privateKeyECDSA, chainID)
- if err != nil {
- fmt.Println("bind.NewKeyedTransactorWithChainID error ,", err)
- return
- }
- //设置参数
- opts.GasFeeCap = big.NewInt(108694000460)
- opts.GasLimit = uint64(100000)
- opts.GasTipCap = gasTipCap
-
- amount, _ := new(big.Int).SetString("100000000000000000000", 10)
- //调用合约transfer方法
- tx, err := token.Transfer(opts, common.HexToAddress(toAddress), amount)
- if err != nil {
- fmt.Println("token.Transfer error ,", err)
- return
- }
-
- fmt.Println("使用go调用智能合约第三讲:transfer tx : ", tx.Hash().Hex())
成功返回哈希值,让我们去区块链浏览器里看一下
可以看到链上交易已经完成
到此,使用go与智能合约的教程全部完成,你学会了吗?没有学会不要紧,多做两遍,熟能生巧,你一定也可以的!如果在学习过程中有任何问题,欢迎给我留言,另外公众号也会不定期分享关于区块链、web3的前沿信息,感兴趣的朋友可以保持关注
请关注公众号:外柏叁布道者(web3_preacher),回复 “go合约调用” 领取完整代码
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。