如何使用ethers.js获取智能合约函数的返回数据?

4

我正在尝试使用 ethers.js 调用智能合约中的函数。该函数通过其他函数的帮助检索先前登录用户的信息。以下是该函数代码片段。

function getUser(address _userAddress)
        public
        onlyAuthCaller
        returns (
            string memory name,
            string memory info,
            string memory role,
        )
    {
        User memory tmpData = userDetails[_userAddress];
        return (
            tmpData.name,
            tmpData.info,
            tmpData.role
        );
    }

我用React渲染了一个按钮,用于获取用户信息,代码如下:

const GetUser = () => {
    const askUser = async () => {
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const account = await window.ethereum.request({
            method: "eth_requestAccounts",
        });
        const signer = provider.getSigner();
        const erc20 = new ethers.Contract(
            ContractAddress,
            ContractABI.abi,
            signer
        );

        try {
            const user = await erc20.getUser(account[0]);
            console.log(user);
        } catch (error) {
            console.log("ERROR AT GETTING USER: ", error);
        }
    };
    return (
        <div>
            <Button type="submit" variant="contained" onClick={askUser}>
                GET USER
            </Button>
        </div>
    );
};

我想知道为什么没有得到智能合约 getUser 函数的返回结果,我在等待该函数后期望在 const user 中获得信息。但是实际上在 const user 中,我得到了交易元数据,如下:

{hash: '0x24818569ec29d328b66f58736750a420a5a3bd8e28a72a6a0f72fd8ba5e088d8', type: 2, accessList: null, blockHash: null, blockNumber: null, }
accessList: null
blockHash: null
blockNumber: null
chainId: 0
confirmations: 0
creates: null
data: "0x6f77926b00000000000000000000000086b2b772014a87730928c7e54f4762d2c09ea4e5"
from: "0x86b2b772014A87730928c7e54F4762d2c09eA4e5"
gasLimit: BigNumber {_hex: '0xd15f', _isBigNumber: true}
gasPrice: BigNumber {_hex: '0x73a20d0c', _isBigNumber: true}
hash: "0x24818569ec29d328b66f58736750a420a5a3bd8e28a72a6a0f72fd8ba5e088d8"
maxFeePerGas: BigNumber {_hex: '0x73a20d0c', _isBigNumber: true}
maxPriorityFeePerGas: BigNumber {_hex: '0x73a20d00', _isBigNumber: true}
nonce: 5
r: "0x6a8fed76397e03a2fc564d18e1ec12abdf39a38fbe825df990f744bb50fc4a8b"
s: "0x66e9b4513047b65aac724dc6fb07d069967f6ca6fd8cd5fe85f6dbe495864765"
to: "0x9719E9dC77A7eDD3825844c77a68c896d4a7BB2b"
transactionIndex: null
type: 2
v: 0
value: BigNumber {_hex: '0x00', _isBigNumber: true}
wait: confirmations => {}
length: 1
name: ""
arguments: (…)
caller: (…)
[[FunctionLocation]]: index.ts:336
[[Prototype]]: ƒ ()
[[Scopes]]: Scopes[4]
[[Prototype]]: Object

当我在Remix IDE上尝试我的合约函数时,所有功能都按预期工作。例如,在Remix中,我得到了这个答案,其中由函数检索的数据位于解码输出中。
status  true Transaction mined and execution succeed
transaction hash    0x206af46a0f8e6bcc04ae632c85da005c901d8fc82f650e8d40a445f6988adcc2
from    0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
to  SupplychainUser.getUser(address) 0xD7ACd2a9FD159E69Bb102A1ca21C9a3e3A5F771B
gas 61639 gas
transaction cost    53599 gas 
execution cost  53599 gas 
input   0x6f7...35cb2
decoded input   {
    "address _userAddress": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2"
}
decoded output  {
    "0": "string: name Carl Bertz",
    "1": "string: info 0987654321",
    "2": "string: role processor",
}
logs    []
val 0 wei


我希望进行相同操作,但是使用React,请问如何从合约的getUser函数中获取返回数据?
3个回答

2
你需要在合约上使用callStatic,类似这样的代码:
await erc20.callStatic.getUser(account[0])

这样你就可以调用函数,而不是运行一个执行交易(需要消耗 gas)的 send


我能在无视图函数中使用callStatic吗? - 0x55b1E06FF
因为我的函数没有声明为视图,所以它不能被声明为视图,因为它似乎正在写入“userDetails”(它是一个映射),否则,如果我尝试声明为视图,我会在Remix上得到一个错误。 - 0x55b1E06FF
我还有一个问题,ethers.js 中的 .callStatic 是否与 web3.js 中的 .call 相同? - 0x55b1E06FF

2
调用getUser时获取交易元数据的原因是getUser函数不是一个view函数。 不是view函数会导致区块链为该特定函数调用创建一个交易,必须由区块链进行验证,并且在执行getUser函数时不可用。
对于这种情况,推荐的方法是使用事件,即发出带有所需信息的事件,在React端监听事件。
考虑将getUser函数作为view函数,因为它不会更改合约的状态:
function getUser(address _userAddress)
        public
        view
        onlyAuthCaller
        returns (
            string memory name,
            string memory info,
            string memory role,
        )
    {
        User memory tmpData = userDetails[_userAddress];
        return (
            tmpData.name,
            tmpData.info,
            tmpData.role
        );
    }

1
这是回答评论中的一个问题:EVM支持几种“调用”,您可以在此处查看Yul文档(https://docs.soliditylang.org/en/latest/yul.html#evm-dialect)。静态调用告诉EVM预期不会运行改变状态的指令(即强制调用“视图”函数)。其他调用类型与其他类型的调用相关(例如,delegatecall()是支持代理的核心)。
call(g, a, v, in, insize, out, outsize)

call contract at address a with input mem[in…(in+insize)) providing g gas and v wei and output area mem[out…(out+outsize)) returning 0 on error (eg. out of gas) and 1 on success See more


callcode(g, a, v, in, insize, out, outsize)

identical to call but only use the code from a and stay in the context of the current contract otherwise See more


delegatecall(g, a, in, insize, out, outsize)

identical to callcode but also keep caller and callvalue See more


staticcall(g, a, in, insize, out, outsize)

identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modifications See more

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接