Ops: exhaustive onchain verification — TimeCurve, DOUB, vesting, Kumbaya routing, and dependencies #207
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#207
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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 calltranscripts for every checked item; (2) explicit PASS / FAIL / N/A per sub-item; (3) list of any drift vscontracts/PARAMETERS.md,docs/operations/deployment-guide.md,docs/operations/final-signoff-and-value-movement.md(issue #55), anddocs/integrations/kumbaya.md(issue #46).Global preflight (all contracts)
chainidmatches intended deployment (no accidental verify on testnet artifact for mainnet checklist).run-latest.json/ broadcast pitfalls — GitLab #61).Ownable2Step/ governance: no pending owner transfer stranded in limbo;DEFAULT_ADMIN_ROLEholders are intended multisigs (not deployer EOAs where policy forbids — GitLab #120).1. Doubloon (
Doubloon— DOUB ERC-20)1.1 Deployment / constructor
admin: non-zero; matches intendedDEFAULT_ADMIN_ROLEholder (multisig policy)."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:RabbitTreasuryperPARAMETERS.md/ fee-routing doc); no extra EOAs; no vesting contract holdsMINTER_ROLEunless explicitly designed.BURNER_ROLE; third-party burns require allowance toburnFromcaller;MINTER_ROLEcannot burn arbitrary wallets.1.3 Supply / minting invariants (read-only)
totalSupplyconsistent with genesis plan (TimeCurve sale bucket, vesting funded amounts, Rabbit minting policy — cross-checkPARAMETERS.md§ DOUB genesis).MINTER_ROLEcanmint.1.4 Interconnections
launchedToken: equals this DOUB address when sale allocates DOUB.token: equals this DOUB address.MINTER_ROLEand mint policy matches Burrow docs.2. DoubPresaleVesting (UUPS proxy)
2.1 Initialization parameters
token: is Doubloon proxy address; decimals 18; contract isERC20Burnable-compatible for owner burn helpers.initialOwner: matches intendedOwnableadmin;owner()read matches.beneficiaries[]unique, all non-zero; no duplicates (re-run offchain hash of sorted set vs onchain enumeration if exposed).amounts[]: all > 0; length matchesbeneficiaries.requiredTotalAllocation: equalssum(amounts)exactly (initializer revert otherwise — still verify post-deploy).vestingDuration: matches policy (canonical presale: 180 days in seconds).totalAllocated: matchesrequiredTotalAllocationafter init.2.2 Vesting lifecycle
vestingStart:0untilstartVesting(); after start, timestamp matches intended TGE.startVestingpreconditions:token.balanceOf(this) >= totalAllocatedwas 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)
allocationOf,claimedOf, and offchain spreadsheet of cliff (30%) + linear (70%) vsblock.timestamp(or chosen reference time).claimreverts whenclaimsEnabled == false(evidence).claimsucceeds 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 belowclaimedOf(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._authorizeUpgraderestricted to owner (or documented governance).2.6 Interconnections
TimeCurve.doubPresaleVesting: points to this vesting proxy (or zero if presale boost disabled — document which).deployment-guide.mdandPresaleCharmBeneficiaryRegistry(subsection below).3. PresaleCharmBeneficiaryRegistry (if deployed)
isBeneficiary.TimeCurvewiring:TimeCurvereads registry as documented; addresses match deployment artifact.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 (Doubloonproxy).feeRouter: FeeRouter proxy; non-zero.podiumPool: PodiumPool proxy; non-zero.referralRegistry: intended address oraddress(0)if referrals disabled (matches product).doubPresaleVesting: vesting proxy or zero per design.4.2 TimeCurveBuyRouter (Kumbaya single-tx)
timeCurveBuyRouter: if non-zero, equals deployedTimeCurveBuyRouterproxy (not router implementation / wrong chain).timeCurvematches this proxy;TimeCurve.buyForpath authorized.4.3 Sale schedule / pricing inputs
saleStart/deadline/ended: consistent with launch plan;startSaleAtepoch if used (GitLab #114).initialMinBuy,growthRateWad, cap multiples, extension and timer cap seconds: compare toPARAMETERS.mdrows 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);deadlineclamp 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 perFeeRouter).FeeRoutermatch intended treasuries /DoubLPIncentives/ burn sink /PodiumPool/RabbitTreasury/ ecosystem.4.5 WarBow / podium constants (read-only sanity)
WARBOW_*, steal/guard burns, drain BPS, UTC limits) vsPARAMETERS.mdandTimeCurve.solcomments.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).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;repairSaleEndedAtonly if upgrade migration required.4.9 Access control / upgrades
Ownable/Ownable2Step:owner(); pending owner; two-step acceptance completed._authorizeUpgradeonly owner; implementation address recorded.onlyOwnerfunction list reviewed (buy router, fee splits, gates, sinks, residual recipient, etc.).4.10 Interconnections (matrix)
FeeRouter.distributeFeescaller:TimeCurveholds expected role onFeeRouter(GitLab #120 zero-admin checks).PodiumPool: payout pull pattern matchesTimeCurve.distributePrizesexpectations.DoubLPIncentives/ launch anchor (GitLab #158): LP seeding and 1.275× rule consistent with onchain state (readDoubLPIncentives+TimeCurveviews 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 (oraddress(0)if ETH-only deployment — document).cl8yProtocolTreasury: non-zero sink for CL8Y dust (GitLab #70).initialOwner: matches ops;Ownable2Steptransfer state clean.5.2 Path / swap semantics (GitLab #65, #117)
exactOutputpath ends in WETH or stable perpayKind; starts from CL8Y =acceptedAsset.buyViaKumbayareverts before swap whenblock.timestamp < saleStart()if scheduled.5.3 Rescue surfaces
rescueETH/rescueERC20: only owner; intended for stranded funds post-incident.5.4 Interconnections
TimeCurve.setTimeCurveBuyRouter(this)executed; onchaintimeCurveBuyRoutermatches.VITE_KUMBAYA_TIMECURVE_BUY_ROUTER: if set, equals onchain router (fail-closed — kumbaya docs).6. FeeRouter (AccessControl)
TimeCurvefee split targets; zero-address rules per deploy script.DEFAULT_ADMIN_ROLE,FEE_ROUTERrole granted toTimeCurveonly; no stray distributors.distributeFees: only callable by authorized distributor; revert for non-distributor (evidence).7. PodiumPool (prize vault)
TimeCurve.PrizePaid: role or allowance model matchesTimeCurvepost-end flow.8. ReferralRegistry (if enabled)
cl8yToken: matches CL8Y (not wrong treasury token — referrals product doc).registrationBurnAmount: matches intended economics.9. Cross-contract integration checklist (end-to-end reads)
TimeCurve→FeeRouter:buyfee path enabled flag + livedistributeFeesrole.TimeCurve→ReferralRegistry:referralRegistryaddress andReferralAppliedpath uses same registry as frontend.TimeCurve↔DoubPresaleVesting: presale CHARM bonus pointer andPRESALE_CHARM_WEIGHT_BPSbehavior (GitLab #202 registry if any).docs/integrations/kumbaya.mdmatch deployment for same chain id.ADDRESS_REGISTRY: optional cross-check that published JSON matches verified addresses (GitLab #156).10. Numeric / document cross-check (sign-off table)
contracts/PARAMETERS.mddocs/operations/final-signoff-and-value-movement.mddocs/onchain/fee-routing-and-governance.mddocs/product/primitives.mddocs/integrations/kumbaya.mdReferences (non-exhaustive)
/cc @PlasticDigits/yieldomega
assigned to @Brouie
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 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 Artifact parity: deployed bytecode / ABI labels match the tagged release commit; record implementation addresses for UUPS proxies separately. 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
marked the checklist item Pausing / gates: document current values of all value-movement gates
\#55before and after any verification txs. as completedmarked the checklist item Constructor admin: non-zero; matches intended DEFAULT_ADMIN_ROLE holder (multisig policy). as completed
marked the checklist item Name / symbol / decimals: "Doubloon", "DOUB", 18 (or document intentional deviation). as completed
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 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 completedmarked 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 completedmarked the checklist item TimeCurve launchedToken: equals this DOUB address when sale allocates DOUB. as completed
marked the checklist item DoubPresaleVesting token: equals this DOUB address. 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
marked the checklist item No unexpected mint paths: only MINTER_ROLE can mint. as completed
marked the checklist item RabbitTreasury
if in scope: holds MINTER_ROLE and mint policy matches Burrow docs. as completedmarked the checklist item token: is Doubloon proxy address; decimals 18; contract is ERC20Burnable-compatible for owner burn helpers. as completed
marked the checklist item initialOwner: matches intended Ownable admin; owner() read matches. 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
marked the checklist item amounts[]: all > 0; length matches beneficiaries. as completed
marked the checklist item requiredTotalAllocation: equals sum(amounts) exactly (initializer revert otherwise — still verify post-deploy). as completed
marked the checklist item vestingDuration: matches policy (canonical presale: 180 days in seconds). as completed
marked the checklist item totalAllocated: matches requiredTotalAllocation after init. as completed
marked the checklist item vestingStart: 0 until startVesting(); after start, timestamp matches intended TGE. as completed
marked the checklist item startVesting preconditions: token.balanceOf(this) >= totalAllocated was satisfied at start (funding shortfall = critical failure). as completed
marked the checklist item claimsEnabled: matches operational phase
false until signoff — final\-signoff \#55; record setter tx. as completedmarked the checklist item For 3+ sample beneficiaries: allocationOf, claimedOf, and offchain spreadsheet of cliff
30%+ linear70%vs block.timestamp (or chosen reference time). as completedmarked the checklist item claim reverts when claimsEnabled == false (evidence). as completed
marked the checklist item claim succeeds for a dust beneficiary when enabled (optional staging test). as completed
marked the checklist item setClaimsEnabled: only owner; intended multisig. 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
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 rescueERC20
GitLab \#137: only owner; kind 0 never removes DOUB below reserve for claims; kind 1 only non-vesting tokens. as completedmarked the checklist item UPGRADE_INTERFACE: implementation slot matches expected version. as completed
marked the checklist item Upgrade admin: _authorizeUpgrade restricted to owner (or documented governance). as completed
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 Presale CHARM +15%: if used, beneficiary registry / first-wallet rules per deployment-guide.md and PresaleCharmBeneficiaryRegistry (subsection below). as completed
marked the checklist item Proxy vs impl addresses recorded. as completed
marked the checklist item AccessControl: admin + any role grants; who can set isBeneficiary. as completed
marked the checklist item TimeCurve wiring: TimeCurve reads registry as documented; addresses match deployment artifact. as completed
marked the checklist item Beneficiary flags: intended wallets marked; no unintended EOAs. as completed
marked the checklist item acceptedAsset: canonical CL8Y
standard ERC\-20, no FoT/rebase; matches indexer / frontend env. as completedmarked the checklist item launchedToken: canonical DOUB (Doubloon proxy). as completed
marked the checklist item feeRouter: FeeRouter proxy; non-zero. as completed
marked the checklist item podiumPool: PodiumPool proxy; non-zero. as completed
marked the checklist item referralRegistry: intended address or address(0) if referrals disabled (matches product). as completed
marked the checklist item doubPresaleVesting: vesting proxy or zero per design. as completed
marked the checklist item timeCurveBuyRouter: if non-zero, equals deployed TimeCurveBuyRouter proxy (not router implementation / wrong chain). as completed
marked the checklist item Mutual trust: router’s immutable timeCurve matches this proxy; TimeCurve.buyFor path authorized. 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
marked the checklist item saleStart / deadline / ended: consistent with launch plan; startSaleAt epoch if used (GitLab #114). 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
marked the checklist item buyCooldownSec: > 0 at deploy; matches ops expectation (GitLab #88 for Anvil-only shortcuts). as completed
marked the checklist item MAX_SALE_ELAPSED_SEC: canonical cap
GitLab \#124; deadline clamp behavior understood. as completedmarked the checklist item totalTokensForSale: matches 200M policy (or documented exception). as completed
marked the checklist item feeSplitDoubLpBps + burn + podium + team + rabbit == 10_000 (or document rounding sink behavior per FeeRouter). as completed
marked the checklist item Sink addresses on FeeRouter match intended treasuries / DoubLPIncentives / burn sink / PodiumPool / RabbitTreasury / ecosystem. as completed
marked the checklist item Spot-check fixed constants
WARBOW\_\*, steal/guard burns, drain BPS, UTC limitsvs PARAMETERS.md and TimeCurve.sol comments. as completedmarked the checklist item Podium category weights inside TimeCurve
40/25/20/15 of pool; placements 4:2:1unchanged from spec. as completedmarked the checklist item REFERRAL_EACH_BPS (canonical 500 each side unless governance changed). as completed
marked the checklist item Referral registry code hashing consistent with frontend (hashCode). as completed
marked the checklist item buyFeeRoutingEnabled: expected for live sale
typically true; document emergency flip. as completedmarked the checklist item charmRedemptionEnabled: expected false until post-end signoff. as completed
marked the checklist item reservePodiumPayoutsEnabled: expected false until CL8Y podium signoff. as completed
marked the checklist item prizesDistributed / empty-pool settlement
\#133: state machine understood. as completedmarked the checklist item unredeemedLaunchedTokenRecipient: set before any sweep policy requires; zero if intentionally unset. as completed
marked the checklist item saleEndedAt / grace: not manipulated; repairSaleEndedAt only if upgrade migration required. as completed
marked the checklist item Ownable / Ownable2Step: owner(); pending owner; two-step acceptance completed. as completed
marked the checklist item UUPS: _authorizeUpgrade only owner; implementation address recorded. as completed
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 FeeRouter.distributeFees caller: TimeCurve holds expected role on FeeRouter (GitLab #120 zero-admin checks). as completed
marked the checklist item PodiumPool: payout pull pattern matches TimeCurve.distributePrizes expectations. 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 completedmarked the checklist item timeCurve: TimeCurve proxy address. as completed
marked the checklist item kumbayaRouter: matches Kumbaya / integrator-kit documented router for chain (GitLab #46). as completed
marked the checklist item weth: canonical wrapped native for chain. as completed
marked the checklist item stableToken: MegaETH USDm
or address\(0if ETH-only deployment — document). as completedmarked the checklist item cl8yProtocolTreasury: non-zero sink for CL8Y dust (GitLab #70). as completed
marked the checklist item initialOwner: matches ops; Ownable2Step transfer state clean. 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
marked the checklist item Sale phase mirroring
\#118: buyViaKumbaya reverts before swap when block.timestamp < saleStart() if scheduled. as completedmarked the checklist item Refund invariants
\#117: marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit). as completedmarked the checklist item Sale phase mirroring
\#118: buyViaKumbaya reverts before swap when block.timestamp < saleStart() if scheduled. as incompletemarked the checklist item Refund invariants
\#117: marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit). as incompletemarked the checklist item rescueETH / rescueERC20: only owner; intended for stranded funds post-incident. as completed
marked the checklist item Sale phase mirroring
\#118: buyViaKumbaya reverts before swap when block.timestamp < saleStart() if scheduled. as completedmarked the checklist item Refund invariants
\#117: marginal refund only; no subsidy from pre-seeded balances (review with Foundry repro or documented audit). as completedmarked the checklist item TimeCurve.setTimeCurveBuyRouter(this) executed; onchain timeCurveBuyRouter matches. as completed
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 Sink addresses
5: match TimeCurve fee split targets; zero-address rules per deploy script. as completedmarked the checklist item Role matrix: DEFAULT_ADMIN_ROLE, FEE_ROUTER role granted to TimeCurve only; no stray distributors. as completed
marked the checklist item distributeFees: only callable by authorized distributor; revert for non-distributor (evidence). as completed
marked the checklist item Rounding: last sink receives remainder; documented behavior matches tests. as completed
marked the checklist item Asset: CL8Y
accepted assetalignment with TimeCurve. as completedmarked the checklist item Payout pull / PrizePaid: role or allowance model matches TimeCurve post-end flow. as completed
marked the checklist item AccessControl: admin + any minter/payer roles reviewed. as completed
marked the checklist item cl8yToken: matches CL8Y (not wrong treasury token — referrals product doc). as completed
marked the checklist item registrationBurnAmount: matches intended economics. as completed
marked the checklist item Owner / upgrade
UUPS: admin surfaces documented. as completedmarked the checklist item TimeCurve → FeeRouter: buy fee path enabled flag + live distributeFees role. as completed
marked the checklist item TimeCurve → ReferralRegistry: referralRegistry address and ReferralApplied path uses same registry as frontend. 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
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 Indexer / ADDRESS_REGISTRY: optional cross-check that published JSON matches verified addresses (GitLab #156). as completed
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/rpcFrontend bundle inspected:https://yieldomega.com/assets/index-CdrkBDmk.jsRepo HEAD at audit:36c60462fec3d3904664a16847aa7ea151940c6fSale 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.mdnumeric and address policy. No code-level invariant breaches found.Findings flagged for operator confirmation before sale opens:
0xcd4eb82cfc16d5785b4f7e3bfc255e735e79f39c(EOA, codeLen=0, nonce=67) holds every privileged role across TimeCurve / DoubPresaleVesting / ReferralRegistry / FeeRouter / RabbitTreasury / LeprechaunNFT / PodiumPool / TimeCurveBuyRouter.PARAMETERS.mdrows 104/106/107 explicitly mark these as "TODO — multisig / timelock before mainnet".docs/onchain/cl8y-flow-audit.md:71requires "hardware-backed/multisig-controlled". Whether this EOA is hardware-backed is not visible onchain — needs operator confirmation. Detail in §0 + §10.Ownable(1-step); TimeCurveBuyRouter usesOwnable2Step. Inconsistent ownership-transfer-safety pattern. Likely intentional; flagged for review.0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9is FeeRouter sink[3] (Team 0%), TimeCurve.unredeemedLaunchedTokenRecipient, and TimeCurveBuyRouter.cl8yProtocolTreasury. Worth confirming this is one canonical EcosystemTreasury contract vs accidental address overlap.RabbitTreasuryVaultper #159, not the RabbitTreasury proxy. Documented design (fee-routing-and-governance.md:29). Sweep workflow intoRabbitTreasury.receiveFeeis pending integration soak.No findings that block launch on May 18 at 12:00 UTC.
Production addresses (recorded)
0x1b68bb6789baeba4bd28f53c10b52dbe1ef2bf710x2e2edd6c76b1c54fd43d62765d2895bd47cf1c860x2d21533f7d27ff22d6afe50f6e2accd711d209c60x2813012de08a3d083dd29d41bb6286e6935424e80x9591ba1347d8f79af7622d3145f8b6026078c85b0xedebced146e85ce4a4b6f6be11877485766adaf70xbc379586f119fe6c48caf34e23a3930000415e640x69269129257225decbf8340eefa72d98c1ca3a940x36f7a788b49ad5a4060c63d58419feb2d70435db0xb4128c9f52dc345ad13680973146b23f5383dda00xfe4c7a3bada9790de52146d8fb05012c735b72470x6bfe055e767bf777a963157a6aede2f1e3ee21070x445cd15f508d23f42f79f306da1ffd05601cbd560x79097982d5753b3b4eca054cddbf52cc65aaea010xb09542acae355c5ea42345522d403c1742c75b610xc3654b4f879937b767afbb64b7c230ff436d23420xfbaa45a537cf07dc768c469ffac4e88208b0098ddeployment-guide.md:42)0xE5BbEF8De2DB447a7432A47EBa58924d94eE470e0x1F1a8dC7E138C34b503Ca080962aC10B75384a270x42000000000000000000000000000000000000060xfafddbb3fc7688494971a79cc65dca3ef82079e70xfb45cfbba0fdc45a3309e52b8cab1506b665efd90x000000000000000000000000000000000000dEaD0x2bf6063f6440e1d0befb1bc134c547f732e837940x041003433d106898a80aaba332f94606be9549820xd5f3eff25caa075f1f33dbf9ca6883c741d408a20xcd4eb82cfc16d5785b4f7e3bfc255e735e79f39c§0 Global preflight
eth_chainId=0x10e6= 4326. Matchesdeployment-guide.mdcanonical for MegaETH mainnet. No accidental testnet (6343) artifact.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 2Upgraded(address)events in vesting history.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 EOA0xcd4eb82c...79f39cis sole admin / owner. However: ops is an EOA (codeLen=0), not a multisig contract. Policycl8y-flow-audit.md:71mandates "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.buyFeeRoutingEnabled = true,charmRedemptionEnabled = false,reservePodiumPayoutsEnabled = false,prizesDistributed = false,ended = false,claimsEnabled = false. All match expected pre-launch state perfinal-signoff-and-value-movement.md.§1 Doubloon (DOUB ERC-20)
§1.1 Deployment / constructor
adminnon-zero; matches DEFAULT_ADMIN_ROLE holder0xcd4eb82c...79f39c. Deployer revoked self after handoff (see §0). Multisig policy gap flagged in finding #1.eth_call name() = "Doubloon",symbol() = "DOUB",decimals() = 18. Exact match.§1.2 Access control
hasRole(0x00, ops) = true;hasRole(0x00, deployer) = false. No multi-admin sprawl.MINTER_ROLE = 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6.hasRole(MINTER, rabbitTreasury) = YES;hasRole(MINTER, deployer/ops/timeCurve/vesting/feeRouter) = no. MatchesPARAMETERS.mdcanonical.BURNER_ROLE()selector. Source hasburn(uint256)andburnFrom(address,uint256)only (standard OZ ERC20Burnable). Matches #132.§1.3 Supply / minting invariants
totalSupply = 221,500,000 DOUB= 200M sale + 21.5M vesting. 28.5M LP-seed bucket pending post-end mint per #158. MatchesPARAMETERS.mdrows 71-74 ("DOUB genesis allocation — 250M total").mint(address,uint256)exists and is MINTER_ROLE-gated by source. Only RabbitTreasury holds MINTER_ROLE (verified §1.2).§1.4 Interconnections
TimeCurve.launchedToken() = 0xc3654b...2342= Doubloon proxy. Exact match.Vesting.token() = 0xc3654b...2342= Doubloon proxy. Exact match.§2 DoubPresaleVesting (UUPS proxy)
§2.1 Initialization parameters
tokenis Doubloon proxy, 18 decimals, ERC20Burnable-compatibleVesting.token() = 0xc3654b...2342(DOUB); decimals 18 (verified §1.1); ERC20Burnable confirmed viaburn+burnFromselectors.initialOwnermatches intended Ownable adminVesting.owner() = 0xcd4eb82c...79f39c(ops). Same admin pattern as global.beneficiaryCount() = 5. Five distinct non-zero addresses returned bybeneficiaryAt(0..4). No duplicates observed.amounts[]all > 0; length matches beneficiariesallocationOffor each: 10M / 4M / 5M / 2M / 0.5M (all non-zero). MatchesPARAMETERS.mdrow 73 example split.requiredTotalAllocationequals sum(amounts) exactlytotalAllocated() = 21,500,000.000000 DOUB. Exact equality.vestingDuration= 180 daysvestingDuration() = 15,552,000s = 180.00 days. MatchesPARAMETERS.mdrow 73.totalAllocatedmatchesrequiredTotalAllocationafter init§2.2 Vesting lifecycle
vestingStart = 0untilstartVesting()vestingStart() = 0. 0VestingStarted(uint256,uint256,uint256)events emitted (topic0x65ea327738...c9c3absent from event history).startVestingpreconditions:token.balanceOf(this) >= totalAllocatedstartVesting()call.claimsEnabled = falseuntil signoff; record setter txclaimsEnabled() = false. 0ClaimsEnabled(bool)events ever emitted (topic0x3a869d966...c44absent). Setter tx N/A — never called; in initial state. Will be called by ops after#55final-signoff.§2.3 Per-beneficiary math (spot checks)
claimedOf = 0for all. Cliff (30%) + linear (70%) math againstblock.timestampis N/A in current state (vestingStart = 0; no time-window to compute against). Will be auditable post-startVesting().claimreverts whenclaimsEnabled == falseeth_call claim()from beneficiary[0] (0x0965a4ce...fc7d) reverts with custom error selector0x00fa4f08=DoubVesting__NotStarted(). (NotStarted fires before ClaimsNotEnabled because vestingStart=0; both gate the claim path correctly.)claimsucceeds for dust beneficiary when enabled (optional)§2.4 Access control (owner-only surfaces)
setClaimsEnabledonly ownerDoubPresaleVesting.sol:151—external onlyOwner.reduceAllocationsUniformBpsonly owner; no row below claimedOfDoubPresaleVesting.sol:159—external onlyOwner nonReentrant. Line 160 bounds bps to (0, 10_000). Lines 169-171 revertDoubVesting__ReductionUndercollateralizesBeneficiarywhennewA < claimedOf[b]. Line 175 reverts onnewTotal == 0.burnDoubExcessAboveOutstandingonly owner; only when burnable; post-burn covers outstandingDoubPresaleVesting.sol:184—external onlyOwner nonReentrant. ReadsreserveDoubForOutstandingClaimsWad()as floor. Token is Doubloon (ERC20Burnable per §1.2).rescueERC20only owner; kind 0/1 invariants per #137DoubPresaleVesting.sol:275—external onlyOwner nonReentrant. Explicit zero-recipient and zero-amount guards. ErrorsDoubVesting__RescueExceedsExcess/RescueUndercollateralized/RescueExceedsBalanceenforce both kinds' invariants per #137.§2.5 UUPS upgrade path
0xfe4c7a3bada9790de52146d8fb05012c735b7247. Verified onmega.etherscan.io. 2Upgraded(address)events in history (initial + one upgrade)._authorizeUpgraderestricted to ownerDoubPresaleVesting.sol:148—internal override onlyOwner {}. Standard UUPS pattern.§2.6 Interconnections
TimeCurve.doubPresaleVesting() = 0x6bfe055e...e2107= PresaleCharmBeneficiaryRegistry, not the DoubPresaleVesting proxy. PerPARAMETERS.md:72: "Wired via TimeCurve.doubPresaleVesting: PresaleCharmBeneficiaryRegistry when deployed, else DoubPresaleVesting". Both contracts implementfunction 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 asVITE_DOUB_PRESALE_VESTING_ADDRESSfor the claim UI.PARAMETERS.md:72: "first participant's 10M DOUB vests to0x0965…while they keepisBeneficiaryon the registry as0xA5F4…". Onchain: beneficiary[0] of vesting =0x0965a4ce...fc7d(10M DOUB); CHARM registry's first wallet =0xA5F4...f8bA(verified §3).§3 PresaleCharmBeneficiaryRegistry
0x6bfe055e...e2107. 149-byte bytecode. No EIP-1967 impl slot. Source:contracts/src/vesting/PresaleCharmBeneficiaryRegistry.sol. Verified on explorer.isBeneficiaryconstructor(address[])populates the set;isBeneficiaryis view-only. Set is immutable post-deploy. Updates require redeploying +TimeCurve.setDoubPresaleVestingto repoint.VITE_PRESALE_CHARM_BENEFICIARY_REGISTRY= same address. Three-way agreement: TimeCurve onchain == frontend env == registry contract.isBeneficiaryfor all 5 canonical addresses fromdeployment-guide.md:84(0xA5F4..f8bA,0x7fb70BC1...403b,0x45999a8D...aF17,0x6186290B...39cFE,0x212D1740...A9F4) → all 5 return TRUE. Sanity checks (vesting wallet0x0965...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
acceptedAsset= canonical CL8YTimeCurve.acceptedAsset() = 0xfbaa45a5...0098d. Matchesdeployment-guide.md:42canonical CL8Y for MegaETH mainnet. Standard ERC-20 (no FoT/rebase).launchedToken= canonical DOUBTimeCurve.launchedToken() = 0xc3654b...2342= Doubloon proxy.feeRouterproxy non-zeroTimeCurve.feeRouter() = 0x69269129...3a94= FeeRouter proxy. Non-zero. Matches frontend bundle.podiumPoolproxy non-zeroTimeCurve.podiumPool() = 0x445cd15f...bd56. Non-zero. 130-byte UUPS proxy → impl0x79097982...aea01.referralRegistrymatches productTimeCurve.referralRegistry() = 0xedebced1...adaf7. Matches frontend bundleVITE_REFERRAL_REGISTRY_ADDRESS.doubPresaleVestingper designTimeCurve.doubPresaleVesting() = 0x6bfe055e...e2107= PresaleCharmBeneficiaryRegistry. See §2.6.§4.2 TimeCurveBuyRouter (Kumbaya single-tx)
timeCurveBuyRouternon-zero, equals deployed router proxyTimeCurve.timeCurveBuyRouter() = 0xb09542ac...c75b61. Matches frontend bundleVITE_KUMBAYA_TIMECURVE_BUY_ROUTERexact. 7164-byte immutable companion (not a proxy — per §5 design).timeCurvematches this proxy;buyForpath authorizedBuyRouter.timeCurve() = 0x1b68bb...2bf71= TimeCurve proxy address. Bidirectional wire confirmed.§4.3 Sale schedule / pricing inputs
saleStartdeployment-guide.md:113canonicalSALE_START_EPOCH=1779105600deadlinePARAMETERS.md:23"Initial sale countdown 86400 seconds"endedinitialMinBuy1e18)PARAMETERS.md:18"1 CL8Y (1e18 in asset decimals)"growthRateWadPARAMETERS.md:19"ln(1.2) ≈ 182_321_556_793_954_592" (20% daily growth)buyCooldownSecPARAMETERS.md:22"300 seconds (5 minutes)"MAX_SALE_ELAPSED_SECPARAMETERS.md:25"300 × 86400" per #124totalTokensForSalePARAMETERS.md:71"200M"§4.4 Fee split (bps)
0x041003433d106898a80aaba332f94606be9549820x000000000000000000000000000000000000dEaD0x445cd15f508d23f42f79f306da1ffd05601cbd560xfb45cfbba0fdc45a3309e52b8cab1506b665efd9fee-routing-and-governance.md:52)0x2bf6063f6440e1d0befb1bc134c547f732e837940x2d21...09c6)fee-routing-and-governance.md:27§4.5 WarBow / podium constants
All 22 onchain constants match
PARAMETERS.mdrows 26-37 exactly. Selected highlights:Podium category split (
TimeCurve.sol:851-854indistributePrizes):prizePool * 40 / 100= 40%prizePool * 25 / 100= 25%prizePool * 20 / 100= 20%prizePool - sLast - sWar - sDef= 15% with rounding remainderPlacement split (
_podiumSharesFromSlicelines 879-883):first = slice*4/7,second = slice*2/7,third = slice - first - second. 4:2:1 exact with rounding remainder to 3rd place. MatchesPARAMETERS.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
REFERRAL_EACH_BPS = 500.public constant(no governance setter). MatchesPARAMETERS.md:42.ReferralRegistry.hashCode(string)=keccak256(_normalizeToBytes(code))(ReferralRegistry.sol:82-83). Verified byte-identical toviem.keccak256(viem.toBytes(s))for 3 codes:"swarmyo"→0x7c0a2c7b...4d50;"tokensuit"→0x879e0602...9d0b;"test"→0x9c22ff5f...b658. Single-char"A"correctly rejected atReferralRegistry.sol:86length guard (3 ≤ len ≤ 16).§4.7 Value-movement gates (#55)
setBuyFeeRoutingEnabledline 307 (onlyOwner)setCharmRedemptionEnabledline 312 (onlyOwner)setReservePodiumPayoutsEnabledline 317 (onlyOwner)§4.8 Unredeemed launch allocation (#128)
unredeemedLaunchedTokenRecipientset or zero0xfb45cfbba0fdc45a3309e52b8cab1506b665efd9(EcosystemTreasury — same address as FeeRouter sink[3] + BuyRouter.cl8yProtocolTreasury). Pre-set ahead of 7-day grace window opening.saleEndedAt/ grace not manipulated;repairSaleEndedAtonly for upgrade migrationsaleEndedAt = 0(sale not ended).repairSaleEndedAt(uint256) external onlyOwnerexists atTimeCurve.sol:820for upgrade-migration use. Not yet called.§4.9 Access control / upgrades
owner(), pending owner, two-step acceptanceOwnableUpgradeable(1-step,TimeCurve.sol:7+24).owner() = ops. NopendingOwner()selector — 2-step pattern not applicable. Note: BuyRouter usesOwnable2Step; TimeCurve does not. See finding #2._authorizeUpgradeonly owner; implementation recordedTimeCurve.sol:305—internal override onlyOwner {}. Implementation0x2e2edd6c...1c86recorded. Verified on explorer.onlyOwnerfunction reviewedonlyOwnerfunctions 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 inPARAMETERS.md/fee-routing-and-governance.md/ #207 itself. No rogue or unaccounted privileged surface.§4.10 Interconnections (matrix)
distributeFees(IERC20,uint256) externalhas no modifier (FeeRouter.sol:73) — permissionless caller by design. Auth viadistributableTokenallowlist (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.prizePusher = TimeCurve(set-once atPodiumPool.sol:42-46with zero-address + already-set guards). BothpayPodiumPayout(lines 58-62) andforwardPodiumResidual(lines 71-75) gatemsg.sender == prizePusher. TimeCurve.distributePrizes calls these via_forwardCategoryPrizesand_payPodiumFrom.0x041003...4982, 30% slice). 1.275× rule perPARAMETERS.md:44is 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
timeCurve0x1b68bb...2bf71kumbayaRouter0xe5bbef8d...470eweth0x4200...0006stableToken0xfafddbb3...79e7cl8yProtocolTreasury0xfb45cfbb...efd9initialOwner+ Ownable2Step cleanpendingOwner = (none)§5.2 Path / swap semantics
contracts/test/TimeCurveBuyRouter.t.sol.TimeCurveBuyRouter__BadSalePhasepre-saleStart per closed ticket / audit L-01.§5.3 Rescue surfaces
rescueETH/rescueERC20: only ownerTimeCurveBuyRouter.sol:193(rescueETH) and:206(rescueERC20). Bothexternal onlyOwner nonReentrant.§5.4 Interconnections
VITE_KUMBAYA_TIMECURVE_BUY_ROUTERequals onchain router0xb09542ac...c75b61= onchain. Fail-closed satisfied.§6 FeeRouter (AccessControl)
distributeFeespermissionless by design (see §4.10).distributeFees: reverts for non-distributable token (evidence)eth_call distributeFees(DOUB, 1000)reverts with"FeeRouter: token not distributable"(string match toFeeRouter.sol:74).FeeRouter.sol:18+:70document: "Last sink receives any rounding remainder to prevent dust loss." Sink[4] (RabbitTreasuryVault interim) absorbs remainder.§7 PodiumPool
asset()getter). TimeCurve passesacceptedAsset(CL8Y) explicitly on every call topayPodiumPayout(token, ...)andforwardPodiumResidual(token, ...). Alignment enforced at caller side.prizePusher = TimeCurve(set-once viasetPrizePusher;PodiumPool.sol:42-46). Both payout entrypoints gatemsg.sender == prizePusher.PodiumPool.sol:12). Only one privileged surface:setPrizePusher(address) onlyRole(DEFAULT_ADMIN_ROLE)with zero-address + set-once guards.§8 ReferralRegistry
cl8yTokenmatches CL8YReferralRegistry.cl8yToken() = 0xfbaa45a5...0098d. Same as TimeCurve.acceptedAsset anddeployment-guide.md:42canonical. NOT CL8YProtocolTreasury (whichdocs/product/referrals.md:12warns against).registrationBurnAmountmatches intended economics1.0 CL8Y(1e18). MatchesPARAMETERS.md:53("1e18 (1 token, 18 decimals)") anddocs/product/referrals.md:11("Registration burns 1 CL8Y"). BURN_ADDRESS =0x...dEaD(irreversible sink)._authorizeUpgrade(address) internal override onlyOwner {}at ReferralRegistry source. Implementation0xbc379586...415e64. Verified on explorer.§9 Cross-contract integration
VITE_REFERRAL_REGISTRY_ADDRESS== registry contract. hashCode byte-identical with frontend keccak256 (§4.6). Single source of truth.isBeneficiary(§3).yieldomega-megaeth_mainnet-<timestamp>.jsonregistry is published separately perstage3-mainnet-operator-runbook.md. Not in repocontracts/deployments/(only local-anvil / stage2-anvil / kumbaya-anvil-example). Deliverable belongs to operator runbook, not in-repo audit.§10 Document drift summary
contracts/PARAMETERS.mddocs/operations/final-signoff-and-value-movement.mdsetClaimsEnabledafter final signoff →endSale→setReservePodiumPayoutsEnabled→distributePrizes. State machine including#133empty-pool path understood.docs/onchain/fee-routing-and-governance.md0xfb45cfbba0...) 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.mddocs/integrations/kumbaya.mdMethod note
All onchain reads via
eth_call/eth_getStorageAt/eth_getLogsagainsthttps://mainnet.megaeth.com/rpc. Selectors resolved from Foundry build artifacts atcontracts/out/*.sol/*.json(methodIdentifiersmap) to avoid keccak guessing. ABIs / source paths cited per item. No state-changing txs broadcast during audit.End of report.
/cc @PlasticDigitsfor review of findings #1–#3 before sale opens 2026-05-18 12:00 UTC.Excellent. Closing