Skip to content

Commit 28cc486

Browse files
committed
refacto: remove a verification from the order and deploy a new contract
1 parent 8983be8 commit 28cc486

File tree

7 files changed

+105
-43
lines changed

7 files changed

+105
-43
lines changed

.env.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
POLYSWAP_HANDLER=0x0D92aFDB0D334c221A946927877b12157Bed2F5d # with polymarket mock
1+
POLYSWAP_HANDLER=0x868c662d186689f773fAb37ea617E7cbeB293D86
22
COMPOSABLE_COW=0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74
33
POLYMARKET_MOCK=0x62e36fBc7D6518A3DeEA0f863f50c8AC4c4a7695 # Polymarket mock contract
44
POLYMARKET=0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The `Polyswap` contract uses the Polymarket `CTFExchange` contract to check, via
2121
* the remaining amount is `0`.
2222

2323
If both conditions are met, the swap is executed using the Composable CoW Swap protocol.
24+
Both conditions being met means that the limit order has been filled and that the price of the limit order has been achieved.
2425

2526
## 🧪 Stack
2627

@@ -32,8 +33,8 @@ If both conditions are met, the swap is executed using the Composable CoW Swap p
3233

3334
All contracts are deployed on **Polygon** for compatibility with Polymarket's on-chain infrastructure.
3435

35-
Contract Address: `0xC75f4070B794cE1EC7273767a7d67354F845c7ce`
36-
View on [Sourcify](https://repo.sourcify.dev/137/0xC75f4070B794cE1EC7273767a7d67354F845c7ce)
36+
Contract Address: `0x868c662d186689f773fAb37ea617E7cbeB293D86`
37+
View on [Sourcify](https://repo.sourcify.dev/137/0x868c662d186689f773fAb37ea617E7cbeB293D86)
3738

3839
## 🧑‍💻 Authors
3940

script/SubmitSingleOrder.s.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pragma solidity >=0.8.0 <0.9.0;
44
import {Script} from "forge-std/Script.sol";
55
import {console} from "forge-std/console.sol";
66

7+
import {GPv2Order} from "cowprotocol/contracts/libraries/GPv2Order.sol";
78
import {IERC20} from "cowprotocol/contracts/interfaces/IERC20.sol";
89

910
// Safe contracts
@@ -51,7 +52,7 @@ contract SubmitSingleOrder is Script {
5152
staticInput: abi.encode(polyswapOrder)
5253
});
5354

54-
console.logBytes32(composableCow.hash(params));
55+
console.logBytes32(GPv2Order.hash(PolyswapOrder.orderFor(polyswapOrder), composableCow.domainSeparator()));
5556

5657
vm.startBroadcast(deployerPrivateKey);
5758

src/Polyswap.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ contract Polyswap is BaseConditionalOrder {
5050
*/
5151
PolyswapOrder.Data memory polyswapOrder = abi.decode(staticInput, (PolyswapOrder.Data));
5252

53-
order = PolyswapOrder.orderFor(polyswapOrder, polymarket);
53+
order = PolyswapOrder.orderFor(polyswapOrder);
5454

5555
// check if the polymarket order is fulfilled
5656
OrderStatus memory status = polymarket.getOrderStatus(polyswapOrder.polymarketOrderHash);

src/PolyswapOrder.sol

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ string constant INVALID_MIN_BUY_AMOUNT = "invalid min buy amount";
1818
string constant INVALID_POLYMARKET_ORDER_HASH = "invalid order hash";
1919
string constant INVALID_START_DATE = "invalid start date";
2020
string constant INVALID_END_DATE = "invalid end date";
21+
string constant INVALID_RECEIVER = "invalid receiver";
2122

2223
/**
2324
* @title Polyswap Order Library
@@ -46,32 +47,29 @@ library PolyswapOrder {
4647
* @dev revert if the order is invalid
4748
* @param self The PolyswapOrder order to validate
4849
*/
49-
function validate(Data memory self, Trading polymarket) internal view {
50+
function validate(Data memory self) internal view {
5051
if (self.sellToken == self.buyToken) revert IConditionalOrder.OrderNotValid(INVALID_SAME_TOKEN);
5152
if (address(self.sellToken) == address(0) || address(self.buyToken) == address(0)) {
5253
revert IConditionalOrder.OrderNotValid(INVALID_TOKEN);
5354
}
55+
if (self.receiver == address(0)) revert IConditionalOrder.OrderNotValid(INVALID_RECEIVER);
5456
if (self.t0 > block.timestamp) revert IConditionalOrder.OrderNotValid(INVALID_START_DATE);
5557
if (self.t <= self.t0 || self.t < block.timestamp) revert IConditionalOrder.OrderNotValid(INVALID_END_DATE);
5658
if (self.sellAmount < 0) revert IConditionalOrder.OrderNotValid(INVALID_SELL_AMOUNT);
5759
if (self.minBuyAmount < 0) revert IConditionalOrder.OrderNotValid(INVALID_MIN_BUY_AMOUNT);
5860

5961
// Check if the Polymarket order is valid and not filled or cancelled.
6062
if (self.polymarketOrderHash == 0) revert IConditionalOrder.OrderNotValid(INVALID_POLYMARKET_ORDER_HASH);
61-
OrderStatus memory order = polymarket.getOrderStatus(self.polymarketOrderHash);
62-
if (order.remaining == 0 && order.isFilledOrCancelled == false) {
63-
revert IConditionalOrder.OrderNotValid(INVALID_POLYMARKET_ORDER_HASH);
64-
}
6563
}
6664

6765
/**
6866
* @dev Generate the `GPv2Order` of the Polyswap order.
6967
* @param self The Polyswap order to generate the order for.
7068
* @return order The `GPv2Order` of the Polyswap order.
7169
*/
72-
function orderFor(Data memory self, Trading polymarket) internal view returns (GPv2Order.Data memory order) {
70+
function orderFor(Data memory self) internal view returns (GPv2Order.Data memory order) {
7371
// First, validate and revert if the order is invalid.
74-
validate(self, polymarket);
72+
validate(self);
7573

7674
order = GPv2Order.Data({
7775
sellToken: self.sellToken,

test/Notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@
2424
#### create a polyswap order
2525
`forge script ./script/SubmitSingleOrder.s.sol:SubmitSingleOrder --rpc-url https://polygon-rpc.com --broadcast --private-key $PRIVATE_KEY`
2626

27-
### set the order status to filled in the mock contract
27+
### set the order status to filled in the mock contract (simulate polymarket limit order filled)
2828
`cast send $POLYMARKET_MOCK "setOrderStatus(bytes32,bool,uint256)" $POLYMARKET_ORDER_HASH true 0 --private-key $PRIVATE_KEY --rpc-url https://polygon-rpc.com`

test/PolyswapTest.t.sol

Lines changed: 92 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {Enum} from "safe/common/Enum.sol";
1515
// Composable CoW
1616
import {IConditionalOrder, ComposableCoW} from "composable-cow/src/ComposableCoW.sol";
1717
import {IValueFactory} from "composable-cow/src/interfaces/IValueFactory.sol";
18-
import {ExtensibleFallbackHandler} from "safe/handler/ExtensibleFallbackHandler.sol";
18+
import {ExtensibleFallbackHandler, ERC1271} from "safe/handler/ExtensibleFallbackHandler.sol";
1919
import {SignatureVerifierMuxer} from "safe/handler/extensible/SignatureVerifierMuxer.sol";
2020
import {ISafeSignatureVerifier} from "safe/handler/extensible/SignatureVerifierMuxer.sol";
2121

@@ -50,8 +50,8 @@ contract PolyswapTest is Test {
5050
address public constant POLYGON_TIMESTAMP_VALUE_FACTORY = 0x52eD56Da04309Aca4c3FECC595298d80C2f16BAc; // TimestampValueFactory on Polygon
5151

5252
// BLINDSPOT: Need real Polymarket contract address on Polygon
53-
// Polymarket CTF Exchange contract address - needs to be updated with real address
54-
address public constant POLYGON_POLYMARKET_EXCHANGE = address(0); // UPDATE REQUIRED
53+
// Polymarket CTF Exchange contract address
54+
address public constant POLYGON_POLYMARKET_EXCHANGE = address(0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E);
5555

5656
// Test tokens on Polygon
5757
address public constant USDC_POLYGON = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;
@@ -145,10 +145,10 @@ contract PolyswapTest is Test {
145145
PolyswapOrder.Data memory order = _createTestOrder();
146146

147147
// Should not revert on validation
148-
PolyswapOrder.validate(order, Trading(address(mockPolymarket)));
148+
PolyswapOrder.validate(order);
149149

150150
// Should generate valid GPv2Order
151-
GPv2Order.Data memory gpv2Order = PolyswapOrder.orderFor(order, Trading(address(mockPolymarket)));
151+
GPv2Order.Data memory gpv2Order = PolyswapOrder.orderFor(order);
152152

153153
assertEq(address(gpv2Order.sellToken), address(order.sellToken));
154154
assertEq(address(gpv2Order.buyToken), address(order.buyToken));
@@ -206,28 +206,28 @@ contract PolyswapTest is Test {
206206
invalidOrder.buyToken = invalidOrder.sellToken;
207207

208208
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, "same token"));
209-
PolyswapOrder.validate(invalidOrder, Trading(address(mockPolymarket)));
209+
PolyswapOrder.validate(invalidOrder);
210210

211211
// Test zero sell amount
212212
invalidOrder = _createTestOrder();
213213
invalidOrder.sellAmount = 0;
214214

215215
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, "invalid sell amount"));
216-
PolyswapOrder.validate(invalidOrder, Trading(address(mockPolymarket)));
216+
PolyswapOrder.validate(invalidOrder);
217217

218218
// Test zero buy amount
219219
invalidOrder = _createTestOrder();
220220
invalidOrder.minBuyAmount = 0;
221221

222222
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, "invalid min buy amount"));
223-
PolyswapOrder.validate(invalidOrder, Trading(address(mockPolymarket)));
223+
PolyswapOrder.validate(invalidOrder);
224224

225225
// Test invalid time range
226226
invalidOrder = _createTestOrder();
227227
invalidOrder.t = invalidOrder.t0 - 1;
228228

229229
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, "invalid end date"));
230-
PolyswapOrder.validate(invalidOrder, Trading(address(mockPolymarket)));
230+
PolyswapOrder.validate(invalidOrder);
231231
}
232232

233233
// ===== INTEGRATION TESTS WITH REAL CONTRACTS =====
@@ -325,7 +325,7 @@ contract PolyswapTest is Test {
325325
order.t = uint256(block.timestamp - 1); // Expired
326326

327327
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, INVALID_END_DATE));
328-
PolyswapOrder.validate(order, Trading(address(mockPolymarket)));
328+
PolyswapOrder.validate(order);
329329
}
330330

331331
/**
@@ -337,7 +337,7 @@ contract PolyswapTest is Test {
337337
order.polymarketOrderHash = bytes32(0);
338338

339339
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, INVALID_POLYMARKET_ORDER_HASH));
340-
PolyswapOrder.validate(order, Trading(address(mockPolymarket)));
340+
PolyswapOrder.validate(order);
341341
}
342342

343343
// TODO more negative tests
@@ -352,13 +352,13 @@ contract PolyswapTest is Test {
352352

353353
// Measure validation gas
354354
uint256 gasStart = gasleft();
355-
PolyswapOrder.validate(order, Trading(address(mockPolymarket)));
355+
PolyswapOrder.validate(order);
356356
uint256 validationGas = gasStart - gasleft();
357357
console.log("Validation gas:", validationGas);
358358

359359
// Measure order generation gas
360360
gasStart = gasleft();
361-
PolyswapOrder.orderFor(order, Trading(address(mockPolymarket)));
361+
PolyswapOrder.orderFor(order);
362362
uint256 orderGenGas = gasStart - gasleft();
363363
console.log("Order generation gas:", orderGenGas);
364364

@@ -367,6 +367,68 @@ contract PolyswapTest is Test {
367367
assertTrue(orderGenGas < 30000, "Order generation gas too high");
368368
}
369369

370+
/**
371+
* @dev Test isValidSafeSignature function from ComposableCoW in real context
372+
*/
373+
function test_integration_isValidSafeSignature() public {
374+
// Setup: Polymarket order fulfilled so condition is met
375+
mockPolymarket.setOrderStatus(TEST_ORDER_HASH, true, 0);
376+
377+
PolyswapOrder.Data memory order = _createTestOrder();
378+
379+
// Create conditional order params
380+
IConditionalOrder.ConditionalOrderParams memory params = IConditionalOrder.ConditionalOrderParams({
381+
handler: IConditionalOrder(address(polyswap)),
382+
salt: keccak256(abi.encodePacked("signature-test", block.timestamp)),
383+
staticInput: abi.encode(order)
384+
});
385+
386+
// Create the conditional order first
387+
vm.prank(owner);
388+
testSafe.executeSingleOwner(
389+
address(composableCow),
390+
0,
391+
abi.encodeCall(composableCow.create, (params, false)),
392+
Enum.Operation.Call,
393+
owner
394+
);
395+
396+
// Get the tradeable order that would be generated
397+
GPv2Order.Data memory gpv2Order = polyswap.getTradeableOrder(
398+
address(testSafe),
399+
address(this),
400+
bytes32(0),
401+
abi.encode(order),
402+
bytes("")
403+
);
404+
405+
// Create the payload for isValidSafeSignature
406+
ComposableCoW.PayloadStruct memory payload = ComposableCoW.PayloadStruct({
407+
proof: new bytes32[](0), // Empty proof for single order
408+
params: params,
409+
offchainInput: bytes("")
410+
});
411+
412+
// Hash the order for signature validation
413+
bytes32 orderHash = GPv2Order.hash(gpv2Order, composableCow.domainSeparator());
414+
415+
bytes32 domainSeparator = composableCow.domainSeparator();
416+
417+
// Call isValidSafeSignature - this should succeed
418+
bytes4 result = composableCow.isValidSafeSignature(
419+
testSafe,
420+
address(this),
421+
orderHash,
422+
domainSeparator,
423+
GPv2Order.TYPE_HASH,
424+
abi.encode(gpv2Order),
425+
abi.encode(payload)
426+
);
427+
428+
// Should return the ERC1271 magic value
429+
assertEq(result, ERC1271.isValidSignature.selector);
430+
}
431+
370432
// /**
371433
// * @dev Test with maximum uint256 values to check for overflows
372434
// */
@@ -409,7 +471,7 @@ contract PolyswapTest is Test {
409471
PolyswapOrder.Data memory order = PolyswapOrder.Data({
410472
sellToken: IERC20(USDC_POLYGON),
411473
buyToken: IERC20(USDT_POLYGON),
412-
receiver: address(0),
474+
receiver: address(1),
413475
sellAmount: sellAmount,
414476
minBuyAmount: minBuyAmount,
415477
t0: block.timestamp,
@@ -419,8 +481,8 @@ contract PolyswapTest is Test {
419481
});
420482

421483
// Should not revert with valid parameters
422-
PolyswapOrder.validate(order, Trading(address(mockPolymarket)));
423-
GPv2Order.Data memory gpv2Order = PolyswapOrder.orderFor(order, Trading(address(mockPolymarket)));
484+
PolyswapOrder.validate(order);
485+
GPv2Order.Data memory gpv2Order = PolyswapOrder.orderFor(order);
424486

425487
assertEq(gpv2Order.sellAmount, sellAmount);
426488
assertEq(gpv2Order.buyAmount, minBuyAmount);
@@ -469,19 +531,19 @@ contract PolyswapTest is Test {
469531
safeOwner
470532
);
471533

472-
// // Set domain verifier for CoW Protocol through the fallback handler
473-
// bytes32 domainSeparator = composableCow.domainSeparator();
474-
// safe.executeSingleOwner(
475-
// address(safe),
476-
// 0,
477-
// abi.encodeWithSelector(
478-
// SignatureVerifierMuxer.setDomainVerifier.selector,
479-
// domainSeparator,
480-
// ISafeSignatureVerifier(composableCow)
481-
// ),
482-
// Enum.Operation.Call,
483-
// safeOwner
484-
// );
534+
// Set domain verifier for CoW Protocol through the fallback handler
535+
bytes32 domainSeparator = composableCow.domainSeparator();
536+
safe.executeSingleOwner(
537+
address(safe),
538+
0,
539+
abi.encodeWithSelector(
540+
SignatureVerifierMuxer.setDomainVerifier.selector,
541+
domainSeparator,
542+
ISafeSignatureVerifier(composableCow)
543+
),
544+
Enum.Operation.Call,
545+
safeOwner
546+
);
485547

486548
vm.stopPrank();
487549
}
@@ -507,7 +569,7 @@ contract PolyswapTest is Test {
507569
return PolyswapOrder.Data({
508570
sellToken: IERC20(USDC_POLYGON),
509571
buyToken: IERC20(USDT_POLYGON),
510-
receiver: address(0),
572+
receiver: address(1),
511573
sellAmount: 100000, // 0.1 USDC (6 decimals)
512574
minBuyAmount: 80000, // 0.08 USDT (6 decimals)
513575
t0: block.timestamp,

0 commit comments

Comments
 (0)