在本地以太坊私链上,使用go调用智能合约,获取事件日志

1、关于开发环境搭建配置等可参考之前的文章
2、部署合约代码erc20.js

const hre = require("hardhat");
async function main() {
  const CONTRACT = await hre.ethers.getContractFactory("ERC20");

  const contract = await CONTRACT.deploy();
  await contract.init("ERC20Name","ERC20Symbol");
  console.log("name:",contract.name(),"symbol:",contract.symbol());
  await contract.deployed();

  console.log(`contract deployed to ${contract.address}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

3、启动并上链

#切换到智能合约项目位置
npx hardhat node
#新开一个窗口,确保localhost已经在hardhat.config.js中配置了,可查看第一步链接对照
npx hardhat run scripts/erc.js --network localhost


4、新建一个文件夹,存放go项目,完成mod初始化等

完整go项目文件目录在本地以太坊私链上,使用go调用智能合约,获取事件日志_第1张图片


5、拷贝智能合约compile产生的ABI,在新文件夹中新建一个erc20.json文件

 

在本地以太坊私链上,使用go调用智能合约,获取事件日志_第2张图片

 

6、安装abigen

go get github.com/ethereum/go-ethereum

#切换路径 cd $GOPATH/pkg/mod/github.com/ethereum/[email protected]
sudo make && make devtools

#测试安装
abigen --help

7、将ABI生成GO文件

abigen --abi erc20.json -pkg json -type erc20 --out erc20.go

8、编写调用文件 main.go

package main

import (
	"context"
	"crypto/ecdsa"
	"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"
	"mysolidity/json"
)

func main() {
	//合约地址
	contractAddress := "0x5FbDB2315678afecb367f032d93F642f64180aa3"
	//连接本地的以太坊私链(一定要保证本地以太坊私链已经启动)
	conn, err := ethclient.Dial("http://127.0.0.1:8545")
	fmt.Println("connect to local node...", conn)
	if err != nil {
		fmt.Errorf("could not connect to local node: %v", err)
		return
	}
	//创建合约
	erc20, err := json.NewErc20(common.HexToAddress(contractAddress), conn)
	if err != nil {
		fmt.Errorf("failed to instantiate a Token contract: %v", err)
		return
	}
	fmt.Println("contract token:", erc20)
	//调用合约查询方法
	name, err := erc20.Name(&bind.CallOpts{
		Pending:     false,
		From:        common.Address{},
		BlockNumber: nil,
		Context:     nil,
	})
	if err != nil {
		fmt.Errorf("name:%v", err)
		return
	}
	symbol, err := erc20.Symbol(&bind.CallOpts{
		Pending:     false,
		From:        common.Address{},
		BlockNumber: nil,
		Context:     nil,
	})
	if err != nil {
		fmt.Errorf("name:%v", err)
		return
	}
	fmt.Println("name:", name, "symbol:", symbol)
	//查询第一个账户余额
	b, err := erc20.BalanceOf(&bind.CallOpts{
		Pending:     false,
		From:        common.Address{},
		BlockNumber: nil,
		Context:     nil,
	}, common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))//启动node默认生成的第1个账户地址
	if err != nil {
		fmt.Errorf("BalanceOf:%v", err)
		return
	}
	fmt.Println("first balance:", b)
	第一个账户给第二个账户转账
	//私钥,需要生成签名
	privateKey, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")//私钥注意去掉ox,这是启动node默认生成的第1个账户的私钥
	if err != nil {
		fmt.Errorf("err:%v\n", err)
		return
	}
	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
		return
	}
	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
	nonce, err := conn.PendingNonceAt(context.Background(), fromAddress)
	if err != nil {
		fmt.Errorf("%v", err)
		return
	}
	gasPrice, err := conn.SuggestGasPrice(context.Background())
	if err != nil {
		fmt.Errorf("%v", err)
		return
	}
	//chainID
	id, err := conn.ChainID(context.Background())
	if err != nil {
		return
	}
	fmt.Println("chainID:", id)
	auth, _ := bind.NewKeyedTransactorWithChainID(privateKey, id)
	auth.Nonce = big.NewInt(int64(nonce))
	auth.Value = big.NewInt(10)    
	auth.GasLimit = uint64(300000) 
	auth.GasPrice = gasPrice
	tx, err := erc20.Transfer(&bind.TransactOpts{
		Signer:   auth.Signer,
		From:     fromAddress,
		GasLimit: auth.GasLimit,
		GasPrice: auth.GasPrice,
		Context:  nil,
	}, common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), big.NewInt(8))
	if err != nil {
		fmt.Errorf("transfer:%v", err)
		return
	}
	fmt.Println("tx.GasPrice():", tx.GasPrice())
	//查询两个账户余额
	b, err = erc20.BalanceOf(&bind.CallOpts{From: fromAddress}, common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"))//启动node默认生成的第2个账户地址
	if err != nil {
		fmt.Errorf("BalanceOf:%v", err)
		return
	}
	fmt.Println("second balance:", b)
	b, err = erc20.BalanceOf(&bind.CallOpts{From: fromAddress}, fromAddress)
	if err != nil {
		fmt.Errorf("BalanceOf:%v", err)
		return
	}
	fmt.Println("first balance:", b)
}

9、查询事件日志(完整开发的一般流程,在链端开发智能合约后,由前端使用web3.js等进行交互,后端通过查询事件日志修改数据库信息展示)

package main

import (
	"context"
	"fmt"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	"math/big"
	"mysolidity/json"
)

func main() {
	//合约地址
	contractAddress := common.HexToAddress("0x5FbDB2315678afecb367f032d93F642f64180aa3")
	//websocket监听
	client, err := ethclient.Dial("ws://127.0.0.1:8545/ws")
	if err != nil {
		fmt.Errorf("could not connect to local node: %v", err)
		return
	}
	query := ethereum.FilterQuery{
		FromBlock: big.NewInt(1), //生产环境中,从0开始,查询后修改区块记录,下一次就从后一个有记录的区块数开始
		ToBlock:   big.NewInt(100),
		Addresses: []common.Address{
			contractAddress,
		},
	}
	erc20, _ := json.NewErc20(contractAddress, client)
	logs, err := client.FilterLogs(context.Background(), query)
	if err != nil {
		fmt.Errorf("err:%v\n", err)
		return
	}
	for _, vLog := range logs {
		if len(vLog.Topics) == 0 {
			continue
		}
		event := vLog.Topics[0].Hex()
		if event == TransferEvent() { //对对应的事件进行对应的处理
			fmt.Println(vLog.Data)
			data, err := erc20.ParseTransfer(vLog)
			if err != nil {
				fmt.Errorf("err:%v\n", err)
				continue
			}
			fmt.Println(data.From.Hex(), data.To.Hex(), data.Value.Int64(), data.Raw.Data)
		}
	}
}
func TransferEvent() string {
	event := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")).Hex()
	return event
}

10、测试结果(main.go为最后一次运行结果)

在本地以太坊私链上,使用go调用智能合约,获取事件日志_第3张图片

在本地以太坊私链上,使用go调用智能合约,获取事件日志_第4张图片

 

你可能感兴趣的:(智能合约,GO,区块链,golang,solidity,hardhat,event)