Fix wallet bridge on Firefox: use postMessage instead of CustomEvent
Manual testing on Zen/Firefox surfaced "Uncaught Error: Permission denied to access property 'id'" the moment the isolated-world relay dispatched a CustomEvent to the world:'MAIN' injected script. That's a Firefox-specific Xray-wrapper restriction: a CustomEvent's `detail` object created in one world can't have its properties read from the other, even though the event itself fires fine. Chromium doesn't enforce this, which is why it wasn't caught until testing on the actual target browser (Zen). Switched both sides of the bridge (wallet-bridge/inject.ts, wallet-bridge/relay.ts) to window.postMessage with a `channel` field and same-window source check, since postMessage structured-clones its payload across the boundary correctly on both browsers -- the same approach Phantom's own inpage<->content-script bridge uses. Also added [nexa/...]-prefixed console.debug breadcrumbs through the wallet-connect/wallet-bridge/wallet-auth chain, since diagnosing this without them (previous commit shipped none) took several rounds of "nothing happened" back and forth. Still not fully verified end-to-end against live Phantom -- the crash is fixed, but a full connect -> sign -> verify round trip hasn't been confirmed yet. See CLAUDE.md. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01YXiHuScXrjxBh7yFGAPq3B
This commit is contained in:
@@ -12,18 +12,21 @@ interface SignResult {
|
||||
}
|
||||
|
||||
async function reportConnected(walletAddress: string): Promise<void> {
|
||||
console.debug('[nexa/wallet-connect] connected', walletAddress);
|
||||
hideConnectBanner();
|
||||
await browser.runtime
|
||||
.sendMessage({ type: 'nexa:wallet-connected', walletAddress } satisfies NexaMessage)
|
||||
.catch(() => undefined);
|
||||
.catch((err) => console.debug('[nexa/wallet-connect] failed to notify background:', err));
|
||||
}
|
||||
|
||||
/** Real user gesture (banner button click) — required for Phantom to show its connect approval popup on a first-ever connect. */
|
||||
async function connectWithGesture(): Promise<void> {
|
||||
console.debug('[nexa/wallet-connect] banner clicked, calling connect()');
|
||||
try {
|
||||
const { walletAddress } = await callWallet<ConnectResult>('connect', {});
|
||||
await reportConnected(walletAddress);
|
||||
} catch (err) {
|
||||
console.debug('[nexa/wallet-connect] connect() failed:', err);
|
||||
setConnectBannerError(err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
@@ -34,16 +37,19 @@ async function connectWithGesture(): Promise<void> {
|
||||
* connect. Falls back to the on-page banner (a real click) only when it isn't.
|
||||
*/
|
||||
async function attemptSilentConnect(): Promise<void> {
|
||||
console.debug('[nexa/wallet-connect] attempting silent connect');
|
||||
try {
|
||||
const { walletAddress } = await callWallet<ConnectResult>('connect', { onlyIfTrusted: true });
|
||||
await reportConnected(walletAddress);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.debug('[nexa/wallet-connect] silent connect failed, showing banner:', err);
|
||||
showConnectBanner(() => void connectWithGesture());
|
||||
}
|
||||
}
|
||||
|
||||
/** Wires wallet connect/sign into the page. Independent of any site adapter — runs regardless of whether a buy-button adapter matched. */
|
||||
export function initWalletConnect(): void {
|
||||
console.debug('[nexa/wallet-connect] init on', window.location.href);
|
||||
void attemptSilentConnect();
|
||||
|
||||
browser.runtime.onMessage.addListener((message: NexaMessage, _sender, sendResponse) => {
|
||||
|
||||
Reference in New Issue
Block a user