Skip to content

Commit f417e0e

Browse files
committed
Make MultiCall an ERC2771 forwarder
1 parent 318ef05 commit f417e0e

File tree

1 file changed

+19
-8
lines changed

1 file changed

+19
-8
lines changed

src/multicall/MultiCall.sol

+19-8
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ interface IMultiCall {
2727
// you need to know is the interface above.
2828

2929
library SafeCall {
30-
function safeCall(address target, bytes calldata data, uint256 contextdepth)
30+
function safeCall(address target, bytes calldata data, address sender, uint256 contextdepth)
3131
internal
3232
returns (bool success, bytes memory returndata)
3333
{
3434
assembly ("memory-safe") {
3535
returndata := mload(0x40)
3636
calldatacopy(returndata, data.offset, data.length)
37+
mstore(add(returndata, data.length), shl(0x60, sender))
3738
let beforeGas := gas()
38-
success := call(gas(), target, 0x00, returndata, data.length, 0x00, 0x00)
39+
success := call(gas(), target, 0x00, returndata, add(0x14, data.length), 0x00, 0x00)
3940
// `verbatim` can't work in inline assembly. Assignment of a value to a variable costs
4041
// gas (although how much is unpredictable because it depends on the Yul/IR optimizer),
4142
// as does the `GAS` opcode itself. Therefore, the `gas()` below returns less than the
@@ -76,11 +77,12 @@ library SafeCall {
7677

7778
/// This version of `safeCall` omits the OOG check because it bubbles the revert if the call
7879
/// reverts. Therefore, `success` is always `true`.
79-
function safeCall(address target, bytes calldata data) internal returns (bool success, bytes memory returndata) {
80+
function safeCall(address target, bytes calldata data, address sender) internal returns (bool success, bytes memory returndata) {
8081
assembly ("memory-safe") {
8182
returndata := mload(0x40)
8283
calldatacopy(returndata, data.offset, data.length)
83-
success := call(gas(), target, 0x00, returndata, data.length, 0x00, 0x00)
84+
mstore(add(returndata, data.length), shl(0x60, sender))
85+
success := call(gas(), target, 0x00, returndata, add(0x14, data.length), 0x00, 0x00)
8486
let dst := add(0x20, returndata)
8587
returndatacopy(dst, 0x00, returndatasize())
8688
if iszero(success) { revert(dst, returndatasize()) }
@@ -149,7 +151,7 @@ library UnsafeArray {
149151
function unsafeSet(Result[] memory a, uint256 i, bool success, bytes memory data) internal pure {
150152
assembly ("memory-safe") {
151153
let dst := mload(add(add(0x20, shl(0x05, i)), a))
152-
mstore(dst, and(0x01, success))
154+
mstore(dst, success)
153155
mstore(add(0x20, dst), data)
154156
}
155157
}
@@ -218,10 +220,19 @@ contract MultiCall {
218220
using UnsafeReturn for Result[];
219221

220222
constructor() {
221-
assert(address(this) == 0x000000000000175a8b9bC6d539B3708EEd92EA6c || block.chainid == 31337);
223+
assert(uint160(address(this)) >> 112 == 0 || block.chainid == 31337);
224+
}
225+
226+
function _msgSender() private view returns (address sender) {
227+
if ((sender = msg.sender) == address(this)) {
228+
assembly ("memory-safe") {
229+
sender := shr(0x60, calldataload(sub(calldatasize(), 0x14)))
230+
}
231+
}
222232
}
223233

224234
function multicall(Call[] calldata calls, uint256 contextdepth) internal returns (Result[] memory result) {
235+
address sender = _msgSender();
225236
result = new Result[](calls.length);
226237
for (uint256 i; i < calls.length; i = i.unsafeInc()) {
227238
(address target, bytes calldata data, RevertPolicy revertPolicy) = calls.unsafeGet(i);
@@ -230,9 +241,9 @@ contract MultiCall {
230241
if (revertPolicy == RevertPolicy.REVERT) {
231242
// We don't need to use the OOG-protected `safeCall` here because an OOG will result
232243
// in a bubbled revert anyways.
233-
(success, returndata) = target.safeCall(data);
244+
(success, returndata) = target.safeCall(data, sender);
234245
} else {
235-
(success, returndata) = target.safeCall(data, contextdepth);
246+
(success, returndata) = target.safeCall(data, sender, contextdepth);
236247
// This could be implemented in assembly as (equivalently) `(revertPolicy ==
237248
// RevertPolicy.STOP) > success`, but that only optimizes the `success == false`
238249
// condition and significantly compromises readability.

0 commit comments

Comments
 (0)