Skip to content

Commit 76cdeb0

Browse files
committed
Merge branch 'feat/specialized-wrappers' of https://github.com/cowprotocol/euler-integration-contracts into feat/specialized-wrappers
2 parents bbe733c + b0f7d8f commit 76cdeb0

File tree

2 files changed

+90
-12
lines changed

2 files changed

+90
-12
lines changed

src/vendor/CowWrapperHelpers.sol

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ contract CowWrapperHelpers {
2424
/// @param wrapperError The error returned by the wrapper's parseWrapperData
2525
error WrapperDataMalformed(uint256 wrapperIndex, bytes wrapperError);
2626

27+
/// @notice Thrown when the data for the wrapper is too long. Its limited to 65535 bytes.
28+
/// @param wrapperIndex The index of the wrapper with data that is too long
29+
/// @param exceedingLength The observed length of the data
30+
error WrapperDataTooLong(uint256 wrapperIndex, uint256 exceedingLength);
31+
2732
/// @notice Thrown when the settlement contract is authenticated as a solver
2833
/// @dev The settlement contract should not be a solver to prevent direct settlement calls bypassing wrappers
2934
/// @param settlementContract The settlement contract address
@@ -82,9 +87,10 @@ contract CowWrapperHelpers {
8287

8388
// First pass: verify all wrappers are authenticated
8489
for (uint256 i = 0; i < wrapperCalls.length; i++) {
85-
if (!WRAPPER_AUTHENTICATOR.isSolver(wrapperCalls[i].target)) {
86-
revert NotAWrapper(i, wrapperCalls[i].target, address(WRAPPER_AUTHENTICATOR));
87-
}
90+
require(
91+
WRAPPER_AUTHENTICATOR.isSolver(wrapperCalls[i].target),
92+
NotAWrapper(i, wrapperCalls[i].target, address(WRAPPER_AUTHENTICATOR))
93+
);
8894
}
8995

9096
// Get the expected settlement from the first wrapper
@@ -93,9 +99,10 @@ contract CowWrapperHelpers {
9399
for (uint256 i = 0; i < wrapperCalls.length; i++) {
94100
// All wrappers must use the same settlement contract
95101
address wrapperSettlement = address(ICowWrapper(wrapperCalls[i].target).SETTLEMENT());
96-
if (wrapperSettlement != expectedSettlement) {
97-
revert SettlementMismatch(i, expectedSettlement, wrapperSettlement);
98-
}
102+
103+
require(
104+
wrapperSettlement == expectedSettlement, SettlementMismatch(i, expectedSettlement, wrapperSettlement)
105+
);
99106

100107
// The wrapper data must be parsable and fully consumed
101108
try ICowWrapper(wrapperCalls[i].target).parseWrapperData(wrapperCalls[i].data) returns (
@@ -114,13 +121,14 @@ contract CowWrapperHelpers {
114121
revert SettlementContractShouldNotBeSolver(expectedSettlement, address(SOLVER_AUTHENTICATOR));
115122
}
116123

117-
// Build wrapper data without settlement address at the end
118-
wrapperData = abi.encodePacked(uint16(wrapperCalls[0].data.length), wrapperCalls[0].data);
124+
// Build wrapper data
125+
for (uint256 i = 0; i < wrapperCalls.length; i++) {
126+
if (i > 0) {
127+
wrapperData = abi.encodePacked(wrapperData, wrapperCalls[i].target);
128+
}
119129

120-
for (uint256 i = 1; i < wrapperCalls.length; i++) {
121-
wrapperData = abi.encodePacked(
122-
wrapperData, wrapperCalls[i].target, uint16(wrapperCalls[i].data.length), wrapperCalls[i].data
123-
);
130+
require(wrapperCalls[i].data.length < 65536, WrapperDataTooLong(i, wrapperCalls[i].data.length));
131+
wrapperData = abi.encodePacked(wrapperData, uint16(wrapperCalls[i].data.length), wrapperCalls[i].data);
124132
}
125133

126134
return wrapperData;

test/CowWrapperHelpers.t.sol

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,78 @@ contract CowWrapperHelpersTest is Test {
246246
assertEq(result, expected);
247247
}
248248

249+
function test_verifyAndBuildWrapperData_RevertsOnSettlementMismatch() public {
250+
MockSettlement differentSettlement = new MockSettlement(CowAuthentication(address(wrapperAuth)));
251+
MockWrapper differentWrapper = new MockWrapper(CowSettlement(address(differentSettlement)), 4);
252+
wrapperAuth.addSolver(address(differentWrapper));
253+
254+
CowWrapperHelpers.WrapperCall[] memory wrapperCalls = new CowWrapperHelpers.WrapperCall[](2);
255+
wrapperCalls[0] = CowWrapperHelpers.WrapperCall({target: address(wrapper1), data: hex"deadbeef"});
256+
wrapperCalls[1] = CowWrapperHelpers.WrapperCall({target: address(differentWrapper), data: hex"cafebabe"});
257+
258+
vm.expectRevert(
259+
abi.encodeWithSelector(
260+
CowWrapperHelpers.SettlementMismatch.selector, 1, address(mockSettlement), address(differentSettlement)
261+
)
262+
);
263+
helpers.verifyAndBuildWrapperData(wrapperCalls);
264+
}
265+
249266
function test_immutableAuthenticators() public view {
250267
assertEq(address(helpers.WRAPPER_AUTHENTICATOR()), address(wrapperAuth));
251268
assertEq(address(helpers.SOLVER_AUTHENTICATOR()), address(solverAuth));
252269
}
270+
271+
function test_verifyAndBuildWrapperData_RevertsOnWrapperDataTooLong_FirstWrapper() public {
272+
// Create data that's exactly 65536 bytes (exceeds uint16 max of 65535)
273+
bytes memory tooLongData = new bytes(65536);
274+
275+
// Create a wrapper that consumes all bytes passed to it
276+
MockWrapper largeWrapper = new MockWrapper(CowSettlement(address(mockSettlement)), 65536);
277+
wrapperAuth.addSolver(address(largeWrapper));
278+
279+
CowWrapperHelpers.WrapperCall[] memory wrapperCalls = new CowWrapperHelpers.WrapperCall[](1);
280+
wrapperCalls[0] = CowWrapperHelpers.WrapperCall({target: address(largeWrapper), data: tooLongData});
281+
282+
vm.expectRevert(abi.encodeWithSelector(CowWrapperHelpers.WrapperDataTooLong.selector, 0, 65536));
283+
helpers.verifyAndBuildWrapperData(wrapperCalls);
284+
}
285+
286+
function test_verifyAndBuildWrapperData_RevertsOnWrapperDataTooLong_SecondWrapper() public {
287+
// Create data that's exactly 65536 bytes for the second wrapper
288+
bytes memory tooLongData = new bytes(65536);
289+
290+
// Create a wrapper that consumes all bytes passed to it
291+
MockWrapper largeWrapper = new MockWrapper(CowSettlement(address(mockSettlement)), 65536);
292+
wrapperAuth.addSolver(address(largeWrapper));
293+
294+
CowWrapperHelpers.WrapperCall[] memory wrapperCalls = new CowWrapperHelpers.WrapperCall[](2);
295+
wrapperCalls[0] = CowWrapperHelpers.WrapperCall({target: address(wrapper1), data: hex"deadbeef"});
296+
wrapperCalls[1] = CowWrapperHelpers.WrapperCall({target: address(largeWrapper), data: tooLongData});
297+
298+
vm.expectRevert(abi.encodeWithSelector(CowWrapperHelpers.WrapperDataTooLong.selector, 1, 65536));
299+
helpers.verifyAndBuildWrapperData(wrapperCalls);
300+
}
301+
302+
function test_verifyAndBuildWrapperData_SucceedsWithMaxLengthData() public {
303+
// Create data that's exactly 65535 bytes (max valid uint16)
304+
bytes memory maxLengthData = new bytes(65535);
305+
306+
// Create a wrapper that consumes all bytes
307+
MockWrapper largeWrapper = new MockWrapper(CowSettlement(address(mockSettlement)), 65535);
308+
wrapperAuth.addSolver(address(largeWrapper));
309+
310+
CowWrapperHelpers.WrapperCall[] memory wrapperCalls = new CowWrapperHelpers.WrapperCall[](1);
311+
wrapperCalls[0] = CowWrapperHelpers.WrapperCall({target: address(largeWrapper), data: maxLengthData});
312+
313+
// Should not revert - 65535 is the max valid length
314+
bytes memory result = helpers.verifyAndBuildWrapperData(wrapperCalls);
315+
316+
// Verify the length prefix is correct (first 2 bytes)
317+
bytes2 lengthPrefix;
318+
assembly {
319+
lengthPrefix := mload(add(result, 32))
320+
}
321+
assertEq(uint16(lengthPrefix), 65535);
322+
}
253323
}

0 commit comments

Comments
 (0)