Arena buys API: expose actual_seconds_added + verify-wallet-profile-anvil.sh (#258 QA) #282

Closed
opened 2026-06-02 14:50:46 +00:00 by PlasticDigits · 15 comments
PlasticDigits commented 2026-06-02 14:50:46 +00:00 (Migrated from gitlab.com)

Context / parent

Follow-up from #258 (wallet profile modal) QA: the /arena “Just +Xs by …” timer-extension chip (data-testid="arena-simple-last-extension") never renders because GET /v1/arena/buys omits fields the DB already stores. A hermetic Anvil verify script is also missing (peer scripts exist for cred buy, podium live, donate pools, etc.).


Current codebase

Indexer — persisted but not exposed

Frontend — expects field, mapper strips it

Verify scripts — pattern, no wallet-profile helper


Why this is needed

  1. Product UX: The play surface /arena should show the live “Just +Ns by …” chip when a buy extends the Last Buy timer — currently impossible with indexer JSON as shipped.
  2. QA / agents: #258 manual checklist item (“click buyer on timer extension chip → profile modal”) cannot be signed off without populated buy rows in the API response.
  3. Contract drift: Frontend types and tests assume an extended buy row; the indexer HTTP layer is the missing link.
  4. CI hygiene: A single bash scripts/verify-wallet-profile-anvil.sh gives agents and CI a repeatable hook for wallet-stats + buy-row + (optional) profile-modal prerequisites without ad-hoc cast/curl.

Constraints & guardrails

  • Onchain authority: actual_seconds_added in logs/DB must match chain semantics; indexer only projects decoded values — do not recompute caps client-side for API truth.
  • AGPL on new scripts/docs; SPDX header on scripts/verify-wallet-profile-anvil.sh.
  • No legacy /v1/timecurve/* — arena routes only (#266).
  • Indexer ingest: One SQL transaction per block (#140); verify script must not break that invariant.
  • Public API errors: DB failures stay generic on HTTP 500 (#157).
  • Verify script isolation: Prefer dedicated ANVIL_PORT / INDEXER_PORT (see verify-podium-live-anvil.sh) so local dev stacks on 8545/3100 are not killed unless documented; gitleaks-safe (unlocked Anvil accounts, no committed keys).
  • Scope boundary (this issue): Expose actual_seconds_added (and wire frontend mapper/types). Optional follow-up (separate issue if needed): expose new_deadline, buy_index, block_timestamp, log_index on the same endpoint for full BuyItem parity — do not block #258 QA on full parity unless cheap.

A. Indexer — extend GET /v1/arena/buys

  1. Add actual_seconds_added::text to the SQL SELECT in arena_buys.
  2. Emit JSON field actual_seconds_added as a decimal string (same pattern as charm_wad).
  3. Extend integration_stage2 (or dedicated test) with a persisted buy fixture asserting the field is present and matches ingested value.
  4. Document field on docs/indexer/design.md arena HTTP section; add invariant row INV-INDEXER-*-ARENA-BUYS-SECONDS (or extend INV-INDEXER-254-ARENA-SCHEMA) in docs/testing/invariants-and-business-logic.md.

B. Frontend — pass-through

  1. Add actual_seconds_added?: string to ArenaBuyItem.
  2. Map it in mapArenaBuyToBuyItem (and any direct fetchArenaBuys consumers).
  3. Optional: extend indexerApi.test.ts with fixture JSON.

C. scripts/verify-wallet-profile-anvil.sh

Mirror existing verify scripts:

  1. Start Anvil (+ Postgres + indexer) or document reuse of running stack via env.
  2. YIELDOMEGA_DEPLOY_NO_COOLDOWN=1 + yieldomega_anvil_deploy_dev.
  3. Mint DOUB, approve, buy(uint256) on TimeArena.
  4. Wait for indexer; curl GET /v1/arena/buys?limit=1 → actual_seconds_added present and > 0 (or timer_hard_reset true).
  5. curl GET /v1/arena/wallet/{buyer}/stats → HTTP 200, buy_count >= 1.
  6. Optional: topUpPodiumPools + assert donate-pools recent donor (unblocks #258 donate-row profile click QA).
  7. Print manual step: open /arena / /arena/protocol with VITE_INDEXER_URL and click profile buttons (Playwright not required in-script unless added later).
  8. Cross-link in INV-FRONTEND-258-WALLET-PROFILE, arena-views § wallet-profile, .cursor/skills/yieldomega-guardrails/SKILL.md, docs/testing/ci.md if CI job added.

Relevant files

Area Files
Indexer API indexer/src/api_arena.rs
Indexer tests indexer/tests/integration_stage2.rs
Frontend API/types frontend/src/lib/indexerApi.ts
Play UI frontend/src/pages/arena/ArenaSimplePage.tsx
New script scripts/verify-wallet-profile-anvil.sh (create)
Docs docs/indexer/design.md, docs/testing/invariants-and-business-logic.md, docs/testing/ci.md
Peer scripts scripts/verify-cred-buy-anvil.sh, scripts/lib/anvil_deploy_dev.sh

Acceptance criteria

  • GET /v1/arena/buys JSON items include actual_seconds_added (string integer) for ingested DOUB/CRED buys.
  • After a qualifying buy on Anvil, actual_seconds_added in API matches the value stored in idx_arena_buy for that tx_hash/log_index.
  • fetchArenaBuysAsBuyItems surfaces actual_seconds_added on each BuyItem.
  • With local stack + sale active, /arena renders arena-simple-last-extension when the latest buy has actual_seconds_added > 0 or timer_hard_reset.
  • bash scripts/verify-wallet-profile-anvil.sh exits 0 on a clean host (documents PATH for Foundry + Cargo ≥ 1.85).
  • Verify script asserts GET /v1/arena/wallet/{buyer}/stats returns buy_count >= 1 after the buy.
  • Invariants/docs updated with verify command + new indexer invariant (if added).

Test plan — functional paths

Path Steps Expected
Happy — DOUB buy extends timer DeployDev → buy → poll /v1/arena/buys actual_seconds_added > 0
Hard reset buy Buy large enough to trigger timer_hard_reset (if feasible on Anvil config) timer_hard_reset: true; chip uses reset styling
CRED buy buyWithCred Field present; seconds may be 0 — chip may not show (documented)
Empty table Fresh DB before any buy items: [] — no error
Pagination limit/offset Field present on each row returned
Frontend chip Dev server + stack; one extending buy Chip visible; click opens WalletProfileModal (#258)
Verify script Run script twice on clean ports Idempotent pass; no orphan Anvil on success exit

Automated:

  • cd indexer && cargo test (new/extended integration assertion)
  • cd frontend && npm test -- indexerApi (if added)
  • bash scripts/verify-wallet-profile-anvil.sh

Test plan — attack / abuse / misuse

Vector Risk Mitigation / test
Oversized numeric strings JSON bloat / client BigInt throw API returns DB text as-is; frontend already uses BigInt in chip path — test with max realistic seconds (< timer cap)
Negative or non-numeric actual_seconds_added in DB Broken UI DB NOT NULL from decode; add test that API does not invent values; corrupted row should fail ingest or be impossible from decoder
Address in /v1/arena/wallet/{address}/stats Injection / path traversal Reuse existing address validation (42-char 0x); verify script uses lowercase checksummed buyer only
Indexer spoofing Fake “extension” chip Chip is derived-only; document that wallet modal stats are non-authoritative per architecture
Rate limit / DoS on /v1/arena/buys Large limit Existing limit clamp 1–200; verify script uses small limit
Verify script port hijack Wrong chain data Script uses dedicated ports or explicit RPC_URL/INDEXER_PORT env; fail if chainId ≠ 31337
Malicious jq in script N/A Script only parses known JSON paths; no eval

Verification criteria (sign-off)

  1. curl -s http://127.0.0.1:<port>/v1/arena/buys?limit=1 | jq '.items[0].actual_seconds_added' — non-null after buy.
  2. psql spot-check: SELECT actual_seconds_added FROM idx_arena_buy ORDER BY block_number DESC LIMIT 1 matches API.
  3. Browser: /arena shows timer-extension chip; profile button on chip opens modal with seven sections.
  4. bash scripts/verify-wallet-profile-anvil.sh — exit 0, logs include buyer address and stats buy_count.
  5. cargo test + npm run typecheck in touched packages green.

  • #258 — wallet profile modal (QA blocked on chip + verify script)
  • #255 — wallet stats API
  • #254 — arena schema / HTTP
## Context / parent Follow-up from [**#258**](https://gitlab.com/PlasticDigits/yieldomega/-/issues/258) (wallet profile modal) QA: the `/arena` **“Just +Xs by …”** timer-extension chip (`data-testid="arena-simple-last-extension"`) never renders because **`GET /v1/arena/buys`** omits fields the DB already stores. A hermetic Anvil verify script is also missing (peer scripts exist for cred buy, podium live, donate pools, etc.). --- ## Current codebase ### Indexer — persisted but not exposed - Table **`idx_arena_buy`** (migration [`indexer/migrations/20240601000000_arena_v2.up.sql`](indexer/migrations/20240601000000_arena_v2.up.sql)) stores **`actual_seconds_added`**, **`new_deadline`**, **`buy_index`**, **`block_timestamp`**, **`log_index`**, etc., populated from **`TimeArena` `Buy`** logs in [`indexer/src/decoder.rs`](indexer/src/decoder.rs) / [`indexer/src/persist.rs`](indexer/src/persist.rs). - **`GET /v1/arena/buys`** in [`indexer/src/api_arena.rs`](indexer/src/api_arena.rs) (`arena_buys`) **SELECTs only** `buyer`, `charm_wad`, `doub_paid`, `block_number`, `tx_hash`, `timer_hard_reset`, `paid_with_cred` — **`actual_seconds_added` is dropped** at the HTTP boundary. - Wallet stats aggregation **already reads** `actual_seconds_added` from SQL ([`indexer/src/arena_wallet_stats.rs`](indexer/src/arena_wallet_stats.rs)). - Integration smoke only asserts HTTP 200 for `/v1/arena/buys?limit=2` ([`indexer/tests/integration_stage2.rs`](indexer/tests/integration_stage2.rs)); no JSON field contract test. ### Frontend — expects field, mapper strips it - [`frontend/src/lib/indexerApi.ts`](frontend/src/lib/indexerApi.ts): **`BuyItem.actual_seconds_added`** is optional; **`ArenaBuyItem`** type has no such field. - **`fetchArenaBuysAsBuyItems`** → **`mapArenaBuyToBuyItem`** zeroes `new_deadline`, `buy_index`, and **never maps `actual_seconds_added`** even if the API were fixed. - **Consumers** (all gate UI on `actual_seconds_added` / `timer_hard_reset`): - [`frontend/src/pages/arena/ArenaSimplePage.tsx`](frontend/src/pages/arena/ArenaSimplePage.tsx) — `lastExtension` chip (requires `saleActive` + `secs > 0` or hard reset) - [`frontend/src/pages/arena/ArenaLiveBuysActivitySection.tsx`](frontend/src/pages/arena/ArenaLiveBuysActivitySection.tsx) — row tone - [`frontend/src/pages/arena/TimerHeroLiveBuys.tsx`](frontend/src/pages/arena/TimerHeroLiveBuys.tsx), [`frontend/src/lib/timeArenaUx.ts`](frontend/src/lib/timeArenaUx.ts), [`frontend/src/lib/timeArenaBuyPreview.ts`](frontend/src/lib/timeArenaBuyPreview.ts) - Unit tests already use mock buys **with** `actual_seconds_added: "120"` ([`LiveBuyRow.test.tsx`](frontend/src/pages/arena/LiveBuyRow.test.tsx), [`ArenaLiveBuysActivitySection.test.tsx`](frontend/src/pages/arena/ArenaLiveBuysActivitySection.test.tsx)). ### Verify scripts — pattern, no wallet-profile helper - Hermetic Anvil scripts: [`scripts/verify-cred-buy-anvil.sh`](scripts/verify-cred-buy-anvil.sh), [`scripts/verify-podium-live-anvil.sh`](scripts/verify-podium-live-anvil.sh), [`scripts/verify-donate-pools-anvil.sh`](scripts/verify-donate-pools-anvil.sh), etc. - Each: isolated Anvil (+ optional Postgres/indexer), **`yieldomega_anvil_deploy_dev`**, onchain action, **`curl`/`jq` assertions**, exit non-zero on failure. - **No** `scripts/verify-wallet-profile-anvil.sh` today; **`INV-FRONTEND-258-WALLET-PROFILE`** points only at frontend unit tests ([`docs/testing/invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md)). --- ## Why this is needed 1. **Product UX:** The play surface `/arena` should show the live “Just +Ns by …” chip when a buy extends the Last Buy timer — currently impossible with indexer JSON as shipped. 2. **QA / agents:** [#258](https://gitlab.com/PlasticDigits/yieldomega/-/issues/258) manual checklist item (“click buyer on timer extension chip → profile modal”) cannot be signed off without populated buy rows in the API response. 3. **Contract drift:** Frontend types and tests assume an extended buy row; the indexer HTTP layer is the missing link. 4. **CI hygiene:** A single `bash scripts/verify-wallet-profile-anvil.sh` gives agents and CI a repeatable hook for wallet-stats + buy-row + (optional) profile-modal prerequisites without ad-hoc `cast`/`curl`. --- ## Constraints & guardrails - **Onchain authority:** `actual_seconds_added` in logs/DB must match chain semantics; indexer only **projects** decoded values — do not recompute caps client-side for API truth. - **AGPL** on new scripts/docs; SPDX header on `scripts/verify-wallet-profile-anvil.sh`. - **No legacy `/v1/timecurve/*`** — arena routes only ([#266](https://gitlab.com/PlasticDigits/yieldomega/-/issues/266)). - **Indexer ingest:** One SQL transaction per block ([#140](https://gitlab.com/PlasticDigits/yieldomega/-/issues/140)); verify script must not break that invariant. - **Public API errors:** DB failures stay generic on HTTP 500 ([#157](https://gitlab.com/PlasticDigits/yieldomega/-/issues/157)). - **Verify script isolation:** Prefer dedicated **`ANVIL_PORT` / `INDEXER_PORT`** (see `verify-podium-live-anvil.sh`) so local dev stacks on 8545/3100 are not killed unless documented; gitleaks-safe (unlocked Anvil accounts, no committed keys). - **Scope boundary (this issue):** Expose **`actual_seconds_added`** (and wire frontend mapper/types). **Optional follow-up** (separate issue if needed): expose `new_deadline`, `buy_index`, `block_timestamp`, `log_index` on the same endpoint for full `BuyItem` parity — do not block #258 QA on full parity unless cheap. --- ## Recommended solution direction ### A. Indexer — extend `GET /v1/arena/buys` 1. Add **`actual_seconds_added::text`** to the SQL SELECT in `arena_buys`. 2. Emit JSON field **`actual_seconds_added`** as a decimal string (same pattern as `charm_wad`). 3. Extend **`integration_stage2`** (or dedicated test) with a persisted buy fixture asserting the field is present and matches ingested value. 4. Document field on [`docs/indexer/design.md`](docs/indexer/design.md) arena HTTP section; add invariant row **`INV-INDEXER-*-ARENA-BUYS-SECONDS`** (or extend **`INV-INDEXER-254-ARENA-SCHEMA`**) in [`docs/testing/invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md). ### B. Frontend — pass-through 1. Add **`actual_seconds_added?: string`** to **`ArenaBuyItem`**. 2. Map it in **`mapArenaBuyToBuyItem`** (and any direct `fetchArenaBuys` consumers). 3. Optional: extend **`indexerApi.test.ts`** with fixture JSON. ### C. `scripts/verify-wallet-profile-anvil.sh` Mirror existing verify scripts: 1. Start Anvil (+ Postgres + indexer) or document reuse of running stack via env. 2. `YIELDOMEGA_DEPLOY_NO_COOLDOWN=1` + **`yieldomega_anvil_deploy_dev`**. 3. Mint DOUB, approve, **`buy(uint256)`** on `TimeArena`. 4. Wait for indexer; **`curl`** `GET /v1/arena/buys?limit=1` → **`actual_seconds_added`** present and `> 0` (or `timer_hard_reset` true). 5. **`curl`** `GET /v1/arena/wallet/{buyer}/stats` → HTTP 200, `buy_count >= 1`. 6. Optional: **`topUpPodiumPools`** + assert donate-pools recent donor (unblocks [#258](https://gitlab.com/PlasticDigits/yieldomega/-/issues/258) donate-row profile click QA). 7. Print manual step: open `/arena` / `/arena/protocol` with `VITE_INDEXER_URL` and click profile buttons (Playwright not required in-script unless added later). 8. Cross-link in **`INV-FRONTEND-258-WALLET-PROFILE`**, [arena-views § wallet-profile](docs/frontend/arena-views.md#wallet-profile-modal-gitlab-258), [`.cursor/skills/yieldomega-guardrails/SKILL.md`](.cursor/skills/yieldomega-guardrails/SKILL.md), [`docs/testing/ci.md`](docs/testing/ci.md) if CI job added. --- ## Relevant files | Area | Files | |------|--------| | Indexer API | [`indexer/src/api_arena.rs`](indexer/src/api_arena.rs) | | Indexer tests | [`indexer/tests/integration_stage2.rs`](indexer/tests/integration_stage2.rs) | | Frontend API/types | [`frontend/src/lib/indexerApi.ts`](frontend/src/lib/indexerApi.ts) | | Play UI | [`frontend/src/pages/arena/ArenaSimplePage.tsx`](frontend/src/pages/arena/ArenaSimplePage.tsx) | | New script | `scripts/verify-wallet-profile-anvil.sh` (create) | | Docs | [`docs/indexer/design.md`](docs/indexer/design.md), [`docs/testing/invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md), [`docs/testing/ci.md`](docs/testing/ci.md) | | Peer scripts | [`scripts/verify-cred-buy-anvil.sh`](scripts/verify-cred-buy-anvil.sh), [`scripts/lib/anvil_deploy_dev.sh`](scripts/lib/anvil_deploy_dev.sh) | --- ## Acceptance criteria - [ ] **`GET /v1/arena/buys`** JSON items include **`actual_seconds_added`** (string integer) for ingested DOUB/CRED buys. - [ ] After a qualifying buy on Anvil, **`actual_seconds_added`** in API matches the value stored in **`idx_arena_buy`** for that `tx_hash`/`log_index`. - [ ] **`fetchArenaBuysAsBuyItems`** surfaces **`actual_seconds_added`** on each **`BuyItem`**. - [ ] With local stack + sale active, **`/arena`** renders **`arena-simple-last-extension`** when the latest buy has `actual_seconds_added > 0` or `timer_hard_reset`. - [ ] **`bash scripts/verify-wallet-profile-anvil.sh`** exits 0 on a clean host (documents `PATH` for Foundry + Cargo ≥ 1.85). - [ ] Verify script asserts **`GET /v1/arena/wallet/{buyer}/stats`** returns `buy_count >= 1` after the buy. - [ ] Invariants/docs updated with verify command + new indexer invariant (if added). --- ## Test plan — functional paths | Path | Steps | Expected | |------|--------|----------| | Happy — DOUB buy extends timer | DeployDev → buy → poll `/v1/arena/buys` | `actual_seconds_added` > 0 | | Hard reset buy | Buy large enough to trigger `timer_hard_reset` (if feasible on Anvil config) | `timer_hard_reset: true`; chip uses reset styling | | CRED buy | `buyWithCred` | Field present; seconds may be 0 — chip may not show (documented) | | Empty table | Fresh DB before any buy | `items: []` — no error | | Pagination | `limit`/`offset` | Field present on each row returned | | Frontend chip | Dev server + stack; one extending buy | Chip visible; click opens **`WalletProfileModal`** ([#258](https://gitlab.com/PlasticDigits/yieldomega/-/issues/258)) | | Verify script | Run script twice on clean ports | Idempotent pass; no orphan Anvil on success exit | **Automated:** - `cd indexer && cargo test` (new/extended integration assertion) - `cd frontend && npm test -- indexerApi` (if added) - `bash scripts/verify-wallet-profile-anvil.sh` --- ## Test plan — attack / abuse / misuse | Vector | Risk | Mitigation / test | |--------|------|-------------------| | **Oversized numeric strings** | JSON bloat / client `BigInt` throw | API returns DB text as-is; frontend already uses `BigInt` in chip path — test with max realistic seconds (< timer cap) | | **Negative or non-numeric `actual_seconds_added` in DB** | Broken UI | DB NOT NULL from decode; add test that API does not invent values; corrupted row should fail ingest or be impossible from decoder | | **Address in `/v1/arena/wallet/{address}/stats`** | Injection / path traversal | Reuse existing address validation (42-char `0x`); verify script uses lowercase checksummed buyer only | | **Indexer spoofing** | Fake “extension” chip | Chip is derived-only; document that wallet modal stats are non-authoritative per architecture | | **Rate limit / DoS on `/v1/arena/buys`** | Large `limit` | Existing `limit` clamp 1–200; verify script uses small limit | | **Verify script port hijack** | Wrong chain data | Script uses dedicated ports or explicit `RPC_URL`/`INDEXER_PORT` env; fail if `chainId` ≠ 31337 | | **Malicious `jq` in script** | N/A | Script only parses known JSON paths; no `eval` | --- ## Verification criteria (sign-off) 1. `curl -s http://127.0.0.1:<port>/v1/arena/buys?limit=1 | jq '.items[0].actual_seconds_added'` — non-null after buy. 2. `psql` spot-check: `SELECT actual_seconds_added FROM idx_arena_buy ORDER BY block_number DESC LIMIT 1` matches API. 3. Browser: `/arena` shows timer-extension chip; profile button on chip opens modal with seven sections. 4. `bash scripts/verify-wallet-profile-anvil.sh` — exit 0, logs include buyer address and stats `buy_count`. 5. `cargo test` + `npm run typecheck` in touched packages green. --- ## Related issues - [#258](https://gitlab.com/PlasticDigits/yieldomega/-/issues/258) — wallet profile modal (QA blocked on chip + verify script) - [#255](https://gitlab.com/PlasticDigits/yieldomega/-/issues/255) — wallet stats API - [#254](https://gitlab.com/PlasticDigits/yieldomega/-/issues/254) — arena schema / HTTP
PlasticDigits commented 2026-06-02 14:50:47 +00:00 (Migrated from gitlab.com)

marked as related to #258

marked as related to #258
ghost1 commented 2026-06-03 11:33:07 +00:00 (Migrated from gitlab.com)

mentioned in commit b39b47dbac116e49949ee0e17b96823471c491e3

mentioned in commit b39b47dbac116e49949ee0e17b96823471c491e3
ghost1 commented 2026-06-03 11:33:07 +00:00 (Migrated from gitlab.com)

mentioned in commit e0d9b4c04d2438ec3801e0e48526e7ad8079d976

mentioned in commit e0d9b4c04d2438ec3801e0e48526e7ad8079d976
PlasticDigits commented 2026-06-03 11:33:27 +00:00 (Migrated from gitlab.com)

test

test
PlasticDigits commented 2026-06-03 11:33:37 +00:00 (Migrated from gitlab.com)

Implementation complete (branch cursor/arena-buys-seconds-282-f1de)

main is protected in this project, so changes are on cursor/arena-buys-seconds-282-f1de (merge request pending). Local agent main includes the merge commit for continuity.

What changed

  1. Indexer — GET /v1/arena/buys now returns actual_seconds_added (decimal string from idx_arena_buy, same pattern as charm_wad).
  2. Frontend — ArenaBuyItem + mapArenaBuyToBuyItem pass actual_seconds_added through to BuyItem so /arena can render arena-simple-last-extension and live-buy tone styling.
  3. Hermetic verify — bash scripts/verify-wallet-profile-anvil.sh (defaults: Anvil 8548, indexer 3103, Postgres 5433): DeployDev → DOUB buy → API/DB seconds parity → GET /v1/arena/wallet/{buyer}/stats (buy_count >= 1) → optional topUpPodiumPools donor row.
  4. Docs / invariants — INV-INDEXER-282-ARENA-BUYS-SECONDS; updated INV-FRONTEND-258-WALLET-PROFILE; cross-links in docs/indexer/design.md, docs/frontend/arena-views.md, AGENTS.md, docs/testing/ci.md, guardrails skill, skills/play-time-arena-doub/SKILL.md.

Automated verification (agent run)

  • cd indexer && cargo clippy --all-targets -- -D warnings
  • YIELDOMEGA_PG_TEST_URL=postgres://yieldomega:password@127.0.0.1:5433/yieldomega_indexer_test cargo test --test integration_stage2
  • cd frontend && npm test -- indexerApi && npm run typecheck
  • bash scripts/verify-wallet-profile-anvil.sh → actual_seconds_added=120, buy_count=1

QA checklist (please verify after merge)

  • Merge MR for cursor/arena-buys-seconds-282-f1de → main
  • bash scripts/verify-wallet-profile-anvil.sh on a clean host (Foundry on PATH, Cargo ≥ 1.85, Postgres on :5433)
  • After a DOUB buy on local stack: curl -s http://127.0.0.1:<indexer>/v1/arena/buys?limit=1 | jq '.items[0].actual_seconds_added' — non-null, matches psql on idx_arena_buy
  • /arena with sale active shows arena-simple-last-extension when latest buy has actual_seconds_added > 0 or timer_hard_reset
  • Click buyer on extension chip / live-buy row → WalletProfileModal with all seven sections (#258)

Follow-up (optional, out of scope)

Expose remaining BuyItem fields on /v1/arena/buys (new_deadline, buy_index, block_timestamp, log_index) if full parity is needed — not required for #258 QA.


Requesting verification from the QA agent team when the MR is merged.

## Implementation complete (branch `cursor/arena-buys-seconds-282-f1de`) `main` is protected in this project, so changes are on **`cursor/arena-buys-seconds-282-f1de`** (merge request pending). Local agent `main` includes the merge commit for continuity. ### What changed 1. **Indexer** — `GET /v1/arena/buys` now returns **`actual_seconds_added`** (decimal string from `idx_arena_buy`, same pattern as `charm_wad`). 2. **Frontend** — `ArenaBuyItem` + `mapArenaBuyToBuyItem` pass **`actual_seconds_added`** through to **`BuyItem`** so `/arena` can render **`arena-simple-last-extension`** and live-buy tone styling. 3. **Hermetic verify** — `bash scripts/verify-wallet-profile-anvil.sh` (defaults: Anvil **8548**, indexer **3103**, Postgres **5433**): DeployDev → DOUB buy → API/DB seconds parity → `GET /v1/arena/wallet/{buyer}/stats` (`buy_count >= 1`) → optional `topUpPodiumPools` donor row. 4. **Docs / invariants** — **`INV-INDEXER-282-ARENA-BUYS-SECONDS`**; updated **`INV-FRONTEND-258-WALLET-PROFILE`**; cross-links in `docs/indexer/design.md`, `docs/frontend/arena-views.md`, `AGENTS.md`, `docs/testing/ci.md`, guardrails skill, `skills/play-time-arena-doub/SKILL.md`. ### Automated verification (agent run) - [x] `cd indexer && cargo clippy --all-targets -- -D warnings` - [x] `YIELDOMEGA_PG_TEST_URL=postgres://yieldomega:password@127.0.0.1:5433/yieldomega_indexer_test cargo test --test integration_stage2` - [x] `cd frontend && npm test -- indexerApi && npm run typecheck` - [x] `bash scripts/verify-wallet-profile-anvil.sh` → `actual_seconds_added=120`, `buy_count=1` ### QA checklist (please verify after merge) - [ ] Merge MR for `cursor/arena-buys-seconds-282-f1de` → `main` - [ ] `bash scripts/verify-wallet-profile-anvil.sh` on a clean host (Foundry on `PATH`, Cargo ≥ 1.85, Postgres on `:5433`) - [ ] After a DOUB buy on local stack: `curl -s http://127.0.0.1:<indexer>/v1/arena/buys?limit=1 | jq '.items[0].actual_seconds_added'` — non-null, matches `psql` on `idx_arena_buy` - [ ] `/arena` with sale active shows **`arena-simple-last-extension`** when latest buy has `actual_seconds_added > 0` or `timer_hard_reset` - [ ] Click buyer on extension chip / live-buy row → **WalletProfileModal** with all seven sections (#258) ### Follow-up (optional, out of scope) Expose remaining `BuyItem` fields on `/v1/arena/buys` (`new_deadline`, `buy_index`, `block_timestamp`, `log_index`) if full parity is needed — not required for #258 QA. --- Requesting verification from the **QA agent team** when the MR is merged.
PlasticDigits commented 2026-06-03 11:34:04 +00:00 (Migrated from gitlab.com)

mentioned in merge request !406

mentioned in merge request !406
PlasticDigits commented 2026-06-03 11:34:07 +00:00 (Migrated from gitlab.com)

Merge request: https://gitlab.com/PlasticDigits/yieldomega/-/merge_requests/36 (!406) — source cursor/arena-buys-seconds-282-f1de → main.

Merge request: https://gitlab.com/PlasticDigits/yieldomega/-/merge_requests/36 (!406) — source `cursor/arena-buys-seconds-282-f1de` → `main`.
PlasticDigits commented 2026-06-03 11:40:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #283

mentioned in issue #283
PlasticDigits commented 2026-06-03 11:40:13 +00:00 (Migrated from gitlab.com)

marked as related to #283

marked as related to #283
ghost1 commented 2026-06-03 12:05:49 +00:00 (Migrated from gitlab.com)

mentioned in commit 1b207c5ac8

mentioned in commit 1b207c5ac8495ceb8b9430dfd357042a731c780b
PlasticDigits commented 2026-06-03 12:18:06 +00:00 (Migrated from gitlab.com)

mentioned in commit 32ed77a179

mentioned in commit 32ed77a179ee556d7bbec945cc45976e39a64c7b
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-03 12:18:12 +00:00
PlasticDigits commented 2026-06-03 12:28:24 +00:00 (Migrated from gitlab.com)

mentioned in merge request !410

mentioned in merge request !410
PlasticDigits commented 2026-06-03 13:37:20 +00:00 (Migrated from gitlab.com)

mentioned in merge request !414

mentioned in merge request !414
PlasticDigits commented 2026-06-03 13:37:29 +00:00 (Migrated from gitlab.com)

mentioned in issue #258

mentioned in issue #258
PlasticDigits commented 2026-07-07 02:38:41 +00:00 (Migrated from gitlab.com)

mentioned in issue #364

mentioned in issue #364
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#282
No description provided.