Missing reentrancy guard in the cancel
function
Description
The source-chain cancel
function allows a solver to cancel an active single-chain order or allows the offerer to cancel the single-chain order when it expires.
However, the cancel
function lacks a nonReentrant
modifier, exposing it to potential reentrancy attacks.
function cancel(bytes32 orderId) external whenNotPaused {
Impact
If a solver's account were compromised, an attacker could exploit this vulnerability for profit. By converting the solver's address into a malicious contract via EIP-7702, the attacker can execute a reentrancy attack during a native token transfer.
An attacker could perform the following steps:
Create orders. The attacker, in control of a solver on Arbitrum, creates two orders.
Order 1: Deposit 2,500 USDT on Arbitrum → receive 1 ETH on Arbitrum (recipient: solver address)
Order 2: Deposit 2,500 USDT on Arbitrum → receive 2,500 USDC on Optimism
Fill order 1. The attacker uses the compromised solver to fill order 1. The native ETH transfer to the solver's address triggers the malicious contract's fallback function.
Reenter
cancel
. Inside the fallback function, the malicious contract calls the cancel function for order 1. Because there is no reentrancy guard, the call succeeds. The attacker's locked USDT balance decreases due to both cancel and fill actions, resulting in a zero locked balance.Exploit order 2. Since the attacker's locked USDT balance on the source chain (Arbitrum) is zero, any attempt to settle order 2 will fail on Arbitrum. However, solvers on the destination chain (Optimism) may still attempt to fill order 2. Each time an Optimism solver fills the order, the attacker receives 2,500 USDC on Optimism, while the order status remains active because the source-chain settlement fails repeatedly.
Recommendations
Add a reentrancy guard to the cancel
function.
-function cancel(bytes32 orderId) external whenNotPaused {
+function cancel(bytes32 orderId) external nonReentrant whenNotPaused {
Remediation
This issue has been acknowledged by Aori, and a fix was implemented in commit 0b18c84f↗.