使用go与智能合约交互之abi调用

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

本系列课程:

第一节:使用go与智能合约交互之函数选择器调用

第二节:使用go与智能合约交互之abi调用

第三节:使用go与智能合约交互之使用abigen生成合约go文件进行调用

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())

4.2、构造请求参数

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

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

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

[
  {
    "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"
  },
  .....
]
4.2.2、读取abi文件
//读取abi文件
	abiData, err := os.ReadFile("./part2/abi.json")
	if err != nil {
		fmt.Println("os.ReadFile error ,", err)
		return "", err
	}
4.2.3、将abi数据转成合约abi对象
contractAbi, err := abi.JSON(bytes.NewReader(abiData))
	if err != nil {
		fmt.Println("abi.JSON error ,", err)
		return "", err
	}
4.2.4、合约abi对象打包要调用的方法和参数
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
	}

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

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...)

4.3、构造交易对象

    //获取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,
	})

4.4、交易签名/发送交易

    // 获取当前区块链的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
	}

4.5、main函数里来调用函数

func main() {
	client, err := ethclient.Dial("https://goerli.infura.io/v3/3214cac49d354e48ad196cdfcefae1f8")
	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)

}

 5、执行main方法

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

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

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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,以下是一个简单的前端web3应用程序,用于与上面编写的ERC20智能合约交互: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Web3 App</title> <script src="https://cdn.jsdelivr.net/npm/web3@1.3.5/dist/web3.min.js"></script> </head> <body> <h1>Web3 App</h1> <p>Account: <span id="account"></span></p> <p>Balance: <span id="balance"></span> ERC20 tokens</p> <input id="to" type="text" placeholder="Recipient address"> <input id="value" type="text" placeholder="Amount"> <button onclick="transfer()">Transfer</button> <script> window.addEventListener('load', async () => { if (typeof window.ethereum !== 'undefined') { await window.ethereum.enable(); const web3 = new Web3(window.ethereum); const contractAddress = 'CONTRACT_ADDRESS'; const contractAbi = [{ 'constant': true, 'inputs': [{'name': '', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'name', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': false, 'inputs': [{'name': '_to', 'type': 'address'}, {'name': '_value', 'type': 'uint256'}], 'name': 'transfer', 'outputs': [{'name': 'success', 'type': 'bool'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'symbol', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': false, 'inputs': [{'name': '_spender', 'type': 'address'}, {'name': '_value', 'type': 'uint256'}], 'name': 'approve', 'outputs': [{'name': 'success', 'type': 'bool'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': true, 'inputs': [{'name': '', 'type': 'address'}, {'name': '', 'type': 'address'}], 'name': 'allowance', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': false, 'inputs': [{'name': '_from', 'type': 'address'}, {'name': '_to', 'type': 'address'}, { 'name': '_value', 'type': 'uint256' }], 'name': 'transferFrom', 'outputs': [{'name': 'success', 'type': 'bool'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'inputs': [{'name': '_name', 'type': 'string'}, {'name': '_symbol', 'type': 'string'}, { 'name': '_totalSupply', 'type': 'uint256' }], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor' }, { 'anonymous': false, 'inputs': [{'indexed': true, 'name': '_from', 'type': 'address'}, { 'indexed': true, 'name': '_to', 'type': 'address' }, {'indexed': false, 'name': '_value', 'type': 'uint256'}], 'name': 'Transfer', 'type': 'event' }, { 'anonymous': false, 'inputs': [{'indexed': true, 'name': '_owner', 'type': 'address'}, { 'indexed': true, 'name': '_spender', 'type': 'address' }, {'indexed': false, 'name': '_value', 'type': 'uint256'}], 'name': 'Approval', 'type': 'event' }]; const contract = new web3.eth.Contract(contractAbi, contractAddress); const accounts = await web3.eth.getAccounts(); const account = accounts[0]; document.getElementById('account').textContent = account; const balance = await contract.methods.balanceOf(account).call(); document.getElementById('balance').textContent = balance; transfer = async () => { const to = document.getElementById('to').value; const value = document.getElementById('value').value; await contract.methods.transfer(to, value).send({from: account}); location.reload(); }; } else { alert('Please install MetaMask to use this dApp!'); } }); </script> </body> </html> ``` 在这个应用程序中,我们使用web3.js库来与以太坊网络交互,首先需要检查MetaMask是否已安装并启用。然后,我们使用智能合约地址和ABI创建了一个智能合约实例,以便可以与合约交互。我们还获取了当前用户的帐户和智能合约中该帐户的余额,并将其显示在页面上。最后,我们定义了一个转移函数,该函数在用户输入收件人地址和金额后,调用智能合约的“transfer”函数来发送代币,并重新加载页面以更新余额。 请注意,在这个示例中,你需要将“CONTRACT_ADDRESS”替换为你部署的ERC20智能合约的地址。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

外柏叁布道者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值