Testnet — this app is connected to Hedera testnet

For issues or feedback, join our Discord →

Security

Lynx Smart Contract Security Review

Hardened direct-deploy ERC-4626 stack on Hedera mainnet (June 2026 redeploy). Reviewed against ERC-4626 exploit classes, Slither, Aderyn, Echidna, Halmos, and 38 automated regression tests.

Reviewed by: Slither · Aderyn · Echidna · Halmos · ERC-4626 exploit tests · unit/fuzzReport date: June 2026Network: Hedera Hashgraph
Hedera HashgraphHardened v4 MainnetERC-4626Offset 6 Inflation Defense
100%
triaged
Hardened mainnet deploy

38/38 tests passing · ERC-4626 threats mitigated · vault + bridge paused until migration verified

Critical
0
None found
High
2
Accepted: 2
Medium
19
False positive: 19
Low
18
Accepted / ignored: 18
Informational
1
Resolved: 1

Security summary

  • Direct-deploy ERC-4626 vault (no proxy). Asset: WHBAR. Cost-basis NAV (no price oracle).
  • ERC-4626 inflation defense: OpenZeppelin 5.x virtual shares with _decimalsOffset() = 6 (14-decimal shares).
  • HBAR entry slippage: depositHbar(receiver, minSharesOut) and depositHbarAndWrap(minSharesOut); minSharesOut = 0 skips check (migration only).
  • Profit streaming: realized gains locked 7 days; losses immediate. Harvest sandwich mitigated.
  • Redeem buffer: maxRedeem / maxWithdraw capped to idle WHBAR only.
  • Pause: vault + bridge inflows pausable (guardian or owner); redeem/unwrap always open. Mainnet: paused until migration verified.
  • Bridge invariant: LYNX.totalSupply() == vault.balanceOf(bridge) (unit + fuzz + Echidna).
  • Legacy burn-reward / transferHbarTo drain path removed (not present on v4 stack).
  • 38 automated tests + property fuzz + Halmos + static analysis (triaged).

ERC-4626 exploit review

Tested against known ERC-4626 attack classes

LynxVault inherits OpenZeppelin 5.x ERC-4626 mitigations and adds Lynx-specific controls. Each class below was reviewed with dedicated unit tests, property fuzz, and/or Echidna where applicable. The legacy signed reward drain path does not exist on the v4 stack.

Inflation / donation (first depositor)

Mitigations: _decimalsOffset = 6, cost-basis NAV, migration deposit while vault is paused.

Test evidence

  • first-depositor inflationtest/LynxVault.test.ts
    Pass
  • Property fuzztest/fuzz/vault-bridge.fuzz.test.ts
    Pass
  • Echidna echidna_backingEchidnaLynxHarness.sol
    Pass

Lending oracle abuse

Mitigations: Not in scope in-contract. Do not use raw convertToAssets() as a lending collateral price without extra guards.

Test evidence

  • In-contract lending
    N/A

Fake yield / inflated NAV

Mitigations: Keeper-only reportGain; lockedProfit streaming over profitUnlockPeriod.

Test evidence

  • Profit streaming + manager gaintest/LynxVault.test.ts
    Pass

Bridge 1:1 semantics

Mitigations: 1 LYNX base unit = 1 vault share base unit escrowed in bridge (not 1 HBAR per deposit).

Test evidence

  • Wrap/unwrap backingtest/LynxHTSBridge.test.ts
    Pass

HBAR / bridge deposit slippage

Mitigations: depositHbar(receiver, minSharesOut) and depositHbarAndWrap(minSharesOut) revert SharesSlippage when floor not met.

Test evidence

  • depositHbar slippagetest/LynxVault.test.ts
    Pass
  • depositHbarAndWrap slippagetest/LynxHTSBridge.test.ts
    Pass
  • decimals offsettest/LynxVault.test.ts
    Pass

Legacy vs v4: The historical proxy vault allowed backend-signed rewards and transferHbarTo drains without burning shares. The new ERC-4626 stack removed that path entirely. Share-price manipulation is mitigated via virtual offset 6 plus on-chain minSharesOut on HBAR deposit paths.

Threat status

Known exploit classes reviewed for the hardened mainnet v4 stack. Status reflects code mitigations, automated tests, and current operational state.

Threat classApplicableMitigationTestedStatus
Legacy signed reward drainNo on v4Path removedN/ANot applicable
ERC-4626 inflation / donationYesOZ virtual shares + offset 6Unit + fuzzMitigated
First depositor on empty vaultYesOffset + paused until migrationUnit + opsPaused
Fake yield / instant redeemPartialLocked profit + manager-only gainUnitMitigated
Lending oracle (Venus-style)External onlyNo in-contract lendingN/APolicy: no raw oracle
Bridge backing shortfallYesOps: fund bridge before unpauseFuzz + EchidnaPending migration
HBAR deposit slippageYesminSharesOut on HBAR/bridge pathsUnitDeployed
Governance / keeper trustYesOperationalPartialTrusted roles

Security layers

Layer 1 inherits OpenZeppelin ERC-4626 defenses. Layer 2 adds Lynx-specific economic and operational controls. June 2026 redeploy required new bytecode (contracts are not upgradeable).

Layer 1 — OpenZeppelin ERC-4626

ItemImplementation
Virtual shares
Industry-standard post–v4.9 mitigation
shares = assets × (supply + 10^offset) / (assets + 1)
_decimalsOffset()
Donation/inflation attacks orders of magnitude more expensive than offset = 0
Constant 6 in LynxVault.sol
Share decimals()
Display only; bridge 1:1 is raw base units (share wei ↔ LYNX wei)
8 (WHBAR) + 6 = 14
Standard deposit()
Off-chain previewDeposit + UI slippage for WHBAR ERC-20 path
Unmodified IERC-4626 signature

June 2026 hardened redeploy

ChangeBefore → After
_decimalsOffset()0 (OZ default)6
HBAR slippagedepositHbar(receiver) onlydepositHbar(receiver, minSharesOut)
Bridge mintdepositHbarAndWrap()depositHbarAndWrap(minSharesOut)
Post-deploy pauseManual (ops:mainnet:pause-stack)Automatic in deploy scripts
Contract IDs10509714 / 10509719 / 10509723 / 1050974910510134 / 10510137 / 10510139 / 10510145

Layer 2 — Lynx-specific controls

ControlWhat it doesRisk reducedEvidence
Cost-basis totalAssets()idle + deployedPrincipal - lockedProfit; no mark-to-marketOracle / manipulated NAVUnit + fuzz I2
lockedProfit + 7d unlockGains stream in; not instantly withdrawableHarvest sandwich, fake yield redeemLynxVault.test.ts
Buffer maxRedeemRedeem ≤ idle WHBAR onlyForced LP unwind in user txLynxVault.test.ts
reportGain / reportLossStrategyManager onlyPublic NAV inflationUnit + Halmos
No transferHbarTo / rewardsRemoved vs legacy architectureHistorical ~25k HBAR drain classNot in codebase
depositHbar minSharesOutRevert SharesSlippage if minted shares < floorDonation before HBAR mintdepositHbar slippage tests
depositHbarAndWrapForwards slippage check to vaultBad bridge mint UXLynxHTSBridge.test.ts
Pausable inflowsBlocks deposit/mint/wrap; not redeem/unwrapEmpty vault incident windowHardening.test.ts
Auto-pause on deploypause() after vault + bridge createPost-deploy exposureDeploy scripts
Ownable2Step + guardianTwo-step ownership; optional fast pauseGovernance hijack / incident responseHardening.test.ts
Exit fee capMAX_EXIT_FEE_BPS = 10%Fee governance mistakeHalmos
HtsOps._toInt64Revert on HTS amount overflowSilent truncationHardening.test.ts
Strategy manager CEIPrincipal before external calls on pushReentrancy accounting driftCode review + unit
setStrategyManager guardNo swap with outstanding principalNAV desyncHardening.test.ts
LP position capMAX_ACTIVE_LP_POSITIONS = 32Unbounded harvest gasSaucerSwapLiquidityStrategy.sol
Bridge backing1 LYNX wei = 1 share wei escrowedUnder-backed unwrapUnit + fuzz + Echidna

Mainnet contracts (current)

  • LynxVault
    0.0.10510134
    0x0000000000000000000000000000000000a05f36
  • StrategyManager
    0.0.10510137
    0x0000000000000000000000000000000000a05f39
  • LynxHTSBridge
    0.0.10510139
    0x0000000000000000000000000000000000a05f3b
  • SaucerSwapLiquidityStrategy
    0.0.10510145
    0x0000000000000000000000000000000000a05f41
  • LYNX (HTS, reused)
    0.0.9633601
    Supply key on legacy proxy until TokenUpdate

Mainnet operational state

  • Vault paused()Yes (auto at deploy)
  • Bridge paused()Yes
  • Vault totalSupply0 (pre-migration)
  • LYNX supply keyLegacy proxy 0.0.9766114 until cutover
  • Bridge lynxTokenSet to 0.0.9633601

Before public use: migrate backing → verify backingShares → TokenUpdate → unpause. Vault pause blocks all inflows including direct depositHbar.

Integrator / frontend requirements

  1. Mint / wrap: call previewDeposit(hbarAmount) then depositHbarAndWrap(minShares) with tolerance (e.g. 99.5% of preview). Use 0 only for trusted migration scripts.
  2. Display: 1 LYNX = 1 vault share base unit, not 1 HBAR per deposit.
  3. Do not use convertToAssets() as a lending collateral price without extra guards.
  4. Redeem: respect maxWithdraw / buffer; large exits need keeper pull from strategies.

Findings by Severity

Critical
0
High
2
Medium
19
Low
18
Info
1
Total Slither findings40
Triaged / no open exploits40 / 40
Aderyn themes (High / Low)2 / 8

Security Checklist

Compile (Cancun + viaIR)
Hardhat 3, solc 0.8.28
Passed
Unit + hardening tests
38 / 38 Mocha tests
Passed
Hardhat property fuzz
2,000 ops, seed 42
Passed
Echidna invariant fuzz
echidna_backing @ testLimit 50,000
Passed
Formal verification (Halmos)
3 / 3 symbolic checks
Passed
Static analysis (Slither + Aderyn)
Reports generated; all findings triaged
Passed

Core Invariants

I1
Bridge backing (1:1)LYNX.totalSupply() == vault.balanceOf(bridge) — unit, fuzz, Echidna
I2
Cost-basis NAVtotalAssets() == idle + deployedPrincipal - lockedProfit — unit + fuzz
I3
Buffer-capped redemptionmaxRedeem / maxWithdraw capped to idle WHBAR — unit tests
I4
Strategy capital accountingPrincipal/cap/PnL consistent on push/pull/harvest — unit tests
I5
Access controlManager-only vault mutations; fee ceiling; Ownable2Step — unit + Halmos
I6
HTS amount safetyAmountExceedsInt64 guard prevents int64 truncation — hardening tests
I7
Emergency pause (inflows only)Deposits/wrap pausable; redeem/unwrap stay open — hardening tests
I8
Strategy manager migration safetysetStrategyManager rejects zero address and non-zero principal swap — hardening tests
I9
ERC-4626 inflation + HBAR slippage_decimalsOffset = 6; depositHbar/depositHbarAndWrap minSharesOut — first-depositor inflation, slippage, decimals offset tests

Audit Scope

ContractScope
LynxVault.sol
ERC-4626 vault (WHBAR); cost-basis NAV; profit streaming; exit fee
Production
LynxHTSBridge.sol
1:1 wrap of vault shares ↔ HTS LYNX; bridge is treasury + supply key
Production
StrategyManager.sol
Allocates vault WHBAR to strategies; cost-basis principal accounting
Production
SaucerSwapLiquidityStrategy.sol
Production LP strategy (SaucerSwap V1/V2); admin-driven position ops
Production
libraries/HtsOps.sol
HTS precompile (0x167) wrapper with uniform revert on failure
Production
Total nSLOC reviewed1,015

Out of scope

  • · mocks/ — test doubles only
  • · HarnessStrategy.sol — testnet scripting, not mainnet LP
  • · echidna/EchidnaLynxHarness.sol — fuzz harness
  • · HederaResponseCodes.sol — vendored Hedera constants

Seven-Layer Review Stack

1
CompileHardhat 3, solc 0.8.28, Cancun, viaIR
2
Unit + hardening regressionMocha — 38 tests (incl. ERC-4626 exploit suite)
3
Hardhat property fuzz2,000 ops, seed 42
4
Echidna (Trail of Bits)echidna_backing invariant @ 50k limit
5
Halmos3 symbolic checks (fee ceiling, reportGain, setExitFee)
6
Slither (Trail of Bits)Static analysis, 100+ detectors
7
Aderyn (Cyfrin)AST-based static analysis
CI Pipeline (GitHub Actions)All green

Wake is intentionally not used (scope control; Slither + Aderyn + Echidna + Halmos suffice).

Hardening Applied

Real mitigations implemented in code — not triage waivers.

  • _decimalsOffset() = 6 — OpenZeppelin ERC-4626 virtual-share inflation defense (June 2026 redeploy)
  • depositHbar(receiver, minSharesOut) + depositHbarAndWrap(minSharesOut) — on-chain HBAR slippage
  • Auto-pause on vault + bridge deploy (DEPLOY_SKIP_AUTO_PAUSE for testnet smoke only)
  • Cost-basis NAV, locked profit streaming (7d), buffer-capped redeem
  • Ownable2Step on vault, bridge, manager, and LP strategy
  • Pausable on vault/bridge inflows; guardian + owner pause; redeem/unwrap never paused
  • No legacy transferHbarTo / signed reward drain path
  • HtsOps._toInt64 guard (AmountExceedsInt64) prevents silent HTS truncation
  • StrategyManager.push uses CEI — principal booked before external calls
  • setStrategyManager rejects zero address; requires zero outstanding principal to swap
  • SaucerSwapLiquidityStrategy: MAX_ACTIVE_LP_POSITIONS = 32; bounded harvest loop

Accepted Residual Behavior

Centralized governance

Owner/keeper can change fees, strategies, caps, pause inflows, and move capital. Accepted under Ownable2Step with expected multisig governance.

Accepted by design

Why this is safe

  • Production governance is expected to be a multisig; keeper and governance must be honest and competent.
  • Optional guardian enables fast pause; exit/redemption paths stay open when inflows are paused.
  • MAX_EXIT_FEE_BPS ceiling, strategy registration checks, and per-strategy caps limit parameter risk.

Impact by role

Depositors
Trust governance for fee/cap/strategy changes; exits remain available when inflows paused
Keeper
Authorized to push/pull/harvest; nonReentrant and CEI on push reduce reentrancy surface
Governance
Ownable2Step two-step transfer; guardian can be cleared via setGuardian(0)

Accepted Residual Behavior

Cost-basis NAV and profit streaming

block.timestamp used in lockedProfit() and totalAssets(). Accepted — Yearn-style linear unlock prevents harvest sandwiching.

Accepted by design

Why this is safe

  • NAV uses cost basis (no oracle). Realized gains stream over profitUnlockPeriod; losses apply immediately.
  • Timestamp manipulation only shifts unlock speed within miner skew bounds (~10–15s on Hedera).

Impact by role

Depositors
Cannot front-run harvests; unlock period is a governance-set economic parameter
Strategies
Realized PnL reported by keeper; principal accounting enforced in unit tests
Protocol
No oracle dependency; cost-basis model is deliberate product choice

Accepted Residual Behavior

Payable contracts / native HBAR

LynxVault, LynxHTSBridge, and SaucerSwapLiquidityStrategy accept native value without a generic withdraw() sweep.

Accepted by design

Why this is safe

  • Vault and bridge must receive HBAR for depositHbar / depositHbarAndWrap.
  • LP strategy holds HBAR for SaucerSwap V2 NFT mint/increase fees (mintFeeHbar).
  • Residual dust is an operational concern; governance can add a sweep if needed — not a user-fund lock.

Impact by role

Users
HBAR deposits work as designed; funds are not locked by missing sweep
LP strategy
Native HBAR required for SaucerSwap V2 fee payments
Governance
Optional future sweep for operational dust cleanup

Accepted Residual Behavior

Buffer-capped instant redemption

Users redeem only from idle WHBAR in the vault — not forced strategy unwind in the user transaction.

Accepted by design

Why this is safe

  • Instant liquidity comes from the redemption buffer; keeper/governance replenishes via pull.
  • Users needing full withdrawal may wait for buffer refill or redeem the available portion.

Impact by role

Depositors
Instant exit up to idle buffer; larger exits require keeper pull
Keeper
Responsible for replenishing buffer from strategies
Strategies
No forced unwind on user redeem — capital pulled asynchronously

Accepted Residual Behavior

Trusted strategies and HTS precompile

Registered IStrategy implementations receive WHBAR; all LYNX/WHBAR movement depends on HTS at 0x167.

Accepted by design

Why this is safe

  • Only governance registers strategies. Production strategy is SaucerSwapLiquidityStrategy.
  • HtsOps wraps the precompile and reverts on non-success codes; AmountExceedsInt64 guard prevents truncation.
  • Mainnet assumes Hedera HTS behaves as documented.

Impact by role

Depositors
Strategy risk gated by governance registration; bridge 1:1 backing enforced in fuzz
Bridge
HTS mint/burn/transfer via HtsOps with uniform revert semantics
Platform
HTS precompile correctness is a Hedera platform assumption

Findings & Disposition

Slither and Aderyn flag code patterns that may indicate bugs. After human review, each alert is assigned a disposition. “Accepted” does not mean an open vulnerability — it means the pattern is intentional design or an acceptable tradeoff with no exploitable path for users.

Accepted

The tool flagged something real, but it is intentional design or an acceptable tradeoff — not an exploitable bug.

False positive

The tool misread the code (common with HTS precompile wrappers and Solidity overrides).

Resolved

We changed the code to address the finding.

Ignored

Style or readability nit with no demonstrated security impact.

IDFindingSeverityDisposition
H-01
Slither + Aderyn
SaucerSwap LP admin reentrancy ordering
createLPPosition / increaseLPPosition update storage after external SaucerSwap calls. Slither reentrancy-eth; Aderyn H-2.
Why accepted

These functions are onlyOwner and nonReentrant. The external caller is the configured SaucerSwap NFT manager — a trusted protocol contract, not arbitrary users. There is no practical reentrancy attack path for depositors; reordering to strict checks-effects-interactions would be a style improvement, not a security fix.

SaucerSwapLiquidityStrategy.sol
highAccepted
H-02
Aderyn
Payable contracts lock native HBAR
LynxVault, LynxHTSBridge, and SaucerSwapLiquidityStrategy accept native value without a generic withdraw() sweep. Aderyn H-1.
Why accepted

The vault and bridge must receive HBAR for depositHbar and depositHbarAndWrap. The LP strategy holds HBAR for SaucerSwap V2 NFT mint/increase fees. User funds are not trapped — the missing sweep only affects residual operational dust, which governance can recover later if needed.

LynxVault.sol · LynxHTSBridge.sol · SaucerSwapLiquidityStrategy.sol
highAccepted
M-01
Slither
HTS return values ignored at call sites
Slither unused-return on HtsOps.mint, transfer, associate, approve.
Why false positive

HtsOps checks HederaResponseCodes.SUCCESS internally and reverts with HTSCallFailed on failure. Callers do not need to re-check the int64 return value — the library already enforces success or revert.

libraries/HtsOps.sol
mediumFalse positive
M-02
Slither
Cost-basis NAV uses block.timestamp
block.timestamp used in lockedProfit(), totalAssets(), setProfitUnlockPeriod.
Why accepted

Profit streaming (Yearn-style linear unlock) deliberately uses timestamps so depositors cannot sandwich harvests. On Hedera, consensus timestamps have narrow skew (~10–15s), which is negligible for hour/day unlock periods set by governance.

LynxVault.sol
mediumAccepted
M-03
Aderyn
StrategyManager pull/harvest interaction order
State updates after withdraw() / harvest() external calls.
Why accepted

The manager must observe strategy return values before adjusting principal or reporting PnL — reversing the order would break accounting. Access is keeper-only with nonReentrant; push was reordered to CEI, but pull/harvest order is intentional.

StrategyManager.sol
mediumAccepted
M-04
Aderyn
Centralized governance
Owner/keeper can change fees, strategies, caps, pause inflows, and move capital.
Why accepted

This is the protocol trust model, not a code bug. Mitigations include Ownable2Step (two-step ownership transfer), optional guardian for fast pause, exit/redemption paths that stay open when inflows are paused, MAX_EXIT_FEE_BPS ceiling, and per-strategy caps. Production governance is expected to be a multisig.

Core contracts
mediumAccepted
L-01
Slither
configure() allows zero addresses
SaucerSwapLiquidityStrategy.configure allows zero addresses for SaucerSwap dependencies.
Why accepted

Zero means “not configured yet.” All mutating LP functions revert with NotConfigured() until the admin sets real SaucerSwap router and NFT manager addresses. This is an intentional two-phase setup pattern.

SaucerSwapLiquidityStrategy.configure()
lowAccepted
L-02
Slither
setGuardian(address(0)) allowed
Guardian can be set to the zero address.
Why accepted

Clearing the guardian is intentional — it disables guardian-only pause so only the owner can pause. Documented in NatSpec. Useful when rotating or removing the guardian role without transferring ownership.

LynxVault.sol · LynxHTSBridge.sol
lowAccepted
L-03
Aderyn
Unsafe ERC20 on HTS paths
HtsOps.transfer / approve flagged as unsafe ERC20 by Aderyn.
Why false positive

These are Hedera HTS precompile calls at 0x167, not standard ERC20 transfer/approve with boolean return values. Aderyn’s ERC20 checker does not apply to HTS semantics.

libraries/HtsOps.sol
lowFalse positive
L-04
Aderyn
Cancun / PUSH0 bytecode
Bytecode may include PUSH0 (Cancun opcode).
Why accepted

evmVersion: cancun is pinned in Hardhat and Foundry profiles to match Hedera mainnet (Besu EVM, release 0.50.0+). PUSH0 is expected and required for Cancun-targeted deployments.

All production contracts
lowAccepted
L-05
Slither
LynxVault asset() unimplemented
Slither claims ILynxVault.asset() is not implemented.
Why false positive

asset() is implemented via override resolving both ERC4626 and ILynxVault in LynxVault.sol. Slither’s inheritance analysis missed the dual-interface override.

LynxVault.sol
lowFalse positive
L-06
Aderyn
Large numeric literal BASIS = 10_000
Aderyn L-2 flags the BASIS constant as a large numeric literal.
Why ignored

10_000 is the standard basis-points denominator (100.00%). Named constant improves readability; no security impact.

Core contracts
lowIgnored
L-07
Aderyn
nonReentrant not first modifier
Aderyn L-3: nonReentrant is not the first modifier on some LP admin functions.
Why ignored

Modifier ordering on owner-only admin functions has no demonstrated security impact. nonReentrant is still applied; reordering would be cosmetic.

SaucerSwapLiquidityStrategy.sol
lowIgnored
L-08
Aderyn
Unspecific pragma ^0.8.20
Aderyn L-8: broad ^0.8.20 pragma on production contracts.
Why ignored

CI compiles all contracts with solc 0.8.28. The broad pragma allows compatibility; the pinned compiler version in CI is what matters for deployment.

Production contracts
lowIgnored
L-09
Slither
Reentrancy-benign on StrategyManager
Slither reentrancy-benign on manager push/pull/harvest paths.
Why ignored

Paths are access-controlled (keeper or owner only). push was hardened with checks-effects-interactions (principal booked before external calls). No user-facing reentrancy surface.

StrategyManager.sol
lowIgnored
I-01
Slither
Solc version warning on HtsOps
Broad ^0.8.0 pragma triggered Slither’s historical solc bug list.
Why resolved

Pragma updated to ^0.8.28 aligned with the rest of the stack. CI compiles with solc 0.8.28 — the warning is resolved in the current codebase.

libraries/HtsOps.sol
infoResolved

Notable disposition rows from Slither and Aderyn triage. Slither snapshot: 40 findings (2 High, 19 Medium, 18 Low, 1 Informational) — all triaged. Aderyn: 2 High themes, 8 Low themes (~1015 nSLOC). Wake not used. No open exploits from tooling.