Skip to content

feat(dao): add PreIPODistributor subscription contract - #118

Merged
lawson-ccy merged 4 commits into
masterfrom
feat/pre-ipo-distributor
Jul 27, 2026
Merged

feat(dao): add PreIPODistributor subscription contract#118
lawson-ccy merged 4 commits into
masterfrom
feat/pre-ipo-distributor

Conversation

@razww

@razww razww commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds PreIPODistributor — an escrow subscription contract for a two-round pre-IPO / launchpad sale. Users deposit an ERC-20 (e.g. USDT) into escrow during a whitelist round and an optional public round; each address picks a delivery tranche at its first deposit. Scoring, weighting and the final pro-rata + cap + waterfall allocation are computed off-chain — this contract is a pure escrow ledger. Settlement, refunds and token delivery are intentionally out of scope and land in a follow-up upgrade.

What's included

  • contracts/dao/PreIPODistributor.sol — the contract
  • test/dao/PreIPODistributor.t.sol — Foundry tests (26 cases, all passing)

Design

  • Whitelist round — gated by an OpenZeppelin MerkleProof whitelist root. Leaf = keccak256(abi.encode(chainid, account)); it proves membership only (no sale id, no tier/weight), so a single whitelist tree can be reused across sales. First deposit verifies the proof; top-ups skip it.
  • Public round — opened by the manager after the whitelist round via setPublicRound (must start strictly after the whitelist round ends). Open to everyone, tracked in a separate accounting pool.
  • Tranche — each address selects a delivery tranche (TRANCHE_UNLOCKED / TRANCHE_LOCKED) at its first deposit; it is locked for the whole sale and inherited by top-ups across both rounds. The tranche does not affect accounting, only the recorded delivery choice.
  • Locked deposits — deposits can only be topped up, never withdrawn by the user (removes end-of-round withdraw-to-manipulate behavior).
  • RolesMANAGER manages sales (create / update / open public round / pause) and holds the emergencyWithdraw safety valve; DEFAULT_ADMIN_ROLE authorizes UUPS upgrades.
  • Upgradeable — UUPS, _authorizeUpgrade restricted to DEFAULT_ADMIN_ROLE; ReentrancyGuard + CEI on deposits.

Testing

forge test --match-contract PreIPODistributorTest

Escrow subscription contract for a two-round pre-IPO sale:
- Whitelist round gated by an OZ MerkleProof whitelist root
  (leaf = keccak256(abi.encode(chainid, account)), reusable across sales)
- Public round, opened by the manager after the whitelist round
- Per-user delivery tranche, locked at first deposit and inherited on top-ups
- Deposits are locked (top-up only, no user withdrawal)
- UUPS upgradeable; MANAGER role for sale management + emergency withdraw

Includes Foundry tests (26 cases) covering sale config, both deposit
rounds, tranche locking, access control, and upgrade authorization.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hashdit-bot

hashdit-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

Pull Request Review

This Solidity PR adds an upgradeable PreIPODistributor escrow contract supporting Merkle-gated whitelist deposits, a subsequent public round, locked tranche selection, role-based administration, pausing, and emergency withdrawals. It also adds Foundry tests covering sale configuration, deposits, tranche enforcement, access control, pausing, emergency withdrawal, and UUPS upgrades.

Sensitive Content

No sensitive content detected.

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

@razww
razww requested a review from qingyang-lista July 23, 2026 01:43
@hashdit-bot

hashdit-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

Pull Request Review

This Solidity PR adds an upgradeable PreIPODistributor escrow contract supporting Merkle-gated whitelist deposits, a subsequent public round, fixed delivery-tranche selection, role-based administration, and emergency withdrawals. It also adds Foundry tests covering initialization, access control, sale configuration, deposits, tranche locking, pausing, withdrawals, and UUPS upgrades.

Sensitive Content

No sensitive content detected.

Security Issues

🟠 [HIGH] Deposit accounting trusts the requested amount rather than tokens received

File: contracts/dao/PreIPODistributor.sol
Both deposit functions credit _amount to user and sale totals without checking the contract’s actual balance increase. A fee-on-transfer, rebasing, or otherwise non-standard deposit token can therefore leave the escrow undercollateralized and make the off-chain allocation ledger materially exceed the assets held by the contract.
Recommendation: Calculate the received amount from the contract’s token balance before and after safeTransferFrom, and use that value for all accounting and minimum-deposit checks. Alternatively, explicitly restrict sales to allowlisted tokens guaranteed to transfer the exact requested amount.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

@hashdit-bot

hashdit-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

Pull Request Review

This PR adds an upgradeable Solidity escrow contract for two-round pre-IPO subscriptions, supporting Merkle-gated whitelist deposits, public deposits, persistent tranche selection, role-based administration, pausing, and emergency withdrawals. It also adds Foundry tests covering initialization, access control, sale configuration, deposit behavior, tranche locking, and UUPS upgrades.

Sensitive Content

No sensitive content detected.

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

Deploys the subscription contract and grants roles to both the deployer
(temporary, for setup) and the real admin/manager/bot holders. createSale
and deployer-role revocation are handled by separate scripts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hashdit-bot

hashdit-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

Pull Request Review

This PR adds an upgradeable Solidity escrow contract for two-round pre-IPO subscriptions, including Merkle-gated whitelist deposits, public deposits, tranche selection, role-based management, and emergency withdrawals. It also adds Foundry tests and a BSC mainnet deployment script that configures the proxy and production role holders.

Sensitive Content

No sensitive content detected.

Security Issues

🟠 [HIGH] Deployment leaves the deployer with upgrade and withdrawal privileges

File: scripts/foundry/dao/deploy_PreIPO_phase1.sol
The proxy is initialized with the deployer holding DEFAULT_ADMIN_ROLE, MANAGER, and BOT, and the script grants production roles without revoking the deployer's roles. Until a separate revocation transaction is successfully executed, the deployer key can upgrade the contract to arbitrary logic or use emergencyWithdraw to drain all escrowed assets. The referenced revocation script is not included in this PR, making this a security-critical post-deployment dependency.
Recommendation: Transfer and revoke privileges atomically in the deployment script whenever possible. At minimum, include and execute a verified revocation step before accepting deposits, assert that the deployer has none of the privileged roles afterward, and ensure the production timelock/multisig can administer all required roles.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

@lawson-ccy lawson-ccy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lawson-ccy
lawson-ccy merged commit b60cdc8 into master Jul 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants