Serve wallet level history in profile modal via indexer #336

Closed
opened 2026-06-19 04:27:45 +00:00 by PlasticDigits · 12 comments
PlasticDigits commented 2026-06-19 04:27:45 +00:00 (Migrated from gitlab.com)

Parent

Gap analysis follow-up from #331.

Current codebase

  • Indexer persists LevelUp and FeatureUnlocked events:
    • idx_arena_level_up (player, new_level, block_timestamp) — indexer/migrations/20260613120000_idx_arena_timearena_events_gl317.up.sql
    • idx_arena_feature_unlocked (player, feature_level, block_timestamp)
  • Ingestion: indexer/src/persist.rs · DecodedEvent::ArenaLevelUp, ArenaFeatureUnlocked
  • GET /v1/arena/wallet/{address}/stats (indexer/src/arena_wallet_stats.rs) returns current level, xp, first_buy_at but no level milestone history.
  • WalletProfileModal + WalletProfileModalSections.tsx show balances, stats, prizes, scores — opened from play/AUDIT on participant click (#258).
  • formatWalletProfileUnixSec exists for local-time display.

Why needed

Approved: store UTC timestamps for each wallet's progression milestones and surface Level History in the existing account/profile modal — Level 1 = first buy time; Levels 2–5 = LevelUp event times; display in local time.

Constraints / guardrails

  • Read-only profile data; no new write APIs.
  • Level 1 timestamp: derive from first indexed buy (idx_arena_buy / existing first_buy_at on wallet stats) unless a dedicated first-buy event is more accurate.
  • Levels 2–5: earliest idx_arena_level_up row per new_level for that player (handle reorg/idempotency via existing persist keys).
  • Display in viewer's local timezone (frontend formatting); store/query UTC in DB.
  • Extend wallet stats JSON (preferred) or add GET /v1/arena/wallet/{address}/level-history — bump schema version if shape changes.
  • AGPL; onchain remains source of truth for current level.

Relevant files

Layer Path
Migrations / tables indexer/migrations/20260613120000_idx_arena_timearena_events_gl317.up.sql
Persist indexer/src/persist.rs
Wallet stats SQL indexer/src/arena_wallet_stats.rs
HTTP indexer/src/api_arena.rs
Integration tests indexer/tests/integration_stage2.rs
Frontend modal frontend/src/components/WalletProfileModal.tsx, WalletProfileModalSections.tsx
API client frontend/src/lib/indexerApi.ts · ArenaWalletStats
Formatting frontend/src/lib/walletProfileFormat.ts
  1. Indexer: Add level_history: [{ level: "1", reached_at: "<iso8601>" }, …] to wallet stats response (levels 1–5; null when unknown).
    • L1: MIN(block_timestamp) from first buy for player.
    • L2–L5: MIN(block_timestamp) from idx_arena_level_up WHERE new_level = N.
  2. Integration tests: Fixture with buy + level-ups asserts ordered history array.
  3. Frontend: New WalletProfileLevelHistorySection — heading "Level history", rows 1–5 with local datetime + short label (reuse progression tier labels).
  4. Mount section in WalletProfileModal when stats loaded (not balances-only layout).

Acceptance criteria

  • Wallet stats (or dedicated endpoint) returns level history with UTC timestamps for ingested milestones.
  • Profile modal shows Level History section with levels 1–5 and local-time formatting.
  • Missing milestones show em dash placeholder (no fabricated times).
  • Reorg rollback keeps history consistent (integration test).
  • cd indexer && cargo clippy --all-targets -- -D warnings && cargo test passes.
  • cd frontend && npm run typecheck && npm run lint && npm test passes.

Test plan

Command Path
cd indexer && cargo test arena_wallet_stats.rs unit tests + integration_stage2.rs
YIELDOMEGA_PG_TEST_URL=... cargo test --test integration_stage2 arena_wallet_stats Postgres fixtures
cd frontend && npm test WalletProfileModalSections.test.tsx
bash scripts/verify-wallet-profile-anvil.sh End-to-end indexer + modal data

Happy path: wallet with buy + L2 level-up. Sad path: wallet never bought → empty history; malformed address → 400.

Attack / abuse test plan

  • History is derived from public chain events; no private data beyond indexed onchain activity.
  • Path-param address validation unchanged; rate-limit via existing API patterns.

Verification criteria

  • curl wallet stats for Anvil buyer shows level_history entries after scripted buys.
  • Clicking rival in play UI opens modal with Level History section populated.
## Parent Gap analysis follow-up from [#331](https://gitlab.com/PlasticDigits/yieldomega/-/issues/331). ## Current codebase - Indexer persists `LevelUp` and `FeatureUnlocked` events: - `idx_arena_level_up` (player, new_level, block_timestamp) — `indexer/migrations/20260613120000_idx_arena_timearena_events_gl317.up.sql` - `idx_arena_feature_unlocked` (player, feature_level, block_timestamp) - Ingestion: `indexer/src/persist.rs` · `DecodedEvent::ArenaLevelUp`, `ArenaFeatureUnlocked` - `GET /v1/arena/wallet/{address}/stats` (`indexer/src/arena_wallet_stats.rs`) returns current `level`, `xp`, `first_buy_at` but **no level milestone history**. - `WalletProfileModal` + `WalletProfileModalSections.tsx` show balances, stats, prizes, scores — opened from play/AUDIT on participant click ([#258](https://gitlab.com/PlasticDigits/yieldomega/-/issues/258)). - `formatWalletProfileUnixSec` exists for local-time display. ## Why needed Approved: store UTC timestamps for each wallet's progression milestones and surface **Level History** in the existing account/profile modal — Level 1 = first buy time; Levels 2–5 = `LevelUp` event times; display in **local time**. ## Constraints / guardrails - Read-only profile data; no new write APIs. - Level 1 timestamp: derive from **first indexed buy** (`idx_arena_buy` / existing `first_buy_at` on wallet stats) unless a dedicated first-buy event is more accurate. - Levels 2–5: earliest `idx_arena_level_up` row per `new_level` for that player (handle reorg/idempotency via existing persist keys). - Display in viewer's local timezone (frontend formatting); store/query UTC in DB. - Extend wallet stats JSON (preferred) or add `GET /v1/arena/wallet/{address}/level-history` — bump schema version if shape changes. - AGPL; onchain remains source of truth for current level. ## Relevant files | Layer | Path | |-------|------| | Migrations / tables | `indexer/migrations/20260613120000_idx_arena_timearena_events_gl317.up.sql` | | Persist | `indexer/src/persist.rs` | | Wallet stats SQL | `indexer/src/arena_wallet_stats.rs` | | HTTP | `indexer/src/api_arena.rs` | | Integration tests | `indexer/tests/integration_stage2.rs` | | Frontend modal | `frontend/src/components/WalletProfileModal.tsx`, `WalletProfileModalSections.tsx` | | API client | `frontend/src/lib/indexerApi.ts` · `ArenaWalletStats` | | Formatting | `frontend/src/lib/walletProfileFormat.ts` | ## Recommended direction 1. **Indexer**: Add `level_history: [{ level: "1", reached_at: "<iso8601>" }, …]` to wallet stats response (levels 1–5; null when unknown). - L1: `MIN(block_timestamp)` from first buy for player. - L2–L5: `MIN(block_timestamp)` from `idx_arena_level_up WHERE new_level = N`. 2. **Integration tests**: Fixture with buy + level-ups asserts ordered history array. 3. **Frontend**: New `WalletProfileLevelHistorySection` — heading "Level history", rows 1–5 with local datetime + short label (reuse progression tier labels). 4. Mount section in `WalletProfileModal` when stats loaded (not balances-only layout). ## Acceptance criteria - [ ] Wallet stats (or dedicated endpoint) returns level history with UTC timestamps for ingested milestones. - [ ] Profile modal shows Level History section with levels 1–5 and local-time formatting. - [ ] Missing milestones show em dash placeholder (no fabricated times). - [ ] Reorg rollback keeps history consistent (integration test). - [ ] `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` passes. - [ ] `cd frontend && npm run typecheck && npm run lint && npm test` passes. ## Test plan | Command | Path | |---------|------| | `cd indexer && cargo test` | `arena_wallet_stats.rs` unit tests + `integration_stage2.rs` | | `YIELDOMEGA_PG_TEST_URL=... cargo test --test integration_stage2 arena_wallet_stats` | Postgres fixtures | | `cd frontend && npm test` | `WalletProfileModalSections.test.tsx` | | `bash scripts/verify-wallet-profile-anvil.sh` | End-to-end indexer + modal data | Happy path: wallet with buy + L2 level-up. Sad path: wallet never bought → empty history; malformed address → 400. ## Attack / abuse test plan - History is derived from public chain events; no private data beyond indexed onchain activity. - Path-param address validation unchanged; rate-limit via existing API patterns. ## Verification criteria - `curl` wallet stats for Anvil buyer shows `level_history` entries after scripted buys. - Clicking rival in play UI opens modal with Level History section populated.
PlasticDigits commented 2026-06-19 04:28:09 +00:00 (Migrated from gitlab.com)

mentioned in issue #331

mentioned in issue #331
PlasticDigits commented 2026-06-19 06:35:34 +00:00 (Migrated from gitlab.com)

mentioned in merge request !509

mentioned in merge request !509
PlasticDigits commented 2026-06-19 07:45:10 +00:00 (Migrated from gitlab.com)

mentioned in commit 8452c43484

mentioned in commit 8452c43484d30fb50d26fb7b014662816c702f55
PlasticDigits commented 2026-06-19 08:44:10 +00:00 (Migrated from gitlab.com)

mentioned in commit 5b3bf97e59

mentioned in commit 5b3bf97e59f08c6cb71fb9115b5a596393e511c8
PlasticDigits commented 2026-06-19 08:44:37 +00:00 (Migrated from gitlab.com)

mentioned in merge request !512

mentioned in merge request !512
PlasticDigits commented 2026-06-19 10:08:11 +00:00 (Migrated from gitlab.com)

mentioned in commit e60a100f0a

mentioned in commit e60a100f0afc8d51c92387470f1d0dbd18bf3a15
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-19 10:08:14 +00:00
PlasticDigits commented 2026-06-19 10:59:43 +00:00 (Migrated from gitlab.com)

mentioned in merge request !514

mentioned in merge request !514
PlasticDigits commented 2026-06-19 11:07:41 +00:00 (Migrated from gitlab.com)

mentioned in issue #339

mentioned in issue #339
PlasticDigits commented 2026-06-19 11:55:54 +00:00 (Migrated from gitlab.com)

mentioned in merge request !515

mentioned in merge request !515
PlasticDigits commented 2026-06-19 13:31:43 +00:00 (Migrated from gitlab.com)

mentioned in issue #340

mentioned in issue #340
PlasticDigits commented 2026-06-25 14:28:40 +00:00 (Migrated from gitlab.com)

mentioned in issue #342

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

mentioned in issue #343

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