Skip to content

fix: normalize accountsChanged payload in FarcasterProvider - #50

Open
amirosein-union wants to merge 1 commit into
StartaleGroup:masterfrom
amirosein-union:fix-farcaster-accounts-changed-normalization
Open

fix: normalize accountsChanged payload in FarcasterProvider#50
amirosein-union wants to merge 1 commit into
StartaleGroup:masterfrom
amirosein-union:fix-farcaster-accounts-changed-normalization

Conversation

@amirosein-union

Copy link
Copy Markdown

Guard the host event boundary so malformed payloads (plain
string, object with .accounts, or other non-array) are coerced to
string[] before forwarding. Drops and warns on unrecognisable shapes to
prevent Wagmi's onAccountsChanged from calling .map() on a non-array.

Guard the host event boundary so malformed payloads (plain string,
object with .accounts, or other non-array) are coerced to string[]
before forwarding. Drops and warns on unrecognisable shapes to prevent
Wagmi's onAccountsChanged from calling .map() on a non-array.
@amirosein-union

Copy link
Copy Markdown
Author

events were being forwarded raw to Wagmi's onAccountsChanged handler, which
unconditionally calls .map() on the payload. When the host emits a
non-array payload (plain string, object with an .accounts property, or
other malformed shape), this throws TypeError: accounts.map is not a
function, crashing the connector.

Root cause: FarcasterProvider blindly re-emitted all EIP-1193 events
with this.emit(event, ...args) without validating the accountsChanged
payload against the EIP-1193 spec (which requires string[]).

Fix: Added normalizeAccountsChangedPayload at the host event boundary
to coerce the three known malformed shapes into string[]. If the
payload is unrecognisable, the event is dropped with a console.warn
rather than forwarding a non-array and triggering the crash downstream.

@Juminstock Juminstock left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good direction overall. Two things that aren't coverable as inline comments:

Request path gap (line 77-79): handleWalletConnect casts the eth_requestAccounts response directly to Address[] and calls .map() on it. If Farcaster returns { accounts: [...] } from that method (the same non-standard shape this PR guards on the event side), .map() throws at runtime. Consider running that response through normalizeAccountsChangedPayload, or extracting a shared helper so both the event and request paths are protected.

Missing unit tests: normalizeAccountsChangedPayload is the core logic of this fix and has three branches, but no dedicated unit tests cover it. Each shape deserves its own test: plain string, valid string[], non-string array elements, { accounts: string[] }, { accounts: 'single-string' }, and an unrecognized shape. End-to-end forwarder tests won't catch element-type issues.

}

function normalizeAccountsChangedPayload(payload: unknown): string[] | undefined {
if (Array.isArray(payload)) return payload as string[]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array.isArray passes for any array regardless of element type, so a payload like [42, null] or [{ address: '0x...' }] clears this check and gets returned typed as string[]. Downstream callers that assume strings (.toLowerCase(), isAddress()) will throw at runtime with no warning.

Suggested fix:

if (Array.isArray(payload)) return payload.filter((v): v is string => typeof v === 'string')

Apply the same .filter to line 123 (the .accounts branch).

if (typeof payload === 'string') return [payload]
if (Array.isArray((payload as { accounts?: unknown }).accounts))
return (payload as { accounts: string[] }).accounts
return undefined

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ accounts: '0xabc' } (a single address as a string, not an array) falls through all three branches and returns undefined here, silently dropping the event. Array.isArray(payload) is false (it's an object), typeof payload === 'string' is false, and Array.isArray(payload.accounts) is false because '0xabc' is not an array.

Suggested fix, add this branch before return undefined:

if (typeof (payload as { accounts?: unknown }).accounts === 'string')
    return [(payload as { accounts: string }).accounts]

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.

2 participants