智能合约中的数据获取一般都有点难度,不是速度慢,就是节点限速等。对于要求速度快且准确的应用来讲,如何快速地获取数据就是一个值得重视的问题。
对于我来讲,我也遇到了相似的问题:在NFT合约中如何快速读取全部或是多数的NFT数据。大家都知道,一个NFT合约都是几百上千,很多是一万个数量,如何一次性读取多个,也尝试过不少方法。当然,现在推荐的方法是multicall。
multicall可以一次性读取一个或多个合约的函数,快速得到结果。它主要是通到call
或 staticcall
来读取合约数据。这两个函数比较底层,理解起来有些难度,而且还要提前计算出calldata
。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MultiCall {
function multiCall(address[] calldata targets, bytes[] calldata data)
external
view
returns (bytes[] memory)
{
require(targets.length == data.length, "target length != data length");
bytes[] memory results = new bytes[](data.length);
for (uint i; i < targets.length; i++) {
(bool success, bytes memory result) = targets[i].staticcall(data[i]);
require(success, "call failed");
results[i] = result;
}
return results;
}
}
//测试合约,最下面两个是计算 `calldata`的方法
contract Test {
function test1() external view returns (uint,uint) {
return (1, block.timestamp);
}
function test2() external view returns (uint,uint) {
return (2, block.timestamp);
}
function getData1() external pure returns (bytes memory) {
return abi.encodeWithSelector(this.test1.selector); //计算出`calldata`
}
function getData3() external pure returns (bytes memory) {
return abi.encodeWithSignature("test2()"); //计算出`calldata`
}
}
multicall合约只有一个方法,用合约地址和calldata去调用函数获取结果,一般只应用于view属性的函数。合约地址就明白,calldata
要如何去计算呢?如上所示的Test
合约中getData
列出两种计算calldata
的方法,略有差异,但结果一致。在应用较复杂,需要一次性得到多条数据的情况下,multicall就非常适用啰!