Security & Audit Protocol
Two independent static analyzers, a full forge test-suite on a live BSC mainnet fork, a full manual deep-review on every contract, and a symbolic-verification pass with Halmos. All findings curated; raw reports linked below. Audit date 14 Jun 2026.
Two independent scanners
Aderyn (Cyfrin) and Slither (Trail of Bits) ran to completion against the full contract set. Every High finding was manually triaged against the actual code path.
nonReentrant + trusted-role modifiers. Manually verified per
callsite.
incorrect-exp (same Uniswap caret) + 6× arbitrary-send-erc20
— every callsite is the canonical ERC-4626 strategy / venue-controller pattern
guarded by onlyVault / onlyController + nonReentrant.
91 Mediums: ~33 divide-before-multiply in Uniswap math libs, 27 unused-return
on _grantRole, 19 incorrect-equality stylistic. All triaged.
Forge suite on live BSC fork
25 test suites · 333 concrete tests · 13 fork tests against live BSC contracts
(Beefy CLM strategy 0xD9ee…eCE7, Pancake V3 NFPM, Masterchef V3) via Ankr archive.
Suite Result
332 pass 0 fail 1 skip
Ran 25 test suites in 48.37s (184.54s CPU time).
The single skip is test_StakedPositionReadable — requires a historical fork
block where the venue holds a staked NFT; logic is independently proven via mock-test
VaultBVenueReadLogic.t.sol.
Coverage
64.72% lines 78.42% functions 26.61% branches
Per-file branch coverage on partner contracts is the known gap:
PartnerAttributedSplitter 6.7%, WrapperFactory 0%.
BeefyCLMAdapter + DedicatedVaultMain excluded by --ir-minimum tooling limit
(require via_ir for stack-depth). Pre-mainnet TODO: invariant tests
(invariant_*) — none in current suite.
Manual + symbolic verification
Static analyzers and concrete tests can't reason about adversarial multi-step attack sequences. We read every contract end-to-end and ran Halmos symbolic verification on the ERC-4626 vault to formally check inflation-attack safety.
Claude 4.7 Manual Deep Review
All 10 src/ contracts read end-to-end. Function-by-function trace of state transitions, MEV / sandwich / flash-loan exposure, trust-boundary checks, operational bricking risk, slippage / deadline consistency.
0 High confirmed 5 Medium 2 Retracted
M-4 (NAV TWAP) and an early P1 (notify reentrancy) both retracted on Stage 2 re-read after following cross-contract paths — kept in the protocol for honesty.
Halmos Symbolic Verification
96 symbolic paths explored over 1 h 03 min. Concrete counterexample found
for the ERC-4626 first-depositor inflation attack at the current
_decimalsOffset = 3.
Counterexample: attacker=101, donate=1e6,
victim=6 wei → victim receives 0 shares (integer-division floor).
Mitigation queued: raise offset to 8+ and add MIN_DEPOSIT. Bait cost
> reward at any sane deposit size, but proven.
Confirmed action items (after full triage)
| ID | Severity | Finding | Source |
|---|---|---|---|
| AI-M-1 | Medium | Tighten closePosition slippage: require BOTH mins nonzero (matches openPosition) | Claude deep review |
| AI-M-2 | Medium | Gate setBeefyClm / setRouter / setUnderlyingVault on active balance — currently swappable mid-position | Claude deep review |
| AI-M-5 | Medium | Add guardian-only force-skip-harvest fallback for Masterchef-broken case | Claude deep review |
| AI-M-6 | Medium | Apply Task 1.32 fee-crystallization to DeepYieldStrategyAdapter.panic() (mirror BeefyCLMAdapter pattern) | Claude deep review |
| AI-M-7 | Medium | Raise _decimalsOffset from 3 → 8 and/or add MIN_DEPOSIT — Halmos counterexample for inflation attack | Halmos symbolic |
| AI-L-1 | Low | Document closeIfStale zero-min MEV trade-off in user docs | Claude deep review |
| AI-L-2 | Low | Remove or clarify setTreasury single-arg semantics (silent reset of fee-sink flag) | Claude deep review |
| AI-L-4 | Low | Remove dead treasury state variable in DeepYieldVault (never read, ~22k gas waste) | Claude deep review |
| AI-L-5 | Low | Pass minShares to beefyClm.deposit() for V2 (Beefy calm-gate is current proxy) | Downgraded M3 |
| AI-T1-3 | Coverage | Partner contract branch coverage: PartnerAttributedSplitter 6.7%, WrapperFactory 0%, VaultFeesLib 20% | forge coverage |
| AI-T5 | Gap | Add forge invariant_* tests for ERC-4626 invariants — currently zero | Pre-mainnet TODO |
| ~AI-M-4 | RETRACTED | NAV TWAP-resistance concern — Router does use TWAP with circuit-breaker. False alarm, kept for honesty. | Stage 2 re-read |
Methodology & Scope
- Repo:
github.com/Deepyield-labs/deepyield-vault· public audit mirror - Scope: all 10 contracts in
src/· 4 282 lines · 2 497 nSLOC - Test suite: 25 files · 13 fork tests · 332/333 pass on Ankr BSC fork @ block
104 162 596 - Toolchain:
solc 0.8.24·foundry 1.6.0·via_ir=true· optimizer 200 runs - All mutators guarded by nonReentrant + role-gated (
MANAGER_ROLE/KEEPER_ROLE/ADMIN_ROLE) - Admin role = Gnosis Safe 4-of-7 · withdraw is never bot-signed (user → vault directly, manager has zero withdraw power, L1-enforced)
- Bot keys segregated · separate role for
harvest/rebalanceonly - Static + functional + manual + symbolic — full quadrant coverage
⚠ Caveats & Honest Gaps
- Branch coverage on partner contracts is the real gap (6.7%–0%). All findings traced manually; tests to-be-added pre-mainnet.
via_ir=truecompile mode required for stack-depth — excludes BeefyCLMAdapter + DedicatedVaultMain from automated coverage report. These were manually reviewed instead.- Mythril (symbolic) blocked by
via_irinput mode. Sherlock AI not run (requires org-level GitHub App install). - Halmos
check_DepositRedeem_NoFreeMoneyinconclusive — solver budget exhausted (1939s). Counts as "no bug proved but state-space too large", not as pass/fail. - Findings AI-M-1 through AI-M-7 are not yet patched in deployed contracts. Listed transparently here; PRs in flight.