VimenDocs
Security & Risk

Audits & analysis

Test coverage, fuzzing, invariants, and the static-analysis triage.

No professional audit yet. That is stated plainly wherever it matters, and Phase 3 of the cap roadmap is gated on one. Here is what stands in the meantime, all reproducible from the public repo.

Test suite

107 tests, 100% line/statement/branch/function coverage on all five platform contracts. The suite is layered:

LayerWhat it proves
Unit testsEvery function, every custom error, constructor validation ladder
Fuzz testsRoundtrip bound: redeem(mint(x)) returns each constituent within [required − nTokens wei, required]; users never profit from rounding; reward-dust bounds in the registry
Invariant testsFull backing after any call sequence: balanceOf(vault) ≥ totalSupply × units[i] / 1e18 for all constituents
Attack mocksReentrancy on transfer hooks (all four entry combinations), fee-on-transfer / lying tokens rejected by balance-delta checks
Dedicated guaranteesRedeem succeeds while minting is paused and supply is above cap; guardian can do exactly three things and nothing else
Mainnet fork testsFull mint→transfer→redeem cycles against the real Stock Token contracts; zap quote == execution in the same block against live Uniswap v4 pools

Run them yourself:

cd contracts && forge test
# fork suites against Robinhood Chain mainnet (self-skip when RH_RPC is unset)
RH_RPC=https://rpc.mainnet.chain.robinhood.com forge test --match-contract Fork

Static analysis: full triage

Zero unexplained findings is the policy: every tool finding is either fixed or documented with its rationale.

Slither

Zero findings of any severity in BasketToken.sol except:

  • calls-loop (Low): mint/redeem/isFullyBacked call constituent ERC-20s in a loop. Accepted: inherent to an in-kind multi-token vault; the set is bounded (2–20) and fixed at deploy.
  • cyclomatic-complexity (Informational): the constructor validation ladder. Accepted: a sequence of independent input checks.
  • Reported High (incorrect-exp) and Medium (divide-before-multiply) sit inside OpenZeppelin's Math.mulDiv. These are well-known false positives on the Remco Bloemen implementation (the ^ is an intentional XOR in the Newton–Raphson inverse). Unmodified, audited library code.

Aderyn

2 High, both triaged as false positives / intentional:

  • "State change after external call" (factory/registry): the external calls are to protocol-owned contracts wired at deployment, or the deliberate balance-delta pattern; every flagged function is nonReentrant. Accepted.
  • "Unsafe int cast" (IUniswapV4.sol): truncating to the low 128 bits is the defined decoding of Uniswap v4's BalanceDelta (two int128s packed in an int256). Accepted.

Low findings (require-in-loop, 10_000 bps literals, missing events on internal accounting, and similar) are accepted with the same rationales; the full report ships in the repo (contracts/aderyn-report.md).

Known spec deviations

One, documented and owner-approved: the constructor takes an extra initialSupplyCap parameter (validated 0 < initialSupplyCap ≤ maxSupplyCap) instead of the deploy script calling setSupplyCap post-deploy; that call is guardian-gated and the guardian is a Safe, not the deploy key. Setting it in the constructor removes a transaction and the window where the cap is unset.

Build note: via_ir = true is enabled because the 9-argument BasketToken constructor call in BasketFactory exceeds legacy-codegen stack depth. The full suite passes under via-IR.

Continuous checks

CI runs forge fmt --check, the full test suite, frontend lint/typecheck/build and scripts typecheck on every push. Contract source is verified on Blockscout for every deployment.

On this page