Status: testnet-bound. The suite compiles and passes (130 tests across 18 suites on a cleared cache). A formal security review precedes mainnet. Some invariants are proven only against the implemented surface: their end-to-end halves are gated behind two settlement seams (see
docs/ARCHITECTURE.mdSection 7), and those tests assert the seam boundary today so they stay green without ever reading as a false pass.
Design reference: the invariant suite (25 invariants), docs/INVARIANTS.md, and docs/ARCHITECTURE.md.
The always-current invariant map is the header of test/Invariants.t.sol.
The suite uses the conventional Foundry layout (separate unit/ invariant/ integration/ plus a
shared base + handler pattern). All code is authored for this project.
contracts/remappings.txt declares the remappings; contracts/lib/ is git-ignored, so vendor the
libraries with forge install. The commits below are the versions this suite is verified against; pin
them (or use submodules) so CI and a fresh clone build the same bytecode:
| Library | Remapping | Verified commit |
|---|---|---|
foundry-rs/forge-std |
forge-std/ |
a6d71da |
Uniswap/v4-core |
v4-core/, @uniswap/v4-core/ |
d153b04 |
Uniswap/v4-periphery |
v4-periphery/, @uniswap/v4-periphery/ |
7ebd04b |
OpenZeppelin/openzeppelin-contracts |
@openzeppelin/contracts/ |
fcbae53 |
OpenZeppelin/openzeppelin-contracts-upgradeable |
@openzeppelin/contracts-upgradeable/ |
aa677e9 |
OpenZeppelin/uniswap-hooks |
uniswap-hooks/, openzeppelin-uniswap-hooks/ |
acbd604 |
transmissions11/solmate |
solmate/ |
4b47a19 |
Uniswap/permit2 |
permit2/ |
cc56ad0 |
Install (Foundry), pinned:
cd contracts
forge install foundry-rs/forge-std@a6d71da
forge install Uniswap/v4-core@d153b04
forge install Uniswap/v4-periphery@7ebd04b
forge install OpenZeppelin/openzeppelin-contracts@fcbae53
forge install OpenZeppelin/openzeppelin-contracts-upgradeable@aa677e9
forge install OpenZeppelin/uniswap-hooks@acbd604
forge install transmissions11/solmate@4b47a19
forge install Uniswap/permit2@cc56ad0
If the exact submodule paths differ from the remappings, adjust remappings.txt, not the imports.
foundry.toml is set for solc 0.8.26, evm_version = cancun (transient storage, required by v4 and
by the hook's transient guards), via_ir = true, and optimizer_runs matching v4-core / v4-periphery;
fuzz runs = 512, invariant runs = 256 / depth = 64 (CI profile: fuzz 5000, invariant 1000 / depth 128).
cd contracts
# compile
forge build
# the full suite (130 tests, 18 suites)
forge test -vvv
# just the unit lemmas
forge test --match-path "test/unit/*"
# just the stateful invariants
forge test --match-path "test/invariant/*"
# a single property
forge test --match-test invariant_14_splitConservation -vvvv
# the CI profile (deep fuzz + invariant)
FOUNDRY_PROFILE=ci forge test
contracts/test/
Invariants.t.sol # the INDEX: 25 invariants -> file/function -> status (read first)
unit/
BondingCurve.t.sol # curve integral, crossing/no-straddle, continuity reference
FeeRouter.t.sol # four-bucket split conservation, creator anti-grief, reward pro-rata, wash
LaunchToken.t.sol # sole-minter gate, one-way renounce, supply accounting
AntiSniper.t.sol # decaying-fee monotonicity + per-sender cap
Pause.t.sol # pause-new-launches, guardian authority, bounded/auto-expiry, no fund lever
TokenFactory.t.sol # fail-safe param validation, team cap, bucket sum, clone prediction
ReferralAndHookToken.t.sol # stake-multiplier timing + referral bind
integration/
HookTestBase.sol # shared setup: v4 PoolManager + HookMiner + hook + factory + one launch
GraduationLifecycle.t.sol # registration + immutability + lifecycle
invariant/
SplitConservation.invariant.sol # inv 14/18/19/24 accrual conservation
AccrueOnly.invariant.sol # inv 15 no-external-call-on-swap + inv 8 sink isolation
CurveConservation.invariant.sol # inv 1/2/4/5/6 curve loop
FlipReentrancy.invariant.sol # inv 3/16 flip + steady-state guards
NoRug.invariant.sol # inv 6/24 structural + accrual non-custody
handlers/
SplitHandler.sol # bounded fuzz actor for the FeeRouter accrual
LaunchHandler.sol # bounded fuzz actor for the full swap path
mocks/
FeeRouterHarness.sol # exposes the abstract FeeRouter internals for direct testing
LaunchHookHarness.sol # exposes LaunchHook internals + a test registrar
MaliciousFeeSink.sol # revert / gas-bomb / reentrancy sink (inv 8/15)
MaliciousRecipient.sol # reverting + reentrant creator recipient, malicious router (inv 16/18)
MockHookToken.sol # Model B token read shape (inv 21/22)
Actors.sol # FlashStakeActor (inv 22), WashFarmerActor (inv 23)
The authoritative, always-current map is the header of test/Invariants.t.sol. In summary:
- Proven today (RUNS): four-bucket split conservation (14), accrue-only swap path (15), creator anti-grief precision (18), participation-reward conservation (19), O(1) pull distribution (20), multiplier-times-zero (21), and the RUNS halves of supply accounting (2), curve crossing math (4), the marginal-price reference (5), the accrual side of non-custody (6/24), pause blast radius (7), param immutability (9), decaying-fee monotonicity (10), the CEI + guard shape (16), flash-stake consumption (22), own-pool exclusion (23), and referral-bind integrity (25).
- Gated on a settlement seam (SEAM): the end-to-end halves of 1, 2, 3, 4, 5, 6, 7, 8, 10, 16 and
the full graduation lifecycle. These depend on
docs/ARCHITECTURE.mdSection 7's two seams (bonding-swap currency settlement, and the graduated-swap fee read). The tests assert the seam boundary today and are switched to the full property once the seam is authored. - Model B (UNIMPL): 11/12/13 (the platform token + BuyAndBurnSink) are out of this repo under the Model-A-first scope; their tests point at the Model B deliverable.
- Sim / review-layer (SIM): the economic halves of 22 (time-weighting gameability) and 23 (thin-epoch stacked net-negativity), plus continuity (5) and delta settlement (17), which depend on v4 flash-accounting composition that is the external review's call.
Per docs/ARCHITECTURE.md Section 7, these LaunchHook functions are provisional reverting seams.
Authoring them unblocks the tests noted:
| Seam | Unblocks |
|---|---|
bonding-swap currency settlement (the _beforeSwap take/settle) |
every bonding swap through the base; inv 1, 3, 5, 6 (POL side), full lifecycle |
_graduatedFee (realized fee on a graduated swap) |
inv 14/15 on the graduated afterSwap path |
_payout (fee-asset transfer) |
inv 8 sink isolation, reentrancy end-to-end (inv 16), claim transfers |
A SEAM/UNIMPL test carries _BLOCKED_ON_SEAM or _BLOCKED_ON_UNIMPLEMENTED in its name and either
(a) vm.expectRevert() around the seam call so the file compiles and the status is explicit, or
(b) a revert(...) with a message pointing at the unimplemented feature. When the seam is authored,
remove the expectRevert / revert and assert the property documented in the test's NatSpec. This
keeps a blocked property from ever reading as a false pass.