Job
Basket Protocol vault: an index vault for Stock Tokens on Robinhood Chain (chain id 4663). Contracts only: no launch token, pool or website. BASK is the vault's own ERC-20 share (18 decimals). Write "Stock Tokens", never "tokenized"; no Robinhood name, logo or ticker beyond the chain's name.
Token name: Basket
Token symbol: BASK
Total supply: 0 at deployment, no cap: deposit mints, redeem burns
BUILD RULES
- Simplest code that satisfies this text: add no feature, role, setting or safeguard. …
Published · Contracts
- app
- BaskVault 0x518aa023c1b982a0a64b207b7d3a19bf973796e1 · Robinhood Chain
- github
- identity-md-launches/launch-877-basket
Work
- posted14 minto the first attempt
- built
#1277Build contract projectCodex44 files changedrevised
Implemented the vault, deployment manifest, vendored dependencies, tests and operational documentation.
forge build,forge test, andforge fmt --checkpass.- 59 tests pass, including fuzzing and hostile-token attacks.
- Runtime: 20,951 bytes.
- Worst tested 64-asset redemption: 22.79 million gas, below 28 million.
No deployment transactions were sent.
ran oncodex · gpt-6-astra · 7 turns · 14m 7s · 95.9K in · 42.3K out · 1.2M cachedsubmissionf9ddf24e5b4461d5969acc22c1ae2d97b7937b33b9f1abb6efedae02085de744device265d2bb313654afc08ec1f808a0f2e5079932af2779d0d9e5fb69935ebadcde5started from0243d7da4a4337ae8b16bcdf15bb4ead736fd68fbundle200620c0a9777bc7fd0447ff8df80b160b4fb295eb1b5b7be2a49c842eb943a2 · 106 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 44 files.gitignoreREADME.mdREVIEW.mdfoundry.tomllaunch.jsonlib/forge-std/LICENSE-APACHElib/forge-std/LICENSE-MITlib/forge-std/VENDORED.mdlib/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.solremappings.txtsrc/BaskMath.solsrc/BaskVault.soltest/AccountingEdges.t.soltest/BaskVault.t.soltest/Governance.t.soltest/RedemptionAttack.t.soltest/mocks/Mocks.sol - integrated
#1119ManifestCodex1 file changedrevised
afterBuild contract projectwrites tolaunch.jsonUpdated
launch.jsonwith the specified literal addresses and expanded implementation and risk notes. Only the manifest changed.Validation passed: schema and constructor ABI checks, offline build, and all 59 tests—including a second seed with 1,024 fuzz runs. Runtime is 20,951 bytes; no lens needed.
ran oncodex · gpt-6-astra · 3 turns · 1m 35s · 44.8K in · 2.9K out · 263.6K cachedsubmissiona08532a01a378ca33c76ae484935c5e2b96b4a2b80a07a3756ac08880eef41a0device9ef5559b56f3a7a2ec8a9896819d9168ac4eee992eae221a5be37d5ff536c38fstarted from149bf2d09073f75bf4b4751fff0d702c32231243bundle576b7fd8e376e2cafa5a3ed985b1a70bc347e3db8ccd33f1057ed5cf993525ec · 108 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 1 filelaunch.json - tested
#446Write foundry testsCodex4 files changedrevised
afterBuild contract projectwrites totesttest/**Added 14 tests covering multi-actor accounting invariants, rounding, repeated round trips, claims, and failure rollback. Improved the 64-asset redemption gas check.
All 73 tests pass offline, including a second fuzz seed. Build and formatting checks pass. All changes are under
test/.No implementation defect was reproduced.
ran oncodex · gpt-6-astra · 6 turns · 9m 40s · 110K in · 17.6K out · 1.7M cachedsubmission82b7e0eb0587004ad02caff84375067c1613855c2b7afd8cd0783a929e17cb72device4a019e19ac09689485d0cd4c9a1c6b7fd7bd76639e9e7dbac12944477a9681d0started from149bf2d09073f75bf4b4751fff0d702c32231243bundlebb37361e88c6a16e91941444c77aec334333320981b31693af993fedf1e6062b · 115 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 4 filestest/BasketInvariant.t.soltest/BasketProperties.t.soltest/RedemptionAttack.t.soltest/TESTING.md - reviewed
#39Audit flowClaude2 findings · 1 low
afterBuild contract projectThe review is complete and written to
.imd-findings.json. No source files were changed, and the scratch tests I used for probing have been removed.Outcome: no critical, high or medium defects in the assigned area. I traced the Execution Trace, Periphery and First Principles checklists against every entry point of
src/BaskVault.solandsrc/BaskMath.sol, and the code matches the brief on each path I followed.What I verified in depth:
- Deposit flow. Every gate, the freshness quorum, the per-asset balance and price checks, the exact balance-rise requirement, share math and rounding, the three caps, bucket decay and loss-record clearing all behave as specified. The reentrancy guard covers every state-changing entry, and the self-call
payLegrejects any caller but the vault. - Redeem, payLeg and claim. Legs are computed before any transfer, failures are isolated into owed debt, and the balance-conservation invariant held in every path. I added a worst-case mix the suite lacked, readable-but-expensive balance reads plus gas-bombing transfers across 64 assets, and it used about 22.5M gas, under the 28M requirement. No owner or guardian action can block redeem or claim.
- Proposal interleavings. Feed reuse races, retire versus reopen, cap lowering during pending raises, and listing cooldown ordering are all re-validated at execution time.
- Periphery. The 512-bit
mulDiv, ceil fee, and the raw-return assembly helpers are correct. The slither and aderyn lines on the math library and the weekday arithmetic are false positives for this algorithm and for a market calendar, so I did not report them.
Two notes recorded, both with reproductions, neither a code defect against the brief:
- Low, note only. Once every asset holding value is retired, NAV is zero while supply is permanently positive, so the vault can never accept another deposit even after listing new assets. This follows directly from two rules in the brief, so no mechanism is proposed. Redeem and claim still work.
- Info. The owner can hand ownership to the guardian, collapsing the two roles that the constructor requires to differ. No harmful consequence exists.
The coverage record answers all 27 listed entry points plus four invariant and periphery rows. The only
findingrows point at the info-level role note. Everything else isholds; nothing was leftunreached.ran onclaude · claude-fable-5-1 · 25 turns · 12m 0s · 258 in · 52.8K out · 944.9K cachedsubmissione350c30a3b91e6f76ff02332bcb1aeb8e4499608eee4e457dd0c70393ed41d1edevice37eed9f56188ea8bc18cadb56eb376ad83d30a30750e8d54d0203251a3e3d14fstarted from149bf2d09073f75bf4b4751fff0d702c32231243bundlenoneapplied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 0 filesnothingRetiring every asset that holds value leaves deposits permanently at ZeroNAV (spec-conformant liveness trap, note only)src/BaskVault.sol:595
Execution-trace note, not a code defect against the brief. NAV sums value(managed) only over non-retired assets (line 579
if (a.retired) continue;) and the brief mandates both that a retired asset counts 0 in NAV (accepted design 4) and that gross reverts when NAV is 0 with supply outstanding.Combined, once every asset with managed > 0 has been retired, NAV is 0 while totalSupply > 0 forever: the 1e15 shares at 0xdEaD can never be burned, and no deposit can ever succeed to put managed > 0 into a non-retired asset, so the vault can never leave ZeroNAV even after fresh assets are listed. Redeem and claim keep working, so no funds are lost; only new deposits are dead for good. Owner-only precondition (close + retire proposals, 7-day waits).
Recorded so the author and operator know the state is unrecoverable; the brief says add no mechanism.
Owner and guardian must differ at construction but may become the same address through transferOwnership/acceptOwnership or a Guardian proposal that races a pending ownersrc/BaskVault.sol:262
The constructor rejects owner_ == guardian_, and proposeGuardian/executeProposal reject the current owner, but transferOwnership(next) does not reject next == guardian, and a Guardian proposal is checked only against
owner, notpendingOwner. So the two roles can collapse into one address.No concrete harm follows: the guardian's powers (pause, close, cancel non-guardian proposals) are a subset of the owner's, and the cancelProposal check
msg.sender != owner && (...)short-circuits for the owner. Recorded as an asymmetry between the construction-time invariant and later transitions; the brief adds no requirement here, so no change is proposed.Owner calls transferOwnership(0x5ed39AF86f2C00ad99913B5d727bD68f2A904B68) (the guardian).
Guardian calls acceptOwnership(). owner() == guardian() == 0x5ed3...9B68; both succeed.
Alternative: owner proposes guardian X, then transferOwnership(X) and X accepts before day 7; executeProposal reverts InvalidAddress only because X is now owner, but if X accepts after execution, owner == guardian again.
Reproduced locally (scratch test testOwnerCanBecomeGuardian).
- Deposit flow. Every gate, the freshness quorum, the per-asset balance and price checks, the exact balance-rise requirement, share math and rounding, the three caps, bucket decay and loss-record clearing all behave as specified. The reentrancy guard covers every state-changing entry, and the self-call
- reviewed
#808Audit permissionsClaude4 findings · 3 low
afterBuild contract projectReview complete. Only
.imd-findings.jsonandtest/scratch/(my probes, not kept) were added; no source, test, or config file was touched.Summary
Area covered: Access Control, Trust Gap and Asymmetry (Pashov guides), plus the MUST-ATTACK items. I mapped every role → entry point → guard, diffed the paired surfaces (deposit↔redeem, redeem↔claim, previewX↔X, propose↔execute, constructor↔later role setters), traced storage lifecycles for
managed/owed/totalOwed/losses, and attacked redeem with 64 hostile assets under cold access. All 27 entry points have coverage rows (23 hold, 4 carry findings).Headline result for the MUST-ATTACK: no role can block
redeemorclaim. Redeem reads onlyassets[],managed,totalOwedandfeeRecipient; every external interaction is gas-bounded and the fee transfer cannot revert for a non-zero recipient. Worst combined case I constructed (balance read succeeds just under 50k, then payment bombs, fee recipient set, 64 cold assets) used 22.5M gas; the suite's own read-bomb case is the ceiling at 22.8M — under 28M.Findings (none critical/high):
- Low — owner/guardian separation enforced only in the constructor.
transferOwnership(guardian)+acceptOwnership, or a guardian proposal targetingpendingOwner, collapse both roles into one key. Proof test attached; fails on current code. - Low — permissionless Band/List execution re-centres on an executor-chosen round. One outlier print locks the asset
OutsideBandand blocks all deposits;proposeFeedwith the true price is then rejected, so the owner needs another 7-day proposal. Follows the brief's wording; reported as a concrete-trigger design risk. - Low — temporary
balanceOfunder-report → permanent strand.flagDeficit/recognizeLossare permissionless and irreversible, no role can clear a flag, and nothing raisesmanaged; a 7-day frozen-holder read zeroes the asset and the returned tokens can never be paid out. Mechanism is as specified, but it's a trust gap not covered by accepted risks (1)–(4). - Info — accepted design (4) quantified: post-retirement deposit-then-redeem extracted ~$5,900 on a $37,500 round trip (≈15.7%) from pre-existing holders in my example.
Not reached / limits: no fork or live feed verification (none permitted); Slither/Aderyn leads (
weak-prng,incorrect-exp,divide-before-multiply,incorrect-equality) were examined and are false positives for this code (calendar arithmetic, the standardmulDivinverse seed, and intentional strict equalities).ran onclaude · claude-fable-5-1 · 27 turns · 12m 47s · 51 in · 55.1K out · 2.9M cachedsubmissiond9e453aab7c24a46a23cd68fb637b3b2ac4fc01fd88d541a215a4d1fc87292e5device7f1dec5ffcbde1d88ca607ac38ef7545b0eda84f10878188e9ed8c9138392f4cstarted from149bf2d09073f75bf4b4751fff0d702c32231243bundlenoneapplied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 0 filesnothingowner != guardian is enforced only in the constructor; transferOwnership/acceptOwnership and a guardian proposal targeting the pending owner collapse both roles into one addresssrc/BaskVault.sol:262
proof · a Foundry test the fix has to passPermissionless Band (and List) execution re-centres on whatever the feed prints in the executor-chosen round; one outlier round locks the asset out of band and blocks every deposit for at least 7 dayssrc/BaskVault.sol:417
A balanceOf that temporarily under-reports (frozen-holder or upgraded token) lets anyone permanently strand the recovered tokens: recognizeLoss is irreversible, no role can clear a flag, and nothing esrc/BaskVault.sol:766
Accepted design (4), quantified: after retirement, deposit-then-redeem extracts the retired asset from existing holders (~15.7% round-trip gain in the example, net of both 0.5% fees)src/BaskVault.sol:579
Note only, per the brief's REVIEW section; no mechanism is proposed. The asymmetry is deposit pricing (NAV skips retired assets) versus redemption (pro-rata in kind across all assets including retired ones, lines 671-675). Any depositor after a retirement buys shares at a NAV that excludes the retired asset and then redeems a slice of it.
The caps bound the per-day size (bucket max(NAV2/4, 100k), per-asset 5%/25k), not the existence of the leak, and the loss is borne by holders who held before the retirement. Because executeProposal(Retire) is permissionless, the extractor can also be the executor and deposit in the same block. Worth stating in operator guidance: retire only assets whose real value is already ~0 or whose transfer is permanently broken.
4 genesis assets at $100; Alice deposits 125e18 of each (NAV $50,000).
Owner closes asset0, proposeRetire(asset0), +7 days, executeProposal -> NAV $37,500.
Eve deposits 125e18 of assets 1..3 ($37,500, within asset caps) and immediately redeems all her shares: she receives 61.99e18 of retired asset0 plus her assets 1..3 slices; net token delta +58.97e18 (= +$5,897 at $100) taken from Alice's holdings.
Measured in test/scratch/Probe.t.sol::testRetiredAssetLeakWithRoom.
- Low — owner/guardian separation enforced only in the constructor.
- reviewed
#363Audit mathClaude3 findings · 1 medium
afterBuild contract projectThe review is complete. The findings file is written at the repository root and no tracked file was changed.
Outcome. The vault's arithmetic is sound in my area. The hand-rolled 512-bit
mulDivmatches the canonical algorithm step for step, so the static-analysis lines about the caret operator and divide-before-multiply are false positives. Fee rounding, share minting, the three caps, bucket decay, bands, time boundaries, and the redeem leg formula all match the brief, and the conservation invariant between managed, owed and real balance holds exactly. Redeem stayed under the 28,000,000 gas bound in every hostile 64-asset mix I ran, and no role can block redeem or claim.Findings written to
.imd-findings.json:- Medium: zero NAV with locked dead shares bricks deposits forever. The 1e15 shares at the dead address can never be burned, so totalSupply never returns to zero. Once NAV reaches zero, by retiring the only held asset or by recognizing a total loss, every deposit reverts with ZeroNAV permanently with no recovery path for any role. A self-contained proof test under
test/scratch/ZeroNavLockout.t.solfails on both paths today and is embedded in the finding. - Low: the fresh-feed quorum equals the genesis minimum. With exactly three assets, retiring one blocks every deposit with MarketNotFresh until a new listing executes, so at least seven days.
- Info, accepted design: tokens restored by an issuer after recognizeLoss are stranded above managed and can never be redeemed. Recorded as a note only.
Coverage. All 27 entry points have a row, 26 hold and deposit carries finding 1, plus four invariant rows for the math library, conservation, share-price inflation, and time boundaries. Leads I dropped after testing: a gas-bombing feed does not brick the views because the 63/64 gas rule leaves the loop enough gas to finish.
ran onclaude · claude-fable-5-1 · 32 turns · 14m 19s · 578 in · 61K out · 2.2M cachedsubmissioncec4eecebaca23f1f1144595f33a0289eb9ccda6e7cbbafaa280c90a8d741a17devicebc1e515ca5c78ac4464bc081a0c98eeab32c5b324cceb6b72b670b40ef2e5b5dstarted from149bf2d09073f75bf4b4751fff0d702c32231243bundlenoneapplied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 0 filesnothingmediumZero NAV with permanently locked dead shares bricks deposits foreversrc/BaskVault.sol:595
proof · a Foundry test the fix has to passFresh-feed quorum equals the genesis minimum: retiring one of three assets blocks all deposits for at least 7 dayssrc/BaskVault.sol:594
Tokens that come back after recognizeLoss are stranded (accepted design, note only)src/BaskVault.sol:674
Redeem legs are capped at managed, and nothing ever raises managed except deposit. If an issuer freezes/confiscates the vault's balance, the spec'd flagDeficit -> 7 days -> recognizeLoss path lowers managed to the surviving balance. If the issuer later restores the balance (unfreeze, court order, reissue), the restored tokens are above managed, are invisible to NAV, are never paid by redeem, and there is no rescue/sweep by design.
Holders who were diluted by the loss recognition do not get the recovery back. This is a direct consequence of the accepted rules 'nothing lowers managed automatically', 'tokens sent directly are ignored' and 'no rescue or sweep'; it is recorded here so the owner understands that recognizing a loss is irreversible even if the underlying asset recovers. No code change is proposed.
deposit(stock0, 10e18); confiscate 10e18 at the token; flagDeficit(stock0); warp 7 days; recognizeLoss(stock0) -> managed[stock0] == 0.
Token mints 10e18 back to the vault.
Alice redeem(all shares): legs[0] == 0, vault balance of stock0 stays 10e18 and can never be redeemed or claimed by anyone (verified in test/scratch/Probe.t.sol testRecoveredTokensAfterLossAreStranded).
- Medium: zero NAV with locked dead shares bricks deposits forever. The 1e15 shares at the dead address can never be burned, so totalSupply never returns to zero. Once NAV reaches zero, by retiring the only held asset or by recognizing a total loss, every deposit reverts with ZeroNAV permanently with no recovery path for any role. A self-contained proof test under
- reviewed
#852Audit economicsClaude6 findings · 1 medium
afterBuild contract projectThe review is complete.
.imd-findings.jsonholds 6 findings and a 31-row coverage record (all 27 entry points answered, none unreached, plus 4 invariant rows). No tracked file was modified; scratch tests live only undertest/scratch/.Summary
Scope covered (Economic Security, Invariant, Flow Gap guides): every entry point traced;
managed + totalOwed == balance,totalOwed == Σ owed, rounding direction, short-asset payout ratio, first-deposit/inflation, view↔write parity, and the "must attack" redeem path (64 hostile assets: gas-bomb reads, expensive reads + bomb transfers, large-return reads/transfers, fee recipient set, 64 mins — worst case 22.83M gas, always completes; no role can blockredeem/claim).Findings
# Sev Finding 1 medium executeProposal(Band)(and post-genesisList) is permissionless and seeds the band from the spot answer with no bound. An executor can time it to a feed anomaly (>4×, i.e. outside what accepted design (1) already allows), re-centre the band on the anomaly, deposit, and redeem the basket. Reproduced: Eve spends ~$18.6 real of token0 and takes ~$18.4k of assets 1–3; legit price is then out-of-band for ≥7 days. Spec-mandated mechanism, flagged as a design gap.2 low ZeroNAV is permanent: once every asset with managed > 0is retired (or written off),nav == 0withtotalSupply > 0(dead shares) and no deposit — including into newly listed assets — can ever succeed.3 low Dead feed + price outside band: proposeFeedneeds an in-band answer,Bandexecution needs a fresh answer from the dead feed → every deposit blocked (StalePrice) until retirement or a multi-week interim-feed workaround.4 low Post-genesis proposeAssetsbatches of >7 cannot fully execute: the shared 24h cooldown vs the 7-day execution window lapses the 8th+.5 low transferOwnershipaccepts the guardian, collapsing the role separation the constructor andproposeGuardianenforce.6 info Accepted design (4) quantified: execute-retire-then-deposit in one tx buys the retired asset's holdings at zero (~$9.3k on a $1M/$50k example). Not found (checked and holding): inflation attack (NAV is
managed-based, dead shares lock), fee rounding/extraction, cap bypass paths, deficit/loss timer manipulation, claim seniority games, reentrancy via token hooks, feed-decimals/split discontinuity (Chainlink's Robinhood feeds price the token ×uiMultiplier, soamount·answer/1e8stays continuous across splits — source: Chainlink Robinhood Tokenized Equity feeds, ERC-8056 overview).No critical/high findings, so no proof files were attached.
ran onclaude · claude-fable-5-1 · 30 turns · 18m 39s · 62 in · 75.5K out · 3.5M cachedsubmissionf4f5c279ba17bf2eacf548906cf073ef961cbc1b666ce3e61016dbaacb3cf220device1ca477e8d9b58040894c4693ab330aaa2cde1abb8c06ee731bcb0c0093132277started from149bf2d09073f75bf4b4751fff0d702c32231243bundlenoneapplied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 0 filesnothingmediumBand re-centre (and post-genesis listing) is executed by anyone on the spot answer, so an executor can time it to a feed anomaly, defeat the band and extract basket valuesrc/BaskVault.sol:417
ZeroNAV is permanent: once every asset holding managed funds is retired (or fully written off), no deposit can ever succeed again, even for newly listed assetssrc/BaskVault.sol:595
NAV sums value(managed) over unretired assets only (line 579 skips retired). totalSupply never returns to 0 after the first deposit because LOCKED_SHARES sit at 0xdEaD. So after the owner retires every asset that has managed > 0 (or recognizeLoss zeroes them), nav == 0 with totalSupply > 0 and _depositContext returns ZeroNAV for every token, including assets listed afterwards.
Nothing can raise managed except a deposit, and nothing can un-retire, so the vault is dead for deposits forever while redeem keeps paying the retired legs. The spec's 'revert if NAV is 0' is implemented literally; the permanent consequence is the defect to decide on.
An asset whose feed has gone stale while the price left the band has no in-protocol recovery except retirement: feed replacement needs an in-band answer and band re-centre needs a fresh answer from thsrc/BaskVault.sol:470
_checkReplacement (line 470) requires the replacement feed's answer to be inside the current band at proposal and execution, and the Band proposal (lines 417-421) reads the asset's current feed and reverts when it is >= 26 hours old.
If a feed is deprecated/stops updating and the true price has moved more than 4x from the band centre (a replacement feed therefore answers outside the band), both paths revert and the asset keeps blocking every deposit with StalePrice while managed > 0.
The only exits are retiring the asset (its value leaves NAV, accepted design (4) then dilutes existing holders) or the owner deploying an interim feed that answers inside the old band, waiting 7 days, re-centring on that interim feed's later answer, then replacing again: 2-3 weeks of blocked deposits.
Alice deposits 100 of asset0 (band [25e8, 400e8]). feed0 stops updating; a new feed f2 answers 10e8 (fresh). owner proposeFeed(asset0, f2) reverts InvalidFeed(f2) (10e8 < minAnswer). owner proposeBand(asset0); +7 days executeProposal reverts InvalidFeed(feed0) because feed0.updatedAt is 7 days old. At the next market open with feed0 27h old, depositStatus(asset1) = (StalePrice, asset0): every deposit into any asset is blocked. test/scratch/Econ.t.sol::testDeadFeedOutOfBandDeadlock.
proposeAssets after genesis creates proposals that cannot all execute: the shared 24-hour cooldown lets at most 7 listing/feed proposals from one batch execute before the rest lapsesrc/BaskVault.sol:403
A proposal is executable from createdAt + 7 days up to createdAt + 14 days (exclusive, line 385). List and Feed executions share nextAssetChangeAt = now + 24 hours (lines 403-404). A batch created at T can execute at T+7d, T+8d, ..., T+13d: seven slots.
The 8th and later proposals of the batch (or any Feed proposal created around the same time) are Expired before their first legal execution time. proposeAssets(tokens[], feeds[]) invites exactly this batching after genesis, and each lapsed listing costs another 7-day wait. Spec-literal, but the function's post-genesis purpose is partly unreachable.
After genesis, owner proposeAssets with 8 valid token/feed pairs at T -> ids[0..7].
Execute ids[0] at T+7d, ids[1] at T+8d, ... ids[6] at T+13d (each succeeds).
At T+14d-1s executeProposal(ids[7]) reverts ChangeCooldown; at T+14d it reverts InvalidProposal and proposalState(ids[7]) == Expired.
Expected: a batch of 8 listings proposed together can all be executed; actual: the 8th can never execute. test/scratch/Econ.t.sol::testBatchedListingsLapse.
transferOwnership can hand ownership to the current guardian, collapsing the owner/guardian separation the constructor and proposeGuardian enforcesrc/BaskVault.sol:262
The constructor reverts when owner_ == guardian_ and proposeGuardian/executeProposal reject next == owner, but transferOwnership only rejects address(0). After the guardian accepts, one key holds both roles: it can cancel guardian-replacement proposals (as owner) that the guardian role was meant not to be able to block, and the two-party check on pauses/closes/cancellations is gone.
owner.transferOwnership(0x5ed39AF86f2C00ad99913B5d727bD68f2A904B68); guardian.acceptOwnership(). owner() == guardian() == 0x5ed3.... Expected (by analogy with the constructor and proposeGuardian): revert InvalidAddress; actual: accepted. test/scratch/Econ.t.sol::testOwnerCanBecomeGuardian.
Accepted design (4) quantified: executing a retire proposal and depositing in the same transaction buys the retired asset's holdings at zerosrc/BaskVault.sol:579
Note only, per the brief. Retire execution is permissionless at day 7; immediately after it the retired asset is 0 in NAV (line 579 skips it) but still pays redemption legs. A depositor who executes the retire and deposits in the same transaction mints at the reduced NAV and redeems pro rata of the retired asset too.
With NAV $1,000,000 of which the retired asset is $50,000, a deposit of $316,000 across six or more assets (bucket cap 25% of NAV2, asset cap 5%) yields ~25% of supply; redeeming returns ~$12,500 of the retired asset for a 1% round-trip fee of ~$3,160, i.e. ~$9,300 taken from existing holders. Same mechanism also applies to the window between any NAV-lowering event and the next deposit; recorded here so the author sees the magnitude.
Owner closeAsset(A), proposeRetire(A); +7 days, attacker contract: executeProposal(id); deposit(B..G, ...) up to the caps; redeem(all) — legs include A. No test written: accepted design.
- reviewed
#1295Audit judgeClaude8 findings · 2 mediumrevised
afterBuild contract project, Write foundry tests, Manifest, Audit math, Audit permissions, Audit economics, Audit flowThe review is complete. The findings file is at
.imd-findings.jsonin the repository root, and the scratch directory has been removed so no source or test file in the tree is changed.Outcome. No critical or high defect. I kept 8 findings after reproducing all 15 specialist claims against the code and merging duplicates, and I answered all 27 entry points plus three invariants in the coverage record.
Findings kept, by severity
- Medium, band re-centre on an outlier. A Band or List proposal is executed by anyone and re-centres the band on whatever the feed prints at that moment, with no relation to the old band. An executor who sees a 1000x round executes, deposits at that price and redeems pro rata. In the reproduction Eve pays about $25 of real value and takes about 61 tokens of each of three real assets from Alice, and deposits are then locked out of band until another 7-day proposal. Permissions and economics findings merged. The attached proof fails on current code.
- Medium, permanent ZeroNAV. Retiring every held asset, or writing it off through loss recognition, leaves NAV at 0 while the 1e15 dead shares keep supply above 0. Every deposit reverts ZeroNAV forever, including into newly listed assets. Three specialists merged. The math specialist's proof was rerun and fails on current code.
- Low, dead feed out of band. Feed replacement needs an in-band answer and band re-centre needs a fresh answer from the dead feed, so the only exit is retirement.
- Low, fresh quorum excludes retired assets. With exactly three assets, retiring one blocks all deposits until a new listing executes.
- Low, irreversible loss recognition. A temporary under-reporting balance lets anyone zero managed after 7 days. Tokens that return are stranded above managed forever. Permissions and math findings merged.
- Low, batched listings lapse. The shared 24-hour cooldown gives a 7-day window seven slots, so the eighth proposal of a batch expires unexecuted.
- Info, owner and guardian collapse. Ownership transfer to the guardian, or a guardian proposal racing a pending owner, puts both roles on one key. Three specialists merged. No harm path found.
- Info, accepted design 4 quantified. After a retirement, deposit-then-redeem buys the retired holdings at zero. My own amplification case shows $100 of live NAV buying about 98.5 of 100 retired tokens.
What I verified myself. Build at the required settings gives 20,951 bytes of runtime with no forbidden opcodes. All 73 project tests pass, including the 64-asset hostile redemption tests under a 27.9M gas bound. The redeem and claim paths read no prices, make only gas-bounded self-calls, and no owner or guardian power enters them. The slither mulDiv lines are false positives on the standard 512-bit routine. The first two findings are spec-literal consequences of the brief, so I flagged both as scope decisions for the requester rather than implementation slips.
ran onclaude · claude-fable-5-1 · 26 turns · 12m 27s · 322 in · 54.2K out · 1.2M cachedsubmission035ddb566079ed864b7b48de8821b5a4841b956cf4748ceb7f497fb7be7e0d4bdevicebd7adba3a80458536c80f1f3abca218143308f2a67acbdf6148524561ea3eaedstarted frombc36b32f17748245ff923a1a65b371da41921758bundlenoneapplied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0b, 9b4e5cc01b368b15fb10bee1e3cadf98cd49f91c1cc2a497fbcd7987030ac89e, 6b9abee764acea55feb0d1b2a225fe3d363317bcaee0d4ce9f0b525c3d9d8b0achanged · 0 filesnothingmediumBand re-centre and post-genesis listing set the band from whatever the feed prints at a permissionless execution; one outlier round lets the executor deposit at the outlier and drain other holders, thsrc/BaskVault.sol:421
proof · a Foundry test the fix has to passmediumZeroNAV is a terminal state: once every asset holding managed funds is retired or fully written off, the 1e15 dead shares keep totalSupply > 0 and no deposit can ever succeed againsrc/BaskVault.sol:595
proof · a Foundry test the fix has to passAn asset whose feed went stale after the price left the band has no in-protocol recovery except retirement: feed replacement needs an in-band answer and band re-centre needs a fresh answer from the desrc/BaskVault.sol:470
_checkReplacement requires the replacement feed's answer inside the current band at proposal and execution, and the Band branch (lines 417-420) reads the asset's current feed and reverts InvalidFeed when it is 26 hours old or more. If a feed is deprecated and the true price has moved more than 4x from the band centre, both paths revert and the asset keeps every deposit blocked with StalePrice while managed > 0.
The only exits are retiring the asset (its value leaves NAV, diluting holders per accepted design 4) or an interim feed that lies inside the old band followed by two more 7-day proposals. Spec-literal; reported so the requester can decide whether feed replacement should be allowed when the current feed is itself stale.
Fresh-feed quorum skips retired assets and equals the genesis minimum: retiring one of three assets blocks every deposit until a new listing executes (7 days or more)src/BaskVault.sol:594
The quorum loop
continues on retired assets (line 579) before counting fresh feeds, and finalizeGenesis requires only 3 assets. With exactly 3 listed assets, retiring one capsfreshat 2, so _depositContext returns MarketNotFresh for every token no matter how fresh the remaining feeds are. Recovery needs a List proposal (7-day wait plus the 24-hour cooldown).The brief's '3 or more listed assets have feed updatedAt within the last 4 hours' can be read either way (a retired asset stays listed and its feed may still update; a retired asset is 'skipped by every deposit check'); the implementation picks the reading that produces the outage. Availability only; no loss.
Genesis with exactly 3 assets, finalize, warp 72h, refresh.
Alice deposit(stock0, 10e18).
OWNER closeAsset(stock0), proposeRetire(stock0); +7 days (market open); refresh all three feeds to now; executeProposal.
Expected: depositStatus(stock1) == OK; actual: depositStatus(stock1) == (MarketNotFresh = 7, 0x0) and deposit(stock1, 1e18, ...) reverts DepositUnavailable(MarketNotFresh, 0x0). test/scratch/Review.t.sol::ReviewRepro3::testRetireOneOfThreeBlocksDeposits.
recognizeLoss is irreversible and permissionless: a balance that temporarily under-reports (frozen holder, upgrade glitch) or later recovers leaves the returned tokens stranded above managed foreversrc/BaskVault.sol:768
proposeAssets after genesis creates batches that cannot all execute: the shared 24-hour listing/feed cooldown gives a 7-day execution window only seven slots, so the eighth and later proposals lapsesrc/BaskVault.sol:404
A proposal is executable from createdAt + 7 days until createdAt + 14 days (exclusive, line 385). List and Feed executions share nextAssetChangeAt (lines 403-404). A batch created at T can execute at T+7d, T+8d, ..., T+13d: seven slots.
The eighth proposal of the batch, or any Feed proposal created around the same time, is Expired before its first legal execution time and costs another 7-day wait. Spec-literal (one listing or feed replacement per 24 hours), but proposeAssets(tokens[], feeds[]) invites exactly this batching after genesis.
After genesis, OWNER proposeAssets with 8 valid token/feed pairs at T -> ids[0..7]. executeProposal(ids[i]) at T+7d+i days for i = 0..6 all succeed.
At T+14d-1s executeProposal(ids[7]) reverts ChangeCooldown; at T+14d it reverts InvalidProposal(ids[7]) and proposalState(ids[7]) == Expired.
Expected: a batch proposed together can all execute; actual: the eighth never can. test/scratch/Review.t.sol::testBatchedListingsLapse.
owner != guardian is enforced only in the constructor and proposeGuardian: transferOwnership to the guardian, or a Guardian proposal that races a pending owner, collapses both roles into one addresssrc/BaskVault.sol:262
The constructor rejects owner_ == guardian_ (line 196) and proposeGuardian/executeProposal reject a target equal to the current owner (lines 360, 431), but transferOwnership only rejects address(0), acceptOwnership performs no identity check, and the Guardian proposal is compared against owner, not pendingOwner. Two sequences make one key hold both roles.
No fund-loss path follows: the guardian's powers are a subset of the owner's and cancelProposal's owner branch already short-circuits. Reported as an asymmetry between the construction-time invariant and later transitions, merged from the permissions (low), flow (info) and economics (low) specialists; the brief adds no requirement here and says to add no safeguard, so the requester decides whether a one-line
next == guardian/p.target == pendingOwnercheck is wanted.Accepted design (4) quantified: after a retirement, deposit-then-redeem buys the retired asset's holdings at zero, and the extraction approaches the whole retired position when the live NAV is smallsrc/BaskVault.sol:579
- updated
#434Build contract projectCodex3 files changed
Recorded all eight findings in .imd-responses.json.
Contracts remain unchanged: seven requested behavior changes conflict with the explicit brief. Documented the retirement risk and added 13 reproduction tests. Supplied proof failures are explicitly disputed; the underlying risks remain.
Offline build, formatting, and all 72 project tests pass, including a second fuzz seed with 1,024 runs.
ran oncodex · gpt-6-astra · 5 turns · 9m 6s · 107.1K in · 15.6K out · 1.2M cachedsubmission87d54f8451701ab8aa50548fb452c14c33657e540eeaf4de59d8951642a19289device985b73231a8fa1f82a5c4d37d645165a0eb5a4b16d0178d825a377aa8ca30f2fstarted from149bf2d09073f75bf4b4751fff0d702c32231243bundle5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0b · 113 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 3 filesREADME.mdREVIEW.mdtest/RevisionFindings.t.sol - updated
#1433ManifestCodex1 file changed
afterBuild contract projectwrites tolaunch.jsonUpdated launch.json to clarify the redemption balance-read gas limit. Preserved the contract and required address literals.
Schema and ABI checks passed.
forge buildsucceeded; all 72 tests passed. Worst tested 64-asset redemption used 22.79 million gas.Only
launch.jsonchanged.ran oncodex · gpt-6-astra · 3 turns · 2m 35s · 68.8K in · 5.1K out · 203.8K cachedsubmissionacb68d24e040a0b20746f730f2c2b1cdee8c2df932743adf21ae9aff774d42fcdevice1f805d33d69c340e932c676368423c4ba8825910d39eb41fe72909413c31961dstarted from003a7ebb93107f482e560e2b855153e2f73254e5bundle6b9abee764acea55feb0d1b2a225fe3d363317bcaee0d4ce9f0b525c3d9d8b0a · 115 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 1 filelaunch.json - updated
#1580Write foundry testsCodex2 files changed
afterBuild contract projectwrites totesttest/**Added 128-run randomized coverage across 64 assets, checking hostile-token redemption, prior claims, accounting, and the gas limit.
Only
test/changed. Offline build succeeded; all 87 tests passed, including both invariant suites. No new defect was reproduced.ran oncodex · gpt-6-astra · 5 turns · 4m 44s · 101.9K in · 6.2K out · 947.1K cachedsubmission2b474470146020e2d21184204db17529aab3e12f7b6b3d6185a91ce5123b68e8device593a9c894818e368a7352606ee2fdddadb1ad552015a1dcd2744dc8325d7caedstarted from8acff76cab784d2e3c405434b16fd1881888dc30bundle9b4e5cc01b368b15fb10bee1e3cadf98cd49f91c1cc2a497fbcd7987030ac89e · 124 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0bchanged · 2 filestest/RedemptionAttack.t.soltest/TESTING.md - reviewed
#1803Audit judgeClaude8 findings · 6 low
afterBuild contract project, Write foundry tests, Manifest, Audit math, Audit permissions, Audit economics, Audit flowReview settled. The findings file is written at
.imd-findings.jsonwith 8 settled entries and 29 coverage rows, all snippets verified against the tree.Outcome: no blocking findings remain. The source is byte-for-byte unchanged since round 1, so every behaviour I reported still reproduces, and all four attached proofs still fail on this tree. The author did not dispute the reproductions. They disputed that the brief permits a fix, and on the brief's own text those disputes hold:
- Band re-centre on an outlier (was medium, now low design note). The brief says "re-centre a band on the answer at execution", "anyone may execute", and "add no safeguard". Requiring the old band or owner-only execution changes those rules, and a new listing has no old band. The author also correctly found a flaw in my proof: its setUp mints the executor 1e18 of every stock and then asserts the final balance is below 1e18, so it fails even when the attack is prevented. The extraction risk is real and is now documented in README.md and REVIEW.md. Changing it needs a scope decision from the requester, not a code fix.
- ZeroNAV terminal state (was medium, now low design note). "Revert if NAV is 0", the 1e15 dead shares, and retired assets counting 0 are all explicit. Any recovery path is unrequested behaviour. Availability only, no loss, and documented.
- The five advisories (dead-feed deadlock, three-asset quorum, irreversible recognizeLoss, batch lapse, owner/guardian collapse) all reproduce unchanged and are spec-literal; each is now documented. Kept at low/info as settled notes.
- Accepted design 4 quantified is confirmed addressed: README now carries the 98.51-of-100 retired-token example and the operator guidance.
Verification this round. All 87 project tests pass, including the four 64-asset redemption attacks under the 27.9M gas ceiling. I re-read the full contract once more and found no new defect to report.
What's next for the requester. Only one decision is outstanding and it is theirs, not the author's: whether the brief should be amended so the band cannot be re-centred on a print outside the previous band. As written, the contract does what the brief asks.
ran onclaude · claude-fable-5-1 · 13 turns · 5m 21s · 194 in · 22.3K out · 589.6K cachedsubmission6456a20344b08643e40bccf36d178f0aa1f241895fd60057913555f393042293device02f22d6f13810ca8c6edce1203dbc336b0a785f87bb7354c4428c81847aebe93started from74e89415f2ad1ad764688b620ab98872f2f832a9bundlenoneapplied on5ddb92e23836fdbe95afa2e31e801d649b1423e7dfc655bc6b248441b32a1e0b, 9b4e5cc01b368b15fb10bee1e3cadf98cd49f91c1cc2a497fbcd7987030ac89e, 6b9abee764acea55feb0d1b2a225fe3d363317bcaee0d4ce9f0b525c3d9d8b0achanged · 0 filesnothingSETTLED (design risk, dispute upheld): band re-centre and post-genesis listing take the band from the execution-time answer with no relation to the old band; one outlier round in the 7-day permissionlsrc/BaskVault.sol:421
SETTLED (design risk, dispute upheld): ZeroNAV is a terminal state once every asset holding managed funds is retired or fully written off, because the 1e15 dead shares keep totalSupply > 0 foreversrc/BaskVault.sol:595
SETTLED (design limitation, dispute upheld): an asset whose feed went stale after the price left the band has no in-protocol repair except retirementsrc/BaskVault.sol:470
Round-2 settlement of 837d258ed4ac.
Source unchanged; reproduces. _checkReplacement requires the replacement answer inside the current band at proposal and execution, and the Band branch (lines 417-420) reverts InvalidFeed when the asset's current feed is 26 hours old or more, so a dead feed whose last price is more than 4x from the band centre blocks every deposit (StalePrice while managed > 0) until the asset is retired or an interim in-band feed is used over two or three 7-day cycles.
The brief requires both checks verbatim ('feed checks and new answer inside the band, both times'; 're-centre a band on the answer at execution, under 26 hours old'), so the author's dispute holds and the limitation is documented in README.md. Availability only, no loss. No code change required under the current brief.
SETTLED (design limitation, dispute upheld): fresh-feed quorum skips retired assets and equals the genesis minimum, so retiring one of exactly three assets blocks every deposit until a new listing exesrc/BaskVault.sol:594
Round-2 settlement of 952288203837. Source unchanged; reproduces. The loop at line 579 'continue's on retired assets before counting fresh feeds, and finalizeGenesis requires only 3 assets, so with 3 listed assets retiring one caps fresh at 2 and every deposit returns MarketNotFresh regardless of feed freshness; recovery needs a List proposal (7 days plus the 24-hour cooldown).
The author's reading ('a retired asset is ... skipped by every deposit check', so its feed cannot count toward the deposit quorum) is the more literal one and the brief says to add no safeguard, so the dispute holds; README.md now states that three fresh unretired feeds are needed and that replacement listings should precede retirement. Availability only. No code change required under the current brief.
SETTLED (design limitation, dispute upheld): recognizeLoss is irreversible and permissionless; a balance that temporarily under-reports for 7 days lets anyone zero managed, and tokens that return aftesrc/BaskVault.sol:768
Round-2 settlement of 1a92e4b7aee8 (merged permissions/math). Source unchanged; reproduces. flagDeficit records any readable shortfall, recognizeLoss lowers managed after 7 days, nothing raises managed except a deposit (impossible while Reason.Deficit holds or for a retired asset), and no role can cancel a flag or stop the clock; pauseDeposits has no effect on the loss path.
The brief specifies exactly this ('flagDeficit(token), by anyone ... recognizeLoss(token), by anyone 7 days or more later ... Nothing lowers managed automatically', 'no ... rescue or sweep'), and the author's control test shows recovery before recognition preserves managed because the current shortfall is re-checked (line 766). The dispute holds; README.md now documents recognition finality and the before/after-recognition distinction.
No code change required under the current brief.
SETTLED (design limitation, dispute upheld): proposeAssets after genesis creates batches of which at most seven can execute; the shared 24-hour listing/feed cooldown leaves the eighth and later propossrc/BaskVault.sol:404
Round-2 settlement of 95658eb9f8df. Source unchanged; reproduces. A proposal is executable from createdAt + 7 days until createdAt + 14 days exclusive (line 385); List and Feed executions share nextAssetChangeAt (lines 403-404); a batch created at T has seven execution slots, so the eighth proposal of the batch is Expired before its first legal execution time.
The brief fixes both the 7/14-day window and 'One listing or feed replacement executes per 24 hours' and does not guarantee batch completion, so the dispute holds; README.md now explains staggering and re-proposal. Availability only. No code change required under the current brief.
After genesis, OWNER proposeAssets with 8 valid token/feed pairs at T -> ids[0..7]. executeProposal(ids[i]) at T + 7d + i days for i = 0..6 all succeed.
At T + 14d - 1s executeProposal(ids[7]) reverts ChangeCooldown; at T + 14d it reverts InvalidProposal(ids[7]) and proposalState(ids[7]) == Expired; assetCount() == 11.
Verified: test/RevisionFindings.t.sol::testEighthSimultaneousListingExpiresBeforeCooldownEnds passes on this code.
SETTLED (asymmetry noted, dispute upheld): owner != guardian is enforced only in the constructor and proposeGuardian; transferOwnership to the guardian, or a Guardian proposal racing a pending owner, src/BaskVault.sol:262
SETTLED (accepted design 4 quantified; documentation added as asked): after a retirement, deposit-then-redeem buys the retired asset's holdings at zero, and the extraction approaches the whole retiredsrc/BaskVault.sol:579
Round-2 settlement of d3e5853ef3e9.
Note only, per the brief's REVIEW section; the author answered 'fixed' by documentation and that is confirmed: README.md now carries the quantified example ('retiring a $10,000 position while leaving only $1 of live NAV lets a $100 deposit followed by redemption receive about 98.51 of the 100 retired Stock Tokens'), states that deposit caps do not cap the extraction, and advises retiring only positions of negligible value or permanently broken transfer.
Contract behaviour is unchanged and is the accepted design. Nothing further is required.
4 genesis assets at $100.
Alice deposits 100e18 of asset0 ($10,000) and 1e16 of asset1 ($1).
OWNER closeAsset(asset0), proposeRetire(asset0); +7 days.
BOB executeProposal(id); previewDeposit(asset1, 1e18).nav == 1e18; deposit(asset1, 1e18, BOB, 0, now); redeem(all): legs[0] == 98.509950248756218905e18 of retired asset0 for $100 plus fees.
Verified: test/RevisionFindings.t.sol::testRetiredPositionExtractionAmplifiesWhenLiveNAVIsSmall passes on this code.
- onchain
- publishedidentity-md-launches/launch-877-basketpull request
- deployed
1 contracton Robinhood Chain, 7 gates passedtransaction
- rebuilt
- BaskMath, BaskVault · verifier 0.1.0 · solc 0.8.26
- gates
- provenance
- findings
- independent review
- bytecode
- manifest
- protected invariants
- economics
- proof
commit, attestation, manifest, tree, per-contract hashes
- repository
- identity-md-launches/launch-877-basket
- commit
- 3b1fe81cab5e4d6b148399ce2af5d84ebefeacb4
- attestation
- 3371b249dd28ce1b45ae1624f9d05faec680105995fcdd46318155892ba49723
- manifest
- 84b7eff9f5e73ed35d3baf3f9bf30278d2e215e9d47c0204be6984f789181b52
- constructor
- BaskVault: 0x30B57ECf51D19ABcED7F6f70974e6fBb6f3b9Da3, 0x5ed39AF86f2C00ad99913B5d727bD68f2A904B68
- tree
- e1bea7ee27f1292a97c5e5f9847bafa26ac149ed
- compiler
- solc 0.8.26, optimizer 200 runs, reproducible
- contract
- BaskMath
src/BaskMath.sol · 94 bytes
creation 03f00af6a2c1e216c5142290f5a7c5a73b7dca9ff4182f298fb7a6b46fc82bef
abi 5ff5499febb7d544e4e1909348158dd92a5c67d231bfbd8960fdd2f087d4fc45
metadata 36a012aee99a372d62e6b87df8591b7a7351ddac18b37e521edeb0800ca5e9b5 - contract
- BaskVault
src/BaskVault.sol · 21338 bytes
creation 541ec2ed756441069160a7524785276ff63d26f5e23f9e8c9860d9a0cff73175
abi a544e47473b63007f79a156bb1126ddd87d72cc1d59605b2d8ce8161780c8161
metadata 24c00872350b11f192d1aace70976736a9644fd6a364cbae463199a019029415
onchain at 0x518a…96e1, block 82,259,501 · creation code matches