Frontend: wallet blockies must be identical for the same address on every surface #226

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

Summary

The same wallet address can render with different blockie colors depending on which part of the app shows it. Blockies must be pixel-identical for a given address everywhere — only size/scaling may differ, not hue or pattern.

Canonical rendering today goes through WalletBlockie (frontend/src/components/WalletBlockie.tsx) via AddressInline (frontend/src/components/AddressInline.tsx), using ethereum-blockies@0.1.1. Despite a single component, surfaces still diverge visually.


Expected behavior

For any valid non-zero 0x… address:

  1. Deterministic identicon — same address → same foreground/background/spot colors and same 8×8 pattern on every surface (MetaMask / Etherscan parity).
  2. Surface styling must not recolor the identicon — borders, radius, and layout chrome are fine; CSS must not apply filters, blend modes, gradient backdrops, or opacity that change perceived blockie colors.
  3. Size may vary — callers pass different size props (16–40px today); canvas may scale, but the underlying bitmap must be the same for the same seed.

Observed behavior

A wallet seen in one surface (e.g. TimeCurve live buys, podium rows, Arena WarBow, referrals leaderboard) can show a different-colored blockie for the same address in another surface.


Likely causes (investigate in this order)

1. Surface-specific CSS tinting

.live-buy-row .address-inline__blockie applies a radial-gradient background, circular crop, glow border, and inset shadow behind the canvas (frontend/src/index.css ~5465–5488). Other surfaces use the default square .address-inline__blockie chrome only. That alone can make the same canvas read as a different color.

Audit all selectors touching .address-inline__blockie, .address-inline__blockie canvas, or parent filters.

2. ethereum-blockies global PRNG / color generation

The library uses module-level mutable state (randseed). In buildOpts, default colors are produced via createColor() and the PRNG is re-seeded with the address — but create() calls buildOpts twice per render. Verify determinism under React Strict Mode, list re-mounts, and concurrent useLayoutEffect passes. If non-deterministic, fix by:

  • seeding colors from the normalized address before any createColor() calls, or
  • switching to a fork with deterministic color-from-seed behavior (e.g. @ethereum/blockies / @metamask/blockies patterns), or
  • memoizing a data-URL / offscreen canvas keyed by normalized address so each address renders once.

3. Seed normalization drift

WalletBlockie.normalizedSeed() lowercases and ensures 0x prefix. Confirm every blockie path uses it (no duplicate identicon logic). Grep:

rg 'WalletBlockie|AddressInline|blockies' frontend/src -g '*.{tsx,ts}'

Surfaces to regression-test

Area File(s) Typical size
TimeCurve live buy rows (hero + modal) LiveBuyRow.tsx 36 / 40
TimeCurve simple ticker / podium TimeCurveLiveBuysActivitySection.tsx, TimeCurveSimplePodiumSection.tsx 16–22
Arena WarBow / wallet mono WarbowHeroActions.tsx, TimeCurveArenaWalletMono.tsx, useTimeCurveArenaModel.tsx 16–22
Referrals leaderboard ReferralLeaderboardSection.tsx varies
Protocol / sections TimeCurveSections.tsx, TimeCurveSimplePage.tsx 16

Pick one known address that appears in at least two of these surfaces and compare side-by-side (screenshot diff or canvas toDataURL() equality in a unit test).


Acceptance criteria

  1. Visual: Same address → indistinguishable blockie colors/pattern across all AddressInline surfaces (document the test address + screenshots in the MR).
  2. Automated: Add a test that renders WalletBlockie twice (or at two sizes) for a fixed address and asserts identical canvas.toDataURL() or identical color samples at fixed pixels — unless product explicitly allows scale-only differences.
  3. CSS: Remove or isolate decorative backgrounds/filters so they sit outside the canvas, not under/over it.
  4. Docs: Extend GitLab #98 invariant row in docs/testing/invariants-and-business-logic.md with INV-FRONTEND-*-BLOCKIE-DETERMINISM (assign next ID) and a short manual QA step in docs/testing/manual-qa-checklists.md.

  • #98 — canonical AddressInline (blockie + label + explorer link)
  • docs/testing/manual-qa-checklists.md — address display QA
## Summary The same wallet address can render with **different blockie colors** depending on which part of the app shows it. Blockies must be **pixel-identical for a given address everywhere** — only size/scaling may differ, not hue or pattern. Canonical rendering today goes through **`WalletBlockie`** (`frontend/src/components/WalletBlockie.tsx`) via **`AddressInline`** (`frontend/src/components/AddressInline.tsx`), using **`ethereum-blockies@0.1.1`**. Despite a single component, surfaces still diverge visually. --- ## Expected behavior For any valid non-zero `0x…` address: 1. **Deterministic identicon** — same address → same foreground/background/spot colors and same 8×8 pattern on every surface (MetaMask / Etherscan parity). 2. **Surface styling must not recolor the identicon** — borders, radius, and layout chrome are fine; CSS must not apply filters, blend modes, gradient backdrops, or opacity that change perceived blockie colors. 3. **Size may vary** — callers pass different `size` props (16–40px today); canvas may scale, but the underlying bitmap must be the same for the same seed. --- ## Observed behavior A wallet seen in one surface (e.g. TimeCurve live buys, podium rows, Arena WarBow, referrals leaderboard) can show a **different-colored blockie** for the same address in another surface. --- ## Likely causes (investigate in this order) ### 1. Surface-specific CSS tinting `.live-buy-row .address-inline__blockie` applies a **radial-gradient background**, circular crop, glow border, and inset shadow behind the canvas (`frontend/src/index.css` ~5465–5488). Other surfaces use the default square `.address-inline__blockie` chrome only. That alone can make the same canvas read as a different color. Audit all selectors touching `.address-inline__blockie`, `.address-inline__blockie canvas`, or parent filters. ### 2. `ethereum-blockies` global PRNG / color generation The library uses **module-level mutable state** (`randseed`). In `buildOpts`, default colors are produced via `createColor()` and the PRNG is re-seeded with the address — but `create()` calls `buildOpts` twice per render. Verify determinism under React Strict Mode, list re-mounts, and concurrent `useLayoutEffect` passes. If non-deterministic, fix by: - seeding colors from the normalized address **before** any `createColor()` calls, or - switching to a fork with deterministic color-from-seed behavior (e.g. `@ethereum/blockies` / `@metamask/blockies` patterns), or - memoizing a data-URL / offscreen canvas keyed by normalized address so each address renders once. ### 3. Seed normalization drift `WalletBlockie.normalizedSeed()` lowercases and ensures `0x` prefix. Confirm **every** blockie path uses it (no duplicate identicon logic). Grep: ```bash rg 'WalletBlockie|AddressInline|blockies' frontend/src -g '*.{tsx,ts}' ``` --- ## Surfaces to regression-test | Area | File(s) | Typical `size` | |------|---------|----------------| | TimeCurve live buy rows (hero + modal) | `LiveBuyRow.tsx` | 36 / 40 | | TimeCurve simple ticker / podium | `TimeCurveLiveBuysActivitySection.tsx`, `TimeCurveSimplePodiumSection.tsx` | 16–22 | | Arena WarBow / wallet mono | `WarbowHeroActions.tsx`, `TimeCurveArenaWalletMono.tsx`, `useTimeCurveArenaModel.tsx` | 16–22 | | Referrals leaderboard | `ReferralLeaderboardSection.tsx` | varies | | Protocol / sections | `TimeCurveSections.tsx`, `TimeCurveSimplePage.tsx` | 16 | Pick **one known address** that appears in at least two of these surfaces and compare side-by-side (screenshot diff or canvas `toDataURL()` equality in a unit test). --- ## Acceptance criteria 1. **Visual:** Same address → indistinguishable blockie colors/pattern across all `AddressInline` surfaces (document the test address + screenshots in the MR). 2. **Automated:** Add a test that renders `WalletBlockie` twice (or at two sizes) for a fixed address and asserts identical `canvas.toDataURL()` **or** identical color samples at fixed pixels — unless product explicitly allows scale-only differences. 3. **CSS:** Remove or isolate decorative backgrounds/filters so they sit **outside** the canvas, not under/over it. 4. **Docs:** Extend GitLab #98 invariant row in `docs/testing/invariants-and-business-logic.md` with **`INV-FRONTEND-*-BLOCKIE-DETERMINISM`** (assign next ID) and a short manual QA step in `docs/testing/manual-qa-checklists.md`. --- ## Related - #98 — canonical `AddressInline` (blockie + label + explorer link) - `docs/testing/manual-qa-checklists.md` — address display QA
PlasticDigits commented 2026-05-20 05:15:47 +00:00 (Migrated from gitlab.com)

marked as related to #98

marked as related to #98
PlasticDigits commented 2026-05-20 05:46:18 +00:00 (Migrated from gitlab.com)

Fix pushed — please verify

@nullvector_ux — could you confirm blockie colors/patterns match for the same wallet across surfaces?

Branch: fix/blockie-determinism-226 (2830cce)

What changed

  1. walletBlockieCanvas.ts — deterministic palette via an isolated PRNG (no ethereum-blockies module-global pollution); memoized 64×64 source canvas per normalized address; display size only scales the shared bitmap.
  2. WalletBlockie.tsx — uses the cached source canvas clone; removed per-size regeneration.
  3. CSS — removed the live-buy-row radial-gradient backdrop that tinted blockies; global image-rendering: pixelated on .address-inline__blockie canvas.
  4. Tests — palette/seed determinism + CSS regression guards.

Suggested QA

Pick one address visible in ≥2 places (e.g. live buy row + podium + referrals leaderboard) and confirm the blockie is visually identical (colors + pattern), not just similar.

## Fix pushed — please verify @nullvector_ux — could you confirm blockie colors/patterns match for the same wallet across surfaces? **Branch:** `fix/blockie-determinism-226` (`2830cce`) ### What changed 1. **`walletBlockieCanvas.ts`** — deterministic palette via an isolated PRNG (no `ethereum-blockies` module-global pollution); memoized **64×64** source canvas per normalized address; display size only scales the shared bitmap. 2. **`WalletBlockie.tsx`** — uses the cached source canvas clone; removed per-size regeneration. 3. **CSS** — removed the live-buy-row **radial-gradient** backdrop that tinted blockies; global **`image-rendering: pixelated`** on `.address-inline__blockie canvas`. 4. **Tests** — palette/seed determinism + CSS regression guards. ### Suggested QA Pick one address visible in **≥2** places (e.g. live buy row + podium + referrals leaderboard) and confirm the blockie is visually identical (colors + pattern), not just similar.
PlasticDigits commented 2026-05-20 05:46:19 +00:00 (Migrated from gitlab.com)

mentioned in commit 2830cce8be

mentioned in commit 2830cce8bedd2cbd956fd7fb5a67380095a0850f
nullvector_ux commented 2026-05-20 06:47:43 +00:00 (Migrated from gitlab.com)

@PlasticDigits

I checked the wallet shortcut/blockie display across multiple routes. On http://localhost:5173/timecurve and http://localhost:5173/timecurve/arena,

(9c1199)

the wallet shortcut appears to use the same blockie pattern. This may be expected only if both entries represent the same wallet/address, but it needs confirmation because blockies are supposed to help visually distinguish different wallet addresses.

I also checked http://localhost:5173/timecurve/protocol and http://localhost:5173/referrals

(0x87bdce72…c78b5e)

These two routes show the same wallet shortcut pattern and the same shortened code/address display. If these are pointing to the same wallet/referral address, this is expected behavior. If they represent different wallets or different data sources, then this may be a visual identity issue because different entries should not appear identical.

@PlasticDigits I checked the wallet shortcut/blockie display across multiple routes. On `http://localhost:5173/timecurve` and `http://localhost:5173/timecurve/arena`, **(**[**9c1199**](https://mega.etherscan.io/address/0x8626f6940e2eb28930efb4cef49b2d1f2c9c1199)**)** the wallet shortcut appears to use the same blockie pattern. This may be expected only if both entries represent the same wallet/address, but it needs confirmation because blockies are supposed to help visually distinguish different wallet addresses. I also checked `http://localhost:5173/timecurve/protocol` and `http://localhost:5173/referrals` ([0x87bdce72…c78b5e](https://mega.etherscan.io/address/0x87bdce72c06c21cd96219bd8521bdf1f42c78b5e)) These two routes show the same wallet shortcut pattern and the same shortened code/address display. If these are pointing to the same wallet/referral address, this is expected behavior. If they represent different wallets or different data sources, then this may be a visual identity issue because different entries should not appear identical.
PlasticDigits commented 2026-05-21 04:48:16 +00:00 (Migrated from gitlab.com)

mentioned in issue #230

mentioned in issue #230
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-21 12:00:45 +00:00
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#226
No description provided.