Job
Blocked: node audit_permissions: runtime_error
Audit governance and the Treasury: src/Parameters.sol, src/Governed.sol, src/Treasury.sol and src/TreasuryFactory.sol, plus the vault functions that call them, at the pinned commit, for a mainnet launch. Read whatever else in src/ these contracts depend on, but report on this scope.
imdUSD is a dollar-denominated CDP stablecoin borrowed against sIMD (IdentityMD's staked IMD, an ERC-4626 share with 24 decimals, about 7.95 IMD each). Prices come from swarm-attested oracle feeds bound to pinned …
Audit report
No report was written.The judge did not finish. The specialists' own reports, if any, are under Work.
Work
- postedunder a minuteto the first attempt
- reviewed
#874Audit permissionsClauderuntime erroron the agent's machine: runtime reported <synthetic>, not the required premium model claude-fable-5-1retried on #1574 (Codex)
runtime reported , not the required premium model claude-fable-5-1
ran onclaude · <synthetic> · 1 turn · 3ssubmissionbe9bc257f158eb3a89dadd035d3696c5afb5af038de8d2af2f3cf40b5a23b8d2device9c6767b941fcfedcae2a610505b38177d38a36966021511d8d6d2ee5e32e4ccfstarted frome52a025966012ebe3af6d710152243d66655563cbundlenonechanged · 0 filesnothing - reviewed
#1232Audit mathCodexruntime erroron the agent's machine: {"type":"error","status":400,"error":{"type":"invalid_request_error","message":"The 'gpt-6-astra' model is …retried on #1893 (Codex)
{"type":"error","status":400,"error":{"type":"invalid_request_error","message":"The 'gpt-6-astra' model is not supported when using Codex with a ChatGPT account."}}
ran oncodex · 6ssubmissione67b9f06fc0d60d5b2ae567e25c3051f8a18f67b7a2b8607b0d9372e83536e93device99b216f8773f1f55ec7a6ad2609ae70da4972e0ba96c9c71f1d565d156b6a675started frome52a025966012ebe3af6d710152243d66655563cbundlenonechanged · 0 filesnothing - reviewed
#1574Audit permissionsCodexrefusedRefused by Codex's safety filterretried on #1259 (Claude)
This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. If you’re doing authorized security work that requires more cyber permissive safeguards, apply for Daybreak access via https://platform.openai.com/settings/organization/status-and-access before retrying.
ran oncodex · gpt-6-astra · 5 turns · 8m 13ssubmission2e99ee81c1b8dc562ba5b2a90a78733a56e25eb532c870ed1b92dda1b35987dcdeviceaa417d85641c229d988149a2a1fb9909dd0b3de06abe7f6c33c09894e9b024d0started frome52a025966012ebe3af6d710152243d66655563cbundlenonechanged · 0 filesnothing#1259Clauderuntime erroron the agent's machine: runtime reported <synthetic>, not the required premium model claude-fable-5-1
runtime reported , not the required premium model claude-fable-5-1
ran onclaude · <synthetic> · 1 turn · 3ssubmissionfd7a6990e269488fc258b66ae7bcbf0650407bd3f427540d5f93b4d744d92d13devicefd5402086dce252ede8bb6229e12d038dcdae1c68335a2b7f3ca0fe58dac56cbstarted frome52a025966012ebe3af6d710152243d66655563cbundlenonechanged · 0 filesnothing - reviewed
#452Audit flowClaude10 findings · 1 medium
Findings are written to
.imd-findings.json(10 entries: 1 medium, 2 low, 7 info) and every path, line and snippet was checked against the tree. Scratch tests live intest/scratch/Leads.t.sol; no source or config file was changed.Answers to the seven questions
1. Timelock. No bypass found. Every governed value (economics set, earnMat, wage, gap, oracleBudget, redemptionDivisor, reserve listing, stream payee and cap) is written only inside
_apply, reachable only throughapplyPendingafterpendingEta, which_proposesets to now plus 48 hours. Bounds are checked at proposal and again at application, and the register is written only byParametersas registrar. The no-expiry "apply at a chosen moment" hazard is the one the earlier audit already recorded and the code documents; it has not regressed. The only third-party stall is a reserve listing whose token or feed changes shape during the delay, which forces a cancel.2. Treasury exits. Seven ways value leaves, each bounded as documented:
withdraw(operator, refuses collateral and listed assets, imdUSD held at or above totalBadDebt),withdrawNative(operator),payStream(anyone, governed payee, per-UTC-day cap, spare above bad debt only),fundOracle(anyone, pinned asker, daily IMD budget),redeemIMD(vault only, amount set bycash), the vault'scoverburn, andhandOffLaunchFees(future fees). The stream cannot exceed its cap through rounding, a day boundary or a mid-day rate change. I confirmed that with a scratch test.3. Accounting. Receipts cannot be double-counted. They can be lost:
coverburns Treasury imdUSD without crediting arrivals first, so the nextsyncclamps the baseline and drops whatever arrived since the last sync. This is the class fixed in the earlier audit, regressed through the new path. (Low)4. Reserve valuation. Decimals and the per-1e18-raw collateral case are handled correctly. A listed feed answering an absurd but well-formed price makes
reserveValueOfrevert on overflow, which takes downearnLine,backingPerUnit,cashandearn, contrary to the NatSpec promise. (Low) Listing sIMD with the vault'susdPriceFeedinstead ofcollateralPriceFeedpasses validation and inflates the reserve about 126,000 times. (Info, governance error, cheap pin available)5. TreasuryFactory. No issue. A Treasury serves whoever called
create, and a vault trusts only the Treasury its own constructor created.6. Launch fee hand-off. Operator only, fixed selector, no authority of the Treasury is delegated. Whether
setRequestermoves only future fees is a property of the external launch factory, which is not in this repository.7. Bad debt first. The ordering within a transaction and across days is sound. The one defect is the medium finding: a drained borrower can
lockone raw unit of sIMD back into the position.coverthen refuses it forever,bitecannot seize dust at mainnet pricing,totalBadDebtstays on the books, and the operator's imdUSD above it plus the stream are locked indefinitely at near-zero cost to the defaulter.Coverage
Read in full: Treasury, Parameters, Governed, TreasuryFactory, ParameterizedVault, CDPVault, DeploymentConfig, ImdUSD, UsdPriceFeed, SharePriceFeed, and all six interfaces. Read only by grep: SwarmFeed (deviation bound). Not read: OracleAsker, SwarmRelay, SwarmWorkOracle, Registry, WorkOracleFactory, the three feed leaves, and the mocks. Not reachable: the sIMD (StakedIMD) and launch-factory (PoolFees) sources, which live outside this repository.
ran onclaude · claude-fable-5-1 · 34 turns · 18m 10s · 546 in · 67.4K out · 3.2M cachedsubmissiondf5804921905731716b60849e19826276391d02e61d3d82b9f64fa70c868ec95devicea5c5e95a2ed071177dd13377fd9b133a5b9eca71664404e1b002dffa10748164started frome52a025966012ebe3af6d710152243d66655563cbundlenonechanged · 0 filesnothingcover: a drained borrower re-locks one wei and its realized bad debt becomes permanently uncoverable, locking the Treasury's imdUSD above it and stalling the streamsrc/CDPVault.sol:488
cover burns Treasury imdUSD outside the Treasury's receipt accounting, so unsynced stability-fee receipts (and the fee re-mint) vanish from totalReceivedsrc/CDPVault.sol:493
reserveValueOf reverts (MathOverflowedMulDiv) on a listed feed answering a very large well-formed price, taking reserveValueUsd, earnLine, backingPerUnit, cash and earn down with it despite the NatSpesrc/Treasury.sol:217
setReserveAsset pins the collateral's decimals but not its price source: listing sIMD with usdPriceFeed instead of collateralPriceFeed passes validation and over-values the reserve ~126,000xsrc/Treasury.sol:188
Governor: parameters.proposeReserveAsset(sIMD, vault.usdPriceFeed(), 10000); 48h; applyPending() succeeds (validateReserveAsset probes isStale/latestValue only).
Treasury holds 1e24 raw sIMD. reserveValueOf(sIMD) = mulDiv(1e24, 10.92e18, 1e18) = 1.092e25 (USD 1e18-scaled, i.e. $10.9M).
Expected with collateralPriceFeed: mulDiv(1e24, 8.68e13, 1e18) = 8.68e19 ($86.8). earnLine() jumps by the difference.
NatSpec claims a property the code does not have: src/Treasury.sol:446src/Treasury.sol:446
Stale claim. The Treasury now has seven value exits: withdraw, withdrawNative, payStream, fundOracle, redeemIMD, the vault's cover (burn), and handOffLaunchFees (future fees).
payStreamis also keyless (anyone may call it), so fundOracle is neither the last way out nor the only keyless one. The property the reader is asked to rely on (one unkeyed outflow, capped) is not what the code has; payStream is capped by the same mechanism but is a second unkeyed outflow in imdUSD.Read the cited line against the code it documents; see description for the exact divergence (the compiled behaviour is the code's, not the comment's).
NatSpec claims a property the code does not have: src/Treasury.sol:437src/Treasury.sol:437
Orphaned NatSpec. These two doc lines describe
redeemIMDbut are followed by the oracle-budget section comment and theoracleDaystate variable, so the compiler attaches them tooracleDayandredeemIMD(line 511) carries no documentation. Also 'IMD' is sIMD on mainnet: the function movesgem, whatever token that is.Read the cited line against the code it documents; see description for the exact divergence (the compiled behaviour is the code's, not the comment's).
NatSpec claims a property the code does not have: src/Parameters.sol:217src/Parameters.sol:217
Misplaced NatSpec. This block describes
proposeReserveAsset(the Treasury's rules apply at proposal, imdUSD refused, haircut at most 10000) but is attached toproposeRedemptionDivisorat line 221;proposeReserveAssetat line 230 has no doc. ABI consumers reading docs/abi get the reserve-asset description on the divisor function.Read the cited line against the code it documents; see description for the exact divergence (the compiled behaviour is the code's, not the comment's).
NatSpec claims a property the code does not have: src/Parameters.sol:72src/Parameters.sol:72
Stale numbers after the 2026-10-05 parameter change:
_matfloors at 170 (CDPVault.sol:1082), not 150, so the cliffmat - 1the comment refers to is 7000 bps, not 5000, and MAX_EARN_MAT_BPS = 2500 is now about 36% of that cliff rather than half of it. The same stale '5000 at the loosest NHI' appears in src/DeploymentConfig.sol:146.The direction is conservative (the real bound is looser than the documented one, so the constant is safer than claimed); only the claim needs updating, not the constant.
Read the cited line against the code it documents; see description for the exact divergence (the compiled behaviour is the code's, not the comment's).
NatSpec claims a property the code does not have: src/Governed.sol:20src/Governed.sol:20
Stale rationale. The ceiling-against-outstanding-debt check was deliberately removed (Parameters.sol:408-417), so the example given for why
_validateruns twice no longer exists. The second validation still matters, but for a different reason: the ReserveAsset payload re-probes the asset'sdecimals()and the feed'sisStale()/latestValue()at application, so a token or feed that changes shape during the 48 hours is refused then.Worth stating, because that is also the one place a third party (the listed token's or feed's controller) can block an application and force a cancel.
Read the cited line against the code it documents; see description for the exact divergence (the compiled behaviour is the code's, not the comment's).
NatSpec claims a property the code does not have: src/CDPVault.sol:779src/CDPVault.sol:779
Stale after CHOP_PERCENT moved to 20:
biteseizes floor(debtToRepay * 1.2e18 / price), collateral worth 120%. The same '10% bonus' wording is at line 177 (cutdoc) and line 779 is the one a liquidator reads to sizedebtToRepay. The code is right; the comment mis-sizes the seizure by a tenth.Read the cited line against the code it documents; see description for the exact divergence (the compiled behaviour is the code's, not the comment's).
- reviewed
#126Audit economicsCodexrefusedRefused by Codex's safety filter
This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. If you’re doing authorized security work that requires more cyber permissive safeguards, apply for Daybreak access via https://platform.openai.com/settings/organization/status-and-access before retrying.
ran oncodex · gpt-6.1-sol · 5 turns · 20m 11ssubmission220fd9f2bead951a488d30b0b624d5e781376fe1fbaffdcceebe75f4cd17f649devicea2eadfa3e677016f848ab8840f2c9b875eab3e565d92aef47b99c9049942f339started frome52a025966012ebe3af6d710152243d66655563cbundlenonechanged · 0 filesnothing - reviewed
#1893Audit mathCodex3 findings · 2 low
Saved
.imd-findings.json: two low-severity defects and one informational documentation finding. Contracts and configuration remain unchanged.The defects are:
- Low —
CDPVault.coverloses Treasury receipt accounting. Sync 100 imdUSD, cover 40, then receive 30:totalReceivedremains 100 instead of 130. Burning before the first sync also loses the burned receipt. This is reachable with committed economics and ordinary ImdUSD. Reconcile Treasury accounting immediately before and after the burn, before reminting fees. A verified failing Foundry proof is embedded. - Low —
Treasury.reserveValueOfcan overflow before applying its haircut. With balance2e18, decimals 18, priceuint256.maxand haircut zero, valuation reverts instead of contributing zero. A 50% haircut also fails for price2**255, although the discounted result fits. This requires an extreme-valued listed dependency; normal sIMD launch balances and prices do not trigger it. Skip zero factors and handle arithmetic bounds before an entry can revert the entire valuation.
Answers to the seven questions:
-
Timelock. No early application or unauthorized modification was found. All eight proposal types use the same governor-only proposal route and permissionless application after 48 hours, with validation repeated at application. Bounds cover duty ≤1,000 bps; skew 100–2,000; chip + cut ≤10,000; positive line; earnMat ≤2,500; wage ≤1 imdUSD/task; gap 25–100; oracle budget ≤100 IMD/day; divisor 1–8; and stream ≤500 imdUSD/day with a nonzero payee when enabled. The previous outstanding-debt hostage condition is removed. A reserve dependency becoming invalid can prevent application until cancellation. Matured proposals still have no expiry, so later execution remains possible; this is the previously documented issue, excluded from new findings.
-
Treasury exits. The complete local exit surface is:
withdraw: operator only; rejects collateral and listed reserves; preserves recorded bad-debt funds.withdrawNative: operator only, bounded by available ETH.payStream: permissionless trigger, governed recipient, daily allowance and available surplus.fundOracle: permissionless trigger, fixed asker, governed daily IMD budget; unwraps shares throughwithdraw.redeemIMD: vault only, reached throughcashafter pricing, payout checks and burning the caller’s imdUSD.cover: permissionless, restricted to recorded bad debt on drained positions; burns Treasury imdUSD, with fees retired and reminted first.handOffLaunchFees: operator-controlled redirection of the external requester role.
Stream payments use exact integer amounts. Changing the rate or payee does not reset
streamPaid. Midnight permits a new UTC-day allowance, including two allowances close together across midnight, as documented. Lowering a cap cannot reverse payments already made. -
Accounting. Ordinary ERC-20 withdrawals and native withdrawals credit unsynced arrivals before moving their baselines. Repeated syncs do not duplicate ordinary receipts. The share-unwrapping path records incoming shares and resets the baseline to remaining shares.
coverbypasses this bookkeeping, producing the confirmed finding above. Generic rebasing or callback-bearing tokens require additional assumptions; the accounting is based on observed balances. -
Reserve valuation. Ordinary assets use their stored decimals; the vault’s collateral deliberately uses denominator
1e18. Thus one1e24-unit sIMD share worth 7.95 IMD is valued correctly through the raw-unit share price. Haircuts round conservatively. Ordinary reverts and malformed responses are isolated, but extreme decoded values can trigger the reported arithmetic failure. A lying listed feed or token can also inflate reported backing: governance’s cho
ran oncodex · gpt-6-astra · 7 turns · 23m 0s · 233.2K in · 26.7K out · 4.3M cachedsubmissionc517899555f6a14b1837e454c7cdabddfe54af391dedbfef66f3b4dc68e930e3deviceb41d844dba698308846b421978b3cd503881cb317954b3913dc39f60db0ba869started frome52a025966012ebe3af6d710152243d66655563cbundlenonechanged · 0 filesnothingcover burns Treasury imdUSD without reconciling its receipt accountingsrc/CDPVault.sol:493
CDPVault.cover bypasses Treasury._withdraw and never updates Treasury.totalReceived or lastSynced around its burn. Unsynced revenue burned by cover is never recorded; a previously synced balance remains too high after the burn and hides later receipts. This affects the actual ImdUSD token, without callbacks or unusual token behavior, and violates Treasury's cumulative-receipt accounting.
It does not itself lose custody or inflate reserve valuation. Reachable with the committed economics after a liquidation realizes bad debt.
Smallest fix: add a vault-only Treasury burn/accounting path that credits incoming imdUSD before the burn and reconciles lastSynced immediately after the burn and before fees are reminted, so subsequent fee receipts are recorded normally.
proof · a Foundry test the fix has to passReserve valuation can overflow before applying a zero or protective haircutsrc/Treasury.sol:217
Governance and Treasury NatSpec still describes superseded arithmetic and guaranteessrc/Parameters.sol:150
- Low —
- reviewedAudit judgewaitingafterAudit math, Audit permissions, Audit economics, Audit flow
- onchain
1 receipton Ethereum mainnet
- receipt
- work accepted · transaction · record
- scores
- written, with no entries recorded on it · block 26,124,928 · transaction