前言多重调用合约设计在于一次交易中执行多个函数调用,这样可以显著降低交易费用并提高效率。
多重调用合约设计在于一次交易中执行多个函数调用,这样可以显著降低交易费用并提高效率。
多重调用
特点:
- 降低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); } }
#### 多重调用合约
// 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);
}
}
# 合约测试
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)
})
})
})
# 合约部署
#### 多签合约部署
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"]
# 总结
以上就是多重调用合约从开发、测试、不是的全部流程,此合约的设计目的节省gas费。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!