feat: Changing pre-filled value for interact tab#950
feat: Changing pre-filled value for interact tab#950C0mberry wants to merge 3 commits intosolana-foundation:masterfrom
Conversation
|
@C0mberry is attempting to deploy a commit to the Solana Foundation Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR refactors wallet address pre-filling in the IDL interact tab so that switching wallets updates signer fields that were previously auto-filled, while leaving fields the user edited manually untouched. The The implementation logic is correct, but the new Confidence Score: 5/5Safe to merge — the implementation is correct and the only finding is missing test coverage for the new wallet-switching path. All findings are P2 (missing test for new branch). The logic itself is sound: wallet-prefill-provider.spec.ts — add a test that exercises the Important Files Changed
Sequence DiagramsequenceDiagram
participant W as Wallet Adapter
participant C as InteractInstruction
participant R as lastPrefillAddressRef
participant P as createWalletPrefillDependency
participant F as React Hook Form
Note over C: Component mounts, ref = undefined
W->>C: publicKey changes (Wallet A)
C->>P: createWalletPrefillDependency(instruction, fieldNames, ref)
P->>F: getValues(signerPath) → ""
Note over P: isEmpty = true → fill
P->>F: setValue(signerPath, "AddrA")
P->>R: ref.current = "AddrA"
W->>C: publicKey changes (Wallet B)
C->>P: createWalletPrefillDependency(instruction, fieldNames, ref)
P->>F: getValues(signerPath) → "AddrA"
Note over P: hasPreviousWallet = ("AddrA" === ref.current) → true → fill
P->>F: setValue(signerPath, "AddrB")
P->>R: ref.current = "AddrB"
Note over C: User manually edits signer field to "CustomAddr"
W->>C: publicKey changes (Wallet C)
C->>P: createWalletPrefillDependency(instruction, fieldNames, ref)
P->>F: getValues(signerPath) → "CustomAddr"
Note over P: isEmpty=false, hasPreviousWallet=false → skip
P->>R: ref.current = "AddrC"
Reviews (1): Last reviewed commit: "chore: build info" | Re-trigger Greptile |
| const pdaPrefillDependency = createPdaPrefillDependency(idl, instruction, fieldNames); | ||
| useFormPrefill({ | ||
| config: { | ||
| externalDependencies: [walletPrefillDependency, knownAccountsPrefillDependency, pdaPrefillDependency], |
There was a problem hiding this comment.
issue: There are some concerns with architecture and a simpler fix available.
The current approach lifts wallet prefill out of the useFormPrefill framework into a an ad-hoc useEffect + useRef in InteractInstruction.tsx, while pda and knownAccounts providers still live inside the framework. A few problems with this:
-
Asymmetric pattern. Three sibling providers, two follow the
ExternalDependencycontract, one doesn't. A future maintainer adding a fourth provider has to guess which pattern to copy. The "stale prefill" problem isn't unique to wallet - it could apply to any provider, so the right fix is either fully inside the framework or fully outside, not split. -
Implementation detail leaks into the call site.
lastPrefillAddressRefis created in the UI component and threaded into the provider as a parameter. The provider's internal bookkeeping should not appear in the component that renders accounts. -
Misleading eslint-disable. The comment claims
fieldNamesis stable, butuseInstructionFormreturns a freshfieldNamesobject on every render (seeuse-instruction-form.ts:48). The closure captures the latest value, so it's harmless in practice - but the comment will mislead the next reader. -
Reinvents what react-hook-form already tracks. We're maintaining external state to answer "did the user type this value, or did we prefill it?" RHF already answers that via
isDirty. Every prefill in this provider is called withshouldDirty: false, so:- prefilled fields →
isDirty: false - user-typed fields →
isDirty: true(RHF sets this automatically ononChange)
The "match previous wallet address" check is a workaround for not using the signal RHF already gives us.
- prefilled fields →
Suggested fix:
-
Revert
InteractInstruction.tsxto the symmetric form — drop theuseRef,useEffect, and the eslint-disable. Wallet rejoins theexternalDependenciesarray next to the other two:const walletPrefillDependency = createWalletPrefillDependency(instruction, publicKey, fieldNames); const knownAccountsPrefillDependency = createKnownAccountsPrefillDependency(instruction, fieldNames); const pdaPrefillDependency = createPdaPrefillDependency(idl, instruction, fieldNames); useFormPrefill({ config: { externalDependencies: [walletPrefillDependency, knownAccountsPrefillDependency, pdaPrefillDependency] }, form, });
-
Restore
idandgetValueoncreateWalletPrefillDependencyso it conforms toExternalDependency<PublicKey>. Drop thelastPrefillAddressRefparameter — no external state needed. -
Replace the
isEmpty || hasPreviousWalletcheck with a single dirty check insideonValueChange:for (const path of signerPaths) { if (form.getFieldState(path).isDirty) continue; form.setValue(path, walletAddress as unknown as FormValue, { shouldDirty: false, shouldValidate: false, }); }
Note:
getFieldStatereads internal RHF state directly, noformStatesubscription required. -
Keep
shouldDirty: falseon everysetValue(already the case) — that's the load-bearing invariant that makes step 3 work.
Description
Type of change
Screenshots
Screen.Recording.2026-04-16.at.14.24.17.mov
Testing
Related Issues
HOO-263
Checklist
build:infoscript to update build information