Golang 没有abi文件构造合约请求

  • nono
  • 更新于 2022-10-04 03:44
  • 阅读 3018

写代码的时候有时候只需要调用某一个合约的方法,但是又不想通过abigen生成golang的wapper类,这种情况下可以直接代码构造合约调用,来完成.

写代码的时候有时候只需要调用某一个 合约的方法,但是又不想通过abigen 生成golang的wapper类,这种情况下可以直接代码构造 合约调用,来完成.不依赖完整的 abi 文件.当然直接构造 abi字符串然后abi.JSON().只要理解了 ABI的创建步骤,使用json文件创建还是代码直接创建都是可以.

观察abigen生成的wapper.go是如何处理合约调用的

// 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")

使用abi.go调用WETH

    pancakev2, _ := NewPancakev2(address, client)
    weth, err := pancakev2.WETH(&bind.CallOpts{Pending: true})
    if err != nil {
        return
    }
    println(weth.String())

自己构造调用WETH

        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 这种.暂时就想到这些,又想起来的再补充

  • 原创
  • 学分: 12
  • 分类: Go
  • 标签: golang 
点赞 3
收藏 4
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
nono
nono
0xcC85...BC32
江湖只有他的大名,没有他的介绍。