███████╗ █████╗ ██████╗ ███╗ ██╗ ██████╗ ██████╗ ██╗██████╗ ██╔════╝██╔══██╗██╔══██╗████╗ ██║██╔════╝ ██╔══██╗██║██╔══██╗ █████╗ ███████║██████╔╝██╔██╗ ██║██║ ███╗██████╔╝██║██║ ██║ ██╔══╝ ██╔══██║██╔══██╗██║╚██╗██║██║ ██║██╔══██╗██║██║ ██║ ███████╗██║ ██║██║ ██║██║ ╚████║╚██████╔╝██║ ██║██║██████╔╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝╚═════╝

SECURITY AUDIT

BlendedVault v1.0.7 · Base Mainnet

Date: 2026-08-13 Vault: 0x8694…233dE SwapAndEarn: 0x7025…93cD Chain: Base (8453) Auditor: 🤖
Sourcify Verified ERC-4626 Compliant Reentrancy Protected Invariant Tested Timelock Governance Automated Audit

Scope. v1.0.7 is a maintenance release — no changes to the smart contracts. The vault runs the same audited, Sourcify-verified bytecode as v1.0.4 (exact_match). One governance write: the deposit queue was reordered to the allocator's risk-adjusted ranking, and the allocator now keeps it in sync every tick. Static-analysis reviewed (APPROVE), fork-tested on a Base fork (5/5).

📊

BY THE NUMBERS

233 Unit Tests (CI)
5 Fork Tests
8 Invariants
819,200 Invariant Calls
1,486 Solidity Lines
24,529 Contract Bytes
99.81% EIP-170 Used
18 Test Files
42+ Findings Resolved
🔧

v1.0.7 — FIXES WAVE + SEO

Change What it does
Allocator — measured withdrawals (E-04) The deposit leg is sized against the amount the withdrawal leg actually delivered (read from the Rebalanced receipt), not the amount requested. A strategy that under-delivers no longer strands capital or reverts the deposit.
Allocator — missing APY ≠ 0% (A13-01) A strategy the Morpho API failed to price is omitted from ranking (its position is preserved) instead of being quoted as 0% yield and withdrawn.
Allocator — queue deferral Strategies clamped by tier caps are deferred to the tail of the deposit queue rather than holding a queue slot they cannot fund.
Swap form — share floor (B-06) The swap form waits for the share preview before signing; it can no longer submit a deposit with minShares=0 while the quote is still loading. A rejected approval no longer fires the deposit either.
Indexer /api/allocations now reports unreadable strategies instead of dropping them silently; proof uploads pin their file extension to the allowlisted type with nosniff/CSP headers.
Scripts Daily-report metrics (rebalances, harvests, TVL fallback) now read live data instead of structurally zero; harvest.sh fee parsing fixed.
SEO & docs Per-route canonicals, llms.txt, GA4, IndexNow, Search Console, one-command web deploy, REFERALS.md, and a full-stack audit pass.
🔄

v1.0.6 — DEPOSIT-QUEUE AUTO-SYNC

Change What it does
Allocator feature The allocator now rewrites the vault's deposit queue to its risk-adjusted ranking every tick. New deposits land in the top-paying strategy in the deposit transaction itself — previously the queue was frozen at bootstrap tier order, so deposits parked in Prime (4.12%) while 99.5% of TVL sat in Frontier (5.93%).
Governance write One onchain setDepositQueue (tx 0x8d97db…2bf, 75,271 gas): [Frontier, HY, Prime, Moonwell, Steakhouse] = model ranking.
Fix 1 (automated review) desiredDepositQueue returned registration order when ALL strategies were frozen/unpriced — a market-wide freeze would have reverted deposit routing to bootstrap order (Prime first). Now proposes no queue; the last good order stays in force.
Fix 2 (automated review) Queue sync moved BEFORE the rebalance legs so a repeating TierLimitExceeded can't silently disable deposit routing.
Fork-tested DepositQueueFork.t.sol (5/5 pass against the live vault on a Base fork): deposit reaches the queue head in the same tx, reordering reroutes instantly, duplicates/unregistered entries revert InvalidQueueStrategy. Review verdict: APPROVE.
Test counts Contracts 233 (CI) + 5 fork · Allocator 129 (up from 74) · Indexer 130 · 492 total (497 with fork).
🏗️

ARCHITECTURE

BlendedVault — ERC-4626 Vault-of-Vaults

Users deposit USDC and receive shares. The vault allocates idle liquidity across a curated allowlist of ERC-4626 strategy vaults (MetaMorpho markets), subject to per-strategy caps and per-tier exposure limits. Five strategies are currently registered, spread across all three risk tiers.

architecture.sol
// Contract inheritance chain BlendedVault ├── ERC4626 // Standard vault interface │ └── ERC20 // Share token (12 decimals) ├── AccessControl // Role-based authorization │ ├── CURATOR // Strategy curation │ ├── ALLOCATOR // Rebalance execution │ └── GUARDIAN // Emergency response └── ReentrancyGuardTransient // EIP-1153 SwapAndEarn ├── Ownable2Step // Two-step ownership └── ReentrancyGuardTransient // EIP-1153
🛡️

SECURITY PRACTICES

Reentrancy Protection — EIP-1153 Transient Storage

Every state-changing entrypoint is guarded by ReentrancyGuardTransient using EIP-1153 transient storage (Cancun). External strategy calls sit inside the guard. Tests assert the re-entrant call failed, not just that something reverted.

Inflation Attack Resistance — 1e6 Virtual Shares

_decimalsOffset() = 6 gives 1,000,000 virtual shares against 1 virtual asset. A donation/first-depositor attack must burn ~1,000,000× what it can steal. Fuzz-tested: the attacker cannot profit and the victim keeps ≥99.9% of principal.

Fee Escape Prevention

Withdraw and redeem settle fees before pricing shares, so a holder cannot exit ahead of harvest() and dodge their share of performance fees. The high-water mark records the post-dilution price, preventing double-charging.

Strategy Transfer Measurement

Deposits and withdrawals use the asset balance delta rather than trusting a strategy to move exactly the requested amount. Unconsumed approvals are revoked immediately. A lossy strategy cannot corrupt vault accounting.

Bounded Loops & Governance Safety

MAX_STRATEGIES = 16 caps every accounting pass. Scheduled changes expire after 30 days so a forgotten or pre-planted change cannot execute months later. MAX_FEE_BPS = 2,000 (20%) is a hard, non-governable ceiling.

🔑

ACCESS CONTROL

Check Status Detail
Role separation in code 4 distinct roles: Admin, Curator, Allocator, Guardian
Two-step admin transfer beginAdminTransferacceptAdminTransfer
Admin grant/revoke blocked Only via two-step path; direct grantRole reverts
Timelock for risk-increasing changes New strategies, cap increases, parameter changes
Scheduled-change expiry 30-day grace period after becoming executable
Risk-reducing changes Immediate: lower caps, pause, increase cooldowns
📐

ERC-4626 COMPLIANCE

Requirement Status Notes
asset, totalAssets, convertToX, previewX previewX asserted equal to actual in fuzz tests
maxDeposit / maxMint Returns 0 when paused
maxWithdraw / maxRedeem Capped at reachable liquidity, and fee-aware as of v1.0.3 (D-01)
Rounding direction Always favours the vault; fuzz-tested round-trip never profits
First-deposit attack 1e6 virtual shares + minInitialDeposit
mint mints exactly shares Tested
Slippage protection deposit(…, minShares), redeem(…, minAssets)
🧪

TEST COVERAGE

8 invariants exercised across 819,200 calls (1,024 runs × 100 depth):

Invariant Asserts
totalAssetsEqualsIdlePlusStrategies Accounting identity holds exactly
totalAssetsCoversIdle No assets conjured from idle cash
shareSupplyIsFullyAttributed Every share belongs to a known holder
solvency All holders redeeming at once cannot exceed totalAssets
strategyListIndexIntegrity listIndex stays in sync with strategyList
liquidityWithinTotalAssets Never advertise more liquidity than owned
queuesOnlyContainRegisteredStrategies No stale queue entries
emptyVaultPricesAtPar HWM resets when fully drained
test-suite.sh
$ forge test --summary # 233 tests CI + 5 fork tests (DepositQueueFork, live vault on a Base fork) # BlendedVault.t.sol — core deposit/withdraw/mint/redeem # BlendedVaultFuzz.t.sol — fuzz testing # BlendedVaultInvariants.t.sol — 819,200 invariant calls # BlendedVaultLifecycle.t.sol — strategy lifecycle # BlendedVaultTimelock.t.sol — governance timelock # BlendedVaultFees.t.sol — HWM, dilution, fee math # BlendedVaultGas.t.sol — gas benchmarks # BlendedVaultReentrancy.t.sol — attack scenario tests # BlendedVaultAuditRegressions.t.sol — regression suite # SwapAndEarn.t.sol — peripheral router # DepositQueueFork.t.sol — deposit-queue routing vs live vault (v1.0.6) All tests passed # +129 allocator tests, +130 indexer tests = 492 total (497 with fork)
📦

DEPENDENCIES

Package Version Notes
OpenZeppelin Contracts 5.5.0 Industry standard, audited
Solidity 0.8.28 evm_version = cancun for EIP-1153
Node.js 26.5.0 Runtime
viem 2.55.0 TypeScript Ethereum client
Next.js 16.2.12 Web frontend
📡

MONITORING & DEFENSE

🔍

VERIFY YOURSELF

verify.sh
# Check the onchain version string $ cast call 0x8694D7D44309665D51Cb5002fceC0454f1c233dE \ "VERSION()(string)" --rpc-url https://base-rpc.publicnode.com "1.0.4" ← bytecode unchanged in v1.0.6 (off-chain release) # Verify source on Sourcify (exact match, creation + runtime) $ curl -s "https://sourcify.dev/server/v2/contract/8453/\ 0x8694D7D44309665D51Cb5002fceC0454f1c233dE?fields=compilation" \ | jq '{match, creationMatch, runtimeMatch}' { "match": "exact_match", "creationMatch": "exact_match", "runtimeMatch": "exact_match" } # Check total assets $ cast call 0x8694D7D44309665D51Cb5002fceC0454f1c233dE \ "totalAssets()(uint256)" --rpc-url https://base-rpc.publicnode.com # Read the assets-per-share price index $ cast call 0x8694D7D44309665D51Cb5002fceC0454f1c233dE \ "assetsPerShare()(uint256)" --rpc-url https://base-rpc.publicnode.com