Extend arena activity API with state transition events #345

Closed
opened 2026-06-25 20:07:15 +00:00 by PlasticDigits · 5 comments
PlasticDigits commented 2026-06-25 20:07:15 +00:00 (Migrated from gitlab.com)

Parent

Transition Gap Analysis (#342) — P0 #3. Bundles H-I2.

Current codebase

  • GET /v1/arena/activity (api_arena.rs, ACTIVITY_UNION_SQL) unions only buy, steal, guard, revenge from indexed tables.
  • Indexer already decodes and persists LevelUp, CredClaimed, PodiumEpochRolled, FeatureUnlocked, LastBuyEpochStarted in decoder.rs / persist.rs — but they are not exposed on the activity feed.
  • Frontend celebration/toast stacks are buy-centric; level-up uses wallet stats polling.

Why needed

Frontend cannot indexer-first drive celebration/toast stacks for non-buy transitions (level-up, CRED claim, podium roll, epoch start, feature unlock) without a unified feed.

Constraints / guardrails

  • Paginate with existing BlockLogCursor pattern.
  • Stable kind discriminator per event type.
  • AGPL; document new kinds in API docs / invariants.
  • Prefer extending /v1/arena/activity over a parallel endpoint unless union SQL becomes unmaintainable.

Relevant files

  • indexer/src/api_arena.rs (ACTIVITY_UNION_SQL, arena_activity)
  • indexer/src/persist.rs
  • indexer/src/decoder.rs
  • DB tables: idx_arena_* for level_up, cred_claimed, podium_epoch_rolled, etc.
  • frontend/src/lib/indexerApi.ts
  1. Extend ACTIVITY_UNION_SQL with UNION ALL branches for:
    • level_up (idx_arena_level_up or equivalent)
    • cred_claim (idx_arena_cred_claimed)
    • podium_epoch (idx_arena_podium_epoch_rolled)
    • epoch_started (idx_arena_last_buy_epoch_started)
    • feature_unlocked (idx_arena_feature_unlocked)
  2. Normalize column shape (actor, target, amounts, block metadata) per kind.
  3. Add integration test asserting persisted events appear in activity query.
  4. Optional follow-up: frontend consumers for toast/celebration (may be separate MR).

Acceptance criteria

  • Activity feed returns all five new kinds when present in DB.
  • Cursor pagination works across mixed kinds.
  • OpenAPI/docs or invariants entry for new kind values.
  • No SQL injection — use existing parameterized pattern.

Test plan

Path Command
Indexer cd indexer && cargo clippy --all-targets -- -D warnings && cargo test
Integration integration_stage2.rs — seed events, query /v1/arena/activity
Smoke bash scripts/verify-wallet-profile-anvil.sh (level history baseline)

Attack / abuse test plan

  • Activity feed must not leak private keys or non-public fields.
  • Pagination cursors must not skip or duplicate rows at kind boundaries.

Verification criteria

  • curl /v1/arena/activity after scripted level-up / roll / claim shows corresponding kinds.
  • Integration test PASS.
## Parent [Transition Gap Analysis (#342)](https://gitlab.com/PlasticDigits/yieldomega/-/issues/342) — P0 #3. Bundles **H-I2**. ## Current codebase - `GET /v1/arena/activity` (`api_arena.rs`, `ACTIVITY_UNION_SQL`) unions only `buy`, `steal`, `guard`, `revenge` from indexed tables. - Indexer already decodes and persists `LevelUp`, `CredClaimed`, `PodiumEpochRolled`, `FeatureUnlocked`, `LastBuyEpochStarted` in `decoder.rs` / `persist.rs` — but they are not exposed on the activity feed. - Frontend celebration/toast stacks are buy-centric; level-up uses wallet stats polling. ## Why needed Frontend cannot indexer-first drive celebration/toast stacks for non-buy transitions (level-up, CRED claim, podium roll, epoch start, feature unlock) without a unified feed. ## Constraints / guardrails - Paginate with existing `BlockLogCursor` pattern. - Stable `kind` discriminator per event type. - AGPL; document new kinds in API docs / invariants. - Prefer extending `/v1/arena/activity` over a parallel endpoint unless union SQL becomes unmaintainable. ## Relevant files - `indexer/src/api_arena.rs` (`ACTIVITY_UNION_SQL`, `arena_activity`) - `indexer/src/persist.rs` - `indexer/src/decoder.rs` - DB tables: `idx_arena_*` for level_up, cred_claimed, podium_epoch_rolled, etc. - `frontend/src/lib/indexerApi.ts` ## Recommended direction 1. Extend `ACTIVITY_UNION_SQL` with `UNION ALL` branches for: - `level_up` (`idx_arena_level_up` or equivalent) - `cred_claim` (`idx_arena_cred_claimed`) - `podium_epoch` (`idx_arena_podium_epoch_rolled`) - `epoch_started` (`idx_arena_last_buy_epoch_started`) - `feature_unlocked` (`idx_arena_feature_unlocked`) 2. Normalize column shape (actor, target, amounts, block metadata) per kind. 3. Add integration test asserting persisted events appear in activity query. 4. Optional follow-up: frontend consumers for toast/celebration (may be separate MR). ## Acceptance criteria - [ ] Activity feed returns all five new kinds when present in DB. - [ ] Cursor pagination works across mixed kinds. - [ ] OpenAPI/docs or invariants entry for new `kind` values. - [ ] No SQL injection — use existing parameterized pattern. ## Test plan | Path | Command | |------|---------| | Indexer | `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` | | Integration | `integration_stage2.rs` — seed events, query `/v1/arena/activity` | | Smoke | `bash scripts/verify-wallet-profile-anvil.sh` (level history baseline) | ## Attack / abuse test plan - Activity feed must not leak private keys or non-public fields. - Pagination cursors must not skip or duplicate rows at kind boundaries. ## Verification criteria - `curl /v1/arena/activity` after scripted level-up / roll / claim shows corresponding kinds. - Integration test PASS.
PlasticDigits commented 2026-06-25 20:07:48 +00:00 (Migrated from gitlab.com)

mentioned in issue #342

mentioned in issue #342
PlasticDigits commented 2026-06-25 20:49:13 +00:00 (Migrated from gitlab.com)

mentioned in merge request !516

mentioned in merge request !516
PlasticDigits commented 2026-06-25 21:53:18 +00:00 (Migrated from gitlab.com)

mentioned in commit 5507efc596

mentioned in commit 5507efc5967cd8d18ed0c1be645f24c0087e9b92
PlasticDigits commented 2026-06-26 06:52:42 +00:00 (Migrated from gitlab.com)

Verification — #345 Extend arena activity API with state transition events

Result: PASS (no repo changes; implementation already on main)

Acceptance criteria

Criterion Result How verified
Activity feed returns all five new kinds when present in DB PASS integration_stage2.rs::api_arena_activity_smoke asserts level_up, cred_claim, podium_epoch, epoch_started, feature_unlocked (plus buy/steal/guard/revenge) and per-kind field mapping (bp_delta, amount_doub_wad, target, etc.)
Cursor pagination works across mixed kinds PASS integration_stage2.rs::api_activity_cursor_smoke — page-1/page-2 no duplicate (tx_hash, log_index) keys; mixed kinds on first page; invalid cursor → 400
OpenAPI/docs or invariants entry for new kind values PASS INV-INDEXER-345-ACTIVITY-TRANSITIONS in docs/testing/invariants-and-business-logic.md; docs/indexer/design.md#arena-activity-http-gitlab-292 per-kind semantics; indexer/README.md § Arena activity; frontend/src/lib/indexerApi.ts ArenaActivityKind union
No SQL injection — parameterized pattern PASS api_arena.rs arena_activity: ACTIVITY_UNION_SQL is static; cursor/limit/offset bound via $1/$2/$3 (BlockLogCursor decode + sqlx::query().bind(...))

Test plan

Path Command Result
Indexer clippy cd indexer && cargo clippy --all-targets -- -D warnings PASS
Indexer unit + integration YIELDOMEGA_PG_TEST_URL=… cargo test (7 integration_stage2 tests incl. postgres_stage2_persist_all_events_and_rollback_after) PASS (82 unit + 7 integration)
Postgres bash scripts/verify-cloud-postgres.sh PASS
Anvil smoke (level history baseline) bash scripts/verify-wallet-profile-anvil.sh PASS (~8.5 min; buy ingest + wallet stats + re-ran integration_stage2)

Verification criteria (issue body)

Item Result Notes
GET /v1/arena/activity shows transition kinds after seeded events PASS Covered by api_arena_activity_smoke (HTTP router oneshot ≡ curl against running indexer)
Integration test PASS PASS See above

Attack / abuse test plan

Item Result Notes
No private keys or non-public fields in feed PASS Response fields: kind, actor, target, amounts/deltas, tx identity, block_timestamp — no key material
Pagination cursors do not skip/duplicate at kind boundaries PASS api_activity_cursor_smoke

Implementation spot-check

  • indexer/src/api_arena.rs — ACTIVITY_UNION_SQL unions nine branches (4 WarBow + buy + 5 transition tables).
  • Frontend types extended in indexerApi.ts; docs cross-linked from arena-views and frontend-content-audit.

Follow-ups

  • Frontend toast/celebration consumers for non-buy transitions remain optional (called out in issue as separate MR).
## Verification — #345 Extend arena activity API with state transition events **Result: PASS** (no repo changes; implementation already on `main`) ### Acceptance criteria | Criterion | Result | How verified | |-----------|--------|--------------| | Activity feed returns all five new kinds when present in DB | **PASS** | `integration_stage2.rs::api_arena_activity_smoke` asserts `level_up`, `cred_claim`, `podium_epoch`, `epoch_started`, `feature_unlocked` (plus buy/steal/guard/revenge) and per-kind field mapping (`bp_delta`, `amount_doub_wad`, `target`, etc.) | | Cursor pagination works across mixed kinds | **PASS** | `integration_stage2.rs::api_activity_cursor_smoke` — page-1/page-2 no duplicate `(tx_hash, log_index)` keys; mixed kinds on first page; invalid cursor → 400 | | OpenAPI/docs or invariants entry for new `kind` values | **PASS** | `INV-INDEXER-345-ACTIVITY-TRANSITIONS` in `docs/testing/invariants-and-business-logic.md`; `docs/indexer/design.md#arena-activity-http-gitlab-292` per-kind semantics; `indexer/README.md` § Arena activity; `frontend/src/lib/indexerApi.ts` `ArenaActivityKind` union | | No SQL injection — parameterized pattern | **PASS** | `api_arena.rs` `arena_activity`: `ACTIVITY_UNION_SQL` is static; cursor/limit/offset bound via `$1`/`$2`/`$3` (`BlockLogCursor` decode + `sqlx::query().bind(...)`) | ### Test plan | Path | Command | Result | |------|---------|--------| | Indexer clippy | `cd indexer && cargo clippy --all-targets -- -D warnings` | **PASS** | | Indexer unit + integration | `YIELDOMEGA_PG_TEST_URL=… cargo test` (7 `integration_stage2` tests incl. `postgres_stage2_persist_all_events_and_rollback_after`) | **PASS** (82 unit + 7 integration) | | Postgres | `bash scripts/verify-cloud-postgres.sh` | **PASS** | | Anvil smoke (level history baseline) | `bash scripts/verify-wallet-profile-anvil.sh` | **PASS** (~8.5 min; buy ingest + wallet stats + re-ran `integration_stage2`) | ### Verification criteria (issue body) | Item | Result | Notes | |------|--------|-------| | `GET /v1/arena/activity` shows transition kinds after seeded events | **PASS** | Covered by `api_arena_activity_smoke` (HTTP router oneshot ≡ curl against running indexer) | | Integration test PASS | **PASS** | See above | ### Attack / abuse test plan | Item | Result | Notes | |------|--------|-------| | No private keys or non-public fields in feed | **PASS** | Response fields: `kind`, `actor`, `target`, amounts/deltas, tx identity, `block_timestamp` — no key material | | Pagination cursors do not skip/duplicate at kind boundaries | **PASS** | `api_activity_cursor_smoke` | ### Implementation spot-check - `indexer/src/api_arena.rs` — `ACTIVITY_UNION_SQL` unions nine branches (4 WarBow + buy + 5 transition tables). - Frontend types extended in `indexerApi.ts`; docs cross-linked from arena-views and frontend-content-audit. ### Follow-ups - Frontend toast/celebration consumers for non-buy transitions remain optional (called out in issue as separate MR).
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-26 06:52:43 +00:00
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#345
No description provided.