Skip to content

Commit 86a65ac

Browse files
committed
fix(walletconnect): remove duplicate chain-change relay and clarify chain permission errors
1 parent 5d0bb08 commit 86a65ac

3 files changed

Lines changed: 6 additions & 209 deletions

File tree

app/core/WalletConnect/WalletConnect2Session.test.ts

Lines changed: 1 addition & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { IWalletKit, WalletKitTypes } from '@reown/walletkit';
88
import { SessionTypes } from '@walletconnect/types';
99
import { store } from '../../store';
1010
import { selectEvmChainId } from '../../selectors/networkController';
11-
import { selectPerOriginChainId } from '../../selectors/selectedNetworkController';
1211
import { Platform, Linking } from 'react-native';
1312
import Routes from '../../../app/constants/navigation/Routes';
1413
import Device from '../../util/device';
@@ -557,122 +556,6 @@ describe('WalletConnect2Session', () => {
557556
session.updateSession = originalUpdateSession;
558557
});
559558

560-
it('subscribes to chain changes', async () => {
561-
// eslint-disable-next-line no-empty-function
562-
let subscriberCallback: () => void = () => {};
563-
(store.subscribe as jest.Mock).mockImplementation(
564-
(callback: () => void) => {
565-
subscriberCallback = callback;
566-
},
567-
);
568-
569-
// Mock initial chain ID
570-
(selectPerOriginChainId as unknown as jest.Mock).mockReturnValue('0x1');
571-
572-
session = new WalletConnect2Session({
573-
web3Wallet: mockClient,
574-
session: mockSession,
575-
channelId: 'test-channel',
576-
deeplink: true,
577-
navigation: mockNavigation,
578-
});
579-
580-
const handleChainChangeSpy = jest.spyOn(
581-
session as any,
582-
'handleChainChange',
583-
);
584-
585-
// Change the chain ID
586-
(selectPerOriginChainId as unknown as jest.Mock).mockReturnValue('0x2');
587-
588-
subscriberCallback();
589-
590-
await new Promise(process.nextTick);
591-
592-
expect(handleChainChangeSpy).toHaveBeenCalledWith(2);
593-
594-
subscriberCallback();
595-
expect(handleChainChangeSpy).toHaveBeenCalledTimes(1);
596-
597-
handleChainChangeSpy.mockRestore();
598-
});
599-
600-
it('does not trigger handleChainChange when handler is already running', async () => {
601-
// eslint-disable-next-line no-empty-function
602-
let subscriberCallback: () => void = () => {};
603-
(store.subscribe as jest.Mock).mockImplementation(
604-
(callback: () => void) => {
605-
subscriberCallback = callback;
606-
},
607-
);
608-
609-
(selectPerOriginChainId as unknown as jest.Mock).mockReturnValue('0x1');
610-
611-
session = new WalletConnect2Session({
612-
web3Wallet: mockClient,
613-
session: mockSession,
614-
channelId: 'test-channel',
615-
deeplink: true,
616-
navigation: mockNavigation,
617-
});
618-
619-
(session as any).isHandlingChainChange = true;
620-
621-
const handleChainChangeSpy = jest.spyOn(
622-
session as any,
623-
'handleChainChange',
624-
);
625-
626-
(selectPerOriginChainId as unknown as jest.Mock).mockReturnValue('0x2');
627-
628-
subscriberCallback();
629-
630-
await new Promise(process.nextTick);
631-
632-
expect(handleChainChangeSpy).not.toHaveBeenCalled();
633-
634-
handleChainChangeSpy.mockRestore();
635-
});
636-
637-
it('logs warning on handleChainChange error', async () => {
638-
// eslint-disable-next-line no-empty-function
639-
let subscriberCallback: () => void = () => {};
640-
(store.subscribe as jest.Mock).mockImplementation(
641-
(callback: () => void) => {
642-
subscriberCallback = callback;
643-
},
644-
);
645-
646-
const devLoggerSpy = jest.spyOn(DevLogger, 'log').mockImplementation();
647-
648-
(selectPerOriginChainId as unknown as jest.Mock).mockReturnValue('0x1');
649-
650-
session = new WalletConnect2Session({
651-
web3Wallet: mockClient,
652-
session: mockSession,
653-
channelId: 'test-channel',
654-
deeplink: true,
655-
navigation: mockNavigation,
656-
});
657-
658-
const error = new Error('Chain change failed');
659-
jest
660-
.spyOn(session as any, 'handleChainChange')
661-
.mockRejectedValueOnce(error);
662-
663-
(selectPerOriginChainId as unknown as jest.Mock).mockReturnValue('0x2');
664-
665-
subscriberCallback();
666-
667-
await new Promise(process.nextTick);
668-
669-
expect(devLoggerSpy).toHaveBeenCalledWith(
670-
'WC2::store.subscribe Error handling chain change:',
671-
error,
672-
);
673-
674-
devLoggerSpy.mockRestore();
675-
});
676559
describe('redirect', () => {
677560
beforeEach(() => {
678561
jest.clearAllMocks();
@@ -1103,7 +986,7 @@ describe('WalletConnect2Session', () => {
1103986
'eip155:1234567890abcdef1234567890abcdef12345678',
1104987
),
1105988
).rejects.toThrow(
1106-
'Invalid parameters: active chainId is different than the one provided.',
989+
'Requested chain is not permitted for this WalletConnect session.',
1107990
);
1108991
});
1109992

app/core/WalletConnect/WalletConnect2Session.ts

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ class WalletConnect2Session {
8181
private timeoutRef: NodeJS.Timeout | null = null;
8282
private requestsToRedirect: { [request: string]: boolean } = {};
8383
private topicByRequestId: { [requestId: string]: string } = {};
84-
private lastChainId: Hex;
85-
private isHandlingChainChange = false;
86-
private storeUnsubscribe: (() => void) | null = null;
8784

8885
private _isHandlingRequest = false;
8986

@@ -209,8 +206,6 @@ class WalletConnect2Session {
209206
});
210207

211208
this.checkPendingRequests();
212-
this.lastChainId = this.getCurrentChainId();
213-
this.storeUnsubscribe = store.subscribe(this.onStoreChange.bind(this));
214209
}
215210

216211
/**
@@ -235,19 +230,6 @@ class WalletConnect2Session {
235230
return getHostname(this.selfReportedUrl);
236231
}
237232

238-
private onStoreChange() {
239-
const newChainId = this.getCurrentChainId();
240-
if (newChainId !== this.lastChainId && !this.isHandlingChainChange) {
241-
this.lastChainId = newChainId;
242-
const decimalChainId = Number.parseInt(newChainId, 16);
243-
this.handleChainChange(decimalChainId).catch((error) => {
244-
DevLogger.log(
245-
'WC2::store.subscribe Error handling chain change:',
246-
error,
247-
);
248-
});
249-
}
250-
}
251233
public getCurrentChainId() {
252234
const perOriginChainId = selectPerOriginChainId(
253235
store.getState(),
@@ -377,72 +359,6 @@ class WalletConnect2Session {
377359
}
378360
}
379361

380-
/** Handle chain change by updating session namespaces and emitting event */
381-
private async handleChainChange(chainIdDecimal: number) {
382-
if (this.isHandlingChainChange) return;
383-
this.isHandlingChainChange = true;
384-
385-
try {
386-
// Update session namespaces
387-
const currentNamespaces = this.normalizeSessionNamespaces(
388-
this.session.namespaces,
389-
);
390-
const newChainId = `eip155:${chainIdDecimal}`;
391-
const updatedChains = [
392-
...new Set([...(currentNamespaces?.eip155?.chains || []), newChainId]),
393-
];
394-
395-
const accounts = [
396-
...new Set(
397-
(currentNamespaces?.eip155?.accounts || []).map(
398-
(acc) => acc.split(':')[2],
399-
),
400-
),
401-
].map((account) => `${newChainId}:${account}`);
402-
403-
const updatedAccounts = [
404-
...new Set([
405-
...(currentNamespaces?.eip155?.accounts || []),
406-
...accounts,
407-
]),
408-
];
409-
410-
const updatedNamespaces = {
411-
...currentNamespaces,
412-
eip155: {
413-
...(currentNamespaces?.eip155 || {}),
414-
chains: updatedChains,
415-
methods: currentNamespaces?.eip155?.methods || [],
416-
events: currentNamespaces?.eip155?.events || [],
417-
accounts: updatedAccounts,
418-
},
419-
};
420-
421-
DevLogger.log(
422-
`WC2::handleChainChange updating session with namespaces`,
423-
updatedNamespaces,
424-
);
425-
426-
await this.web3Wallet.updateSession({
427-
topic: this.session.topic,
428-
namespaces: updatedNamespaces,
429-
});
430-
// await acknowledged();
431-
432-
await new Promise((resolve) => setTimeout(resolve, 100));
433-
434-
// Emit chainChanged event
435-
await this.emitEvent('chainChanged', chainIdDecimal);
436-
} catch (error) {
437-
DevLogger.log(
438-
`WC2::handleChainChange error while updating session`,
439-
error,
440-
);
441-
throw error;
442-
} finally {
443-
this.isHandlingChainChange = false;
444-
}
445-
}
446362
approveRequest = async ({ id, result }: { id: string; result: unknown }) => {
447363
const topic = this.topicByRequestId[id];
448364

@@ -706,8 +622,8 @@ class WalletConnect2Session {
706622
await hasPermissionsToSwitchChainRequest(caip2ChainId, channelId);
707623

708624
if (!allowed && !allowSwitchingToNewChain) {
709-
throw rpcErrors.invalidParams({
710-
message: `Invalid parameters: active chainId is different than the one provided.`,
625+
throw providerErrors.unauthorized({
626+
message: `Requested chain is not permitted for this WalletConnect session. Reconnect and approve ${caip2ChainId} to continue.`,
711627
});
712628
}
713629

@@ -926,8 +842,8 @@ class WalletConnect2Session {
926842

927843
if (!isAllowedChainId) {
928844
DevLogger.log(`WC::checkWCPermissions chainId is not permitted`);
929-
throw rpcErrors.invalidParams({
930-
message: `Invalid parameters: active chainId is different than the one provided.`,
845+
throw providerErrors.unauthorized({
846+
message: `Requested chain is not permitted for this WalletConnect session. Reconnect and approve ${caip2ChainId} to continue.`,
931847
});
932848
}
933849

@@ -1024,8 +940,6 @@ class WalletConnect2Session {
1024940
};
1025941

1026942
removeListeners = async () => {
1027-
this.storeUnsubscribe?.();
1028-
this.storeUnsubscribe = null;
1029943
this.backgroundBridge.onDisconnect();
1030944
};
1031945

app/core/WalletConnect/wc-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ export const hasPermissionsToSwitchChainRequest = async (
500500
if (!existingNetwork) {
501501
DevLogger.log(`WC::checkWCPermissions no existing network found`);
502502
throw rpcErrors.invalidParams({
503-
message: `Invalid parameters: active chainId is different than the one provided.`,
503+
message: `Requested chain does not exist in wallet configuration.`,
504504
});
505505
}
506506

0 commit comments

Comments
 (0)