golang调用智能合约,获取合约函数的返回值

如果不是只读取数据的合约函数,需要异步的执行,因此并不能直接获取到合约函数的返回值,需要等到交易执行完毕,得到确认后才能获取到合约函数的返回值。而且合约函数返回值一般是通过事件日志获取到的。

这里给出一个例子来展示我是如何获取合约函数返回值的。

我使用的以太坊版本为:github.com/ethereum/go-ethereum v1.13.0

solidity合约:

javascript 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract StoreString {
  event ItemSetStr(bytes32 indexed hash,string key,string value);
  
  mapping (string => string) public itemstr;

  function setItemstr(string memory _key, string memory _value) external returns(bytes32){
    itemstr[_key] = _value;
    bytes32 hash=sha256(abi.encodePacked(_key,_value));
    emit ItemSetStr(hash,_key,_value);
    return hash;
  }
}

这是一个存储键值对的合约,利用函数setItemstr向map中存储键值对,并通过key,value计算哈希值,要求返回哈希值。通过事件ItemSetStr将哈希值(返回值)、key、value记录下来。

将合约的abi编码通过abigen工具生成go代码后。

以下是测试获取合约函数返回值的代码:

go 复制代码
func TestSetStoreString(t *testing.T) {
	url="" //节点链接
	client, err := ethclient.Dial(url)
	if err != nil {
		log.Fatal(err)
	}

	//合约地址
	contractAddress := common.HexToAddress("0xd9Ed5E352F84E182eB499ae1b5F9935C06d78Ddb")

	privateKeyStr := "" //私钥字符串

	auth := InitAuth(privateKeyStr, client, nil)
	
	//得到合约实例
	storeString, err := NewStoreString(contractAddress, client)
	if err != nil {
		log.Fatal("NewStoreString: ", err)
	}
	
	tx, err := storeString.SetItemstr(auth, "key10", "9900")
	if err != nil {
		log.Fatal("SetItemstr: ", err)
	}
	
	//等待交易确认,获取到交易的收据
	receipt, err := bind.WaitMined(context.Background(), client, tx)
	if err != nil {
		log.Fatal("WaitMined: ", err)
	}
	
	//如果交易成功
	if receipt.Status == 1 {
		//解析该交易收据里包含的日志
		eventItemSetStr, err := storeString.ParseItemSetStr(*receipt.Logs[0])
		if err != nil {
			log.Fatal("ParseItemSetStr: ", err)
		}
		fmt.Println("交易成功!")
		fmt.Println("txHash:", tx.Hash().Hex())
		fmt.Println("返回值:", eventItemSetStr.Hash)
		fmt.Println("日志Key:", eventItemSetStr.Key)
		fmt.Println("日志Value:", eventItemSetStr.Value)
	} else {
		t.Error("交易失败")
	}

}


func InitAuth(privateKeyStr string, client *ethclient.Client, value *big.Int) *bind.TransactOpts {
	privateKeyECDSA, err := crypto.HexToECDSA(privateKeyStr)
	if err != nil {
		log.Fatal("crypto.HexToECDSA: ", err)
	}

	chainID, err := client.ChainID(context.Background())
	if err != nil {
		log.Fatal("ChainID: ", err)
	}

	auth, err := bind.NewKeyedTransactorWithChainID(privateKeyECDSA, chainID)
	if err != nil {
		log.Fatal("NewKeyedTransactorWithChainID: ", err)
	}

	//设置交易参数,例如gas费等,这里全部由区块链系统评估决定
	auth.GasFeeCap = big.NewInt(1).Mul(big.NewInt(10), big.NewInt(1000000000)) // maxFee Per Gas:100gwei

	//获取平均小费
	gasTipCap, err := client.SuggestGasTipCap(context.Background())
	if err != nil {
		log.Fatal("SuggestGasTipCap: ", err)
	}
	auth.GasTipCap = gasTipCap //priorityfee Per Gas:
	auth.GasLimit = uint64(3000000)

	//还需要设置锁定金额
	auth.Value = value
	return auth
}

返回值并不能立马获得,需要等待交易成功上链。再从事件日志中解析出返回值。因此这个过程中用到了两个重要代码:

bind包下的WaitMined:等待tx交易的确认,并获取其收据,该收据中必然包含我们想要的日志

go 复制代码
func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error)

合约代码自动生成的go文件中的ParseItemSetStr,传入日志即可解析出信息。

go 复制代码
func (_StoreString *StoreStringFilterer) ParseItemSetStr(log types.Log) (*StoreStringItemSetStr, error)
相关推荐
Tony Bai3 小时前
Rust 看了流泪,AI 看了沉默:扒开 Go 泛型最让你抓狂的“残疾”类型推断
开发语言·人工智能·后端·golang·rust
ん贤4 小时前
AI 大模型落地系列|Eino 编排进阶篇:一文讲透编排(Chain 与 Graph)
人工智能·golang·编排·eino
GDAL6 小时前
BoltDB vs SQLite:极简高并发、低配置场景下的终极对比
golang·sqlite·boltdb
ruxingli8 小时前
GoLang的并发如何避免死锁
开发语言·后端·golang
暴躁小师兄数据学院8 小时前
【WEB3.0零基础转行笔记】go编程篇-第12讲:go-zero入门实战
开发语言·笔记·golang·web3·区块链
无心水9 小时前
【OpenClaw:赚钱】案例9、模拟盘ROI+1560%:跨平台加密预测市场套利机器人全栈开发指南
机器人·区块链·金融科技·roi·openclaw·openclaw 变现
念何架构之路9 小时前
Go语言表达式的求值顺序
开发语言·后端·golang
低调的JVM13 小时前
Golang下kafka可观测数据采集组件Otelsarama详解
golang·kafka·可观测·opentelemetry
傻小胖1 天前
北大肖臻老师区块链课程完结反思和自我灵魂拷问
区块链·智能合约
golang学习记1 天前
Go 实时批处理:让数据库少挨点打 [特殊字符]
开发语言·数据库·golang