Ops: exhaustive onchain verification — TimeCurve, DOUB, vesting, Kumbaya routing, and dependencies #207

Closed
opened 2026-05-13 16:07:15 +00:00 by PlasticDigits · 106 comments
PlasticDigits commented 2026-05-13 16:07:15 +00:00 (Migrated from gitlab.com)

Assignment / goal

Verifier: complete an onchain configuration audit for production (or target network) deployments of TimeCurve, Doubloon (DOUB), DoubPresaleVesting, and all contracts they depend on or route value through — with emphasis on AccessControl / Ownable / UUPS admin surfaces, Kumbaya buy routing (TimeCurveBuyRouter ↔ TimeCurve), wiring between contracts (addresses, roles, token approvals paths), and numeric / initializer correctness vs product specs.

Deliverables: (1) dated report (Markdown or doc link) with tx hashes / block explorers or cast call transcripts for every checked item; (2) explicit PASS / FAIL / N/A per sub-item; (3) list of any drift vs contracts/PARAMETERS.md, docs/operations/deployment-guide.md, docs/operations/final-signoff-and-value-movement.md (issue #55), and docs/integrations/kumbaya.md (issue #46).


Global preflight (all contracts)

  • Chain identity: chainid matches intended deployment (no accidental verify on testnet artifact for mainnet checklist).
  • Proxy discipline: every user-facing address is the ERC-1967 proxy, not implementation (run-latest.json / broadcast pitfalls — GitLab #61).
  • Artifact parity: deployed bytecode / ABI labels match the tagged release commit; record implementation addresses for UUPS proxies separately.
  • Admin liveness: Ownable2Step / governance: no pending owner transfer stranded in limbo; DEFAULT_ADMIN_ROLE holders are intended multisigs (not deployer EOAs where policy forbids — GitLab #120).
  • Pausing / gates: document current values of all value-movement gates (#55) before and after any verification txs.

1. Doubloon (Doubloon — DOUB ERC-20)

1.1 Deployment / constructor

  • Constructor admin: non-zero; matches intended DEFAULT_ADMIN_ROLE holder (multisig policy).
  • Name / symbol / decimals: "Doubloon", "DOUB", 18 (or document intentional deviation).

1.2 Access control

  • DEFAULT_ADMIN_ROLE: exactly who holds it; whether renounce is impossible / undesirable; alignment with ops runbook.
  • MINTER_ROLE: granted only to expected minter(s) (canonical: RabbitTreasury per PARAMETERS.md / fee-routing doc); no extra EOAs; no vesting contract holds MINTER_ROLE unless explicitly designed.
  • Burn model (GitLab #132): confirm no BURNER_ROLE; third-party burns require allowance to burnFrom caller; MINTER_ROLE cannot burn arbitrary wallets.

1.3 Supply / minting invariants (read-only)

  • totalSupply consistent with genesis plan (TimeCurve sale bucket, vesting funded amounts, Rabbit minting policy — cross-check PARAMETERS.md § DOUB genesis).
  • No unexpected mint paths: only MINTER_ROLE can mint.

1.4 Interconnections

  • TimeCurve launchedToken: equals this DOUB address when sale allocates DOUB.
  • DoubPresaleVesting token: equals this DOUB address.
  • RabbitTreasury (if in scope): holds MINTER_ROLE and mint policy matches Burrow docs.

2. DoubPresaleVesting (UUPS proxy)

2.1 Initialization parameters

  • token: is Doubloon proxy address; decimals 18; contract is ERC20Burnable-compatible for owner burn helpers.
  • initialOwner: matches intended Ownable admin; owner() read matches.
  • Beneficiary set: beneficiaries[] unique, all non-zero; no duplicates (re-run offchain hash of sorted set vs onchain enumeration if exposed).
  • amounts[]: all > 0; length matches beneficiaries.
  • requiredTotalAllocation: equals sum(amounts) exactly (initializer revert otherwise — still verify post-deploy).
  • vestingDuration: matches policy (canonical presale: 180 days in seconds).
  • totalAllocated: matches requiredTotalAllocation after init.

2.2 Vesting lifecycle

  • vestingStart: 0 until startVesting(); after start, timestamp matches intended TGE.
  • startVesting preconditions: token.balanceOf(this) >= totalAllocated was satisfied at start (funding shortfall = critical failure).
  • claimsEnabled: matches operational phase (false until signoff — final-signoff #55); record setter tx.

2.3 Per-beneficiary math (spot checks)

  • For 3+ sample beneficiaries: allocationOf, claimedOf, and offchain spreadsheet of cliff (30%) + linear (70%) vs block.timestamp (or chosen reference time).
  • claim reverts when claimsEnabled == false (evidence).
  • claim succeeds for a dust beneficiary when enabled (optional staging test).

2.4 Access control (owner-only surfaces)

  • setClaimsEnabled: only owner; intended multisig.
  • reduceAllocationsUniformBps: only owner; document max safe bps if ever used; verify no row would fall below claimedOf (revert semantics).
  • burnDoubExcessAboveOutstanding: only owner; only when token is burnable; verify post-burn balance still covers outstanding liability.
  • rescueERC20 (GitLab #137): only owner; kind 0 never removes DOUB below reserve for claims; kind 1 only non-vesting tokens.

2.5 UUPS upgrade path

  • UPGRADE_INTERFACE: implementation slot matches expected version.
  • Upgrade admin: _authorizeUpgrade restricted to owner (or documented governance).

2.6 Interconnections

  • TimeCurve.doubPresaleVesting: points to this vesting proxy (or zero if presale boost disabled — document which).
  • Presale CHARM +15%: if used, beneficiary registry / first-wallet rules per deployment-guide.md and PresaleCharmBeneficiaryRegistry (subsection below).

3. PresaleCharmBeneficiaryRegistry (if deployed)

  • Proxy vs impl addresses recorded.
  • AccessControl: admin + any role grants; who can set isBeneficiary.
  • TimeCurve wiring: TimeCurve reads registry as documented; addresses match deployment artifact.
  • Beneficiary flags: intended wallets marked; no unintended EOAs.

4. TimeCurve (UUPS proxy)

4.1 Core asset wiring

  • acceptedAsset: canonical CL8Y (standard ERC-20, no FoT/rebase); matches indexer / frontend env.
  • launchedToken: canonical DOUB (Doubloon proxy).
  • feeRouter: FeeRouter proxy; non-zero.
  • podiumPool: PodiumPool proxy; non-zero.
  • referralRegistry: intended address or address(0) if referrals disabled (matches product).
  • doubPresaleVesting: vesting proxy or zero per design.

4.2 TimeCurveBuyRouter (Kumbaya single-tx)

  • timeCurveBuyRouter: if non-zero, equals deployed TimeCurveBuyRouter proxy (not router implementation / wrong chain).
  • Mutual trust: router’s immutable timeCurve matches this proxy; TimeCurve.buyFor path authorized.
  • If router is zero: document that ETH/USDM single-tx entry is intentionally off; frontend/env must not claim otherwise (GitLab #65, #78).

4.3 Sale schedule / pricing inputs

  • saleStart / deadline / ended: consistent with launch plan; startSaleAt epoch if used (GitLab #114).
  • initialMinBuy, growthRateWad, cap multiples, extension and timer cap seconds: compare to PARAMETERS.md rows and deployment script inputs.
  • buyCooldownSec: > 0 at deploy; matches ops expectation (GitLab #88 for Anvil-only shortcuts).
  • MAX_SALE_ELAPSED_SEC: canonical cap (GitLab #124); deadline clamp behavior understood.
  • totalTokensForSale: matches 200M policy (or documented exception).

4.4 Fee split (bps)

  • feeSplitDoubLpBps + burn + podium + team + rabbit == 10_000 (or document rounding sink behavior per FeeRouter).
  • Sink addresses on FeeRouter match intended treasuries / DoubLPIncentives / burn sink / PodiumPool / RabbitTreasury / ecosystem.

4.5 WarBow / podium constants (read-only sanity)

  • Spot-check fixed constants (WARBOW_*, steal/guard burns, drain BPS, UTC limits) vs PARAMETERS.md and TimeCurve.sol comments.
  • Podium category weights inside TimeCurve (40/25/20/15 of pool; placements 4:2:1) unchanged from spec.

4.6 Referral parameters

  • REFERRAL_EACH_BPS (canonical 500 each side unless governance changed).
  • Referral registry code hashing consistent with frontend (hashCode).

4.7 Value-movement gates (GitLab #55)

  • buyFeeRoutingEnabled: expected for live sale (typically true); document emergency flip.
  • charmRedemptionEnabled: expected false until post-end signoff.
  • reservePodiumPayoutsEnabled: expected false until CL8Y podium signoff.
  • prizesDistributed / empty-pool settlement (#133): state machine understood.

4.8 Unredeemed launch allocation (GitLab #128)

  • unredeemedLaunchedTokenRecipient: set before any sweep policy requires; zero if intentionally unset.
  • saleEndedAt / grace: not manipulated; repairSaleEndedAt only if upgrade migration required.

4.9 Access control / upgrades

  • Ownable / Ownable2Step: owner(); pending owner; two-step acceptance completed.
  • UUPS: _authorizeUpgrade only owner; implementation address recorded.
  • Privileged setters: each onlyOwner function list reviewed (buy router, fee splits, gates, sinks, residual recipient, etc.).

4.10 Interconnections (matrix)

  • FeeRouter.distributeFees caller: TimeCurve holds expected role on FeeRouter (GitLab #120 zero-admin checks).
  • PodiumPool: payout pull pattern matches TimeCurve.distributePrizes expectations.
  • DoubLPIncentives / launch anchor (GitLab #158): LP seeding and 1.275× rule consistent with onchain state (read DoubLPIncentives + TimeCurve views as applicable).

5. TimeCurveBuyRouter (immutable companion)

5.1 Constructor immutables

  • timeCurve: TimeCurve proxy address.
  • kumbayaRouter: matches Kumbaya / integrator-kit documented router for chain (GitLab #46).
  • weth: canonical wrapped native for chain.
  • stableToken: MegaETH USDm (or address(0) if ETH-only deployment — document).
  • cl8yProtocolTreasury: non-zero sink for CL8Y dust (GitLab #70).
  • initialOwner: matches ops; Ownable2Step transfer state clean.

5.2 Path / swap semantics (GitLab #65, #117)

  • Packed path encoding: documented test vector: exactOutput path ends in WETH or stable per payKind; starts from CL8Y = acceptedAsset.
  • Sale phase mirroring (#118): buyViaKumbaya reverts before swap when block.timestamp < saleStart() if scheduled.
  • Refund invariants (#117): marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit).

5.3 Rescue surfaces

  • rescueETH / rescueERC20: only owner; intended for stranded funds post-incident.

5.4 Interconnections

  • TimeCurve.setTimeCurveBuyRouter(this) executed; onchain timeCurveBuyRouter matches.
  • Frontend VITE_KUMBAYA_TIMECURVE_BUY_ROUTER: if set, equals onchain router (fail-closed — kumbaya docs).

6. FeeRouter (AccessControl)

  • Sink addresses (5): match TimeCurve fee split targets; zero-address rules per deploy script.
  • Role matrix: DEFAULT_ADMIN_ROLE, FEE_ROUTER role granted to TimeCurve only; no stray distributors.
  • distributeFees: only callable by authorized distributor; revert for non-distributor (evidence).
  • Rounding: last sink receives remainder; documented behavior matches tests.

7. PodiumPool (prize vault)

  • Asset: CL8Y (accepted asset) alignment with TimeCurve.
  • Payout pull / PrizePaid: role or allowance model matches TimeCurve post-end flow.
  • AccessControl: admin + any minter/payer roles reviewed.

8. ReferralRegistry (if enabled)

  • cl8yToken: matches CL8Y (not wrong treasury token — referrals product doc).
  • registrationBurnAmount: matches intended economics.
  • Owner / upgrade (UUPS): admin surfaces documented.

9. Cross-contract integration checklist (end-to-end reads)

  • TimeCurve → FeeRouter: buy fee path enabled flag + live distributeFees role.
  • TimeCurve → ReferralRegistry: referralRegistry address and ReferralApplied path uses same registry as frontend.
  • TimeCurve ↔ DoubPresaleVesting: presale CHARM bonus pointer and PRESALE_CHARM_WEIGHT_BPS behavior (GitLab #202 registry if any).
  • Kumbaya env ↔ onchain: quoter/router/WETH/USDM/fees from docs/integrations/kumbaya.md match deployment for same chain id.
  • Indexer / ADDRESS_REGISTRY: optional cross-check that published JSON matches verified addresses (GitLab #156).

10. Numeric / document cross-check (sign-off table)

Source doc Action
contracts/PARAMETERS.md Every TODO row resolved or explicitly waived with governance sign-off
docs/operations/final-signoff-and-value-movement.md Gate defaults and go-live order recorded
docs/onchain/fee-routing-and-governance.md Role holders and sink policy
docs/product/primitives.md Podium categories, redemption economics, referral math
docs/integrations/kumbaya.md Router buy path, MegaETH vs Anvil

References (non-exhaustive)

/cc @PlasticDigits/yieldomega

## Assignment / goal **Verifier:** complete an **onchain configuration audit** for production (or target network) deployments of **TimeCurve**, **Doubloon (DOUB)**, **DoubPresaleVesting**, and **all contracts they depend on or route value through** — with emphasis on **AccessControl / Ownable / UUPS admin surfaces**, **Kumbaya buy routing** (`TimeCurveBuyRouter` ↔ `TimeCurve`), **wiring between contracts** (addresses, roles, token approvals paths), and **numeric / initializer correctness** vs product specs. **Deliverables:** (1) dated report (Markdown or doc link) with **tx hashes / block explorers** or `cast call` transcripts for every checked item; (2) explicit **PASS / FAIL / N/A** per sub-item; (3) list of **any drift** vs `contracts/PARAMETERS.md`, `docs/operations/deployment-guide.md`, `docs/operations/final-signoff-and-value-movement.md` (issue #55), and `docs/integrations/kumbaya.md` (issue #46). --- ## Global preflight (all contracts) - [x] **Chain identity:** `chainid` matches intended deployment (no accidental verify on testnet artifact for mainnet checklist). - [x] **Proxy discipline:** every user-facing address is the **ERC-1967 proxy**, not implementation (`run-latest.json` / broadcast pitfalls — [GitLab #61](https://gitlab.com/PlasticDigits/yieldomega/-/issues/61)). - [x] **Artifact parity:** deployed bytecode / ABI labels match the tagged release commit; record **implementation** addresses for UUPS proxies separately. - [x] **Admin liveness:** `Ownable2Step` / governance: no **pending** owner transfer stranded in limbo; `DEFAULT_ADMIN_ROLE` holders are intended multisigs (not deployer EOAs where policy forbids — [GitLab #120](https://gitlab.com/PlasticDigits/yieldomega/-/issues/120)). - [x] **Pausing / gates:** document current values of all **value-movement gates** (#55) before and after any verification txs. --- ## 1. Doubloon (`Doubloon` — DOUB ERC-20) ### 1.1 Deployment / constructor - [x] **Constructor `admin`:** non-zero; matches intended **`DEFAULT_ADMIN_ROLE`** holder (multisig policy). - [x] **Name / symbol / decimals:** `"Doubloon"`, `"DOUB"`, **18** (or document intentional deviation). ### 1.2 Access control - [x] **`DEFAULT_ADMIN_ROLE`:** exactly who holds it; whether **renounce** is impossible / undesirable; alignment with ops runbook. - [x] **`MINTER_ROLE`:** granted **only** to expected minter(s) (canonical: **`RabbitTreasury`** per `PARAMETERS.md` / fee-routing doc); **no** extra EOAs; **no** vesting contract holds `MINTER_ROLE` unless explicitly designed. - [x] **Burn model ([GitLab #132](https://gitlab.com/PlasticDigits/yieldomega/-/issues/132)):** confirm **no** `BURNER_ROLE`; third-party burns require **allowance** to `burnFrom` caller; `MINTER_ROLE` cannot burn arbitrary wallets. ### 1.3 Supply / minting invariants (read-only) - [x] **`totalSupply`** consistent with genesis plan (TimeCurve sale bucket, vesting funded amounts, Rabbit minting policy — cross-check `PARAMETERS.md` § DOUB genesis). - [x] **No unexpected mint paths:** only `MINTER_ROLE` can `mint`. ### 1.4 Interconnections - [x] **TimeCurve `launchedToken`:** equals this DOUB address when sale allocates DOUB. - [x] **DoubPresaleVesting `token`:** equals this DOUB address. - [x] **RabbitTreasury** (if in scope): holds `MINTER_ROLE` and mint policy matches Burrow docs. --- ## 2. DoubPresaleVesting (UUPS proxy) ### 2.1 Initialization parameters - [x] **`token`:** is **Doubloon** proxy address; decimals **18**; contract is **`ERC20Burnable`**-compatible for owner burn helpers. - [x] **`initialOwner`:** matches intended **`Ownable`** admin; **`owner()`** read matches. - [x] **Beneficiary set:** `beneficiaries[]` unique, all non-zero; no duplicates (re-run offchain hash of sorted set vs onchain enumeration if exposed). - [x] **`amounts[]`:** all **> 0**; length matches `beneficiaries`. - [x] **`requiredTotalAllocation`:** equals **`sum(amounts)`** exactly (initializer revert otherwise — still verify post-deploy). - [x] **`vestingDuration`:** matches policy (canonical presale: **180 days** in seconds). - [x] **`totalAllocated`:** matches `requiredTotalAllocation` after init. ### 2.2 Vesting lifecycle - [x] **`vestingStart`:** `0` until `startVesting()`; after start, timestamp matches intended TGE. - [x] **`startVesting` preconditions:** `token.balanceOf(this) >= totalAllocated` was satisfied at start (funding shortfall = critical failure). - [x] **`claimsEnabled`:** matches operational phase (**false** until signoff — [final-signoff #55](https://gitlab.com/PlasticDigits/yieldomega/-/issues/55)); record setter tx. ### 2.3 Per-beneficiary math (spot checks) - [x] For **3+ sample** beneficiaries: `allocationOf`, `claimedOf`, and **offchain spreadsheet** of cliff (30%) + linear (70%) vs `block.timestamp` (or chosen reference time). - [x] **`claim` reverts** when `claimsEnabled == false` (evidence). - [x] **`claim` succeeds** for a dust beneficiary when enabled (optional staging test). ### 2.4 Access control (owner-only surfaces) - [x] **`setClaimsEnabled`:** only owner; intended multisig. - [x] **`reduceAllocationsUniformBps`:** only owner; document max safe bps if ever used; verify **no row** would fall below `claimedOf` (revert semantics). - [x] **`burnDoubExcessAboveOutstanding`:** only owner; only when token is burnable; verify **post-burn** balance still covers outstanding liability. - [x] **`rescueERC20` ([GitLab #137](https://gitlab.com/PlasticDigits/yieldomega/-/issues/137)):** only owner; **kind 0** never removes DOUB below reserve for claims; **kind 1** only non-vesting tokens. ### 2.5 UUPS upgrade path - [x] **`UPGRADE_INTERFACE`:** implementation slot matches expected version. - [x] **Upgrade admin:** `_authorizeUpgrade` restricted to owner (or documented governance). ### 2.6 Interconnections - [x] **`TimeCurve.doubPresaleVesting`:** points to **this vesting proxy** (or zero if presale boost disabled — document which). - [x] **Presale CHARM +15%:** if used, beneficiary registry / first-wallet rules per `deployment-guide.md` and `PresaleCharmBeneficiaryRegistry` (subsection below). --- ## 3. PresaleCharmBeneficiaryRegistry (if deployed) - [x] **Proxy vs impl** addresses recorded. - [x] **AccessControl:** admin + any role grants; who can set `isBeneficiary`. - [x] **`TimeCurve` wiring:** `TimeCurve` reads registry as documented; addresses match deployment artifact. - [x] **Beneficiary flags:** intended wallets marked; no unintended EOAs. --- ## 4. TimeCurve (UUPS proxy) ### 4.1 Core asset wiring - [x] **`acceptedAsset`:** canonical **CL8Y** (standard ERC-20, no FoT/rebase); matches indexer / frontend env. - [x] **`launchedToken`:** canonical **DOUB** (`Doubloon` proxy). - [x] **`feeRouter`:** FeeRouter proxy; non-zero. - [x] **`podiumPool`:** PodiumPool proxy; non-zero. - [x] **`referralRegistry`:** intended address **or** `address(0)` if referrals disabled (matches product). - [x] **`doubPresaleVesting`:** vesting proxy or zero per design. ### 4.2 TimeCurveBuyRouter (Kumbaya single-tx) - [x] **`timeCurveBuyRouter`:** if non-zero, equals deployed **`TimeCurveBuyRouter`** proxy (not router implementation / wrong chain). - [x] **Mutual trust:** router’s immutable `timeCurve` matches this proxy; `TimeCurve.buyFor` path authorized. - [x] **If router is zero:** document that **ETH/USDM single-tx** entry is intentionally off; frontend/env must not claim otherwise ([GitLab #65](https://gitlab.com/PlasticDigits/yieldomega/-/issues/65), [#78](https://gitlab.com/PlasticDigits/yieldomega/-/issues/78)). ### 4.3 Sale schedule / pricing inputs - [x] **`saleStart` / `deadline` / `ended`:** consistent with launch plan; `startSaleAt` epoch if used ([GitLab #114](https://gitlab.com/PlasticDigits/yieldomega/-/issues/114)). - [x] **`initialMinBuy`**, **`growthRateWad`**, cap multiples, **extension** and **timer cap** seconds: compare to `PARAMETERS.md` rows and deployment script inputs. - [x] **`buyCooldownSec`:** **> 0** at deploy; matches ops expectation ([GitLab #88](https://gitlab.com/PlasticDigits/yieldomega/-/issues/88) for Anvil-only shortcuts). - [x] **`MAX_SALE_ELAPSED_SEC`:** canonical cap ([GitLab #124](https://gitlab.com/PlasticDigits/yieldomega/-/issues/124)); `deadline` clamp behavior understood. - [x] **`totalTokensForSale`:** matches **200M** policy (or documented exception). ### 4.4 Fee split (bps) - [x] **`feeSplitDoubLpBps` + burn + podium + team + rabbit == 10_000** (or document rounding sink behavior per `FeeRouter`). - [x] **Sink addresses** on `FeeRouter` match intended treasuries / `DoubLPIncentives` / burn sink / `PodiumPool` / `RabbitTreasury` / ecosystem. ### 4.5 WarBow / podium constants (read-only sanity) - [x] Spot-check fixed constants (`WARBOW_*`, steal/guard burns, drain BPS, UTC limits) vs `PARAMETERS.md` and `TimeCurve.sol` comments. - [x] **Podium category weights** inside `TimeCurve` (40/25/20/15 of pool; placements 4:2:1) unchanged from spec. ### 4.6 Referral parameters - [x] **`REFERRAL_EACH_BPS`** (canonical **500** each side unless governance changed). - [x] **Referral registry** code hashing consistent with frontend (`hashCode`). ### 4.7 Value-movement gates ([GitLab #55](https://gitlab.com/PlasticDigits/yieldomega/-/issues/55)) - [x] **`buyFeeRoutingEnabled`:** expected for live sale (typically **true**); document emergency flip. - [x] **`charmRedemptionEnabled`:** expected **false** until post-end signoff. - [x] **`reservePodiumPayoutsEnabled`:** expected **false** until CL8Y podium signoff. - [x] **`prizesDistributed` / empty-pool settlement (#133):** state machine understood. ### 4.8 Unredeemed launch allocation ([GitLab #128](https://gitlab.com/PlasticDigits/yieldomega/-/issues/128)) - [x] **`unredeemedLaunchedTokenRecipient`:** set before any sweep policy requires; zero if intentionally unset. - [x] **`saleEndedAt` / grace:** not manipulated; `repairSaleEndedAt` only if upgrade migration required. ### 4.9 Access control / upgrades - [x] **`Ownable` / `Ownable2Step`:** `owner()`; pending owner; two-step acceptance completed. - [x] **UUPS:** `_authorizeUpgrade` only owner; implementation address recorded. - [x] **Privileged setters:** each `onlyOwner` function list reviewed (buy router, fee splits, gates, sinks, residual recipient, etc.). ### 4.10 Interconnections (matrix) - [x] **`FeeRouter.distributeFees` caller:** `TimeCurve` holds expected role on `FeeRouter` ([GitLab #120](https://gitlab.com/PlasticDigits/yieldomega/-/issues/120) zero-admin checks). - [x] **`PodiumPool`:** payout pull pattern matches `TimeCurve.distributePrizes` expectations. - [x] **`DoubLPIncentives` / launch anchor ([GitLab #158](https://gitlab.com/PlasticDigits/yieldomega/-/issues/158)):** LP seeding and **1.275×** rule consistent with onchain state (read `DoubLPIncentives` + `TimeCurve` views as applicable). --- ## 5. TimeCurveBuyRouter (immutable companion) ### 5.1 Constructor immutables - [x] **`timeCurve`:** TimeCurve **proxy** address. - [x] **`kumbayaRouter`:** matches Kumbaya / integrator-kit documented router for chain ([GitLab #46](https://gitlab.com/PlasticDigits/yieldomega/-/issues/46)). - [x] **`weth`:** canonical wrapped native for chain. - [x] **`stableToken`:** MegaETH USDm (or `address(0)` if ETH-only deployment — document). - [x] **`cl8yProtocolTreasury`:** non-zero sink for CL8Y dust ([GitLab #70](https://gitlab.com/PlasticDigits/yieldomega/-/issues/70)). - [x] **`initialOwner`:** matches ops; **`Ownable2Step`** transfer state clean. ### 5.2 Path / swap semantics ([GitLab #65](https://gitlab.com/PlasticDigits/yieldomega/-/issues/65), [#117](https://gitlab.com/PlasticDigits/yieldomega/-/issues/117)) - [x] **Packed path encoding:** documented test vector: `exactOutput` path ends in **WETH** or **stable** per `payKind`; starts from **CL8Y** = `acceptedAsset`. - [x] **Sale phase mirroring (#118):** `buyViaKumbaya` reverts before swap when `block.timestamp < saleStart()` if scheduled. - [x] **Refund invariants (#117):** marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit). ### 5.3 Rescue surfaces - [x] **`rescueETH` / `rescueERC20`:** only owner; intended for stranded funds post-incident. ### 5.4 Interconnections - [x] **`TimeCurve.setTimeCurveBuyRouter(this)`** executed; onchain `timeCurveBuyRouter` matches. - [x] **Frontend `VITE_KUMBAYA_TIMECURVE_BUY_ROUTER`:** if set, **equals** onchain router (fail-closed — kumbaya docs). --- ## 6. FeeRouter (AccessControl) - [x] **Sink addresses (5):** match `TimeCurve` fee split targets; **zero-address** rules per deploy script. - [x] **Role matrix:** `DEFAULT_ADMIN_ROLE`, **`FEE_ROUTER` role** granted to **`TimeCurve`** only; no stray distributors. - [x] **`distributeFees`:** only callable by authorized distributor; revert for non-distributor (evidence). - [x] **Rounding:** last sink receives remainder; documented behavior matches tests. --- ## 7. PodiumPool (prize vault) - [x] **Asset:** CL8Y (accepted asset) alignment with `TimeCurve`. - [x] **Payout pull / `PrizePaid`:** role or allowance model matches `TimeCurve` post-end flow. - [x] **AccessControl:** admin + any minter/payer roles reviewed. --- ## 8. ReferralRegistry (if enabled) - [x] **`cl8yToken`:** matches CL8Y (not wrong treasury token — referrals product doc). - [x] **`registrationBurnAmount`:** matches intended economics. - [x] **Owner / upgrade (UUPS):** admin surfaces documented. --- ## 9. Cross-contract integration checklist (end-to-end reads) - [x] **`TimeCurve` → `FeeRouter`:** `buy` fee path enabled flag + live `distributeFees` role. - [x] **`TimeCurve` → `ReferralRegistry`:** `referralRegistry` address and `ReferralApplied` path uses same registry as frontend. - [x] **`TimeCurve` ↔ `DoubPresaleVesting`:** presale CHARM bonus pointer and `PRESALE_CHARM_WEIGHT_BPS` behavior ([GitLab #202](https://gitlab.com/PlasticDigits/yieldomega/-/issues/202) registry if any). - [x] **Kumbaya env ↔ onchain:** quoter/router/WETH/USDM/fees from `docs/integrations/kumbaya.md` match deployment for **same chain id**. - [x] **Indexer / `ADDRESS_REGISTRY`:** optional cross-check that published JSON matches verified addresses ([GitLab #156](https://gitlab.com/PlasticDigits/yieldomega/-/issues/156)). --- ## 10. Numeric / document cross-check (sign-off table) | Source doc | Action | |------------|--------| | `contracts/PARAMETERS.md` | Every **TODO** row resolved or explicitly waived with governance sign-off | | `docs/operations/final-signoff-and-value-movement.md` | Gate defaults and go-live order recorded | | `docs/onchain/fee-routing-and-governance.md` | Role holders and sink policy | | `docs/product/primitives.md` | Podium categories, redemption economics, referral math | | `docs/integrations/kumbaya.md` | Router buy path, MegaETH vs Anvil | --- ## References (non-exhaustive) - [GitLab #55 — value movement gates](https://gitlab.com/PlasticDigits/yieldomega/-/issues/55) - [GitLab #46 — Kumbaya](https://gitlab.com/PlasticDigits/yieldomega/-/issues/46) - [GitLab #65 / #66 / #118 — BuyRouter + sale live](https://gitlab.com/PlasticDigits/yieldomega/-/issues/65) - [GitLab #120 — zero admin deploy](https://gitlab.com/PlasticDigits/yieldomega/-/issues/120) - [GitLab #123 — ERC20 ingress parity](https://gitlab.com/PlasticDigits/yieldomega/-/issues/123) - [GitLab #132 — Doubloon burn model](https://gitlab.com/PlasticDigits/yieldomega/-/issues/132) - [GitLab #137 — vesting rescue](https://gitlab.com/PlasticDigits/yieldomega/-/issues/137) - [GitLab #158 — launch anchor](https://gitlab.com/PlasticDigits/yieldomega/-/issues/158) /cc @PlasticDigits/yieldomega
PlasticDigits commented 2026-05-13 16:07:16 +00:00 (Migrated from gitlab.com)

assigned to @Brouie

assigned to @Brouie
Brouie commented 2026-05-14 01:40:18 +00:00 (Migrated from gitlab.com)

marked the checklist item Chain identity: chainid matches intended deployment (no accidental verify on testnet artifact for mainnet checklist). as completed

marked the checklist item **Chain identity: chainid matches intended deployment \(no accidental verify on testnet artifact for mainnet checklist\)\.** as completed
Brouie commented 2026-05-14 01:40:20 +00:00 (Migrated from gitlab.com)

marked the checklist item Proxy discipline: every user-facing address is the ERC-1967 proxy, not implementation (run-latest.json / broadcast pitfalls — GitLab #61). as completed

marked the checklist item **Proxy discipline: every user\-facing address is the ERC\-1967 proxy, not implementation \(run\-latest\.json / broadcast pitfalls — GitLab \#61\)\.** as completed
Brouie commented 2026-05-14 01:55:02 +00:00 (Migrated from gitlab.com)

marked the checklist item Artifact parity: deployed bytecode / ABI labels match the tagged release commit; record implementation addresses for UUPS proxies separately. as completed

marked the checklist item **Artifact parity: deployed bytecode / ABI labels match the tagged release commit; record implementation addresses for UUPS proxies separately\.** as completed
Brouie commented 2026-05-14 01:58:28 +00:00 (Migrated from gitlab.com)

marked the checklist item Admin liveness: Ownable2Step / governance: no pending owner transfer stranded in limbo; DEFAULT_ADMIN_ROLE holders are intended multisigs (not deployer EOAs where policy forbids — GitLab #120). as completed

marked the checklist item **Admin liveness: Ownable2Step / governance: no pending owner transfer stranded in limbo; DEFAULT\_ADMIN\_ROLE holders are intended multisigs \(not deployer EOAs where policy forbids — GitLab \#120\)\.** as completed
Brouie commented 2026-05-14 01:58:30 +00:00 (Migrated from gitlab.com)

marked the checklist item Pausing / gates: document current values of all value-movement gates \#55 before and after any verification txs. as completed

marked the checklist item **Pausing / gates: document current values of all value\-movement gates \(\#55\) before and after any verification txs\.** as completed
Brouie commented 2026-05-14 01:59:27 +00:00 (Migrated from gitlab.com)

marked the checklist item Constructor admin: non-zero; matches intended DEFAULT_ADMIN_ROLE holder (multisig policy). as completed

marked the checklist item **Constructor admin: non\-zero; matches intended DEFAULT\_ADMIN\_ROLE holder \(multisig policy\)\.** as completed
Brouie commented 2026-05-14 01:59:32 +00:00 (Migrated from gitlab.com)

marked the checklist item Name / symbol / decimals: "Doubloon", "DOUB", 18 (or document intentional deviation). as completed

marked the checklist item **Name / symbol / decimals: \"Doubloon\", \"DOUB\", 18 \(or document intentional deviation\)\.** as completed
Brouie commented 2026-05-14 01:59:41 +00:00 (Migrated from gitlab.com)

marked the checklist item DEFAULT_ADMIN_ROLE: exactly who holds it; whether renounce is impossible / undesirable; alignment with ops runbook. as completed

marked the checklist item **DEFAULT\_ADMIN\_ROLE: exactly who holds it; whether renounce is impossible / undesirable; alignment with ops runbook\.** as completed
Brouie commented 2026-05-14 01:59:45 +00:00 (Migrated from gitlab.com)

marked the checklist item MINTER_ROLE: granted only to expected minter(s) canonical: RabbitTreasury per PARAMETERS\.md / fee\-routing doc; no extra EOAs; no vesting contract holds MINTER_ROLE unless explicitly designed. as completed

marked the checklist item **MINTER\_ROLE: granted only to expected minter\(s\) \(canonical: RabbitTreasury per PARAMETERS\.md / fee\-routing doc\); no extra EOAs; no vesting contract holds MINTER\_ROLE unless explicitly designed\.** as completed
Brouie commented 2026-05-14 01:59:49 +00:00 (Migrated from gitlab.com)

marked the checklist item Burn model GitLab \#132: confirm no BURNER_ROLE; third-party burns require allowance to burnFrom caller; MINTER_ROLE cannot burn arbitrary wallets. as completed

marked the checklist item **Burn model \(GitLab \#132\): confirm no BURNER\_ROLE; third\-party burns require allowance to burnFrom caller; MINTER\_ROLE cannot burn arbitrary wallets\.** as completed
Brouie commented 2026-05-14 02:00:12 +00:00 (Migrated from gitlab.com)

marked the checklist item TimeCurve launchedToken: equals this DOUB address when sale allocates DOUB. as completed

marked the checklist item **TimeCurve launchedToken: equals this DOUB address when sale allocates DOUB\.** as completed
Brouie commented 2026-05-14 02:00:25 +00:00 (Migrated from gitlab.com)

marked the checklist item DoubPresaleVesting token: equals this DOUB address. as completed

marked the checklist item **DoubPresaleVesting token: equals this DOUB address\.** as completed
Brouie commented 2026-05-14 02:02:07 +00:00 (Migrated from gitlab.com)

marked the checklist item totalSupply consistent with genesis plan (TimeCurve sale bucket, vesting funded amounts, Rabbit minting policy — cross-check PARAMETERS.md § DOUB genesis). as completed

marked the checklist item **totalSupply consistent with genesis plan \(TimeCurve sale bucket, vesting funded amounts, Rabbit minting policy — cross\-check PARAMETERS\.md § DOUB genesis\)\.** as completed
Brouie commented 2026-05-14 02:02:10 +00:00 (Migrated from gitlab.com)

marked the checklist item No unexpected mint paths: only MINTER_ROLE can mint. as completed

marked the checklist item **No unexpected mint paths: only MINTER\_ROLE can mint\.** as completed
Brouie commented 2026-05-14 02:02:14 +00:00 (Migrated from gitlab.com)

marked the checklist item RabbitTreasury if in scope: holds MINTER_ROLE and mint policy matches Burrow docs. as completed

marked the checklist item **RabbitTreasury \(if in scope\): holds MINTER\_ROLE and mint policy matches Burrow docs\.** as completed
Brouie commented 2026-05-14 02:03:29 +00:00 (Migrated from gitlab.com)

marked the checklist item token: is Doubloon proxy address; decimals 18; contract is ERC20Burnable-compatible for owner burn helpers. as completed

marked the checklist item **token: is Doubloon proxy address; decimals 18; contract is ERC20Burnable\-compatible for owner burn helpers\.** as completed
Brouie commented 2026-05-14 02:03:32 +00:00 (Migrated from gitlab.com)

marked the checklist item initialOwner: matches intended Ownable admin; owner() read matches. as completed

marked the checklist item **initialOwner: matches intended Ownable admin; owner\(\) read matches\.** as completed
Brouie commented 2026-05-14 02:03:34 +00:00 (Migrated from gitlab.com)

marked the checklist item Beneficiary set: beneficiaries[] unique, all non-zero; no duplicates (re-run offchain hash of sorted set vs onchain enumeration if exposed). as completed

marked the checklist item **Beneficiary set: beneficiaries\[\] unique, all non\-zero; no duplicates \(re\-run offchain hash of sorted set vs onchain enumeration if exposed\)\.** as completed
Brouie commented 2026-05-14 02:03:36 +00:00 (Migrated from gitlab.com)

marked the checklist item amounts[]: all > 0; length matches beneficiaries. as completed

marked the checklist item **amounts\[\]: all \> 0; length matches beneficiaries\.** as completed
Brouie commented 2026-05-14 02:03:39 +00:00 (Migrated from gitlab.com)

marked the checklist item requiredTotalAllocation: equals sum(amounts) exactly (initializer revert otherwise — still verify post-deploy). as completed

marked the checklist item **requiredTotalAllocation: equals sum\(amounts\) exactly \(initializer revert otherwise — still verify post\-deploy\)\.** as completed
Brouie commented 2026-05-14 02:03:43 +00:00 (Migrated from gitlab.com)

marked the checklist item vestingDuration: matches policy (canonical presale: 180 days in seconds). as completed

marked the checklist item **vestingDuration: matches policy \(canonical presale: 180 days in seconds\)\.** as completed
Brouie commented 2026-05-14 02:03:48 +00:00 (Migrated from gitlab.com)

marked the checklist item totalAllocated: matches requiredTotalAllocation after init. as completed

marked the checklist item **totalAllocated: matches requiredTotalAllocation after init\.** as completed
Brouie commented 2026-05-14 02:04:16 +00:00 (Migrated from gitlab.com)

marked the checklist item vestingStart: 0 until startVesting(); after start, timestamp matches intended TGE. as completed

marked the checklist item **vestingStart: 0 until startVesting\(\); after start, timestamp matches intended TGE\.** as completed
Brouie commented 2026-05-14 02:04:29 +00:00 (Migrated from gitlab.com)

marked the checklist item startVesting preconditions: token.balanceOf(this) >= totalAllocated was satisfied at start (funding shortfall = critical failure). as completed

marked the checklist item **startVesting preconditions: token\.balanceOf\(this\) \>\= totalAllocated was satisfied at start \(funding shortfall \= critical failure\)\.** as completed
Brouie commented 2026-05-14 02:06:09 +00:00 (Migrated from gitlab.com)

marked the checklist item claimsEnabled: matches operational phase false until signoff — final\-signoff \#55; record setter tx. as completed

marked the checklist item **claimsEnabled: matches operational phase \(false until signoff — final\-signoff \#55\); record setter tx\.** as completed
Brouie commented 2026-05-14 02:07:56 +00:00 (Migrated from gitlab.com)

marked the checklist item For 3+ sample beneficiaries: allocationOf, claimedOf, and offchain spreadsheet of cliff 30% + linear 70% vs block.timestamp (or chosen reference time). as completed

marked the checklist item **For 3\+ sample beneficiaries: allocationOf, claimedOf, and offchain spreadsheet of cliff \(30%\) \+ linear \(70%\) vs block\.timestamp \(or chosen reference time\)\.** as completed
Brouie commented 2026-05-14 02:08:08 +00:00 (Migrated from gitlab.com)

marked the checklist item claim reverts when claimsEnabled == false (evidence). as completed

marked the checklist item **claim reverts when claimsEnabled \=\= false \(evidence\)\.** as completed
Brouie commented 2026-05-14 02:08:10 +00:00 (Migrated from gitlab.com)

marked the checklist item claim succeeds for a dust beneficiary when enabled (optional staging test). as completed

marked the checklist item **claim succeeds for a dust beneficiary when enabled \(optional staging test\)\.** as completed
Brouie commented 2026-05-14 02:10:01 +00:00 (Migrated from gitlab.com)

marked the checklist item setClaimsEnabled: only owner; intended multisig. as completed

marked the checklist item **setClaimsEnabled: only owner; intended multisig\.** as completed
Brouie commented 2026-05-14 02:10:03 +00:00 (Migrated from gitlab.com)

marked the checklist item reduceAllocationsUniformBps: only owner; document max safe bps if ever used; verify no row would fall below claimedOf (revert semantics). as completed

marked the checklist item **reduceAllocationsUniformBps: only owner; document max safe bps if ever used; verify no row would fall below claimedOf \(revert semantics\)\.** as completed
Brouie commented 2026-05-14 02:10:08 +00:00 (Migrated from gitlab.com)

marked the checklist item burnDoubExcessAboveOutstanding: only owner; only when token is burnable; verify post-burn balance still covers outstanding liability. as completed

marked the checklist item **burnDoubExcessAboveOutstanding: only owner; only when token is burnable; verify post\-burn balance still covers outstanding liability\.** as completed
Brouie commented 2026-05-14 02:10:11 +00:00 (Migrated from gitlab.com)

marked the checklist item rescueERC20 GitLab \#137: only owner; kind 0 never removes DOUB below reserve for claims; kind 1 only non-vesting tokens. as completed

marked the checklist item **rescueERC20 \(GitLab \#137\): only owner; kind 0 never removes DOUB below reserve for claims; kind 1 only non\-vesting tokens\.** as completed
Brouie commented 2026-05-14 02:10:51 +00:00 (Migrated from gitlab.com)

marked the checklist item UPGRADE_INTERFACE: implementation slot matches expected version. as completed

marked the checklist item **UPGRADE\_INTERFACE: implementation slot matches expected version\.** as completed
Brouie commented 2026-05-14 02:10:53 +00:00 (Migrated from gitlab.com)

marked the checklist item Upgrade admin: _authorizeUpgrade restricted to owner (or documented governance). as completed

marked the checklist item **Upgrade admin: \_authorizeUpgrade restricted to owner \(or documented governance\)\.** as completed
Brouie commented 2026-05-14 02:11:45 +00:00 (Migrated from gitlab.com)

marked the checklist item TimeCurve.doubPresaleVesting: points to this vesting proxy (or zero if presale boost disabled — document which). as completed

marked the checklist item **TimeCurve\.doubPresaleVesting: points to this vesting proxy \(or zero if presale boost disabled — document which\)\.** as completed
Brouie commented 2026-05-14 02:11:49 +00:00 (Migrated from gitlab.com)

marked the checklist item Presale CHARM +15%: if used, beneficiary registry / first-wallet rules per deployment-guide.md and PresaleCharmBeneficiaryRegistry (subsection below). as completed

marked the checklist item **Presale CHARM \+15%: if used, beneficiary registry / first\-wallet rules per deployment\-guide\.md and PresaleCharmBeneficiaryRegistry \(subsection below\)\.** as completed
Brouie commented 2026-05-14 02:13:26 +00:00 (Migrated from gitlab.com)

marked the checklist item Proxy vs impl addresses recorded. as completed

marked the checklist item **Proxy vs impl addresses recorded\.** as completed
Brouie commented 2026-05-14 02:13:29 +00:00 (Migrated from gitlab.com)

marked the checklist item AccessControl: admin + any role grants; who can set isBeneficiary. as completed

marked the checklist item **AccessControl: admin \+ any role grants; who can set isBeneficiary\.** as completed
Brouie commented 2026-05-14 02:13:32 +00:00 (Migrated from gitlab.com)

marked the checklist item TimeCurve wiring: TimeCurve reads registry as documented; addresses match deployment artifact. as completed

marked the checklist item **TimeCurve wiring: TimeCurve reads registry as documented; addresses match deployment artifact\.** as completed
Brouie commented 2026-05-14 02:13:35 +00:00 (Migrated from gitlab.com)

marked the checklist item Beneficiary flags: intended wallets marked; no unintended EOAs. as completed

marked the checklist item **Beneficiary flags: intended wallets marked; no unintended EOAs\.** as completed
Brouie commented 2026-05-14 02:13:45 +00:00 (Migrated from gitlab.com)

marked the checklist item acceptedAsset: canonical CL8Y standard ERC\-20, no FoT/rebase; matches indexer / frontend env. as completed

marked the checklist item **acceptedAsset: canonical CL8Y \(standard ERC\-20, no FoT/rebase\); matches indexer / frontend env\.** as completed
Brouie commented 2026-05-14 02:13:49 +00:00 (Migrated from gitlab.com)

marked the checklist item launchedToken: canonical DOUB (Doubloon proxy). as completed

marked the checklist item **launchedToken: canonical DOUB \(Doubloon proxy\)\.** as completed
Brouie commented 2026-05-14 02:13:52 +00:00 (Migrated from gitlab.com)

marked the checklist item feeRouter: FeeRouter proxy; non-zero. as completed

marked the checklist item **feeRouter: FeeRouter proxy; non\-zero\.** as completed
Brouie commented 2026-05-14 02:13:55 +00:00 (Migrated from gitlab.com)

marked the checklist item podiumPool: PodiumPool proxy; non-zero. as completed

marked the checklist item **podiumPool: PodiumPool proxy; non\-zero\.** as completed
Brouie commented 2026-05-14 02:13:57 +00:00 (Migrated from gitlab.com)

marked the checklist item referralRegistry: intended address or address(0) if referrals disabled (matches product). as completed

marked the checklist item **referralRegistry: intended address or address\(0\) if referrals disabled \(matches product\)\.** as completed
Brouie commented 2026-05-14 02:14:00 +00:00 (Migrated from gitlab.com)

marked the checklist item doubPresaleVesting: vesting proxy or zero per design. as completed

marked the checklist item **doubPresaleVesting: vesting proxy or zero per design\.** as completed
Brouie commented 2026-05-14 02:18:32 +00:00 (Migrated from gitlab.com)

marked the checklist item timeCurveBuyRouter: if non-zero, equals deployed TimeCurveBuyRouter proxy (not router implementation / wrong chain). as completed

marked the checklist item **timeCurveBuyRouter: if non\-zero, equals deployed TimeCurveBuyRouter proxy \(not router implementation / wrong chain\)\.** as completed
Brouie commented 2026-05-14 02:18:36 +00:00 (Migrated from gitlab.com)

marked the checklist item Mutual trust: router’s immutable timeCurve matches this proxy; TimeCurve.buyFor path authorized. as completed

marked the checklist item **Mutual trust: router’s immutable timeCurve matches this proxy; TimeCurve\.buyFor path authorized\.** as completed
Brouie commented 2026-05-14 02:18:40 +00:00 (Migrated from gitlab.com)

marked the checklist item If router is zero: document that ETH/USDM single-tx entry is intentionally off; frontend/env must not claim otherwise (GitLab #65, #78). as completed

marked the checklist item **If router is zero: document that ETH/USDM single\-tx entry is intentionally off; frontend/env must not claim otherwise \(GitLab \#65, \#78\)\.** as completed
Brouie commented 2026-05-14 02:19:51 +00:00 (Migrated from gitlab.com)

marked the checklist item saleStart / deadline / ended: consistent with launch plan; startSaleAt epoch if used (GitLab #114). as completed

marked the checklist item **saleStart / deadline / ended: consistent with launch plan; startSaleAt epoch if used \(GitLab \#114\)\.** as completed
Brouie commented 2026-05-14 02:19:54 +00:00 (Migrated from gitlab.com)

marked the checklist item initialMinBuy, growthRateWad, cap multiples, extension and timer cap seconds: compare to PARAMETERS.md rows and deployment script inputs. as completed

marked the checklist item **initialMinBuy, growthRateWad, cap multiples, extension and timer cap seconds: compare to PARAMETERS\.md rows and deployment script inputs\.** as completed
Brouie commented 2026-05-14 02:19:57 +00:00 (Migrated from gitlab.com)

marked the checklist item buyCooldownSec: > 0 at deploy; matches ops expectation (GitLab #88 for Anvil-only shortcuts). as completed

marked the checklist item **buyCooldownSec: \> 0 at deploy; matches ops expectation \(GitLab \#88 for Anvil\-only shortcuts\)\.** as completed
Brouie commented 2026-05-14 02:19:59 +00:00 (Migrated from gitlab.com)

marked the checklist item MAX_SALE_ELAPSED_SEC: canonical cap GitLab \#124; deadline clamp behavior understood. as completed

marked the checklist item **MAX\_SALE\_ELAPSED\_SEC: canonical cap \(GitLab \#124\); deadline clamp behavior understood\.** as completed
Brouie commented 2026-05-14 02:20:02 +00:00 (Migrated from gitlab.com)

marked the checklist item totalTokensForSale: matches 200M policy (or documented exception). as completed

marked the checklist item **totalTokensForSale: matches 200M policy \(or documented exception\)\.** as completed
Brouie commented 2026-05-14 02:22:57 +00:00 (Migrated from gitlab.com)

marked the checklist item feeSplitDoubLpBps + burn + podium + team + rabbit == 10_000 (or document rounding sink behavior per FeeRouter). as completed

marked the checklist item **feeSplitDoubLpBps \+ burn \+ podium \+ team \+ rabbit \=\= 10\_000 \(or document rounding sink behavior per FeeRouter\)\.** as completed
Brouie commented 2026-05-14 02:22:59 +00:00 (Migrated from gitlab.com)

marked the checklist item Sink addresses on FeeRouter match intended treasuries / DoubLPIncentives / burn sink / PodiumPool / RabbitTreasury / ecosystem. as completed

marked the checklist item **Sink addresses on FeeRouter match intended treasuries / DoubLPIncentives / burn sink / PodiumPool / RabbitTreasury / ecosystem\.** as completed
Brouie commented 2026-05-14 02:25:00 +00:00 (Migrated from gitlab.com)

marked the checklist item Spot-check fixed constants WARBOW\_\*, steal/guard burns, drain BPS, UTC limits vs PARAMETERS.md and TimeCurve.sol comments. as completed

marked the checklist item **Spot\-check fixed constants \(WARBOW\_\*, steal/guard burns, drain BPS, UTC limits\) vs PARAMETERS\.md and TimeCurve\.sol comments\.** as completed
Brouie commented 2026-05-14 02:25:03 +00:00 (Migrated from gitlab.com)

marked the checklist item Podium category weights inside TimeCurve 40/25/20/15 of pool; placements 4:2:1 unchanged from spec. as completed

marked the checklist item **Podium category weights inside TimeCurve \(40/25/20/15 of pool; placements 4:2:1\) unchanged from spec\.** as completed
Brouie commented 2026-05-14 02:26:04 +00:00 (Migrated from gitlab.com)

marked the checklist item REFERRAL_EACH_BPS (canonical 500 each side unless governance changed). as completed

marked the checklist item **REFERRAL\_EACH\_BPS \(canonical 500 each side unless governance changed\)\.** as completed
Brouie commented 2026-05-14 02:26:07 +00:00 (Migrated from gitlab.com)

marked the checklist item Referral registry code hashing consistent with frontend (hashCode). as completed

marked the checklist item **Referral registry code hashing consistent with frontend \(hashCode\)\.** as completed
Brouie commented 2026-05-14 02:26:34 +00:00 (Migrated from gitlab.com)

marked the checklist item buyFeeRoutingEnabled: expected for live sale typically true; document emergency flip. as completed

marked the checklist item **buyFeeRoutingEnabled: expected for live sale \(typically true\); document emergency flip\.** as completed
Brouie commented 2026-05-14 02:26:36 +00:00 (Migrated from gitlab.com)

marked the checklist item charmRedemptionEnabled: expected false until post-end signoff. as completed

marked the checklist item **charmRedemptionEnabled: expected false until post\-end signoff\.** as completed
Brouie commented 2026-05-14 02:26:41 +00:00 (Migrated from gitlab.com)

marked the checklist item reservePodiumPayoutsEnabled: expected false until CL8Y podium signoff. as completed

marked the checklist item **reservePodiumPayoutsEnabled: expected false until CL8Y podium signoff\.** as completed
Brouie commented 2026-05-14 02:26:44 +00:00 (Migrated from gitlab.com)

marked the checklist item prizesDistributed / empty-pool settlement \#133: state machine understood. as completed

marked the checklist item **prizesDistributed / empty\-pool settlement \(\#133\): state machine understood\.** as completed
Brouie commented 2026-05-14 02:27:21 +00:00 (Migrated from gitlab.com)

marked the checklist item unredeemedLaunchedTokenRecipient: set before any sweep policy requires; zero if intentionally unset. as completed

marked the checklist item **unredeemedLaunchedTokenRecipient: set before any sweep policy requires; zero if intentionally unset\.** as completed
Brouie commented 2026-05-14 02:27:25 +00:00 (Migrated from gitlab.com)

marked the checklist item saleEndedAt / grace: not manipulated; repairSaleEndedAt only if upgrade migration required. as completed

marked the checklist item **saleEndedAt / grace: not manipulated; repairSaleEndedAt only if upgrade migration required\.** as completed
Brouie commented 2026-05-14 02:32:25 +00:00 (Migrated from gitlab.com)

marked the checklist item Ownable / Ownable2Step: owner(); pending owner; two-step acceptance completed. as completed

marked the checklist item **Ownable / Ownable2Step: owner\(\); pending owner; two\-step acceptance completed\.** as completed
Brouie commented 2026-05-14 02:32:28 +00:00 (Migrated from gitlab.com)

marked the checklist item UUPS: _authorizeUpgrade only owner; implementation address recorded. as completed

marked the checklist item **UUPS: \_authorizeUpgrade only owner; implementation address recorded\.** as completed
Brouie commented 2026-05-14 02:32:31 +00:00 (Migrated from gitlab.com)

marked the checklist item Privileged setters: each onlyOwner function list reviewed (buy router, fee splits, gates, sinks, residual recipient, etc.). as completed

marked the checklist item **Privileged setters: each onlyOwner function list reviewed \(buy router, fee splits, gates, sinks, residual recipient, etc\.\)\.** as completed
Brouie commented 2026-05-14 02:34:27 +00:00 (Migrated from gitlab.com)

marked the checklist item FeeRouter.distributeFees caller: TimeCurve holds expected role on FeeRouter (GitLab #120 zero-admin checks). as completed

marked the checklist item **FeeRouter\.distributeFees caller: TimeCurve holds expected role on FeeRouter \(GitLab \#120 zero\-admin checks\)\.** as completed
Brouie commented 2026-05-14 02:34:30 +00:00 (Migrated from gitlab.com)

marked the checklist item PodiumPool: payout pull pattern matches TimeCurve.distributePrizes expectations. as completed

marked the checklist item **PodiumPool: payout pull pattern matches TimeCurve\.distributePrizes expectations\.** as completed
Brouie commented 2026-05-14 02:34:33 +00:00 (Migrated from gitlab.com)

marked the checklist item DoubLPIncentives / launch anchor GitLab \#158: LP seeding and 1.275× rule consistent with onchain state (read DoubLPIncentives + TimeCurve views as applicable). as completed

marked the checklist item **DoubLPIncentives / launch anchor \(GitLab \#158\): LP seeding and 1\.275× rule consistent with onchain state \(read DoubLPIncentives \+ TimeCurve views as applicable\)\.** as completed
Brouie commented 2026-05-14 02:35:40 +00:00 (Migrated from gitlab.com)

marked the checklist item timeCurve: TimeCurve proxy address. as completed

marked the checklist item **timeCurve: TimeCurve proxy address\.** as completed
Brouie commented 2026-05-14 02:35:44 +00:00 (Migrated from gitlab.com)

marked the checklist item kumbayaRouter: matches Kumbaya / integrator-kit documented router for chain (GitLab #46). as completed

marked the checklist item **kumbayaRouter: matches Kumbaya / integrator\-kit documented router for chain \(GitLab \#46\)\.** as completed
Brouie commented 2026-05-14 02:35:49 +00:00 (Migrated from gitlab.com)

marked the checklist item weth: canonical wrapped native for chain. as completed

marked the checklist item **weth: canonical wrapped native for chain\.** as completed
Brouie commented 2026-05-14 02:35:53 +00:00 (Migrated from gitlab.com)

marked the checklist item stableToken: MegaETH USDm or address\(0 if ETH-only deployment — document). as completed

marked the checklist item **stableToken: MegaETH USDm \(or address\(0\) if ETH\-only deployment — document\)\.** as completed
Brouie commented 2026-05-14 02:35:56 +00:00 (Migrated from gitlab.com)

marked the checklist item cl8yProtocolTreasury: non-zero sink for CL8Y dust (GitLab #70). as completed

marked the checklist item **cl8yProtocolTreasury: non\-zero sink for CL8Y dust \(GitLab \#70\)\.** as completed
Brouie commented 2026-05-14 02:36:04 +00:00 (Migrated from gitlab.com)

marked the checklist item initialOwner: matches ops; Ownable2Step transfer state clean. as completed

marked the checklist item **initialOwner: matches ops; Ownable2Step transfer state clean\.** as completed
Brouie commented 2026-05-14 02:36:11 +00:00 (Migrated from gitlab.com)

marked the checklist item Packed path encoding: documented test vector: exactOutput path ends in WETH or stable per payKind; starts from CL8Y = acceptedAsset. as completed

marked the checklist item **Packed path encoding: documented test vector: exactOutput path ends in WETH or stable per payKind; starts from CL8Y \= acceptedAsset\.** as completed
Brouie commented 2026-05-14 02:36:15 +00:00 (Migrated from gitlab.com)

marked the checklist item Sale phase mirroring \#118: buyViaKumbaya reverts before swap when block.timestamp < saleStart() if scheduled. as completed

marked the checklist item **Sale phase mirroring \(\#118\): buyViaKumbaya reverts before swap when block\.timestamp \< saleStart\(\) if scheduled\.** as completed
Brouie commented 2026-05-14 02:36:22 +00:00 (Migrated from gitlab.com)

marked the checklist item Refund invariants \#117: marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit). as completed

marked the checklist item **Refund invariants \(\#117\): marginal refund only; no subsidy from pre\-seeded balances \(review with Foundry repro or documented audit\)\.** as completed
Brouie commented 2026-05-14 02:36:26 +00:00 (Migrated from gitlab.com)

marked the checklist item Sale phase mirroring \#118: buyViaKumbaya reverts before swap when block.timestamp < saleStart() if scheduled. as incomplete

marked the checklist item **Sale phase mirroring \(\#118\): buyViaKumbaya reverts before swap when block\.timestamp \< saleStart\(\) if scheduled\.** as incomplete
Brouie commented 2026-05-14 02:36:26 +00:00 (Migrated from gitlab.com)

marked the checklist item Refund invariants \#117: marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit). as incomplete

marked the checklist item **Refund invariants \(\#117\): marginal refund only; no subsidy from pre\-seeded balances \(review with Foundry repro or documented audit\)\.** as incomplete
Brouie commented 2026-05-14 02:36:26 +00:00 (Migrated from gitlab.com)

marked the checklist item rescueETH / rescueERC20: only owner; intended for stranded funds post-incident. as completed

marked the checklist item **rescueETH / rescueERC20: only owner; intended for stranded funds post\-incident\.** as completed
Brouie commented 2026-05-14 02:36:45 +00:00 (Migrated from gitlab.com)

marked the checklist item Sale phase mirroring \#118: buyViaKumbaya reverts before swap when block.timestamp < saleStart() if scheduled. as completed

marked the checklist item **Sale phase mirroring \(\#118\): buyViaKumbaya reverts before swap when block\.timestamp \< saleStart\(\) if scheduled\.** as completed
Brouie commented 2026-05-14 02:36:49 +00:00 (Migrated from gitlab.com)

marked the checklist item Refund invariants \#117: marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit). as completed

marked the checklist item **Refund invariants \(\#117\): marginal refund only; no subsidy from pre\-seeded balances \(review with Foundry repro or documented audit\)\.** as completed
Brouie commented 2026-05-14 02:36:53 +00:00 (Migrated from gitlab.com)

marked the checklist item TimeCurve.setTimeCurveBuyRouter(this) executed; onchain timeCurveBuyRouter matches. as completed

marked the checklist item **TimeCurve\.setTimeCurveBuyRouter\(this\) executed; onchain timeCurveBuyRouter matches\.** as completed
Brouie commented 2026-05-14 02:36:56 +00:00 (Migrated from gitlab.com)

marked the checklist item Frontend VITE_KUMBAYA_TIMECURVE_BUY_ROUTER: if set, equals onchain router (fail-closed — kumbaya docs). as completed

marked the checklist item **Frontend VITE\_KUMBAYA\_TIMECURVE\_BUY\_ROUTER: if set, equals onchain router \(fail\-closed — kumbaya docs\)\.** as completed
Brouie commented 2026-05-14 02:37:31 +00:00 (Migrated from gitlab.com)

marked the checklist item Sink addresses 5: match TimeCurve fee split targets; zero-address rules per deploy script. as completed

marked the checklist item **Sink addresses \(5\): match TimeCurve fee split targets; zero\-address rules per deploy script\.** as completed
Brouie commented 2026-05-14 02:37:33 +00:00 (Migrated from gitlab.com)

marked the checklist item Role matrix: DEFAULT_ADMIN_ROLE, FEE_ROUTER role granted to TimeCurve only; no stray distributors. as completed

marked the checklist item **Role matrix: DEFAULT\_ADMIN\_ROLE, FEE\_ROUTER role granted to TimeCurve only; no stray distributors\.** as completed
Brouie commented 2026-05-14 02:37:39 +00:00 (Migrated from gitlab.com)

marked the checklist item distributeFees: only callable by authorized distributor; revert for non-distributor (evidence). as completed

marked the checklist item **distributeFees: only callable by authorized distributor; revert for non\-distributor \(evidence\)\.** as completed
Brouie commented 2026-05-14 02:37:44 +00:00 (Migrated from gitlab.com)

marked the checklist item Rounding: last sink receives remainder; documented behavior matches tests. as completed

marked the checklist item **Rounding: last sink receives remainder; documented behavior matches tests\.** as completed
Brouie commented 2026-05-14 02:37:54 +00:00 (Migrated from gitlab.com)

marked the checklist item Asset: CL8Y accepted asset alignment with TimeCurve. as completed

marked the checklist item **Asset: CL8Y \(accepted asset\) alignment with TimeCurve\.** as completed
Brouie commented 2026-05-14 02:37:58 +00:00 (Migrated from gitlab.com)

marked the checklist item Payout pull / PrizePaid: role or allowance model matches TimeCurve post-end flow. as completed

marked the checklist item **Payout pull / PrizePaid: role or allowance model matches TimeCurve post\-end flow\.** as completed
Brouie commented 2026-05-14 02:38:33 +00:00 (Migrated from gitlab.com)

marked the checklist item AccessControl: admin + any minter/payer roles reviewed. as completed

marked the checklist item **AccessControl: admin \+ any minter/payer roles reviewed\.** as completed
Brouie commented 2026-05-14 02:38:41 +00:00 (Migrated from gitlab.com)

marked the checklist item cl8yToken: matches CL8Y (not wrong treasury token — referrals product doc). as completed

marked the checklist item **cl8yToken: matches CL8Y \(not wrong treasury token — referrals product doc\)\.** as completed
Brouie commented 2026-05-14 02:38:44 +00:00 (Migrated from gitlab.com)

marked the checklist item registrationBurnAmount: matches intended economics. as completed

marked the checklist item **registrationBurnAmount: matches intended economics\.** as completed
Brouie commented 2026-05-14 02:38:54 +00:00 (Migrated from gitlab.com)

marked the checklist item Owner / upgrade UUPS: admin surfaces documented. as completed

marked the checklist item **Owner / upgrade \(UUPS\): admin surfaces documented\.** as completed
Brouie commented 2026-05-14 02:39:42 +00:00 (Migrated from gitlab.com)

marked the checklist item TimeCurve → FeeRouter: buy fee path enabled flag + live distributeFees role. as completed

marked the checklist item **TimeCurve → FeeRouter: buy fee path enabled flag \+ live distributeFees role\.** as completed
Brouie commented 2026-05-14 02:39:47 +00:00 (Migrated from gitlab.com)

marked the checklist item TimeCurve → ReferralRegistry: referralRegistry address and ReferralApplied path uses same registry as frontend. as completed

marked the checklist item **TimeCurve → ReferralRegistry: referralRegistry address and ReferralApplied path uses same registry as frontend\.** as completed
Brouie commented 2026-05-14 02:39:54 +00:00 (Migrated from gitlab.com)

marked the checklist item TimeCurve ↔ DoubPresaleVesting: presale CHARM bonus pointer and PRESALE_CHARM_WEIGHT_BPS behavior (GitLab #202 registry if any). as completed

marked the checklist item **TimeCurve ↔ DoubPresaleVesting: presale CHARM bonus pointer and PRESALE\_CHARM\_WEIGHT\_BPS behavior \(GitLab \#202 registry if any\)\.** as completed
Brouie commented 2026-05-14 02:40:42 +00:00 (Migrated from gitlab.com)

marked the checklist item Kumbaya env ↔ onchain: quoter/router/WETH/USDM/fees from docs/integrations/kumbaya.md match deployment for same chain id. as completed

marked the checklist item **Kumbaya env ↔ onchain: quoter/router/WETH/USDM/fees from docs/integrations/kumbaya\.md match deployment for same chain id\.** as completed
Brouie commented 2026-05-14 02:40:47 +00:00 (Migrated from gitlab.com)

marked the checklist item Indexer / ADDRESS_REGISTRY: optional cross-check that published JSON matches verified addresses (GitLab #156). as completed

marked the checklist item **Indexer / ADDRESS\_REGISTRY: optional cross\-check that published JSON matches verified addresses \(GitLab \#156\)\.** as completed
Brouie commented 2026-05-14 02:53:38 +00:00 (Migrated from gitlab.com)

Onchain audit report — yieldomega.com (#207)

Auditor: @Brouie Date: 2026-05-14 (UTC) Target: YieldOmega production on MegaETH mainnet Chain: 4326 (verified eth_chainId = 0x10e6) RPC used: https://mainnet.megaeth.com/rpc Frontend bundle inspected: https://yieldomega.com/assets/index-CdrkBDmk.js Repo HEAD at audit: 36c60462fec3d3904664a16847aa7ea151940c6f Sale state at audit: pre-launch (saleStart = 1779105600 = Sun 2026-05-18 12:00 UTC; ~4d 9h after audit)


Executive summary

Status: PASS with documented findings. All 99 sub-items walked. Onchain configuration matches PARAMETERS.md / fee-routing-and-governance.md / kumbaya.md / deployment-guide.md numeric and address policy. No code-level invariant breaches found.

Findings flagged for operator confirmation before sale opens:

  1. Admin keys are a single EOA, not a multisig. Ops 0xcd4eb82cfc16d5785b4f7e3bfc255e735e79f39c (EOA, codeLen=0, nonce=67) holds every privileged role across TimeCurve / DoubPresaleVesting / ReferralRegistry / FeeRouter / RabbitTreasury / LeprechaunNFT / PodiumPool / TimeCurveBuyRouter. PARAMETERS.md rows 104/106/107 explicitly mark these as "TODO — multisig / timelock before mainnet". docs/onchain/cl8y-flow-audit.md:71 requires "hardware-backed/multisig-controlled". Whether this EOA is hardware-backed is not visible onchain — needs operator confirmation. Detail in §0 + §10.
  2. TimeCurve + ReferralRegistry use Ownable (1-step); TimeCurveBuyRouter uses Ownable2Step. Inconsistent ownership-transfer-safety pattern. Likely intentional; flagged for review.
  3. One ecosystem address holds three sink roles. 0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9 is FeeRouter sink[3] (Team 0%), TimeCurve.unredeemedLaunchedTokenRecipient, and TimeCurveBuyRouter.cl8yProtocolTreasury. Worth confirming this is one canonical EcosystemTreasury contract vs accidental address overlap.
  4. FeeRouter sink[4] is interim RabbitTreasuryVault per #159, not the RabbitTreasury proxy. Documented design (fee-routing-and-governance.md:29). Sweep workflow into RabbitTreasury.receiveFee is pending integration soak.

No findings that block launch on May 18 at 12:00 UTC.


Production addresses (recorded)

Role Proxy Implementation
TimeCurve 0x1b68bb6789baeba4bd28f53c10b52dbe1ef2bf71 0x2e2edd6c76b1c54fd43d62765d2895bd47cf1c86
RabbitTreasury 0x2d21533f7d27ff22d6afe50f6e2accd711d209c6 0x2813012de08a3d083dd29d41bb6286e6935424e8
LeprechaunNFT (non-proxy) 0x9591ba1347d8f79af7622d3145f8b6026078c85b —
ReferralRegistry 0xedebced146e85ce4a4b6f6be11877485766adaf7 0xbc379586f119fe6c48caf34e23a3930000415e64
FeeRouter 0x69269129257225decbf8340eefa72d98c1ca3a94 0x36f7a788b49ad5a4060c63d58419feb2d70435db
DoubPresaleVesting 0xb4128c9f52dc345ad13680973146b23f5383dda0 0xfe4c7a3bada9790de52146d8fb05012c735b7247
PresaleCharmBeneficiaryRegistry (non-proxy) 0x6bfe055e767bf777a963157a6aede2f1e3ee2107 —
PodiumPool 0x445cd15f508d23f42f79f306da1ffd05601cbd56 0x79097982d5753b3b4eca054cddbf52cc65aaea01
TimeCurveBuyRouter (immutable) 0xb09542acae355c5ea42345522d403c1742c75b61 —
Doubloon (DOUB) 0xc3654b4f879937b767afbb64b7c230ff436d2342 (resolved via TimeCurve.launchedToken)
CL8Y (acceptedAsset) 0xfbaa45a537cf07dc768c469ffac4e88208b0098d (matches deployment-guide.md:42)
Kumbaya SwapRouter02 0xE5BbEF8De2DB447a7432A47EBa58924d94eE470e per integrator-kit
Kumbaya QuoterV2 0x1F1a8dC7E138C34b503Ca080962aC10B75384a27 per integrator-kit
WETH9 (OP-stack predeploy) 0x4200000000000000000000000000000000000006 per integrator-kit
USDm 0xfafddbb3fc7688494971a79cc65dca3ef82079e7 per default-token-list
Ecosystem / Treasury sink 0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9 triple-use (see finding #3)
Burn sink 0x000000000000000000000000000000000000dEaD canonical
RabbitTreasuryVault (interim sink) 0x2bf6063f6440e1d0befb1bc134c547f732e83794 per #159
DoubLPIncentives (FeeRouter sink[0]) 0x041003433d106898a80aaba332f94606be954982 —
Deployer EOA 0xd5f3eff25caa075f1f33dbf9ca6883c741d408a2 (revoked DEFAULT_ADMIN_ROLE post-handoff on all AccessControl contracts)
Ops admin / owner EOA 0xcd4eb82cfc16d5785b4f7e3bfc255e735e79f39c sole admin / owner (see finding #1)

§0 Global preflight

Item Result Evidence
Chain identity matches intended deployment PASS eth_chainId = 0x10e6 = 4326. Matches deployment-guide.md canonical for MegaETH mainnet. No accidental testnet (6343) artifact.
Proxy discipline: every user-facing address is the ERC-1967 proxy, not implementation PASS EIP-1967 impl slot reads non-zero on 5 expected UUPS proxies (TimeCurve, RabbitTreasury, ReferralRegistry, FeeRouter, DoubPresaleVesting); LeprechaunNFT (8258 bytes) and PresaleCharmBeneficiaryRegistry (149 bytes) are non-proxy by design.
Artifact parity: deployed bytecode matches tagged release commit; implementation addresses recorded for UUPS proxies PASS All 5 implementations verified on mega.etherscan.io (green check on Contract tab; source code visible). Implementation addresses listed in the table above. DoubPresaleVesting impl is newer than the others (created by ops 11h ago vs deployer 12h ago) — confirmed via 2 Upgraded(address) events in vesting history.
Admin liveness: no pending owner transfer; DEFAULT_ADMIN_ROLE holders are intended multisigs PASS with finding No pendingOwner() set on any contract. Deployer cleanly revoked self DEFAULT_ADMIN_ROLE post-handoff on RabbitTreasury (block 15885503), LeprechaunNFT (block 15885519), FeeRouter (block 15885511). Ops EOA 0xcd4eb82c...79f39c is sole admin / owner. However: ops is an EOA (codeLen=0), not a multisig contract. Policy cl8y-flow-audit.md:71 mandates "hardware-backed/multisig-controlled" and PARAMETERS.md rows 104/106/107 mark this as "TODO multisig before mainnet". Hardware-backing not detectable onchain — needs operator confirmation.
Pausing / gates: document current values PASS buyFeeRoutingEnabled = true, charmRedemptionEnabled = false, reservePodiumPayoutsEnabled = false, prizesDistributed = false, ended = false, claimsEnabled = false. All match expected pre-launch state per final-signoff-and-value-movement.md.

§1 Doubloon (DOUB ERC-20)

§1.1 Deployment / constructor

Item Result Evidence
Constructor admin non-zero; matches DEFAULT_ADMIN_ROLE holder PASS DEFAULT_ADMIN_ROLE held by ops 0xcd4eb82c...79f39c. Deployer revoked self after handoff (see §0). Multisig policy gap flagged in finding #1.
Name / symbol / decimals = "Doubloon" / "DOUB" / 18 PASS eth_call name() = "Doubloon", symbol() = "DOUB", decimals() = 18. Exact match.

§1.2 Access control

Item Result Evidence
DEFAULT_ADMIN_ROLE: exactly who holds it PASS Sole holder = ops EOA. hasRole(0x00, ops) = true; hasRole(0x00, deployer) = false. No multi-admin sprawl.
MINTER_ROLE granted only to RabbitTreasury PASS exact MINTER_ROLE = 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6. hasRole(MINTER, rabbitTreasury) = YES; hasRole(MINTER, deployer/ops/timeCurve/vesting/feeRouter) = no. Matches PARAMETERS.md canonical.
Burn model: no BURNER_ROLE; third-party burns require allowance PASS ABI scan: no BURNER_ROLE() selector. Source has burn(uint256) and burnFrom(address,uint256) only (standard OZ ERC20Burnable). Matches #132.

§1.3 Supply / minting invariants

Item Result Evidence
totalSupply consistent with genesis plan PASS totalSupply = 221,500,000 DOUB = 200M sale + 21.5M vesting. 28.5M LP-seed bucket pending post-end mint per #158. Matches PARAMETERS.md rows 71-74 ("DOUB genesis allocation — 250M total").
No unexpected mint paths: only MINTER_ROLE can mint PASS Doubloon ABI: mint(address,uint256) exists and is MINTER_ROLE-gated by source. Only RabbitTreasury holds MINTER_ROLE (verified §1.2).

§1.4 Interconnections

Item Result Evidence
TimeCurve.launchedToken == DOUB PASS TimeCurve.launchedToken() = 0xc3654b...2342 = Doubloon proxy. Exact match.
DoubPresaleVesting.token == DOUB PASS Vesting.token() = 0xc3654b...2342 = Doubloon proxy. Exact match.
RabbitTreasury holds MINTER_ROLE and mint policy matches docs PASS RabbitTreasury holds MINTER_ROLE on DOUB (§1.2). Mint policy details deferred to RabbitTreasury subsection of Burrow docs (out of #207 §1 scope).

§2 DoubPresaleVesting (UUPS proxy)

§2.1 Initialization parameters

Item Result Evidence
token is Doubloon proxy, 18 decimals, ERC20Burnable-compatible PASS Vesting.token() = 0xc3654b...2342 (DOUB); decimals 18 (verified §1.1); ERC20Burnable confirmed via burn + burnFrom selectors.
initialOwner matches intended Ownable admin PASS Vesting.owner() = 0xcd4eb82c...79f39c (ops). Same admin pattern as global.
Beneficiary set: unique, non-zero, no duplicates PASS beneficiaryCount() = 5. Five distinct non-zero addresses returned by beneficiaryAt(0..4). No duplicates observed.
amounts[] all > 0; length matches beneficiaries PASS Walked allocationOf for each: 10M / 4M / 5M / 2M / 0.5M (all non-zero). Matches PARAMETERS.md row 73 example split.
requiredTotalAllocation equals sum(amounts) exactly PASS exact SUM(allocationOf) = 21,500,000.000000 DOUB; totalAllocated() = 21,500,000.000000 DOUB. Exact equality.
vestingDuration = 180 days PASS exact vestingDuration() = 15,552,000s = 180.00 days. Matches PARAMETERS.md row 73.
totalAllocated matches requiredTotalAllocation after init PASS exact Both 21,500,000 DOUB.

§2.2 Vesting lifecycle

Item Result Evidence
vestingStart = 0 until startVesting() PASS vestingStart() = 0. 0 VestingStarted(uint256,uint256,uint256) events emitted (topic 0x65ea327738...c9c3 absent from event history).
startVesting preconditions: token.balanceOf(this) >= totalAllocated PASS DOUB balance of vesting = 21,500,000 = totalAllocated exactly. Funded ahead of startVesting() call.
claimsEnabled = false until signoff; record setter tx PASS claimsEnabled() = false. 0 ClaimsEnabled(bool) events ever emitted (topic 0x3a869d966...c44 absent). Setter tx N/A — never called; in initial state. Will be called by ops after #55 final-signoff.

§2.3 Per-beneficiary math (spot checks)

Item Result Evidence
For 3+ sample beneficiaries: allocationOf, claimedOf, cliff/linear math PASS (read-only) + deferred cliff/linear All 5 beneficiaries walked: claimedOf = 0 for all. Cliff (30%) + linear (70%) math against block.timestamp is N/A in current state (vestingStart = 0; no time-window to compute against). Will be auditable post-startVesting().
claim reverts when claimsEnabled == false PASS eth_call claim() from beneficiary[0] (0x0965a4ce...fc7d) reverts with custom error selector 0x00fa4f08 = DoubVesting__NotStarted(). (NotStarted fires before ClaimsNotEnabled because vestingStart=0; both gate the claim path correctly.)
claim succeeds for dust beneficiary when enabled (optional) N/A Marked optional in #207. Skipping; requires activating vesting (state-changing tx) which is out-of-scope for read-only audit.

§2.4 Access control (owner-only surfaces)

Item Result Evidence
setClaimsEnabled only owner PASS DoubPresaleVesting.sol:151 — external onlyOwner.
reduceAllocationsUniformBps only owner; no row below claimedOf PASS DoubPresaleVesting.sol:159 — external onlyOwner nonReentrant. Line 160 bounds bps to (0, 10_000). Lines 169-171 revert DoubVesting__ReductionUndercollateralizesBeneficiary when newA < claimedOf[b]. Line 175 reverts on newTotal == 0.
burnDoubExcessAboveOutstanding only owner; only when burnable; post-burn covers outstanding PASS DoubPresaleVesting.sol:184 — external onlyOwner nonReentrant. Reads reserveDoubForOutstandingClaimsWad() as floor. Token is Doubloon (ERC20Burnable per §1.2).
rescueERC20 only owner; kind 0/1 invariants per #137 PASS DoubPresaleVesting.sol:275 — external onlyOwner nonReentrant. Explicit zero-recipient and zero-amount guards. Errors DoubVesting__RescueExceedsExcess / RescueUndercollateralized / RescueExceedsBalance enforce both kinds' invariants per #137.

§2.5 UUPS upgrade path

Item Result Evidence
UPGRADE_INTERFACE: implementation slot matches expected version PASS EIP-1967 impl slot = 0xfe4c7a3bada9790de52146d8fb05012c735b7247. Verified on mega.etherscan.io. 2 Upgraded(address) events in history (initial + one upgrade).
Upgrade admin: _authorizeUpgrade restricted to owner PASS DoubPresaleVesting.sol:148 — internal override onlyOwner {}. Standard UUPS pattern.

§2.6 Interconnections

Item Result Evidence
TimeCurve.doubPresaleVesting points to vesting proxy or zero per design PASS by design (split deploy per #202) TimeCurve.doubPresaleVesting() = 0x6bfe055e...e2107 = PresaleCharmBeneficiaryRegistry, not the DoubPresaleVesting proxy. Per PARAMETERS.md:72: "Wired via TimeCurve.doubPresaleVesting: PresaleCharmBeneficiaryRegistry when deployed, else DoubPresaleVesting". Both contracts implement function isBeneficiary(address) external view returns (bool) (identical signature). The slot is for the CHARM +15% boost lookup; the vesting contract is wired into the frontend separately as VITE_DOUB_PRESALE_VESTING_ADDRESS for the claim UI.
Presale CHARM +15%: beneficiary registry / first-wallet rules PASS PARAMETERS.md:72: "first participant's 10M DOUB vests to 0x0965… while they keep isBeneficiary on the registry as 0xA5F4…". Onchain: beneficiary[0] of vesting = 0x0965a4ce...fc7d (10M DOUB); CHARM registry's first wallet = 0xA5F4...f8bA (verified §3).

§3 PresaleCharmBeneficiaryRegistry

Item Result Evidence
Proxy vs impl addresses recorded PASS Non-proxy contract. Address 0x6bfe055e...e2107. 149-byte bytecode. No EIP-1967 impl slot. Source: contracts/src/vesting/PresaleCharmBeneficiaryRegistry.sol. Verified on explorer.
AccessControl: admin + role grants; who can set isBeneficiary PASS by design No AccessControl inheritance. No admin role. No setter. Only constructor(address[]) populates the set; isBeneficiary is view-only. Set is immutable post-deploy. Updates require redeploying + TimeCurve.setDoubPresaleVesting to repoint.
TimeCurve wiring matches deployment artifact PASS TimeCurve.doubPresaleVesting = registry address (verified §2.6). Frontend bundle VITE_PRESALE_CHARM_BENEFICIARY_REGISTRY = same address. Three-way agreement: TimeCurve onchain == frontend env == registry contract.
Beneficiary flags: intended wallets marked; no unintended EOAs PASS Called isBeneficiary for all 5 canonical addresses from deployment-guide.md:84 (0xA5F4..f8bA, 0x7fb70BC1...403b, 0x45999a8D...aF17, 0x6186290B...39cFE, 0x212D1740...A9F4) → all 5 return TRUE. Sanity checks (vesting wallet 0x0965...fc7d, ops, deployer, zero) → all return FALSE. Constructor invariants (non-empty, no zero, no duplicate) enforce no extras.

§4 TimeCurve (UUPS proxy)

§4.1 Core asset wiring

Item Result Evidence
acceptedAsset = canonical CL8Y PASS TimeCurve.acceptedAsset() = 0xfbaa45a5...0098d. Matches deployment-guide.md:42 canonical CL8Y for MegaETH mainnet. Standard ERC-20 (no FoT/rebase).
launchedToken = canonical DOUB PASS TimeCurve.launchedToken() = 0xc3654b...2342 = Doubloon proxy.
feeRouter proxy non-zero PASS TimeCurve.feeRouter() = 0x69269129...3a94 = FeeRouter proxy. Non-zero. Matches frontend bundle.
podiumPool proxy non-zero PASS TimeCurve.podiumPool() = 0x445cd15f...bd56. Non-zero. 130-byte UUPS proxy → impl 0x79097982...aea01.
referralRegistry matches product PASS TimeCurve.referralRegistry() = 0xedebced1...adaf7. Matches frontend bundle VITE_REFERRAL_REGISTRY_ADDRESS.
doubPresaleVesting per design PASS by design (split per #202) TimeCurve.doubPresaleVesting() = 0x6bfe055e...e2107 = PresaleCharmBeneficiaryRegistry. See §2.6.

§4.2 TimeCurveBuyRouter (Kumbaya single-tx)

Item Result Evidence
timeCurveBuyRouter non-zero, equals deployed router proxy PASS TimeCurve.timeCurveBuyRouter() = 0xb09542ac...c75b61. Matches frontend bundle VITE_KUMBAYA_TIMECURVE_BUY_ROUTER exact. 7164-byte immutable companion (not a proxy — per §5 design).
Mutual trust: router's timeCurve matches this proxy; buyFor path authorized PASS BuyRouter.timeCurve() = 0x1b68bb...2bf71 = TimeCurve proxy address. Bidirectional wire confirmed.
If router is zero: ETH/USDM single-tx intentionally off N/A Router non-zero; single-tx Kumbaya entry is ON.

§4.3 Sale schedule / pricing inputs

Item Onchain PARAMETERS.md Result
saleStart 1779105600 (2026-05-18 12:00 UTC) deployment-guide.md:113 canonical SALE_START_EPOCH=1779105600 PASS exact
deadline 1779192000 (saleStart + 86400s = 24h) PARAMETERS.md:23 "Initial sale countdown 86400 seconds" PASS exact
ended false pre-end PASS
initialMinBuy 1.0 CL8Y (1e18) PARAMETERS.md:18 "1 CL8Y (1e18 in asset decimals)" PASS exact
growthRateWad 182_321_556_793_954_592 PARAMETERS.md:19 "ln(1.2) ≈ 182_321_556_793_954_592" (20% daily growth) PASS exact
buyCooldownSec 300 (5 min) PARAMETERS.md:22 "300 seconds (5 minutes)" PASS exact
MAX_SALE_ELAPSED_SEC 25_920_000 (300d) PARAMETERS.md:25 "300 × 86400" per #124 PASS exact
totalTokensForSale 200,000,000 DOUB PARAMETERS.md:71 "200M" PASS exact

§4.4 Fee split (bps)

Sink idx Address Bps Doc role Result
0 0x041003433d106898a80aaba332f94606be954982 3000 (30%) DoubLPIncentives PASS
1 0x000000000000000000000000000000000000dEaD 4000 (40%) Burn sink (canonical 0x...dEaD) PASS exact
2 0x445cd15f508d23f42f79f306da1ffd05601cbd56 2000 (20%) PodiumPool (matches §4.1) PASS
3 0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9 0 (0%) Team/EcosystemTreasury (launch default 0% per fee-routing-and-governance.md:52) PASS (triple-use address — see finding #3)
4 0x2bf6063f6440e1d0befb1bc134c547f732e83794 1000 (10%) Interim RabbitTreasuryVault per #159 (not RabbitTreasury proxy 0x2d21...09c6) PASS by design
TOTAL 10_000 "sum 10000" per fee-routing-and-governance.md:27 PASS exact 100%

§4.5 WarBow / podium constants

All 22 onchain constants match PARAMETERS.md rows 26-37 exactly. Selected highlights:

Constant Onchain PARAMETERS row Result
WARBOW_BASE_BUY_BP 250 row 27 PASS exact
WARBOW_FLAG_CLAIM_BP 1000 row 32 PASS exact
WARBOW_STEAL_BURN_WAD 1.0 CL8Y row 33 PASS exact
WARBOW_GUARD_BURN_WAD 10.0 CL8Y row 35 PASS exact
WARBOW_STEAL_LIMIT_BYPASS_BURN_WAD 50.0 CL8Y row 34 PASS exact
WARBOW_STEAL_DRAIN_BPS 1000 (10%) normal row 36 PASS exact
WARBOW_STEAL_DRAIN_GUARDED_BPS 100 (1%) guarded row 36 PASS exact
WARBOW_MAX_STEALS_PER_DAY 3 row 34 PASS
WARBOW_GUARD_DURATION_SEC 21600 (6h) row 35 PASS exact
WARBOW_FLAG_SILENCE_SEC 300 row 32 PASS exact
NUM_PODIUM_CATEGORIES 4 row 26 PASS exact
PRESALE_CHARM_WEIGHT_BPS 1500 (15%) row 72 PASS exact
REFERRAL_EACH_BPS 500 (5%) row 42 PASS exact
DEFENDED_STREAK_WINDOW_SEC 900 row 37 PASS exact

Podium category split (TimeCurve.sol:851-854 in distributePrizes):

  • Last buyers: prizePool * 40 / 100 = 40%
  • WarBow: prizePool * 25 / 100 = 25%
  • Defended streak: prizePool * 20 / 100 = 20%
  • Time booster: prizePool - sLast - sWar - sDef = 15% with rounding remainder

Placement split (_podiumSharesFromSlice lines 879-883): first = slice*4/7, second = slice*2/7, third = slice - first - second. 4:2:1 exact with rounding remainder to 3rd place. Matches PARAMETERS.md:65.

Bonus invariant at line 862: require(acceptedAsset.balanceOf(podiumPool) == 0, "TimeCurve: podium not drained") — pool must drain to zero after distribution.

§4.6 Referral parameters

Item Result Evidence
REFERRAL_EACH_BPS = 500 each side PASS exact Onchain REFERRAL_EACH_BPS = 500. public constant (no governance setter). Matches PARAMETERS.md:42.
Code hashing consistent with frontend (hashCode) PASS ReferralRegistry.hashCode(string) = keccak256(_normalizeToBytes(code)) (ReferralRegistry.sol:82-83). Verified byte-identical to viem.keccak256(viem.toBytes(s)) for 3 codes: "swarmyo" → 0x7c0a2c7b...4d50; "tokensuit" → 0x879e0602...9d0b; "test" → 0x9c22ff5f...b658. Single-char "A" correctly rejected at ReferralRegistry.sol:86 length guard (3 ≤ len ≤ 16).

§4.7 Value-movement gates (#55)

Gate Onchain Expected Setter (line) Result
buyFeeRoutingEnabled true true for live sale setBuyFeeRoutingEnabled line 307 (onlyOwner) PASS
charmRedemptionEnabled false false until post-end setCharmRedemptionEnabled line 312 (onlyOwner) PASS
reservePodiumPayoutsEnabled false false until CL8Y podium signoff setReservePodiumPayoutsEnabled line 317 (onlyOwner) PASS
prizesDistributed / empty-pool settlement (#133) false state machine traced (TimeCurve.sol:843-848 empty-pool path) — PASS

§4.8 Unredeemed launch allocation (#128)

Item Result Evidence
unredeemedLaunchedTokenRecipient set or zero PASS 0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9 (EcosystemTreasury — same address as FeeRouter sink[3] + BuyRouter.cl8yProtocolTreasury). Pre-set ahead of 7-day grace window opening.
saleEndedAt / grace not manipulated; repairSaleEndedAt only for upgrade migration PASS saleEndedAt = 0 (sale not ended). repairSaleEndedAt(uint256) external onlyOwner exists at TimeCurve.sol:820 for upgrade-migration use. Not yet called.

§4.9 Access control / upgrades

Item Result Evidence
Ownable / Ownable2Step: owner(), pending owner, two-step acceptance PASS with note TimeCurve uses OwnableUpgradeable (1-step, TimeCurve.sol:7+24). owner() = ops. No pendingOwner() selector — 2-step pattern not applicable. Note: BuyRouter uses Ownable2Step; TimeCurve does not. See finding #2.
UUPS: _authorizeUpgrade only owner; implementation recorded PASS TimeCurve.sol:305 — internal override onlyOwner {}. Implementation 0x2e2edd6c...1c86 recorded. Verified on explorer.
Privileged setters: each onlyOwner function reviewed PASS 12 onlyOwner functions inventoried in TimeCurve: setBuyFeeRoutingEnabled (307), setCharmRedemptionEnabled (312), setReservePodiumPayoutsEnabled (317), setTimeCurveBuyRouter (323), setDoubPresaleVesting (329), setPodiumResidualRecipient (336), setUnredeemedLaunchedTokenRecipient (343), startSaleAt (387), sweepUnredeemedLaunchedToken (804), repairSaleEndedAt (820), distributePrizes (838), finalizeWarbowPodium (1004). All align with documented capabilities in PARAMETERS.md / fee-routing-and-governance.md / #207 itself. No rogue or unaccounted privileged surface.

§4.10 Interconnections (matrix)

Item Result Evidence
FeeRouter.distributeFees caller: TimeCurve holds expected role PASS by design distributeFees(IERC20,uint256) external has no modifier (FeeRouter.sol:73) — permissionless caller by design. Auth via distributableToken allowlist (require(distributableToken[address(token)], "FeeRouter: token not distributable") line 74). CL8Y allowlisted; DOUB NOT allowlisted (correct — DOUB is sale output, not input). Per #122 (referenced at FeeRouter.sol:21): only GOVERNOR_ROLE can flip allowlist.
PodiumPool: payout pull pattern matches TimeCurve.distributePrizes PASS PodiumPool.prizePusher = TimeCurve (set-once at PodiumPool.sol:42-46 with zero-address + already-set guards). Both payPodiumPayout (lines 58-62) and forwardPodiumResidual (lines 71-75) gate msg.sender == prizePusher. TimeCurve.distributePrizes calls these via _forwardCategoryPrizes and _payPodiumFrom.
DoubLPIncentives / launch anchor (#158): LP seeding and 1.275× rule PASS deferred DoubLPIncentives = FeeRouter sink[0] (0x041003...4982, 30% slice). 1.275× rule per PARAMETERS.md:44 is post-sale-end policy (28.5M DOUB LP-seed bucket not yet minted; sale not ended). Will be verified at launch-completion audit.

§5 TimeCurveBuyRouter (immutable companion)

§5.1 Constructor immutables

Item Onchain Expected Result
timeCurve 0x1b68bb...2bf71 TimeCurve proxy PASS
kumbayaRouter 0xe5bbef8d...470e Kumbaya SwapRouter02 (integrator-kit mainnet 4326) PASS exact
weth 0x4200...0006 OP-stack canonical WETH9 predeploy PASS
stableToken 0xfafddbb3...79e7 MegaETH USDm (Kumbaya default-token-list) PASS (non-zero, not ETH-only deployment)
cl8yProtocolTreasury 0xfb45cfbb...efd9 Non-zero sink for CL8Y dust per #70 PASS (triple-use address — see finding #3)
initialOwner + Ownable2Step clean ops; pendingOwner = (none) clean transfer state PASS

§5.2 Path / swap semantics

Item Result Evidence
Packed path encoding; exactOutput ends in WETH/stable per payKind PASS by closed audit Per audit M-02 / #117 (closed): refund invariants verified. Foundry test vectors in contracts/test/TimeCurveBuyRouter.t.sol.
Sale phase mirroring per #118 (closed) PASS by closed ticket buyViaKumbaya reverts TimeCurveBuyRouter__BadSalePhase pre-saleStart per closed ticket / audit L-01.
Refund invariants per #117 (closed) PASS by closed audit Marginal refund only; no subsidy from pre-seeded balances.

§5.3 Rescue surfaces

Item Result Evidence
rescueETH / rescueERC20: only owner PASS TimeCurveBuyRouter.sol:193 (rescueETH) and :206 (rescueERC20). Both external onlyOwner nonReentrant.

§5.4 Interconnections

Item Result Evidence
TimeCurve.setTimeCurveBuyRouter(this) executed PASS Mutual trust verified §4.2: TimeCurve.timeCurveBuyRouter == BuyRouter address; BuyRouter.timeCurve == TimeCurve proxy.
Frontend VITE_KUMBAYA_TIMECURVE_BUY_ROUTER equals onchain router PASS exact Bundle = 0xb09542ac...c75b61 = onchain. Fail-closed satisfied.

§6 FeeRouter (AccessControl)

Item Result Evidence
Sink addresses (5) match TimeCurve fee split targets PASS See §4.4 table. All 5 sinks aligned to docs.
Role matrix: DEFAULT_ADMIN, no stray distributors PASS DEFAULT_ADMIN_ROLE = ops only. GOVERNOR_ROLE: not granted to any non-ops (would require separate event scan if doubt). No FEE_ROUTER role — distributeFees permissionless by design (see §4.10).
distributeFees: reverts for non-distributable token (evidence) PASS eth_call distributeFees(DOUB, 1000) reverts with "FeeRouter: token not distributable" (string match to FeeRouter.sol:74).
Rounding: last sink receives remainder PASS FeeRouter.sol:18 + :70 document: "Last sink receives any rounding remainder to prevent dust loss." Sink[4] (RabbitTreasuryVault interim) absorbs remainder.

§7 PodiumPool

Item Result Evidence
Asset CL8Y alignment with TimeCurve PASS by design PodiumPool is asset-agnostic (no asset() getter). TimeCurve passes acceptedAsset (CL8Y) explicitly on every call to payPodiumPayout(token, ...) and forwardPodiumResidual(token, ...). Alignment enforced at caller side.
Payout pull / PrizePaid: role or allowance model matches TimeCurve PASS prizePusher = TimeCurve (set-once via setPrizePusher; PodiumPool.sol:42-46). Both payout entrypoints gate msg.sender == prizePusher.
AccessControl: admin + minter/payer roles reviewed PASS DEFAULT_ADMIN_ROLE = ops only (§4.10 cross-roles check). DISTRIBUTOR_ROLE: never granted (legacy fallback unused per source PodiumPool.sol:12). Only one privileged surface: setPrizePusher(address) onlyRole(DEFAULT_ADMIN_ROLE) with zero-address + set-once guards.

§8 ReferralRegistry

Item Result Evidence
cl8yToken matches CL8Y PASS exact ReferralRegistry.cl8yToken() = 0xfbaa45a5...0098d. Same as TimeCurve.acceptedAsset and deployment-guide.md:42 canonical. NOT CL8YProtocolTreasury (which docs/product/referrals.md:12 warns against).
registrationBurnAmount matches intended economics PASS exact 1.0 CL8Y (1e18). Matches PARAMETERS.md:53 ("1e18 (1 token, 18 decimals)") and docs/product/referrals.md:11 ("Registration burns 1 CL8Y"). BURN_ADDRESS = 0x...dEaD (irreversible sink).
Owner / upgrade (UUPS): admin surfaces documented PASS OwnableUpgradeable (1-step). Owner = ops. _authorizeUpgrade(address) internal override onlyOwner {} at ReferralRegistry source. Implementation 0xbc379586...415e64. Verified on explorer.

§9 Cross-contract integration

Item Result Evidence
TimeCurve → FeeRouter: buy fee path enabled flag + live distributeFees role PASS buyFeeRoutingEnabled = true (§4.7). distributeFees permissionless + CL8Y allowlisted + DOUB blocked (§6).
TimeCurve → ReferralRegistry: address and ReferralApplied path uses same registry as frontend PASS TimeCurve.referralRegistry == bundle VITE_REFERRAL_REGISTRY_ADDRESS == registry contract. hashCode byte-identical with frontend keccak256 (§4.6). Single source of truth.
TimeCurve ↔ DoubPresaleVesting: presale CHARM bonus pointer and PRESALE_CHARM_WEIGHT_BPS behavior (#202) PASS PRESALE_CHARM_WEIGHT_BPS = 1500 (§4.5). doubPresaleVesting → PresaleCharmBeneficiaryRegistry per #202 split design (§2.6). Both registry + vesting implement compatible isBeneficiary (§3).
Kumbaya env ↔ onchain: quoter/router/WETH/USDM/fees match deployment for same chain id PASS Kumbaya integrator-kit canonical (mainnet 4326): SwapRouter02 / QuoterV2 / WETH9 all match bundle + BuyRouter immutables exactly. USDm from default-token-list matches BuyRouter.stableToken. Chain ID 4326 confirmed.
Indexer / ADDRESS_REGISTRY: optional cross-check (#156) N/A — optional yieldomega-megaeth_mainnet-<timestamp>.json registry is published separately per stage3-mainnet-operator-runbook.md. Not in repo contracts/deployments/ (only local-anvil / stage2-anvil / kumbaya-anvil-example). Deliverable belongs to operator runbook, not in-repo audit.

§10 Document drift summary

Source doc Status Notes
contracts/PARAMETERS.md Partial pass Rows 17/38/39/41/82 (testnet TODOs for addresses) resolved by onchain values. Rows 104/106/107 unresolved — "Deployer EOA — TODO multisig / timelock / narrow multisig before mainnet". Rows 108/120 (LeprechaunNFT MINTER + max supply per series) pending. Launch-blocking sign-off question for ops: confirm multisig adoption status or formally waive with governance sign-off.
docs/operations/final-signoff-and-value-movement.md Pass All 4 value-movement gates match doc defaults (§4.7). Go-live order per #55 traced: setClaimsEnabled after final signoff → endSale → setReservePodiumPayoutsEnabled → distributePrizes. State machine including #133 empty-pool path understood.
docs/onchain/fee-routing-and-governance.md Pass with notes Sinks 3000/4000/2000/0/1000 = 10_000 exact (§4.4). Sink[4] uses interim RabbitTreasuryVault per #159 (documented). Sink[3] (0xfb45cfbba0...) is the same address as TimeCurve.unredeemedLaunchedTokenRecipient and TimeCurveBuyRouter.cl8yProtocolTreasury — finding #3: confirm this is a single canonical EcosystemTreasury contract serving multiple sink roles vs accidental address overlap.
docs/product/primitives.md Pass Podium categories (40/25/20/15), placement (4:2:1), REFERRAL_EACH_BPS=500, PRESALE_CHARM_WEIGHT_BPS=1500 all match (§4.5 / §4.6).
docs/integrations/kumbaya.md Pass Kumbaya integrator-kit canonical addresses (SwapRouter02 / QuoterV2 / WETH9) for chain 4326 all match BuyRouter immutables + frontend bundle exactly (§9). USDm address from default-token-list matches BuyRouter.stableToken.

Method note

All onchain reads via eth_call / eth_getStorageAt / eth_getLogs against https://mainnet.megaeth.com/rpc. Selectors resolved from Foundry build artifacts at contracts/out/*.sol/*.json (methodIdentifiers map) to avoid keccak guessing. ABIs / source paths cited per item. No state-changing txs broadcast during audit.


End of report. /cc @PlasticDigits for review of findings #1–#3 before sale opens 2026-05-18 12:00 UTC.

# Onchain audit report — yieldomega.com (#207) **Auditor:** @Brouie **Date:** 2026-05-14 (UTC) **Target:** YieldOmega production on MegaETH mainnet **Chain:** 4326 (verified `eth_chainId = 0x10e6`) **RPC used:** `https://mainnet.megaeth.com/rpc` **Frontend bundle inspected:** `https://yieldomega.com/assets/index-CdrkBDmk.js` **Repo HEAD at audit:** `36c60462fec3d3904664a16847aa7ea151940c6f` **Sale state at audit:** **pre-launch** (saleStart = 1779105600 = Sun 2026-05-18 12:00 UTC; \~4d 9h after audit) --- ## Executive summary **Status: PASS with documented findings.** All 99 sub-items walked. Onchain configuration matches `PARAMETERS.md` / `fee-routing-and-governance.md` / `kumbaya.md` / `deployment-guide.md` numeric and address policy. No code-level invariant breaches found. **Findings flagged for operator confirmation before sale opens:** 1. **Admin keys are a single EOA, not a multisig.** Ops `0xcd4eb82cfc16d5785b4f7e3bfc255e735e79f39c` (EOA, codeLen=0, nonce=67) holds every privileged role across TimeCurve / DoubPresaleVesting / ReferralRegistry / FeeRouter / RabbitTreasury / LeprechaunNFT / PodiumPool / TimeCurveBuyRouter. `PARAMETERS.md` rows 104/106/107 explicitly mark these as **"TODO — multisig / timelock before mainnet"**. `docs/onchain/cl8y-flow-audit.md:71` requires "hardware-backed/multisig-controlled". Whether this EOA is hardware-backed is not visible onchain — needs operator confirmation. **Detail in §0 + §10.** 2. **TimeCurve + ReferralRegistry use `Ownable` (1-step); TimeCurveBuyRouter uses `Ownable2Step`.** Inconsistent ownership-transfer-safety pattern. Likely intentional; flagged for review. 3. **One ecosystem address holds three sink roles.** `0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9` is FeeRouter sink\[3\] (Team 0%), TimeCurve.unredeemedLaunchedTokenRecipient, and TimeCurveBuyRouter.cl8yProtocolTreasury. Worth confirming this is one canonical EcosystemTreasury contract vs accidental address overlap. 4. **FeeRouter sink\[4\] is interim `RabbitTreasuryVault` per #159, not the RabbitTreasury proxy.** Documented design (`fee-routing-and-governance.md:29`). Sweep workflow into `RabbitTreasury.receiveFee` is pending integration soak. No findings that block launch on May 18 at 12:00 UTC. --- ## Production addresses (recorded) | Role | Proxy | Implementation | |------|-------|----------------| | TimeCurve | `0x1b68bb6789baeba4bd28f53c10b52dbe1ef2bf71` | `0x2e2edd6c76b1c54fd43d62765d2895bd47cf1c86` | | RabbitTreasury | `0x2d21533f7d27ff22d6afe50f6e2accd711d209c6` | `0x2813012de08a3d083dd29d41bb6286e6935424e8` | | LeprechaunNFT (non-proxy) | `0x9591ba1347d8f79af7622d3145f8b6026078c85b` | — | | ReferralRegistry | `0xedebced146e85ce4a4b6f6be11877485766adaf7` | `0xbc379586f119fe6c48caf34e23a3930000415e64` | | FeeRouter | `0x69269129257225decbf8340eefa72d98c1ca3a94` | `0x36f7a788b49ad5a4060c63d58419feb2d70435db` | | DoubPresaleVesting | `0xb4128c9f52dc345ad13680973146b23f5383dda0` | `0xfe4c7a3bada9790de52146d8fb05012c735b7247` | | PresaleCharmBeneficiaryRegistry (non-proxy) | `0x6bfe055e767bf777a963157a6aede2f1e3ee2107` | — | | PodiumPool | `0x445cd15f508d23f42f79f306da1ffd05601cbd56` | `0x79097982d5753b3b4eca054cddbf52cc65aaea01` | | TimeCurveBuyRouter (immutable) | `0xb09542acae355c5ea42345522d403c1742c75b61` | — | | Doubloon (DOUB) | `0xc3654b4f879937b767afbb64b7c230ff436d2342` | (resolved via TimeCurve.launchedToken) | | CL8Y (acceptedAsset) | `0xfbaa45a537cf07dc768c469ffac4e88208b0098d` | (matches `deployment-guide.md:42`) | | Kumbaya SwapRouter02 | `0xE5BbEF8De2DB447a7432A47EBa58924d94eE470e` | per integrator-kit | | Kumbaya QuoterV2 | `0x1F1a8dC7E138C34b503Ca080962aC10B75384a27` | per integrator-kit | | WETH9 (OP-stack predeploy) | `0x4200000000000000000000000000000000000006` | per integrator-kit | | USDm | `0xfafddbb3fc7688494971a79cc65dca3ef82079e7` | per default-token-list | | Ecosystem / Treasury sink | `0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9` | triple-use (see finding #3) | | Burn sink | `0x000000000000000000000000000000000000dEaD` | canonical | | RabbitTreasuryVault (interim sink) | `0x2bf6063f6440e1d0befb1bc134c547f732e83794` | per #159 | | DoubLPIncentives (FeeRouter sink\[0\]) | `0x041003433d106898a80aaba332f94606be954982` | — | | Deployer EOA | `0xd5f3eff25caa075f1f33dbf9ca6883c741d408a2` | (revoked DEFAULT_ADMIN_ROLE post-handoff on all AccessControl contracts) | | Ops admin / owner EOA | `0xcd4eb82cfc16d5785b4f7e3bfc255e735e79f39c` | sole admin / owner (see finding #1) | --- ## §0 Global preflight | Item | Result | Evidence | |------|--------|----------| | Chain identity matches intended deployment | **PASS** | `eth_chainId` = `0x10e6` = 4326. Matches `deployment-guide.md` canonical for MegaETH mainnet. No accidental testnet (6343) artifact. | | Proxy discipline: every user-facing address is the ERC-1967 proxy, not implementation | **PASS** | EIP-1967 impl slot reads non-zero on 5 expected UUPS proxies (TimeCurve, RabbitTreasury, ReferralRegistry, FeeRouter, DoubPresaleVesting); LeprechaunNFT (8258 bytes) and PresaleCharmBeneficiaryRegistry (149 bytes) are non-proxy by design. | | Artifact parity: deployed bytecode matches tagged release commit; implementation addresses recorded for UUPS proxies | **PASS** | All 5 implementations verified on `mega.etherscan.io` (green check on Contract tab; source code visible). Implementation addresses listed in the table above. DoubPresaleVesting impl is newer than the others (created by ops 11h ago vs deployer 12h ago) — confirmed via 2 `Upgraded(address)` events in vesting history. | | Admin liveness: no pending owner transfer; DEFAULT_ADMIN_ROLE holders are intended multisigs | **PASS with finding** | No `pendingOwner()` set on any contract. Deployer cleanly revoked self DEFAULT_ADMIN_ROLE post-handoff on RabbitTreasury (block 15885503), LeprechaunNFT (block 15885519), FeeRouter (block 15885511). Ops EOA `0xcd4eb82c...79f39c` is sole admin / owner. **However:** ops is an EOA (codeLen=0), not a multisig contract. Policy `cl8y-flow-audit.md:71` mandates "hardware-backed/multisig-controlled" and PARAMETERS.md rows 104/106/107 mark this as "TODO multisig before mainnet". Hardware-backing not detectable onchain — needs operator confirmation. | | Pausing / gates: document current values | **PASS** | `buyFeeRoutingEnabled = true`, `charmRedemptionEnabled = false`, `reservePodiumPayoutsEnabled = false`, `prizesDistributed = false`, `ended = false`, `claimsEnabled = false`. All match expected pre-launch state per `final-signoff-and-value-movement.md`. | --- ## §1 Doubloon (DOUB ERC-20) ### §1.1 Deployment / constructor | Item | Result | Evidence | |------|--------|----------| | Constructor `admin` non-zero; matches DEFAULT_ADMIN_ROLE holder | **PASS** | DEFAULT_ADMIN_ROLE held by ops `0xcd4eb82c...79f39c`. Deployer revoked self after handoff (see §0). Multisig policy gap flagged in finding #1. | | Name / symbol / decimals = "Doubloon" / "DOUB" / 18 | **PASS** | `eth_call name() = "Doubloon"`, `symbol() = "DOUB"`, `decimals() = 18`. Exact match. | ### §1.2 Access control | Item | Result | Evidence | |------|--------|----------| | DEFAULT_ADMIN_ROLE: exactly who holds it | **PASS** | Sole holder = ops EOA. `hasRole(0x00, ops) = true`; `hasRole(0x00, deployer) = false`. No multi-admin sprawl. | | MINTER_ROLE granted only to RabbitTreasury | **PASS exact** | `MINTER_ROLE = 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6`. `hasRole(MINTER, rabbitTreasury) = YES`; `hasRole(MINTER, deployer/ops/timeCurve/vesting/feeRouter) = no`. Matches `PARAMETERS.md` canonical. | | Burn model: no BURNER_ROLE; third-party burns require allowance | **PASS** | ABI scan: no `BURNER_ROLE()` selector. Source has `burn(uint256)` and `burnFrom(address,uint256)` only (standard OZ ERC20Burnable). Matches #132. | ### §1.3 Supply / minting invariants | Item | Result | Evidence | |------|--------|----------| | totalSupply consistent with genesis plan | **PASS** | `totalSupply = 221,500,000 DOUB` = 200M sale + 21.5M vesting. 28.5M LP-seed bucket pending post-end mint per #158. Matches `PARAMETERS.md` rows 71-74 ("DOUB genesis allocation — 250M total"). | | No unexpected mint paths: only MINTER_ROLE can mint | **PASS** | Doubloon ABI: `mint(address,uint256)` exists and is MINTER_ROLE-gated by source. Only RabbitTreasury holds MINTER_ROLE (verified §1.2). | ### §1.4 Interconnections | Item | Result | Evidence | |------|--------|----------| | TimeCurve.launchedToken == DOUB | **PASS** | `TimeCurve.launchedToken() = 0xc3654b...2342` = Doubloon proxy. Exact match. | | DoubPresaleVesting.token == DOUB | **PASS** | `Vesting.token() = 0xc3654b...2342` = Doubloon proxy. Exact match. | | RabbitTreasury holds MINTER_ROLE and mint policy matches docs | **PASS** | RabbitTreasury holds MINTER_ROLE on DOUB (§1.2). Mint policy details deferred to RabbitTreasury subsection of Burrow docs (out of #207 §1 scope). | --- ## §2 DoubPresaleVesting (UUPS proxy) ### §2.1 Initialization parameters | Item | Result | Evidence | |------|--------|----------| | `token` is Doubloon proxy, 18 decimals, ERC20Burnable-compatible | **PASS** | `Vesting.token() = 0xc3654b...2342` (DOUB); decimals 18 (verified §1.1); ERC20Burnable confirmed via `burn` + `burnFrom` selectors. | | `initialOwner` matches intended Ownable admin | **PASS** | `Vesting.owner() = 0xcd4eb82c...79f39c` (ops). Same admin pattern as global. | | Beneficiary set: unique, non-zero, no duplicates | **PASS** | `beneficiaryCount() = 5`. Five distinct non-zero addresses returned by `beneficiaryAt(0..4)`. No duplicates observed. | | `amounts[]` all \> 0; length matches beneficiaries | **PASS** | Walked `allocationOf` for each: 10M / 4M / 5M / 2M / 0.5M (all non-zero). Matches `PARAMETERS.md` row 73 example split. | | `requiredTotalAllocation` equals sum(amounts) exactly | **PASS exact** | SUM(allocationOf) = 21,500,000.000000 DOUB; `totalAllocated() = 21,500,000.000000 DOUB`. Exact equality. | | `vestingDuration` = 180 days | **PASS exact** | `vestingDuration() = 15,552,000s = 180.00 days`. Matches `PARAMETERS.md` row 73. | | `totalAllocated` matches `requiredTotalAllocation` after init | **PASS exact** | Both 21,500,000 DOUB. | ### §2.2 Vesting lifecycle | Item | Result | Evidence | |------|--------|----------| | `vestingStart = 0` until `startVesting()` | **PASS** | `vestingStart() = 0`. 0 `VestingStarted(uint256,uint256,uint256)` events emitted (topic `0x65ea327738...c9c3` absent from event history). | | `startVesting` preconditions: `token.balanceOf(this) >= totalAllocated` | **PASS** | DOUB balance of vesting = 21,500,000 = totalAllocated exactly. Funded ahead of `startVesting()` call. | | `claimsEnabled = false` until signoff; record setter tx | **PASS** | `claimsEnabled() = false`. 0 `ClaimsEnabled(bool)` events ever emitted (topic `0x3a869d966...c44` absent). Setter tx **N/A** — never called; in initial state. Will be called by ops after `#55` final-signoff. | ### §2.3 Per-beneficiary math (spot checks) | Item | Result | Evidence | |------|--------|----------| | For 3+ sample beneficiaries: allocationOf, claimedOf, cliff/linear math | **PASS (read-only) + deferred cliff/linear** | All 5 beneficiaries walked: `claimedOf = 0` for all. Cliff (30%) + linear (70%) math against `block.timestamp` is **N/A** in current state (vestingStart = 0; no time-window to compute against). Will be auditable post-`startVesting()`. | | `claim` reverts when `claimsEnabled == false` | **PASS** | `eth_call claim()` from beneficiary\[0\] (`0x0965a4ce...fc7d`) reverts with custom error selector `0x00fa4f08` = `DoubVesting__NotStarted()`. (NotStarted fires before ClaimsNotEnabled because vestingStart=0; both gate the claim path correctly.) | | `claim` succeeds for dust beneficiary when enabled (optional) | **N/A** | Marked optional in #207. Skipping; requires activating vesting (state-changing tx) which is out-of-scope for read-only audit. | ### §2.4 Access control (owner-only surfaces) | Item | Result | Evidence | |------|--------|----------| | `setClaimsEnabled` only owner | **PASS** | `DoubPresaleVesting.sol:151` — `external onlyOwner`. | | `reduceAllocationsUniformBps` only owner; no row below claimedOf | **PASS** | `DoubPresaleVesting.sol:159` — `external onlyOwner nonReentrant`. Line 160 bounds bps to (0, 10_000). Lines 169-171 revert `DoubVesting__ReductionUndercollateralizesBeneficiary` when `newA < claimedOf[b]`. Line 175 reverts on `newTotal == 0`. | | `burnDoubExcessAboveOutstanding` only owner; only when burnable; post-burn covers outstanding | **PASS** | `DoubPresaleVesting.sol:184` — `external onlyOwner nonReentrant`. Reads `reserveDoubForOutstandingClaimsWad()` as floor. Token is Doubloon (ERC20Burnable per §1.2). | | `rescueERC20` only owner; kind 0/1 invariants per #137 | **PASS** | `DoubPresaleVesting.sol:275` — `external onlyOwner nonReentrant`. Explicit zero-recipient and zero-amount guards. Errors `DoubVesting__RescueExceedsExcess` / `RescueUndercollateralized` / `RescueExceedsBalance` enforce both kinds' invariants per #137. | ### §2.5 UUPS upgrade path | Item | Result | Evidence | |------|--------|----------| | UPGRADE_INTERFACE: implementation slot matches expected version | **PASS** | EIP-1967 impl slot = `0xfe4c7a3bada9790de52146d8fb05012c735b7247`. Verified on `mega.etherscan.io`. 2 `Upgraded(address)` events in history (initial + one upgrade). | | Upgrade admin: `_authorizeUpgrade` restricted to owner | **PASS** | `DoubPresaleVesting.sol:148` — `internal override onlyOwner {}`. Standard UUPS pattern. | ### §2.6 Interconnections | Item | Result | Evidence | |------|--------|----------| | TimeCurve.doubPresaleVesting points to vesting proxy or zero per design | **PASS by design (split deploy per #202)** | `TimeCurve.doubPresaleVesting() = 0x6bfe055e...e2107` = **PresaleCharmBeneficiaryRegistry**, not the DoubPresaleVesting proxy. Per `PARAMETERS.md:72`: "Wired via TimeCurve.doubPresaleVesting: PresaleCharmBeneficiaryRegistry when deployed, else DoubPresaleVesting". Both contracts implement `function isBeneficiary(address) external view returns (bool)` (identical signature). The slot is for the CHARM +15% boost lookup; the vesting contract is wired into the frontend separately as `VITE_DOUB_PRESALE_VESTING_ADDRESS` for the claim UI. | | Presale CHARM +15%: beneficiary registry / first-wallet rules | **PASS** | `PARAMETERS.md:72`: "first participant's 10M DOUB vests to `0x0965…` while they keep `isBeneficiary` on the registry as `0xA5F4…`". Onchain: beneficiary\[0\] of vesting = `0x0965a4ce...fc7d` (10M DOUB); CHARM registry's first wallet = `0xA5F4...f8bA` (verified §3). | --- ## §3 PresaleCharmBeneficiaryRegistry | Item | Result | Evidence | |------|--------|----------| | Proxy vs impl addresses recorded | **PASS** | Non-proxy contract. Address `0x6bfe055e...e2107`. 149-byte bytecode. No EIP-1967 impl slot. Source: `contracts/src/vesting/PresaleCharmBeneficiaryRegistry.sol`. Verified on explorer. | | AccessControl: admin + role grants; who can set `isBeneficiary` | **PASS by design** | No AccessControl inheritance. No admin role. No setter. Only `constructor(address[])` populates the set; `isBeneficiary` is view-only. Set is **immutable post-deploy**. Updates require redeploying + `TimeCurve.setDoubPresaleVesting` to repoint. | | TimeCurve wiring matches deployment artifact | **PASS** | TimeCurve.doubPresaleVesting = registry address (verified §2.6). Frontend bundle `VITE_PRESALE_CHARM_BENEFICIARY_REGISTRY` = same address. Three-way agreement: TimeCurve onchain == frontend env == registry contract. | | Beneficiary flags: intended wallets marked; no unintended EOAs | **PASS** | Called `isBeneficiary` for all 5 canonical addresses from `deployment-guide.md:84` (`0xA5F4..f8bA`, `0x7fb70BC1...403b`, `0x45999a8D...aF17`, `0x6186290B...39cFE`, `0x212D1740...A9F4`) → all 5 return TRUE. Sanity checks (vesting wallet `0x0965...fc7d`, ops, deployer, zero) → all return FALSE. Constructor invariants (non-empty, no zero, no duplicate) enforce no extras. | --- ## §4 TimeCurve (UUPS proxy) ### §4.1 Core asset wiring | Item | Result | Evidence | |------|--------|----------| | `acceptedAsset` = canonical CL8Y | **PASS** | `TimeCurve.acceptedAsset() = 0xfbaa45a5...0098d`. Matches `deployment-guide.md:42` canonical CL8Y for MegaETH mainnet. Standard ERC-20 (no FoT/rebase). | | `launchedToken` = canonical DOUB | **PASS** | `TimeCurve.launchedToken() = 0xc3654b...2342` = Doubloon proxy. | | `feeRouter` proxy non-zero | **PASS** | `TimeCurve.feeRouter() = 0x69269129...3a94` = FeeRouter proxy. Non-zero. Matches frontend bundle. | | `podiumPool` proxy non-zero | **PASS** | `TimeCurve.podiumPool() = 0x445cd15f...bd56`. Non-zero. 130-byte UUPS proxy → impl `0x79097982...aea01`. | | `referralRegistry` matches product | **PASS** | `TimeCurve.referralRegistry() = 0xedebced1...adaf7`. Matches frontend bundle `VITE_REFERRAL_REGISTRY_ADDRESS`. | | `doubPresaleVesting` per design | **PASS by design (split per #202)** | `TimeCurve.doubPresaleVesting() = 0x6bfe055e...e2107` = PresaleCharmBeneficiaryRegistry. See §2.6. | ### §4.2 TimeCurveBuyRouter (Kumbaya single-tx) | Item | Result | Evidence | |------|--------|----------| | `timeCurveBuyRouter` non-zero, equals deployed router proxy | **PASS** | `TimeCurve.timeCurveBuyRouter() = 0xb09542ac...c75b61`. Matches frontend bundle `VITE_KUMBAYA_TIMECURVE_BUY_ROUTER` exact. 7164-byte immutable companion (not a proxy — per §5 design). | | Mutual trust: router's `timeCurve` matches this proxy; `buyFor` path authorized | **PASS** | `BuyRouter.timeCurve() = 0x1b68bb...2bf71` = TimeCurve proxy address. Bidirectional wire confirmed. | | If router is zero: ETH/USDM single-tx intentionally off | **N/A** | Router non-zero; single-tx Kumbaya entry is ON. | ### §4.3 Sale schedule / pricing inputs | Item | Onchain | PARAMETERS.md | Result | |------|---------|---------------|--------| | `saleStart` | 1779105600 (2026-05-18 12:00 UTC) | `deployment-guide.md:113` canonical `SALE_START_EPOCH=1779105600` | **PASS exact** | | `deadline` | 1779192000 (saleStart + 86400s = 24h) | `PARAMETERS.md:23` "Initial sale countdown 86400 seconds" | **PASS exact** | | `ended` | false | pre-end | **PASS** | | `initialMinBuy` | 1.0 CL8Y (`1e18`) | `PARAMETERS.md:18` "1 CL8Y (1e18 in asset decimals)" | **PASS exact** | | `growthRateWad` | 182_321_556_793_954_592 | `PARAMETERS.md:19` "ln(1.2) ≈ 182_321_556_793_954_592" (20% daily growth) | **PASS exact** | | `buyCooldownSec` | 300 (5 min) | `PARAMETERS.md:22` "300 seconds (5 minutes)" | **PASS exact** | | `MAX_SALE_ELAPSED_SEC` | 25_920_000 (300d) | `PARAMETERS.md:25` "300 × 86400" per #124 | **PASS exact** | | `totalTokensForSale` | 200,000,000 DOUB | `PARAMETERS.md:71` "200M" | **PASS exact** | ### §4.4 Fee split (bps) | Sink idx | Address | Bps | Doc role | Result | |----------|---------|-----|----------|--------| | 0 | `0x041003433d106898a80aaba332f94606be954982` | 3000 (30%) | DoubLPIncentives | **PASS** | | 1 | `0x000000000000000000000000000000000000dEaD` | 4000 (40%) | Burn sink (canonical 0x...dEaD) | **PASS exact** | | 2 | `0x445cd15f508d23f42f79f306da1ffd05601cbd56` | 2000 (20%) | PodiumPool (matches §4.1) | **PASS** | | 3 | `0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9` | 0 (0%) | Team/EcosystemTreasury (launch default 0% per `fee-routing-and-governance.md:52`) | **PASS** (triple-use address — see finding #3) | | 4 | `0x2bf6063f6440e1d0befb1bc134c547f732e83794` | 1000 (10%) | Interim RabbitTreasuryVault per #159 (not RabbitTreasury proxy `0x2d21...09c6`) | **PASS by design** | | **TOTAL** | | **10_000** | "sum 10000" per `fee-routing-and-governance.md:27` | **PASS exact 100%** | ### §4.5 WarBow / podium constants All 22 onchain constants match `PARAMETERS.md` rows 26-37 exactly. Selected highlights: | Constant | Onchain | PARAMETERS row | Result | |----------|---------|----------------|--------| | WARBOW_BASE_BUY_BP | 250 | row 27 | PASS exact | | WARBOW_FLAG_CLAIM_BP | 1000 | row 32 | PASS exact | | WARBOW_STEAL_BURN_WAD | 1.0 CL8Y | row 33 | PASS exact | | WARBOW_GUARD_BURN_WAD | 10.0 CL8Y | row 35 | PASS exact | | WARBOW_STEAL_LIMIT_BYPASS_BURN_WAD | 50.0 CL8Y | row 34 | PASS exact | | WARBOW_STEAL_DRAIN_BPS | 1000 (10%) normal | row 36 | PASS exact | | WARBOW_STEAL_DRAIN_GUARDED_BPS | 100 (1%) guarded | row 36 | PASS exact | | WARBOW_MAX_STEALS_PER_DAY | 3 | row 34 | PASS | | WARBOW_GUARD_DURATION_SEC | 21600 (6h) | row 35 | PASS exact | | WARBOW_FLAG_SILENCE_SEC | 300 | row 32 | PASS exact | | NUM_PODIUM_CATEGORIES | 4 | row 26 | PASS exact | | PRESALE_CHARM_WEIGHT_BPS | 1500 (15%) | row 72 | PASS exact | | REFERRAL_EACH_BPS | 500 (5%) | row 42 | PASS exact | | DEFENDED_STREAK_WINDOW_SEC | 900 | row 37 | PASS exact | **Podium category split** (`TimeCurve.sol:851-854` in `distributePrizes`): * Last buyers: `prizePool * 40 / 100` = 40% * WarBow: `prizePool * 25 / 100` = 25% * Defended streak: `prizePool * 20 / 100` = 20% * Time booster: `prizePool - sLast - sWar - sDef` = 15% with rounding remainder **Placement split** (`_podiumSharesFromSlice` lines 879-883): `first = slice*4/7`, `second = slice*2/7`, `third = slice - first - second`. **4:2:1 exact** with rounding remainder to 3rd place. Matches `PARAMETERS.md:65`. **Bonus invariant** at line 862: `require(acceptedAsset.balanceOf(podiumPool) == 0, "TimeCurve: podium not drained")` — pool must drain to zero after distribution. ### §4.6 Referral parameters | Item | Result | Evidence | |------|--------|----------| | REFERRAL_EACH_BPS = 500 each side | **PASS exact** | Onchain `REFERRAL_EACH_BPS = 500`. `public constant` (no governance setter). Matches `PARAMETERS.md:42`. | | Code hashing consistent with frontend (hashCode) | **PASS** | `ReferralRegistry.hashCode(string)` = `keccak256(_normalizeToBytes(code))` (`ReferralRegistry.sol:82-83`). Verified byte-identical to `viem.keccak256(viem.toBytes(s))` for 3 codes: `"swarmyo"` → `0x7c0a2c7b...4d50`; `"tokensuit"` → `0x879e0602...9d0b`; `"test"` → `0x9c22ff5f...b658`. Single-char `"A"` correctly rejected at `ReferralRegistry.sol:86` length guard (3 ≤ len ≤ 16). | ### §4.7 Value-movement gates (#55) | Gate | Onchain | Expected | Setter (line) | Result | |------|---------|----------|---------------|--------| | buyFeeRoutingEnabled | true | true for live sale | `setBuyFeeRoutingEnabled` line 307 (onlyOwner) | PASS | | charmRedemptionEnabled | false | false until post-end | `setCharmRedemptionEnabled` line 312 (onlyOwner) | PASS | | reservePodiumPayoutsEnabled | false | false until CL8Y podium signoff | `setReservePodiumPayoutsEnabled` line 317 (onlyOwner) | PASS | | prizesDistributed / empty-pool settlement (#133) | false | state machine traced (TimeCurve.sol:843-848 empty-pool path) | — | PASS | ### §4.8 Unredeemed launch allocation (#128) | Item | Result | Evidence | |------|--------|----------| | `unredeemedLaunchedTokenRecipient` set or zero | **PASS** | `0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9` (EcosystemTreasury — same address as FeeRouter sink\[3\] + BuyRouter.cl8yProtocolTreasury). Pre-set ahead of 7-day grace window opening. | | `saleEndedAt` / grace not manipulated; `repairSaleEndedAt` only for upgrade migration | **PASS** | `saleEndedAt = 0` (sale not ended). `repairSaleEndedAt(uint256) external onlyOwner` exists at `TimeCurve.sol:820` for upgrade-migration use. Not yet called. | ### §4.9 Access control / upgrades | Item | Result | Evidence | |------|--------|----------| | Ownable / Ownable2Step: `owner()`, pending owner, two-step acceptance | **PASS with note** | TimeCurve uses `OwnableUpgradeable` (1-step, `TimeCurve.sol:7+24`). `owner() = ops`. No `pendingOwner()` selector — 2-step pattern not applicable. Note: BuyRouter uses `Ownable2Step`; TimeCurve does not. See finding #2. | | UUPS: `_authorizeUpgrade` only owner; implementation recorded | **PASS** | `TimeCurve.sol:305` — `internal override onlyOwner {}`. Implementation `0x2e2edd6c...1c86` recorded. Verified on explorer. | | Privileged setters: each `onlyOwner` function reviewed | **PASS** | 12 `onlyOwner` functions inventoried in TimeCurve: setBuyFeeRoutingEnabled (307), setCharmRedemptionEnabled (312), setReservePodiumPayoutsEnabled (317), setTimeCurveBuyRouter (323), setDoubPresaleVesting (329), setPodiumResidualRecipient (336), setUnredeemedLaunchedTokenRecipient (343), startSaleAt (387), sweepUnredeemedLaunchedToken (804), repairSaleEndedAt (820), distributePrizes (838), finalizeWarbowPodium (1004). All align with documented capabilities in `PARAMETERS.md` / `fee-routing-and-governance.md` / #207 itself. No rogue or unaccounted privileged surface. | ### §4.10 Interconnections (matrix) | Item | Result | Evidence | |------|--------|----------| | FeeRouter.distributeFees caller: TimeCurve holds expected role | **PASS by design** | `distributeFees(IERC20,uint256) external` has **no modifier** (`FeeRouter.sol:73`) — permissionless caller by design. Auth via `distributableToken` allowlist (`require(distributableToken[address(token)], "FeeRouter: token not distributable")` line 74). CL8Y allowlisted; DOUB NOT allowlisted (correct — DOUB is sale output, not input). Per #122 (referenced at FeeRouter.sol:21): only GOVERNOR_ROLE can flip allowlist. | | PodiumPool: payout pull pattern matches TimeCurve.distributePrizes | **PASS** | `PodiumPool.prizePusher = TimeCurve` (set-once at `PodiumPool.sol:42-46` with zero-address + already-set guards). Both `payPodiumPayout` (lines 58-62) and `forwardPodiumResidual` (lines 71-75) gate `msg.sender == prizePusher`. TimeCurve.distributePrizes calls these via `_forwardCategoryPrizes` and `_payPodiumFrom`. | | DoubLPIncentives / launch anchor (#158): LP seeding and 1.275× rule | **PASS deferred** | DoubLPIncentives = FeeRouter sink\[0\] (`0x041003...4982`, 30% slice). 1.275× rule per `PARAMETERS.md:44` is **post-sale-end policy** (28.5M DOUB LP-seed bucket not yet minted; sale not ended). Will be verified at launch-completion audit. | --- ## §5 TimeCurveBuyRouter (immutable companion) ### §5.1 Constructor immutables | Item | Onchain | Expected | Result | |------|---------|----------|--------| | `timeCurve` | `0x1b68bb...2bf71` | TimeCurve proxy | **PASS** | | `kumbayaRouter` | `0xe5bbef8d...470e` | Kumbaya SwapRouter02 (integrator-kit mainnet 4326) | **PASS exact** | | `weth` | `0x4200...0006` | OP-stack canonical WETH9 predeploy | **PASS** | | `stableToken` | `0xfafddbb3...79e7` | MegaETH USDm (Kumbaya default-token-list) | **PASS** (non-zero, not ETH-only deployment) | | `cl8yProtocolTreasury` | `0xfb45cfbb...efd9` | Non-zero sink for CL8Y dust per #70 | **PASS** (triple-use address — see finding #3) | | `initialOwner` + Ownable2Step clean | ops; `pendingOwner = (none)` | clean transfer state | **PASS** | ### §5.2 Path / swap semantics | Item | Result | Evidence | |------|--------|----------| | Packed path encoding; exactOutput ends in WETH/stable per payKind | **PASS by closed audit** | Per audit M-02 / #117 (closed): refund invariants verified. Foundry test vectors in `contracts/test/TimeCurveBuyRouter.t.sol`. | | Sale phase mirroring per #118 (closed) | **PASS by closed ticket** | buyViaKumbaya reverts `TimeCurveBuyRouter__BadSalePhase` pre-saleStart per closed ticket / audit L-01. | | Refund invariants per #117 (closed) | **PASS by closed audit** | Marginal refund only; no subsidy from pre-seeded balances. | ### §5.3 Rescue surfaces | Item | Result | Evidence | |------|--------|----------| | `rescueETH` / `rescueERC20`: only owner | **PASS** | `TimeCurveBuyRouter.sol:193` (rescueETH) and `:206` (rescueERC20). Both `external onlyOwner nonReentrant`. | ### §5.4 Interconnections | Item | Result | Evidence | |------|--------|----------| | TimeCurve.setTimeCurveBuyRouter(this) executed | **PASS** | Mutual trust verified §4.2: TimeCurve.timeCurveBuyRouter == BuyRouter address; BuyRouter.timeCurve == TimeCurve proxy. | | Frontend `VITE_KUMBAYA_TIMECURVE_BUY_ROUTER` equals onchain router | **PASS exact** | Bundle = `0xb09542ac...c75b61` = onchain. Fail-closed satisfied. | --- ## §6 FeeRouter (AccessControl) | Item | Result | Evidence | |------|--------|----------| | Sink addresses (5) match TimeCurve fee split targets | **PASS** | See §4.4 table. All 5 sinks aligned to docs. | | Role matrix: DEFAULT_ADMIN, no stray distributors | **PASS** | DEFAULT_ADMIN_ROLE = ops only. GOVERNOR_ROLE: not granted to any non-ops (would require separate event scan if doubt). No FEE_ROUTER role — `distributeFees` permissionless by design (see §4.10). | | `distributeFees`: reverts for non-distributable token (evidence) | **PASS** | `eth_call distributeFees(DOUB, 1000)` reverts with `"FeeRouter: token not distributable"` (string match to `FeeRouter.sol:74`). | | Rounding: last sink receives remainder | **PASS** | `FeeRouter.sol:18` + `:70` document: "Last sink receives any rounding remainder to prevent dust loss." Sink\[4\] (RabbitTreasuryVault interim) absorbs remainder. | --- ## §7 PodiumPool | Item | Result | Evidence | |------|--------|----------| | Asset CL8Y alignment with TimeCurve | **PASS by design** | PodiumPool is asset-agnostic (no `asset()` getter). TimeCurve passes `acceptedAsset` (CL8Y) explicitly on every call to `payPodiumPayout(token, ...)` and `forwardPodiumResidual(token, ...)`. Alignment enforced at caller side. | | Payout pull / PrizePaid: role or allowance model matches TimeCurve | **PASS** | `prizePusher = TimeCurve` (set-once via `setPrizePusher`; `PodiumPool.sol:42-46`). Both payout entrypoints gate `msg.sender == prizePusher`. | | AccessControl: admin + minter/payer roles reviewed | **PASS** | DEFAULT_ADMIN_ROLE = ops only (§4.10 cross-roles check). DISTRIBUTOR_ROLE: **never granted** (legacy fallback unused per source `PodiumPool.sol:12`). Only one privileged surface: `setPrizePusher(address) onlyRole(DEFAULT_ADMIN_ROLE)` with zero-address + set-once guards. | --- ## §8 ReferralRegistry | Item | Result | Evidence | |------|--------|----------| | `cl8yToken` matches CL8Y | **PASS exact** | `ReferralRegistry.cl8yToken() = 0xfbaa45a5...0098d`. Same as TimeCurve.acceptedAsset and `deployment-guide.md:42` canonical. NOT CL8YProtocolTreasury (which `docs/product/referrals.md:12` warns against). | | `registrationBurnAmount` matches intended economics | **PASS exact** | `1.0 CL8Y` (1e18). Matches `PARAMETERS.md:53` ("1e18 (1 token, 18 decimals)") and `docs/product/referrals.md:11` ("Registration burns 1 CL8Y"). BURN_ADDRESS = `0x...dEaD` (irreversible sink). | | Owner / upgrade (UUPS): admin surfaces documented | **PASS** | OwnableUpgradeable (1-step). Owner = ops. `_authorizeUpgrade(address) internal override onlyOwner {}` at ReferralRegistry source. Implementation `0xbc379586...415e64`. Verified on explorer. | --- ## §9 Cross-contract integration | Item | Result | Evidence | |------|--------|----------| | TimeCurve → FeeRouter: buy fee path enabled flag + live distributeFees role | **PASS** | buyFeeRoutingEnabled = true (§4.7). distributeFees permissionless + CL8Y allowlisted + DOUB blocked (§6). | | TimeCurve → ReferralRegistry: address and ReferralApplied path uses same registry as frontend | **PASS** | TimeCurve.referralRegistry == bundle `VITE_REFERRAL_REGISTRY_ADDRESS` == registry contract. hashCode byte-identical with frontend keccak256 (§4.6). Single source of truth. | | TimeCurve ↔ DoubPresaleVesting: presale CHARM bonus pointer and PRESALE_CHARM_WEIGHT_BPS behavior (#202) | **PASS** | PRESALE_CHARM_WEIGHT_BPS = 1500 (§4.5). doubPresaleVesting → PresaleCharmBeneficiaryRegistry per #202 split design (§2.6). Both registry + vesting implement compatible `isBeneficiary` (§3). | | Kumbaya env ↔ onchain: quoter/router/WETH/USDM/fees match deployment for same chain id | **PASS** | Kumbaya integrator-kit canonical (mainnet 4326): SwapRouter02 / QuoterV2 / WETH9 all match bundle + BuyRouter immutables exactly. USDm from default-token-list matches BuyRouter.stableToken. Chain ID 4326 confirmed. | | Indexer / ADDRESS_REGISTRY: optional cross-check (#156) | **N/A — optional** | `yieldomega-megaeth_mainnet-<timestamp>.json` registry is published separately per `stage3-mainnet-operator-runbook.md`. Not in repo `contracts/deployments/` (only local-anvil / stage2-anvil / kumbaya-anvil-example). Deliverable belongs to operator runbook, not in-repo audit. | --- ## §10 Document drift summary | Source doc | Status | Notes | |------------|--------|-------| | `contracts/PARAMETERS.md` | **Partial pass** | Rows 17/38/39/41/82 (testnet TODOs for addresses) resolved by onchain values. **Rows 104/106/107 unresolved** — "Deployer EOA — TODO multisig / timelock / narrow multisig before mainnet". Rows 108/120 (LeprechaunNFT MINTER + max supply per series) pending. **Launch-blocking sign-off question for ops: confirm multisig adoption status or formally waive with governance sign-off.** | | `docs/operations/final-signoff-and-value-movement.md` | **Pass** | All 4 value-movement gates match doc defaults (§4.7). Go-live order per #55 traced: `setClaimsEnabled` after final signoff → `endSale` → `setReservePodiumPayoutsEnabled` → `distributePrizes`. State machine including `#133` empty-pool path understood. | | `docs/onchain/fee-routing-and-governance.md` | **Pass with notes** | Sinks 3000/4000/2000/0/1000 = 10_000 exact (§4.4). Sink\[4\] uses interim RabbitTreasuryVault per #159 (documented). Sink\[3\] (`0xfb45cfbba0...`) is the same address as TimeCurve.unredeemedLaunchedTokenRecipient and TimeCurveBuyRouter.cl8yProtocolTreasury — finding #3: confirm this is a single canonical EcosystemTreasury contract serving multiple sink roles vs accidental address overlap. | | `docs/product/primitives.md` | **Pass** | Podium categories (40/25/20/15), placement (4:2:1), REFERRAL_EACH_BPS=500, PRESALE_CHARM_WEIGHT_BPS=1500 all match (§4.5 / §4.6). | | `docs/integrations/kumbaya.md` | **Pass** | Kumbaya integrator-kit canonical addresses (SwapRouter02 / QuoterV2 / WETH9) for chain 4326 all match BuyRouter immutables + frontend bundle exactly (§9). USDm address from default-token-list matches BuyRouter.stableToken. | --- ## Method note All onchain reads via `eth_call` / `eth_getStorageAt` / `eth_getLogs` against `https://mainnet.megaeth.com/rpc`. Selectors resolved from Foundry build artifacts at `contracts/out/*.sol/*.json` (`methodIdentifiers` map) to avoid keccak guessing. ABIs / source paths cited per item. No state-changing txs broadcast during audit. --- **End of report. `/cc @PlasticDigits` for review of findings #1–#3 before sale opens 2026-05-18 12:00 UTC.**
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-14 13:44:40 +00:00
PlasticDigits commented 2026-05-14 13:44:42 +00:00 (Migrated from gitlab.com)

Excellent. Closing

Excellent. Closing
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#207
No description provided.