Protocol
Security model
Trust assumptions, privileged roles, invariants, what is tested, and what is not.
Phase 1 has not been audited by a third party yet. The suite is small (≈1,200 lines of Solidity), built on OpenZeppelin, and covered by fork tests against Robinhood Chain, but treat it as unaudited code.
Privileged roles
| Role | Held by | Can | Cannot |
|---|---|---|---|
PinarcConfig.owner | Pinarc admin (later governance) | Change curve, fee and guard parameters for future launches; set the guardian | Change any existing curve (parameters are snapshotted at creation); touch funds |
PinarcConfig.guardian | Admin by default; multisig later | CreatorBond.slash(token, reason) — move an active bond into that token's floor reserve | Send a bond anywhere else; slash twice; touch curves, floors, vaults or locks |
| Token creator | Launch wallet | setMetadataURI, transferMetadataAuthority, CreatorBond.release after graduation + lock, withdraw/extend/transfer the LP lock after it expires | Mint, pause, blacklist, change fees, pull liquidity |
| Factory | Contract | BondingCurve.initialize, commitBatchFor, CreatorBond.post | Anything after creation |
BondingCurve, FloorReserve, VestingVault and LPLocker have no owner and no admin functions.
Upgradeability
None. BondingCurve clones point at one immutable implementation (EIP-1167); the other contracts are plain. A parameter change means new launches behave differently; existing launches never change.
Invariants
tokensSold ≤ supplyOnCurve; graduation happens exactly when equal.- Selling exactly
supplyOnCurvetokens raises exactlygraduationUsdg(by construction ofvirtualUsdg). - The curve never holds fees: every fee is split and transferred in the same call.
- After graduation the curve holds only unclaimed batch tokens and refunds.
FloorReserve.redeemburns before it pays, and paysreserve × amount / totalSupply, so the floor price is non-decreasing under redemptions.- A vesting schedule releases at most
total;vestedAmountis monotone in time. - A lock can only be extended, transferred or withdrawn by its owner, and only withdrawn after
unlockAt.
Defences
ReentrancyGuardon every state-changing external function of every contract.SafeERC20everywhere;forceApprovebefore router / vault / floor / bond calls.- Rounding is always against the caller (
ceilDivin quotes) so the curve cannot be drained by dust. - Pair-price alignment neutralises pre-seeded pools at graduation; if alignment is too expensive the migration still completes.
_validaterejects floor > max, team > max, bad vesting schedules and bonds below the minimum.- Anti-sniper limits are enforced on the recipient (
to), so routing through a fresh contract does not bypass them.
Fork test coverage
test/Launchpad.t.sol runs against a fork of Robinhood Chain with the real Uniswap V2 deployment: parameter snapshotting, bond + dev buy + team vesting at creation, rejected choices, single clearing price and claims, per-wallet batch cap, anti-sniper limits then relaxation, buy/sell round trip fee accounting, quotes vs execution, graduation with LP lock and floor funding, LP burn variant, graduation against a pre-seeded skewed pair, bond slash into the floor, and a batch that graduates on its own.
Known limitations
- The guardian is a single key until the multisig is in place; it can only move bonds into the floor, but it can do so at its discretion.
maxWalletis checked onto's balance only during the window; a buyer can still spread across wallets.- Metadata is off-chain by design; the app sanitises it, but a self-hosted
metadataURIcan point anywhere. - If a pre-seeded pair is skewed so badly that alignment would cost more than half of either side, the migration adds at the pool's price.
- Phase 1 has no presale, allowlist, escrow or governance contracts; those designs are not audited either because they do not exist yet.