Skip to content

Commit 3253a26

Browse files
committed
Fix _get
1 parent cf6991e commit 3253a26

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

src/accounts/ext/ithaca/ERC7821.sol

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,9 @@ contract ERC7821 is Receiver {
282282
// Override this function to perform more complex auth with `opData`.
283283
if (opData.length == uint256(0)) {
284284
require(msg.sender == address(this));
285-
// Remember to return `_executeOptimizedBatch(dataArr, to, extraData)`
285+
// Remember to return `_executeOptimizedBatch(to, dataArr, extraData)`
286286
// when you override this function.
287-
return _executeOptimizedBatch(dataArr, to, bytes32(0));
287+
return _executeOptimizedBatch(to, dataArr, bytes32(0));
288288
}
289289
revert(); // In your override, replace this with logic to operate on `opData`.
290290
}
@@ -303,12 +303,15 @@ contract ERC7821 is Receiver {
303303
}
304304
}
305305

306+
event LogBytes(bytes);
307+
event LogUint(uint256);
308+
306309
/// @dev Executes the `dataArr`, with a common `to` address.
307310
/// If any `to == address(0)`, it will be replaced with `address(this)`.
308311
/// Value for all calls is zero.
309312
/// Reverts and bubbles up error if any call fails.
310313
/// `extraData` can be any supplementary data (e.g. a memory pointer, some hash).
311-
function _executeOptimizedBatch(bytes[] calldata dataArr, address to, bytes32 extraData)
314+
function _executeOptimizedBatch(address to, bytes[] calldata dataArr, bytes32 extraData)
312315
internal
313316
virtual
314317
{
@@ -319,9 +322,15 @@ contract ERC7821 is Receiver {
319322
let t := shr(96, shl(96, to))
320323
to := or(mul(address(), iszero(t)), t)
321324
}
325+
326+
emit LogUint(dataArr.length);
327+
emit LogUint(_get(dataArr, 0).length);
328+
emit LogUint(_get(dataArr, 1).length);
329+
322330
if (dataArr.length == uint256(0)) return;
323331
do {
324-
_execute(to, 0, dataArr[i], extraData);
332+
emit LogBytes(_get(dataArr, i));
333+
_execute(to, 0, _get(dataArr, i), extraData);
325334
} while (++i != dataArr.length);
326335
}
327336
}
@@ -365,4 +374,19 @@ contract ERC7821 is Receiver {
365374
data.length := calldataload(o)
366375
}
367376
}
377+
378+
/// @dev Convenience function for getting `dataArr[i]`, without bounds checks.
379+
function _get(bytes[] calldata dataArr, uint256 i)
380+
internal
381+
view
382+
virtual
383+
returns (bytes calldata data)
384+
{
385+
/// @solidity memory-safe-assembly
386+
assembly {
387+
let c := add(dataArr.offset, calldataload(add(dataArr.offset, shl(5, i))))
388+
data.offset := add(c, 0x20)
389+
data.length := calldataload(c)
390+
}
391+
}
368392
}

test/ext/ithaca/ERC7821.t.sol

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.4;
33

44
import "../../utils/SoladyTest.sol";
5-
import {ERC7821, MockERC7821} from "../../utils/mocks/MockERC7821.sol";
5+
import {ERC7821, MockERC7821} from "../../utils/mocks/ext/ithaca/MockERC7821.sol";
66
import {LibClone} from "../../../src/utils/LibClone.sol";
77

88
contract ERC7821Test is SoladyTest {
@@ -13,7 +13,7 @@ contract ERC7821Test is SoladyTest {
1313
address target;
1414

1515
bytes32 internal constant _SUPPORTED_MODE = bytes10(0x01000000000078210001);
16-
16+
bytes32 internal constant _OPTIMIZED_BATCH_MODE = bytes10(0x01000000000078210003);
1717
bytes[] internal _bytes;
1818

1919
function setUp() public {
@@ -54,6 +54,21 @@ contract ERC7821Test is SoladyTest {
5454
mbe.execute{value: _totalValue(calls)}(_SUPPORTED_MODE, data);
5555
}
5656

57+
function testERC7821OptimizedBatchGas() public {
58+
vm.pauseGasMetering();
59+
vm.deal(address(this), 1 ether);
60+
61+
bytes[] memory dataArr = new bytes[](2);
62+
63+
dataArr[0] = abi.encodeWithSignature("returnsBytes(bytes)", "hehe");
64+
dataArr[1] = abi.encodeWithSignature("returnsHash(bytes)", "lol");
65+
66+
bytes memory data = abi.encode(target, dataArr);
67+
vm.resumeGasMetering();
68+
69+
mbe.execute(_OPTIMIZED_BATCH_MODE, data);
70+
}
71+
5772
function testERC7821(bytes memory opData) public {
5873
vm.deal(address(this), 1 ether);
5974

@@ -72,6 +87,18 @@ contract ERC7821Test is SoladyTest {
7287
assertEq(mbe.lastOpData(), opData);
7388
}
7489

90+
function testERC7821OptimizedBatch(bytes memory opData) public {
91+
vm.deal(address(this), 1 ether);
92+
93+
bytes[] memory dataArr = new bytes[](2);
94+
dataArr[0] = abi.encodeWithSignature("returnsBytes(bytes)", "hehe");
95+
dataArr[1] = abi.encodeWithSignature("returnsHash(bytes)", "lol");
96+
97+
mbe.execute(_OPTIMIZED_BATCH_MODE, _encodeOptimizedBatch(target, dataArr, opData));
98+
99+
assertEq(mbe.lastOpData(), opData);
100+
}
101+
75102
function testERC7821ForRevert() public {
76103
ERC7821.Call[] memory calls = new ERC7821.Call[](1);
77104
calls[0].to = target;
@@ -82,6 +109,14 @@ contract ERC7821Test is SoladyTest {
82109
mbe.execute{value: _totalValue(calls)}(_SUPPORTED_MODE, _encode(calls, ""));
83110
}
84111

112+
function testERC7821OptimizedBatchForRevert() public {
113+
bytes[] memory dataArr = new bytes[](1);
114+
dataArr[0] = abi.encodeWithSignature("revertsWithCustomError()");
115+
116+
vm.expectRevert(CustomError.selector);
117+
mbe.execute(_OPTIMIZED_BATCH_MODE, _encodeOptimizedBatch(target, dataArr, ""));
118+
}
119+
85120
function _encode(ERC7821.Call[] memory calls, bytes memory opData)
86121
internal
87122
returns (bytes memory)
@@ -90,6 +125,14 @@ contract ERC7821Test is SoladyTest {
90125
return abi.encode(calls, opData);
91126
}
92127

128+
function _encodeOptimizedBatch(address to, bytes[] memory dataArr, bytes memory opData)
129+
internal
130+
returns (bytes memory)
131+
{
132+
if (_randomChance(2) && opData.length == 0) return abi.encode(to, dataArr);
133+
return abi.encode(to, dataArr, opData);
134+
}
135+
93136
struct Payload {
94137
bytes data;
95138
uint256 mode;
@@ -125,6 +168,34 @@ contract ERC7821Test is SoladyTest {
125168
}
126169
}
127170

171+
function testERC7821OptimizedBatch(bytes32) public {
172+
vm.deal(address(this), 1 ether);
173+
174+
bytes[] memory dataArr = new bytes[](_randomUniform() & 3);
175+
Payload[] memory payloads = new Payload[](dataArr.length);
176+
177+
for (uint256 i; i < dataArr.length; ++i) {
178+
bytes memory data = _truncateBytes(_randomBytes(), 0x1ff);
179+
payloads[i].data = data;
180+
if (_randomChance(2)) {
181+
payloads[i].mode = 0;
182+
dataArr[i] = abi.encodeWithSignature("returnsBytes(bytes)", data);
183+
} else {
184+
payloads[i].mode = 1;
185+
dataArr[i] = abi.encodeWithSignature("returnsHash(bytes)", data);
186+
}
187+
}
188+
189+
mbe.executeDirect(target, dataArr);
190+
191+
if (dataArr.length != 0 && _randomChance(32)) {
192+
dataArr[_randomUniform() % dataArr.length] =
193+
abi.encodeWithSignature("revertsWithCustomError()");
194+
vm.expectRevert(CustomError.selector);
195+
mbe.executeDirect(target, dataArr);
196+
}
197+
}
198+
128199
function _totalValue(ERC7821.Call[] memory calls) internal pure returns (uint256 result) {
129200
unchecked {
130201
for (uint256 i; i < calls.length; ++i) {
@@ -182,4 +253,18 @@ contract ERC7821Test is SoladyTest {
182253
function pushBytes(bytes memory x) public {
183254
_bytes.push(x);
184255
}
256+
257+
function testERC7821OptimizedBatchWithZeroAddress() public {
258+
// Test that when to=address(0), it gets replaced with address(this) (the MockERC7821 contract)
259+
// We'll call executeDirect which directly calls the internal _execute function
260+
bytes[] memory dataArr = new bytes[](1);
261+
dataArr[0] =
262+
abi.encodeWithSignature("setAuthorizedCaller(address,bool)", address(0x123), true);
263+
264+
// This should replace address(0) with address(mbe) and call setAuthorizedCaller on itself
265+
mbe.executeDirect(address(0), dataArr);
266+
267+
// Verify the call succeeded by checking that address(0x123) is now authorized
268+
assertTrue(mbe.isAuthorizedCaller(address(0x123)));
269+
}
185270
}

test/utils/mocks/ext/ithaca/MockERC7821.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ contract MockERC7821 is ERC7821, Brutalizer {
2222
_execute(calls, bytes32(0));
2323
}
2424

25+
function _executeOptimizedBatch(
26+
bytes32,
27+
bytes calldata,
28+
address to,
29+
bytes[] calldata dataArr,
30+
bytes calldata opData
31+
) internal virtual override {
32+
lastOpData = opData;
33+
_executeOptimizedBatch(to, dataArr, bytes32(0));
34+
}
35+
2536
function execute(bytes32 mode, bytes calldata executionData) public payable virtual override {
2637
if (!isAuthorizedCaller[msg.sender]) revert Unauthorized();
2738
super.execute(mode, executionData);
@@ -34,6 +45,13 @@ contract MockERC7821 is ERC7821, Brutalizer {
3445
_checkMemory();
3546
}
3647

48+
function executeDirect(address to, bytes[] calldata dataArr) public payable virtual {
49+
_misalignFreeMemoryPointer();
50+
_brutalizeMemory();
51+
_executeOptimizedBatch(to, dataArr, bytes32(0));
52+
_checkMemory();
53+
}
54+
3755
function setAuthorizedCaller(address target, bool status) public {
3856
isAuthorizedCaller[target] = status;
3957
}

0 commit comments

Comments
 (0)