Job
Build a production-ready DeFi lending protocol and a complete user-facing web application on Ethereum Mainnet (Chain ID 1), inspired by Aave V3, where IMD (0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7) is used as collateral to borrow USDC, USDT, and WETH with variable-rate debt. Before implementation, research and verify Aave V3 architecture/licensing, canonical Ethereum asset addresses, IMD contract behavior and admin risks, liquidity, volatility, holder concentration, and reliable oracle …
Published · Contracts
Work
- posted14 minto the first attempt
- built
#1654Build contract projectCodexruntime erroron the agent's machine: Your workspace is out of credits.retried on #121 (Codex)
Your workspace is out of credits. Ask your workspace owner to refill in order to continue.
ran oncodex · gpt-6-astra · 3 turns · 13m 56ssubmission6aa00a027bd78ac2dd05bef493984653c3a519332a2147a292b7d280c106f6f3devicec9668a6c89d33b13f4269bd20683d770ac94499bf506f6d2bb4623a73f1e6792started from0243d7da4a4337ae8b16bcdf15bb4ead736fd68fbundlenonechanged · 0 filesnothing#121Codex109 files changedrevised
Delivered contracts, wallet-connected frontend, cited research, tests, and deployment documentation. See the project README.
Validation passed:
forge build, 47 Foundry tests, formatting, 12 frontend tests, three Mainnet fork tests, and 46 frontend integration transactions on a fork.Production launch remains incomplete. No public HTTPS URL exists. Approved IMD pricing, hosting access, WalletConnect, real-browser E2E, and external audit remain outstanding. One Medium availability finding remains documented. Supply/borrow caps stay zero and borrowing starts frozen.
ran oncodex · gpt-6-astra · 8 turns · 34m 37s · 122.7K in · 41.7K out · 6M cachedsubmissionaaeadfb60c0255006f3fa8e8c0e4cee9feb92d7aa0f9f0783c216e8232dc3858device2e343a06f770172de5eab077f3100876d28f021a08dcd0005553a27f479e02ddstarted from0243d7da4a4337ae8b16bcdf15bb4ead736fd68fbundle64445b0c3e8157b62e1619b5e42254b9d37f87047aae98e0bd5767120f4ffc6b · 392 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 109 files.gitignoreREADME.mddocs/ARCHITECTURE.mddocs/DEPENDENCIES.mddocs/DEPLOYMENT.mddocs/FRONTEND.mddocs/RESEARCH.mddocs/SECURITY_REVIEW.mddocs/VALIDATION.mddocs/evidence/aave-legacy-license.jsondocs/evidence/aave-legacy-tag.jsondocs/evidence/aave-origin-commit.jsondocs/evidence/aave-origin-license.jsondocs/evidence/bytecode-check.txtdocs/evidence/chainlink-mainnet-catalog.jsondocs/evidence/chainlink-onchain.jsondocs/evidence/economic-stress.csvdocs/evidence/forge-build.txtdocs/evidence/forge-fmt.txtdocs/evidence/forge-tests.txtdocs/evidence/fork-rpc-limitation.jsondocs/evidence/frontend-abi-check.txtdocs/evidence/frontend-fork-initial.jsondocs/evidence/frontend-fork.jsondocs/evidence/frontend-unit-tests.txtdocs/evidence/imd-admin-holders-onchain.jsondocs/evidence/imd-blockrazor.jsondocs/evidence/imd-dexscreener.jsondocs/evidence/imd-etherscan.jsondocs/evidence/imd-holders-blockscout.jsondocs/evidence/imd-onchain.jsondocs/evidence/imd-sourcify.jsondocs/evidence/imd-token-blockscout.jsondocs/evidence/independent-review-checks.jsondocs/evidence/mainnet-block.jsondocs/evidence/mainnet-fork-tests.txtdocs/evidence/usdc-blockrazor.jsondocs/evidence/usdc-onchain.jsondocs/evidence/usdt-onchain.jsondocs/evidence/weth-onchain.jsonfoundry.tomllib/forge-std/LICENSE-APACHElib/forge-std/LICENSE-MITlib/forge-std/src/Base.sollib/forge-std/src/Script.sollib/forge-std/src/StdAssertions.sollib/forge-std/src/StdChains.sollib/forge-std/src/StdCheats.sollib/forge-std/src/StdConstants.sollib/forge-std/src/StdError.sollib/forge-std/src/StdInvariant.sollib/forge-std/src/StdJson.sollib/forge-std/src/StdMath.sollib/forge-std/src/StdStorage.sollib/forge-std/src/StdStyle.sollib/forge-std/src/StdToml.sollib/forge-std/src/StdUtils.sollib/forge-std/src/Test.sollib/forge-std/src/Vm.sollib/forge-std/src/console.sollib/forge-std/src/console2.sollib/forge-std/src/interfaces/IERC1155.sollib/forge-std/src/interfaces/IERC165.sollib/forge-std/src/interfaces/IERC20.sollib/forge-std/src/interfaces/IERC4626.sollib/forge-std/src/interfaces/IERC6909.sollib/forge-std/src/interfaces/IERC721.sollib/forge-std/src/interfaces/IERC7540.sollib/forge-std/src/interfaces/IERC7575.sollib/forge-std/src/interfaces/IMulticall3.sollib/forge-std/src/safeconsole.sollib/openzeppelin-contracts/LICENSElib/openzeppelin-contracts/contracts/utils/Panic.sollib/openzeppelin-contracts/contracts/utils/math/Math.sollib/openzeppelin-contracts/contracts/utils/math/SafeCast.solremappings.txtscript/DeployMainnet.s.solsrc/GovernanceTimelock.solsrc/IMDBank.solsrc/RiskOracle.solsrc/lib/ExactToken.soltest-fork/Mainnet.t.soltest/Audit.t.soltest/DeploymentRehearsal.t.soltest/GovernanceTimelock.t.soltest/IMDBank.t.soltest/Invariants.t.soltest/RiskControls.t.soltest/RiskOracle.t.soltest/helpers/BankFixture.soltest/helpers/Mocks.soltools/check_bytecode.pytools/economic_stress.pyweb/_headersweb/abi.jsweb/app.jsweb/check-abi.mjsweb/config.jsonweb/core.jsweb/fonts.cssweb/index.htmlweb/package.jsonweb/styles.cssweb/tests/abi-compatibility.mjsweb/tests/core.test.mjsweb/tests/fork-integration.mjsweb/vendor/ETHERS-LICENSE.mdweb/vendor/README.mdweb/vendor/ethers-6.15.0.min.js - reviewed
#1696Audit flowClauderuntime erroron the agent's machine: runtime reported <synthetic>, not the required premium model claude-fable-5-1retried on #403 (Claude)
afterBuild contract projectruntime reported , not the required premium model claude-fable-5-1
ran onclaude · <synthetic> · 1 turn · 2ssubmissionc2473d8ce3a9fc68224969365088434affa9e75b3874c86fe31844bbb2b0a2bddevice5278e52f0f1704f6a6142f2efa12037c80c13e6e9a5927a995555492c03e7d72started from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothing - reviewed
#1797Audit economicsClauderuntime erroron the agent's machine: runtime reported <synthetic>, not the required premium model claude-fable-5-1retried on #715 (Claude)
afterBuild contract projectruntime reported , not the required premium model claude-fable-5-1
ran onclaude · <synthetic> · 1 turn · 3ssubmission84d142c8237e910cf4ac5851cd57778163b137e8d168dc6a69d9b914ea973751deviceac55933908aba3e5385722c0a39e793c7f49115562a240c4dc881b42f313ed02started from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothing - reviewed
#403Audit flowClauderuntime erroron the agent's machine: runtime reported <synthetic>, not the required premium model claude-fable-5-1retried on #559 (Claude)
afterBuild contract projectruntime reported , not the required premium model claude-fable-5-1
ran onclaude · <synthetic> · 1 turn · 3ssubmissionc03ec47af119d135510d6b02ba316fa3ee8e6517f8cdffc77a2d6f82bc5a751fdevicee51446c90abcd5cc8453ee167fe15495ef7f54f9eb2b5ea36827757ea9bee960started from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothing - integrated
#858ManifestCodexruntime erroron the agent's machine: the task produced no changes; the agent's last message was: `launch.json` is blocked by one missing input: …retried on #1598 (Codex)
the task produced no changes; the agent's last message was:
launch.jsonis blocked by one missing input: the authorized emergency guardian/canceller address.GovernanceTimelockrequires it to differ from$owner; duplicate or zero addresses revert.Please provide that Ethereum Mainnet address.
forge buildpassed with lint warnings; all 47 existing tests and two scratch checks passed. No submission files were changed.ran oncodex · gpt-6-astra · 5 turns · 3m 32s · 50K in · 3.6K out · 495.6K cachedsubmission877043b30d84f0dba91d10bb288b100ec1926ca90d9af89139618b82283d05afdevice68fd8dd6450ac70c17815a4b8406630872b015196d49e05b17fe03c8fb319fe6started from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothing#1598Codex1 file changedrevised
afterBuild contract projectwrites tolaunch.jsonCreated launch.json as an explicitly blocked draft. It needs an authorized emergency guardian/canceller address distinct from
$owner; the current zero placeholders intentionally prevent deployment.forge buildpassed with warnings; all 47 tests passed. Onlylaunch.jsonis a submission change. Deployment acceptance remains unmet pending that address.ran oncodex · gpt-6-astra · 5 turns · 3m 55s · 79.5K in · 6K out · 287.6K cachedsubmission5ef5b3838f0f7f9d184d038dce724fabe20e06d28b237484ead0bf5879f3ed30device75474cc326e8a8175c443ef81be56c3114b383c14b2ef987184b1433e5b720ecstarted from6ca1f036ccc9466a456fa6fac067c50383f0420fbundle866b2b2573a0a9719d31b900b6e69c2c507a66edffb360af2cd7ae8bfe27f589 · 393 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 1 filelaunch.json - reviewed
#354Audit permissionsClaude3 findings · 1 medium
afterBuild contract projectThe review is complete. The findings file is written and validated, and no tracked source file was changed.
Outcome. The permission model of all three contracts is sound at the function level: every guard I traced (
onlyGovernor, the governor/guardian freeze split, the oracle's disable-only guardian, the timelock's proposer/canceller checks, replay and expiry handling) does what it claims, and the 47 existing tests pass. The defects I found live in how the roles are wired together rather than in any single guard.Findings written to
.imd-findings.json:-
Medium, trust gap. The production wiring in the deploy script makes the emergency multisig the bank guardian, the oracle guardian, and the timelock canceller at once. The guardian can disable the IMD feed and freeze the bank, then cancel every governance proposal that would re-enable, unfreeze, or rotate it. The oracle guardian is immutable, so even an executed re-enable can be undone the next block. Result: a rogue or compromised emergency key blocks liquidation and bad-debt recognition indefinitely, which contradicts the documented "only governance can reverse" model and the timelock's own NatSpec. A scratch test reproduces this and fails on the current tree.
-
Low, trust gap with race amplifier. Risk-parameter cuts apply to open positions in the same call as their activation, and timelock execution is permissionless. A searcher can execute a threshold cut and liquidate in one transaction. Reproduced: HF moves from 1.75 to 0.75 with no price change, and the executor collects the 8% bonus.
-
Low, access asymmetry. The governance-only frozen flag is also written by the permissionless dust write-off path. This is the author's retained M-05 residual. I added the timelock interaction: a pre-positioned micro-account can be finalized just before a matured unfreeze executes, making it revert and burning the grace window.
Coverage. All 20 verifier entry points have a row plus three invariant rows. Fourteen hold, six carry a finding reference, none are unreached. Items outside my area I did not pursue in depth: oracle feed-level arithmetic and the frontend beyond checking that approvals are exact-amount with a zero reset.
ran onclaude · claude-fable-5-1 · 40 turns · 14m 2s · 354 in · 54K out · 1.9M cachedsubmission59fe3089536337805dd78bbfcbaef4b53e1d984cb74c8abb67b6c11856daf283device523ef565dd740e258967569a789ffae5b08d99a774b8ea6a2ecfb7478b5eba5dstarted from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothingmediumEmergency key is timelock canceller and immutable oracle guardian: it can veto every governance reversal and keep liquidations blocked indefinitelyscript/DeployMainnet.s.sol:35
proof · a Foundry test the fix has to passRisk-parameter cuts take effect atomically with permissionless timelock execution; executor can liquidate positions that were healthy one call earliersrc/IMDBank.sol:389
Permissionless finalizeDust writes the governance-only frozen flag; a pre-positioned micro-account can fail a matured setFrozen(false) proposalsrc/IMDBank.sol:509
-
- reviewed
#1927Audit mathClaude4 findings · 2 medium
afterBuild contract projectReview complete. Findings are in
.imd-findings.json(4 findings, 23 coverage rows, 2 Foundry proofs). No source files were changed;git statusshows only the gitignored scratch tests and the findings file.What I covered. I traced the full math surface of
IMDBank,RiskOracleandGovernanceTimelockagainst the Math Precision, Boundary and Numerical Gap guides: the per-second compounding index, share minting and burning, exact-reduction repayment, liquidation quoting and the dust sweep, health-factor and LTV checks, oracle normalisation and band logic, and the frontend's projected health factor. I also fuzzed liquidation value bounds, repayment exactness and borrow rounding at several thousand runs each. The core accounting held up: debt always rounds in the reserve's favour, partial repayment never charges more than the budget, seized collateral never exceeds paid value times the bonus plus the documented dust sweep, and checkpoint frequency does not change interest.Findings, most severe first.
- Medium, RiskOracle band blocks liquidation (proof attached). Every feed must have a
minPrice, andprice()reverts when two agreeing, fresh feeds fall below it, on the collateral side too. In the proof, IMD configured with a $5 floor crashes to $4.00 and a borrower at health factor 0.56 cannot be liquidated or finalized; the only repair is a timelocked reconfiguration. The suggested fix keeps the band but hard-rejects only the direction that would overvalue the borrower. - Medium, sub-unit debt positions freeze the whole bank (proof attached). A one-base-unit USDC debt is admitted at exactly 25% LTV. After one second the ceil rounding on one share makes the displayed debt two units, dropping the health factor from 1.40 to 0.70 with no price move. There is then a band where neither liquidate nor finalizeDust can execute, and after a 51% move the account is finalized as two units of bad debt that set the global freeze. This sharpens the accepted M-05 residual: the rounding halves the price move needed. Suggested fix is a minimum debt value in borrow and/or a reserve-scoped freeze for dust losses.
- Low, liquidation sweep skipped when
seizedfloors to zero. With collateral near the price bound,paid > 0butseized == 0, so liquidate reverts Dust and finalizeDust rejects because a share is affordable. Precondition is only reachable nearMAX_PRICE; the fix is dropping one conjunct. - Low, frontend projection omits the LTV capacity check. The projected health factor only flags values below 1.0, while the contract rejects borrows and withdrawals above 25% LTV, so projections between 1.0 and 1.4 look safe and revert on chain.
Static-analysis leads on strict equality, reentrancy and the uninitialised
quotelocal were traced and are benign under the shared reentrancy lock and the timelock's pre-call state writes. Not reached in depth: the Mainnet fork test and the browser transaction flow, which need network access.ran onclaude · claude-fable-5-1 · 41 turns · 16m 23s · 578 in · 68.3K out · 2.6M cachedsubmissionae81ceffbaf22d9d5ec325be07e27fd9e899a4cc0b322ee61e06c9552befb74cdevice4d71347e3f9991623fc8b22fb5133ea5cb14898b0042e468ccd14919288434e7started from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothingmediumValid collateral price below the mandatory minPrice band makes liquidation and loss recognition revert exactly when they are neededsrc/RiskOracle.sol:107
proof · a Foundry test the fix has to passmediumSub-unit debt positions: one-share debt doubles after one second of accrual, sits in a band where neither liquidate nor finalizeDust can execute, then a 51% move turns $0.000002 into bad debt that fresrc/IMDBank.sol:593
proof · a Foundry test the fix has to passLiquidation dust sweep is skipped when seized floors to zero, leaving a position that neither liquidate nor finalizeDust can clearsrc/IMDBank.sol:477
Boundary x precision. _liquidationQuote computes seized = floor(seizedValue * collateralUnit / collateralPrice). When one wei of IMD is worth more than paid * debtPrice * 1.08 (collateral price near the 1e27 MAX_PRICE bound with a few wei of collateral), seized rounds to 0 while paid > 0.
The dust sweep that would hand over the whole residual requires seized != 0, so the quote falls through to 'if (paid == 0 || seized == 0) revert Dust()'. finalizeDust then rejects the same account because _requireUnliquidatableDust sees paid != 0 (a share is affordable). The account is unreachable by both paths.
The precondition (IMD above roughly $2e8 per token) is only reachable at the accepted price boundary, so this is reported as a logic gap with a trivial fix rather than a live loss.
Frontend projected health factor omits the LTV capacity check the contract actually enforces on borrow and withdraw, so a projection shown as safe is rejected on chainweb/core.js:68
Numerical gap (queryX vs doX). projectedHealth reproduces accountData's health factor (threshold-based, 35%), and app.js only marks the projection as dangerous when it is below 1.0. IMDBank._requireBorrowSafe (src/IMDBank.sol:536) rejects borrow and debt-bearing withdraw when debtUsd > borrowCapacityUsd, which uses ltvBps (25%).
Every borrow or withdrawal that leaves the account between HF 1.0 and HF 1.4 (threshold/LTV) is shown as a non-danger projection and then fails with UnsafePosition at simulation. The review dialog's pre-checks (cash, supplied balance, wallet balance) do not include capacity either. No funds are at risk because the contract is the source of truth; the gap is that the displayed number cannot tell users the real rejection boundary.
- Medium, RiskOracle band blocks liquidation (proof attached). Every feed must have a
- reviewed
#715Audit economicsClaude4 findings · 2 medium
afterBuild contract projectThe review is complete. The findings are in
.imd-findings.jsonat the repository root; no source files were changed (the findings file andtest/scratch/are gitignored, so the tree is clean).What I audited. The Economic Security, Invariant and Flow Gap passes over
IMDBank.sol,RiskOracle.sol,GovernanceTimelock.solandExactToken.sol, plus the test suites. I traced the indexed debt accounting, every rounding direction, the liquidation quote and dust rule, the write-off and recapitalization cycle, the rate model, the oracle validation and the timelock. The core accounting holds: share minting and burning stay paired, partial repayments telescope so no rounding farm exists, the collateral-limited liquidation path is reachable only when the position is already insolvent at bonus-adjusted terms, repeated liquidations converge, and the documented M-05 dust-halt needs a genuine 73%+ IMD drop rather than anything an attacker controls.Findings reported (4, none critical or high):
- Medium, oracle floor blocks liquidation in a crash.
RiskOracle.configureforces a nonzerominPriceandprice()reverts below it even for the collateral feed, where a low price is the safe direction. Two agreeing feeds at $1.90 under a $2 floor makeliquidate,finalizeDust,accountDataall revert on an already insolvent position, and only a 2-day timelock can fix it. Includes a Foundry proof (test/scratch/OracleFloorProof.t.sol) that fails now and passes with a one-line fix allowing a zero floor on collateral-side feeds, verified in a throwaway copy. - Medium, zero-cost supply-cap fill. A debt-free depositor can hold the remaining cap headroom, so borrowers get
CapExceededon top-ups during a dip, get liquidated for the 8% bonus, and the filler withdraws immediately with no fee, freeze or oracle dependency. - Low, 1-day maximum age equals the stablecoin heartbeat. With Chainlink USDC/USD and USDT/USD at 86,400 s and the bound immutable, every daily round latency is a window where stablecoin-debt accounts cannot be liquidated, borrow or withdraw.
- Info, test gaps. The invariant handler only exercises USDC paths and never calls
finalizeDust,coverBadDebt, reconfiguration or above-kink rates.
Coverage. All 20 verifier entry points have a row (16 hold, 4 reference findings), plus 4 invariant rows. Nothing was left unreached.
ran onclaude · claude-fable-5-1 · 38 turns · 16m 31s · 386 in · 71.8K out · 1.7M cachedsubmissione0e3d0a3c4a3950b29e2c8883a86c0616700becff330c9883478f9f01c3d6e88device87804e27e9c9f85a56b7d27769006acebfcf590ed64f6eef9617da5195c9d826started from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothingmediumCollateral-side oracle floor is mandatory and reverts, so a genuine IMD crash through the configured floor blocks all liquidations and dust finalization until a 2-day timelock reconfigurationsrc/RiskOracle.sol:68
proof · a Foundry test the fix has to passmediumSupply cap can be filled at zero cost by a debt-free depositor, blocking every borrower's collateral top-up ahead of a liquidation the filler can then executesrc/IMDBank.sol:175
Hard 1-day maximum feed age equals Chainlink's 24h heartbeat for USDC/USD and USDT/USD, creating a recurring daily window in which stablecoin-debt positions cannot be liquidated, borrowed against or wsrc/RiskOracle.sol:67
test/scratch/Econ.t.sol test_maxAgeHardBoundVsHeartbeat: USDC feeds configured with primaryMaxAge = secondaryMaxAge = 1 days; both USDC rounds updated at t0.
At t0 + 1 day accountData(ALICE) succeeds.
At t0 + 1 day + 60 s (next round not yet landed) accountData reverts InvalidPrice; IMD feeds drop to $5 (HF 0.7) and liquidate(ALICE, USDC, max, 0, now) reverts InvalidPrice instead of liquidating. configure(..., 1 days + 1, ...) reverts InvalidConfiguration, so no buffer can be configured.
Stateful invariant and fuzz suites never exercise multi-asset debt, finalizeDust, coverBadDebt, reserve/risk reconfiguration or high-utilization rates, so the conservation claims are only established test/Invariants.t.sol:45
- Medium, oracle floor blocks liquidation in a crash.
- tested
#174Write foundry testsCodex7 files changedrevised
afterBuild contract projectwrites totesttest/**Added failure-path, fuzz, oracle, token, governance, and three-reserve invariant tests under
test/.forge build: passed.forge test: 90 passed, 0 failed.- Invariants: 24,576 randomized calls.
- Fork suite: skipped because no RPC was configured.
No new reproducible contract defects found. Coverage and limitations are documented in
test/ADDED_TEST_COVERAGE.md.ran oncodex · gpt-6-astra · 9 turns · 18m 24s · 114.2K in · 35.4K out · 1.9M cachedsubmission49a36f82c701b679436131675cfb5e0922bb5793a63237dd8c2918033ef9ec7ddevice5b85781dd6c37ccaeda9dbf53016e112345016c3c49cd514c92b88006e49998astarted from6ca1f036ccc9466a456fa6fac067c50383f0420fbundle1d7cc3051cad4c48435ade87a26a7f1422cef0cb85551526da090f538934d2a7 · 410 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 7 filestest/ADDED_TEST_COVERAGE.mdtest/AccountingEdges.t.soltest/GovernanceEdges.t.soltest/OptionalMainnet.t.soltest/OracleIntegrationEdges.t.soltest/ThreeReserveInvariants.t.soltest/TokenBoundaryEdges.t.sol - reviewed
#559Audit flowClaude3 findings · 1 medium
afterBuild contract projectThe review is complete. The findings file is valid, every quoted snippet matches the tree at the cited line, and the working tree is clean apart from the required
.imd-findings.jsonand the ignoredtest/scratch/directory.What I reviewed. I traced all 20 listed entry points across
IMDBank,RiskOracleandGovernanceTimelock, plus theExactTokenlibrary, through the Execution Trace, Periphery and First Principles lenses. The existing 47-test suite passes. I also read the frontend transaction path and the deployment and research docs for periphery context.Findings written to
.imd-findings.json:- Medium,
src/RiskOracle.sol:67. The oracle hard-caps feed max age at one day. The project's own research selects the Chainlink USDT/USD feed whose heartbeat is exactly 86,400 s, so there is zero slack. Each day, until the heartbeat round is mined, USDT pricing reverts, and becauseaccountDataprices every owed reserve, liquidation of any USDT debtor fails on every asset. Scratch test shows an HF 0.7 position that cannot be liquidated at heartbeat plus one second and can at heartbeat exactly. - Low,
src/IMDBank.sol:474. Liquidation seizure uses the high debt feed and the low collateral feed. With both pairs disagreeing inside the permitted 20% band, a liquidator receives 191.25 IMD for 1,250 USDC, a 53% premium versus the 8% bonus and the 15% hard bound. Solvency is unaffected. The borrower bears the loss. - Info,
docs/DEPLOYMENT.md:5. No public HTTPS frontend exists and the web config is unpopulated. This is a completeness gap against the brief, not a code defect, and is outside my area.
What held. Indexed accounting and rounding direction, reentrancy ordering, freeze and bad-debt gates, dust finalization admission, timelock replay and atomicity, and the exact-delta token adapter all behaved as intended under the traces I ran. The static-analysis leads were sentinel equality, guarded timestamp use, and external calls made after the lock, none reproducible as defects.
Not reached. Fork tests and browser or WalletConnect flows need network access that this environment lacks. Coverage rows record this explicitly.
ran onclaude · claude-fable-5-1 · 40 turns · 17m 21s · 450 in · 62.1K out · 2M cachedsubmission9aae0ba85080bbb6e2b8f6a8f4757de12e9a4ddbc8b8925b2daf3cd4af011b73device6208734cdf5317a188e5c6dc2af68514fe66d13f7620146df9d349eb7e0db04fstarted from6ca1f036ccc9466a456fa6fac067c50383f0420fbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 0 filesnothingmediumOracle max-age hard bound of 1 day leaves zero slack for the 86,400 s USDT/USD heartbeat, so USDT pricing and every liquidation touching USDT debt fail in a recurring daily windowsrc/RiskOracle.sol:67
Liquidation seizure prices debt at the HIGH feed and collateral at the LOW feed, so the realized liquidator premium is bonus x (1+devCollateral) x (1+devDebt) and bypasses the 15% liquidationBonusBps src/IMDBank.sol:474
Required public HTTPS frontend deployment is absent; the repository states the website is not deployed and the frontend config is unpopulateddocs/DEPLOYMENT.md:5
The brief requires the frontend to be hosted as a usable public website with a final public HTTPS URL and states deployment is not complete if the frontend only runs locally. docs/DEPLOYMENT.md line 5 records that no public URL exists, web/config.json has bankAddress/oracleAddress/code hashes set to null with deploymentStatus NOT_DEPLOYED, and WalletConnect is documented as not implemented.
This is a completeness gap against the stated acceptance criteria rather than a code defect, and is outside this reviewer's control-flow area; it is recorded so the judge can track it. No code change is implied by this note; the frontend correctly refuses to invent addresses when unconfigured.
- Medium,
- reviewed
#573Audit judgeClaude11 findings · 4 mediumrevised
afterBuild contract project, Write foundry tests, Manifest, Audit math, Audit permissions, Audit economics, Audit flowAll snippets match the tree, the four embedded proofs fail on the current code, and
.imd-findings.jsonis in place. Nothing outside test/scratch and the findings file was changed.Outcome: 11 findings written to
.imd-findings.jsonwith a 21-row coverage record (all 20 entry points plus one invariant). No critical or high defects reproduced. Four mediums carry failing Foundry proofs.Findings kept, after reproduction and merging
- Medium, oracle band (merged economics + math): a valid two-feed crash below the mandatory collateral
minPricemakes liquidate, finalizeDust, borrow and debt-bearing withdraw revert. Proof fails withInvalidPrice(). - Medium, no minimum position (merged math + permissions): a 1-unit USDC debt doubles after one second of ceil-rounded accrual, sits in a band unreachable by both liquidate and finalizeDust, then a $0.000002 loss freezes the whole bank and can front-run a matured unfreeze. Proof fails.
- Medium, immutable oracle guardian + canceller wiring: the emergency key can veto every governance reversal and re-disable the IMD feed forever. Proof fails.
- Medium, 1-day maxAge cap (merged economics + flow): equals the selected USDT/USD heartbeat, so stablecoin-debt positions are unliquidatable in a daily window. I wrote a fix-agnostic proof that fails with
InvalidPrice(). - Low: supply-cap fill griefing, cross-side feed pricing inflating the liquidator premium, zero-seize sweep gap, atomic parameter cut via permissionless execute, frontend projection omitting LTV capacity.
- Info: no public HTTPS deployment; launch.json zero-address roles that all three constructors reject.
Dropped: the economics specialist's invariant-coverage claim, because
test/ThreeReserveInvariants.t.solalready exercises multi-asset borrow, finalizeDust and coverBadDebt. The alternative floor proof that hard-codesminPrice == 0as the fix was replaced by the fix-agnostic one.My own pass over the remaining entry points (schedule, accrue, repay, donate, coverBadDebt, configureReserve, setCollateralEnabled, setReserveFrozen, withdraw) found no additional defects. Each row carries a one-line reason.
ran onclaude · claude-fable-5-1 · 33 turns · 10m 1s · 386 in · 49.4K out · 1.4M cachedsubmission4500c54fcbb5d2d47d51da4470eb512c08ca733ea0ccede2f1761e246e61abc1device22c9b760d0a15149e66c355d3badb12ec2af1f24a269554b3481bd43c2a15408started from3a1bd36ec61908d89a83bb0450f6b17038cf9f9bbundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424cc, c2e7933221a6e80872118673ded0c5b1753bce90741d11aeb5f033cb64bf1f02, a8df1a27c663f6ec376110f0a768490cce75000d39ac33577152b064664b8a43changed · 0 filesnothingmediumHard oracle price band rejects a valid two-feed-agreed collateral crash (or debt spike), so liquidation, finalizeDust, borrow and debt-bearing withdraw all revert exactly when loss recognition is needsrc/RiskOracle.sol:108
proof · a Foundry test the fix has to passmediumNo minimum position size: a one-base-unit debt doubles after one second of accrual, is unreachable by both liquidate and finalizeDust in a price band, and then a $0.000002 loss freezes the whole bank src/IMDBank.sol:510
proof · a Foundry test the fix has to passmediumRiskOracle guardian is immutable and the production wiring makes the same emergency key the timelock canceller, so one key can keep liquidations and loss recognition blocked indefinitely with no goversrc/RiskOracle.sol:32
proof · a Foundry test the fix has to passmediumOracle maxAge is hard-capped at exactly 1 day with no grace, equal to the 86,400 s heartbeat of the selected USDT/USD feed, so stablecoin-debt positions are unliquidatable for a window every daysrc/RiskOracle.sol:67
proof · a Foundry test the fix has to passShared supply cap can be filled at zero cost by a debt-free depositor, blocking every borrower's collateral top-up ahead of a liquidation the filler can then execute and unwindsrc/IMDBank.sol:175
Liquidation seizure values debt at the HIGH feed and collateral at the LOW feed, so the realized liquidator premium is bonus x (1+devCollateral) x (1+devDebt) and exceeds the 15% liquidationBonusBps bsrc/IMDBank.sol:475
Collateral-limited dust sweep is skipped when seized floors to zero, leaving a position that neither liquidate nor finalizeDust can clearsrc/IMDBank.sol:477
_liquidationQuote computes seized = floor(seizedValue * collateralUnit / collateralPrice). When one wei of IMD is worth more than paid x debtPrice x 1.08 (collateral price near the 1e27 MAX_PRICE bound with a few wei of collateral), seized rounds to 0 while paid > 0.
The dust sweep that would hand over the whole residual requires seized != 0, so the quote falls through to 'if (paid == 0 || seized == 0) revert Dust()'. finalizeDust then rejects the same account because _requireUnliquidatableDust sees paid != 0 (a share is affordable). The account is unreachable by both paths.
The precondition is only reachable at the accepted price boundary, so this is a logic gap with a trivial fix rather than a live loss: drop the 'seized != 0' conjunct (the paid == 0 check after it still protects the liquidator).
Risk-parameter cuts apply atomically on permissionless timelock execution, so any searcher can execute and liquidate positions that were healthy one call earlier in the same transactionsrc/IMDBank.sol:389
Frontend projected health factor omits the LTV capacity check the contract enforces on borrow and withdraw, so a projection shown as safe is rejected on chainweb/core.js:68
projectedHealth reproduces accountData's threshold-based health factor (35%), and app.js line 218 only marks the projection as dangerous when it is below 1.0. IMDBank._requireBorrowSafe rejects borrow and debt-bearing withdraw when debtUsd > borrowCapacityUsd, which uses ltvBps (25%). Every borrow or withdrawal that leaves the account between HF 1.0 and HF 1.4 (threshold/LTV) is displayed as a non-danger projection and then fails with UnsafePosition at simulation.
The review dialog's pre-checks (cash, supplied balance, wallet balance) do not include capacity either. No funds are at risk because the contract is the source of truth; the displayed number cannot tell users the real rejection boundary.
Fix: compute remaining capacity (ltvBps) in projectedHealth or review() and flag actions whose projected debt exceeds projected collateral * ltvBps / 10000.
Required public HTTPS frontend deployment is absent; the repository states the website is not deployed and web/config.json is unpopulateddocs/DEPLOYMENT.md:5
The brief requires the frontend to be hosted as a usable public website with a final public HTTPS URL and states deployment is not complete if the frontend only runs locally. docs/DEPLOYMENT.md records that no public URL exists, web/config.json has bankAddress/oracleAddress/code hashes null with deploymentStatus NOT_DEPLOYED, and WalletConnect is documented as not implemented.
This is a completeness gap against the stated acceptance criteria rather than a code defect; the frontend correctly refuses to invent addresses when unconfigured.
launch.json passes the zero address as timelock canceller and as oracle/bank guardian, which all three constructors reject, so the manifest cannot deploylaunch.json:8
The manifest's second constructor argument for GovernanceTimelock, RiskOracle and IMDBank is the zero address. GovernanceTimelock's constructor reverts InvalidOperation when canceller_ == address(0) (src/GovernanceTimelock.sol line 25), and RiskOracle/IMDBank revert InvalidConfiguration when guardian_ == address(0) (src/RiskOracle.sol line 44, src/IMDBank.sol line 141).
The notes field already declares the manifest a blocked draft awaiting an authorized emergency multisig; recorded here so the judge tracks that the deployment prerequisite is unmet and that the replacement address must be distinct from $owner (finding 3 additionally recommends it be distinct from the timelock canceller).
Deploy the manifest as written: new GovernanceTimelock($owner, 0x0, 172800) reverts InvalidOperation() at the canceller_ == address(0) check, so $contract:GovernanceTimelock never resolves and RiskOracle/IMDBank (which would also revert on guardian_ == address(0)) are never reached.
Expected: a deployable manifest with a nonzero, authorized emergency address.
Actual: factory rehearsal fails at the first constructor.
- Medium, oracle band (merged economics + math): a valid two-feed crash below the mandatory collateral
- updated
#1239Build contract projectClaude29 files changed
All checks pass on the revised tree, so the revision is complete.
Outcome. All four reproducible findings reproduced on the starting tree (each proof failed) and are fixed. Every proof now passes, the committed suite passes, the formatter passes, and the responses file is written.
Check Result forge test(committed suite, no scratch)52 passed, 0 failed Reviewer proofs under test/scratch/4 passed forge build,forge fmt --checkpass Frontend unit tests, ABI compatibility, bytecode policy 13 passed, 34 signatures matched, no forbidden opcodes Foundry fork tests and frontend fork replay 3 passed, 46 receipts at fork block 26,134,418 What changed, by finding
- Oracle band (R2-01): bounds now reject only the direction that overvalues a borrower. Collateral feeds are capped by
maxPrice, debt feeds floored byminPrice, and a zero floor is allowed. - Minimum debt and dust losses (R2-02): borrowing must leave at least $1 of debt per asset. Write-offs only halt the reserve and bank when a reserve's recorded loss exceeds $1. Unfreeze gates use that halt flag, so dust finalizations can no longer block a scheduled unfreeze.
- Guardian powers (R2-03): a guardian feed pause is time-boxed and single-use until governance re-arms it, governance can rotate the oracle guardian, and the deploy rehearsal requires three distinct multisigs with the veto key separate from the guardian.
- Feed age (R2-04): the per-feed maximum age bound is now 48 hours so heartbeat plus grace can be configured.
- Advisories: the zero-wei seizure sweep and the frontend LTV capacity check are fixed with regressions. The supply-cap fill, feed-spread premium and parameter-race items were reproduced in scratch tests but kept as design, with the trade-offs documented. The public hosting gap stays open because hosting credentials and a confirmed address set are not available here.
Documentation and evidence. ARCHITECTURE, DEPLOYMENT, SECURITY_REVIEW (new round-2 section), VALIDATION, FRONTEND and README are updated. Evidence files were regenerated, and the Foundry fork test was re-pinned to block 26,134,418 because the public RPC no longer serves the old block's state.
Not done. No public HTTPS URL exists. The fork validation ran only on a local Anvil fork, and no browser automation or WalletConnect connector was added.
ran onclaude · claude-fable-5-1 · 118 turns · 21m 53s · 994 in · 103.1K out · 6M cachedsubmission859d72692eec08f9913f88c9a85f730008c30531b947a3731409444751a95ed3device3baa2091028c32c1a8e5f26a1e8fe0d5a29ce61044b8dd7451fe26e11065a1adstarted from6ca1f036ccc9466a456fa6fac067c50383f0420fbundled3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424cc · 410 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 29 filesREADME.mddocs/ARCHITECTURE.mddocs/DEPLOYMENT.mddocs/FRONTEND.mddocs/SECURITY_REVIEW.mddocs/VALIDATION.mddocs/evidence/bytecode-check.txtdocs/evidence/forge-build.txtdocs/evidence/forge-tests.txtdocs/evidence/frontend-abi-check.txtdocs/evidence/frontend-fork-round1.jsondocs/evidence/frontend-fork.jsondocs/evidence/frontend-unit-tests.txtdocs/evidence/independent-review-checks.jsondocs/evidence/mainnet-fork-tests.txtscript/DeployMainnet.s.solsrc/IMDBank.solsrc/RiskOracle.soltest-fork/Mainnet.t.soltest/Audit.t.soltest/DeploymentRehearsal.t.soltest/IMDBank.t.soltest/Invariants.t.soltest/RiskControls.t.soltest/RiskOracle.t.solweb/abi.jsweb/app.jsweb/core.jsweb/tests/core.test.mjs - Oracle band (R2-01): bounds now reject only the direction that overvalues a borrower. Collateral feeds are capped by
- updated
#813ManifestCodex1 file changed
afterBuild contract projectwrites tolaunch.jsonReproduced the failure, corrected
launch.json’s role notes, and recorded the unresolved finding in.imd-responses.json.forge buildpassed with warnings; all 56 tests passed.The manifest remains non-deployable. Completion requires authorized canceller and guardian addresses, distinct from each other and
$owner; none were supplied.ran oncodex · gpt-6-astra · 5 turns · 4m 27s · 52.1K in · 5.8K out · 355.3K cachedsubmission7cd29902101eb209fb89c69a9dfc06705560795999f59b6fbd85d05c161f4e63device8f87b0b1cd164f2bcf8366ab985b673c726e65f54b45b6ae486c077da3f41772started from4c2c5c7660b92d937a7f4ce984c4dae7601aef06bundlea8df1a27c663f6ec376110f0a768490cce75000d39ac33577152b064664b8a43 · 413 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 1 filelaunch.json - updated
#1498Write foundry testsCodex4 files changed
afterBuild contract projectwrites totesttest/**Updated tests only under
test/, preserving existing coverage and adding regressions for minimum debt, loss halts, oracle price moves, and guardian-pause expiry.forge build: passed.forge test: 101 passed, 0 failed, 1 skipped.- Invariants: 24,576 calls, zero handler reverts.
Mainnet fork remains unverified without an RPC. No new reproducible contract defects found.
ran oncodex · gpt-6-astra · 5 turns · 8m 58s · 243.7K in · 12.7K out · 1.4M cachedsubmission7f8db58646b1b068377bc70993767061dea354f2e52da4526f1ee9a0520aebb3device9a29c101a23c14cc1a27a875f2c1b5eceaf7ebad46ca78d581e333462a34af88started froma7c9ec44e560c12119b59a4190ab3041d0efb11dbundlec2e7933221a6e80872118673ded0c5b1753bce90741d11aeb5f033cb64bf1f02 · 434 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424ccchanged · 4 filestest/ADDED_TEST_COVERAGE.mdtest/AccountingEdges.t.soltest/OracleIntegrationEdges.t.soltest/ThreeReserveInvariants.t.sol - reviewed
#535Audit judgeClaude5 findings · 3 low
afterBuild contract project, Write foundry tests, Manifest, Audit math, Audit permissions, Audit economics, Audit flowReview complete. The findings file is written at
.imd-findings.jsonwith 5 entries and a 24-row coverage record.All four medium findings from last round are fixed. Each attached proof now passes on the revised tree, and my own scratch test confirms the behaviour the author described:
- Oracle price band now rejects only the direction that would overvalue a borrower. A collateral crash through the old floor and a debt spike through the ceiling are priced as reported, and a zero floor is accepted.
- Minimum debt and loss halt. A borrow must leave at least $1 of debt in that reserve. Dust write-offs are recorded without halting, and lending halts only once a reserve's recorded loss exceeds $1. Unfreeze is gated on that halt flag, which clears only at full cover, so a dust finalization can no longer make a scheduled unfreeze revert.
- Guardian pause is bounded. The oracle guardian gets one time-boxed pause per governance decision, governance can rotate the oracle guardian, and the deploy helper now requires three distinct multisigs.
- Feed age bound raised to 2 days, so heartbeat plus grace can be configured.
The low seized-to-zero sweep gap and the frontend capacity projection are also fixed. The full project suite passes with 101 Foundry tests and 13 frontend tests.
What remains in the file is unchanged, accepted residual risk, kept at low or info so the record is complete rather than to reopen work:
- Supply cap fillable by a debt-free depositor, feed-spread liquidation premium, and atomic parameter-cut execution. The author reproduced each, chose to keep the design, and documented it. I re-ran the first and third on the revised code and they still reproduce.
- No public HTTPS deployment, and launch.json still carries zero addresses for the veto and guardian roles. Both are input gaps that need the network owner to supply hosting and two authorized multisigs. One note for the judge: the contracts cannot enforce that veto and guardian differ, so whoever fills the manifest must honour the separation that the deploy helper and notes require.
I found no new reproducible defect in the changed lines. Nothing outside
test/scratch/and the findings file was modified.ran onclaude · claude-fable-5-1 · 29 turns · 7m 1s · 386 in · 28.9K out · 1.4M cachedsubmission04fda0f3980bca86ec1fb47246eaa64402a4ecf5ed999e3f9e18ac1746ce9f2adevice3516474d8a268bd881d353f80dfd9aac9f71259a623f003d76ecd829ffca4e10started fromd90fc149bc4b2afe51a424d7ed0af7f18cbe7735bundlenoneapplied ond3ae7a425afc52f763ff6032cd08adb0630201a478db448cf4164629d0f424cc, c2e7933221a6e80872118673ded0c5b1753bce90741d11aeb5f033cb64bf1f02, a8df1a27c663f6ec376110f0a768490cce75000d39ac33577152b064664b8a43changed · 0 filesnothingAccepted residual (unchanged, documented): shared supply cap can be filled at zero cost by a debt-free depositor, blocking every borrower's collateral top-up ahead of a liquidationsrc/IMDBank.sol:184
Round-2 settlement of prior finding 43d27997. The author reproduced it and chose not to change the cap semantics (an exemption for indebted accounts would let any account with $1 of debt bypass the custody cap); ARCHITECTURE.md and DEPLOYMENT.md now state that cap headroom is fillable by a debt-free depositor and that governance must size the cap with rescue headroom.
The mechanism is unchanged and still reproduces: supply() applies one shared cap to top-ups by accounts that already carry debt, while a debt-free depositor can hold the headroom at no cost and withdraw at any time (debt-free withdraw skips oracle, freeze and health checks). Severity stays low (needs capital equal to the headroom, mirrors Aave cap semantics, borrower keeps repayment as a defence).
Recorded for the judge as an accepted, documented residual; no change is requested for admission.
Accepted residual (unchanged, documented): liquidation seizure values debt at the high feed and collateral at the low feed, so the realized liquidator premium is bonus x (1+devCollateral) x (1+devDebtsrc/IMDBank.sol:493
Accepted residual (unchanged, documented): risk-parameter cuts apply atomically on permissionless timelock execution, so any searcher can execute and liquidate positions that were healthy one call earsrc/IMDBank.sol:406
Round-2 settlement of prior finding aa6de509. The author confirmed the mechanism and did not add an activation ramp: GovernanceTimelock.execute (src/GovernanceTimelock.sol line 55) has no caller restriction by design (restricting it to the proposer would let a slow or lost proposer key expire every proposal), and configureRisk writes the new ltvBps/liquidationThresholdBps immediately for existing positions.
ARCHITECTURE.md now states that whoever executes a scheduled cut picks its timing and may liquidate in the same transaction, and DEPLOYMENT.md instructs operators to publish scheduled changes to borrowers at scheduling time. Still reproduces on the revised tree.
Severity stays low: governance-scheduled, two-day public notice, borrower can act in the window. Recorded as an accepted, documented residual; no change is requested for admission.
Completeness gap (unchanged): the required public HTTPS frontend deployment is absent; docs/DEPLOYMENT.md states the website is not deployed and web/config.json is unpopulateddocs/DEPLOYMENT.md:5
Round-2 settlement of prior finding c0655c7b. The author agrees it is a completeness gap and states it cannot be closed within the assignment: hosting credentials and the confirmed Mainnet contract address set from the launch handoff do not exist, and the frontend deliberately refuses invented addresses.
The frontend-to-contract flow was re-validated on a local Anvil mainnet fork (web/tests/fork-integration.mjs, docs/evidence/frontend-fork.json at block 26,134,418) and the 13 frontend unit tests pass on this tree (node --test web/tests/core.test.mjs). The brief's acceptance criterion of a working public URL remains unmet; this is not a code defect and depends on later deployment-stage inputs. No change is requested of the contract code.
Deployment prerequisite unmet (unchanged): launch.json passes the zero address as timelock canceller and as oracle/bank guardian, which all three constructors reject, so the manifest cannot deploy untlaunch.json:8
Round-2 settlement of prior finding 04869145. Constructor arguments are unchanged; the notes were corrected to require two authorized nonzero multisigs (a veto multisig for GovernanceTimelock.constructorArgs[1] and an emergency multisig for RiskOracle.constructorArgs[1] and IMDBank.constructorArgs[1]), distinct from each other and from $owner, and to explain why the veto and guardian roles must not be the same key.
This is the right call: no address was supplied and substituting an invented one would hand control to an unauthorized party. Note for the judge: the constructors themselves cannot enforce veto != guardian (GovernanceTimelock does not know the guardian); that separation is enforced only by script/DeployMainnet.s.sol lines 28-32, the rehearsal test and the manifest notes, so whoever fills the manifest must honour it. The remaining blocker is an input gap, not a code defect.
Deploy the manifest as written: new GovernanceTimelock($owner, 0x0, 172800) reverts InvalidOperation() at the canceller_ == address(0) check (src/GovernanceTimelock.sol line 25), so $contract:GovernanceTimelock never resolves; RiskOracle (line 56) and IMDBank (line 149) would also revert InvalidConfiguration() on guardian_ == address(0).
Expected: a deployable manifest with two nonzero, authorized, mutually distinct role addresses.
Actual: factory rehearsal fails at the first constructor.
- publishedidentity-md-launches/launch-813-imdbankpull request
- deployedProtected_invariants: invariants-11aebc2aca1e: [FAIL: application constructor failed] setUp() (gas: 0); [FAIL: application constructor failed] setUp() (gas: 0).
how it was checked
- rebuilt
- GovernanceTimelock, IMDBank, ExactToken, RiskOracle · verifier 0.1.0 · solc 0.8.26
- gates
- 6 of 7 passed
- provenance
- findings
- independent review
- bytecode
- manifest
- protected invariants
- economics
- parked
- protected_invariants: invariants-11aebc2aca1e: [FAIL: application constructor failed] setUp() (gas: 0); [FAIL: application constructor failed] setUp() (gas: 0)
- proof
commit, attestation, manifest, tree, per-contract hashes
- repository
- identity-md-launches/launch-813-imdbank
- commit
- 2af0ef2b14568887541fa347d1dc9eacfd6e6c5e
- attestation
- 4298252cd5b9e44404aff73506eabf0b6fa46bbae2cbd8e9d12f134898a2f4ef
- manifest
- 5be4bf1722f65df7f4d6091cb0822efe6b9876f0e0004a18bdb6d822acc60c59
- constructor
- GovernanceTimelock: $owner, 0x0000000000000000000000000000000000000000, 172800
- constructor
- RiskOracle: $contract:GovernanceTimelock, 0x0000000000000000000000000000000000000000
- constructor
- IMDBank: $contract:GovernanceTimelock, 0x0000000000000000000000000000000000000000, 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7, $contract:RiskOracle, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xdAC17F958D2ee523a2206206994597C13D831ec7, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
- tree
- 728cdfd6f78cc8145d24149a992e7685d20be137
- compiler
- solc 0.8.26, optimizer 200 runs, reproducible
- contract
- GovernanceTimelock
src/GovernanceTimelock.sol · 2273 bytes
creation 5bd67decfe50a861aaa6d395997d6d2baab34172c25d5229ea07d2e8b1d5ecaf
abi 1885305515c864d26646db0f96e7aaa526689b9b96deeef9dc8369dcd4dd52ed
metadata 2a048bf83e7bb53c47c85b21872095bba08da57676f248a853b311379ad750e2 - contract
- IMDBank
src/IMDBank.sol · 14584 bytes
creation 60a34363ba3603ab0c88df7633e952540530fc7c1037e2d412e7ea46d93d1745
abi 73934adbbc296545fe1571b43ba4f9f3c3756b40d665c2aee046bfd87c6be818
metadata 3364dd3fe8644e17309b94ab30f533abb8d5e75c7df326a0db70bb8a8a1813df - contract
- ExactToken
src/lib/ExactToken.sol · 94 bytes
creation 03f00af6a2c1e216c5142290f5a7c5a73b7dca9ff4182f298fb7a6b46fc82bef
abi f2ab5f21ac7695e05cfe987afcae963ee8f2bb28feb90ab90ca59e8fd25629d8
metadata bdcabb26f802baa057cc2bc39cde9cd54b71cc5ca493e9bfa7e62930adbab3ad - contract
- RiskOracle
src/RiskOracle.sol · 5481 bytes
creation 655bbcff3708ba6c52a32c45a0cc6ae3bfe4d7ffa6251dde021ad1856db3f5f9
abi b855252a940ac176821fcc1443a066c94174e454392f0aa39363454bbe5ddf04
metadata 03fe64cef1b36a499af1d798344cbc6a76d33ec7a2edb646781d63809994179a
- onchain