diff --git a/packages/sdk-multichain/src/multichain/transports/default/index.ts b/packages/sdk-multichain/src/multichain/transports/default/index.ts index 35031893e..ccb8665c3 100644 --- a/packages/sdk-multichain/src/multichain/transports/default/index.ts +++ b/packages/sdk-multichain/src/multichain/transports/default/index.ts @@ -3,34 +3,63 @@ import type { CaipAccountId } from '@metamask/utils'; import type { ExtendedTransport, RPCAPI, Scope, SessionData } from 'src/domain'; import { addValidAccounts, getOptionalScopes, getValidAccounts } from 'src/multichain/utils'; +const DEFAULT_REQUEST_TIMEOUT = 60 * 1000; + export class DefaultTransport implements ExtendedTransport { + #notificationCallbacks: Set<(data: unknown) => void> = new Set(); #requestId = 0; #transport: Transport = getDefaultTransport(); + #defaultRequestOptions = { + timeout: DEFAULT_REQUEST_TIMEOUT, + }; + + #notifyCallbacks(data: unknown) { + for (const cb of this.#notificationCallbacks) { + try { + cb(data); + } catch (err) { + console.log('[WindowPostMessageTransport] notifyCallbacks error:', err); + } + } + } async connect(options?: { scopes: Scope[]; caipAccountIds: CaipAccountId[] }): Promise { await this.#transport.connect(); + //Get wallet session - const sessionRequest = await this.request({ method: 'wallet_getSession' }); + const sessionRequest = await this.request({ method: 'wallet_getSession' }, this.#defaultRequestOptions); let walletSession = sessionRequest.result as SessionData; if (walletSession && options) { const currentScopes = Object.keys(walletSession?.sessionScopes ?? {}) as Scope[]; const proposedScopes = options?.scopes ?? []; const isSameScopes = currentScopes.every((scope) => proposedScopes.includes(scope)) && proposedScopes.every((scope) => currentScopes.includes(scope)); if (!isSameScopes) { - await this.request({ method: 'wallet_revokeSession', params: walletSession }); + await this.request({ method: 'wallet_revokeSession', params: walletSession }, this.#defaultRequestOptions); const optionalScopes = addValidAccounts(getOptionalScopes(options?.scopes ?? []), getValidAccounts(options?.caipAccountIds ?? [])); const sessionRequest: CreateSessionParams = { optionalScopes }; - const response = await this.request({ method: 'wallet_createSession', params: sessionRequest }); + const response = await this.request({ method: 'wallet_createSession', params: sessionRequest }, this.#defaultRequestOptions); + if (response.error) { + throw new Error(response.error.message); + } walletSession = response.result as SessionData; } - } else { + } else if (!walletSession) { const optionalScopes = addValidAccounts(getOptionalScopes(options?.scopes ?? []), getValidAccounts(options?.caipAccountIds ?? [])); const sessionRequest: CreateSessionParams = { optionalScopes }; - await this.request({ method: 'wallet_createSession', params: sessionRequest }); + const response = await this.request({ method: 'wallet_createSession', params: sessionRequest }, this.#defaultRequestOptions); + if (response.error) { + throw new Error(response.error.message); + } + walletSession = response.result as SessionData; } + this.#notifyCallbacks({ + method: 'wallet_sessionChanged', + params: walletSession, + }); } async disconnect(): Promise { + this.#notificationCallbacks.clear(); return this.#transport.disconnect(); } @@ -39,15 +68,14 @@ export class DefaultTransport implements ExtendedTransport { } async request(request: TRequest, options?: { timeout?: number }) { - const requestWithId = { - ...request, - jsonrpc: '2.0', - id: `${this.#requestId++}`, - }; - return this.#transport.request(requestWithId, options) as Promise; + return this.#transport.request(request, options) as Promise; } onNotification(callback: (data: unknown) => void) { - return this.#transport.onNotification(callback); + this.#transport.onNotification(callback); + this.#notificationCallbacks.add(callback); + return () => { + this.#notificationCallbacks.delete(callback); + }; } } diff --git a/packages/sdk-multichain/src/session.test.ts b/packages/sdk-multichain/src/session.test.ts index 9da35919d..862177407 100644 --- a/packages/sdk-multichain/src/session.test.ts +++ b/packages/sdk-multichain/src/session.test.ts @@ -98,18 +98,17 @@ function testSuite({ platform, createSDK, options: if (platform === 'web') { t.expect(mockedData.mockDefaultTransport.request).toHaveBeenCalledWith( t.expect.objectContaining({ - jsonrpc: '2.0', method: 'wallet_getSession', }), - undefined, + { timeout: 60 * 1000 }, ); t.expect(mockedData.mockDefaultTransport.request).toHaveBeenCalledWith( t.expect.objectContaining({ method: 'wallet_revokeSession', params: mockSessionData, }), - undefined, + { timeout: 60 * 1000 }, ); t.expect(mockedData.mockDefaultTransport.request).toHaveBeenCalledWith( @@ -119,7 +118,7 @@ function testSuite({ platform, createSDK, options: optionalScopes: mockedSessionUpgradeData.sessionScopes, }, }), - undefined, + { timeout: 60 * 1000 }, ); } else { t.expect(mockedData.mockDappClient.sendRequest).toHaveBeenCalledWith( @@ -168,10 +167,9 @@ function testSuite({ platform, createSDK, options: if (platform === 'web') { t.expect(mockedData.mockDefaultTransport.request).toHaveBeenCalledWith( t.expect.objectContaining({ - jsonrpc: '2.0', method: 'wallet_getSession', }), - undefined, + { timeout: 60 * 1000 }, ); t.expect(mockedData.mockDefaultTransport.request).toHaveBeenCalledWith( t.expect.objectContaining({ @@ -180,7 +178,7 @@ function testSuite({ platform, createSDK, options: optionalScopes: mockSessionData.sessionScopes, }, }), - undefined, + { timeout: 60 * 1000 }, ); } else { t.expect(mockedData.mockDappClient.sendRequest).toHaveBeenCalledWith( diff --git a/packages/sdk-multichain/src/ui/index.test.ts b/packages/sdk-multichain/src/ui/index.test.ts index dbe761012..4b0f64a7c 100644 --- a/packages/sdk-multichain/src/ui/index.test.ts +++ b/packages/sdk-multichain/src/ui/index.test.ts @@ -330,7 +330,7 @@ t.describe('ModalFactory', () => { await constructorArgs.onClose(); // Multichain SDK is what will close the modal instead - t.expect(mockModal.unmount).not.toHaveBeenCalled(); + t.expect(mockModal.unmount).toHaveBeenCalled(); }); t.it('should handle desktop onboarding correctly', async () => {