multicall一次获取多条数据 / 学习智能合约#62

Words
351
Reading
2 min
Listen
Play
4y

智能合约中的数据获取一般都有点难度,不是速度慢,就是节点限速等。对于要求速度快且准确的应用来讲,如何快速地获取数据就是一个值得重视的问题。

对于我来讲,我也遇到了相似的问题:在NFT合约中如何快速读取全部或是多数的NFT数据。大家都知道,一个NFT合约都是几百上千,很多是一万个数量,如何一次性读取多个,也尝试过不少方法。当然,现在推荐的方法是multicall。

multicall可以一次性读取一个或多个合约的函数,快速得到结果。它主要是通到callstaticcall来读取合约数据。这两个函数比较底层,理解起来有些难度,而且还要提前计算出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就非常适用啰!