Serve wallet level history in profile modal via indexer (#336) #509

Merged
PlasticDigits merged 1 commit from issue-336-wallet-level-history into main 2026-06-19 07:45:09 +00:00
PlasticDigits commented 2026-06-19 06:35:31 +00:00 (Migrated from gitlab.com)

Summary

Adds wallet progression Level history to the participant profile modal (#336).

  • Indexer (schema 2.18.0): GET /v1/arena/wallet/{address}/stats now includes level_history — five entries (level "1"…"5", reached_at UTC ISO-8601 or null). Level 1 = earliest idx_arena_buy; levels 2–5 = earliest idx_arena_level_up per new_level.
  • Frontend: New WalletProfileLevelHistorySection in WalletProfileModal — local-time display, em dash for unknown milestones, progression tier short labels from arenaProgression.ts.
  • Docs / invariants: INV-INDEXER-336-WALLET-LEVEL-HISTORY, cross-links in indexer design, arena-views, play skill, manual QA.

Acceptance checklist

Criterion Command / step Result
Wallet stats returns level history with UTC timestamps cd indexer && cargo test arena_wallet_stats_level_history PASS
Profile modal shows Level History (levels 1–5, local time) cd frontend && npm test -- WalletProfileModalSections PASS
Missing milestones show em dash (no fabricated times) WalletProfileModalSections.test.tsx level history case PASS
Reorg rollback keeps history consistent integration_stage2::arena_wallet_stats_level_history_and_reorg PASS
Indexer clippy + tests cd indexer && cargo clippy --all-targets -- -D warnings && cargo test PASS
Frontend typecheck + lint + test cd frontend && npm run typecheck && npm run lint && npm test PASS
Anvil wallet stats smoke bash scripts/verify-wallet-profile-anvil.sh PASS

Third-party verification

  1. bash scripts/bootstrap-cloud-postgres-native.sh
  2. export PATH="$HOME/.foundry/bin:$PATH" && bash scripts/verify-wallet-profile-anvil.sh
  3. curl -sf http://127.0.0.1:3103/v1/arena/wallet/<buyer>/stats | jq '.level_history'
  4. Start play stack; click a participant address → Level history section after XP / Level
  • Parent gap: #331
  • Profile modal base: #258
## Summary Adds wallet progression **Level history** to the participant profile modal ([#336](https://gitlab.com/PlasticDigits/yieldomega/-/issues/336)). - **Indexer (schema 2.18.0):** `GET /v1/arena/wallet/{address}/stats` now includes `level_history` — five entries (`level` `"1"`…`"5"`, `reached_at` UTC ISO-8601 or `null`). Level 1 = earliest `idx_arena_buy`; levels 2–5 = earliest `idx_arena_level_up` per `new_level`. - **Frontend:** New `WalletProfileLevelHistorySection` in `WalletProfileModal` — local-time display, em dash for unknown milestones, progression tier short labels from `arenaProgression.ts`. - **Docs / invariants:** `INV-INDEXER-336-WALLET-LEVEL-HISTORY`, cross-links in indexer design, arena-views, play skill, manual QA. ## Acceptance checklist | Criterion | Command / step | Result | |-----------|----------------|--------| | Wallet stats returns level history with UTC timestamps | `cd indexer && cargo test arena_wallet_stats_level_history` | PASS | | Profile modal shows Level History (levels 1–5, local time) | `cd frontend && npm test -- WalletProfileModalSections` | PASS | | Missing milestones show em dash (no fabricated times) | `WalletProfileModalSections.test.tsx` level history case | PASS | | Reorg rollback keeps history consistent | `integration_stage2::arena_wallet_stats_level_history_and_reorg` | PASS | | Indexer clippy + tests | `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` | PASS | | Frontend typecheck + lint + test | `cd frontend && npm run typecheck && npm run lint && npm test` | PASS | | Anvil wallet stats smoke | `bash scripts/verify-wallet-profile-anvil.sh` | PASS | ## Third-party verification 1. `bash scripts/bootstrap-cloud-postgres-native.sh` 2. `export PATH="$HOME/.foundry/bin:$PATH" && bash scripts/verify-wallet-profile-anvil.sh` 3. `curl -sf http://127.0.0.1:3103/v1/arena/wallet/<buyer>/stats | jq '.level_history'` 4. Start play stack; click a participant address → **Level history** section after **XP / Level** ## Related - Parent gap: [#331](https://gitlab.com/PlasticDigits/yieldomega/-/issues/331) - Profile modal base: [#258](https://gitlab.com/PlasticDigits/yieldomega/-/issues/258)
PlasticDigits commented 2026-06-19 06:39:06 +00:00 (Migrated from gitlab.com)

Security review — MR !509

Commit reviewed: 9c75cd22bd99b14f2f1d5ef153bbaa7aca9ad8d1 (base 3d6d3b21)

Scope: Wallet level_history on GET /v1/arena/wallet/{address}/stats (indexer SQL aggregations over idx_arena_buy / idx_arena_level_up), frontend WalletProfileLevelHistorySection + formatters, optimistic stats stub, integration/reorg test, docs/smoke script.

Method: Traced attacker-controlled inputs ({address} path param, indexer JSON level_history[]) to sinks (parameterized SQL, React text rendering). Compared new queries and UI paths to existing wallet-stats controls (#329 address gate, reorg rollback, public read-only API).

Outcome: FINDINGS: 0 medium+

No inline threads — nothing to block on.

Areas reviewed (no issues)

Area Result
SQL injection New queries bind wallet as $1; no string concatenation.
Authn/authz Public aggregate endpoint; level milestones are on-chain public data — intended.
XSS StatRow renders level / formatted timestamps as React text nodes; formatWalletProfileIso8601 returns locale string or em dash.
SSRF / shell / path traversal Not in diff.
EVM / contract Read-only indexer over indexed events; no new on-chain surface.
DoS Two indexed MIN aggregates per stats request; bounded 5-row level_history from server.
Reorg consistency Integration test confirms rollback clears rolled-back level-up milestone.
Secret leakage None introduced.

Label: block:security not applied.

## Security review — MR !509 **Commit reviewed:** `9c75cd22bd99b14f2f1d5ef153bbaa7aca9ad8d1` (base `3d6d3b21`) **Scope:** Wallet `level_history` on `GET /v1/arena/wallet/{address}/stats` (indexer SQL aggregations over `idx_arena_buy` / `idx_arena_level_up`), frontend `WalletProfileLevelHistorySection` + formatters, optimistic stats stub, integration/reorg test, docs/smoke script. **Method:** Traced attacker-controlled inputs (`{address}` path param, indexer JSON `level_history[]`) to sinks (parameterized SQL, React text rendering). Compared new queries and UI paths to existing wallet-stats controls (#329 address gate, reorg rollback, public read-only API). **Outcome:** `FINDINGS: 0` medium+ No inline threads — nothing to block on. ### Areas reviewed (no issues) | Area | Result | |------|--------| | SQL injection | New queries bind `wallet` as `$1`; no string concatenation. | | Authn/authz | Public aggregate endpoint; level milestones are on-chain public data — intended. | | XSS | `StatRow` renders `level` / formatted timestamps as React text nodes; `formatWalletProfileIso8601` returns locale string or em dash. | | SSRF / shell / path traversal | Not in diff. | | EVM / contract | Read-only indexer over indexed events; no new on-chain surface. | | DoS | Two indexed `MIN` aggregates per stats request; bounded 5-row `level_history` from server. | | Reorg consistency | Integration test confirms rollback clears rolled-back level-up milestone. | | Secret leakage | None introduced. | **Label:** `block:security` not applied.
PlasticDigits commented 2026-06-19 07:45:10 +00:00 (Migrated from gitlab.com)

mentioned in commit 8452c43484

mentioned in commit 8452c43484d30fb50d26fb7b014662816c702f55
PlasticDigits (Migrated from gitlab.com) merged commit 8452c43484 into main 2026-06-19 07:45:10 +00:00
Sign in to join this conversation.
No reviewers
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!509
No description provided.