【使用go开发区块链】之智能合约交互(02)

让我们一起学习一下如何使用abi的方式进行智能合约的调用

上一篇文章,我们讲解了go如何使用函数选择器的方式进行智能合约的调用,接下来让我们一起学习一下如何使用abi的方式进行智能合约的调用

本系列课程:

第一节:【使用go开发区块链】之智能合约交互(01)

第二节:【使用go开发区块链】之智能合约交互(02)

第三节:【使用go开发区块链】之智能合约交互(03)

1、首先我们安装一下go-ethereum

go get -u github.com/ethereum/go-ethereum

2、新建main.go文件,添加依赖

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "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"
    "math/big"
    "os"
)

3、定义常量

const (
    privateKey      = "你的钱包私钥"
    contractAddress = "调用合约地址"
    toAddress       = "接收转账地址" //这里我使用transfer方法作为案例,所以需要一个接收转账地址
)

4、定义调用函数

func transfer(client *ethclient.Client, privateKey, toAddress, contract string) (string, error) {}

4.1、先从私钥推导出公钥,再从公钥推导出钱包地址

  //从私钥推导出 公钥
    privateKeyECDSA, err := crypto.HexToECDSA(privateKey)
    if err != nil {
        fmt.Println("crypto.HexToECDSA error ,", err)
        return "", err
    }
    publicKey := privateKeyECDSA.Public()
    publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
    if !ok {
        fmt.Println("publicKeyECDSA error ,", err)
        return "", err
    }
    //从公钥推导出钱包地址
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
    fmt.Println("钱包地址:", fromAddress.Hex())

<!--StartFragment-->

4.2、构造请求参数

这里就跟使用函数选择器的方式不一样了,也是本篇文章的重点介绍内容

<!--EndFragment--> <!--StartFragment-->

4.2.1、准备好合约的abi文件 abi.json

不会获取合约abi文件 可以关注公众号:外柏叁布道者(web3_preacher)给我留言

<!--EndFragment-->

[
  {
    "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"
  },
  .....
]

<!--StartFragment-->

4.2.2、读取abi文件

<!--EndFragment-->

//读取abi文件
    abiData, err := os.ReadFile("./part2/abi.json")
    if err != nil {
        fmt.Println("os.ReadFile error ,", err)
        return "", err
    }

<!--StartFragment-->

4.2.3、将abi数据转成合约abi对象

<!--EndFragment-->

contractAbi, err := abi.JSON(bytes.NewReader(abiData))
    if err != nil {
        fmt.Println("abi.JSON error ,", err)
        return "", err
    }

<!--StartFragment-->

4.2.4、合约abi对象打包要调用的方法和参数

<!--EndFragment-->

amount, _ := new(big.Int).SetString("100000000000000000000", 10)
    data, err := contractAbi.Pack("transfer", common.HexToAddress(toAddress), amount)
    if err != nil {
        fmt.Println("contractAbi.Pack error ,", err)
        return "", err
    }

<!--StartFragment-->

实际上这步操作跟我们第一章里讲到的代码(见下面)的作用是一样的,有兴趣的朋友,可以自行阅读一下contractAbi.Pack这个方法的源码

<!--EndFragment-->

var data []byte
    methodName := crypto.Keccak256([]byte("transfer(address,uint256)"))[:4]
    paddedToAddress := common.LeftPadBytes(common.HexToAddress(toAddress).Bytes(), 32)
    amount, _ := new(big.Int).SetString("100000000000000000000", 10)
    paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
    data = append(data, methodName...)
    data = append(data, paddedToAddress...)
    data = append(data, paddedAmount...)

<!--StartFragment-->

4.3、构造交易对象

<!--EndFragment-->

    //获取nonce
    nonce, err := client.NonceAt(context.Background(), fromAddress, nil)
    if err != nil {
        return "", err
    }
    //获取小费
    gasTipCap, _ := client.SuggestGasTipCap(context.Background())
    //transfer 默认是 使用 21000 gas
    gas := uint64(100000)
    //最大gas fee
    gasFeeCap := big.NewInt(38694000460)

    contractAddress := common.HexToAddress(contract)
    //创建交易
    tx := types.NewTx(&types.DynamicFeeTx{
        Nonce:     nonce,
        GasTipCap: gasTipCap,
        GasFeeCap: gasFeeCap,
        Gas:       gas,
        To:        &contractAddress,
        Value:     big.NewInt(0),
        Data:      data,
    })

<!--StartFragment-->

4.4、交易签名/发送交易

<!--EndFragment-->

    // 获取当前区块链的ChainID
    chainID, err := client.ChainID(context.Background())
    if err != nil {
        fmt.Println("获取ChainID失败:", err)
        return "", err
    }

    fmt.Println("当前区块链的ChainID:", chainID)
    //创建签名者
    signer := types.NewLondonSigner(chainID)
    //对交易进行签名
    signTx, err := types.SignTx(tx, signer, privateKeyECDSA)
    if err != nil {
        return "", err
    }
    //发送交易
    err = client.SendTransaction(context.Background(), signTx)
    if err != nil {
        return "", err
    }

<!--StartFragment-->

4.5、main函数里来调用函数

<!--EndFragment-->

func main() {
    client, err := ethclient.Dial("https://goerli.infura.io/v3/……")
    if err != nil {
        fmt.Println("ethclient.Dial error : ", err)
        os.Exit(0)
    }
    tx, err := transfer(client, privateKey, toAddress, contractAddress)
    if err != nil {
        fmt.Println("transfer error : ", err)
        os.Exit(0)
    }

    fmt.Println("使用go调用智能合约第二讲:transfer tx : ", tx)

}

<!--StartFragment-->

 5、执行main方法

<!--EndFragment-->

image.png

<!--StartFragment-->

 我们的代码已经成功执行了,让我们去区块链浏览器了看一下

<!--EndFragment-->

image.png

image.png

<!--StartFragment-->

可以看到区块链已经确认了我们的本次交易

<!--EndFragment-->

本次教程,我们学会了如何使用go调用合约abi的方式与智能合约进行交互,如果在学习的过程中有任何问题可以在公众号给我留言,看到我会第一时间回复你的,另外公众号也会不定期分享关于区块链、web3的前沿信息,感兴趣的朋友可以保持关注

 请关注公众号:外柏叁布道者(web3_preacher),回复 “go合约调用” 领取完整代码

web3_preacher.jpg

点赞 3
收藏 3
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
外柏叁布道者
外柏叁布道者
0x6ea1...9dbe
全网同名 资深区块链专家 更多web3、区块链技术与前沿信息 请关注公众号:外柏叁布道者(web3_preacher) Web3工具网站现已上线: https://utools.me 不定时更新各种实用工具,敬请关注 接各种Dapp、合约、web3相关工具开发