1- // SPDX-License-Identifier: GPL-3.0-or-later
1+ // SPDX-License-Identifier: MIT OR Apache-2.0
22pragma solidity ^ 0.8 ;
33
44import {Test} from "forge-std/Test.sol " ;
5- import {CowWrapper, ICowSettlement, ICowAuthentication} from "../../src/CowWrapper.sol " ;
5+ import {CowWrapper, ICowWrapper, ICowSettlement, ICowAuthentication} from "../../src/CowWrapper.sol " ;
66import {EmptyWrapper} from "../EmptyWrapper.sol " ;
77
88import {MockWrapper, MockCowSettlement, MockCowAuthentication} from "./mocks/MockCowProtocol.sol " ;
@@ -28,9 +28,11 @@ contract CowWrapperTest is Test {
2828 authenticator.setSolver (solver, true );
2929
3030 // Create test wrapper and three EmptyWrapper instances with the settlement contract
31- wrapper1 = new MockWrapper (ICowSettlement (address (mockSettlement)), 65536 );
32- wrapper2 = new MockWrapper (ICowSettlement (address (mockSettlement)), 65536 );
33- wrapper3 = new MockWrapper (ICowSettlement (address (mockSettlement)), 65536 );
31+ // (use type(uint16).max because it will force consuming all the wrapper data, which is
32+ // most useful for these tests)
33+ wrapper1 = new MockWrapper (ICowSettlement (address (mockSettlement)), type (uint16 ).max);
34+ wrapper2 = new MockWrapper (ICowSettlement (address (mockSettlement)), type (uint16 ).max);
35+ wrapper3 = new MockWrapper (ICowSettlement (address (mockSettlement)), type (uint16 ).max);
3436
3537 // Add all wrappers as solvers
3638 authenticator.setSolver (address (wrapper1), true );
@@ -51,27 +53,39 @@ contract CowWrapperTest is Test {
5153 address [] memory tokens = new address [](tokenCount);
5254 uint256 [] memory clearingPrices = new uint256 [](tokenCount);
5355 for (uint256 i = 0 ; i < tokenCount; i++ ) {
54- tokens[i] = makeAddr (string ( abi.encodePacked ("Settle Token # " , vm.toString (i + 1 ) )));
56+ tokens[i] = makeAddr (string . concat ("Settle Token # " , vm.toString (i + 1 )));
5557 clearingPrices[i] = 100 * (i + 1 );
5658 }
57- return abi.encodeWithSelector (
58- ICowSettlement.settle.selector , tokens, clearingPrices, new ICowSettlement.Trade [](0 ), _emptyInteractions ()
59+ return abi.encodeCall (
60+ ICowSettlement.settle, (tokens, clearingPrices, new ICowSettlement.Trade [](0 ), _emptyInteractions ())
61+ );
62+ }
63+
64+ function test_verifyInitialState () public {
65+ assertEq (
66+ address (wrapper1.SETTLEMENT ()),
67+ address (mockSettlement),
68+ "Settlement contract should be initialized correctly "
69+ );
70+ assertEq (
71+ address (wrapper1.AUTHENTICATOR ()),
72+ address (authenticator),
73+ "Authenticator contract should be initialized from the settlement contract "
5974 );
6075 }
6176
6277 function test_next_CallsWrapperAndThenNextSettlement () public {
6378 bytes memory settleData = abi.encodePacked (_createSimpleSettleData (1 ), hex "123456 " );
64- bytes memory secondCallWrapperData = hex "0003098765 " ;
65- bytes memory wrapperData = abi.encodePacked (hex "00021234 " , address (wrapper1), secondCallWrapperData);
79+ // here we encode [2-byte len] followed by the actual wrapper data (which is 3 bytes, 6 chars hex)
80+ bytes memory secondCallWrapperData = abi.encodePacked (uint16 (3 ), hex "098765 " );
81+ // here we encode [2-byte len] followed by the actual wrapper data (which is 2 bytes, 4 chars hex), and build the chain
82+ bytes memory wrapperData = abi.encodePacked (uint16 (2 ), hex "1234 " , address (wrapper1), secondCallWrapperData);
6683
67- // the wrapper gets called exactly twice (once below and again inside the wrapper data calling self)
68- vm.expectCall (address (wrapper1), 0 , abi.encodeWithSelector (wrapper1 .wrappedSettle. selector ), 2 );
84+ // verify the outside wrapper call data
85+ vm.expectCall (address (wrapper1), abi.encodeCall (ICowWrapper .wrappedSettle, (settleData, wrapperData)) );
6986
7087 // verify the internal wrapper call data
71- vm.expectCall (
72- address (wrapper1),
73- abi.encodeWithSelector (wrapper1.wrappedSettle.selector , settleData, secondCallWrapperData)
74- );
88+ vm.expectCall (address (wrapper1), abi.encodeCall (ICowWrapper.wrappedSettle, (settleData, secondCallWrapperData)));
7589
7690 // the settlement contract gets called once after wrappers (including the surplus data at the end)
7791 vm.expectCall (address (mockSettlement), 0 , settleData, 1 );
@@ -108,20 +122,22 @@ contract CowWrapperTest is Test {
108122 settlement.clearingPrices[0 ] = 100 ;
109123 settlement.clearingPrices[1 ] = 200 ;
110124
111- settlement.trades = new ICowSettlement.Trade [](1 );
112- settlement.trades[0 ] = ICowSettlement.Trade ({
113- sellTokenIndex: 0 ,
114- buyTokenIndex: 1 ,
115- receiver: address (0x123 ),
116- sellAmount: 1000 ,
117- buyAmount: 900 ,
118- validTo: uint32 (block .timestamp + 1000 ),
119- appData: bytes32 (uint256 (1 )),
120- feeAmount: 10 ,
121- flags: 0 ,
122- executedAmount: 0 ,
123- signature: hex "aabbccddee "
124- });
125+ settlement.trades = new ICowSettlement.Trade [](10 );
126+ for (uint256 i = 0 ; i < 10 ; i++ ) {
127+ settlement.trades[i] = ICowSettlement.Trade ({
128+ sellTokenIndex: 0 ,
129+ buyTokenIndex: 1 ,
130+ receiver: address (0x123 ),
131+ sellAmount: 1000 * i,
132+ buyAmount: 900 * i,
133+ validTo: uint32 (block .timestamp + 1000 ),
134+ appData: bytes32 (uint256 (1 )),
135+ feeAmount: 10 ,
136+ flags: 0 ,
137+ executedAmount: 0 ,
138+ signature: hex "aabbccddee "
139+ });
140+ }
125141
126142 settlement.interactions = [
127143 new ICowSettlement.Interaction [](0 ),
@@ -144,10 +160,13 @@ contract CowWrapperTest is Test {
144160 (address target , bytes memory fullCalldata ) =
145161 CowWrapperHelpers.encodeWrapperCall (wrappers, datas, address (mockSettlement), settlement);
146162
147- // all the wrappers gets called, with wrapper 1 called twice
148- vm.expectCall (address (wrapper1), 0 , abi.encodeWithSelector (wrapper1.wrappedSettle.selector ), 2 );
149- vm.expectCall (address (wrapper2), 0 , abi.encodeWithSelector (wrapper1.wrappedSettle.selector ), 1 );
150- vm.expectCall (address (wrapper3), 0 , abi.encodeWithSelector (wrapper1.wrappedSettle.selector ), 1 );
163+ // all the wrappers get called, with wrapper 1 called twice
164+
165+ // we only want to verify that wrappedSettle was called. (not the specific data passed to wrappedSettle)
166+ bytes memory wrappedSettleSelector = abi.encodePacked (ICowWrapper.wrappedSettle.selector );
167+ vm.expectCall (address (wrapper1), 0 , wrappedSettleSelector, 2 );
168+ vm.expectCall (address (wrapper2), 0 , wrappedSettleSelector, 1 );
169+ vm.expectCall (address (wrapper3), 0 , wrappedSettleSelector, 1 );
151170
152171 // the settlement gets called with the full data
153172 vm.expectCall (address (mockSettlement), new bytes (0 ));
@@ -157,4 +176,106 @@ contract CowWrapperTest is Test {
157176 (bool success ,) = target.call (fullCalldata);
158177 assertTrue (success, "Chained wrapper call should succeed " );
159178 }
179+
180+ function test_wrappedSettle_RevertsOnZeroLengthWrapperData () public {
181+ bytes memory settleData = _createSimpleSettleData (0 );
182+ bytes memory wrapperData = hex "" ; // Completely empty wrapper data
183+
184+ vm.prank (solver);
185+ vm.expectRevert (); // Should revert with out-of-bounds array access
186+ wrapper1.wrappedSettle (settleData, wrapperData);
187+ }
188+
189+ function test_wrappedSettle_RevertsOnOneByteWrapperData () public {
190+ bytes memory settleData = _createSimpleSettleData (0 );
191+ bytes memory wrapperData = hex "01 " ; // Only 1 byte - not enough to read the 2-byte length
192+
193+ vm.prank (solver);
194+ vm.expectRevert (); // Should revert with out-of-bounds array access
195+ wrapper1.wrappedSettle (settleData, wrapperData);
196+ }
197+
198+ function test_wrappedSettle_SucceedsWithZeroLengthIndicator () public {
199+ bytes memory settleData = _createSimpleSettleData (0 );
200+ bytes memory wrapperData = hex "0000 " ; // 2 bytes indicating 0-length wrapper data
201+
202+ // Should call settlement directly with no wrapper-specific data
203+ vm.expectCall (address (mockSettlement), 0 , settleData, 1 );
204+
205+ vm.prank (solver);
206+ wrapper1.wrappedSettle (settleData, wrapperData);
207+ }
208+
209+ function test_wrappedSettle_SucceedsWithMaximumLengthWrapperData () public {
210+ bytes memory settleData = _createSimpleSettleData (0 );
211+
212+ // Create maximum length wrapper data (65535 bytes)
213+ // Format: [2-byte length = 0xFFFF][65535 bytes of data]
214+ bytes memory maxData = new bytes (65535 );
215+ for (uint256 i = 0 ; i < 65535 ; i++ ) {
216+ maxData[i] = bytes1 (uint8 (i % 256 ));
217+ }
218+
219+ bytes memory wrapperData = abi.encodePacked (uint16 (65535 ), maxData);
220+
221+ // Should successfully parse the maximum length data and call settlement
222+ vm.expectCall (address (mockSettlement), 0 , settleData, 1 );
223+
224+ vm.prank (solver);
225+ wrapper1.wrappedSettle (settleData, wrapperData);
226+ }
227+
228+ function test_wrappedSettle_RevertsWhenDataShorterThanIndicated () public {
229+ bytes memory settleData = _createSimpleSettleData (0 );
230+
231+ // Wrapper data claims to be 100 bytes but only provides 50
232+ bytes memory shortData = new bytes (50 );
233+ bytes memory wrapperData = abi.encodePacked (uint16 (100 ), shortData);
234+
235+ vm.prank (solver);
236+ vm.expectRevert (); // Should revert with out-of-bounds array access
237+ wrapper1.wrappedSettle (settleData, wrapperData);
238+ }
239+
240+ function test_wrappedSettle_SucceedsWithMaxLengthAndNextWrapper () public {
241+ bytes memory settleData = _createSimpleSettleData (0 );
242+
243+ // Create maximum length wrapper data followed by next wrapper address
244+ bytes memory maxData = new bytes (65535 );
245+ for (uint256 i = 0 ; i < 65535 ; i++ ) {
246+ maxData[i] = bytes1 (uint8 (i % 256 ));
247+ }
248+
249+ // Format: [2-byte length = 0xFFFF][65535 bytes of data][20-byte next wrapper address][remaining data]
250+ bytes memory nextWrapperData = hex "00030000FF " ; // 3 bytes of data for next wrapper
251+ bytes memory wrapperData = abi.encodePacked (type (uint16 ).max, maxData, address (wrapper2), nextWrapperData);
252+
253+ // Should call wrapper2 with the remaining data
254+ vm.expectCall (address (wrapper2), 0 , abi.encodePacked (ICowWrapper.wrappedSettle.selector ), 1 );
255+
256+ vm.prank (solver);
257+ wrapper1.wrappedSettle (settleData, wrapperData);
258+ }
259+
260+ function test_wrappedSettle_RevertsWithInsufficientLengthData () public {
261+ bytes memory settleData = _createSimpleSettleData (0 );
262+
263+ // Format: [1-byte length = 1 (insufficient)]
264+ bytes memory wrapperData = hex "01 " ;
265+
266+ vm.expectRevert (new bytes (0 ));
267+ vm.prank (solver);
268+ wrapper1.wrappedSettle (settleData, wrapperData);
269+ }
270+
271+ function test_wrappedSettle_RevertsWithInsufficientCallData () public {
272+ bytes memory settleData = _createSimpleSettleData (0 );
273+
274+ // Format: [2-byte length = 0xa][9 bytes of data (insufficient)]
275+ bytes memory wrapperData = hex "000A123412341234123412 " ;
276+
277+ vm.expectRevert (new bytes (0 ));
278+ vm.prank (solver);
279+ wrapper1.wrappedSettle (settleData, wrapperData);
280+ }
160281}
0 commit comments