Skip to content

refactor(wallet-flow): stable signerIds for React keys; tripwire test#238

Closed
jinglescode wants to merge 1 commit into
feat/server-hardening-and-audit-logfrom
refactor/wallet-flow-shared
Closed

refactor(wallet-flow): stable signerIds for React keys; tripwire test#238
jinglescode wants to merge 1 commit into
feat/server-hardening-and-audit-logfrom
refactor/wallet-flow-shared

Conversation

@jinglescode
Copy link
Copy Markdown
Member

Summary

Both new-wallet-flow and wallet-migration flow used the raw signer address as a React key in ReviewSignersCard. That breaks badly:

  1. The address can be empty/undefined while the user is editing → the key is non-unique → React reuses inputs across rows, swapping names and addresses while typing
  2. Two signers can momentarily share the same partial input → duplicate keys → same swap

Fix: every signer row gets a stable opaque signerId generated when the row is created; the component uses signerId as the React key. Address becomes regular state, free to be empty/duplicate transiently without breaking React identity.

The same fix applied to both useWalletFlowState and useMigrationWalletFlowState. The shared signerRows.ts module emits the id and keeps the parallel arrays in sync.

Tripwire test

reviewSignersCardKey.test.ts is a source-regex tripwire suite that:

  • greps ReviewSignersCard.tsx to assert the raw address is never used as a React key
  • verifies both flow-state hooks expose signerIds parallel to signersAddresses

This catches future regressions structurally — the type system can't enforce "don't use address as key", but a regex over source can.

Base branch

This PR targets #237 (feat/server-hardening-and-audit-log) because it depends on the test infrastructure changes in that PR. Once #237 merges to main, this will auto-retarget to main.

Test plan

🤖 Generated with Claude Code

The new-wallet-flow and wallet-migration flow used the raw signer
address as a React key in ReviewSignersCard. Two issues:

1. The address can be empty/undefined while the user is editing, which
   makes the key non-unique → React reuses inputs across rows, swapping
   names and addresses while typing.
2. Two signers can momentarily share the same partial input, again
   producing duplicate keys.

Fix: every row gets a stable opaque `signerId` generated when the row
is created; the component uses `signerId` as the React key. Address
becomes regular state, free to be empty/duplicate transiently without
breaking React identity.

The same fix applied to both `useWalletFlowState` (new wallet) and
`useMigrationWalletFlowState` (migration). The shared `signerRows.ts`
module emits the id and keeps the parallel arrays in sync.

`reviewSignersCardKey.test.ts` is a tripwire suite that:
- greps the source to assert the raw address is never used as a key
- verifies both flow-state hooks expose signerIds parallel to
  signersAddresses

This catches future regressions structurally — the type system can't
enforce \"don't use address as key\", but a regex over source can.

Test plan
- 171/171 tests pass deterministically on top of #237
- Typecheck clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
multisig Ready Ready Preview, Comment May 10, 2026 8:35am

Request Review

@jinglescode jinglescode deleted the branch feat/server-hardening-and-audit-log May 10, 2026 08:56
@jinglescode jinglescode deleted the refactor/wallet-flow-shared branch May 10, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant