Audi fix: Pre-IPO - #120
Conversation
- M02: setPublicRound reverts once a settlement root is pending/finalized - I02: remove redundant root inequality check in setSettlementRoot - I03: pause now gates setSettlementRoot / finalizeSettlement / claim - I04: document emergencyWithdraw accounting caveat (NatSpec) - I09: cache duplicate deposit storage read in depositWhitelist - I10/I14: complete claim param docs, header, tranche + permissionless-claim docs - I13: add previewClaim read-only helper L03 handled operationally; M01/L01/I01/I05-I08/I11/I12 acknowledged; L02 pending xKLSH address. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull Request ReviewThis Solidity UUPS upgrade adds a two-step Merkle-based settlement and claim lifecycle, pause enforcement, claim previews, and protections against opening public rounds after settlement begins. It also updates documentation and expands Foundry tests covering settlement, claims, access control, and pause behavior, while adding the associated audit report. Sensitive ContentNo sensitive content detected. Security Issues🟠 [HIGH] Arbitrary share token delivery can drain assets backing other sales
Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits. |
Summary
Addresses the HashDit security audit of
PreIPODistributor(subscription #118 + settlement/claim #119). Applies the accepted code fixes, records the disposition of every finding, and adds the audit report to the repo. Stacked onfeat/pre-ipo-claim(#119) — please set that as the base branch.Fixes applied (code)
setPublicRoundnow reverts ("Settlement started") once a settlement root is pending or finalized, so a public round can't be opened after the settlement snapshot and strand late depositors._root != s.pendingRootcheck insetSettlementRoot(already covered by thependingRoot == 0requirement).pauseis now a real circuit breaker:require(!sale.paused)added tosetSettlementRoot,finalizeSettlement, andclaim(previously only deposits were gated).emergencyWithdrawin NatSpec: it does not adjust internal accounting and can strand refunds/claims; totals must be restaged (or the contract upgraded) before claims resume.deposits[saleId][msg.sender]storage read indepositWhitelist(single SLOAD).claimparameter docs; refreshed the stale contract header; documented the tranche constants' delivery semantics and thatclaimis permissionless (funds always go to the leaf's_account).previewClaim(...)read-only view returning(valid, alreadyClaimed, tranche, sendShares), mirroringclaimvalidation/routing.Not changed (with rationale)
waitingPeriodwhile a settlement is in flight); globalwaitingPeriodmakes a per-sale on-chain guard awkward.pausedto save a slot) / I11 (renamedeposits→wlDeposits) — won't fix: both change the storage layout / public ABI of the already-deployed subscription contract and are not upgrade-safe.Testing
forge test --match-contract PreIPODistributorTest— 56 passing, including new cases:setPublicRoundafter pending/finalized settlement reverts, pause blocks settlement and claim, andpreviewClaimbehavior (pre/post finalize, wrong amount, post-claim).Also included
audits/HashDit-PreIPODistributor-20260727.pdf— the audit report.🤖 Generated with Claude Code