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.
61 of 95 findings resolved before the final reports.
Reports
Newest first. Every report covers its own scope and commit.
Vault, Order Hook, Settlement, Pro Rata Vault & Settlement (Base) smart contract audit
0Critical2High12Medium16Low9Informational23 of 39 resolved
Scope
Source repositorygithub.com/lambdaplexlabs/lambdaplex-balanced-vaultComponents reviewed
14 modules · 24 files- AirdropDistributor.sol
- PLEXPairVault.sol
- PLEXBrokerRegistry.sol
- SupraRegistry.sol
- PLEXVaultHook.sol
- IAirdropDistributor.sol
- IHieroHook.sol
- IOrderFlowAllowance.sol
- IPLEXBrokerRegistry.sol
- IocFokWriteback.sol
- OrderType.sol
- PRBMathCommon.sol
- SafeERC20.sol
- PLEXPairVaultHedera.sol
All 39 findings
ID Title Severity Status 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.
Medium acknowledged 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_`.
Medium fixed 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`.
Medium fixed 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…
Medium acknowledged 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.
Medium acknowledged 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`.
Medium fixed 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.
Medium fixed 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.
Low fixed 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.
Low acknowledged 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…
Low fixed 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…
Low fixed 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.
Low acknowledged 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.
Low fixed 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`.
Low fixed 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.
Low fixed 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.
Low acknowledged 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.
Low acknowledged 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.
Low acknowledged 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.
Low acknowledged 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,…
Informational fixed 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.
Informational acknowledged 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).
Informational acknowledged 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.
Informational acknowledged 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…
Informational acknowledged 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…
Informational fixed 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).
Informational fixed 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.
Informational acknowledged 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.
High fixed 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.
High fixed 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.
Medium fixed 6.2.4Oracle Proof Replay Within Staleness Window The `LambdaplexOrderHook` contract verifies oracle proofs from Supra Oracle using the `_oracleFactor()` function.
Medium fixed 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.
Medium fixed 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.
Medium fixed 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.
Medium fixed 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.
Low fixed 6.2.16Unregistered Reward Token Can Block Claiming `claimAllRewards()` iterates through all registered reward tokens and calls `distributor.claimTo(...)` sequentially.
Low fixed 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.
Low fixed 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…
Low acknowledged 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.
Informational acknowledged Vault, Order Hook, Settlement & Pro Rata Vault smart contract audit
0Critical2High11Medium13Low8Informational22 of 34 resolved
Scope
Source repositorygithub.com/lambdaplexlabs/lambdaplex-balanced-vaultComponents reviewed
14 modules · 24 files- AirdropDistributor.sol
- PLEXPairVault.sol
- PLEXBrokerRegistry.sol
- SupraRegistry.sol
- PLEXVaultHook.sol
- IERC20.sol
- IHieroHook.sol
- IOrderFlowAllowance.sol
- IPLEXBrokerRegistry.sol
- IocFokWriteback.sol
- OrderType.sol
- PRBMathCommon.sol
- SafeERC20.sol
- PLEXPairVaultHedera.sol
All 34 findings
ID Title Severity Status 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.
Medium acknowledged 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.
Medium fixed 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_`.
Medium fixed 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`.
Medium fixed 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…
Medium acknowledged 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.
Medium acknowledged 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`.
Medium fixed 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.
Low fixed 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.
Low acknowledged 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…
Low fixed 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…
Low fixed 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.
Low fixed 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`.
Low fixed 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.
Low fixed 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.
Low acknowledged 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,…
Informational fixed 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.
Informational acknowledged 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).
Informational acknowledged 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.
Informational acknowledged 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…
Informational acknowledged 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…
Informational fixed 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).
Informational fixed 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.
High fixed 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.
High fixed 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.
Medium fixed 6.2.4Oracle Proof Replay Within Staleness Window The `LambdaplexOrderHook` contract verifies oracle proofs from Supra Oracle using the `_oracleFactor()` function.
Medium fixed 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.
Medium fixed 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.
Medium fixed 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.
Low fixed 6.2.15Unregistered Reward Token Can Block Claiming `claimAllRewards()` iterates through all registered reward tokens and calls `distributor.claimTo(...)` sequentially.
Low fixed 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.
Low fixed 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.
Low acknowledged 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…
Low acknowledged 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.
Informational acknowledged Vault, Order Hook & Settlement smart contract audit
0Critical2High11Medium6Low3Informational16 of 22 resolved
Scope
Source repositorygithub.com/lambdaplexlabs/lambdaplex-balanced-vaultComponents reviewed
14 modules · 18 files- AirdropDistributor.sol
- PLEXPairVault.sol
- PLEXBrokerRegistry.sol
- SupraRegistry.sol
- PLEXVaultHook.sol
- IHederaTokenService.sol
- IHieroHook.sol
- IOrderFlowAllowance.sol
- IPLEXBrokerRegistry.sol
- IocFokWriteback.sol
- OrderType.sol
- PRBMathCommon.sol
- SafeERC20.sol
- PLEXPairVaultHedera.sol
All 22 findings
ID Title Severity Status 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.
Medium acknowledged 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.
Medium fixed 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_`.
Medium fixed 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`.
Medium fixed 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…
Medium acknowledged 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.
Medium acknowledged 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.
Medium fixed 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.
Low fixed 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.
Low acknowledged 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…
Low fixed 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,…
Informational fixed 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.
Informational acknowledged 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).
Informational acknowledged 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.
High fixed 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.
High fixed 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.
Medium fixed 6.2.4Oracle Proof Replay Within Staleness Window The `LambdaplexOrderHook` contract verifies oracle proofs from Supra Oracle using the `_oracleFactor()` function.
Medium fixed 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.
Medium fixed 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.
Medium fixed 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.
Low fixed 6.2.15Unregistered Reward Token Can Block Claiming `claimAllRewards()` iterates through all registered reward tokens and calls `distributor.claimTo(...)` sequentially.
Low fixed 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.
Low fixed
Methodology
Review process
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.
Automated analysis
Static analysers sweep the full codebase to surface known weakness patterns, unreachable states and deviations from established conventions.
Manual code review
Independent engineers read the code line by line, concentrating on business logic, access control, arithmetic and the handling of external calls.
Adversarial testing
Property-based fuzzing and invariant tests attack the assumptions the protocol relies on, targeting states that ordinary unit tests never reach.
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.
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.