Deploy: TimeArenaBuyRouter on Anvil + setTimeArenaBuyRouter + default ETH E2E wiring
Summary
Automate local Anvil deployment of TimeArenaBuyRouter, wire TimeArena.setTimeArenaBuyRouter, and export VITE_KUMBAYA_TIME_ARENA_BUY_ROUTER so scripts/e2e-anvil.sh runs the ETH pay Playwright case without manual env. Closes the deploy gap left after onchain #251 (closed) and frontend #264 (closed).
Parent epic: #238 (closed). Related: #84 (closed) (registry + Vite merge), #78 (closed) (buy-router verification).
Current codebase
Onchain (done in #251 (closed))
TimeArenaBuyRouter.sol—buyViaKumbaya(ETH / stable / CL8Y → KumbayaexactOutput→ DOUB →TimeArena.buyFor). Paths end in DOUB (not CL8Y).TimeArena.sol—timeArenaBuyRouter,setTimeArenaBuyRouter(address)(owner),buyForrestricted to router.
Deploy / scripts (gap)
DeployDev.s.soldeploys Arena v2 (TimeArena, vaults,Doubloon, etc.) andstartArena()but does not deploy a buy router or callsetTimeArenaBuyRouter.DeployKumbayaAnvilFixtures.s.solis referenced throughout docs and shell (scripts/lib/anvil_deploy_dev.sh,scripts/start-local-anvil-stack.sh,docs/integrations/kumbaya.md) but is not present undercontracts/script/(onlyDeployDev.s.solandDeployProduction.s.solexist today).- Legacy
TimeCurveBuyRouterSolidity was retired; scripts still grep log lines forTimeCurveBuyRouter (single-txand registry keyTimeCurveBuyRouter(scripts/lib/kumbaya_local_anvil_env.sh). scripts/e2e-anvil.shrunsDeployDevonly; setsVITE_KUMBAYA_TIME_ARENA_BUY_ROUTERonly whenKUMBAYA_BUY_ROUTERis non-empty after optionalYIELDOMEGA_DEPLOY_KUMBAYA=1— which currently does not run in the default E2E path.scripts/verify-time-arena-buy-router-anvil.shis a stub (prints instructions, exits 0).
Frontend (done in #264 (closed))
- ETH/USDM UI calls
resolveTimeArenaBuyRouterForKumbayaSingleTxandsubmitArenaKumbayaSingleTxBuywhen onchaintimeArenaBuyRouteris set. - E2E
anvil-arena-wallet-writes.spec.tsskips ETH pay unlessVITE_KUMBAYA_TIME_ARENA_BUY_ROUTER(or legacy alias) is set at build time.
Fixtures
AnvilKumbayaFixture.sol—AnvilWETH9,AnvilMockUSDM,AnvilKumbayaRouter(swap + quoter) for local constant-product pools.
Why this is needed
- ETH E2E is not CI-realistic today — DOUB buy passes on Anvil; ETH pay is skipped unless operators manually deploy a router and export Vite env.
- Docs/scripts lie about
DeployKumbayaAnvilFixtures— references assume a script that must be restored or replaced with an Arena v2 variant. - Product parity — Arena v2 pay modes (
docs/frontend/arena-views.md) require a non-zerotimeArenaBuyRouteron devnet for integrators and agents runningbash scripts/e2e-anvil.sh.
Constraints and guardrails
- Dev-only: Buy-router deploy scripts must call
DevOnlyChainGuard.assertDevScriptChain()(Anvil / MegaETH testnet only) — same asDeployDev(docs/contracts/foundry-and-megaeth.md). - Onchain authority unchanged: 40/30/30 DOUB routing, CHARM band,
buyForrouter gate — seedocs/product/arena-v2.mdanddocs/testing/invariants-and-business-logic.md. - Do not reintroduce TimeCurve launchpad /
TimeCurveBuyRouteras the Arena authority (#244 (closed)). - Anvil pools: Router deploy must seed DOUB
↔️ WETH and DOUB↔️ CL8Y (and USDM path) reserves onAnvilKumbayaRoutersoexactOutputsucceeds for ETH/USDM paths used by the frontend (#264 (closed)). doubSurplusRecipient: Use a documented dev sink (e.g. deployer orAdminSellVault) — constructor reverts on zero (#251 (closed)).- Registry / indexer: Add
TimeArenaBuyRoutertocontracts/deployments/local-anvil-registry.json(and example JSON); optionalBuyViaKumbayadecode follow-up — minimum: do not break existingBuyingest. - AGPL-3.0; follow
docs/testing/strategy.md.
Relevant files
| Area | Files |
|---|---|
| Contracts | TimeArenaBuyRouter.sol, TimeArena.sol, AnvilKumbayaFixture.sol |
| Deploy script (new/restore) | contracts/script/DeployKumbayaAnvilFixtures.s.sol (or DeployArenaKumbayaAnvilFixtures.s.sol) |
| Shell | scripts/lib/anvil_deploy_dev.sh, scripts/e2e-anvil.sh, scripts/lib/kumbaya_local_anvil_env.sh, scripts/start-local-anvil-stack.sh, scripts/verify-time-arena-buy-router-anvil.sh |
| Registry | contracts/deployments/local-anvil-registry.json, contracts/deployments/kumbaya-anvil-registry.example.json |
| ABI export | contracts/script/export_abi_hashes.sh |
| Tests | New contracts/test/TimeArenaBuyRouter*.t.sol (local + optional fork) |
| E2E | frontend/e2e/anvil-arena-wallet-writes.spec.ts |
| Docs | docs/integrations/kumbaya.md, docs/testing/e2e-anvil.md, docs/testing/local-swap-testing.md, skills/play-time-arena-doub/SKILL.md |
Recommended direction
-
Add
DeployKumbayaAnvilFixtures.s.sol(Arena v2)- Input: deployed
TimeArenaproxy address (fromDeployDev). - Deploy:
AnvilWETH9,AnvilMockUSDM,AnvilKumbayaRouter,TimeArenaBuyRouter(immutable args: arena, kumbaya router,doub, reserve/CL8Y token, WETH, USDM, surplus recipient, owner). - Seed pools with liquidity for DOUB
exactOutputpaths (ETH: DOUB←WETH; USDM: DOUB←WETH←USDM). TimeArena.setTimeArenaBuyRouter(router)in same broadcast (deployer = owner).- Log parseable lines, e.g.
TimeArenaBuyRouter (single-tx): 0x….
- Input: deployed
-
Wire
anvil_deploy_dev.sh- Default
YIELDOMEGA_DEPLOY_KUMBAYA=1forscripts/e2e-anvil.sh(or always run fixtures afterDeployDevin that script only). - Parse
TimeArenaBuyRouterlog label; setKUMBAYA_BUY_ROUTER. - Assert:
cast call $TA "timeArenaBuyRouter()(address)"equals deployed router.
- Default
-
Registry + Vite helpers
- Extend
yieldomega_registry_merge_*forTimeArenaBuyRouter. yieldomega_frontend_merge_kumbaya_vite_full: setVITE_KUMBAYA_TIME_ARENA_BUY_ROUTER(keep legacyVITE_KUMBAYA_TIMECURVE_BUY_ROUTERonly if still needed for transitional tooling).
- Extend
-
Implement
verify-time-arena-buy-router-anvil.sh- DeployDev + fixtures → onchain router match → optional
forge test --match-contract TimeArenaBuyRouter→ spot-check onebuyViaKumbaya(ETH) on Anvil.
- DeployDev + fixtures → onchain router match → optional
-
Forge tests
- Local: happy path ETH + USDM (mock router),
StableIngressParity, bad path, paused arena, charm bounds, expired deadline. - Reuse patterns from retired TimeCurve buy-router tests if any exist in git history.
- Local: happy path ETH + USDM (mock router),
-
E2E
- Remove skip when
e2e-anvil.shalways provides router env; document thatYIELDOMEGA_DEPLOY_KUMBAYA=0opts out.
- Remove skip when
Acceptance criteria
-
DeployKumbayaAnvilFixtures(or renamed equivalent) exists, passesDevOnlyChainGuard, and broadcasts on Anvil. - After default
bash scripts/e2e-anvil.sh:TimeArena.timeArenaBuyRouter()is non-zero and equals deployedTimeArenaBuyRouter. -
scripts/e2e-anvil.shexportsVITE_KUMBAYA_TIME_ARENA_BUY_ROUTERwithout manual steps. - Playwright ETH pay test in
anvil-arena-wallet-writes.spec.tsruns (not skipped) on CI/local when usinge2e-anvil.sh. -
local-anvil-registry.json(or merge helper) includesTimeArenaBuyRouterwhen fixtures run. -
verify-time-arena-buy-router-anvil.shperforms real checks (non-stub) and exits non-zero on failure. - Docs updated: kumbaya integration, e2e-anvil, local-swap-testing, play-time-arena-doub skill — no stale
TimeCurveBuyRouteras Arena authority.
Test plan — functional paths
| # | Path | Steps | Expected |
|---|---|---|---|
| 1 | Default E2E deploy | bash scripts/e2e-anvil.sh |
DeployDev + fixtures; router set; ETH E2E executes |
| 2 | Onchain wiring | cast call timeArenaBuyRouter |
Matches logged router address |
| 3 | ETH buyViaKumbaya |
Anvil account, min CHARM, ETH pay | Tx succeeds; Buy / BuyViaKumbaya event |
| 4 | USDM buyViaKumbaya |
Same with USDM path | Succeeds when USDM pool seeded |
| 5 | DOUB direct (regression) | CL8Y/DOUB pay mode, no router needed for pull | Still works with router set |
| 6 | Opt-out | YIELDOMEGA_DEPLOY_KUMBAYA=0 bash scripts/e2e-anvil.sh |
DOUB E2E passes; ETH test skipped with clear message |
| 7 | Stack script | YIELDOMEGA_DEPLOY_KUMBAYA=1 bash scripts/start-local-anvil-stack.sh |
Registry + .env.local merged |
| 8 | Verify script | bash scripts/verify-time-arena-buy-router-anvil.sh |
All checklist steps green |
Automated: forge test --match-contract TimeArenaBuyRouter; frontend build unchanged; optional indexer cargo test if registry decode extended.
Test plan — attack / abuse vectors
| Vector | Test | Expected mitigation |
|---|---|---|
| Wrong router wired | Deploy script points setTimeArenaBuyRouter at non-router contract |
Script verifies buyViaKumbaya selector or post-deploy smoke call |
| Router not owner-gated | Only deployer/owner can call setTimeArenaBuyRouter on prod paths |
Anvil uses deployer key only; document prod runbook separately |
Malicious setTimeArenaBuyRouter on Anvil |
User cannot call setTimeArenaBuyRouter without owner |
cast send from random key reverts |
| Under-liquidity pool | exactOutput with empty reserves |
Deploy script seeds min reserves; test reverts with slippage/insufficient |
| Stable fee-on-transfer | Mock deflationary stable (if added) | StableIngressParity revert on router |
| Env/router mismatch | Frontend env ≠ onchain | #264 (closed) fail-closed in UI; deploy sets both consistently |
| Mainnet mistaken deploy | Run fixture script against 4326 | DevOnlyChainGuard reverts |
Verification criteria
-
forge testgreen for newTimeArenaBuyRoutersuite. -
bash scripts/e2e-anvil.shgreen including ETH wallet-write spec. -
cast callspot-check: router address,paused()==false, arena live. - Grep:
DeployKumbayaAnvilFixturesreferences resolve to an existing.s.solfile. - Manual:
docs/testing/manual-qa-checklists.mdArena ETH/USDM rows (if present) pass on Anvil. - Production deploy explicitly out of scope — separate mainnet registry issue.