快速实现一个多重调用合约

  • 木西
  • 发布于 11小时前
  • 阅读 27

前言多重调用合约设计在于一次交易中执行多个函数调用,这样可以显著降低交易费用并提高效率。

前言

多重调用合约设计在于一次交易中执行多个函数调用,这样可以显著降低交易费用并提高效率。

多重调用

特点

  • 降低gas费:多个交易合并成一次交易中的多个调用,从而节省gas;
  • 提高效率:在一次交易中对不同合约的不同函数进行调用,同时这些调用还可以使用不同的参数;

    合约开发

    测试合约(一个ERC20代币合约)

    说明:一个名字MockToken 符号为 "MTK" 余额为1000000 MTK标准的代币,具备铸造等功能

    
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken1 is ERC20 { constructor() ERC20("Mock Token", "MTK") { _mint(msg.sender, 1000000 * 10 ** decimals()); } function mint(address to, uint amount) external { _mint(to, amount); } }

编译指令

npx hardhat compile

#### 多重调用合约

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "hardhat/console.sol"; contract Multicall { // Call结构体,包含目标合约target,是否允许调用失败allowFailure,和call data struct Call { address target; bool allowFailure; bytes callData; }

// Result结构体,包含调用是否成功和return data
struct Result {
    bool success;
    bytes returnData;
}

/// @notice 将多个调用(支持不同合约/不同方法/不同参数)合并到一次调用
/// @param calls Call结构体组成的数组
/// @return returnData Result结构体组成的数组
function multicall(Call[] calldata calls) public returns (Result[] memory returnData) {
    uint256 length = calls.length;
    returnData = new Result[](length);
    Call calldata calli;

    // 在循环中依次调用
    for (uint256 i = 0; i < length; i++) {
        Result memory result = returnData[i];
        calli = calls[i];
        (result.success, result.returnData) = calli.target.call(calli.callData);
        // 如果 calli.allowFailure 和 result.success 均为 false,则 revert
        if (!(calli.allowFailure || result.success)){
            revert("Multicall: call failed");
        }
    }
}
/**
 * @dev 批量调用合约(只读)
 * @param calls 调用数组
 * @return blockNumber 区块号
 * @return returnData 返回结果数组
 */
function multiStaticCall(Call[] calldata calls) 
    public 
    view 
    returns(
        uint256 blockNumber,
        Result[] memory returnData
    ) 
{
    returnData = new Result[](calls.length);

    for(uint256 i = 0; i < calls.length; i++) {
        (bool success, bytes memory result) = calls[i].target.staticcall(calls[i].callData);

        if (!success && !calls[i].allowFailure) {
            revert("MultiCall: static call failed");
        }

        returnData[i] = Result(success, result);
    }

    return (block.number, returnData);
}

}

编译指令

npx hardhat compile

# 合约测试

const {ethers,getNamedAccounts,deployments} = require("hardhat"); const { assert,expect } = require("chai"); describe("MultiCall",function(){ let Multicall;//合约地址 let token;//合约地址 let firstAccount//第一个账户 let secondAccount//第二个账户 let addr1,addr2,addr3,addr4,addr5,addr6,addr7,addr8,addr9,addr10;//10个账户 beforeEach(async function(){ await deployments.fixture(["token1","MultiCall"]); [addr1,addr2,addr3,addr4,addr5,addr6,addr7,addr8,addr9,addr10]=await ethers.getSigners(); firstAccount=(await getNamedAccounts()).firstAccount; secondAccount=(await getNamedAccounts()).secondAccount; const tokenDeployment = await deployments.get("MyToken1"); token = await ethers.getContractAt("MyToken1",tokenDeployment.address);//已经部署的合约交互 const MulticallDeployment = await deployments.get("Multicall"); Multicall = await ethers.getContractAt("Multicall",MulticallDeployment.address);//已经部署的合约交互 }) describe("MultiCall合约",function(){ it("多重调用",async function(){ const calls=[ { target:token.address,//合约地址 allowFailure:true,//失败是否继续 callData:token.interface.encodeFunctionData("mint",[firstAccount,ethers.utils.parseEther("100")])//调用铸造的方法 }, { target:token.address, allowFailure:true, callData:token.interface.encodeFunctionData("mint",[secondAccount,ethers.utils.parseEther("200")]) }, { target:token.address, allowFailure:true, callData:token.interface.encodeFunctionData("mint",[addr3.address,ethers.utils.parseEther("300")]) } ] //动态多重调用 await Multicall.multicall(calls) let FirstAccount=await token.balanceOf(firstAccount) let SecondAccount=await token.balanceOf(secondAccount) let ThirdAccount=await token.balanceOf(addr3.address)

        console.log("账号1余额",`${ethers.utils.formatEther(FirstAccount.toString())} MTK`)
        console.log("账号2的余额",`${ethers.utils.formatEther(SecondAccount.toString())} MTK`)
        console.log("账号3的余额",`${ethers.utils.formatEther(ThirdAccount.toString())} MTK`)

       let multiStaticCalls=[
            {
                target:token.address,
                allowFailure:true,
                callData:token.interface.encodeFunctionData("balanceOf",[firstAccount])
            },
            // {
            //     target:token.address,
            //     allowFailure:true,
            //     callData:token.interface.encodeFunctionData("balanceOf",[secondAccount])
            // },
            // {
            //     target:token.address,
            //     allowFailure:true,
            //     callData:token.interface.encodeFunctionData("balanceOf",[addr3.address])
            // }
        ]
        //静态调用只读
        let multiStaticCallArr=await Multicall.multiStaticCall(multiStaticCalls)
        console.log(multiStaticCallArr.returnData)
    })
})

})

测试指令

npx hardhat test ./test/xxx.js

# 合约部署
#### 多签合约部署

module.exports = async function ({getNamedAccounts,deployments}) { const firstAccount = (await getNamedAccounts()).firstAccount; const { deploy, log } = deployments; const MultiCall = await deploy("Multicall", { contract: "Multicall", from: firstAccount, args: [],//参数 owner log: true, // waitConfirmations: 1, }) console.log("MultiCall合约地址",MultiCall.address) } module.exports.tags = ["all", "MultiCall"]

部署指令

npx hardhat deploy

代币部署同上修改各个关键参数即可


# 总结 
以上就是多重调用合约从开发、测试、不是的全部流程,此合约的设计目的节省gas费。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
江湖只有他的大名,没有他的介绍。