我最近在重新学 Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。
所有代码和教程开源在 github: github.com/AmazingAng/WTF-Solidity
这一讲,我们将介绍 MultiCall 多重调用合约,它的设计目的在于一次交易中执行多个函数调用,这样可以显著降低交易费用并提高效率。
MultiCall
在Solidity中,MultiCall(多重调用)合约的设计能让我们在一次交易中执行多个函数调用。它的优点如下:
-
方便性:MultiCall能让你在一次交易中对不同合约的不同函数进行调用,同时这些调用还可以使用不同的参数。比如你可以一次性查询多个地址的ERC20代币余额。
-
节省gas:MultiCall能将多个交易合并成一次交易中的多个调用,从而节省gas。
-
原子性:MultiCall能让用户在一笔交易中执行所有操作,保证所有操作要么全部成功,要么全部失败,这样就保持了原子性。比如,你可以按照特定的顺序进行一系列的代币交易。
MultiCall 合约
接下来让我们一起来研究一下MultiCall合约,它由 MakerDAO 的 MultiCall 简化而成。
MultiCall 合约定义了两个结构体:
-
Call
: 这是一个调用结构体,包含要调用的目标合约target
,指示是否允许调用失败的标记allowFailure
,和要调用的字节码call data
。 -
Result
: 这是一个结果结构体,包含了指示调用是否成功的标记success
和调用返回的字节码return data
。
该合约只包含了一个函数,用于执行多重调用:
multicall()
: 这个函数的参数是一个由Call结构体组成的数组,这样做可以确保传入的target和data的长度一致。函数通过一个循环来执行多个调用,并在调用失败时回滚交易。
Remix 复现
-
我们先部署一个非常简单的ERC20代币合约
MCERC20
,并记录下合约地址。 -
部署
MultiCall
合约。 -
获取要调用的
calldata
。我们会给给2个地址分别铸造 50 和 100 单位的代币,你可以在 remix 的调用页面将mint()
的参数填入,然后点击 Calldata 按钮,将编码好的calldata复制下来。例子:如果你不了解
calldata
,可以阅读WTF Solidity的[第29讲]。 -
利用
MultiCall
的multicall()
函数调用ERC20代币合约的mint()
函数,给2个地址分别铸造 50 和 100 单位的代币。例子: -
利用
MultiCall
的multicall()
函数调用ERC20代币合约的balanceOf()
函数,查询刚才铸造2个地址的余额。balanceOf()
函数的selector为0x70a08231
。例子:可以在
decoded output
中查看调用的返回值,两个地址的余额分别为0x0000000000000000000000000000000000000000000000000000000000000032
和0x0000000000000000000000000000000000000000000000000000000000000064
,也就是 50 和 100,调用成功! .
总结
这一讲,我们介绍了 MultiCall 多重调用合约,允许你在一次交易中执行多个函数调用。要注意的是,不同的 MultiCall 合约在参数和执行逻辑上有一些不同,使用时要仔细阅读源码。