| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
V12 is Zellic's in-house AI auditing tool. It is the only autonomous auditor that reliably finds Highs and Criticals. All issues found by V12 will be judged as out of scope and ineligible for awards.
V12 findings can be viewed here.
Anything included in this section is considered a publicly known issue and is therefore ineligible for awards.
Monetrix is a USDC-backed synthetic dollar protocol deployed on HyperEVM, the EVM chain of the Hyperliquid ecosystem. Users deposit USDC into the Vault and receive USDM 1:1, a 6-decimal stablecoin. USDM holders can stake into sUSDM — an ERC-4626 wrapper with cooldown-based unstaking — to earn yield generated off-chain by a delta-neutral strategy.
The backing of USDM is composed of:
The Accountant reads backing across these venues through HyperCore precompiles and enforces a four-gate settle pipeline (initialization, minimum interval, distributable-surplus cap, annualized-APR cap) before any yield can be declared. Yield accrues into sUSDM via injectYield, while a configurable share is routed to the Insurance Fund and the Foundation.
All user-facing contracts are UUPS-upgradeable; role authority is split across DEFAULT_ADMIN (48h timelock), UPGRADER (48h), GOVERNOR (24h), OPERATOR (instant, code-bounded), and GUARDIAN (instant pause-only).
Note: The nSLoC counts in the following table have been automatically generated and may differ depending on the definition of what a "significant" line of code represents. As such, they should be considered indicative rather than absolute representations of the lines involved in each contract.
| File |
|---|
| lib/**.** (forge-std, OpenZeppelin, hyper-evm-lib) |
| test/**.** (unit, fork, invariant, simulator, mocks, PoC template) |
INV-1 — Peg solvency (soft invariant). USDM total liability is covered by the composite backing:
totalBackingSigned() =
USDC.balanceOf(Vault)
+ USDC.balanceOf(RedeemEscrow)
+ Σ L1 spot USDC
+ Σ L1 spot × oraclePx (whitelist)
+ Σ 0x811 supplied USDC / spot × px (registered slots)
+ perp accountValue (signed)
+ HLP equity (signed, MtM)
Under normal operation: totalBackingSigned() ≥ int256(USDM.totalSupply()).
This is a soft invariant — deposit does not gate on backing (it mints 1:1); only settle indirectly enforces it via Gate 3 (surplus > 0). Transient violations can occur during bank-runs, sustained negative funding, or L1 oracle anomalies. Recovery path: InsuranceFund.withdraw → Vault (Governor, 24h timelock).
Code refs: MonetrixAccountant.sol:117 (totalBackingSigned), :180 (surplus).
INV-2 — sUSDM exchange rate is monotonically non-decreasing. Under normal operation:
rate(t) = totalAssets(t) / totalSupply(t)
Code refs: sUSDM.sol:102 (totalAssets), :234 (injectYield), sUSDMEscrow.sol:33 (deposit), :38 (release).
INV-3 — Redemption accounting correctness. RedeemEscrow.totalOwed precisely tracks outstanding unclaimed USDM redemption commitments:
Add/sub are symmetric; no other path mutates totalOwed.
INV-4 — sUSDM unstake balance ≡ commitments. USDM.balanceOf(sUSDMEscrow) == sUSDM.totalPendingClaims.
Code refs: sUSDM.sol:171-172, :202-203, :227-228.
INV-5 — Gate 1: initialization. settleDailyPnL reverts when lastSettlementTime == 0. lastSettlementTime is set once by Governor via initializeSettlement(), then advanced only by settle itself. Code refs: MonetrixAccountant.sol:206, :312-314.
INV-6 — Gate 2: minimum interval. require(block.timestamp ≥ lastSettlementTime + minSettlementInterval). Code ref: MonetrixAccountant.sol:209.
INV-7 — Gate 3: distributable cap. require(proposedYield ≤ distributableSurplus()), where distributableSurplus() = surplus() - shortfall() and surplus() = totalBackingSigned() - int256(USDM.totalSupply()). Code refs: MonetrixAccountant.sol:213-215, :187.
INV-8 — Gate 4: annualized APR cap.
require(proposedYield ≤ USDM.totalSupply() × maxAnnualYieldBps × Δt / (10000 × 1 year))
Code refs: MonetrixAccountant.sol:217-221, MonetrixConfig.sol:56, :151-156, :88.
INV-9 — Cumulative yield bounded by cumulative surplus. Σ (proposedYield across all settles) ≤ Σ (realized surplus over same window). Jointly enforced by INV-7 and INV-8 under trusted Operator reporting. totalSettledYield is the on-chain cumulative counter. Code refs: MonetrixAccountant.sol:62, :224.
INV-10 — USDM mint/burn callable only by Vault. USDM.mint and USDM.burn gated by onlyVault. Code refs: USDM.sol:22, :46, :53.
INV-11 — sUSDM.injectYield callable only by Vault. sUSDM.injectYield gated by onlyVault. Code refs: sUSDM.sol:79, :234.
INV-12 — Escrow fund movements gated by Vault.
Code refs: RedeemEscrow.sol:76-79, YieldEscrow.sol:48, sUSDMEscrow.sol:21.
INV-13 — Accountant privileged surface callable only by Vault. Accountant.{settleDailyPnL, notifyVaultSupply} gated by onlyVault. Code refs: MonetrixAccountant.sol:46, :202, :250.
| Role | Description | Granted To |
|---|---|---|
| DEFAULT_ADMIN | Grants / revokes all roles; authorizes ACL upgrade. Sits behind a 48h timelock. | Multisig (48h timelock) |
| UPGRADER | Authorizes UUPS upgrade of all 9 proxies (Vault, USDM, sUSDM, Config, Accountant, RedeemEscrow, YieldEscrow, InsuranceFund, ACL). Sits behind a 48h timelock. | Multisig (48h timelock) |
| GOVERNOR | All Config / Accountant / Vault setters; InsuranceFund.withdraw; Vault emergency paths (emergencyRawAction, emergencyBridgePrincipalFromL1 — intentionally bypass both pause flags). Sits behind a 24h timelock. | Multisig (24h timelock) |
| OPERATOR | Bridge, hedge, HLP, BLP, yield pipeline (settle / distributeYield), fundRedemptions, reclaimFromRedeemEscrow. Code-bounded: Operator can only move funds among Vault ↔ L1 own account / Vault ↔ Escrows / sUSDM / InsuranceFund / Foundation. All destination addresses are pre-set by Governor. Operator cannot route funds to an external EOA or arbitrary address. | Hot wallet (instant, no timelock) |
| GUARDIAN | Two independent pause switches: pause freezes user flows + mixed paths (deposit / redeem / keeperBridge / settle / distributeYield); pauseOperator freezes all Operator paths. No fund authority. | Hot wallet (instant, no timelock) |
| Vault (contract) | Via onlyVault: USDM.mint/burn, sUSDM.injectYield, Escrow fund movements, Accountant.settleDailyPnL / notifyVaultSupply. | Vault contract (msg.sender) |
Trust tiers. DEFAULT_ADMIN / UPGRADER / GOVERNOR are held by the same multisig in production, wrapped behind different timelocks. OPERATOR is the largest instant-trust surface in v1; mitigation via off-chain monitoring, multi-Operator redundancy, and Guardian dual-pause. GUARDIAN has pause authority only, no fund access. There are no in-contract roles (no MINTER / KEEPER etc.) — all consolidated into direct onlyVault checks.
The codebase uses a Foundry installation to compile and run tests.
git clone --recurse-submodules https://github.com/code-423n4/2026-04-monetrix
cd 2026-04-monetrix
forge buildCompiler config: solc 0.8.28, via_ir=true, optimizer_runs=200, EVM target Cancun — all auto-resolved from foundry.toml.
Run all non-fork tests:
forge test -vvvRun the PoC template only:
forge test --match-path "test/c4/C4Submission.t.sol" -vvvRun targeted unit suites:
forge test --match-path "test/Monetrix.t.sol" -vvv
forge test --match-path "test/MonetrixAccountant.t.sol" -vvv
forge test --match-path "test/Governance.t.sol" -vvvRun fork tests (requires testnet RPC):
export FOUNDRY_ETH_RPC_URL=https://rpc.hyperliquid-testnet.xyz/evm
forge test --match-path "test/MonetrixFork.t.sol" -vvvGas report:
forge test --gas-reportCoverage:
forge coverage --report summaryEmployees of Monetrix and employees' family members are ineligible to participate in this audit.
Code4rena's rules cannot be overridden by the contents of this README. In case of doubt, please check with C4 staff.
| Back | FazBrowse Home | New Git URL |