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)
相关推荐
YSGZJJ35 分钟前
股指期货套利交易详解
区块链
甘橘籽3 小时前
【RPC】 gRPC、pb基本使用--经验与总结
golang
杜杜的man4 小时前
【go从零单排】HTTP客户端和服务端
开发语言·http·golang
材料苦逼不会梦到计算机白富美4 小时前
golang分布式缓存项目 Day6 防止缓存击穿
分布式·缓存·golang
杜杜的man7 小时前
【go从零单排】Environment Variables环境变量
golang
材料苦逼不会梦到计算机白富美9 小时前
golang HTTP基础
http·golang·iphone
MavenTalk10 小时前
区块链里的Layer 1(L1)和 Layer 2(L2)是什么
区块链·arbitrum·layer2·layer1·optimism
友大冰11 小时前
Go 语言已立足主流,编程语言排行榜24 年 11 月
开发语言·后端·golang
hummhumm12 小时前
第 10 章 - Go语言字符串操作
java·后端·python·sql·算法·golang·database
__AtYou__17 小时前
Golang | Leetcode Golang题解之第563题二叉树的坡度
leetcode·golang·题解