From b4a49c6e5edc50867dfe03dc55a7d1dbeef4f9cf Mon Sep 17 00:00:00 2001 From: Xiangyu Xu Date: Thu, 20 Feb 2025 02:54:46 +0800 Subject: [PATCH 1/6] Resolve the auditor's comments -- fillOrder and cancelOrder --- .../matching/HyperdriveMatchingEngineV2.sol | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/contracts/src/matching/HyperdriveMatchingEngineV2.sol b/contracts/src/matching/HyperdriveMatchingEngineV2.sol index 5dea3486b..2ccff748c 100644 --- a/contracts/src/matching/HyperdriveMatchingEngineV2.sol +++ b/contracts/src/matching/HyperdriveMatchingEngineV2.sol @@ -383,6 +383,11 @@ contract HyperdriveMatchingEngineV2 is OrderIntent calldata _makerOrder, OrderIntent calldata _takerOrder ) external nonReentrant { + // Ensure sender is the trader. + if (msg.sender != _takerOrder.trader) { + revert InvalidSender(); + } + // Validate maker order and taker order. bytes32 makerOrderHash = _validateOrdersWithTaker( _makerOrder, @@ -839,7 +844,20 @@ contract HyperdriveMatchingEngineV2 is OrderIntent[] calldata _orders ) external nonReentrant { bytes32[] memory orderHashes = new bytes32[](_orders.length); - for (uint256 i = 0; i < _orders.length; i++) { + uint256 validOrderCount = 0; + + uint256 orderCount = _orders.length; + for (uint256 i = 0; i < orderCount; i++) { + // Skip if order is already fully executed + bytes32 orderHash = hashOrderIntent(_orders[i]); + if ( + orderAmountsUsed[orderHash].bondAmount >= + _orders[i].bondAmount || + orderAmountsUsed[orderHash].fundAmount >= _orders[i].fundAmount + ) { + continue; + } + // Ensure sender is the trader. if (msg.sender != _orders[i].trader) { revert InvalidSender(); @@ -853,9 +871,14 @@ contract HyperdriveMatchingEngineV2 is // Cancel the order. isCancelled[orderHash] = true; - orderHashes[i] = orderHash; + orderHashes[validOrderCount] = orderHash; + validOrderCount++; } + // Emit event with assembly to truncate array to actual size + assembly { + mstore(orderHashes, validOrderCount) + } emit OrdersCancelled(msg.sender, orderHashes); } From 86301953eb902f659fbd4f0a9455a605254da8e7 Mon Sep 17 00:00:00 2001 From: Xiangyu Xu Date: Thu, 20 Feb 2025 08:43:39 +0800 Subject: [PATCH 2/6] Fix a bug --- contracts/src/matching/HyperdriveMatchingEngineV2.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/src/matching/HyperdriveMatchingEngineV2.sol b/contracts/src/matching/HyperdriveMatchingEngineV2.sol index 2ccff748c..bc0344c55 100644 --- a/contracts/src/matching/HyperdriveMatchingEngineV2.sol +++ b/contracts/src/matching/HyperdriveMatchingEngineV2.sol @@ -864,7 +864,6 @@ contract HyperdriveMatchingEngineV2 is } // Verify signature. - bytes32 orderHash = hashOrderIntent(_orders[i]); if (!verifySignature(orderHash, _orders[i].signature, msg.sender)) { revert InvalidSignature(); } From 47460fc34484f6ade3c6e426ee2eeb9f190f2fd6 Mon Sep 17 00:00:00 2001 From: Xiangyu Xu Date: Thu, 20 Feb 2025 09:05:33 +0800 Subject: [PATCH 3/6] Resolved some informational findings --- contracts/src/matching/HyperdriveMatchingEngineV2.sol | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contracts/src/matching/HyperdriveMatchingEngineV2.sol b/contracts/src/matching/HyperdriveMatchingEngineV2.sol index bc0344c55..cf6379c64 100644 --- a/contracts/src/matching/HyperdriveMatchingEngineV2.sol +++ b/contracts/src/matching/HyperdriveMatchingEngineV2.sol @@ -76,6 +76,9 @@ contract HyperdriveMatchingEngineV2 is /// @param _order2 The second order to match. /// @param _surplusRecipient The address that receives the surplus funds /// from matching the trades. + /// @dev In the case of _handleMint(), the matching logic is "exact price" + /// semantics according to the order intent, to within numerical precision, + /// with any surplus going to the party that executed the match. function matchOrders( OrderIntent calldata _order1, OrderIntent calldata _order2, @@ -848,12 +851,14 @@ contract HyperdriveMatchingEngineV2 is uint256 orderCount = _orders.length; for (uint256 i = 0; i < orderCount; i++) { - // Skip if order is already fully executed + // Skip if order is already fully executed or cancelled bytes32 orderHash = hashOrderIntent(_orders[i]); if ( orderAmountsUsed[orderHash].bondAmount >= _orders[i].bondAmount || - orderAmountsUsed[orderHash].fundAmount >= _orders[i].fundAmount + orderAmountsUsed[orderHash].fundAmount >= + _orders[i].fundAmount || + isCancelled[orderHash] ) { continue; } From dc932fe380238e85f3a52baba3b071782c1fedb9 Mon Sep 17 00:00:00 2001 From: Xiangyu Xu Date: Thu, 20 Feb 2025 09:59:50 +0800 Subject: [PATCH 4/6] Fixed some failing tests --- .../matching/HyperdriveMatchingEngineV2Test.t.sol | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol b/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol index d3cfe95c5..2821ec03e 100644 --- a/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol +++ b/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol @@ -636,7 +636,9 @@ contract HyperdriveMatchingEngineV2Test is HyperdriveTest { uint256 bobShortBalanceBefore = _getShortBalance(bob); // Fill order + vm.startPrank(bob); matchingEngine.fillOrder(makerOrder, takerOrder); + vm.stopPrank(); // Verify balances assertLt(baseToken.balanceOf(alice), aliceBaseBalanceBefore); @@ -675,7 +677,9 @@ contract HyperdriveMatchingEngineV2Test is HyperdriveTest { uint256 bobLongBalanceBefore = _getLongBalance(bob); // Fill order + vm.startPrank(bob); matchingEngine.fillOrder(makerOrder, takerOrder); + vm.stopPrank(); // Verify balances assertLt(baseToken.balanceOf(alice), aliceBaseBalanceBefore); @@ -706,11 +710,13 @@ contract HyperdriveMatchingEngineV2Test is HyperdriveTest { IHyperdriveMatchingEngineV2.OrderType.OpenLong // Same as maker ); + vm.startPrank(bob); vm.expectRevert( IHyperdriveMatchingEngineV2.InvalidOrderCombination.selector ); matchingEngine.fillOrder(makerOrder, invalidTakerOrder); - + vm.stopPrank(); + // Test expired order makerOrder.expiry = block.timestamp - 1; IHyperdriveMatchingEngineV2.OrderIntent @@ -722,8 +728,10 @@ contract HyperdriveMatchingEngineV2Test is HyperdriveTest { IHyperdriveMatchingEngineV2.OrderType.OpenShort ); + vm.startPrank(bob); vm.expectRevert(IHyperdriveMatchingEngineV2.AlreadyExpired.selector); matchingEngine.fillOrder(makerOrder, validTakerOrder); + vm.stopPrank(); } // Helper functions. From 0841ebd36300c5fa1591a6568f5b7435edbe9b2f Mon Sep 17 00:00:00 2001 From: Xiangyu Xu Date: Thu, 20 Feb 2025 10:05:59 +0800 Subject: [PATCH 5/6] Make prettier --- test/units/matching/HyperdriveMatchingEngineV2Test.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol b/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol index 2821ec03e..c454ec6ea 100644 --- a/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol +++ b/test/units/matching/HyperdriveMatchingEngineV2Test.t.sol @@ -716,7 +716,7 @@ contract HyperdriveMatchingEngineV2Test is HyperdriveTest { ); matchingEngine.fillOrder(makerOrder, invalidTakerOrder); vm.stopPrank(); - + // Test expired order makerOrder.expiry = block.timestamp - 1; IHyperdriveMatchingEngineV2.OrderIntent From 85251e2326d309cd09867ee947630af5d40515b5 Mon Sep 17 00:00:00 2001 From: Xiangyu Xu Date: Mon, 24 Feb 2025 20:08:52 +0800 Subject: [PATCH 6/6] Resolve the EIP712 TypeHash issue --- contracts/src/matching/HyperdriveMatchingEngineV2.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/matching/HyperdriveMatchingEngineV2.sol b/contracts/src/matching/HyperdriveMatchingEngineV2.sol index cf6379c64..f6859e8e2 100644 --- a/contracts/src/matching/HyperdriveMatchingEngineV2.sol +++ b/contracts/src/matching/HyperdriveMatchingEngineV2.sol @@ -34,7 +34,7 @@ contract HyperdriveMatchingEngineV2 is /// @notice The EIP712 typehash of the OrderIntent struct. bytes32 public constant ORDER_INTENT_TYPEHASH = keccak256( - "OrderIntent(address trader,address counterparty,address hyperdrive,uint256 fundAmount,uint256 bondAmount,uint256 minVaultSharePrice,Options options,uint8 orderType,uint256 minMaturityTime,uint256 maxMaturityTime,uint256 expiry,bytes32 salt)" + "OrderIntent(address trader,address counterparty,address hyperdrive,uint256 fundAmount,uint256 bondAmount,uint256 minVaultSharePrice,Options options,uint8 orderType,uint256 minMaturityTime,uint256 maxMaturityTime,uint256 expiry,bytes32 salt)Options(address destination,bool asBase)" ); /// @notice The EIP712 typehash of the Options struct.