All audits

Web3 · Smart contract audit

Lambdaplex

softstack has completed 3 smart contract audits for Lambdaplex between June 2026 and September 2026, reporting 95 findings in total (6 high, 34 medium, 35 low and 20 informational), of which 61 were resolved or mitigated before the final reports. Every report is published in the softstack public audit archive.

Key facts

Client
Lambdaplex
Audits completed
3
First audited
Latest audit
Type
Smart contract audit
Chains
Base, Ethereum
Languages
Solidity

About the project

Lambdaplex is a non-custodial trading protocol for spot markets, built by Lambdaplex Labs in Raleigh, North Carolina. Traders sign orders that enter a matching engine queue, where market makers or other counterparties match compatible orders. Settlement is carried out by Lambdaplex smart contracts on the Hedera Smart Contract Service.

Findings

Combined across all 3 reviews softstack has completed for Lambdaplex. Each report is broken out below.

0Critical
6High
34Medium
35Low
20Informational

61 of 95 findings resolved before the final reports.

Reports

Newest first. Every report covers its own scope and commit.

  1. Vault, Order Hook, Settlement, Pro Rata Vault & Settlement (Base) smart contract audit

    · 24 files

    PDF
    0Critical
    2High
    12Medium
    16Low
    9Informational

    23 of 39 resolved

    ScopeSource repositorygithub.com/lambdaplexlabs/lambdaplex-balanced-vault

    Components reviewed

    14 modules · 24 files
    1. AirdropDistributor.sol
    2. PLEXPairVault.sol
    3. PLEXBrokerRegistry.sol
    4. SupraRegistry.sol
    5. PLEXVaultHook.sol
    6. IAirdropDistributor.sol
    7. IHieroHook.sol
    8. IOrderFlowAllowance.sol
    9. IPLEXBrokerRegistry.sol
    10. IocFokWriteback.sol
    11. OrderType.sol
    12. PRBMathCommon.sol
    13. SafeERC20.sol
    14. PLEXPairVaultHedera.sol
    All 39 findings
    IDTitleSeverityStatus
    6.2.5Eligible Shares Can Be Zero During Normal Operation

    The `_eligibleShares()` function calculates shares eligible for rewards by excluding owner fee shares from the total. When all depositors withdraw their funds, only `ownerFeeShares` remain in the vault.

    Mediumacknowledged
    6.2.9lockupSecs and feeChangeDelaySecs Can Be Set to Zero, Disabling Core Protections

    The constructor validates `vestingSecs_ > 0` and `initialBalanceTolBips_ <= 50_000`, but does not validate `lockupSecs_` or `feeChangeDelaySecs_`.

    Mediumfixed
    6.2.10AirdropDistributor.SafeERC20 Still Uses High-Level transfer()/ transferFrom(), Inconsistent With Vault's Fix

    A previous change upgraded the vault's `SafeERC20` to use a low-level `call()` to support tokens that don't return a `bool`.

    Mediumfixed
    6.2.11Mutable Oracle Pair Metadata Can Brick or Reprice Live Stop Orders

    `registerPair()` overwrites `pairs[pairId]` without checking whether the pair already exists. Active stop orders that rely on that pair id can be silently bricked by changing the pair tokens, or repriced by changing the token decimal…

    Mediumacknowledged
    6.2.12Market Orders Can Set deviation == BIPS and Collapse Required Credit

    For market-style oracle orders, `_oracleFactor()` permits `slip <= BIPS` and computes `factorBps = BIPS - slip`. If `slip == BIPS`, `factorBps` becomes zero.

    Mediumacknowledged
    6.2.13Signed Malformed Orders Can Persist and Revert Later Execution

    `_installSignedOrders()` persists any nonzero signed retail detail after signature validation and duplicate checks. It does not reject `denominator == 0`.

    Mediumfixed
    6.2.14Any address can permanently close a MARKET order with a 1 wei fill

    settleTrade is permissionless whenever a batch carries no vault authorizations. Any address, not just the intended market maker, can submit a settlement and fill part of a signed order.

    Mediumfixed
    6.2.18Missing Upper Bounds on uint64 Immutables Can Permanently DoS the Vault

    `lockupSecs`, `feeChangeDelaySecs` and `vestingSecs` are `uint64` immutables set once in the constructor with no upper bound validation.

    Lowfixed
    6.2.19STALE_PRICE Tightened to 30 Seconds Without Complementary Round Validation — Availability Risk

    The staleness window was correctly tightened from 60s to 30s as part of oracle hardening: Reducing the window is the right direction — a shorter staleness window shrinks the exploitation surface for stale-price attacks.

    Lowacknowledged
    6.2.20claimRewards(address) Does Not Use try/catch — Single Bad Token Still Reverts That Path

    `claimAllRewards()` was upgraded with `try/catch` (see L-02) but the single-token `claimRewards(address rewardToken)` was not wrapped in `try/catch`: If a specific reward token is permanently broken (paused, blacklisted, or a…

    Lowfixed
    6.2.21Manager Can Drain the Whole Vault Inventory Through an Arbitrary Swap Path

    The manager rebalance functions move up to a full side's inventory (`amountIn` bounded only by `inBefore >= amountIn`) with `amountOutMin` validated only as `> 0` (so it may be 1) through a swap path whose only checks are the first and…

    Lowfixed
    6.2.22Outbound Fee-on-Transfer Under-Delivery Overstates the Ledger

    `AirdropDistributor.claimTo` and the vault's `_transferOut` send a nominal amount and book it as fully delivered, with no re-check of what arrived. Every inbound leg re-checks with `FOT_FORBIDDEN`, but the outbound legs do not.

    Lowacknowledged
    6.2.23Failed Reward Claim Permanently Strands Accrued Rewards

    `claimRewards` and `claimAllRewards` clear the user's `accrued` only on the success branch; the catch branch emits an event with no write-off or reconcile path.

    Lowfixed
    6.2.24Management-Fee Effective-Boundary Off-by-One Overcharges the Notice Window

    `_accrueMgmtFee` gates the old/new-rate split on strict `nowTs > effTs`, while `_applyPendingFeeIfDue` applies the pending rate on `nowTs >= effTs`.

    Lowfixed
    6.2.26Reward Per-Share Truncation Lets an Attacker Zero Out a Small Stream

    `_updateReward` preserves the remainder in `carry` only when `eligibleShares == 0`; when eligible is positive, the per-share increment is floor-divided and the remainder is discarded.

    Lowfixed
    6.2.27Distributor Owner Can Permanently Latch Emergency Mode on a Co- Tenant Vault

    `enableEmergencyMode` authorizes `owner()` or the distributor's `owner()`, and emergency mode is a one-way latch with no disable function.

    Lowacknowledged
    6.2.28A MARKET order signed with the maximum deviation value settles for close to nothing

    MARKET orders carry a deviationPpm field that is supposed to bound how far the fill price can move away from the signed reference price. The validation only rejects values strictly above PPM (1,000,000), so deviationPpm == PPM is accepted.

    Lowacknowledged
    6.2.29A vault manager's signature is not tied to a specific executor, so revoking one executor does not stop another from using the same signature

    A vault batch needs two things, a signature from the vault's manager over the settlement, and the transaction being sent by an approved executor.

    Lowacknowledged
    6.2.30The vault nonce is marked used after the external calls that can still fail

    When authorizing a vault, the contract calls the vault's manager() function and then checks the manager's signature, both of which are external calls, before it marks the nonce as used.

    Lowacknowledged
    6.2.31ownerRedeemFees Silently Caps feeSharesToBurn to Available Balance

    If the owner requests more fee shares than available, the function silently caps to `available`. This could be surprising if the owner expected to burn exactly `feeSharesToBurn` shares — they would receive a different payout than expected,…

    Informationalfixed
    6.2.32receive() Allows Anyone to Donate HBAR, Inflating Vault TVL

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Informationalacknowledged
    6.2.33Dual SafeERC20 Library Definitions Create Maintenance Risk

    Two different `SafeERC20` library implementations exist in the codebase: 1. The vault's (low-level `call()`, handles non-bool-returning tokens). 2. The distributor's (high-level `transfer()`/`transferFrom()`, reverts on non-bool tokens).

    Informationalacknowledged
    6.2.34Reward Carry Stranded When eligibleShares Hits Zero With No Later Fund

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Informationalacknowledged
    6.2.36Read-Only Reentrancy on the Two-Leg Payout Misleads Integrators

    Withdrawals send the BASE leg (a raw HBAR call when BASE is native) before the QUOTE leg, so during the callback an external integrator pricing shares off `previewWithdrawProRata` sees a decremented supply against a not-yet-decremented…

    Informationalacknowledged
    6.2.37Preview Views Omit Fee Accrual, So Quotes Diverge From Execution

    `previewDepositProRata` and `previewWithdrawProRata` read `totalShares` directly without simulating `_accrueMgmtFee`, while the state-changing paths accrue first; a caller pinning `minOut`/ `maxIn` exactly to the preview can hit a…

    Informationalfixed
    6.2.38Single-Step Ownable With Reachable renounceOwnership

    The contract uses single-step `Ownable` and does not disable `renounceOwnership`; an owner-key mistake irreversibly bricks all owner functions (fee redemption, manager rotation, emergency redemption).

    Informationalfixed
    6.2.39Executor can settle every MARKET fill at the worst allowed price with no upper bound

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Informationalacknowledged
    6.2.1First-Depositor Vault Inflation Attack

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Highfixed
    6.2.2Missing Zero-Price Protection in Oracle Factor

    The `_oracleFactor()` function in both `LambdaplexOrderHook.sol` and `PLEXVaultHook.sol` does not validate that the oracle price returned from Supra is non-zero before using it for trigger price comparisons.

    Highfixed
    6.2.360-Second Staleness Window Vulnerability

    The PLEXPairVault contract uses a 60-second staleness threshold (`STALE_PRICE = 60`) for validating oracle price proofs from Supra Oracle.

    Mediumfixed
    6.2.4Oracle Proof Replay Within Staleness Window

    The `LambdaplexOrderHook` contract verifies oracle proofs from Supra Oracle using the `_oracleFactor()` function.

    Mediumfixed
    6.2.6minFill Validation Uses Executed Quantity, Not Total Order Quantity

    The `minFill` validation in `LambdaplexOrderHook._applyOne()` validates the fill amount against the current remaining quantity instead of the original order quantity.

    Mediumfixed
    6.2.7Reward Token Array Unbounded Growth

    The `rewardTokens` array in `PLEXPairVault` grows unboundedly when new reward tokens are funded via the distributor. Each unique token funded is permanently added to the array with no mechanism to remove exhausted or deprecated tokens.

    Mediumfixed
    6.2.8Owner Fee Share Redemption Race Condition

    The `ownerRedeemFees()` function allows the vault owner to redeem accrued management fee shares for BASE/QUOTE tokens.

    Mediumfixed
    6.2.15Reward Carry Truncation (uint128)

    The `PLEXPairVault` contract uses explicit `uint128()` casts when storing reward carry values. In Solidity 0.8+, explicit type casts do not revert on overflow — they silently truncate the value to fit the smaller type.

    Lowfixed
    6.2.16Unregistered Reward Token Can Block Claiming

    `claimAllRewards()` iterates through all registered reward tokens and calls `distributor.claimTo(...)` sequentially.

    Lowfixed
    6.2.17HTS associateToken Return Value Misinterpreted

    The `AirdropDistributor` contract uses low-level calls to the Hedera Token Service (HTS) precompile at address `0x167` for token association and disassociation. The implementation has several flaws in handling the return values: 1.

    Lowfixed
    6.2.25Permissionless Fund Floods the Reward-Token List

    `distributor.fund` is callable by anyone for an owner-allowlisted token, and each new token is appended to a reward list that `_settleRewards` iterates in full on every deposit, withdrawal, emergency exit, and claim, imposing a permanent…

    Lowacknowledged
    6.2.35Distributor Address Is Immutable With No Setter

    `distributor` is a mutable variable with a `DistributorSet` event but only a constructor assignment, so a bad deploy value or a needed migration permanently disables the reward feature with no on-chain fix.

    Informationalacknowledged
  2. Vault, Order Hook, Settlement & Pro Rata Vault smart contract audit

    · 24 files

    PDF
    0Critical
    2High
    11Medium
    13Low
    8Informational

    22 of 34 resolved

    ScopeSource repositorygithub.com/lambdaplexlabs/lambdaplex-balanced-vault

    Components reviewed

    14 modules · 24 files
    1. AirdropDistributor.sol
    2. PLEXPairVault.sol
    3. PLEXBrokerRegistry.sol
    4. SupraRegistry.sol
    5. PLEXVaultHook.sol
    6. IERC20.sol
    7. IHieroHook.sol
    8. IOrderFlowAllowance.sol
    9. IPLEXBrokerRegistry.sol
    10. IocFokWriteback.sol
    11. OrderType.sol
    12. PRBMathCommon.sol
    13. SafeERC20.sol
    14. PLEXPairVaultHedera.sol
    All 34 findings
    IDTitleSeverityStatus
    6.2.5Eligible Shares Can Be Zero During Normal Operation

    The `_eligibleShares()` function calculates shares eligible for rewards by excluding owner fee shares from the total. When all depositors withdraw their funds, only `ownerFeeShares` remain in the vault.

    Mediumacknowledged
    6.2.6minFill Validation Uses Executed Quantity, Not Total Order Quantity

    The `minFill` validation in `LambdaplexOrderHook._applyOne()` validates the fill amount against the current remaining quantity instead of the original order quantity.

    Mediumfixed
    6.2.9lockupSecs and feeChangeDelaySecs Can Be Set to Zero, Disabling Core Protections

    The constructor validates `vestingSecs_ > 0` and `initialBalanceTolBips_ <= 50_000`, but does not validate `lockupSecs_` or `feeChangeDelaySecs_`.

    Mediumfixed
    6.2.10AirdropDistributor.SafeERC20 Still Uses High-Level transfer()/ transferFrom(), Inconsistent With Vault's Fix

    A previous change upgraded the vault's `SafeERC20` to use a low-level `call()` to support tokens that don't return a `bool`.

    Mediumfixed
    6.2.11Mutable Oracle Pair Metadata Can Brick or Reprice Live Stop Orders

    `registerPair()` overwrites `pairs[pairId]` without checking whether the pair already exists. Active stop orders that rely on that pair id can be silently bricked by changing the pair tokens, or repriced by changing the token decimal…

    Mediumacknowledged
    6.2.12Market Orders Can Set deviation == BIPS and Collapse Required Credit

    For market-style oracle orders, `_oracleFactor()` permits `slip <= BIPS` and computes `factorBps = BIPS - slip`. If `slip == BIPS`, `factorBps` becomes zero.

    Mediumacknowledged
    6.2.13Signed Malformed Orders Can Persist and Revert Later Execution

    `_installSignedOrders()` persists any nonzero signed retail detail after signature validation and duplicate checks. It does not reject `denominator == 0`.

    Mediumfixed
    6.2.17Missing Upper Bounds on uint64 Immutables Can Permanently DoS the Vault

    `lockupSecs`, `feeChangeDelaySecs` and `vestingSecs` are `uint64` immutables set once in the constructor with no upper bound validation.

    Lowfixed
    6.2.18STALE_PRICE Tightened to 30 Seconds Without Complementary Round Validation — Availability Risk

    The staleness window was correctly tightened from 60s to 30s as part of oracle hardening: Reducing the window is the right direction — a shorter staleness window shrinks the exploitation surface for stale-price attacks.

    Lowacknowledged
    6.2.19claimRewards(address) Does Not Use try/catch — Single Bad Token Still Reverts That Path

    `claimAllRewards()` was upgraded with `try/catch` (see L-02) but the single-token `claimRewards(address rewardToken)` was not wrapped in `try/catch`: If a specific reward token is permanently broken (paused, blacklisted, or a…

    Lowfixed
    6.2.20Manager Can Drain the Whole Vault Inventory Through an Arbitrary Swap Path

    The manager rebalance functions move up to a full side's inventory (`amountIn` bounded only by `inBefore >= amountIn`) with `amountOutMin` validated only as `> 0` (so it may be 1) through a swap path whose only checks are the first and…

    Lowfixed
    6.2.22Failed Reward Claim Permanently Strands Accrued Rewards

    `claimRewards` and `claimAllRewards` clear the user's `accrued` only on the success branch; the catch branch emits an event with no write-off or reconcile path.

    Lowfixed
    6.2.23Management-Fee Effective-Boundary Off-by-One Overcharges the Notice Window

    `_accrueMgmtFee` gates the old/new-rate split on strict `nowTs > effTs`, while `_applyPendingFeeIfDue` applies the pending rate on `nowTs >= effTs`.

    Lowfixed
    6.2.25Reward Per-Share Truncation Lets an Attacker Zero Out a Small Stream

    `_updateReward` preserves the remainder in `carry` only when `eligibleShares == 0`; when eligible is positive, the per-share increment is floor-divided and the remainder is discarded.

    Lowfixed
    6.2.26Distributor Owner Can Permanently Latch Emergency Mode on a Co- Tenant Vault

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Lowacknowledged
    6.2.27ownerRedeemFees Silently Caps feeSharesToBurn to Available Balance

    If the owner requests more fee shares than available, the function silently caps to `available`. This could be surprising if the owner expected to burn exactly `feeSharesToBurn` shares — they would receive a different payout than expected,…

    Informationalfixed
    6.2.28receive() Allows Anyone to Donate HBAR, Inflating Vault TVL

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Informationalacknowledged
    6.2.29Dual SafeERC20 Library Definitions Create Maintenance Risk

    Two different `SafeERC20` library implementations exist in the codebase: 1. The vault's (low-level `call()`, handles non-bool-returning tokens). 2. The distributor's (high-level `transfer()`/`transferFrom()`, reverts on non-bool tokens).

    Informationalacknowledged
    6.2.30Reward Carry Stranded When eligibleShares Hits Zero With No Later Fund

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Informationalacknowledged
    6.2.32Read-Only Reentrancy on the Two-Leg Payout Misleads Integrators

    Withdrawals send the BASE leg (a raw HBAR call when BASE is native) before the QUOTE leg, so during the callback an external integrator pricing shares off `previewWithdrawProRata` sees a decremented supply against a not-yet-decremented…

    Informationalacknowledged
    6.2.33Preview Views Omit Fee Accrual, So Quotes Diverge From Execution

    `previewDepositProRata` and `previewWithdrawProRata` read `totalShares` directly without simulating `_accrueMgmtFee`, while the state-changing paths accrue first; a caller pinning `minOut`/ `maxIn` exactly to the preview can hit a…

    Informationalfixed
    6.2.34Single-Step Ownable With Reachable renounceOwnership

    The contract uses single-step `Ownable` and does not disable `renounceOwnership`; an owner-key mistake irreversibly bricks all owner functions (fee redemption, manager rotation, emergency redemption).

    Informationalfixed
    6.2.1First-Depositor Vault Inflation Attack

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Highfixed
    6.2.2Missing Zero-Price Protection in Oracle Factor

    The `_oracleFactor()` function in both `LambdaplexOrderHook.sol` and `PLEXVaultHook.sol` does not validate that the oracle price returned from Supra is non-zero before using it for trigger price comparisons.

    Highfixed
    6.2.360-Second Staleness Window Vulnerability

    The PLEXPairVault contract uses a 60-second staleness threshold (`STALE_PRICE = 60`) for validating oracle price proofs from Supra Oracle.

    Mediumfixed
    6.2.4Oracle Proof Replay Within Staleness Window

    The `LambdaplexOrderHook` contract verifies oracle proofs from Supra Oracle using the `_oracleFactor()` function.

    Mediumfixed
    6.2.7Reward Token Array Unbounded Growth

    The `rewardTokens` array in `PLEXPairVault` grows unboundedly when new reward tokens are funded via the distributor. Each unique token funded is permanently added to the array with no mechanism to remove exhausted or deprecated tokens.

    Mediumfixed
    6.2.8Owner Fee Share Redemption Race Condition

    The `ownerRedeemFees()` function allows the vault owner to redeem accrued management fee shares for BASE/QUOTE tokens.

    Mediumfixed
    6.2.14Reward Carry Truncation (uint128)

    The `PLEXPairVault` contract uses explicit `uint128()` casts when storing reward carry values. In Solidity 0.8+, explicit type casts do not revert on overflow — they silently truncate the value to fit the smaller type.

    Lowfixed
    6.2.15Unregistered Reward Token Can Block Claiming

    `claimAllRewards()` iterates through all registered reward tokens and calls `distributor.claimTo(...)` sequentially.

    Lowfixed
    6.2.16HTS associateToken Return Value Misinterpreted

    The `AirdropDistributor` contract uses low-level calls to the Hedera Token Service (HTS) precompile at address `0x167` for token association and disassociation. The implementation has several flaws in handling the return values: 1.

    Lowfixed
    6.2.21Outbound Fee-on-Transfer Under-Delivery Overstates the Ledger

    `AirdropDistributor.claimTo` and the vault's `_transferOut` send a nominal amount and book it as fully delivered, with no re-check of what arrived. Every inbound leg re-checks with `FOT_FORBIDDEN`, but the outbound legs do not.

    Lowacknowledged
    6.2.24Permissionless Fund Floods the Reward-Token List

    `distributor.fund` is callable by anyone for an owner-allowlisted token, and each new token is appended to a reward list that `_settleRewards` iterates in full on every deposit, withdrawal, emergency exit, and claim, imposing a permanent…

    Lowacknowledged
    6.2.31Distributor Address Is Immutable With No Setter

    `distributor` is a mutable variable with a `DistributorSet` event but only a constructor assignment, so a bad deploy value or a needed migration permanently disables the reward feature with no on-chain fix.

    Informationalacknowledged
  3. Vault, Order Hook & Settlement smart contract audit

    · 18 files

    PDF
    0Critical
    2High
    11Medium
    6Low
    3Informational

    16 of 22 resolved

    ScopeSource repositorygithub.com/lambdaplexlabs/lambdaplex-balanced-vault

    Components reviewed

    14 modules · 18 files
    1. AirdropDistributor.sol
    2. PLEXPairVault.sol
    3. PLEXBrokerRegistry.sol
    4. SupraRegistry.sol
    5. PLEXVaultHook.sol
    6. IHederaTokenService.sol
    7. IHieroHook.sol
    8. IOrderFlowAllowance.sol
    9. IPLEXBrokerRegistry.sol
    10. IocFokWriteback.sol
    11. OrderType.sol
    12. PRBMathCommon.sol
    13. SafeERC20.sol
    14. PLEXPairVaultHedera.sol
    All 22 findings
    IDTitleSeverityStatus
    6.2.5Eligible Shares Can Be Zero During Normal Operation

    The `_eligibleShares()` function calculates shares eligible for rewards by excluding owner fee shares from the total. When all depositors withdraw their funds, only `ownerFeeShares` remain in the vault.

    Mediumacknowledged
    6.2.6minFill Validation Uses Executed Quantity, Not Total Order Quantity

    The `minFill` validation in `LambdaplexOrderHook._applyOne()` validates the fill amount against the current remaining quantity instead of the original order quantity.

    Mediumfixed
    6.2.9lockupSecs and feeChangeDelaySecs Can Be Set to Zero, Disabling Core Protections

    The constructor validates `vestingSecs_ > 0` and `initialBalanceTolBips_ <= 50_000`, but does not validate `lockupSecs_` or `feeChangeDelaySecs_`.

    Mediumfixed
    6.2.10AirdropDistributor.SafeERC20 Still Uses High-Level transfer()/ transferFrom(), Inconsistent With Vault's Fix

    A previous change upgraded the vault's `SafeERC20` to use a low-level `call()` to support tokens that don't return a `bool`.

    Mediumfixed
    6.2.11Mutable Oracle Pair Metadata Can Brick or Reprice Live Stop Orders

    `registerPair()` overwrites `pairs[pairId]` without checking whether the pair already exists. Active stop orders that rely on that pair id can be silently bricked by changing the pair tokens, or repriced by changing the token decimal…

    Mediumacknowledged
    6.2.12Market Orders Can Set deviation == BIPS and Collapse Required Credit

    For market-style oracle orders, `_oracleFactor()` permits `slip <= BIPS` and computes `factorBps = BIPS - slip`. If `slip == BIPS`, `factorBps` becomes zero.

    Mediumacknowledged
    6.2.13Signed Malformed Orders Can Persist and Revert Later Execution

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Mediumfixed
    6.2.17Missing Upper Bounds on uint64 Immutables Can Permanently DoS the Vault

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Lowfixed
    6.2.18STALE_PRICE Tightened to 30 Seconds Without Complementary Round Validation — Availability Risk

    The staleness window was correctly tightened from 60s to 30s as part of oracle hardening: Reducing the window is the right direction — a shorter staleness window shrinks the exploitation surface for stale-price attacks.

    Lowacknowledged
    6.2.19claimRewards(address) Does Not Use try/catch — Single Bad Token Still Reverts That Path

    `claimAllRewards()` was upgraded with `try/catch` (see L-02) but the single-token `claimRewards(address rewardToken)` was not wrapped in `try/catch`: If a specific reward token is permanently broken (paused, blacklisted, or a…

    Lowfixed
    6.2.20ownerRedeemFees Silently Caps feeSharesToBurn to Available Balance

    If the owner requests more fee shares than available, the function silently caps to `available`. This could be surprising if the owner expected to burn exactly `feeSharesToBurn` shares — they would receive a different payout than expected,…

    Informationalfixed
    6.2.21receive() Allows Anyone to Donate HBAR, Inflating Vault TVL

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Informationalacknowledged
    6.2.22Dual SafeERC20 Library Definitions Create Maintenance Risk

    Two different `SafeERC20` library implementations exist in the codebase: 1. The vault's (low-level `call()`, handles non-bool-returning tokens). 2. The distributor's (high-level `transfer()`/`transferFrom()`, reverts on non-bool tokens).

    Informationalacknowledged
    6.2.1First-Depositor Vault Inflation Attack

    The PLEXPairVault contract is vulnerable to a classic first-depositor inflation attack (also known as "donation attack" or "share price manipulation"). The vulnerability exists because: 1.

    Highfixed
    6.2.2Missing Zero-Price Protection in Oracle Factor

    The `_oracleFactor()` function in both `LambdaplexOrderHook.sol` and `PLEXVaultHook.sol` does not validate that the oracle price returned from Supra is non-zero before using it for trigger price comparisons.

    Highfixed
    6.2.360-Second Staleness Window Vulnerability

    The PLEXPairVault contract uses a 60-second staleness threshold (`STALE_PRICE = 60`) for validating oracle price proofs from Supra Oracle.

    Mediumfixed
    6.2.4Oracle Proof Replay Within Staleness Window

    The `LambdaplexOrderHook` contract verifies oracle proofs from Supra Oracle using the `_oracleFactor()` function.

    Mediumfixed
    6.2.7Reward Token Array Unbounded Growth

    The `rewardTokens` array in `PLEXPairVault` grows unboundedly when new reward tokens are funded via the distributor. Each unique token funded is permanently added to the array with no mechanism to remove exhausted or deprecated tokens.

    Mediumfixed
    6.2.8Owner Fee Share Redemption Race Condition

    The `ownerRedeemFees()` function allows the vault owner to redeem accrued management fee shares for BASE/QUOTE tokens.

    Mediumfixed
    6.2.14Reward Carry Truncation (uint128)

    The `PLEXPairVault` contract uses explicit `uint128()` casts when storing reward carry values. In Solidity 0.8+, explicit type casts do not revert on overflow — they silently truncate the value to fit the smaller type.

    Lowfixed
    6.2.15Unregistered Reward Token Can Block Claiming

    `claimAllRewards()` iterates through all registered reward tokens and calls `distributor.claimTo(...)` sequentially.

    Lowfixed
    6.2.16HTS associateToken Return Value Misinterpreted

    The `AirdropDistributor` contract uses low-level calls to the Hedera Token Service (HTS) precompile at address `0x167` for token association and disassociation. The implementation has several flaws in handling the return values: 1.

    Lowfixed

Methodology

Review process

  1. Scoping and threat modelling

    The commit hash is frozen and the attack surface is mapped: asset flows, trust assumptions, privileged roles and the external systems the code depends on.

  2. Automated analysis

    Static analysers sweep the full codebase to surface known weakness patterns, unreachable states and deviations from established conventions.

  3. Manual code review

    Independent engineers read the code line by line, concentrating on business logic, access control, arithmetic and the handling of external calls.

  4. Adversarial testing

    Property-based fuzzing and invariant tests attack the assumptions the protocol relies on, targeting states that ordinary unit tests never reach.

  5. Reporting

    Every finding is documented with a severity rating, its impact, the steps to reproduce it and a concrete remediation the team can act on.

  6. Remediation verification

    Submitted fixes are re-reviewed against the original finding, and the final report records the resolved state of each one.

FAQ

3 separate reviews, the first in June 2026 and the most recent in September 2026. Each one covers its own scope and commit, and is listed individually on this page.

95 findings across all 3 reviews: 6 high, 34 medium, 35 low and 20 informational. 61 of 95 were resolved or mitigated before the final reports.

Each report covers the code as it stood at the commit it names, the most recent in September 2026. Any code changed after that point is outside the reviewed scope.