MUSD Savings Strategy (sMUSD)
Findings
The MUSD savings strategy (sMUSD vault + IdleStrategy + VaultGauge) is sound at the code layer — all three contracts are verified, double-audited with 100% of findings addressed, Slither-clean, and run par 1:1 mechanics with instant fee-free exit; the par invariant holds exactly on-chain (strategy MUSD = sMUSD supply ≈ $6.7M). The grade is held below A− by a governance concentration rather than a code defect: the upgradeable vault and its setStrategy() migration are controlled by a no-timelock 5/9 Mezo-team Safe that can move 100% of principal instantly. Staking routes ~99% of supply through the Velodrome ve/Voter system, which review confirmed touches rewards and emissions only — never staked principal. The deal-breaker gate clears with no failures.
- ▸ Admin can drain 100% of principal at will: upgradeable vault + setStrategy(), both under a no-timelock 5/9 Safe
- ▸ Today's safety depends on the strategy staying Idle — a config switch to a yield venue adds external/DoS risk
- ▸ ~99% of sMUSD lives in the gauge; the Velodrome ve/Voter layer touches rewards only, never staked principal
- ▸ All three contracts are verified, double-audited (Halborn + Thesis Defense, 100% addressed), and Slither-clean
- ▸ Par 1:1 mechanics with instant fee-free exit at every layer; on-chain par invariant holds (strategy MUSD = sMUSD supply)
Technical findings only — not financial advice.
Trust Surfaces
Who can move funds, and how fast| Surface | Controller | Type | Min Delay | Worst Case |
|---|---|---|---|---|
| sMUSD vault — Proxy upgrade | 0x98D8899c3030741925BE630C710A98B57F397C7a ↗ | Multisig 5/9 | Instant | Upgrade the vault implementation to drain 100% of deposited principal — no timelock, single transaction |
| sMUSD vault — setStrategy (onlyOwner) | 0x98D8899c3030741925BE630C710A98B57F397C7a ↗ | Multisig 5/9 | Instant | Migrate 100% of principal to an owner-chosen strategy contract instantly |
| sMUSD vault — setPCV / setVaultGauge / setGaugeYieldClaimCap (onlyOwner) | 0x98D8899c3030741925BE630C710A98B57F397C7a ↗ | Multisig 5/9 | Instant | Redirect the yield source/gauge or change the fee-claim cap — affects rewards, not principal |
| Velodrome Voter / VeBTC — Proxy upgrade & emergencyCouncil | 0x98D8899c3030741925BE630C710A98B57F397C7a ↗ | Multisig 5/9 | Instant | Upgrade the ve/Voter governance contracts or killGauge — extends the governance surface but cannot reach staked principal |
Deal Breaker Matrix
Access Control & Governance
| Item | Status | Evidence |
|---|---|---|
| EOA Upgrade Control | PASS | Vault proxy admin owned by the 5/9 Safe 0x98D8…7C7a; IdleStrategy and VaultGauge are immutable. |
| EOA Fund Control | PASS | No single-EOA withdrawal; principal moves via onlyOwner setStrategy / onlyVault strategy; staked sMUSD is withdrawable only by the staker. |
| >60% Governance Centralization | N/A | No governance token in scope. |
| Governance Mechanism Bypass | N/A | No token voting in scope (MEZO/ve external, audited under Earn). |
| Timelock Backdoors | PASS | No bypass functions, but NO timelock on the vault upgrade / setStrategy path — scored in §2.2, not a hard breaker. |
| No Emergency Controls | N/A | Non-custodial; instant par withdrawal + unconditional unstake are the user's exit. |
Oracle & Price Integrity
| Item | Status | Evidence |
|---|---|---|
| Direct Pool Price Oracle | N/A | No external price feed; sMUSD:MUSD is fixed 1:1 (par). |
| Manual Price Control | N/A | No price oracle; gauge rewards are time-based MEZO emissions. |
Smart Contract Architecture
| Item | Status | Evidence |
|---|---|---|
| Known Compiler Bugs | PASS | Solidity 0.8.33 (vault/strategy), 0.8.24 (gauge); no applicable CVE. |
| No Reentrancy Protection | PASS | nonReentrant on all user/state-changing paths; MUSD/sMUSD have no attacker callback. |
| Unlimited Minting | PASS | sMUSD minted strictly 1:1 vs MUSD pulled in; the gauge mints nothing (emissions pulled from voter, bounded by RewardRateTooHigh). |
| Unsafe Delegatecall / Call | PASS | No delegatecall; SafeERC20 only. |
| Uninitialized Implementation | PASS | Vault calls _disableInitializers(); strategy and gauge are immutable. |
| Unprotected Initializer | PASS | Vault initialize() guarded; no initializer on strategy/gauge. |
Audit & Verification
| Item | Status | Evidence |
|---|---|---|
| No Audit + High TVL | PASS | TVL ~$6.7M; two top-tier audits cover the full Earn system (Halborn + Thesis Defense), 100% of findings addressed. |
| Unverified Contracts | PASS | All three (MUSDSavingsRate, IdleStrategy, VaultGauge) verified on api.explorer.mezo.org; reviewed directly. |
| Critical Unfixed Issues | PASS | Halborn: 0 Critical, 1 High — all reported findings addressed per the report. |
Economic & Liquidity
| Item | Status | Evidence |
|---|---|---|
| Zero Flash Loan Protection | PASS | Par redemption; the vault flushes yield on deposit/withdraw; Halborn 7.1 (flash-loan yield-index) reported addressed (verify deployed mitigation, L-2). |
| Broken Tokenomics | PASS | Fee-yield = real MUSD protocol fees; MEZO emissions depend on the external Velodrome schedule (governance, not a contract defect). |
| No Slippage Protection | N/A | 1:1 par; no swaps. |
Cross-Chain & Bridges
| Item | Status | Evidence |
|---|---|---|
| Centralized Bridge | N/A | Not cross-chain; the strategy operates entirely on Mezo. |
| No Transfer Limits | N/A | Not a bridge. |
| No Token Verification | N/A | Not a bridge. |
Open Issues
- P1 High · Access Control Timeline: 1 monthNo-timelock 5/9 Safe can upgrade the vault or setStrategy to migrate 100% of principal (H-1)Impact: Latent total-loss power over 100% of principal with no reaction windowRecommendation: Timelock the Safe; whitelist/delay strategy changes; cap the migratable amount
- P2 Medium · Smart Contract Timeline: 3 monthsYield misallocation during a strategy migration race (M-1)Impact: Yield can be misallocated during a strategy migrationRecommendation: Found by Halborn 7.6; reported addressed — confirm in the deployed commit
- P2 Medium · Economic Timeline: 3 monthsAmountTooSmall revert can DoS deposit/withdraw if a future yield strategy returns dust (M-2)Impact: Latent deposit/withdraw DoS if a future strategy returns dust yield (N/A while on IdleStrategy)Recommendation: Skip or round dust instead of reverting
- P2 Medium · Smart Contract Timeline: 3 monthsGauge _claimFees uses deprecated safeApprove(feesVotingReward, …) (M-3)Impact: Residual allowance can cause fee-forwarding DoS (rewards only, not principal)Recommendation: Use forceApprove
- P2 Medium · Economic Timeline: 3 monthsFirst depositor can capture accumulated pendingYield after a full-exit period (M-4)Impact: The first depositor after a full exit could capture accrued pending yieldRecommendation: Found by Halborn 7.7; reported addressed — confirm in the deployed commit
- P2 Low · Access Control Timeline: 3 monthsSingle-step Ownable on vault; missing zero-checks (IdleStrategy vault, gauge withdraw) (L-1)Impact: Single-step ownership transfer plus minor missing zero-checksRecommendation: Halborn 7.10 (ownership) reported addressed; minor
- P2 Low · Economic Timeline: 3 monthsHalborn 7.1 flash-loan yield-index fix (min deposit duration) vs deployed instant withdrawal (L-2)Impact: The deployed flash-loan mitigation is unconfirmed given a 0h instant-withdrawal lockRecommendation: Confirm the deployed flash-loan mitigation given the 0h withdrawal lock
- P2 Low · Documentation Timeline: 3 monthsDocs claim exchange-rate appreciation; code is par + separately-claimed yield (L-3)Impact: Docs/code mismatch — docs imply share-price appreciation; the code is par with separately-claimed yieldRecommendation: Fix the docs / clarify the gauge-redirect UX
Contract Inventory
Audit History
Protocol
Operations
All role assignments (5)
| Contract | Role | Holder | Powers |
|---|---|---|---|
| sMUSD vault (MUSDSavingsRate) | owner (OwnableUpgradeable) | 0x98D8…7C7a (5/9 Safe) | setStrategy, setPCV, setVaultGauge, setGaugeYieldClaimCap; upgrade via ProxyAdmin |
| sMUSD vault ProxyAdmin | owner | 0x98D8…7C7a (5/9 Safe) | Upgrade the vault implementation |
| IdleStrategy | onlyVault | sMUSD vault | allocate / deallocate / migrate principal (immutable contract) |
| VaultGauge | voter-only | Velodrome Voter | notifyRewardAmount (MEZO emissions); immutable, staker-only withdraw |
| Velodrome Voter / VeBTC | ProxyAdmin owner / governor / emergencyCouncil | 0x98D8…7C7a (5/9 Safe) | Upgrade ve/Voter; killGauge (emergencyCouncil) — rewards layer only |