写代码的时候有时候只需要调用某一个合约的方法,但是又不想通过abigen生成golang的wapper类,这种情况下可以直接代码构造合约调用,来完成.
写代码的时候有时候只需要调用某一个 合约的方法,但是又不想通过abigen 生成golang的wapper类,这种情况下可以直接代码构造 合约调用,来完成.不依赖完整的 abi 文件.当然直接构造 abi字符串然后abi.JSON()
.只要理解了 ABI
的创建步骤,使用json文件创建还是代码直接创建都是可以.
// Pancakev2 is an auto generated Go binding around an Ethereum contract.
type Pancakev2 struct {
Pancakev2Caller // Read-only binding to the contract
Pancakev2Transactor // Write-only binding to the contract
Pancakev2Filterer // Log filterer for contract events
}
// Pancakev2Caller is an auto generated read-only Go binding around an Ethereum contract.
type Pancakev2Caller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// Pancakev2Transactor is an auto generated write-only Go binding around an Ethereum contract.
type Pancakev2Transactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// Pancakev2Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
type Pancakev2Filterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
使用pancakerouter的abi文件生成.go 包装类 当中定义了结构类.Pancakev2,当中包含三个成员分别是 |
名称 | 作用 |
---|---|---|
Pancakev2Caller | 绑定合约的只读操作 | |
Pancakev2Transactor | 绑定合约的写操作 | |
Pancakev2Filterer | 订阅合约的事件 |
在声明定义的时候 都包含一个*bind.BoundContract
该类并非由abigen 生成的包装类提供.接着但是看名字大概就知道它是对合约操作的封装抽象.实际的类结构如下
// BoundContract is the base wrapper object that reflects a contract on the
// Ethereum network. It contains a collection of methods that are used by the
// higher level contract bindings to operate.
type BoundContract struct {
address common.Address // Deployment address of the contract on the Ethereum blockchain
abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
caller ContractCaller // Read interface to interact with the blockchain
transactor ContractTransactor // Write interface to interact with the blockchain
filterer ContractFilterer // Event filtering to interact with the blockchain
}
在BoundContract中定义了一个Call方法,它的详细定义如下
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
看描述就能知道 是合约的只读操作的实际执行类其中的代码分为两步
1.RLP inputdata
2.根据 opts中pending是否为true 确定调用PendingCallContract
还是CallContract
后者比前者多了一个blocknumber的参数,实际上是PendingCallContract
在调用eth_call rpc的时候写死了blocknumber为pending
3.利用abi.Unpack来解析返回的数据
依据上面的大致流程,我们可以自行构造这些步骤,再将对应的数据传递给ethclient,此处以PancakeV2Router.WETH()
为例子来构造调用,该操作相当于合约的ready-only操作,至于write和deploy后面更新.
首先来看下直接使用生成的abi.go是如何调用的
client, _ := ethclient.Dial(config.RPCURL)
address := common.HexToAddress("0x10ed43c718714eb63d5aa57b78b54704e256024e")
pancakev2, _ := NewPancakev2(address, client)
weth, err := pancakev2.WETH(&bind.CallOpts{Pending: true})
if err != nil {
return
}
println(weth.String())
var inp = *new([]abi.Argument)
addrType, err := abi.NewType("address", "address", nil)
if err != nil {
panic(err)
}
oup := []abi.Argument{
abi.Argument{
Name: "address",
Type: addrType,
Indexed: false,
},
}
newMethod := abi.NewMethod("WETH", "WETH", abi.Function, "pure", false, false, inp, oup)
pack, err := newMethod.Inputs.Pack()
var data = append(newMethod.ID, pack...)
ctx := context.Background()
msg := ethereum.CallMsg{Data: data, Gas: 1e6, GasPrice: big.NewInt(5 * params.GWei), To: &address, Value: big.NewInt(0)}
contract, err := client.PendingCallContract(ctx, msg
if err != nil {
log.Fatal(err)
}
unpack, err := newMethod.Outputs.Unpack(contract)
if err != nil {
log.Fatal(err)
}
out0 := *abi.ConvertType(unpack[0], new(common.Address)).(*common.Address)
println(out0.String())
构造一个合约方法newMethod := abi.NewMethod("WETH", "WETH", abi.Function, "pure", false, false, inp, oup)
.参数含义分别 如下name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments
在计算method.ID的时候使用的是rawName.inputs,outputs分别指的输入的参数以及输出的参数.他们实际的类型都是[]abi.Argument
.构造Argument倒是满方便的直接把对应的类型抄一遍就好了,这里有个技巧就是可以对着abi.json文件看一下具体填写规则.特别是对于[]tupe 这种.暂时就想到这些,又想起来的再补充
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!