New launch model: Shards — locked bonding-curve market for a fixed on-chain-art NFT collection - #43
New launch model: Shards — locked bonding-curve market for a fixed on-chain-art NFT collection#43jesse-stahl wants to merge 18 commits into
Conversation
Updates checkout and Foundry Action pins after Verify, Security and Ethereum Evidence passed.
## What changed Adds the candidate Protocol Revenue Deepener for the canonical `$V4` / ETH pool. - converts bounded ETH batches into a balanced full-range position - permanently keeps the position under an ownerless, add-only contract - enforces a six-hour cooldown and delayed price observation - validates the exact Mainnet pool, hook and fee disclosure in the constructor - accepts permissionless maintenance without caller-controlled amounts - documents the control model, limitations and migration boundary ## Validation - complete Forge suite - CI fuzz tests - stateful invariant suite - Mainnet-fork compound against the canonical pool - Slither review with no unresolved high-severity finding - model registry and release-evidence checks This pull request publishes a deployment candidate. It does not claim a Mainnet deployment or active keeper.
## What changed - Publish the exact seven-contract Classic Mainnet source set. - Add unit, fuzz, invariant and Mainnet-fork lifecycle tests. - Publish the fixed classic-v3 specification and complete deployment evidence. - Remove the cancelled Protocol Revenue Deepener candidate. - Keep network-backed fork evidence separate from deterministic CI. - Document every current Slither finding and its manual disposition. This pull request publishes the source record only. Classic remains pointed at classic-v2 until a follow-up pull request binds the model registry to the resulting main-branch commit. ## Verification - 120 deterministic tests passed. - 1 complete Mainnet-fork lifecycle passed. - All source and test files match the deployed release snapshot. - Registry, release evidence, documentation links, gas snapshot and workflow lint passed. Classic has not received an independent smart-contract audit or public security contest.
This marks the verified Classic Ethereum deployment as the current available release. The activation is bound to public source commit ae5e757. It updates the model registry, deployment record, security properties and release evidence without changing deployed bytecode. Verified before publication: - 120 deterministic Foundry tests - 1 complete Mainnet-fork lifecycle test - 8 recorded runtime code hashes matched Ethereum - model registry, release evidence, documentation links and SHA-256 checksums - formatting, build sizes and gas snapshot All seven Programmable release contracts are Etherscan exact matches and Sourcify matches. No independent audit or public security contest is claimed.
Records the live production interface, deployed source tag, verification status, lifecycle evidence and remaining external review boundaries.
Bonding-curve market for a fixed 10,000-piece on-chain-art NFT collection. The 1.00% native-ETH swap fee splits at accrual: 0.80% to collection holders, 0.10% to the builder beneficiary, 0.10% to Programmable, with rounding dust kept by holders. Donations reach the holder pool whole.
393 tests pass under the CI profile. Existing gas-snapshot entries for other models drift only in fuzz means (medians unchanged): new contracts in the artifact set alter fuzz input sampling.
There was a problem hiding this comment.
Pull request overview
Adds the new Shards launch model to the Programmable codebase: a fixed-supply ERC-20 (“SHARD”) + fixed 10,000-piece ERC-721 collection whose primary market is a locked, single-sided Uniswap v4 bonding-curve hook, with native-ETH fee capture and an 80/10/10 (holders/builder/Programmable) split.
Changes:
- Introduces the Shards onchain contracts (hook, ERC-20, ERC-721, fee accumulator/splitter, router, donation forwarder) plus the v1 spec record.
- Adds extensive unit/integration/fuzz/invariant coverage for market paths, inventory rules, fee accounting, and locked-liquidity properties.
- Registers the model in the repo’s model registry and documentation index.
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/ShardHookV1.sol |
Core Uniswap v4 hook implementing locked liquidity + market paths + fee capture/splitting. |
src/ShardFeeDistributorV1.sol |
Holder fee accumulator with escrow/dust carry and same-block accrual guard; includes builder/launcher accrual bookkeeping. |
src/ShardFeeForwarderV1.sol |
Minimal “plain ETH in → donate()” forwarder for holder donations. |
src/ShardNFTV1.sol |
ERC-721 “archive” inventory + seed-based art lifecycle rules (regenerate on acquire, preserve on transfers). |
src/ShardSwapRouterV1.sol |
Minimal ETH↔SHARD third-party router intended to hold no funds. |
src/ShardTokenV1.sol |
Fixed-supply ERC-20 token contract (10,000e18 minted at construction). |
src/ShardConstantsV1.sol |
Model constants (supply, fee bps, tick spacing, seed split, precision). |
src/ShardErrorsV1.sol |
Centralized custom errors for Shards contracts. |
src/interfaces/IShardHookV1.sol |
Minimal hook interface for NFT transfer settlement + claimable view. |
src/interfaces/IShardNFTV1.sol |
Minimal NFT interface for hook-driven acquire/release + supply views. |
src/interfaces/IShardRendererV1.sol |
Renderer interface for live/dormant SVG and attributes. |
test/ShardTokenV1.t.sol |
ERC-20 supply/metadata/no-mint-no-burn tests. |
test/ShardSwapRouterV1.t.sol |
Router swap/refund/deadline/minOut/approval tests. |
test/ShardScaffoldV1.t.sol |
Dependency “surface area” compile checks (imports/types/constants). |
test/ShardNFTV1.t.sol |
Archive mechanics, seed rules, transfer guards, tokenURI behaviors. |
test/ShardLaunchSequenceV1.t.sol |
Launch ordering, tick price sanity, one-shot wiring/initialise checks. |
test/ShardHookMarketV1.t.sol |
Hook-market buy/sell/redeem/claim behaviors and fee basis assertions. |
test/ShardHookLiquidityV1.t.sol |
initialise(), seeding, hook permissions/address bits, lock/no-withdrawal assertions. |
test/ShardHookExhaustionV1.t.sol |
Thin-curve exhaustion/short-fill behavior and buyMax fee-basis regression coverage. |
test/ShardFeeSplitV1.t.sol |
80/10/10 split arithmetic, beneficiary claims, recipient handover tests. |
test/ShardFeeDonationV1.t.sol |
donate() and forwarder flush behavior; donation bypasses split. |
test/ShardFeeDistributorV1.t.sol |
Accumulator/escrow/dust settlement paths and conservation/solvency checks. |
test/ShardHookBatchV1.t.sol |
Batch buy/sell/redeem paths and MAX_BATCH behavior. |
test/ShardHookFeesV1.t.sol |
Third-party swap fee capture across quadrants + swap cap behaviors. |
test/ShardHookAttackV1.t.sol |
Adversarial-path tests (reentrancy/withdrawal/front-run/callback abuse, etc.). |
test/invariant/ShardHandlerV1.sol |
Invariant-test handler driving randomized sequences. |
test/invariant/ShardV1.t.sol |
Stateful invariants for backing, solvency/custody, and lock permanence. |
test/GeometricRendererV1.t.sol |
Renderer determinism, external-reference avoidance, trait distribution tests. |
spec/shards-v1.json |
Versioned spec record (parameters, permissions, fee math, pinned deps). |
models/shards/README.md |
Model-level documentation of behavior, lifecycle, economics, and release gates. |
models/shards/SECURITY.md |
Security assumptions/invariants/limitations for the model. |
models/shards/TEST_PLAN.md |
Test-plan mapping from suites to owned behaviors and stated gaps. |
models/shards/model.json |
Model manifest entry (status/design gates, docs pointers). |
models/registry.json |
Adds shards model entry and updates registry timestamp. |
MODELS.md |
Adds Shards section to the repo’s model index. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- reword NatSpec references to files that only exist in the reference repo; document that arbBlockNumber() contributes zero entropy on Ethereum mainnet (fails closed) in both code and SECURITY.md - correct the holder-claim payout description and the poolKey immutability wording in README/SECURITY - state the fuzz bound of testFuzz_split_conserves accurately - disclose the creator-share deviation for the acceptance record - restore the pre-existing registry entry order (append shards only) - drop the unused FlushFailed error; MIT-license the scaffold test
39254b0 to
2fab693
Compare
# Conflicts: # .gas-snapshot # README.md # models/registry.json # releases/classic-v3/manifest.json
Shards already declared Ethereum (chainId 1) and its Arbitrum-only entropy source degrades to zero off Arbitrum, but nothing exercised the model against the real Uniswap v4 PoolManager it would market-make on. Add ShardV1MainnetForkTest, which mines and deploys the hook against the pinned canonical v4 deployment on a Mainnet fork, wires and initialises the locked position, then drives the full lifecycle: a first-batch buy, a capped third-party swap, a redeem, a hook-market sell, all three claim paths and a donation, asserting the backing invariant survives every step. It pins the PoolManager runtime code hash and confirms art still regenerates when the entropy precompile returns zero. The suite needs an archive RPC, so exclude it from the default CI test and gas-snapshot runs exactly as the Classic fork suite is excluded. Update the model README, TEST_PLAN and release gates to record the coverage as delivered.
|
Updated this branch to make the Shards model exercisable on Ethereum Mainnet and to clear the merge conflicts with Mainnet v4 coverage
Merge conflicts
Full local gate is green: |
The coverage job runs every non-fork test to measure source coverage; the Shards fork suite needs an archive RPC and must be excluded there too, exactly as ClassicV3MainnetForkTest already is.
Add the Shards fork suite to the scheduled-and-on-PR Ethereum Evidence workflow, alongside the Classic one and behind the same public-RPC retry loop, so the Mainnet-v4 lifecycle is exercised by CI rather than only on demand. Rework the entropy test to assert the load-bearing facts — that the acquisition mints at all (the absent ARB_SYS precompile degrades to zero instead of reverting the gas-capped staticcall) and that the two pieces render different on-chain art — since seeds always differ via the per-acquisition nonce and so proved nothing on their own.
programmable-infra
left a comment
There was a problem hiding this comment.
CHANGES REQUIRED — Reviewed PR head f3ec992. Build, 360/360 tests including fork coverage, 18 invariants, PoolManager authentication, settlement and the fee split passed. Required fixes: replace the manual two-step hook/NFT wiring with an atomic factory and exact bidirectional NFT-to-hook validation so setNFT cannot bind an unintended contract; complete the model, specification, release and deployment metadata; use checked ERC20 transfers; and provide reproducible mined-address and fork launch evidence. Platform routing and UI/indexer work remains separate. Every new commit requires re-review.
Review fixes pushed —
|
…ng verify.yml The fork suite now self-skips when ETHEREUM_RPC_URL is unset, so the default forge test/snapshot run no longer needs a --no-match-contract entry in the shared verify.yml workflow. Restores verify.yml to the base-branch content and regenerates the fixed-seed gas snapshot for the wider default test set.
CI follow-up —
|
programmable-infra
left a comment
There was a problem hiding this comment.
Programmable verification: CHANGES REQUIRED
Reviewed immutable PR head: bbe4dce
Shards is technically advanced and includes real atomic launch code, but two fee-policy gaps and an incomplete immutable launch binding remain.
Applicant changes, in priority order:
- Make the Programmable 10 bps entitlement cumulative and split-invariant. ShardHookV1.sol:422-425 and ShardFeeDistributorV1.sol:69-80 floor independently. Ten 900-wei swaps currently produce zero Programmable fee while one aggregated 9,000-wei swap produces nine wei. Add carried remainder and exact-output gross-up tests across all four quadrants.
- Bind the immutable recipient to 0x4957f49620AFf3Adbbe8195a4f633E49cc93376c. ShardLaunchFactoryV1.sol:85-93 accepts an arbitrary recipient and releases/shards-v1/mainnet-manifest.json still leaves it null. Keep the admin wallet separate.
- Close the exact data-only launch plan. models/shards/model.json still lists contracts as empty and the mainnet manifest contains null/not-run/releaseEligible:false fields. Bind deployer/factory, salts, expected addresses, configuration hash, builder, treasury, transaction order, and postconditions.
- Regenerate the fee specification and regression evidence, then provide a maintained pinned mainnet-fork lifecycle run. The local archive RPC limitation is tooling-blocked, not counted as a contract failure.
Passed: real CREATE2 factory/script, permission and occupancy checks, atomic rollback, PoolManager authentication, five exact permission bits, canonical PoolKey, all swap quadrants, partial-fill fail-closed, claims/settlement, permanent add-only liquidity, no admin/upgrade/withdraw path, 447 passing tests plus 19 invariants, and no reproduced rug/drain issue. One RPC fork test was skipped.
Estimated applicant work: 1-3 engineering days. Re-review: 3-6 hours with working archive RPC.
Platform work after applicant pass: custom model adapter/compiler, registry/UI/API/indexer/trading/claim integration, deployment authorization, runtime readback, monitoring, and signed final verification.
A normal user cannot launch a directly tradable custom coin from this exact revision. Any new commit invalidates this reviewed SHA and must be reverified.
Take the combined builder+launcher (20%) cut of each swap fee with a carried remainder so a stream of tiny swaps can no longer floor the entitlement to zero; split it evenly with the odd wei carried to the launcher. Bind the launcher (Programmable 0.10%) recipient to an immutable constant and drop it from the factory constructor. Deploy the factory through the canonical CREATE2 proxy so its address is nonce-independent, and pin the full data-only launch plan. Add four-quadrant split coverage, regenerate the fee spec, security properties, runbook and mainnet-fork evidence.
Review fixes pushed —
|
programmable-infra
left a comment
There was a problem hiding this comment.
Programmable verification: CHANGES REQUIRED
Reviewed immutable PR head: 0bc629a
The four changes requested on the prior SHA are substantively addressed: the Programmable recipient is fixed, the inner operator split carries its remainder, and the CREATE2 launch plan is pinned. Two applicant issues remain.
- Make the outer swap-fee calculation cumulative and transaction-frequency invariant.
src/ShardHookV1.sol:422-425still floors the 1% fee independently on every swap before_distributeFeereceives it. A reviewer regression with eleven successful 99-wei exact-input swaps produced 1,089 wei of qualifying volume, zero total fee, and zero Programmable accrual, although cumulative 10 bps is already at least one wei. Carry the outer fee remainder across the supported paths and add fragmented-versus-aggregated regressions for all supported swap quadrants and hook-owned market paths. - Restore the declared build-size gate. With the exact source and current stable Foundry,
forge build --sizesexits nonzero becauseFeeHookHarnessis 24,606 bytes, 30 bytes above EIP-170. The productionShardHookV1is 24,420 bytes and remains below the limit; the required repository command itself must also exit successfully without hiding production-size failures.
Passed on this SHA: immutable 0x4957f49620AFf3Adbbe8195a4f633E49cc93376c binding, cumulative inner split, complete CREATE2 predictions and atomic launch, exact permission bits, PoolManager and PoolKey authentication, settlement and claim solvency, permanent add-only liquidity, no admin/upgrade/withdraw path, 449 passing tests, and 19 invariants with 128,000 calls each. The Mainnet fork suite was not independently rerun because no archive RPC was available. Slither completed with known/parser-limited findings and no new reproduced drain or rug path.
Platform work remains separate after applicant pass: model adapter/compiler, registry and product integration, deployment authorization, runtime verification, monitoring, and the signed final-verification path.
A normal user cannot launch this exact revision as a tradable Custom Launch. Any new commit is a new review target.
…nder EIP-170 Carry the outer 1% fee's per-swap remainder on both bases (feeCarryIn for exact-input, feeCarryOut for exact-output) so a stream of tiny swaps accrues the same total as one aggregated swap instead of each flooring to zero. beforeSwap hands afterSwap the exact charged fee via a scratch slot so the partial-fill reconstruction never double-consumes the carry; buyMax clamps to its reserve. Reclaim runtime size (unified _chargeFee, inlined the pure peek, deduped the repeated fee-held post-condition) and merge FeeHookHarness's three test wrappers into one dispatcher so every contract stays under EIP-170 and forge build --sizes exits zero. Re-pin the CREATE2 launch plan and regenerate the fee spec, security notes, runbook and mainnet-fork evidence for the new bytecode.
Both issues addressed —
|
programmable-infra
left a comment
There was a problem hiding this comment.
Programmable verification: CHANGES REQUIRED
Reviewed immutable PR head: f278735
The two previous blockers are substantially fixed: the repository size gate now passes, and the new carry logic preserves the outer fee on the ordinary exact-input and exact-output paths. One reproducible applicant issue remains.
- In
src/ShardHookV1.sol:692-714,buyMaxconsumes the exact-output fee carry in_chargeFeeand then discards the carried wei whenfee > maxFeeis clamped. Two fully consumedbuyMaxcalls of10 ether + 99 weieach collect200000000000000000wei of total fee instead of the cumulative200000000000000001wei. Repeating the boundary repeats the loss, so this is not one globally bounded rounding wei. Preserve cumulative charging against the actually executed qualifying volume without overspending the caller, and add a fragmented-versus-aggregated regression that validates both the outer fee and Programmable accrual. - Update
docs/security/SHARDS_PROPERTIES.md:89-93. Its claim that the outer fee is cumulative and transaction-frequency invariant is contradicted by the currentbuyMaxbehavior; the stated residual is per qualifying call, not globally bounded.
Passed on this SHA: immutable Programmable recipient 0x4957f49620AFf3Adbbe8195a4f633E49cc93376c; cumulative inner operator split; exact five v4 permission bits; PoolManager and PoolKey authentication; settlement, claims, locked add-only liquidity, atomic CREATE2 launch, and no admin, upgrade, or withdrawal path. The exact repository CI suite produced 452 passing tests and one skipped Mainnet-fork test, all 19 Shards invariants passed, the public intake/model/release verifiers passed, and forge build --sizes passed with ShardHookV1 at 24,422 bytes and both fee harnesses below EIP-170. Slither completed with known/parser-limited warnings and no additional reproduced drain or rug path.
Applicant responsibility is limited to the fee-path and matching evidence above. Platform model integration, deployment authorization, registry/product/indexer support, monitoring, and signed final verification remain separate platform work after the applicant revision passes.
A normal user cannot launch this exact revision as an immediately tradable Custom Launch. Any new commit is a new review target.
…edding it When buyMax clamps the inclusive fee to its exact-input reserve so the buyer is not overspent, return the uncollected wei to feeCarryOut instead of dropping it, so the entitlement is preserved and collected on a later exact-output swap rather than shed once per qualifying call. Reclaim the added size by extracting the shared buy head (_chargeBuy) from buyNFT and buyMany, keeping every contract under EIP-170. Add a boundary regression proving the clamped wei is carried and Programmable still accrues, update the fee docs, and re-pin the launch plan.
buyMax fee carry fixed —
|
programmable-infra
left a comment
There was a problem hiding this comment.
Programmable verification: READY FOR FINAL VERIFICATION
Reviewed immutable PR head: b8b3cb8
The previous buyMax fee blocker is fixed. An independent reviewer regression exercised ten clamp-boundary calls and a later exact-output charge: the carried whole wei were released exactly once, the cumulative outer fee remained exact, and the Programmable and builder shares each matched their cumulative entitlement. The buyer is never overspent.
Passed on this exact revision: formatting; build and EIP-170 size gates; 454 local tests; all 19 Shards stateful invariants; the 4-test pinned Ethereum Mainnet v4 lifecycle; registry, release-evidence and documentation verifiers; and coverage floors at 93.15% lines, 90.54% functions and 58.71% branches. PoolManager authentication, the exact five hook permission bits, all four swap quadrants, partial-fill fail-closed behavior, ERC-6909 settlement and claims, backing and solvency, atomic CREATE2 launch, permanent add-only liquidity, and the absence of an admin, upgrade or liquidity-withdrawal path were rechecked. The immutable Programmable recipient is 0x4957f49620AFf3Adbbe8195a4f633E49cc93376c; the admin/deployer wallet remains separate.
No applicant change is required for this decision. Keep this commit unchanged; any new commit is a new review target.
This approval prepares the exact revision for the signed final-verification path. It is not a Mainnet deployment or immediate launch authorization. A normal user cannot launch it as a tradable product today; platform merge, deployment, product integration, monitoring and final authorization remain Programmable work.
Submission type
Change
Shards is a launch model for on-chain generative art: each launch deploys a fresh hook, ERC-20, ERC-721 and renderer for one creator's fixed 10,000-piece collection, market-made on a single-sided bonding curve whose liquidity is permanently locked. Art regenerates on every pool acquisition and never on a wallet-to-wallet transfer, so a piece's history is part of the artwork. It serves creators who want a collection with a built-in, always-on market instead of a mint-then-hope drop, and traders who want continuous exit liquidity. It belongs in Programmable as a distinct model shape: an NFT-collection market rather than a fungible-token launch.
For a new launch model:
jesse-stahl0xceeBB3A6543CeBEB2ED66963897A0abEA52A50cCsrc/ShardHookV1.sol,src/ShardFeeDistributorV1.sol,src/ShardNFTV1.sol,src/ShardTokenV1.sol,src/GeometricRendererV1.sol,src/ShardSwapRouterV1.sol,src/ShardFeeForwarderV1.sol,src/ShardConstantsV1.sol,src/ShardErrorsV1.sol,src/interfaces/models/shards/README.md,models/shards/SECURITY.md,models/shards/TEST_PLAN.md,models/shards/model.json,spec/shards-v1.jsonModel behavior
currency0. All 10,000 SHARD (each backing one NFT) are locked into a full-range plus concentrated-band single-sided position atinitialise(). There is no withdrawal path for the liquidity.beforeInitialize,beforeSwap,afterSwap,beforeSwapReturnDelta,afterSwapReturnDelta. The swap deltas take the 1.00% fee on the ETH leg;beforeInitializevalidates the pool key and exact start price so a front-runner cannot initialise the canonical pool on different terms.gross * 100 / 10000; exact-out:net * 100 / 9900). At accrual it splits:fee * 1000 / 10000to the builder beneficiary, the same to Programmable's recipient, and the remainder — including all rounding dust — to collection holders via a running per-NFT accumulator. That is 0.80% / 0.10% / 0.10% of swap volume. Fees are native ETH, held by the hook until claimed. Donations viadonate()reach holders whole and are never split.arbBlockNumber()staticcall used for entropy, and raw ETH transfers for refunds and claims. Dependencies are this repository's pinned libs; compiler fixed at solc 0.8.26 (spec records lib commits and bytecode size).setNFT+initialise),builderFeeRecipient(claims its share; may hand the role on viasetBuilderFeeRecipient), the immutablelauncherFeeRecipient(claims Programmable's share), and holder claimants. No admin keys, no pausing, nothing upgradeable.shard.balanceOf(hook) == circulating * 1e18 + seedDust;builderCut + launcherCut + holderAmount == feeexactly; beneficiary-only claims. Failure modes and boundaries (per-swap size cap, partial-fill rejection, 188 bytes of EIP-170 headroom, no factory yet) are documented inmodels/shards/SECURITY.md.Verification
node scripts/verify-model-registry.mjsnode scripts/verify-release-evidence.mjsforge fmt --checkforge buildFOUNDRY_PROFILE=ci forge test(--no-match-contract ClassicV3MainnetForkTest, as inverify.yml: 393 passed, 0 failed; the fork suite needs the CI RPC secret)contracts: [],deployment: null)Gas-snapshot note: all new entries are
Shard*V1/GeometricRendererV1. Seven pre-existing fuzz entries changed only in their μ column (medians identical, deltas under 0.1%) because new contracts in the artifact set alter fuzz input sampling; regenerated with the pinned seed and the exactverify.ymlcommand.Test-plan notes recorded honestly in
models/shards/TEST_PLAN.md: no suite yet assertsEthTransferFailedfor a reverting recipient on the claim/refund paths, and the source repo's deploy-script tick-validation tests were not ported because no deploy script ships in this submission — both are listed as required before release, alongside a mainnet-fork lifecycle test.Security
New hook permissions: the five listed above, all disclosed in
models/shards/README.mdwith the return-delta quadrants. New accounting paths: the 80/10/10 split at_distributeFee, the two beneficiary accruals with beneficiary-only claims, and the holder accumulator with escrow/dust carry. External calls: PoolManager, the model's own contracts, the entropy staticcall, raw ETH sends. Trust assumptions: no admin keys; deployer power ends aftersetNFT+initialise; the builder payout address is builder-controlled; Programmable's recipient is immutable. Details inmodels/shards/SECURITY.md.No undisclosed vulnerability is included.
Submission terms
For a new launch model: