From c341cdbeed1a9944aec35eebcdc57f9039ed29cd Mon Sep 17 00:00:00 2001 From: KSS Date: Thu, 25 Sep 2025 16:35:53 +0200 Subject: [PATCH 01/25] feat: verify multiple proofs of read --- .../src/libraries/xChain/IT1XChainReader.sol | 1 + .../src/libraries/xChain/T1XChainReader.sol | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/contracts/src/libraries/xChain/IT1XChainReader.sol b/contracts/src/libraries/xChain/IT1XChainReader.sol index f96aa7ae..caf6c9e5 100644 --- a/contracts/src/libraries/xChain/IT1XChainReader.sol +++ b/contracts/src/libraries/xChain/IT1XChainReader.sol @@ -14,6 +14,7 @@ interface IT1XChainReader { function requestRead(ReadRequest calldata request) external payable returns (bytes32 requestId); function commitProofOfReadRoot(uint256 batchIndex, bytes32 newRoot) external; function verifyProofOfRead(bytes calldata encodedProofOfRead) external view returns (bytes32, bytes memory); + function verifyProofsOfRead(bytes[] calldata encodedProofOfRead) external view returns (mapping(bytes32 => bytes)); function verifyProofOfReadWithResult( bytes calldata encodedProofOfRead, bytes calldata result diff --git a/contracts/src/libraries/xChain/T1XChainReader.sol b/contracts/src/libraries/xChain/T1XChainReader.sol index fe2012b7..f85896e7 100644 --- a/contracts/src/libraries/xChain/T1XChainReader.sol +++ b/contracts/src/libraries/xChain/T1XChainReader.sol @@ -214,6 +214,33 @@ contract T1XChainReader is IT1XChainReader, OwnableUpgradeable, ReentrancyGuardU return requestId; } + /** + * @notice Verifies a batch of many proofs of read and returns the raw function results for all of them + * @param encodedProofsOfRead Array of encoded proofs of read + * @return requestId The ID of the read request + * @return result The raw ABI-encoded return value from the target function + */ + function verifyProofsOfRead(bytes[] calldata encodedProofsOfRead) + external + view + override + returns (mapping(bytes32 => bytes)) + { + mapping(bytes32 => bytes) requestIdToResult; + + for (uint256 i = 0; i < encodedProofsOfRead.length; i++) { + + (uint256 batchIndex, bytes32 requestId, uint256 position, bytes memory result, bytes memory proof) = + abi.decode(encodedProofsOfRead[i], (uint256, bytes32, uint256, bytes, bytes)); + + _verifyProofOfRead(batchIndex, requestId, position, result, proof); + + requestIdToResult[requestId] = result; + } + + return requestIdToResult; + } + function _verifyProofOfRead( uint256 batchIndex, bytes32 requestId, From 9fb43343edb2dfb46490e4524696693b496667af Mon Sep 17 00:00:00 2001 From: KSS Date: Fri, 26 Sep 2025 17:15:56 +0200 Subject: [PATCH 02/25] fix: now it compiles, which is a plus --- contracts/src/libraries/xChain/IT1XChainReader.sol | 2 +- contracts/src/libraries/xChain/T1XChainReader.sol | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/src/libraries/xChain/IT1XChainReader.sol b/contracts/src/libraries/xChain/IT1XChainReader.sol index caf6c9e5..c21b6b97 100644 --- a/contracts/src/libraries/xChain/IT1XChainReader.sol +++ b/contracts/src/libraries/xChain/IT1XChainReader.sol @@ -14,7 +14,7 @@ interface IT1XChainReader { function requestRead(ReadRequest calldata request) external payable returns (bytes32 requestId); function commitProofOfReadRoot(uint256 batchIndex, bytes32 newRoot) external; function verifyProofOfRead(bytes calldata encodedProofOfRead) external view returns (bytes32, bytes memory); - function verifyProofsOfRead(bytes[] calldata encodedProofOfRead) external view returns (mapping(bytes32 => bytes)); + function verifyProofsOfRead(bytes[] calldata encodedProofOfRead) external view returns (bytes32[] memory requestIds, bytes[] memory results); function verifyProofOfReadWithResult( bytes calldata encodedProofOfRead, bytes calldata result diff --git a/contracts/src/libraries/xChain/T1XChainReader.sol b/contracts/src/libraries/xChain/T1XChainReader.sol index f85896e7..a381cc67 100644 --- a/contracts/src/libraries/xChain/T1XChainReader.sol +++ b/contracts/src/libraries/xChain/T1XChainReader.sol @@ -217,16 +217,17 @@ contract T1XChainReader is IT1XChainReader, OwnableUpgradeable, ReentrancyGuardU /** * @notice Verifies a batch of many proofs of read and returns the raw function results for all of them * @param encodedProofsOfRead Array of encoded proofs of read - * @return requestId The ID of the read request - * @return result The raw ABI-encoded return value from the target function + * @return requestIds The IDs of all read requests, in the same order + * @return results The raw ABI-encoded return values from the target function for all read requests, in the same order */ function verifyProofsOfRead(bytes[] calldata encodedProofsOfRead) external view override - returns (mapping(bytes32 => bytes)) + returns (bytes32[] memory requestIds, bytes[] memory results) { - mapping(bytes32 => bytes) requestIdToResult; + requestIds = new bytes32[](encodedProofsOfRead.length); + results = new bytes[](encodedProofsOfRead.length); for (uint256 i = 0; i < encodedProofsOfRead.length; i++) { @@ -235,10 +236,9 @@ contract T1XChainReader is IT1XChainReader, OwnableUpgradeable, ReentrancyGuardU _verifyProofOfRead(batchIndex, requestId, position, result, proof); - requestIdToResult[requestId] = result; + requestIds[i] = requestId; + results[i] = result; } - - return requestIdToResult; } function _verifyProofOfRead( From 0f5731e7ccca44aa346ea536752343149fa8bccb Mon Sep 17 00:00:00 2001 From: KSS Date: Fri, 26 Sep 2025 17:32:26 +0200 Subject: [PATCH 03/25] feat: use batching in T1ERC7683.sol --- contracts/src/7683/T1ERC7683.sol | 34 +++++++++++++++++++++---- contracts/src/interfaces/IT1ERC7683.sol | 6 +++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index 88284ed3..29a90714 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -419,7 +419,33 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { function handleReadResultWithProof(bytes calldata encodedProofOfRead) external whenSettleNotPaused { (bytes32 requestId, bytes memory result) = xChainRead.verifyProofOfRead(encodedProofOfRead); - bytes32 orderId = settlementReadRequestToOrderId[requestId]; + (bytes32 orderId, bool isSettled) = _handleReadResultWithProof(requestId, result); + + emit SettlementVerified(orderId, isSettled); + } + + /// @notice Use result of proof of read to handle batch of orders depending on the result + /// Also enforce auction winner bid if the orderId has closed auction. + /// @param encodedProofsOfRead The encoded proofs of read which are formatted as following: + /// abi.encode(uint256 batchIndex, bytes32 requestId, uint256 position, bytes result, bytes proof) + function handleReadResultsWithProofs(bytes[] calldata encodedProofsOfRead) external whenSettleNotPaused { + (bytes32[] memory requestIds, bytes[] memory results) = xChainRead.verifyProofsOfRead(encodedProofsOfRead); + + bytes32[] orderIds = new bytes32[](encodedProofsOfRead.length); + bytes[] areSettled = new bytes[](encodedProofsOfRead.length); + + for (uint256 i = 0; i < encodedProofsOfRead.length; i++) { + (bytes32 orderId, bool isSettled) = _handleReadResultWithProof(requestIds[i], areSettled[i]); + + orderIds[i] = orderId; + areSettled[i] = isSettled; + } + + emit SettlementBatchVerified(orderIds, areSettled); + } + + function _handleReadResultWithProof(bytes32 requestId, bytes memory result) internal returns (bytes32 orderId, bool isSettled) { + orderId = settlementReadRequestToOrderId[requestId]; // Ensure we have a valid order if (orderId == bytes32(0)) revert InvalidOrder(); @@ -427,7 +453,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { delete settlementReadRequestToOrderId[requestId]; // Check if the order is FILLED based on result length - bool isSettled = (result.length != 0); + isSettled = (result.length != 0); // process the settlement if verified Status status = orderStatus[orderId]; @@ -439,7 +465,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { // Remove the first 32 bytes prefix of the message bytes memory _innerMessage = abi.decode(result, (bytes)); (bool _settled, bytes32[] memory _orderIds, bytes[] memory _ordersFillerData) = - abi.decode(_innerMessage, (bool, bytes32[], bytes[])); + abi.decode(_innerMessage, (bool, bytes32[], bytes[])); for (uint256 i = 0; i < _orderIds.length; i++) { if (_settled) { @@ -450,8 +476,6 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { } } } - - emit SettlementVerified(orderId, isSettled); } /// @dev Handles settling an individual order, should be called by the inheriting contract when receiving a setting diff --git a/contracts/src/interfaces/IT1ERC7683.sol b/contracts/src/interfaces/IT1ERC7683.sol index 5d124db7..4d966918 100644 --- a/contracts/src/interfaces/IT1ERC7683.sol +++ b/contracts/src/interfaces/IT1ERC7683.sol @@ -60,6 +60,12 @@ interface IT1ERC7683 is IOriginSettler, IDestinationSettler { * @param isSettled Whether the order is settled */ event SettlementVerified(bytes32 indexed orderId, bool isSettled); + /** + * @notice Emitted when a batch of order settlements is verified + * @param orderIds The IDs of all verified orders + * @param areSettled Whether given orders are settled + */ + event SettlementBatchVerified(bytes32[] indexed orderIds, bool[] areSettled); /** * @notice Emitted when an order is settled. * @param orderId The ID of the settled order. From 88a0f1078a51b44b87734acb409fb3c1f05eaa25 Mon Sep 17 00:00:00 2001 From: KSS Date: Fri, 26 Sep 2025 17:42:50 +0200 Subject: [PATCH 04/25] chore: better interface --- contracts/src/7683/T1ERC7683.sol | 2 +- contracts/src/interfaces/IT1ERC7683.sol | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index 29a90714..1ba2140a 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -428,7 +428,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { /// Also enforce auction winner bid if the orderId has closed auction. /// @param encodedProofsOfRead The encoded proofs of read which are formatted as following: /// abi.encode(uint256 batchIndex, bytes32 requestId, uint256 position, bytes result, bytes proof) - function handleReadResultsWithProofs(bytes[] calldata encodedProofsOfRead) external whenSettleNotPaused { + function handleBatchOfReadResultsWithProofs(bytes[] calldata encodedProofsOfRead) external whenSettleNotPaused { (bytes32[] memory requestIds, bytes[] memory results) = xChainRead.verifyProofsOfRead(encodedProofsOfRead); bytes32[] orderIds = new bytes32[](encodedProofsOfRead.length); diff --git a/contracts/src/interfaces/IT1ERC7683.sol b/contracts/src/interfaces/IT1ERC7683.sol index 4d966918..933e73dc 100644 --- a/contracts/src/interfaces/IT1ERC7683.sol +++ b/contracts/src/interfaces/IT1ERC7683.sol @@ -148,6 +148,12 @@ interface IT1ERC7683 is IOriginSettler, IDestinationSettler { /// abi.encode(uint256 batchIndex, bytes32 requestId, uint256 position, bytes result, bytes proof) function handleReadResultWithProof(bytes calldata encodedProofOfRead) external; + /// @notice Use result of proof of read to handle batch of orders depending on the result + /// Also enforce auction winner bid if the orderId has closed auction. + /// @param encodedProofsOfRead The encoded proofs of read which are formatted as following: + /// abi.encode(uint256 batchIndex, bytes32 requestId, uint256 position, bytes result, bytes proof) + function handleBatchOfReadResultsWithProofs(bytes[] calldata encodedProofsOfRead) external; + /// @notice Refunds a batch of expired GaslessCrossChainOrders on the chain where the orders were opened. /// This process needs a proof of read triggered by `verifyRefund` that proves the intent has not be filled. /// @param _orders An array of GaslessCrossChainOrders to refund. From d7aabe71ddd52cb4a3d512926df43e692906046a Mon Sep 17 00:00:00 2001 From: KSS Date: Fri, 26 Sep 2025 17:47:43 +0200 Subject: [PATCH 05/25] fix: me silly --- contracts/src/7683/T1ERC7683.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index 1ba2140a..17f506c8 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -431,11 +431,11 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { function handleBatchOfReadResultsWithProofs(bytes[] calldata encodedProofsOfRead) external whenSettleNotPaused { (bytes32[] memory requestIds, bytes[] memory results) = xChainRead.verifyProofsOfRead(encodedProofsOfRead); - bytes32[] orderIds = new bytes32[](encodedProofsOfRead.length); - bytes[] areSettled = new bytes[](encodedProofsOfRead.length); + bytes32[] memory orderIds = new bytes32[](encodedProofsOfRead.length); + bool[] memory areSettled = new bool[](encodedProofsOfRead.length); for (uint256 i = 0; i < encodedProofsOfRead.length; i++) { - (bytes32 orderId, bool isSettled) = _handleReadResultWithProof(requestIds[i], areSettled[i]); + (bytes32 orderId, bool isSettled) = _handleReadResultWithProof(requestIds[i], results[i]); orderIds[i] = orderId; areSettled[i] = isSettled; From 45bd568946c8bb8a65323c308bcfc18bd4874a5d Mon Sep 17 00:00:00 2001 From: KSS Date: Fri, 26 Sep 2025 17:50:53 +0200 Subject: [PATCH 06/25] chore: fmt --- contracts/src/7683/T1ERC7683.sol | 10 ++++++++-- contracts/src/libraries/xChain/IT1XChainReader.sol | 5 ++++- contracts/src/libraries/xChain/T1XChainReader.sol | 14 +++++++------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index 17f506c8..a9f450d8 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -444,7 +444,13 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { emit SettlementBatchVerified(orderIds, areSettled); } - function _handleReadResultWithProof(bytes32 requestId, bytes memory result) internal returns (bytes32 orderId, bool isSettled) { + function _handleReadResultWithProof( + bytes32 requestId, + bytes memory result + ) + internal + returns (bytes32 orderId, bool isSettled) + { orderId = settlementReadRequestToOrderId[requestId]; // Ensure we have a valid order @@ -465,7 +471,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { // Remove the first 32 bytes prefix of the message bytes memory _innerMessage = abi.decode(result, (bytes)); (bool _settled, bytes32[] memory _orderIds, bytes[] memory _ordersFillerData) = - abi.decode(_innerMessage, (bool, bytes32[], bytes[])); + abi.decode(_innerMessage, (bool, bytes32[], bytes[])); for (uint256 i = 0; i < _orderIds.length; i++) { if (_settled) { diff --git a/contracts/src/libraries/xChain/IT1XChainReader.sol b/contracts/src/libraries/xChain/IT1XChainReader.sol index c21b6b97..8107d9f3 100644 --- a/contracts/src/libraries/xChain/IT1XChainReader.sol +++ b/contracts/src/libraries/xChain/IT1XChainReader.sol @@ -14,7 +14,10 @@ interface IT1XChainReader { function requestRead(ReadRequest calldata request) external payable returns (bytes32 requestId); function commitProofOfReadRoot(uint256 batchIndex, bytes32 newRoot) external; function verifyProofOfRead(bytes calldata encodedProofOfRead) external view returns (bytes32, bytes memory); - function verifyProofsOfRead(bytes[] calldata encodedProofOfRead) external view returns (bytes32[] memory requestIds, bytes[] memory results); + function verifyProofsOfRead(bytes[] calldata encodedProofOfRead) + external + view + returns (bytes32[] memory requestIds, bytes[] memory results); function verifyProofOfReadWithResult( bytes calldata encodedProofOfRead, bytes calldata result diff --git a/contracts/src/libraries/xChain/T1XChainReader.sol b/contracts/src/libraries/xChain/T1XChainReader.sol index a381cc67..91c98b4b 100644 --- a/contracts/src/libraries/xChain/T1XChainReader.sol +++ b/contracts/src/libraries/xChain/T1XChainReader.sol @@ -218,21 +218,21 @@ contract T1XChainReader is IT1XChainReader, OwnableUpgradeable, ReentrancyGuardU * @notice Verifies a batch of many proofs of read and returns the raw function results for all of them * @param encodedProofsOfRead Array of encoded proofs of read * @return requestIds The IDs of all read requests, in the same order - * @return results The raw ABI-encoded return values from the target function for all read requests, in the same order + * @return results The raw ABI-encoded return values from the target function for all read requests, in the same + * order */ function verifyProofsOfRead(bytes[] calldata encodedProofsOfRead) - external - view - override - returns (bytes32[] memory requestIds, bytes[] memory results) + external + view + override + returns (bytes32[] memory requestIds, bytes[] memory results) { requestIds = new bytes32[](encodedProofsOfRead.length); results = new bytes[](encodedProofsOfRead.length); for (uint256 i = 0; i < encodedProofsOfRead.length; i++) { - (uint256 batchIndex, bytes32 requestId, uint256 position, bytes memory result, bytes memory proof) = - abi.decode(encodedProofsOfRead[i], (uint256, bytes32, uint256, bytes, bytes)); + abi.decode(encodedProofsOfRead[i], (uint256, bytes32, uint256, bytes, bytes)); _verifyProofOfRead(batchIndex, requestId, position, result, proof); From f0683738fe1bf3257c87fcb3e5eb2ce83f4d6454 Mon Sep 17 00:00:00 2001 From: KSS Date: Sun, 28 Sep 2025 15:26:42 +0200 Subject: [PATCH 07/25] feat: minimalize number of ERC20.transfer calls when processing batch of PoRs --- contracts/src/7683/T1ERC7683.sol | 50 +++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index a9f450d8..c71a8331 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -22,6 +22,11 @@ import { import { T1Permit2 } from "./T1Permit2.sol"; import { IT1XChainReader } from "../libraries/xChain/IT1XChainReader.sol"; +struct UserTokens { + address receiver; + address token; +} + /// @title T1ERC7683 /// @author t1 Labs contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { @@ -419,7 +424,9 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { function handleReadResultWithProof(bytes calldata encodedProofOfRead) external whenSettleNotPaused { (bytes32 requestId, bytes memory result) = xChainRead.verifyProofOfRead(encodedProofOfRead); - (bytes32 orderId, bool isSettled) = _handleReadResultWithProof(requestId, result); + (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) = _decodeOrdersToSettle(requestId, result); + + _transferTokenOut(inputToken, settlementReceiver, amount); emit SettlementVerified(orderId, isSettled); } @@ -433,23 +440,45 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { bytes32[] memory orderIds = new bytes32[](encodedProofsOfRead.length); bool[] memory areSettled = new bool[](encodedProofsOfRead.length); + UserTokens[] memory userTokenKeys = new UserTokens[](encodedProofsOfRead.length); + uint256[] memory amounts = new uint256[](encodedProofsOfRead.length); + uint256 uniqueUserTokenCount = 0; for (uint256 i = 0; i < encodedProofsOfRead.length; i++) { - (bytes32 orderId, bool isSettled) = _handleReadResultWithProof(requestIds[i], results[i]); + (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) = _decodeOrdersToSettle(requestIds[i], results[i]); orderIds[i] = orderId; areSettled[i] = isSettled; + + bool foundExistingUserTokenKey = false; + for (uint256 j = 0; j < uniqueUserTokenCount; j++) { + if (userTokenKeys[j].receiver == settlementReceiver && userTokenKeys[j].token == inputToken) { + amounts[j] += amount; + foundExistingUserTokenKey = true; + break; + } + } + + if (!foundExistingUserTokenKey) { + userTokenKeys[uniqueUserTokenCount] = UserTokens({receiver: settlementReceiver, token: inputToken}); + amounts[uniqueUserTokenCount] = amount; + uniqueUserTokenCount++; + } + } + + for (uint256 i = 0; i < userTokenKeys.length; i++) { + _transferTokenOut(userTokenKeys[i].token, userTokenKeys[i].receiver, amounts[i]); } emit SettlementBatchVerified(orderIds, areSettled); } - function _handleReadResultWithProof( + function _decodeOrdersToSettle( bytes32 requestId, bytes memory result ) internal - returns (bytes32 orderId, bool isSettled) + returns (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) { orderId = settlementReadRequestToOrderId[requestId]; @@ -460,6 +489,9 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { // Check if the order is FILLED based on result length isSettled = (result.length != 0); + inputToken = address(0); + settlementReceiver = address(0); + amount = 0; // process the settlement if verified Status status = orderStatus[orderId]; @@ -475,8 +507,8 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { for (uint256 i = 0; i < _orderIds.length; i++) { if (_settled) { - (, address settlementReceiver) = abi.decode(_ordersFillerData[i], (uint256, address)); - _handleSettleOrder( + (, settlementReceiver) = abi.decode(_ordersFillerData[i], (uint256, address)); + (inputToken, amount) = _handleSettleOrder( orderData.destinationDomain, orderData.destinationSettler, _orderIds[i], settlementReceiver ); } @@ -498,6 +530,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { ) internal virtual + returns (address inputToken, uint256 amount) { (bool isEligible, OrderData memory orderData) = _checkOrderEligibility(_messageOrigin, _messageSender, _orderId); @@ -505,9 +538,10 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { orderStatus[_orderId] = Status.SETTLED; - address inputToken = TypeCasts.bytes32ToAddress(orderData.inputToken); + inputToken = TypeCasts.bytes32ToAddress(orderData.inputToken); + amount = orderData.amountIn; - _transferTokenOut(inputToken, settlementReceiver, orderData.amountIn); +// _transferTokenOut(inputToken, settlementReceiver, orderData.amountIn); emit Settled(_orderId, settlementReceiver); } From 22607f2e0c86e2165c760474a4a0cbd71912249c Mon Sep 17 00:00:00 2001 From: KSS Date: Sun, 28 Sep 2025 15:28:58 +0200 Subject: [PATCH 08/25] chore: fmt --- contracts/src/7683/T1ERC7683.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index c71a8331..53727026 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -424,7 +424,8 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { function handleReadResultWithProof(bytes calldata encodedProofOfRead) external whenSettleNotPaused { (bytes32 requestId, bytes memory result) = xChainRead.verifyProofOfRead(encodedProofOfRead); - (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) = _decodeOrdersToSettle(requestId, result); + (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) = + _decodeOrdersToSettle(requestId, result); _transferTokenOut(inputToken, settlementReceiver, amount); @@ -445,7 +446,8 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { uint256 uniqueUserTokenCount = 0; for (uint256 i = 0; i < encodedProofsOfRead.length; i++) { - (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) = _decodeOrdersToSettle(requestIds[i], results[i]); + (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) = + _decodeOrdersToSettle(requestIds[i], results[i]); orderIds[i] = orderId; areSettled[i] = isSettled; @@ -460,7 +462,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { } if (!foundExistingUserTokenKey) { - userTokenKeys[uniqueUserTokenCount] = UserTokens({receiver: settlementReceiver, token: inputToken}); + userTokenKeys[uniqueUserTokenCount] = UserTokens({ receiver: settlementReceiver, token: inputToken }); amounts[uniqueUserTokenCount] = amount; uniqueUserTokenCount++; } @@ -541,7 +543,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { inputToken = TypeCasts.bytes32ToAddress(orderData.inputToken); amount = orderData.amountIn; -// _transferTokenOut(inputToken, settlementReceiver, orderData.amountIn); + // _transferTokenOut(inputToken, settlementReceiver, orderData.amountIn); emit Settled(_orderId, settlementReceiver); } From a2494e4357702e4b2ddcfd50f6914c9bc2bb5a39 Mon Sep 17 00:00:00 2001 From: KSS Date: Sun, 28 Sep 2025 15:31:46 +0200 Subject: [PATCH 09/25] chore: housekeeping --- contracts/src/7683/T1ERC7683.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index 53727026..a95624a3 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -543,8 +543,6 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { inputToken = TypeCasts.bytes32ToAddress(orderData.inputToken); amount = orderData.amountIn; - // _transferTokenOut(inputToken, settlementReceiver, orderData.amountIn); - emit Settled(_orderId, settlementReceiver); } From 3bf978d5739c039c2bcef78d7c0c25c98f70009b Mon Sep 17 00:00:00 2001 From: KSS Date: Sun, 28 Sep 2025 15:49:30 +0200 Subject: [PATCH 10/25] feat: only call _transferTokenOut if order was settled for extra security --- contracts/src/7683/T1ERC7683.sol | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index a95624a3..d57ca5a5 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -427,7 +427,9 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { (bytes32 orderId, bool isSettled, address inputToken, address settlementReceiver, uint256 amount) = _decodeOrdersToSettle(requestId, result); - _transferTokenOut(inputToken, settlementReceiver, amount); + if (isSettled) { + _transferTokenOut(inputToken, settlementReceiver, amount); + } emit SettlementVerified(orderId, isSettled); } @@ -452,19 +454,21 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { orderIds[i] = orderId; areSettled[i] = isSettled; - bool foundExistingUserTokenKey = false; - for (uint256 j = 0; j < uniqueUserTokenCount; j++) { - if (userTokenKeys[j].receiver == settlementReceiver && userTokenKeys[j].token == inputToken) { - amounts[j] += amount; - foundExistingUserTokenKey = true; - break; + if (isSettled) { + bool foundExistingUserTokenKey = false; + for (uint256 j = 0; j < uniqueUserTokenCount; j++) { + if (userTokenKeys[j].receiver == settlementReceiver && userTokenKeys[j].token == inputToken) { + amounts[j] += amount; + foundExistingUserTokenKey = true; + break; + } } - } - if (!foundExistingUserTokenKey) { - userTokenKeys[uniqueUserTokenCount] = UserTokens({ receiver: settlementReceiver, token: inputToken }); - amounts[uniqueUserTokenCount] = amount; - uniqueUserTokenCount++; + if (!foundExistingUserTokenKey) { + userTokenKeys[uniqueUserTokenCount] = UserTokens({ receiver: settlementReceiver, token: inputToken }); + amounts[uniqueUserTokenCount] = amount; + uniqueUserTokenCount++; + } } } From 7af4fbce6ff041a67d3c9e95dd840a48395007dc Mon Sep 17 00:00:00 2001 From: KSS Date: Mon, 29 Sep 2025 00:35:32 +0200 Subject: [PATCH 11/25] chore: style, comments, simplifications --- contracts/src/7683/T1ERC7683.sol | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index d57ca5a5..b6ddea2c 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -456,6 +456,8 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { if (isSettled) { bool foundExistingUserTokenKey = false; + + // try to find existing (receiver,token) pair and increment amount for (uint256 j = 0; j < uniqueUserTokenCount; j++) { if (userTokenKeys[j].receiver == settlementReceiver && userTokenKeys[j].token == inputToken) { amounts[j] += amount; @@ -464,8 +466,10 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { } } + // create a new (receiver,token) pair otherwise if (!foundExistingUserTokenKey) { - userTokenKeys[uniqueUserTokenCount] = UserTokens({ receiver: settlementReceiver, token: inputToken }); + userTokenKeys[uniqueUserTokenCount] = + UserTokens({ receiver: settlementReceiver, token: inputToken }); amounts[uniqueUserTokenCount] = amount; uniqueUserTokenCount++; } @@ -495,9 +499,6 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { // Check if the order is FILLED based on result length isSettled = (result.length != 0); - inputToken = address(0); - settlementReceiver = address(0); - amount = 0; // process the settlement if verified Status status = orderStatus[orderId]; From 42e8a7d792621598c10227ed329c71329d204f8e Mon Sep 17 00:00:00 2001 From: KSS Date: Mon, 29 Sep 2025 01:04:58 +0200 Subject: [PATCH 12/25] chore: private method to increase readability --- contracts/src/7683/T1ERC7683.sol | 55 +++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index b6ddea2c..9b2733d8 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -443,6 +443,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { bytes32[] memory orderIds = new bytes32[](encodedProofsOfRead.length); bool[] memory areSettled = new bool[](encodedProofsOfRead.length); + UserTokens[] memory userTokenKeys = new UserTokens[](encodedProofsOfRead.length); uint256[] memory amounts = new uint256[](encodedProofsOfRead.length); uint256 uniqueUserTokenCount = 0; @@ -455,24 +456,9 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { areSettled[i] = isSettled; if (isSettled) { - bool foundExistingUserTokenKey = false; - - // try to find existing (receiver,token) pair and increment amount - for (uint256 j = 0; j < uniqueUserTokenCount; j++) { - if (userTokenKeys[j].receiver == settlementReceiver && userTokenKeys[j].token == inputToken) { - amounts[j] += amount; - foundExistingUserTokenKey = true; - break; - } - } - - // create a new (receiver,token) pair otherwise - if (!foundExistingUserTokenKey) { - userTokenKeys[uniqueUserTokenCount] = - UserTokens({ receiver: settlementReceiver, token: inputToken }); - amounts[uniqueUserTokenCount] = amount; - uniqueUserTokenCount++; - } + _updateOrInsertUserToken( + userTokenKeys, amounts, uniqueUserTokenCount, settlementReceiver, inputToken, amount + ); } } @@ -523,6 +509,39 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { } } + /// @dev Tries to increment amount to be sent if given (settlementReceiver,token) pair exists. Creates a new pair + /// otherwise + /// @param userTokenKeys Existing (settlementReceiver,token) pairs + /// @param amountsToBeSent Existing amounts to be sent for these (settlementReceiver,token) pairs + /// @param uniqueUserTokenCount Number of existing (settlementReceiver,token) pairs so far + /// @param settlementReceiver The currently processed receiver address + /// @param inputToken The currently processed token address (could be 0, which is native token) + /// @param amount The currently processed token amount + function _updateOrInsertUserToken( + UserTokens[] memory userTokenKeys, + uint256[] memory amountsToBeSent, + uint256 uniqueUserTokenCount, + address settlementReceiver, + address inputToken, + uint256 amount + ) + internal + pure + { + // try to find existing (receiver,token) pair and increment amount + for (uint256 j = 0; j < uniqueUserTokenCount; j++) { + if (userTokenKeys[j].receiver == settlementReceiver && userTokenKeys[j].token == inputToken) { + amountsToBeSent[j] += amount; + return; + } + } + + // create a new (receiver,token) pair otherwise + userTokenKeys[uniqueUserTokenCount] = UserTokens({ receiver: settlementReceiver, token: inputToken }); + amountsToBeSent[uniqueUserTokenCount] = amount; + uniqueUserTokenCount++; + } + /// @dev Handles settling an individual order, should be called by the inheriting contract when receiving a setting /// instruction from a remote chain. /// @param _messageOrigin The domain from which the message originates. From c551644157ed04e089e18baa0f4da2e1a1f72b0f Mon Sep 17 00:00:00 2001 From: KSS Date: Fri, 3 Oct 2025 11:55:28 +0200 Subject: [PATCH 13/25] chore: more flexible intent opening in tests --- contracts/src/test/7683/PausableTest.t.sol | 4 +- .../test/7683/T1XChainReaderBaseTestSetup.sol | 14 +++--- .../src/test/7683/T1XChainReaderTest.t.sol | 47 ++++++++++++++----- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/contracts/src/test/7683/PausableTest.t.sol b/contracts/src/test/7683/PausableTest.t.sol index ffe1e683..8d5ea343 100644 --- a/contracts/src/test/7683/PausableTest.t.sol +++ b/contracts/src/test/7683/PausableTest.t.sol @@ -167,7 +167,7 @@ contract PausableTest is T1XChainReaderBaseTestSetup { } function test_canSettleWhenNotPaused() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); originReader.commitProofOfReadRoot(batchIndex, root); @@ -217,7 +217,7 @@ contract PausableTest is T1XChainReaderBaseTestSetup { } function test_cannotSettleWhenPaused() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); originReader.commitProofOfReadRoot(batchIndex, root); diff --git a/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol b/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol index 674a102f..bc0c6e29 100644 --- a/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol +++ b/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol @@ -54,13 +54,13 @@ contract T1XChainReaderBaseTestSetup is BaseTest { receive() external payable { } - function _openAndFillOrder() internal virtual returns (OrderData memory, bytes32 orderId, bytes32 requestId) { + function _openAndFillOrder(address opener, address filler, uint256 amountIn) internal virtual returns (OrderData memory, bytes32 orderId, bytes32 requestId) { OrderData memory orderData = _prepareOrderData(); OnchainCrossChainOrder memory order = _prepareOnchainOrder(OrderEncoder.encode(orderData), orderData.fillDeadline, OrderEncoder.orderDataType()); - vm.startPrank(kakaroto); - inputToken.approve(address(l1T1ERC7683), amount); + vm.startPrank(opener); + inputToken.approve(address(l1T1ERC7683), amountIn); vm.recordLogs(); l1T1ERC7683.open(order); vm.stopPrank(); @@ -68,15 +68,15 @@ contract T1XChainReaderBaseTestSetup is BaseTest { (bytes32 orderId_,) = _getOrderIDFromLogs(); assertEq(uint8(l1T1ERC7683.orderStatus(orderId_)), uint8(IT1ERC7683.Status.OPENED)); - vm.startPrank(vegeta); - outputToken.approve(address(l2T1ERC7683), amount); + vm.startPrank(filler); + outputToken.approve(address(l2T1ERC7683), amountIn); bytes memory originData = OrderEncoder.encode(orderData); - bytes memory fillerData = abi.encode(amount, TypeCasts.addressToBytes32(vegeta)); + bytes memory fillerData = abi.encode(amountIn, TypeCasts.addressToBytes32(vegeta)); l2T1ERC7683.fill(orderId_, originData, fillerData); assertEq(uint8(l2T1ERC7683.orderStatus(orderId_)), uint8(IT1ERC7683.Status.FILLED)); vm.stopPrank(); - vm.startPrank(vegeta); + vm.startPrank(filler); bytes32 requestId_ = l1T1ERC7683.verifySettlement(destination, orderId_); vm.stopPrank(); diff --git a/contracts/src/test/7683/T1XChainReaderTest.t.sol b/contracts/src/test/7683/T1XChainReaderTest.t.sol index 01fa9c66..06483585 100644 --- a/contracts/src/test/7683/T1XChainReaderTest.t.sol +++ b/contracts/src/test/7683/T1XChainReaderTest.t.sol @@ -56,7 +56,32 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { // writes the new merkle root for the target batch // 5. Solver calls handleReadResultWithProof on 7683 contract with merkle proof, settles intent and releases funds function test_ERC7683SettlementFlow() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); + + // 4. Process the read request on L2 (destination chain) & Relay the result back to L1 + { + // Construct the read request calldata + bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); + + // Generate merkle tree and proof for the result + (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); + + uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); + originReader.commitProofOfReadRoot(batchIndex, root); + l1T1ERC7683.handleReadResultWithProof(abi.encode(batchIndex, requestId, position, result, proof)); + uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); + + assertEq( + balanceSolverBeforeSettle + amount, balanceSolverAfterSettle, "vegeta balance increased by input amount" + ); + } + + // Verify the final state on L1 + assertEq(uint8(l1T1ERC7683.orderStatus(orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); + } + + function test_ERC7683Batch() public { + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); // 4. Process the read request on L2 (destination chain) & Relay the result back to L1 { @@ -83,7 +108,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { function test_ERC7683SettlementFlowWithAnotherTreePosition() public { position = 3; - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); // 4. Process the read request on L2 (destination chain) & Relay the result back to L1 { @@ -174,7 +199,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_revertWithInvalidProofData() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root,) = _generateMerkleTree(requestId, result, position); @@ -187,7 +212,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_revertWithInvalidProof() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root,) = _generateMerkleTree(requestId, result, position); @@ -204,7 +229,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_revertWithInvalidResultData() public { - (,, bytes32 requestId) = _openAndFillOrder(); + (,, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); // 4. First, set up the proof root by calling handle on the reader @@ -222,7 +247,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_sameProofShouldNotSettleTwice() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); @@ -238,7 +263,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_settlementIfStatusIsRefundRequested() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); @@ -266,7 +291,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_settlementIfReadRequestedTwice() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); @@ -291,7 +316,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_settlementWithEmptyResultData() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); // 4. First, set up the proof root by calling handle on the reader @@ -562,7 +587,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_verifyProofOfReadWithResult() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); @@ -576,7 +601,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_verifyProofOfReadWithResult_InvalidProof() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(); + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); (bytes32 root,) = _generateMerkleTree(requestId, result, position); From 93f4c84afce0a744f5dc25b54f5508551632726f Mon Sep 17 00:00:00 2001 From: KSS Date: Mon, 6 Oct 2025 13:36:22 +0200 Subject: [PATCH 14/25] test: unit test for batch solver repayment using murky for Merkle Tree operations --- .gitmodules | 3 + contracts/murky | 1 + contracts/src/test/7683/ClosedAuctionTest.sol | 2 +- contracts/src/test/7683/PausableTest.t.sol | 10 +- .../7683/SolverRepaymentBatchingTest.t.sol | 105 ++++++++++++++++++ .../test/7683/T1XChainReaderBaseTestSetup.sol | 21 +++- .../src/test/7683/T1XChainReaderTest.t.sol | 29 +---- 7 files changed, 132 insertions(+), 39 deletions(-) create mode 160000 contracts/murky create mode 100644 contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol diff --git a/.gitmodules b/.gitmodules index 0791a632..45995784 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "contracts/lib/euler-vault-kit"] path = contracts/lib/euler-vault-kit url = https://github.com/euler-xyz/euler-vault-kit +[submodule "contracts/murky"] + path = contracts/murky + url = https://github.com/dmfxyz/murky diff --git a/contracts/murky b/contracts/murky new file mode 160000 index 00000000..991e371e --- /dev/null +++ b/contracts/murky @@ -0,0 +1 @@ +Subproject commit 991e371eb1dfa9f86701869eb08ec4e98c3cc0b0 diff --git a/contracts/src/test/7683/ClosedAuctionTest.sol b/contracts/src/test/7683/ClosedAuctionTest.sol index 2f1adcf5..b0b25b06 100644 --- a/contracts/src/test/7683/ClosedAuctionTest.sol +++ b/contracts/src/test/7683/ClosedAuctionTest.sol @@ -189,7 +189,7 @@ contract ClosedAuctionTest is T1XChainReaderBaseTestSetup { } function _openOrder(bool closedAuction) internal returns (OrderData memory orderData, bytes32 orderId) { - orderData = _prepareOrderData(); + orderData = _prepareOrderData(amount); orderData.closedAuction = closedAuction; OnchainCrossChainOrder memory order = _prepareOnchainOrder(OrderEncoder.encode(orderData), orderData.fillDeadline, OrderEncoder.orderDataType()); diff --git a/contracts/src/test/7683/PausableTest.t.sol b/contracts/src/test/7683/PausableTest.t.sol index 8d5ea343..8775282d 100644 --- a/contracts/src/test/7683/PausableTest.t.sol +++ b/contracts/src/test/7683/PausableTest.t.sol @@ -116,7 +116,7 @@ contract PausableTest is T1XChainReaderBaseTestSetup { } function test_canOpenWhenNotPaused() public { - OrderData memory orderData = _prepareOrderData(); + OrderData memory orderData = _prepareOrderData(amount); OnchainCrossChainOrder memory order = _prepareOnchainOrder(OrderEncoder.encode(orderData), orderData.fillDeadline, OrderEncoder.orderDataType()); @@ -135,7 +135,7 @@ contract PausableTest is T1XChainReaderBaseTestSetup { inputToken.approve(permit2, type(uint256).max); uint32 openDeadline = uint32(block.timestamp + 100); - OrderData memory orderData = _prepareOrderData(); + OrderData memory orderData = _prepareOrderData(amount); GaslessCrossChainOrder memory order = _prepareGaslessOrder( address(l1T1ERC7683), kakaroto, @@ -183,7 +183,7 @@ contract PausableTest is T1XChainReaderBaseTestSetup { function test_cannotOpenWhenPaused() public { l1T1ERC7683.pauseOpen(); - OrderData memory orderData = _prepareOrderData(); + OrderData memory orderData = _prepareOrderData(amount); OnchainCrossChainOrder memory order = _prepareOnchainOrder(OrderEncoder.encode(orderData), orderData.fillDeadline, OrderEncoder.orderDataType()); @@ -197,7 +197,7 @@ contract PausableTest is T1XChainReaderBaseTestSetup { function test_cannotOpenForWhenPaused() public { l1T1ERC7683.pauseOpen(); - OrderData memory orderData = _prepareOrderData(); + OrderData memory orderData = _prepareOrderData(amount); GaslessCrossChainOrder memory order = _prepareGaslessOrder( address(l1T1ERC7683), kakaroto, @@ -288,7 +288,7 @@ contract PausableTest is T1XChainReaderBaseTestSetup { function test_cannotRefundForWhenPaused() public { vm.warp(2000); // Set block.timestamp to 2000 uint32 deadline = 1000; // Past deadline for testing refund - OrderData memory defaultOrderData = _prepareOrderData(); + OrderData memory defaultOrderData = _prepareOrderData(amount); defaultOrderData.fillDeadline = deadline; bytes memory orderData = OrderEncoder.encode(defaultOrderData); diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol new file mode 100644 index 00000000..f65aa1b5 --- /dev/null +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { IT1ERC7683 } from "../../../src/interfaces/IT1ERC7683.sol"; + +import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { Merkle } from "../../../murky/src/Merkle.sol"; +import { T1ERC7683 } from "../../7683/T1ERC7683.sol"; +import { T1XChainReader } from "../../libraries/xChain/T1XChainReader.sol"; +import { T1XChainReaderBaseTestSetup } from "./T1XChainReaderBaseTestSetup.sol"; +import { T1XChainReaderTest } from "./T1XChainReaderTest.t.sol"; + +struct MerkleLeafHelper { + bytes32 orderId; + bytes32 treeLeaf; + bytes result; + bytes32 requestId; +} + +contract SolverRepaymentBatchingTest is T1XChainReaderTest { + uint256 internal SOME_AMOUNT_0 = 117; + uint256 internal SOME_AMOUNT_1 = 77; + uint256 internal SOME_AMOUNT_2 = 125; + uint256 internal SOME_AMOUNT_3 = 136; + uint256 internal SOME_AMOUNT_4 = 53; + + Merkle internal tree = new Merkle(); + uint256 internal treePosition = 0; + + function test_ERC7683BatchSolverRepayment_sameSolver() public { + (MerkleLeafHelper memory merkleLeafHelper0) = + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_0, 0); + (MerkleLeafHelper memory merkleLeafHelper1) = + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_1, 1); + (MerkleLeafHelper memory merkleLeafHelper2) = + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_2, 2); + (MerkleLeafHelper memory merkleLeafHelper3) = + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_3, 3); + (MerkleLeafHelper memory merkleLeafHelper4) = + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_4, 4); + + bytes32[] memory treeLeaves = new bytes32[](5); + treeLeaves[0] = merkleLeafHelper0.treeLeaf; + treeLeaves[1] = merkleLeafHelper1.treeLeaf; + treeLeaves[2] = merkleLeafHelper2.treeLeaf; + treeLeaves[3] = merkleLeafHelper3.treeLeaf; + treeLeaves[4] = merkleLeafHelper4.treeLeaf; + + originReader.commitProofOfReadRoot(batchIndex, tree.getRoot(treeLeaves)); + + { + uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); + + bytes[] memory encodedProofs = new bytes[](5); + encodedProofs[0] = abi.encode(batchIndex, merkleLeafHelper0.requestId, 0, merkleLeafHelper0.result, abi.encodePacked(tree.getProof(treeLeaves, 0))); + encodedProofs[1] = abi.encode(batchIndex, merkleLeafHelper1.requestId, 1, merkleLeafHelper1.result, abi.encodePacked(tree.getProof(treeLeaves, 1))); + encodedProofs[2] = abi.encode(batchIndex, merkleLeafHelper2.requestId, 2, merkleLeafHelper2.result, abi.encodePacked(tree.getProof(treeLeaves, 2))); + encodedProofs[3] = abi.encode(batchIndex, merkleLeafHelper3.requestId, 3, merkleLeafHelper3.result, abi.encodePacked(tree.getProof(treeLeaves, 3))); + encodedProofs[4] = abi.encode(batchIndex, merkleLeafHelper4.requestId, 4, merkleLeafHelper4.result, abi.encodePacked(tree.getProof(treeLeaves, 4))); + + l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); + + uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); + + assertEq( + balanceSolverBeforeSettle + SOME_AMOUNT_0 + SOME_AMOUNT_1 + SOME_AMOUNT_2 + SOME_AMOUNT_3 + + SOME_AMOUNT_4, + balanceSolverAfterSettle, + "vegeta balance increased by batch inputs amount" + ); + } + + // Verify the final state on L1 + assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper0.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order0 should be settled"); + assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper1.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order1 should be settled"); + assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper2.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order2 should be settled"); + assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper3.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order3 should be settled"); + assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper4.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order4 should be settled"); + } + + function _openFillOrder_and_generateMerkleLeaf( + address opener, + address solver, + uint256 amount, + uint256 batchIndex + ) + internal + returns (MerkleLeafHelper memory merkleLeafHelper) + { + (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(opener, solver, amount); + + bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(merkleLeafHelper.orderId)); + + bytes32 xChainReadResultHash = keccak256(merkleLeafHelper.result); + + bytes32 treeLeaf = keccak256(abi.encodePacked(xChainReadResultHash, merkleLeafHelper.requestId)); + + merkleLeafHelper = MerkleLeafHelper({ + orderId: orderId, + treeLeaf: treeLeaf, + result: result, + requestId: requestId + }); + } +} diff --git a/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol b/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol index bc0c6e29..e6477f40 100644 --- a/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol +++ b/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol @@ -31,6 +31,7 @@ contract T1XChainReaderBaseTestSetup is BaseTest { address internal owner = makeAddr("owner"); address internal sender = makeAddr("sender"); address internal feeVault; + uint256 internal senderNonce = 1; function labelAccounts() internal { vm.label(owner, "Owner"); @@ -54,8 +55,16 @@ contract T1XChainReaderBaseTestSetup is BaseTest { receive() external payable { } - function _openAndFillOrder(address opener, address filler, uint256 amountIn) internal virtual returns (OrderData memory, bytes32 orderId, bytes32 requestId) { - OrderData memory orderData = _prepareOrderData(); + function _openAndFillOrder( + address opener, + address filler, + uint256 amountIn + ) + internal + virtual + returns (OrderData memory, bytes32 orderId, bytes32 requestId) + { + OrderData memory orderData = _prepareOrderData(amountIn); OnchainCrossChainOrder memory order = _prepareOnchainOrder(OrderEncoder.encode(orderData), orderData.fillDeadline, OrderEncoder.orderDataType()); @@ -83,15 +92,15 @@ contract T1XChainReaderBaseTestSetup is BaseTest { return (orderData, orderId_, requestId_); } - function _prepareOrderData() internal view virtual returns (OrderData memory) { + function _prepareOrderData(uint256 amountIn) internal virtual returns (OrderData memory) { return OrderData({ sender: TypeCasts.addressToBytes32(kakaroto), recipient: TypeCasts.addressToBytes32(karpincho), inputToken: TypeCasts.addressToBytes32(address(inputToken)), outputToken: TypeCasts.addressToBytes32(address(outputToken)), - amountIn: amount, - minAmountOut: amount, - senderNonce: 1, + amountIn: amountIn, + minAmountOut: amountIn, + senderNonce: senderNonce++, originDomain: origin, destinationDomain: destination, destinationSettler: address(l2T1ERC7683).addressToBytes32(), diff --git a/contracts/src/test/7683/T1XChainReaderTest.t.sol b/contracts/src/test/7683/T1XChainReaderTest.t.sol index 06483585..62f3f646 100644 --- a/contracts/src/test/7683/T1XChainReaderTest.t.sol +++ b/contracts/src/test/7683/T1XChainReaderTest.t.sol @@ -80,31 +80,6 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { assertEq(uint8(l1T1ERC7683.orderStatus(orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); } - function test_ERC7683Batch() public { - (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(kakaroto, vegeta, amount); - - // 4. Process the read request on L2 (destination chain) & Relay the result back to L1 - { - // Construct the read request calldata - bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); - - // Generate merkle tree and proof for the result - (bytes32 root, bytes memory proof) = _generateMerkleTree(requestId, result, position); - - uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); - originReader.commitProofOfReadRoot(batchIndex, root); - l1T1ERC7683.handleReadResultWithProof(abi.encode(batchIndex, requestId, position, result, proof)); - uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); - - assertEq( - balanceSolverBeforeSettle + amount, balanceSolverAfterSettle, "vegeta balance increased by input amount" - ); - } - - // Verify the final state on L1 - assertEq(uint8(l1T1ERC7683.orderStatus(orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); - } - function test_ERC7683SettlementFlowWithAnotherTreePosition() public { position = 3; @@ -132,7 +107,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_shouldFillWithAmountOutHigherThanLimit() public { - OrderData memory orderData = _prepareOrderData(); + OrderData memory orderData = _prepareOrderData(amount); OnchainCrossChainOrder memory order = _prepareOnchainOrder(OrderEncoder.encode(orderData), orderData.fillDeadline, OrderEncoder.orderDataType()); @@ -156,7 +131,7 @@ contract T1XChainReaderTest is T1XChainReaderBaseTestSetup { } function test_revertFillWithAmountOutLowerThanLimit() public { - OrderData memory orderData = _prepareOrderData(); + OrderData memory orderData = _prepareOrderData(amount); OnchainCrossChainOrder memory order = _prepareOnchainOrder(OrderEncoder.encode(orderData), orderData.fillDeadline, OrderEncoder.orderDataType()); From 47b20da44748a32dbd81bfdd29e55733199d1bdd Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 00:24:14 +0200 Subject: [PATCH 15/25] feat: introduced murky (with adapted hashing) for merkle tree operations in the test --- contracts/murky | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/murky b/contracts/murky index 991e371e..c7ae847f 160000 --- a/contracts/murky +++ b/contracts/murky @@ -1 +1 @@ -Subproject commit 991e371eb1dfa9f86701869eb08ec4e98c3cc0b0 +Subproject commit c7ae847fc2e151134b3062a0422e3756b674e5d2 From 54993e9c864c8f75c5e56ae1f9d5d60bc2748800 Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 09:55:44 +0200 Subject: [PATCH 16/25] fix: batch test fixes --- contracts/src/7683/T1ERC7683.sol | 7 +- .../7683/SolverRepaymentBatchingTest.t.sol | 68 ++++++++++--------- contracts/src/test/7683/T1Merkle.sol | 25 +++++++ 3 files changed, 65 insertions(+), 35 deletions(-) create mode 100644 contracts/src/test/7683/T1Merkle.sol diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index 9b2733d8..67ef30d9 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -456,7 +456,7 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { areSettled[i] = isSettled; if (isSettled) { - _updateOrInsertUserToken( + uniqueUserTokenCount = _updateOrInsertUserToken( userTokenKeys, amounts, uniqueUserTokenCount, settlementReceiver, inputToken, amount ); } @@ -527,19 +527,20 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { ) internal pure + returns (uint256) { // try to find existing (receiver,token) pair and increment amount for (uint256 j = 0; j < uniqueUserTokenCount; j++) { if (userTokenKeys[j].receiver == settlementReceiver && userTokenKeys[j].token == inputToken) { amountsToBeSent[j] += amount; - return; + return uniqueUserTokenCount; } } // create a new (receiver,token) pair otherwise userTokenKeys[uniqueUserTokenCount] = UserTokens({ receiver: settlementReceiver, token: inputToken }); amountsToBeSent[uniqueUserTokenCount] = amount; - uniqueUserTokenCount++; + return uniqueUserTokenCount + 1; } /// @dev Handles settling an individual order, should be called by the inheriting contract when receiving a setting diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index f65aa1b5..385c2e47 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; +import { T1Merkle } from "./T1Merkle.sol"; + import { IT1ERC7683 } from "../../../src/interfaces/IT1ERC7683.sol"; -import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import { Merkle } from "../../../murky/src/Merkle.sol"; -import { T1ERC7683 } from "../../7683/T1ERC7683.sol"; -import { T1XChainReader } from "../../libraries/xChain/T1XChainReader.sol"; -import { T1XChainReaderBaseTestSetup } from "./T1XChainReaderBaseTestSetup.sol"; import { T1XChainReaderTest } from "./T1XChainReaderTest.t.sol"; struct MerkleLeafHelper { @@ -24,20 +21,19 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 internal SOME_AMOUNT_3 = 136; uint256 internal SOME_AMOUNT_4 = 53; - Merkle internal tree = new Merkle(); - uint256 internal treePosition = 0; + T1Merkle internal tree = new T1Merkle(); function test_ERC7683BatchSolverRepayment_sameSolver() public { (MerkleLeafHelper memory merkleLeafHelper0) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_0, 0); + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_0); (MerkleLeafHelper memory merkleLeafHelper1) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_1, 1); + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_1); (MerkleLeafHelper memory merkleLeafHelper2) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_2, 2); + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_2); (MerkleLeafHelper memory merkleLeafHelper3) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_3, 3); + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_3); (MerkleLeafHelper memory merkleLeafHelper4) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_4, 4); + _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_4); bytes32[] memory treeLeaves = new bytes32[](5); treeLeaves[0] = merkleLeafHelper0.treeLeaf; @@ -46,17 +42,24 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { treeLeaves[3] = merkleLeafHelper3.treeLeaf; treeLeaves[4] = merkleLeafHelper4.treeLeaf; - originReader.commitProofOfReadRoot(batchIndex, tree.getRoot(treeLeaves)); + bytes32 root = tree.getRoot(treeLeaves); + originReader.commitProofOfReadRoot(batchIndex, root); + + bytes memory proof0 = _flattenProof(tree.getProof(treeLeaves, 0)); + bytes memory proof1 = _flattenProof(tree.getProof(treeLeaves, 1)); + bytes memory proof2 = _flattenProof(tree.getProof(treeLeaves, 2)); + bytes memory proof3 = _flattenProof(tree.getProof(treeLeaves, 3)); + bytes memory proof4 = _flattenProof(tree.getProof(treeLeaves, 4)); { uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); bytes[] memory encodedProofs = new bytes[](5); - encodedProofs[0] = abi.encode(batchIndex, merkleLeafHelper0.requestId, 0, merkleLeafHelper0.result, abi.encodePacked(tree.getProof(treeLeaves, 0))); - encodedProofs[1] = abi.encode(batchIndex, merkleLeafHelper1.requestId, 1, merkleLeafHelper1.result, abi.encodePacked(tree.getProof(treeLeaves, 1))); - encodedProofs[2] = abi.encode(batchIndex, merkleLeafHelper2.requestId, 2, merkleLeafHelper2.result, abi.encodePacked(tree.getProof(treeLeaves, 2))); - encodedProofs[3] = abi.encode(batchIndex, merkleLeafHelper3.requestId, 3, merkleLeafHelper3.result, abi.encodePacked(tree.getProof(treeLeaves, 3))); - encodedProofs[4] = abi.encode(batchIndex, merkleLeafHelper4.requestId, 4, merkleLeafHelper4.result, abi.encodePacked(tree.getProof(treeLeaves, 4))); + encodedProofs[0] = abi.encode(batchIndex, merkleLeafHelper0.requestId, 0, merkleLeafHelper0.result, proof0); + encodedProofs[1] = abi.encode(batchIndex, merkleLeafHelper1.requestId, 1, merkleLeafHelper1.result, proof1); + encodedProofs[2] = abi.encode(batchIndex, merkleLeafHelper2.requestId, 2, merkleLeafHelper2.result, proof2); + encodedProofs[3] = abi.encode(batchIndex, merkleLeafHelper3.requestId, 3, merkleLeafHelper3.result, proof3); + encodedProofs[4] = abi.encode(batchIndex, merkleLeafHelper4.requestId, 4, merkleLeafHelper4.result, proof4); l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); @@ -70,7 +73,6 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { ); } - // Verify the final state on L1 assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper0.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order0 should be settled"); assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper1.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order1 should be settled"); assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper2.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order2 should be settled"); @@ -81,20 +83,12 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { function _openFillOrder_and_generateMerkleLeaf( address opener, address solver, - uint256 amount, - uint256 batchIndex - ) - internal - returns (MerkleLeafHelper memory merkleLeafHelper) - { + uint256 amount + ) internal returns (MerkleLeafHelper memory merkleLeafHelper) { (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(opener, solver, amount); - - bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(merkleLeafHelper.orderId)); - - bytes32 xChainReadResultHash = keccak256(merkleLeafHelper.result); - - bytes32 treeLeaf = keccak256(abi.encodePacked(xChainReadResultHash, merkleLeafHelper.requestId)); - + bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); + bytes32 xChainReadResultHash = keccak256(result); + bytes32 treeLeaf = keccak256(abi.encodePacked(xChainReadResultHash, requestId)); merkleLeafHelper = MerkleLeafHelper({ orderId: orderId, treeLeaf: treeLeaf, @@ -102,4 +96,14 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { requestId: requestId }); } + + function _flattenProof(bytes32[] memory proof) internal pure returns (bytes memory proofBytes) { + proofBytes = new bytes(proof.length * 32); + for (uint256 i = 0; i < proof.length; i++) { + assembly { + // store each 32-byte element at the correct offset + mstore(add(proofBytes, add(32, mul(i, 32))), mload(add(proof, add(32, mul(i, 32))))) + } + } + } } diff --git a/contracts/src/test/7683/T1Merkle.sol b/contracts/src/test/7683/T1Merkle.sol new file mode 100644 index 00000000..ec418581 --- /dev/null +++ b/contracts/src/test/7683/T1Merkle.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import { MurkyBase } from "../../../murky/src/common/MurkyBase.sol"; + + +/// @notice Nascent, simple, kinda efficient (and improving!) Merkle proof generator and verifier +/// @author dmfxyz +/// @dev Note Generic Merkle Tree +contract T1Merkle is MurkyBase { + /** + * + * HASHING FUNCTION * + * + */ + + /// ascending sort and concat prior to hashing + function hashLeafPairs(bytes32 left, bytes32 right) public pure override returns (bytes32 _hash) { + assembly { + mstore(0x00, left) + mstore(0x20, right) + _hash := keccak256(0x00, 0x40) + } + } +} From 7a002d792d4caadbec1837ef58156abc15827d38 Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 10:29:17 +0200 Subject: [PATCH 17/25] refactor: allowed batching test for N intents --- .../7683/SolverRepaymentBatchingTest.t.sol | 123 +++++++++++------- 1 file changed, 74 insertions(+), 49 deletions(-) diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index 385c2e47..6c2de716 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -15,69 +15,87 @@ struct MerkleLeafHelper { } contract SolverRepaymentBatchingTest is T1XChainReaderTest { - uint256 internal SOME_AMOUNT_0 = 117; - uint256 internal SOME_AMOUNT_1 = 77; - uint256 internal SOME_AMOUNT_2 = 125; - uint256 internal SOME_AMOUNT_3 = 136; - uint256 internal SOME_AMOUNT_4 = 53; T1Merkle internal tree = new T1Merkle(); - function test_ERC7683BatchSolverRepayment_sameSolver() public { - (MerkleLeafHelper memory merkleLeafHelper0) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_0); - (MerkleLeafHelper memory merkleLeafHelper1) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_1); - (MerkleLeafHelper memory merkleLeafHelper2) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_2); - (MerkleLeafHelper memory merkleLeafHelper3) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_3); - (MerkleLeafHelper memory merkleLeafHelper4) = - _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, SOME_AMOUNT_4); - - bytes32[] memory treeLeaves = new bytes32[](5); - treeLeaves[0] = merkleLeafHelper0.treeLeaf; - treeLeaves[1] = merkleLeafHelper1.treeLeaf; - treeLeaves[2] = merkleLeafHelper2.treeLeaf; - treeLeaves[3] = merkleLeafHelper3.treeLeaf; - treeLeaves[4] = merkleLeafHelper4.treeLeaf; + function test_ERC7683BatchSolverRepayment_sameSolver_withBatching() public { + uint256 n = 100; + uint256[] memory amounts = _generateRandomArray(n); + uint256 summedAmounts = 0; + + bytes32[] memory treeLeaves = new bytes32[](n); + MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](n); + for (uint i = 0; i < n; i++) { + helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, amounts[i]); + treeLeaves[i] = helpers[i].treeLeaf; + summedAmounts += amounts[i]; + } bytes32 root = tree.getRoot(treeLeaves); originReader.commitProofOfReadRoot(batchIndex, root); - bytes memory proof0 = _flattenProof(tree.getProof(treeLeaves, 0)); - bytes memory proof1 = _flattenProof(tree.getProof(treeLeaves, 1)); - bytes memory proof2 = _flattenProof(tree.getProof(treeLeaves, 2)); - bytes memory proof3 = _flattenProof(tree.getProof(treeLeaves, 3)); - bytes memory proof4 = _flattenProof(tree.getProof(treeLeaves, 4)); + bytes[] memory encodedProofs = new bytes[](n); + for (uint i = 0; i < n; i++) { + bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); + encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); + } + + uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); + + l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); + + uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); - { - uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); + assertEq( + balanceSolverBeforeSettle + summedAmounts, + balanceSolverAfterSettle, + "vegeta balance increased by batch inputs amount" + ); - bytes[] memory encodedProofs = new bytes[](5); - encodedProofs[0] = abi.encode(batchIndex, merkleLeafHelper0.requestId, 0, merkleLeafHelper0.result, proof0); - encodedProofs[1] = abi.encode(batchIndex, merkleLeafHelper1.requestId, 1, merkleLeafHelper1.result, proof1); - encodedProofs[2] = abi.encode(batchIndex, merkleLeafHelper2.requestId, 2, merkleLeafHelper2.result, proof2); - encodedProofs[3] = abi.encode(batchIndex, merkleLeafHelper3.requestId, 3, merkleLeafHelper3.result, proof3); - encodedProofs[4] = abi.encode(batchIndex, merkleLeafHelper4.requestId, 4, merkleLeafHelper4.result, proof4); + for (uint i = 0; i < n; i++) { + assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); + } + } - l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); + function test_ERC7683BatchSolverRepayment_sameSolver_noBatching() public { + uint256 n = 100; + uint256[] memory amounts = _generateRandomArray(n); + uint256 summedAmounts = 0; + + bytes32[] memory treeLeaves = new bytes32[](n); + MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](n); + for (uint i = 0; i < n; i++) { + helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, amounts[i]); + treeLeaves[i] = helpers[i].treeLeaf; + summedAmounts += amounts[i]; + } - uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); + bytes32 root = tree.getRoot(treeLeaves); + originReader.commitProofOfReadRoot(batchIndex, root); - assertEq( - balanceSolverBeforeSettle + SOME_AMOUNT_0 + SOME_AMOUNT_1 + SOME_AMOUNT_2 + SOME_AMOUNT_3 - + SOME_AMOUNT_4, - balanceSolverAfterSettle, - "vegeta balance increased by batch inputs amount" - ); + bytes[] memory encodedProofs = new bytes[](n); + for (uint i = 0; i < n; i++) { + bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); + encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); } - assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper0.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order0 should be settled"); - assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper1.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order1 should be settled"); - assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper2.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order2 should be settled"); - assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper3.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order3 should be settled"); - assertEq(uint8(l1T1ERC7683.orderStatus(merkleLeafHelper4.orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order4 should be settled"); + uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); + + for (uint i = 0; i < n; i++) { + l1T1ERC7683.handleReadResultWithProof(encodedProofs[i]); + } + + uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); + + assertEq( + balanceSolverBeforeSettle + summedAmounts, + balanceSolverAfterSettle, + "vegeta balance increased by batch inputs amount" + ); + + for (uint i = 0; i < n; i++) { + assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); + } } function _openFillOrder_and_generateMerkleLeaf( @@ -106,4 +124,11 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { } } } + + function _generateRandomArray(uint256 length) internal view returns (uint256[] memory arr) { + arr = new uint256[](length); + for (uint256 i = 0; i < length; i++) { + arr[i] = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, i))) % 1000; + } + } } From a2e26a02cdf4c93049edf6c0e0d7213213e60c0d Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 10:31:42 +0200 Subject: [PATCH 18/25] chore: shared intentCount --- .../7683/SolverRepaymentBatchingTest.t.sol | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index 6c2de716..d05cc1e7 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -18,14 +18,15 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { T1Merkle internal tree = new T1Merkle(); + uint256 internal intentCount = 100; + function test_ERC7683BatchSolverRepayment_sameSolver_withBatching() public { - uint256 n = 100; - uint256[] memory amounts = _generateRandomArray(n); + uint256[] memory amounts = _generateRandomArray(intentCount); uint256 summedAmounts = 0; - bytes32[] memory treeLeaves = new bytes32[](n); - MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](n); - for (uint i = 0; i < n; i++) { + bytes32[] memory treeLeaves = new bytes32[](intentCount); + MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](intentCount); + for (uint i = 0; i < intentCount; i++) { helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, amounts[i]); treeLeaves[i] = helpers[i].treeLeaf; summedAmounts += amounts[i]; @@ -34,8 +35,8 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { bytes32 root = tree.getRoot(treeLeaves); originReader.commitProofOfReadRoot(batchIndex, root); - bytes[] memory encodedProofs = new bytes[](n); - for (uint i = 0; i < n; i++) { + bytes[] memory encodedProofs = new bytes[](intentCount); + for (uint i = 0; i < intentCount; i++) { bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); } @@ -52,19 +53,18 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { "vegeta balance increased by batch inputs amount" ); - for (uint i = 0; i < n; i++) { + for (uint i = 0; i < intentCount; i++) { assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); } } function test_ERC7683BatchSolverRepayment_sameSolver_noBatching() public { - uint256 n = 100; - uint256[] memory amounts = _generateRandomArray(n); + uint256[] memory amounts = _generateRandomArray(intentCount); uint256 summedAmounts = 0; - bytes32[] memory treeLeaves = new bytes32[](n); - MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](n); - for (uint i = 0; i < n; i++) { + bytes32[] memory treeLeaves = new bytes32[](intentCount); + MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](intentCount); + for (uint i = 0; i < intentCount; i++) { helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, amounts[i]); treeLeaves[i] = helpers[i].treeLeaf; summedAmounts += amounts[i]; @@ -73,15 +73,15 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { bytes32 root = tree.getRoot(treeLeaves); originReader.commitProofOfReadRoot(batchIndex, root); - bytes[] memory encodedProofs = new bytes[](n); - for (uint i = 0; i < n; i++) { + bytes[] memory encodedProofs = new bytes[](intentCount); + for (uint i = 0; i < intentCount; i++) { bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); } uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); - for (uint i = 0; i < n; i++) { + for (uint i = 0; i < intentCount; i++) { l1T1ERC7683.handleReadResultWithProof(encodedProofs[i]); } @@ -93,7 +93,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { "vegeta balance increased by batch inputs amount" ); - for (uint i = 0; i < n; i++) { + for (uint i = 0; i < intentCount; i++) { assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); } } From 5f45d8bedec3e2b81d7afc18f2b1260335400576 Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 11:03:57 +0200 Subject: [PATCH 19/25] fix: only calling ERC20.transfer when necessary --- contracts/src/7683/T1ERC7683.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/src/7683/T1ERC7683.sol b/contracts/src/7683/T1ERC7683.sol index 67ef30d9..b579498c 100644 --- a/contracts/src/7683/T1ERC7683.sol +++ b/contracts/src/7683/T1ERC7683.sol @@ -463,7 +463,9 @@ contract T1ERC7683 is IT1ERC7683, T1Permit2, AccessControlUpgradeable, EIP712 { } for (uint256 i = 0; i < userTokenKeys.length; i++) { - _transferTokenOut(userTokenKeys[i].token, userTokenKeys[i].receiver, amounts[i]); + if (userTokenKeys[i].receiver != address(0)) { + _transferTokenOut(userTokenKeys[i].token, userTokenKeys[i].receiver, amounts[i]); + } } emit SettlementBatchVerified(orderIds, areSettled); From 8e68431e0daf1e3b67f7e62f512e14dc897afbd4 Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 11:04:23 +0200 Subject: [PATCH 20/25] test: testing case for two solvers and calculating gas usage --- .../7683/SolverRepaymentBatchingTest.t.sol | 68 +++++++++++++++++-- .../test/7683/T1XChainReaderBaseTestSetup.sol | 2 +- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index d05cc1e7..e7dc9407 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -7,6 +7,8 @@ import { IT1ERC7683 } from "../../../src/interfaces/IT1ERC7683.sol"; import { T1XChainReaderTest } from "./T1XChainReaderTest.t.sol"; +import { console } from "forge-std/console.sol"; + struct MerkleLeafHelper { bytes32 orderId; bytes32 treeLeaf; @@ -18,9 +20,9 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { T1Merkle internal tree = new T1Merkle(); - uint256 internal intentCount = 100; + uint256 internal intentCount = 20; - function test_ERC7683BatchSolverRepayment_sameSolver_withBatching() public { + function test_ERC7683BatchSolverRepayment_noBatching() public { uint256[] memory amounts = _generateRandomArray(intentCount); uint256 summedAmounts = 0; @@ -43,7 +45,13 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); - l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); + uint256 gasBefore = gasleft(); + for (uint i = 0; i < intentCount; i++) { + l1T1ERC7683.handleReadResultWithProof(encodedProofs[i]); + } + uint256 gasAfter = gasleft(); + uint256 gasUsed = gasBefore - gasAfter; + console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_noBatching:", gasUsed); uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); @@ -58,7 +66,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { } } - function test_ERC7683BatchSolverRepayment_sameSolver_noBatching() public { + function test_ERC7683BatchSolverRepayment_sameSolver_withBatching() public { uint256[] memory amounts = _generateRandomArray(intentCount); uint256 summedAmounts = 0; @@ -81,9 +89,11 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); - for (uint i = 0; i < intentCount; i++) { - l1T1ERC7683.handleReadResultWithProof(encodedProofs[i]); - } + uint256 gasBefore = gasleft(); + l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); + uint256 gasAfter = gasleft(); + uint256 gasUsed = gasBefore - gasAfter; + console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_sameSolver_withBatching:", gasUsed); uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); @@ -98,6 +108,50 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { } } + function test_ERC7683BatchSolverRepayment_twoSolvers_withBatching() public { + uint256[] memory amounts = _generateRandomArray(intentCount); + uint256 summedAmounts = 0; + + bytes32[] memory treeLeaves = new bytes32[](intentCount); + MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](intentCount); + for (uint i = 0; i < intentCount; i++) { + helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, i % 2 == 0 ? vegeta : karpincho, amounts[i]); + treeLeaves[i] = helpers[i].treeLeaf; + summedAmounts += amounts[i]; + } + + bytes32 root = tree.getRoot(treeLeaves); + originReader.commitProofOfReadRoot(batchIndex, root); + + bytes[] memory encodedProofs = new bytes[](intentCount); + for (uint i = 0; i < intentCount; i++) { + bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); + encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); + } + + uint256 balanceVegetaBefore = inputToken.balanceOf(address(vegeta)); + uint256 balanceKarpinchoBefore = inputToken.balanceOf(address(karpincho)); + + uint256 gasBefore = gasleft(); + l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); + uint256 gasAfter = gasleft(); + uint256 gasUsed = gasBefore - gasAfter; + console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_twoSolvers_withBatching:", gasUsed); + + uint256 balanceVegetaAfter = inputToken.balanceOf(address(vegeta)); + uint256 balanceKarpinchoAfter = inputToken.balanceOf(address(karpincho)); + + assertEq( + balanceVegetaBefore + balanceKarpinchoBefore + summedAmounts, + balanceVegetaAfter + balanceKarpinchoAfter, + "vegeta/karpincho balance increased by batch inputs amount" + ); + + for (uint i = 0; i < intentCount; i++) { + assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); + } + } + function _openFillOrder_and_generateMerkleLeaf( address opener, address solver, diff --git a/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol b/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol index e6477f40..53f5403a 100644 --- a/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol +++ b/contracts/src/test/7683/T1XChainReaderBaseTestSetup.sol @@ -80,7 +80,7 @@ contract T1XChainReaderBaseTestSetup is BaseTest { vm.startPrank(filler); outputToken.approve(address(l2T1ERC7683), amountIn); bytes memory originData = OrderEncoder.encode(orderData); - bytes memory fillerData = abi.encode(amountIn, TypeCasts.addressToBytes32(vegeta)); + bytes memory fillerData = abi.encode(amountIn, TypeCasts.addressToBytes32(filler)); l2T1ERC7683.fill(orderId_, originData, fillerData); assertEq(uint8(l2T1ERC7683.orderStatus(orderId_)), uint8(IT1ERC7683.Status.FILLED)); vm.stopPrank(); From 44cd920c0a59a4e73562fa5416d75ccbd3c477bd Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 11:05:53 +0200 Subject: [PATCH 21/25] chore: simplification --- .../src/test/7683/SolverRepaymentBatchingTest.t.sol | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index e7dc9407..ddf7e490 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -50,8 +50,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { l1T1ERC7683.handleReadResultWithProof(encodedProofs[i]); } uint256 gasAfter = gasleft(); - uint256 gasUsed = gasBefore - gasAfter; - console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_noBatching:", gasUsed); + console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_noBatching:", gasBefore - gasAfter); uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); @@ -92,8 +91,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 gasBefore = gasleft(); l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); uint256 gasAfter = gasleft(); - uint256 gasUsed = gasBefore - gasAfter; - console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_sameSolver_withBatching:", gasUsed); + console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_sameSolver_withBatching:", gasBefore - gasAfter); uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); @@ -135,8 +133,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 gasBefore = gasleft(); l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); uint256 gasAfter = gasleft(); - uint256 gasUsed = gasBefore - gasAfter; - console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_twoSolvers_withBatching:", gasUsed); + console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_twoSolvers_withBatching:", gasBefore - gasAfter); uint256 balanceVegetaAfter = inputToken.balanceOf(address(vegeta)); uint256 balanceKarpinchoAfter = inputToken.balanceOf(address(karpincho)); From 2e6e1ed1edfade1eca28b12da934c86ab435c02b Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 11:14:23 +0200 Subject: [PATCH 22/25] chore: dropped Murky dependency by moving needed code inside the repo --- .gitmodules | 3 - contracts/murky | 1 - .../7683/SolverRepaymentBatchingTest.t.sol | 2 +- contracts/src/test/utils/MurkyMerkleBase.sol | 199 ++++++++++++++++++ .../src/test/{7683 => utils}/T1Merkle.sol | 4 +- 5 files changed, 202 insertions(+), 7 deletions(-) delete mode 160000 contracts/murky create mode 100644 contracts/src/test/utils/MurkyMerkleBase.sol rename contracts/src/test/{7683 => utils}/T1Merkle.sol (84%) diff --git a/.gitmodules b/.gitmodules index 45995784..0791a632 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "contracts/lib/euler-vault-kit"] path = contracts/lib/euler-vault-kit url = https://github.com/euler-xyz/euler-vault-kit -[submodule "contracts/murky"] - path = contracts/murky - url = https://github.com/dmfxyz/murky diff --git a/contracts/murky b/contracts/murky deleted file mode 160000 index c7ae847f..00000000 --- a/contracts/murky +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c7ae847fc2e151134b3062a0422e3756b674e5d2 diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index ddf7e490..cf72e545 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; -import { T1Merkle } from "./T1Merkle.sol"; +import { T1Merkle } from "../utils/T1Merkle.sol"; import { IT1ERC7683 } from "../../../src/interfaces/IT1ERC7683.sol"; diff --git a/contracts/src/test/utils/MurkyMerkleBase.sol b/contracts/src/test/utils/MurkyMerkleBase.sol new file mode 100644 index 00000000..ca629bcb --- /dev/null +++ b/contracts/src/test/utils/MurkyMerkleBase.sol @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +abstract contract MurkyMerkleBase { + /** + * + * CONSTRUCTOR * + * + */ + constructor() {} + + /** + * + * VIRTUAL HASHING FUNCTIONS * + * + */ + function hashLeafPairs(bytes32 left, bytes32 right) public pure virtual returns (bytes32 _hash); + + /** + * + * PROOF VERIFICATION * + * + */ + function verifyProof(bytes32 root, bytes32[] memory proof, bytes32 valueToProve) + external + pure + virtual + returns (bool) + { + // proof length must be less than max array size + bytes32 rollingHash = valueToProve; + uint256 length = proof.length; + unchecked { + for (uint256 i = 0; i < length; ++i) { + rollingHash = hashLeafPairs(rollingHash, proof[i]); + } + } + return root == rollingHash; + } + + /** + * + * PROOF GENERATION * + * + */ + function getRoot(bytes32[] memory data) public pure virtual returns (bytes32) { + require(data.length > 1, "won't generate root for single leaf"); + while (data.length > 1) { + data = hashLevel(data); + } + return data[0]; + } + + function getProof(bytes32[] memory data, uint256 node) public pure virtual returns (bytes32[] memory) { + require(data.length > 1, "won't generate proof for single leaf"); + // The size of the proof is equal to the ceiling of log2(numLeaves) + bytes32[] memory result = new bytes32[](log2ceilBitMagic(data.length)); + uint256 pos = 0; + + // Two overflow risks: node, pos + // node: max array size is 2**256-1. Largest index in the array will be 1 less than that. Also, + // for dynamic arrays, size is limited to 2**64-1 + // pos: pos is bounded by log2(data.length), which should be less than type(uint256).max + while (data.length > 1) { + unchecked { + if (node & 0x1 == 1) { + result[pos] = data[node - 1]; + } else if (node + 1 == data.length) { + result[pos] = bytes32(0); + } else { + result[pos] = data[node + 1]; + } + ++pos; + node /= 2; + } + data = hashLevel(data); + } + return result; + } + + ///@dev function is private to prevent unsafe data from being passed + function hashLevel(bytes32[] memory data) private pure returns (bytes32[] memory) { + bytes32[] memory result; + + // Function is private, and all internal callers check that data.length >=2. + // Underflow is not possible as lowest possible value for data/result index is 1 + // overflow should be safe as length is / 2 always. + unchecked { + uint256 length = data.length; + if (length & 0x1 == 1) { + result = new bytes32[](length / 2 + 1); + result[result.length - 1] = hashLeafPairs(data[length - 1], bytes32(0)); + } else { + result = new bytes32[](length / 2); + } + // pos is upper bounded by data.length / 2, so safe even if array is at max size + uint256 pos = 0; + for (uint256 i = 0; i < length - 1; i += 2) { + result[pos] = hashLeafPairs(data[i], data[i + 1]); + ++pos; + } + } + return result; + } + + /** + * + * MATH "LIBRARY" * + * + */ + + /// @dev Note that x is assumed > 0 + function log2ceil(uint256 x) public pure returns (uint256) { + uint256 ceil = 0; + uint256 pOf2; + // If x is a power of 2, then this function will return a ceiling + // that is 1 greater than the actual ceiling. So we need to check if + // x is a power of 2, and subtract one from ceil if so. + assembly { + // we check by seeing if x == (~x + 1) & x. This applies a mask + // to find the lowest set bit of x and then checks it for equality + // with x. If they are equal, then x is a power of 2. + + /* Example + x has single bit set + x := 0000_1000 + (~x + 1) = (1111_0111) + 1 = 1111_1000 + (1111_1000 & 0000_1000) = 0000_1000 == x + + x has multiple bits set + x := 1001_0010 + (~x + 1) = (0110_1101 + 1) = 0110_1110 + (0110_1110 & x) = 0000_0010 != x + */ + + // we do some assembly magic to treat the bool as an integer later on + pOf2 := eq(and(add(not(x), 1), x), x) + } + + // if x == type(uint256).max, than ceil is capped at 256 + // if x == 0, then pO2 == 0, so ceil won't underflow + unchecked { + while (x > 0) { + x >>= 1; + ceil++; + } + ceil -= pOf2; // see above + } + return ceil; + } + + /// Original bitmagic adapted from https://github.com/paulrberg/prb-math/blob/main/contracts/PRBMath.sol + /// @dev Note that x assumed > 1 + function log2ceilBitMagic(uint256 x) public pure returns (uint256) { + if (x <= 1) { + return 0; + } + uint256 msb = 0; + uint256 _x = x; + if (x >= 2 ** 128) { + x >>= 128; + msb += 128; + } + if (x >= 2 ** 64) { + x >>= 64; + msb += 64; + } + if (x >= 2 ** 32) { + x >>= 32; + msb += 32; + } + if (x >= 2 ** 16) { + x >>= 16; + msb += 16; + } + if (x >= 2 ** 8) { + x >>= 8; + msb += 8; + } + if (x >= 2 ** 4) { + x >>= 4; + msb += 4; + } + if (x >= 2 ** 2) { + x >>= 2; + msb += 2; + } + if (x >= 2 ** 1) { + msb += 1; + } + + uint256 lsb = (~_x + 1) & _x; + if ((lsb == _x) && (msb > 0)) { + return msb; + } else { + return msb + 1; + } + } +} diff --git a/contracts/src/test/7683/T1Merkle.sol b/contracts/src/test/utils/T1Merkle.sol similarity index 84% rename from contracts/src/test/7683/T1Merkle.sol rename to contracts/src/test/utils/T1Merkle.sol index ec418581..6b5f42d0 100644 --- a/contracts/src/test/7683/T1Merkle.sol +++ b/contracts/src/test/utils/T1Merkle.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; -import { MurkyBase } from "../../../murky/src/common/MurkyBase.sol"; +import { MurkyMerkleBase } from "./MurkyMerkleBase.sol"; /// @notice Nascent, simple, kinda efficient (and improving!) Merkle proof generator and verifier /// @author dmfxyz /// @dev Note Generic Merkle Tree -contract T1Merkle is MurkyBase { +contract T1Merkle is MurkyMerkleBase { /** * * HASHING FUNCTION * From 783ffa3af4d29f8bfd2e87aa5a53d7ab404d97c2 Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 11:15:38 +0200 Subject: [PATCH 23/25] chore: renames --- contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol | 4 ++-- contracts/src/test/utils/{T1Merkle.sol => T1MurkyMerkle.sol} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename contracts/src/test/utils/{T1Merkle.sol => T1MurkyMerkle.sol} (93%) diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index cf72e545..6751bdb9 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; -import { T1Merkle } from "../utils/T1Merkle.sol"; +import {T1MurkyMerkle} from "../utils/T1MurkyMerkle.sol"; import { IT1ERC7683 } from "../../../src/interfaces/IT1ERC7683.sol"; @@ -18,7 +18,7 @@ struct MerkleLeafHelper { contract SolverRepaymentBatchingTest is T1XChainReaderTest { - T1Merkle internal tree = new T1Merkle(); + T1MurkyMerkle internal tree = new T1MurkyMerkle(); uint256 internal intentCount = 20; diff --git a/contracts/src/test/utils/T1Merkle.sol b/contracts/src/test/utils/T1MurkyMerkle.sol similarity index 93% rename from contracts/src/test/utils/T1Merkle.sol rename to contracts/src/test/utils/T1MurkyMerkle.sol index 6b5f42d0..1c5056fa 100644 --- a/contracts/src/test/utils/T1Merkle.sol +++ b/contracts/src/test/utils/T1MurkyMerkle.sol @@ -7,7 +7,7 @@ import { MurkyMerkleBase } from "./MurkyMerkleBase.sol"; /// @notice Nascent, simple, kinda efficient (and improving!) Merkle proof generator and verifier /// @author dmfxyz /// @dev Note Generic Merkle Tree -contract T1Merkle is MurkyMerkleBase { +contract T1MurkyMerkle is MurkyMerkleBase { /** * * HASHING FUNCTION * From 666dbf806ca0f170e9f4a3020546e03c2f559ac5 Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 11:15:55 +0200 Subject: [PATCH 24/25] chore: fmt --- .../7683/SolverRepaymentBatchingTest.t.sol | 68 ++++++++++++------- contracts/src/test/utils/MurkyMerkleBase.sol | 8 ++- contracts/src/test/utils/T1MurkyMerkle.sol | 1 - 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol index 6751bdb9..3469789b 100644 --- a/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol +++ b/contracts/src/test/7683/SolverRepaymentBatchingTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; -import {T1MurkyMerkle} from "../utils/T1MurkyMerkle.sol"; +import { T1MurkyMerkle } from "../utils/T1MurkyMerkle.sol"; import { IT1ERC7683 } from "../../../src/interfaces/IT1ERC7683.sol"; @@ -17,7 +17,6 @@ struct MerkleLeafHelper { } contract SolverRepaymentBatchingTest is T1XChainReaderTest { - T1MurkyMerkle internal tree = new T1MurkyMerkle(); uint256 internal intentCount = 20; @@ -28,7 +27,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { bytes32[] memory treeLeaves = new bytes32[](intentCount); MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](intentCount); - for (uint i = 0; i < intentCount; i++) { + for (uint256 i = 0; i < intentCount; i++) { helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, amounts[i]); treeLeaves[i] = helpers[i].treeLeaf; summedAmounts += amounts[i]; @@ -38,7 +37,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { originReader.commitProofOfReadRoot(batchIndex, root); bytes[] memory encodedProofs = new bytes[](intentCount); - for (uint i = 0; i < intentCount; i++) { + for (uint256 i = 0; i < intentCount; i++) { bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); } @@ -46,11 +45,13 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 balanceSolverBeforeSettle = inputToken.balanceOf(address(vegeta)); uint256 gasBefore = gasleft(); - for (uint i = 0; i < intentCount; i++) { + for (uint256 i = 0; i < intentCount; i++) { l1T1ERC7683.handleReadResultWithProof(encodedProofs[i]); } uint256 gasAfter = gasleft(); - console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_noBatching:", gasBefore - gasAfter); + console.log( + "Gas used for handling proofs in test_ERC7683BatchSolverRepayment_noBatching:", gasBefore - gasAfter + ); uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); @@ -60,8 +61,12 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { "vegeta balance increased by batch inputs amount" ); - for (uint i = 0; i < intentCount; i++) { - assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); + for (uint256 i = 0; i < intentCount; i++) { + assertEq( + uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), + uint8(IT1ERC7683.Status.SETTLED), + "Order should be settled" + ); } } @@ -71,7 +76,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { bytes32[] memory treeLeaves = new bytes32[](intentCount); MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](intentCount); - for (uint i = 0; i < intentCount; i++) { + for (uint256 i = 0; i < intentCount; i++) { helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, vegeta, amounts[i]); treeLeaves[i] = helpers[i].treeLeaf; summedAmounts += amounts[i]; @@ -81,7 +86,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { originReader.commitProofOfReadRoot(batchIndex, root); bytes[] memory encodedProofs = new bytes[](intentCount); - for (uint i = 0; i < intentCount; i++) { + for (uint256 i = 0; i < intentCount; i++) { bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); } @@ -91,7 +96,10 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 gasBefore = gasleft(); l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); uint256 gasAfter = gasleft(); - console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_sameSolver_withBatching:", gasBefore - gasAfter); + console.log( + "Gas used for handling proofs in test_ERC7683BatchSolverRepayment_sameSolver_withBatching:", + gasBefore - gasAfter + ); uint256 balanceSolverAfterSettle = inputToken.balanceOf(address(vegeta)); @@ -101,8 +109,12 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { "vegeta balance increased by batch inputs amount" ); - for (uint i = 0; i < intentCount; i++) { - assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); + for (uint256 i = 0; i < intentCount; i++) { + assertEq( + uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), + uint8(IT1ERC7683.Status.SETTLED), + "Order should be settled" + ); } } @@ -112,7 +124,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { bytes32[] memory treeLeaves = new bytes32[](intentCount); MerkleLeafHelper[] memory helpers = new MerkleLeafHelper[](intentCount); - for (uint i = 0; i < intentCount; i++) { + for (uint256 i = 0; i < intentCount; i++) { helpers[i] = _openFillOrder_and_generateMerkleLeaf(kakaroto, i % 2 == 0 ? vegeta : karpincho, amounts[i]); treeLeaves[i] = helpers[i].treeLeaf; summedAmounts += amounts[i]; @@ -122,7 +134,7 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { originReader.commitProofOfReadRoot(batchIndex, root); bytes[] memory encodedProofs = new bytes[](intentCount); - for (uint i = 0; i < intentCount; i++) { + for (uint256 i = 0; i < intentCount; i++) { bytes memory flattenedProof = _flattenProof(tree.getProof(treeLeaves, i)); encodedProofs[i] = abi.encode(batchIndex, helpers[i].requestId, i, helpers[i].result, flattenedProof); } @@ -133,7 +145,10 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { uint256 gasBefore = gasleft(); l1T1ERC7683.handleBatchOfReadResultsWithProofs(encodedProofs); uint256 gasAfter = gasleft(); - console.log("Gas used for handling proofs in test_ERC7683BatchSolverRepayment_twoSolvers_withBatching:", gasBefore - gasAfter); + console.log( + "Gas used for handling proofs in test_ERC7683BatchSolverRepayment_twoSolvers_withBatching:", + gasBefore - gasAfter + ); uint256 balanceVegetaAfter = inputToken.balanceOf(address(vegeta)); uint256 balanceKarpinchoAfter = inputToken.balanceOf(address(karpincho)); @@ -144,8 +159,12 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { "vegeta/karpincho balance increased by batch inputs amount" ); - for (uint i = 0; i < intentCount; i++) { - assertEq(uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), uint8(IT1ERC7683.Status.SETTLED), "Order should be settled"); + for (uint256 i = 0; i < intentCount; i++) { + assertEq( + uint8(l1T1ERC7683.orderStatus(helpers[i].orderId)), + uint8(IT1ERC7683.Status.SETTLED), + "Order should be settled" + ); } } @@ -153,17 +172,16 @@ contract SolverRepaymentBatchingTest is T1XChainReaderTest { address opener, address solver, uint256 amount - ) internal returns (MerkleLeafHelper memory merkleLeafHelper) { + ) + internal + returns (MerkleLeafHelper memory merkleLeafHelper) + { (, bytes32 orderId, bytes32 requestId) = _openAndFillOrder(opener, solver, amount); bytes memory result = abi.encode(l2T1ERC7683.getFilledOrderStatus(orderId)); bytes32 xChainReadResultHash = keccak256(result); bytes32 treeLeaf = keccak256(abi.encodePacked(xChainReadResultHash, requestId)); - merkleLeafHelper = MerkleLeafHelper({ - orderId: orderId, - treeLeaf: treeLeaf, - result: result, - requestId: requestId - }); + merkleLeafHelper = + MerkleLeafHelper({ orderId: orderId, treeLeaf: treeLeaf, result: result, requestId: requestId }); } function _flattenProof(bytes32[] memory proof) internal pure returns (bytes memory proofBytes) { diff --git a/contracts/src/test/utils/MurkyMerkleBase.sol b/contracts/src/test/utils/MurkyMerkleBase.sol index ca629bcb..d9248b23 100644 --- a/contracts/src/test/utils/MurkyMerkleBase.sol +++ b/contracts/src/test/utils/MurkyMerkleBase.sol @@ -7,7 +7,7 @@ abstract contract MurkyMerkleBase { * CONSTRUCTOR * * */ - constructor() {} + constructor() { } /** * @@ -21,7 +21,11 @@ abstract contract MurkyMerkleBase { * PROOF VERIFICATION * * */ - function verifyProof(bytes32 root, bytes32[] memory proof, bytes32 valueToProve) + function verifyProof( + bytes32 root, + bytes32[] memory proof, + bytes32 valueToProve + ) external pure virtual diff --git a/contracts/src/test/utils/T1MurkyMerkle.sol b/contracts/src/test/utils/T1MurkyMerkle.sol index 1c5056fa..b170906f 100644 --- a/contracts/src/test/utils/T1MurkyMerkle.sol +++ b/contracts/src/test/utils/T1MurkyMerkle.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.4; import { MurkyMerkleBase } from "./MurkyMerkleBase.sol"; - /// @notice Nascent, simple, kinda efficient (and improving!) Merkle proof generator and verifier /// @author dmfxyz /// @dev Note Generic Merkle Tree From 8ebe098bde7ec3b5d1ed8eeec1000e444837dfb1 Mon Sep 17 00:00:00 2001 From: KSS Date: Tue, 7 Oct 2025 11:23:34 +0200 Subject: [PATCH 25/25] chore: lint --- contracts/.solhintignore | 1 + contracts/src/test/utils/MurkyMerkleBase.sol | 2 +- contracts/src/test/utils/T1MurkyMerkle.sol | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 contracts/.solhintignore diff --git a/contracts/.solhintignore b/contracts/.solhintignore new file mode 100644 index 00000000..bd51b673 --- /dev/null +++ b/contracts/.solhintignore @@ -0,0 +1 @@ +MurkyMerkleBase.sol diff --git a/contracts/src/test/utils/MurkyMerkleBase.sol b/contracts/src/test/utils/MurkyMerkleBase.sol index d9248b23..571b1a60 100644 --- a/contracts/src/test/utils/MurkyMerkleBase.sol +++ b/contracts/src/test/utils/MurkyMerkleBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.25; abstract contract MurkyMerkleBase { /** diff --git a/contracts/src/test/utils/T1MurkyMerkle.sol b/contracts/src/test/utils/T1MurkyMerkle.sol index b170906f..8862fb81 100644 --- a/contracts/src/test/utils/T1MurkyMerkle.sol +++ b/contracts/src/test/utils/T1MurkyMerkle.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.25; import { MurkyMerkleBase } from "./MurkyMerkleBase.sol";