@@ -22,22 +22,17 @@ contract Multicaller {
2222 */
2323 error Reentrancy ();
2424
25- // =============================================================
26- // STORAGE
27- // =============================================================
28-
29- /**
30- * @dev The storage slot for the sender and reentrancy guard flag.
31- */
32- bytes32 private _sender;
33-
3425 // =============================================================
3526 // CONSTRUCTOR
3627 // =============================================================
3728
3829 constructor () payable {
3930 assembly {
40- sstore (_sender.slot, shl (160 , 1 ))
31+ // Throughout this code, we will abuse returndatasize
32+ // in place of zero anywhere before a call to save a bit of gas.
33+ // We will use storage slot zero to store the caller at
34+ // bits [0..159] and reentrancy guard flag at bit 160.
35+ sstore (returndatasize (), shl (160 , 1 ))
4136 }
4237 }
4338
@@ -48,24 +43,23 @@ contract Multicaller {
4843 /**
4944 * @dev Returns the address that called `aggregateWithSender` on this contract.
5045 * The value is always the zero address outside a transaction.
51- * @return The caller address.
5246 */
53- function sender () external view returns ( address ) {
47+ fallback () external payable {
5448 assembly {
55- mstore (0x00 , and (sub (shl (160 , 1 ), 1 ), sload (_sender.slot )))
56- return (0x00 , 0x20 )
49+ mstore (returndatasize () , and (sub (shl (160 , 1 ), 1 ), sload (returndatasize () )))
50+ return (returndatasize () , 0x20 )
5751 }
5852 }
5953
6054 /**
6155 * @dev Aggregates multiple calls in a single transaction.
62- * The `msg.value` will be forwarded to the starting call.
56+ * The `msg.value` will be forwarded to the last call.
6357 * This method will set `sender` to the `msg.sender` temporarily
6458 * for the span of its execution.
6559 * This method does not support reentrancy.
6660 * @param targets An array of addresses to call.
6761 * @param data An array of calldata to forward to the targets.
68- * @return An array of the returndata from each of the call.
62+ * @return An array of the returndata from each call.
6963 */
7064 function aggregateWithSender (address [] calldata targets , bytes [] calldata data )
7165 external
@@ -75,36 +69,38 @@ contract Multicaller {
7569 assembly {
7670 if iszero (eq (targets.length , data.length )) {
7771 // Store the function selector of `ArrayLengthsMismatch()`.
78- mstore (0x00 , 0x3b800a46 )
72+ mstore (returndatasize () , 0x3b800a46 )
7973 // Revert with (offset, size).
8074 revert (0x1c , 0x04 )
8175 }
8276
83- if iszero (and (sload (_sender.slot ), shl (160 , 1 ))) {
77+ if iszero (and (sload (returndatasize () ), shl (160 , 1 ))) {
8478 // Store the function selector of `Reentrancy()`.
85- mstore (0x00 , 0xab143c06 )
79+ mstore (returndatasize () , 0xab143c06 )
8680 // Revert with (offset, size).
8781 revert (0x1c , 0x04 )
8882 }
89- // Set the `_sender` slot temporarily for the span of this transaction.
90- sstore (_sender.slot , caller ())
83+ // Set the sender slot temporarily for the span of this transaction.
84+ sstore (returndatasize () , caller ())
9185
92- mstore (0x00 , 0x20 ) // Store the memory offset of the `results`.
86+ mstore (returndatasize () , 0x20 ) // Store the memory offset of the `results`.
9387 mstore (0x20 , data.length ) // Store `data.length` into `results`.
9488 // Early return if no data.
95- if iszero (data.length ) { return (0x00 , 0x40 ) }
89+ if iszero (data.length ) { return (returndatasize () , 0x40 ) }
9690
9791 let results := 0x40
98- // `shl` 5 is equivalent to multiplying by 0x20.
99- let end := shl (5 , data.length )
92+ // Left shift by 5 is equivalent to multiplying by 0x20.
93+ data. length := shl (5 , data.length )
10094 // Copy the offsets from calldata into memory.
101- calldatacopy (0x40 , data.offset, end)
102- // Pointer to the top of the memory (i.e. start of the free memory).
103- let resultsOffset := end
104- // The callvalue to forward to the starting call.
105- let v := callvalue ()
106-
107- for { end := add (results, end) } 1 {} {
95+ calldatacopy (results, data.offset, data.length )
96+ // Offset into `results`.
97+ let resultsOffset := data.length
98+ // Pointer to the last result.
99+ let lastResults := add (0x20 , data.length )
100+ // Pointer to the end of `results`.
101+ let end := add (results, data.length )
102+
103+ for {} 1 {} {
108104 // The offset of the current bytes in the calldata.
109105 let o := add (data.offset, mload (results))
110106 let memPtr := add (resultsOffset, 0x40 )
@@ -118,7 +114,7 @@ contract Multicaller {
118114 call (
119115 gas (), // Remaining gas.
120116 calldataload (targets.offset), // Address to call.
121- v, // Amount of ETH to send.
117+ mul ( callvalue (), eq (results, lastResults)), // ETH to send.
122118 memPtr, // Start of input calldata in memory.
123119 calldataload (o), // Size of input calldata.
124120 0x00 , // We will use returndatacopy instead.
@@ -129,34 +125,32 @@ contract Multicaller {
129125 returndatacopy (0x00 , 0x00 , returndatasize ())
130126 revert (0x00 , returndatasize ())
131127 }
132- // We only forward the callvalue for the starting call.
133- v := 0
134128 // Advance the `targets.offset`.
135129 targets.offset := add (targets.offset, 0x20 )
136130 // Append the current `resultsOffset` into `results`.
137131 mstore (results, resultsOffset)
138132 results := add (results, 0x20 )
139- // Append the returndatasize, and the return data .
133+ // Append the returndatasize, and the returndata .
140134 mstore (memPtr, returndatasize ())
141135 returndatacopy (add (memPtr, 0x20 ), 0x00 , returndatasize ())
142136 // Advance the `resultsOffset` by `returndatasize() + 0x20`,
143- // rounded up to the next multiple of 32 .
137+ // rounded up to the next multiple of 0x20 .
144138 resultsOffset := and (add (add (resultsOffset, returndatasize ()), 0x3f ), not (0x1f ))
145139 if iszero (lt (results, end)) { break }
146140 }
147- // Restore the `_sender ` slot.
148- sstore (_sender.slot , shl (160 , 1 ))
141+ // Restore the `sender ` slot.
142+ sstore (0 , shl (160 , 1 ))
149143 // Direct return.
150144 return (0x00 , add (resultsOffset, 0x40 ))
151145 }
152146 }
153147
154148 /**
155149 * @dev Aggregates multiple calls in a single transaction.
156- * The `msg.value` will be forwarded to the starting call.
150+ * The `msg.value` will be forwarded to the last call.
157151 * @param targets An array of addresses to call.
158152 * @param data An array of calldata to forward to the targets.
159- * @return An array of the returndata from each of the call.
153+ * @return An array of the returndata from each call.
160154 */
161155 function aggregate (address [] calldata targets , bytes [] calldata data )
162156 external
@@ -166,27 +160,29 @@ contract Multicaller {
166160 assembly {
167161 if iszero (eq (targets.length , data.length )) {
168162 // Store the function selector of `ArrayLengthsMismatch()`.
169- mstore (0x00 , 0x3b800a46 )
163+ mstore (returndatasize () , 0x3b800a46 )
170164 // Revert with (offset, size).
171165 revert (0x1c , 0x04 )
172166 }
173167
174- mstore (0x00 , 0x20 ) // Store the memory offset of the `results`.
168+ mstore (returndatasize () , 0x20 ) // Store the memory offset of the `results`.
175169 mstore (0x20 , data.length ) // Store `data.length` into `results`.
176170 // Early return if no data.
177- if iszero (data.length ) { return (0x00 , 0x40 ) }
171+ if iszero (data.length ) { return (returndatasize () , 0x40 ) }
178172
179173 let results := 0x40
180- // `shl` 5 is equivalent to multiplying by 0x20.
181- let end := shl (5 , data.length )
174+ // Left shift by 5 is equivalent to multiplying by 0x20.
175+ data. length := shl (5 , data.length )
182176 // Copy the offsets from calldata into memory.
183- calldatacopy (0x40 , data.offset, end)
184- // Pointer to the top of the memory (i.e. start of the free memory).
185- let resultsOffset := end
186- // The callvalue to forward to the starting call.
187- let v := callvalue ()
188-
189- for { end := add (results, end) } 1 {} {
177+ calldatacopy (results, data.offset, data.length )
178+ // Offset into `results`.
179+ let resultsOffset := data.length
180+ // Pointer to the last result.
181+ let lastResults := add (0x20 , data.length )
182+ // Pointer to the end of `results`.
183+ let end := add (results, data.length )
184+
185+ for {} 1 {} {
190186 // The offset of the current bytes in the calldata.
191187 let o := add (data.offset, mload (results))
192188 let memPtr := add (resultsOffset, 0x40 )
@@ -200,7 +196,7 @@ contract Multicaller {
200196 call (
201197 gas (), // Remaining gas.
202198 calldataload (targets.offset), // Address to call.
203- v, // Amount of ETH to send.
199+ mul ( callvalue (), eq (results, lastResults)), // ETH to send.
204200 memPtr, // Start of input calldata in memory.
205201 calldataload (o), // Size of input calldata.
206202 0x00 , // We will use returndatacopy instead.
@@ -211,18 +207,16 @@ contract Multicaller {
211207 returndatacopy (0x00 , 0x00 , returndatasize ())
212208 revert (0x00 , returndatasize ())
213209 }
214- // We only forward the callvalue for the starting call.
215- v := 0
216210 // Advance the `targets.offset`.
217211 targets.offset := add (targets.offset, 0x20 )
218212 // Append the current `resultsOffset` into `results`.
219213 mstore (results, resultsOffset)
220214 results := add (results, 0x20 )
221- // Append the returndatasize, and the return data .
215+ // Append the returndatasize, and the returndata .
222216 mstore (memPtr, returndatasize ())
223217 returndatacopy (add (memPtr, 0x20 ), 0x00 , returndatasize ())
224218 // Advance the `resultsOffset` by `returndatasize() + 0x20`,
225- // rounded up to the next multiple of 32 .
219+ // rounded up to the next multiple of 0x20 .
226220 resultsOffset := and (add (add (resultsOffset, returndatasize ()), 0x3f ), not (0x1f ))
227221 if iszero (lt (results, end)) { break }
228222 }
0 commit comments