Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · May 2026

Protocol Review

for Reflex

Reflex engaged Guardian to review the security of their protocol. From March 26 through April 6, 2026, a team of 2 auditors reviewed the source code and recorded the findings in this report.

Published
Rounds
Main Review, Remediation Review
Language
Solidity
Chains
Ethereum, BNB Chain, Arbitrum, Base, Hyperliquid
Sector
Infrastructure
  • 0 Critical
  • 4 High
  • 2 Medium
  • 5 Low
  • 12 Informational

13 resolved · 10 acknowledged

Scope

Overview

Reflex engaged Guardian to review the security of their protocol. From March 26 through April 6, 2026, a team of 2 auditors reviewed the source code and recorded the findings in this report.

Findings 23

Main Review

19 findings
  1. H-01 High Unvalidated Fallback Enables Profit Theft Access Control Resolved
    Location
    ExecutionRouter.sol
    Round
    Main Review

    Description

    The ExecutionRouter fallback can transfer tokens to msg.sender during the LOAN_CALLBACK_TYPE_ONGOING phase based entirely on caller-controlled calldata, without validating that the caller is the expected pool. This is dangerous because the router can temporarily hold intermediate route tokens while a swap is in progress. If external execution is reachable during that callback window, an attacker can trigger the fallback, pull those tokens, complete the arbitrage independently, and return enough value for the router’s own flow to finish with zero profit. The loan still repays, but Reflex captures nothing and the attacker keeps the MEV.

    Recommendation

    Validate msg.sender against the expected callback pool before honoring the LOAN_CALLBACK_TYPE_ONGOING branch. Store the expected callback sender when the swap starts and reject any other caller.

    Resolution

    Reflex: Acknowledged; mitigated off-chain by the Reflex team.

  2. H-02 High Missing Access Control On V4/Pancake Callbacks Access Control Resolved
    Location
    ExecutionRouter.sol
    Round
    Main Review

    Description

    unlockCallback and lockAcquired check the router’s callback state, but do not verify that msg.sender is the expected PoolManager or Pancake vault. During the callback window, any contract that can re-enter can reach these handlers. If a malicious caller reaches the V4/Pancake ongoing-swap path, the router can be induced to settle real tokens to the attacker-controlled contract while treating the fake caller as the swap context. The attacker can then extract the arbitrage profit, return enough assets for the router flow to finish, and leave Reflex with zero captured revenue.

    Recommendation

    Validate callback callers against the tracked in-lock context or an equivalent expected-caller mapping, and reject any callback that does not come from the legitimate PoolManager or vault.

    Resolution

    Reflex: Acknowledged; mitigated off-chain by the Reflex team.

  3. H-03 High Missing Output Meta Causes Profit Loss Logical Error Resolved
    Location
    ExecutionRouter.sol
    Round
    Main Review

    Description

    Standalone V4 and Pancake lock/unlock payloads only carry one token metadata value. The callback then reuses that same metadata as both input and output token info. When a standalone V4 or Pancake hop outputs native ETH, the router decides whether to wrap based on the wrong token. Instead of wrapping the actual output token, it uses the input token metadata. That can make the route revert during output handling, and in after-swap integrations the revert is swallowed, so the user swap lands but Reflex captures no profit.

    Recommendation

    Carry tokenMetaOut through every standalone V4 and Pancake lock/unlock payload and callback instead of reusing tokenMetaIn for output handling.

    Resolution

    Reflex: Resolved.

  4. H-04 High Unfunded Swap Path Leaks Arbitrage Profit Logical Error Resolved
    Location
    ExecutionRouter.sol
    Round
    Main Review

    Description

    The router explicitly pre-funds the next V2-like hop after a V2/V3 loan, but it does not do the same when a V4 or Pancake hop is followed by a V2-like hop. V4 and Pancake outputs stay on the router, and the next V2-like pair is called without receiving its input tokens first. That makes the next V2-like swap revert. In after-swap integrations the revert is swallowed, so the triggering user swap still lands but Reflex captures nothing. The leftover arbitrage can then be taken by an external searcher.

    Recommendation

    Centralize the fix in _swapFlow() and pre-fund a V2-like hop whenever the previous hop was V4 or Pancake.

    Resolution

    Reflex: Resolved.

  5. M-01 Medium Wrong Profit Token When Initial Hop Is Non-zero Logical Error Acknowledged
    Location
    ExecutionRouter.sol
    Round
    Main Review

    Description

    triggerBackrun always extracts the profit token from tokenMeta[0], even though the route can start at any initialHopIndex. When the initial hop is not zero, the router snapshots the wrong token balance, computes profit against the wrong asset, and distributes nothing. The real profit remains stranded in the contract until an admin withdraws it manually.

    Recommendation

    Use initialHopIndex when extracting the profit token so profit accounting follows the actual route entrypoint.

    Resolution

    Reflex: Acknowledged.

  6. M-02 Medium tx.origin Used As Profit Recipient In Hooks Unexpected Behavior Acknowledged
    Location
    UniswapV4Hook.sol, PancakeSwapInfinityHook.sol
    Round
    Main Review

    Description

    The hook contracts use tx.origin as the backrun profit recipient and LP-share fallback. That works for simple EOA flows, but breaks for smart wallets, relayers, and account-abstraction setups. In those cases, profits can be misdirected to the bundler, relayer, or submitting signer instead of the intended user or calling contract.

    Recommendation

    Do not use tx.origin to determine the payout recipient. Pass the intended beneficiary explicitly through the execution flow.

    Resolution

    Reflex: Acknowledged.

  7. L-01 Low Native ETH Routes Revert In triggerBackrun DoS Acknowledged
    Location
    ExecutionRouter.sol
    Round
    Main Review

    Description

    V4 and Pancake hop execution supports native ETH, but triggerBackrun always treats the profit token as an ERC20. If the route starts with native ETH, the router attempts ERC20 balance accounting against address(0) and reverts. This means otherwise valid native-ETH-only routes can fail entirely at profit accounting time.

    Recommendation

    Add explicit native ETH handling to triggerBackrun when the profit token is address(0).

    Resolution

    Reflex: Acknowledged.

  8. L-02 Low V4/Pancake Hops Settle Fixed amountIn DoS Acknowledged
    Location
    ExecutionRouter.sol
    Round
    Main Review

    Description

    The V4 and Pancake full-swap paths settle debt using the quoted amountIn rather than the actual swap input delta. On partial fills, the real debt can be smaller than the quoted amount. That causes the router to over-settle, leaving an unclaimed credit in the pool manager or vault. The leftover delta can then cause the transaction to revert at unlock finalization.

    Recommendation

    Settle using the actual deltaIn returned by the swap rather than the original quoted input amount.

    Resolution

    Reflex: Acknowledged.

  9. L-03 Low Fee-On-Transfer Token Burns Not Validated Validation Acknowledged
    Location
    ExecutionRouter.sol: 226-233
    Round
    Main Review

    Description

    The burn field in tokenMeta is unused everywhere in the router: All FOT handling is completely dependent on the quoter's pre-adjusted amounts. The router never checks that actual received amounts match expected amounts after burns. Loan repayment transfers a fixed pre-computed amount: The pool receives less than valid[initialHopIndex] after the burn fee is deducted, which can then fail pool invariant checks, causing a revert. The quoter must predict exactly which transfer path the router takes per hop to compute correct amounts.

    Recommendation

    Use the burn field to adjust transfer amounts, or implement balance-based accounting rather than relying on pre-computed amounts for FOT tokens.

    Resolution

    Reflex: Acknowledged.

  10. L-04 Low Silent Uint112 Truncation Of amountIn Validation Acknowledged
    Location
    ReflexAfterSwap.sol: 162
    Round
    Main Review

    Description

    In ReflexAfterSwap::_reflexAfterSwap, amountIn is cast from uint256 to uint112 without validation: If amountIn exceeds type(uint112).max (~5.19e33), the value silently truncates, passing an incorrect amount to the quoter. The quoter computes a route based on the wrong amount, resulting in either no profit found or a suboptimal arbitrage. Due to the try-catch, the MEV opportunity is silently missed. While overflow is unlikely for standard 18-decimal tokens, it becomes realistic, for example, for non-standard high-decimal tokens.

    Recommendation

    Add an overflow check before the cast:

    Resolution

    Reflex: Acknowledged.

  11. I-01 Informational Unvalidated Zero Address Recipient Traps Funds Validation Resolved
    Location
    ConfigurableRevenueDistributor.sol: 137
    Round
    Main Review

    Description

    In triggerBackrun, the recipient parameter is passed directly to _splitERC20 as variedRecipient without any zero-address check: When variedRecipient is address(0), the varied share distribution block in _splitERC20 is skipped entirely: With the default config allocating 80% to main recipients and 20% to the varied recipient, passing address(0) silently leaves 20% of profit plus undistributed in the contract. The admin can recover via withdrawToken, but there is no indication to the caller that funds were partially retained.

    Recommendation

    Validate recipient is non-zero in triggerBackrun:

    Resolution

    Reflex: Resolved.

  12. I-02 Informational Unused _splitETH Function Superfluous Code Resolved
    Location
    ConfigurableRevenueDistributor.sol: 160
    Round
    Main Review

    Description

    ConfigurableRevenueDistributor::_splitETH is implemented but never called anywhere in the protocol. ReflexRouter::triggerBackrun uses _splitERC20 for profit distribution, and no other code path invokes _splitETH. Note that if it were ever integrated for native ETH profit distribution, it also introduces reentrancy risk via recipient.call{value: share}("").

    Recommendation

    Remove _splitETH if native ETH distribution is not intended. If it is planned for future use, ensure to add reentrancy protection.

    Resolution

    Reflex: Resolved.

  13. I-03 Informational withdrawEth Uses Fixed 2300 Gas Compatibility Resolved
    Location
    ExecutionRouter.sol: 705
    Round
    Main Review

    Description

    withdrawEth uses .transfer() which forwards only 2300 gas to the recipient: This may fail if _to is a contract that requires more than 2300 gas to receive ETH.

    Recommendation

    Use a low-level call instead:

    Resolution

    Reflex: Resolved.

  14. I-04 Informational Missing Event In updateDefaultConfig Events Resolved
    Location
    ConfigurableRevenueDistributor.sol: 104
    Round
    Main Review

    Description

    ConfigurableRevenueDistributor::updateShares emits SharesUpdated after modifying a configuration, but updateDefaultConfig does not: Off-chain indexers and systems tracking SharesUpdated events will not detect changes to it from the default configuration.

    Recommendation

    Emit SharesUpdated in updateDefaultConfig.

    Resolution

    Reflex: Resolved.

  15. I-05 Informational Fee Discount Requires Dynamic Fee Configuration Documentation Resolved
    Location
    UniswapV4Hook.sol: 118
    Round
    Main Review

    Description

    In both UniswapV4Hook and PancakeSwapInfinityHook, beforeSwap returns LPFeeLibrary.OVERRIDE_FEE_FLAG to grant the router zero fees on backrun swaps: This override is only in effect if the pool was initialized with dynamic fees enabled in its pool key. If the pool uses static fees, the flag is silently ignored and the router pays the full LP fee on every backrun, reducing captured MEV profit for users.

    Recommendation

    Document that pools integrating Reflex hooks must be initialized with dynamic fees enabled for the fee discount to function. Consider adding a validation check during pool initialization to ensure compatibility.

    Resolution

    Reflex: Resolved.

  16. I-06 Informational Immutable Owner In Hook Contracts Configuration Resolved
    Location
    UniswapV4Hook.sol: 23
    Round
    Main Review

    Description

    In both UniswapV4Hook and PancakeSwapInfinityHook, owner is declared immutable. If the owner key needs to be rotated for security reasons or operational changes, the existing hook's admin functions (router updates, config changes, fee discounts) cannot be transferred to a the new owner.

    Recommendation

    Implement a transferOwnership function with a two-step transfer pattern to allow safe owner rotation.

    Resolution

    Reflex: Resolved.

  17. I-07 Informational External Function Has Internal Naming Convention Best Practices Resolved
    Location
    PancakeSwapInfinityHook.sol: 176
    Round
    Main Review

    Description

    _donateToPool in both UniswapV4Hook and PancakeSwapInfinityHook is declared external but uses an underscore prefix, which by Solidity convention indicates internal or private visibility: The function is external to enable try-catch on self-calls, however, the underscore prefix is misleading and may cause confusion during future development.

    Recommendation

    Rename to donateToPool to reflect its external visibility.

    Resolution

    Reflex: Resolved.

  18. I-08 Informational Hooks Can Siphon MEV Via Return Delta Warning Acknowledged
    Location
    ExecutionRouter.sol: 379
    Round
    Main Review

    Description

    The router executes arbitrage routes through any V4 or PancakeSwap pool the quoter selects, however, V4 and PancakeSwap hooks can modify swap return deltas through beforeSwap/afterSwap (including adjusting deltas, taking fees, or making external calls). A hook on an intermediate pool in a route could reduce the swap output, effectively siphoning a portion of the expected arbitrage profit. The router does not validate actual swap output against expected amounts, so reduced output from a hook goes undetected as long as the remaining amount is sufficient to complete the route and repay the loan.

    Recommendation

    Document the trust assumptions around routing through UniswapV4 and PancakeSwap pools, while ensuring the quoter only routes through pools with verified and trusted hooks.

    Resolution

    Reflex: Acknowledged.

  19. I-09 Informational Loan For V2 Without Callback Silently Skipped Informational Resolved
    Location
    ExecutionRouter.sol: 292
    Round
    Main Review

    Description

    _triggerSwapRoute has no branch handling isUniswapV2WithoutCallback as the initial hop. V2 without callback pools cannot execute the initial hop since they lack the data parameter needed for flash swaps. If the quoter selects one, the function silently does nothing (no swap executes, profit = 0, and gas is wasted).

    Recommendation

    Ensure the quoter does not return isUniswapV2WithoutCallback as the initial hop, or add an explicit check/revert to fail gracefully rather than silently revert if this occurs.

    Resolution

    Reflex: Resolved.

Remediation Review

4 findings
  1. L-01 Low Backrun Failure Return Value Is Ambiguous Unexpected Behavior Acknowledged
    Location
    BackrunEnabledSwapProxy.sol: 163-167
    Round
    Remediation Review

    Description

    In BackrunEnabledSwapProxy::swapWithBackrun, the dev comments state that failed backruns are identified by zero return values: However, triggerBackrun returns (0, address(0)) on a successful execution when no arbitrage opportunity exists: This makes a successful, although unprofitable, backrun execution identical to a caught revert/failure. This will affect callers attempting to distinguish between a route with no arbitrage available and a broken route that consistently fails, preventing appropriate retry and error handling.

    Recommendation

    Consider handling the case where a successful triggerBackrun call returns (0, address(0)) as success rather than unexpected failure.

    Resolution

    Reflex: Acknowledged.

  2. I-01 Informational Admin Changes Can Silently Skip MEV Capture Trust Assumptions Acknowledged
    Location
    ReflexAfterSwap.sol: 100
    Round
    Remediation Review

    Description

    If the admin calls setReflexRouter or modifies fee discounts via setGlobalFeeDiscount/setPoolFeeDiscount while a user's swap transaction is pending, the backrun may silently fail. The previous router loses its fee discount, causing the backrun to pay full LP fees, which can eliminate the arbitrage profit. The try-catch in _reflexAfterSwap skips the failure, and the user's swap completes normally but without receiving any MEV redistribution.

    Recommendation

    Consider documenting that admin operations on hook contracts should be performed during low-activity periods to minimize impact on pending user swap

    Resolution

    Reflex: Acknowledged.

  3. I-02 Informational Native ETH Donate Fails In Hook Contracts Informational Acknowledged
    Location
    UniswapV4Hook.sol: 202-211
    Round
    Remediation Review

    Description

    In donateToPool within both UniswapV4Hook and PancakeSwapInfinityHook, the WETH/ETH branch only matches when currency is native ETH and profitToken is WETH: If both currency and profitToken are address(0) (native ETH), the first branch is skipped since profitToken != weth, and the else branch calls IERC20(address(0))::safeTransfer which reverts. Currently, sponsors have confirmed that profit token is not expected to be native ETH. However, if this changes in the future, the current hook contracts are incompatible and will cause unexpected reverts.

    Recommendation

    Consider adding handling for the case where both profitToken and currency are native ETH, or document that if ETH is added as a profit token in the future, the UniswapV4 and PancakeSwap hook contracts are incompatible.

    Resolution

    Reflex: Acknowledged.

  4. I-03 Informational Redundant Add Operation In _bytesToAddress Superfluous Code Resolved
    Location
    ExecutionRouter.sol: 794
    Round
    Remediation Review

    Description

    _bytesToAddress contains a redundant add operation in its assembly block. add(add(d, 20), 0) is functionally identical to add(d, 20), wasting an opcode.

    Recommendation

    Simplify to addr := mload(add(d, 20)).

    Resolution

    Reflex: Resolved.

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote