From 13f0b5aadfab638711ad4dc9de25baef6268a3dd Mon Sep 17 00:00:00 2001 From: Jungho Bang Date: Wed, 15 Nov 2023 21:04:31 -0500 Subject: [PATCH] Refactor data models to improve type safety and code usability (#1064) * refactor ServerMessage type * apply it to ClientMessage * apply changes * lfg * refactoring Web3Request * Web3Response * help git * improvements * introducing SupportedWeb3Methods * cleanup RelayMessage * apply Web3Response changes * no need to be exported RelayMessageType * sync * comment * support case of request and response having different method * consolidate RelayMessage * wtf * no error on relay * fix provider * cleaner * rename relay message to be `WalletLinkEventData` * fix tests * revert rename * clarify param * apply change to selectProvider * WalletLinkEventType * cleanup * fi xWalletUI * cleanup * apply to HTTP API --- .../src/connection/ClientMessage.ts | 100 ++--- .../src/connection/DiagnosticLogger.ts | 9 +- .../src/connection/ServerMessage.ts | 143 +++---- .../src/connection/WalletLinkConnection.ts | 106 +++-- .../src/connection/WalletLinkHTTP.test.ts | 4 +- .../src/connection/WalletLinkHTTP.ts | 8 +- .../connection/WalletLinkWebSocket.test.ts | 9 +- .../wallet-sdk/src/errors/serialize.test.ts | 17 +- packages/wallet-sdk/src/errors/serialize.ts | 6 +- packages/wallet-sdk/src/errors/utils.test.ts | 7 +- packages/wallet-sdk/src/mocks/relay.ts | 100 ++--- .../provider/CoinbaseWalletProvider.test.ts | 14 +- .../src/provider/CoinbaseWalletProvider.ts | 70 ++-- .../wallet-sdk/src/provider/MobileRelayUI.ts | 88 +--- .../src/provider/WalletLinkRelayUI.ts | 85 +--- packages/wallet-sdk/src/provider/WalletUI.ts | 30 +- packages/wallet-sdk/src/relay/MobileRelay.ts | 23 +- packages/wallet-sdk/src/relay/RelayMessage.ts | 41 +- .../src/relay/WalletLinkRelay.test.ts | 16 +- .../wallet-sdk/src/relay/WalletLinkRelay.ts | 383 +++++++++--------- .../src/relay/WalletSDKRelayAbstract.ts | 48 +-- packages/wallet-sdk/src/relay/Web3Method.ts | 31 +- packages/wallet-sdk/src/relay/Web3Request.ts | 284 ++++++------- .../src/relay/Web3RequestCanceledMessage.ts | 13 - .../src/relay/Web3RequestMessage.ts | 14 - packages/wallet-sdk/src/relay/Web3Response.ts | 198 ++++----- .../src/relay/Web3ResponseMessage.ts | 21 - 27 files changed, 764 insertions(+), 1104 deletions(-) delete mode 100644 packages/wallet-sdk/src/relay/Web3RequestCanceledMessage.ts delete mode 100644 packages/wallet-sdk/src/relay/Web3RequestMessage.ts delete mode 100644 packages/wallet-sdk/src/relay/Web3ResponseMessage.ts diff --git a/packages/wallet-sdk/src/connection/ClientMessage.ts b/packages/wallet-sdk/src/connection/ClientMessage.ts index 58d7fe292b..2f537a0bfa 100644 --- a/packages/wallet-sdk/src/connection/ClientMessage.ts +++ b/packages/wallet-sdk/src/connection/ClientMessage.ts @@ -3,70 +3,36 @@ import { IntNumber } from '../types'; -export interface ClientMessage { - type: string; - id: IntNumber; -} - -export interface ClientMessageHostSession extends ClientMessage { - type: 'HostSession'; - sessionId: string; - sessionKey: string; -} - -export function ClientMessageHostSession( - params: Omit -): ClientMessageHostSession { - return { type: 'HostSession', ...params }; -} - -export interface ClientMessageIsLinked extends ClientMessage { - type: 'IsLinked'; - sessionId: string; -} - -export function ClientMessageIsLinked( - params: Omit -): ClientMessageIsLinked { - return { type: 'IsLinked', ...params }; -} - -export interface ClientMessageGetSessionConfig extends ClientMessage { - type: 'GetSessionConfig'; - sessionId: string; -} - -export function ClientMessageGetSessionConfig( - params: Omit -): ClientMessageGetSessionConfig { - return { type: 'GetSessionConfig', ...params }; -} - -export interface ClientMessageSetSessionConfig extends ClientMessage { - type: 'SetSessionConfig'; - id: IntNumber; - sessionId: string; - webhookId?: string | null; - webhookUrl?: string | null; - metadata?: { [key: string]: string | null }; -} - -export function ClientMessageSetSessionConfig( - params: Omit -): ClientMessageSetSessionConfig { - return { type: 'SetSessionConfig', ...params }; -} - -export interface ClientMessagePublishEvent extends ClientMessage { - type: 'PublishEvent'; - sessionId: string; - event: string; - data: string; - callWebhook: boolean; -} - -export function ClientMessagePublishEvent( - params: Omit -): ClientMessagePublishEvent { - return { type: 'PublishEvent', ...params }; -} +export type ClientMessage = + | { + type: 'HostSession'; + id: IntNumber; + sessionId: string; + sessionKey: string; + } + | { + type: 'IsLinked'; + id: IntNumber; + sessionId: string; + } + | { + type: 'GetSessionConfig'; + id: IntNumber; + sessionId: string; + } + | { + type: 'SetSessionConfig'; + id: IntNumber; + sessionId: string; + webhookId?: string | null; + webhookUrl?: string | null; + metadata?: { [key: string]: string | null }; + } + | { + type: 'PublishEvent'; + id: IntNumber; + sessionId: string; + event: string; + data: string; + callWebhook: boolean; + }; diff --git a/packages/wallet-sdk/src/connection/DiagnosticLogger.ts b/packages/wallet-sdk/src/connection/DiagnosticLogger.ts index 4824b524be..13c576a41b 100644 --- a/packages/wallet-sdk/src/connection/DiagnosticLogger.ts +++ b/packages/wallet-sdk/src/connection/DiagnosticLogger.ts @@ -1,16 +1,15 @@ // DiagnosticLogger for debugging purposes only -import { Web3RequestMessage } from '../relay/Web3RequestMessage'; -import { Web3ResponseMessage } from '../relay/Web3ResponseMessage'; -import { ServerMessage, ServerMessageIsLinkedOK } from './ServerMessage'; +import { WalletLinkEventData } from '../relay/RelayMessage'; +import { ServerMessage } from './ServerMessage'; import { ConnectionState } from './WalletLinkWebSocket'; export type LogProperties = { addresses_length?: number; // number of eth addresses alreadyDestroyed?: boolean; // error flag if metadata is already destroyed on resetAndReload - eventId?: Web3RequestMessage['id'] | Web3ResponseMessage['id']; + eventId?: WalletLinkEventData['id']; isSessionMismatched?: string; // storedSession does not match sessionId - linked?: ServerMessageIsLinkedOK['linked']; + linked?: ServerMessage<'IsLinkedOK'>['linked']; message?: string; // error message metadata_keys?: string[]; // session config metadata keys method?: string; // method throwing error message diff --git a/packages/wallet-sdk/src/connection/ServerMessage.ts b/packages/wallet-sdk/src/connection/ServerMessage.ts index 86dc376765..61250bb60f 100644 --- a/packages/wallet-sdk/src/connection/ServerMessage.ts +++ b/packages/wallet-sdk/src/connection/ServerMessage.ts @@ -3,87 +3,66 @@ import { IntNumber } from '../types'; -export interface ServerMessage { - type: string; - id?: IntNumber; -} +export type ServerMessage = Extract<_ServerMessage, { type: T }>; -export interface ServerMessageOK extends ServerMessage { - type: 'OK'; - id: IntNumber; - sessionId: string; -} +export type ServerMessageType = Type; +type Type = _ServerMessage['type']; -export interface ServerMessageFail extends ServerMessage { - type: 'Fail'; - id: IntNumber; - sessionId: string; - error: string; -} - -export function isServerMessageFail(msg: any): msg is ServerMessageFail { - return ( - msg && - msg.type === 'Fail' && - typeof msg.id === 'number' && - typeof msg.sessionId === 'string' && - typeof msg.error === 'string' - ); -} - -export interface ServerMessageIsLinkedOK extends ServerMessage { - type: 'IsLinkedOK'; - id: IntNumber; - sessionId: string; - linked: boolean; - onlineGuests: number; -} - -export interface ServerMessageLinked extends ServerMessage { - type: 'Linked'; - sessionId: string; - onlineGuests: number; -} - -export interface ServerMessageGetSessionConfigOK extends ServerMessage { - type: 'GetSessionConfigOK'; - id: IntNumber; - sessionId: string; - webhookId: string; - webhookUrl: string; - metadata: { [field: string]: string }; -} - -export interface ServerMessageSessionConfigUpdated extends ServerMessage { - type: 'SessionConfigUpdated'; - sessionId: string; - webhookId: string; - webhookUrl: string; - metadata: { [field: string]: string }; -} - -export interface ServerMessagePublishEventOK extends ServerMessage { - type: 'PublishEventOK'; - id: IntNumber; - sessionId: string; - eventId: string; -} - -export interface ServerMessageEvent extends ServerMessage { - type: 'Event'; - sessionId: string; - eventId: string; - event: string; - data: string; -} - -export function isServerMessageEvent(msg: any): msg is ServerMessageEvent { - return ( - msg && - msg.type === 'Event' && - typeof msg.sessionId === 'string' && - typeof msg.eventId === 'string' && - typeof msg.event === 'string' && - typeof msg.data === 'string' - ); -} +type _ServerMessage = + | { + type: 'Heartbeat'; // TODO: remove. it's client side only virtual message. + } + | { + type: 'OK'; + id: IntNumber; + sessionId: string; + } + | { + type: 'Fail'; + id: IntNumber; + sessionId: string; + error: string; + } + | { + type: 'IsLinkedOK'; + id: IntNumber; + sessionId: string; + linked: boolean; + onlineGuests: number; + } + | { + type: 'Linked'; + id?: IntNumber; + sessionId: string; + onlineGuests: number; + } + | { + type: 'GetSessionConfigOK'; + id: IntNumber; + sessionId: string; + webhookId: string; + webhookUrl: string; + metadata: { [field: string]: string }; + } + | { + type: 'SessionConfigUpdated'; + id?: IntNumber; + sessionId: string; + webhookId: string; + webhookUrl: string; + metadata: { [field: string]: string }; + } + | { + type: 'PublishEventOK'; + id: IntNumber; + sessionId: string; + eventId: string; + } + | { + type: 'Event'; + id?: IntNumber; + sessionId: string; + eventId: string; + event: string; + data: string; + }; diff --git a/packages/wallet-sdk/src/connection/WalletLinkConnection.ts b/packages/wallet-sdk/src/connection/WalletLinkConnection.ts index 6679d678f3..22d6449daf 100644 --- a/packages/wallet-sdk/src/connection/WalletLinkConnection.ts +++ b/packages/wallet-sdk/src/connection/WalletLinkConnection.ts @@ -1,32 +1,13 @@ // Copyright (c) 2018-2023 Coinbase, Inc. // Licensed under the Apache License, version 2.0 -import { RelayMessage } from '../relay/RelayMessage'; +import { WalletLinkEventData, WalletLinkResponseEventData } from '../relay/RelayMessage'; import { Session } from '../relay/Session'; import { APP_VERSION_KEY, WALLET_USER_NAME_KEY } from '../relay/WalletSDKRelayAbstract'; -import { isWeb3ResponseMessage, Web3ResponseMessage } from '../relay/Web3ResponseMessage'; import { IntNumber } from '../types'; -import { - ClientMessage, - ClientMessageGetSessionConfig, - ClientMessageHostSession, - ClientMessageIsLinked, - ClientMessagePublishEvent, - ClientMessageSetSessionConfig, -} from './ClientMessage'; +import { ClientMessage } from './ClientMessage'; import { DiagnosticLogger, EVENTS } from './DiagnosticLogger'; -import { - isServerMessageEvent, - isServerMessageFail, - ServerMessage, - ServerMessageFail, - ServerMessageGetSessionConfigOK, - ServerMessageIsLinkedOK, - ServerMessageLinked, - ServerMessageOK, - ServerMessagePublishEventOK, - ServerMessageSessionConfigUpdated, -} from './ServerMessage'; +import { ServerMessage, ServerMessageType } from './ServerMessage'; import { SessionConfig } from './SessionConfig'; import { WalletLinkConnectionCipher } from './WalletLinkConnectionCipher'; import { WalletLinkHTTP } from './WalletLinkHTTP'; @@ -38,7 +19,7 @@ const REQUEST_TIMEOUT = 60000; export interface WalletLinkConnectionUpdateListener { linkedUpdated: (linked: boolean) => void; connectedUpdated: (connected: boolean) => void; - handleWeb3ResponseMessage: (message: Web3ResponseMessage) => void; + handleWeb3ResponseMessage: (message: WalletLinkResponseEventData) => void; chainUpdated: (chainId: string, jsonRpcUrl: string) => void; accountUpdated: (selectedAddress: string) => void; metadataUpdated: (key: string, metadataValue: string) => void; @@ -162,28 +143,26 @@ export class WalletLinkConnection { // handle link status updates case 'IsLinkedOK': case 'Linked': { - const msg = m as Omit & ServerMessageLinked; + const linked = m.type === 'IsLinkedOK' ? m.linked : undefined; this.diagnostic?.log(EVENTS.LINKED, { sessionIdHash: Session.hash(session.id), - linked: msg.linked, + linked, type: m.type, - onlineGuests: msg.onlineGuests, + onlineGuests: m.onlineGuests, }); - this.linked = msg.linked || msg.onlineGuests > 0; + this.linked = linked || m.onlineGuests > 0; break; } // handle session config updates case 'GetSessionConfigOK': case 'SessionConfigUpdated': { - const msg = m as Omit & - ServerMessageSessionConfigUpdated; this.diagnostic?.log(EVENTS.SESSION_CONFIG_RECEIVED, { sessionIdHash: Session.hash(session.id), - metadata_keys: msg && msg.metadata ? Object.keys(msg.metadata) : undefined, + metadata_keys: m && m.metadata ? Object.keys(m.metadata) : undefined, }); - this.handleSessionMetadataUpdated(msg.metadata); + this.handleSessionMetadataUpdated(m.metadata); break; } @@ -298,7 +277,7 @@ export class WalletLinkConnection { } private async handleIncomingEvent(m: ServerMessage) { - if (!isServerMessageEvent(m) || m.event !== 'Web3Response') { + if (m.type !== 'Event' || m.event !== 'Web3Response') { return; } @@ -306,7 +285,7 @@ export class WalletLinkConnection { const decryptedData = await this.cipher.decrypt(m.data); const message = JSON.parse(decryptedData); - if (!isWeb3ResponseMessage(message)) return; + if (message.type !== 'WEB3_RESPONSE') return; this.listener?.handleWeb3ResponseMessage(message); } catch { @@ -347,15 +326,16 @@ export class WalletLinkConnection { * @returns a Promise that completes when successful */ public async setSessionMetadata(key: string, value: string | null) { - const message = ClientMessageSetSessionConfig({ + const message: ClientMessage = { + type: 'SetSessionConfig', id: IntNumber(this.nextReqId++), sessionId: this.session.id, metadata: { [key]: value }, - }); + }; return this.setOnceConnected(async () => { - const res = await this.makeRequest(message); - if (isServerMessageFail(res)) { + const res = await this.makeRequest<'OK' | 'Fail'>(message); + if (res.type === 'Fail') { throw new Error(res.error || 'failed to set session metadata'); } }); @@ -364,30 +344,35 @@ export class WalletLinkConnection { /** * Publish an event and emit event ID when successful * @param event event name - * @param unencryptedMessage unencrypted event message + * @param unencryptedData unencrypted event data * @param callWebhook whether the webhook should be invoked * @returns a Promise that emits event ID when successful */ - public async publishEvent(event: string, unencryptedMessage: RelayMessage, callWebhook = false) { + public async publishEvent( + event: string, + unencryptedData: WalletLinkEventData, + callWebhook = false + ) { const data = await this.cipher.encrypt( JSON.stringify({ - ...unencryptedMessage, + ...unencryptedData, origin: location.origin, relaySource: window.coinbaseWalletExtension ? 'injected_sdk' : 'sdk', }) ); - const message = ClientMessagePublishEvent({ + const message: ClientMessage = { + type: 'PublishEvent', id: IntNumber(this.nextReqId++), sessionId: this.session.id, event, data, callWebhook, - }); + }; return this.setOnceLinked(async () => { - const res = await this.makeRequest(message); - if (isServerMessageFail(res)) { + const res = await this.makeRequest<'PublishEventOK' | 'Fail'>(message); + if (res.type === 'Fail') { throw new Error(res.error || 'failed to publish event'); } return res.eventId; @@ -416,25 +401,25 @@ export class WalletLinkConnection { private requestResolutions = new Map void>(); - private async makeRequest( + private async makeRequest>( message: ClientMessage, timeout: number = REQUEST_TIMEOUT - ): Promise { + ): Promise { const reqId = message.id; this.sendData(message); // await server message with corresponding id let timeoutId: number; return Promise.race([ - new Promise((_, reject) => { + new Promise((_, reject) => { timeoutId = window.setTimeout(() => { reject(new Error(`request ${reqId} timed out`)); }, timeout); }), - new Promise((resolve) => { + new Promise((resolve) => { this.requestResolutions.set(reqId, (m) => { clearTimeout(timeoutId); // clear the timeout - resolve(m as T); + resolve(m as M); this.requestResolutions.delete(reqId); }); }), @@ -442,31 +427,34 @@ export class WalletLinkConnection { } private async authenticate() { - const msg = ClientMessageHostSession({ + const m: ClientMessage = { + type: 'HostSession', id: IntNumber(this.nextReqId++), sessionId: this.session.id, sessionKey: this.session.key, - }); - const res = await this.makeRequest(msg); - if (isServerMessageFail(res)) { + }; + const res = await this.makeRequest<'OK' | 'Fail'>(m); + if (res.type === 'Fail') { throw new Error(res.error || 'failed to authentcate'); } } private sendIsLinked(): void { - const msg = ClientMessageIsLinked({ + const m: ClientMessage = { + type: 'IsLinked', id: IntNumber(this.nextReqId++), sessionId: this.session.id, - }); - this.sendData(msg); + }; + this.sendData(m); } private sendGetSessionConfig(): void { - const msg = ClientMessageGetSessionConfig({ + const m: ClientMessage = { + type: 'GetSessionConfig', id: IntNumber(this.nextReqId++), sessionId: this.session.id, - }); - this.sendData(msg); + }; + this.sendData(m); } private handleSessionMetadataUpdated = (metadata: SessionConfig['metadata']) => { diff --git a/packages/wallet-sdk/src/connection/WalletLinkHTTP.test.ts b/packages/wallet-sdk/src/connection/WalletLinkHTTP.test.ts index f755e19a1b..162c884def 100644 --- a/packages/wallet-sdk/src/connection/WalletLinkHTTP.test.ts +++ b/packages/wallet-sdk/src/connection/WalletLinkHTTP.test.ts @@ -1,4 +1,4 @@ -import { ServerMessageEvent } from './ServerMessage'; +import { ServerMessage } from './ServerMessage'; import { WalletLinkHTTP } from './WalletLinkHTTP'; describe('WalletLinkHTTP', () => { @@ -82,7 +82,7 @@ describe('WalletLinkHTTP', () => { describe('markUnseenEventsAsSeen', () => { it('should mark all unseen events as seen', () => { const walletLinkHTTP = new WalletLinkHTTP(linkAPIUrl, sessionId, sessionKey); - const unseenEvents: ServerMessageEvent[] = [ + const unseenEvents: ServerMessage[] = [ { type: 'Event', sessionId: '1', diff --git a/packages/wallet-sdk/src/connection/WalletLinkHTTP.ts b/packages/wallet-sdk/src/connection/WalletLinkHTTP.ts index fd5bf3d5be..eac33e3534 100644 --- a/packages/wallet-sdk/src/connection/WalletLinkHTTP.ts +++ b/packages/wallet-sdk/src/connection/WalletLinkHTTP.ts @@ -1,4 +1,4 @@ -import { ServerMessageEvent } from './ServerMessage'; +import { ServerMessage } from './ServerMessage'; export class WalletLinkHTTP { private readonly auth: string; @@ -13,7 +13,7 @@ export class WalletLinkHTTP { } // mark unseen events as seen - private async markUnseenEventsAsSeen(events: ServerMessageEvent[]) { + private async markUnseenEventsAsSeen(events: ServerMessage<'Event'>[]) { return Promise.all( events.map((e) => fetch(`${this.linkAPIUrl}/events/${e.eventId}/seen`, { @@ -26,7 +26,7 @@ export class WalletLinkHTTP { ).catch((error) => console.error('Unabled to mark event as failed:', error)); } - async fetchUnseenEvents(): Promise { + async fetchUnseenEvents(): Promise[]> { const response = await fetch(`${this.linkAPIUrl}/events?unseen=true`, { headers: { Authorization: this.auth, @@ -48,7 +48,7 @@ export class WalletLinkHTTP { throw new Error(`Check unseen events failed: ${error}`); } - const responseEvents: ServerMessageEvent[] = + const responseEvents: ServerMessage<'Event'>[] = events ?.filter((e) => e.event === 'Web3Response') .map((e) => ({ diff --git a/packages/wallet-sdk/src/connection/WalletLinkWebSocket.test.ts b/packages/wallet-sdk/src/connection/WalletLinkWebSocket.test.ts index ea654d76ab..fc3db859ea 100644 --- a/packages/wallet-sdk/src/connection/WalletLinkWebSocket.test.ts +++ b/packages/wallet-sdk/src/connection/WalletLinkWebSocket.test.ts @@ -1,5 +1,7 @@ import WS from 'jest-websocket-mock'; +import { IntNumber } from '../types'; +import { ServerMessage } from './ServerMessage'; import { ConnectionState, WalletLinkWebSocket } from './WalletLinkWebSocket'; describe('WalletLinkWebSocket', () => { @@ -71,9 +73,10 @@ describe('WalletLinkWebSocket', () => { await rxWS.connect(); await server.connected; - const message = { - type: 'ServerMessageType', - data: 'hello world', + const message: ServerMessage = { + type: 'OK', + id: IntNumber(1), + sessionId: '123', }; server.send(JSON.stringify(message)); diff --git a/packages/wallet-sdk/src/errors/serialize.test.ts b/packages/wallet-sdk/src/errors/serialize.test.ts index e229085005..70b5f2221f 100644 --- a/packages/wallet-sdk/src/errors/serialize.test.ts +++ b/packages/wallet-sdk/src/errors/serialize.test.ts @@ -1,14 +1,13 @@ import { JSONRPCRequest } from '../provider/JSONRPC'; -import { Web3Method } from '../relay/Web3Method'; -import { ErrorResponse } from '../relay/Web3Response'; +import { Web3Response } from '../relay/Web3Response'; import { standardErrorCodes } from './constants'; import { standardErrors } from './errors'; import { serializeError } from './serialize'; describe('serializeError', () => { test('with ErrorResponse object', () => { - const errorResponse: ErrorResponse = { - method: Web3Method.generic, + const errorResponse: Web3Response = { + method: 'generic', errorMessage: 'test ErrorResponse object', errorCode: standardErrorCodes.provider.unsupportedMethod, }; @@ -78,7 +77,7 @@ describe('serializeError to retrieve the request method', () => { const jsonRpcRequest: JSONRPCRequest = { jsonrpc: '2.0', id: 1, - method: Web3Method.requestEthereumAccounts, + method: 'requestEthereumAccounts', params: [], }; @@ -86,7 +85,7 @@ describe('serializeError to retrieve the request method', () => { const serialized = serializeError(error, jsonRpcRequest); expect(serialized.code).toEqual(standardErrorCodes.provider.userRejectedRequest); - expect(serialized.docUrl).toContain(`method=${Web3Method.requestEthereumAccounts}`); + expect(serialized.docUrl).toContain('method=requestEthereumAccounts'); }); test('with string', () => { @@ -104,13 +103,13 @@ describe('serializeError to retrieve the request method', () => { { jsonrpc: '2.0', id: 1, - method: Web3Method.requestEthereumAccounts, + method: 'requestEthereumAccounts', params: [], }, { jsonrpc: '2.0', id: 1, - method: Web3Method.signEthereumMessage, + method: 'signEthereumMessage', params: [], }, ]; @@ -119,6 +118,6 @@ describe('serializeError to retrieve the request method', () => { const serialized = serializeError(error, jsonRpcRequests); expect(serialized.code).toEqual(standardErrorCodes.provider.userRejectedRequest); - expect(serialized.docUrl).toContain(`method=${Web3Method.requestEthereumAccounts}`); + expect(serialized.docUrl).toContain('method=requestEthereumAccounts'); }); }); diff --git a/packages/wallet-sdk/src/errors/serialize.ts b/packages/wallet-sdk/src/errors/serialize.ts index c0eda975d6..87c81a2423 100644 --- a/packages/wallet-sdk/src/errors/serialize.ts +++ b/packages/wallet-sdk/src/errors/serialize.ts @@ -1,4 +1,4 @@ -import { isErrorResponse } from '../relay/Web3Response'; +import { isErrorResponse, Web3Response } from '../relay/Web3Response'; import { LIB_VERSION } from '../version'; import { standardErrorCodes } from './constants'; import { serialize, SerializedEthereumRpcError } from './utils'; @@ -34,7 +34,7 @@ export function serializeError( /** * Converts an error to a serializable object. */ -function getErrorObject(error: unknown) { +function getErrorObject(error: string | Web3Response | unknown) { if (typeof error === 'string') { return { message: error, @@ -45,7 +45,7 @@ function getErrorObject(error: unknown) { ...error, message: error.errorMessage, code: error.errorCode, - data: { method: error.method, result: error.result }, + data: { method: error.method }, }; } return error; diff --git a/packages/wallet-sdk/src/errors/utils.test.ts b/packages/wallet-sdk/src/errors/utils.test.ts index 13fb46af10..9b901398b9 100644 --- a/packages/wallet-sdk/src/errors/utils.test.ts +++ b/packages/wallet-sdk/src/errors/utils.test.ts @@ -1,5 +1,4 @@ -import { Web3Method } from '../relay/Web3Method'; -import { ErrorResponse, isErrorResponse } from '../relay/Web3Response'; +import { isErrorResponse, Web3Response } from '../relay/Web3Response'; import { standardErrorCodes } from './constants'; import { standardErrors } from './errors'; import { getErrorCode, getMessageFromCode } from './utils'; @@ -24,8 +23,8 @@ describe('errors', () => { expect(getErrorCode(null)).toEqual(undefined); expect(getErrorCode(undefined)).toEqual(undefined); - const errorResponse: ErrorResponse = { - method: Web3Method.generic, + const errorResponse: Web3Response = { + method: 'generic', errorMessage: 'test error message', errorCode: 4137, }; diff --git a/packages/wallet-sdk/src/mocks/relay.ts b/packages/wallet-sdk/src/mocks/relay.ts index 64f3b7201e..b5169608de 100644 --- a/packages/wallet-sdk/src/mocks/relay.ts +++ b/packages/wallet-sdk/src/mocks/relay.ts @@ -1,26 +1,13 @@ import { MOCK_ADDERESS, MOCK_TX } from '../fixtures/provider'; import { ScopedLocalStorage } from '../lib/ScopedLocalStorage'; import { Session } from '../relay/Session'; -import { CancelablePromise, WalletSDKRelayAbstract } from '../relay/WalletSDKRelayAbstract'; -import { Web3Method } from '../relay/Web3Method'; -import { - AddEthereumChainResponse, - EthereumAddressFromSignedMessageResponse, - GenericResponse, - RequestEthereumAccountsResponse, - ScanQRCodeResponse, - SelectProviderResponse, - SignEthereumMessageResponse, - SignEthereumTransactionResponse, - SubmitEthereumTransactionResponse, - SwitchEthereumChainResponse, - WatchAssetResponse, - Web3Response, -} from '../relay/Web3Response'; +import { WalletSDKRelayAbstract } from '../relay/WalletSDKRelayAbstract'; +import { Web3Method as SupportedWeb3Method, Web3Method } from '../relay/Web3Method'; +import { Web3Response } from '../relay/Web3Response'; import { AddressString, HexString, ProviderType } from '../types'; -function makeMockReturn(returnValue?: T) { - return { cancel: () => {}, promise: Promise.resolve(returnValue as T) }; +function makeMockReturn(response: Web3Response) { + return { cancel: () => {}, promise: Promise.resolve>(response) }; } export class MockRelayClass extends WalletSDKRelayAbstract { @@ -31,16 +18,16 @@ export class MockRelayClass extends WalletSDKRelayAbstract { resetAndReload(): void {} - requestEthereumAccounts(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.requestEthereumAccounts, + requestEthereumAccounts() { + return makeMockReturn({ + method: 'requestEthereumAccounts', result: [AddressString(MOCK_ADDERESS)], }); } - addEthereumChain(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.addEthereumChain, + addEthereumChain() { + return makeMockReturn({ + method: 'addEthereumChain', result: { isApproved: true, rpcUrl: 'https://node.ethchain.com', @@ -48,23 +35,23 @@ export class MockRelayClass extends WalletSDKRelayAbstract { }); } - watchAsset(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.watchAsset, + watchAsset() { + return makeMockReturn({ + method: 'watchAsset', result: true, }); } - selectProvider(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.selectProvider, + selectProvider() { + return makeMockReturn({ + method: 'selectProvider', result: ProviderType.CoinbaseWallet, }); } - switchEthereumChain(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.switchEthereumChain, + switchEthereumChain() { + return makeMockReturn({ + method: 'switchEthereumChain', result: { isApproved: true, rpcUrl: 'https://node.ethchain.com', @@ -72,54 +59,57 @@ export class MockRelayClass extends WalletSDKRelayAbstract { }); } - signEthereumMessage(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.signEthereumMessage, + signEthereumMessage() { + return makeMockReturn({ + method: 'signEthereumMessage', result: HexString('0x'), }); } - ethereumAddressFromSignedMessage(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.ethereumAddressFromSignedMessage, + ethereumAddressFromSignedMessage() { + return makeMockReturn({ + method: 'ethereumAddressFromSignedMessage', result: AddressString(MOCK_ADDERESS), }); } - signEthereumTransaction(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.signEthereumTransaction, + signEthereumTransaction() { + return makeMockReturn({ + method: 'signEthereumTransaction', result: HexString(MOCK_TX), }); } - signAndSubmitEthereumTransaction(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.submitEthereumTransaction, + signAndSubmitEthereumTransaction() { + return makeMockReturn({ + method: 'submitEthereumTransaction', result: HexString(MOCK_TX), }); } - submitEthereumTransaction(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.submitEthereumTransaction, + submitEthereumTransaction() { + return makeMockReturn({ + method: 'submitEthereumTransaction', result: HexString(MOCK_TX), }); } - scanQRCode(): CancelablePromise { - return makeMockReturn(); + scanQRCode() { + return makeMockReturn({ + method: 'scanQRCode', + result: 'Success', + }); } - genericRequest(): CancelablePromise { - return makeMockReturn({ - method: Web3Method.generic, + genericRequest() { + return makeMockReturn({ + method: 'generic', result: 'Success', }); } - sendRequest<_, U extends Web3Response>(): CancelablePromise { - return makeMockReturn(); + sendRequest<_, T extends Web3Method>() { + return { cancel: () => {}, promise: Promise.reject>() }; } setAppInfo() { diff --git a/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.test.ts b/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.test.ts index 00104a580e..8cc12365d6 100644 --- a/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.test.ts +++ b/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.test.ts @@ -6,7 +6,6 @@ import { ScopedLocalStorage } from '../lib/ScopedLocalStorage'; import { MockRelayClass } from '../mocks/relay'; import { LOCAL_STORAGE_ADDRESSES_KEY } from '../relay/WalletSDKRelayAbstract'; import { WalletSDKRelayEventManager } from '../relay/WalletSDKRelayEventManager'; -import { Web3Method } from '../relay/Web3Method'; import { ProviderType } from '../types'; import { CoinbaseWalletProvider, CoinbaseWalletProviderOptions } from './CoinbaseWalletProvider'; @@ -206,7 +205,7 @@ describe('CoinbaseWalletProvider', () => { jest.spyOn(relay, 'genericRequest').mockReturnValue({ cancel: () => {}, promise: Promise.resolve({ - method: Web3Method.generic, + method: 'generic', result: 'Success', }), }); @@ -229,7 +228,7 @@ describe('CoinbaseWalletProvider', () => { const provider = setupCoinbaseWalletProvider(); // @ts-expect-error _addresses is private - expect(provider._addresses).toEqual([]) + expect(provider._addresses).toEqual([]); const url = 'dapp.finance'; Object.defineProperty(window, 'location', { value: { origin: url } }); @@ -249,17 +248,16 @@ describe('CoinbaseWalletProvider', () => { ); // @ts-expect-error _addresses is private - expect(provider._addresses).toEqual([]) + expect(provider._addresses).toEqual([]); }); - it('handles error responses with generic requests', async () => { const relay = new MockRelayClass(); jest.spyOn(relay, 'genericRequest').mockReturnValue({ cancel: () => {}, // @ts-expect-error result should be a string promise: Promise.resolve({ - method: Web3Method.generic, + method: 'generic', result: { foo: 'bar' }, }), }); @@ -359,7 +357,7 @@ describe('CoinbaseWalletProvider', () => { cancel: () => {}, // @ts-expect-error result should be a string promise: Promise.resolve({ - method: Web3Method.scanQRCode, + method: 'scanQRCode', result: { foo: 'bar' }, }), }); @@ -378,7 +376,7 @@ describe('CoinbaseWalletProvider', () => { jest.spyOn(relay, 'scanQRCode').mockReturnValue({ cancel: () => {}, promise: Promise.resolve({ - method: Web3Method.scanQRCode, + method: 'scanQRCode', result: 'cbwallet://result', }), }); diff --git a/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.ts b/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.ts index e9fd5e36d4..4ba5e936eb 100644 --- a/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.ts +++ b/packages/wallet-sdk/src/provider/CoinbaseWalletProvider.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// TODO: Address linting issues + // Copyright (c) 2018-2023 Coinbase, Inc. // Licensed under the Apache License, version 2.0 @@ -15,13 +18,7 @@ import { WalletSDKRelayAbstract, } from '../relay/WalletSDKRelayAbstract'; import { WalletSDKRelayEventManager } from '../relay/WalletSDKRelayEventManager'; -import { Web3Method } from '../relay/Web3Method'; -import { - ConnectAndSignInResponse, - isErrorResponse, - RequestEthereumAccountsResponse, - SwitchResponse, -} from '../relay/Web3Response'; +import { isErrorResponse, Web3Response } from '../relay/Web3Response'; import { AddressString, Callback, HexString, IntNumber, ProviderType } from '../types'; import { ensureAddressString, @@ -179,9 +176,7 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider if (event.data.type !== 'walletLinkMessage') return; // compatibility with CBW extension - if ( - event.data.data.action === 'dappChainSwitched' - ) { + if (event.data.data.action === 'dappChainSwitched') { const _chainId = event.data.data.chainId; const jsonRpcUrl = event.data.data.jsonRpcUrl ?? this.jsonRpcUrl; this.updateProviderInfo(jsonRpcUrl, Number(_chainId)); @@ -281,6 +276,8 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider chainId?.toString() ).promise; + if (isErrorResponse(result)) return false; + return !!result.result; } @@ -316,6 +313,8 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider nativeCurrency ).promise; + if (isErrorResponse(res)) return false; + if (res.result?.isApproved === true) { this.updateProviderInfo(rpcUrls[0], chainId); } @@ -331,7 +330,8 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider ).promise; // backward compatibility - if (isErrorResponse(res) && res.errorCode) { + if (isErrorResponse(res)) { + if (!res.errorCode) return; if (res.errorCode === standardErrorCodes.provider.unsupportedChain) { throw standardErrors.provider.unsupportedChain(); } else { @@ -342,7 +342,7 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider } } - const switchResponse = res.result as SwitchResponse; + const switchResponse = res.result; if (switchResponse.isApproved && switchResponse.rpcUrl.length > 0) { this.updateProviderInfo(switchResponse.rpcUrl, chainId); } @@ -525,8 +525,10 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider public async scanQRCode(match?: RegExp): Promise { const relay = await this.initializeRelay(); const res = await relay.scanQRCode(ensureRegExpString(match)).promise; - if (typeof res.result !== 'string') { - throw serializeError(res.errorMessage ?? 'result was not a string', Web3Method.scanQRCode); + if (isErrorResponse(res)) { + throw serializeError(res.errorMessage, 'scanQRCode'); + } else if (typeof res.result !== 'string') { + throw serializeError('result was not a string', 'scanQRCode'); } return res.result; } @@ -534,8 +536,10 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider public async genericRequest(data: object, action: string): Promise { const relay = await this.initializeRelay(); const res = await relay.genericRequest(data, action).promise; - if (typeof res.result !== 'string') { - throw serializeError(res.errorMessage ?? 'result was not a string', Web3Method.generic); + if (isErrorResponse(res)) { + throw serializeError(res.errorMessage, 'generic'); + } else if (typeof res.result !== 'string') { + throw serializeError('result was not a string', 'generic'); } return res.result; } @@ -579,13 +583,16 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider sessionIdHash: this._relay ? Session.hash(this._relay.session.id) : undefined, }); - let res: ConnectAndSignInResponse; + let res: Web3Response<'connectAndSignIn'>; try { const relay = await this.initializeRelay(); if (!(relay instanceof MobileRelay)) { throw new Error('connectAndSignIn is only supported on mobile'); } res = await relay.connectAndSignIn(params).promise; + if (isErrorResponse(res)) { + throw new Error(res.errorMessage); + } } catch (err: any) { if (typeof err.message === 'string' && err.message.match(/(denied|rejected)/i)) { throw standardErrors.provider.userRejectedRequest('User denied account authorization'); @@ -610,11 +617,10 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider public async selectProvider(providerOptions: ProviderType[]): Promise { const relay = await this.initializeRelay(); const res = await relay.selectProvider(providerOptions).promise; - if (typeof res.result !== 'string') { - throw serializeError( - res.errorMessage ?? 'result was not a string', - Web3Method.selectProvider - ); + if (isErrorResponse(res)) { + throw serializeError(res.errorMessage, 'selectProvider'); + } else if (typeof res.result !== 'string') { + throw serializeError('result was not a string', 'selectProvider'); } return res.result; } @@ -942,6 +948,9 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider const relay = await this.initializeRelay(); const res = await relay.signEthereumMessage(message, address, addPrefix, typedDataJson) .promise; + if (isErrorResponse(res)) { + throw new Error(res.errorMessage); + } return { jsonrpc: '2.0', id: 0, result: res.result }; } catch (err: any) { if (typeof err.message === 'string' && err.message.match(/(denied|rejected)/i)) { @@ -958,6 +967,9 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider ): Promise { const relay = await this.initializeRelay(); const res = await relay.ethereumAddressFromSignedMessage(message, signature, addPrefix).promise; + if (isErrorResponse(res)) { + throw new Error(res.errorMessage); + } return { jsonrpc: '2.0', id: 0, result: res.result }; } @@ -1003,10 +1015,13 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider }); } - let res: RequestEthereumAccountsResponse; + let res: Web3Response<'requestEthereumAccounts'>; try { const relay = await this.initializeRelay(); res = await relay.requestEthereumAccounts().promise; + if (isErrorResponse(res)) { + throw new Error(res.errorMessage); + } } catch (err: any) { if (typeof err.message === 'string' && err.message.match(/(denied|rejected)/i)) { throw standardErrors.provider.userRejectedRequest('User denied account authorization'); @@ -1061,6 +1076,9 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider try { const relay = await this.initializeRelay(); const res = await relay.signEthereumTransaction(tx).promise; + if (isErrorResponse(res)) { + throw new Error(res.errorMessage); + } return { jsonrpc: '2.0', id: 0, result: res.result }; } catch (err: any) { if (typeof err.message === 'string' && err.message.match(/(denied|rejected)/i)) { @@ -1074,6 +1092,9 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider const signedTransaction = ensureBuffer(params[0]); const relay = await this.initializeRelay(); const res = await relay.submitEthereumTransaction(signedTransaction, this.getChainId()).promise; + if (isErrorResponse(res)) { + throw new Error(res.errorMessage); + } return { jsonrpc: '2.0', id: 0, result: res.result }; } @@ -1083,6 +1104,9 @@ export class CoinbaseWalletProvider extends EventEmitter implements Web3Provider try { const relay = await this.initializeRelay(); const res = await relay.signAndSubmitEthereumTransaction(tx).promise; + if (isErrorResponse(res)) { + throw new Error(res.errorMessage); + } return { jsonrpc: '2.0', id: 0, result: res.result }; } catch (err: any) { if (typeof err.message === 'string' && err.message.match(/(denied|rejected)/i)) { diff --git a/packages/wallet-sdk/src/provider/MobileRelayUI.ts b/packages/wallet-sdk/src/provider/MobileRelayUI.ts index 66bd1d942a..a98ac6cd69 100644 --- a/packages/wallet-sdk/src/provider/MobileRelayUI.ts +++ b/packages/wallet-sdk/src/provider/MobileRelayUI.ts @@ -1,18 +1,5 @@ import { RedirectDialog } from '../components/RedirectDialog/RedirectDialog'; import { ErrorHandler } from '../errors'; -import { - EthereumAddressFromSignedMessageRequest, - SignEthereumMessageRequest, - SignEthereumTransactionRequest, - SubmitEthereumTransactionRequest, -} from '../relay/Web3Request'; -import { - EthereumAddressFromSignedMessageResponse, - SignEthereumMessageResponse, - SignEthereumTransactionResponse, - SubmitEthereumTransactionResponse, -} from '../relay/Web3Response'; -import { AddressString, ProviderType } from '../types'; import { WalletUI, WalletUIOptions } from './WalletUI'; // TODO: Implement & present in-page wallet picker instead of navigating to www.coinbase.com/connect-dapp @@ -90,80 +77,35 @@ export class MobileRelayUI implements WalletUI { // -- Methods below are not needed for mobile - requestEthereumAccounts(_options: { - onCancel: ErrorHandler; - onAccounts?: ((accounts: [AddressString]) => void) | undefined; - }): void {} // no-op + requestEthereumAccounts() {} // no-op - addEthereumChain(_options: { - onCancel: ErrorHandler; - onApprove: (rpcUrl: string) => void; - chainId: string; - rpcUrls: string[]; - blockExplorerUrls?: string[] | undefined; - chainName?: string | undefined; - iconUrls?: string[] | undefined; - nativeCurrency?: { name: string; symbol: string; decimals: number } | undefined; - }): void {} // no-op - - watchAsset(_options: { - onCancel: ErrorHandler; - onApprove: () => void; - type: string; - address: string; - symbol?: string | undefined; - decimals?: number | undefined; - image?: string | undefined; - chainId?: string | undefined; - }): void {} // no-op - - selectProvider?(_options: { - onCancel: ErrorHandler; - onApprove: (selectedProviderKey: ProviderType) => void; - providerOptions: ProviderType[]; - }): void {} // no-op + addEthereumChain() {} // no-op - switchEthereumChain(_options: { - onCancel: ErrorHandler; - onApprove: (rpcUrl: string) => void; - chainId: string; - address?: string | undefined; - }): void {} // no-op - - signEthereumMessage(_options: { - request: SignEthereumMessageRequest; - onSuccess: (response: SignEthereumMessageResponse) => void; - onCancel: ErrorHandler; - }): void {} // no-op + watchAsset() {} // no-op - signEthereumTransaction(_options: { - request: SignEthereumTransactionRequest; - onSuccess: (response: SignEthereumTransactionResponse) => void; - onCancel: ErrorHandler; - }): void {} // no-op + selectProvider?() {} // no-op - submitEthereumTransaction(_options: { - request: SubmitEthereumTransactionRequest; - onSuccess: (response: SubmitEthereumTransactionResponse) => void; - onCancel: ErrorHandler; - }): void {} // no-op + switchEthereumChain() {} // no-op + + signEthereumMessage() {} // no-op + + signEthereumTransaction() {} // no-op + + submitEthereumTransaction() {} // no-op - ethereumAddressFromSignedMessage(_options: { - request: EthereumAddressFromSignedMessageRequest; - onSuccess: (response: EthereumAddressFromSignedMessageResponse) => void; - }): void {} // no-op + ethereumAddressFromSignedMessage() {} // no-op reloadUI() {} // no-op - setStandalone?(_status: boolean) {} // no-op + setStandalone?() {} // no-op - setConnectDisabled(_: boolean) {} // no-op + setConnectDisabled() {} // no-op inlineAccountsResponse(): boolean { return false; } - inlineAddEthereumChain(_chainId: string): boolean { + inlineAddEthereumChain(): boolean { return false; } diff --git a/packages/wallet-sdk/src/provider/WalletLinkRelayUI.ts b/packages/wallet-sdk/src/provider/WalletLinkRelayUI.ts index 0570a03372..fecad40743 100644 --- a/packages/wallet-sdk/src/provider/WalletLinkRelayUI.ts +++ b/packages/wallet-sdk/src/provider/WalletLinkRelayUI.ts @@ -2,18 +2,8 @@ import { LinkFlow } from '../components/LinkFlow/LinkFlow'; import { Snackbar, SnackbarInstanceProps } from '../components/Snackbar/Snackbar'; import { ErrorHandler } from '../errors'; import { injectCssReset } from '../lib/cssReset'; -import { - EthereumAddressFromSignedMessageRequest, - SignEthereumMessageRequest, - SignEthereumTransactionRequest, - SubmitEthereumTransactionRequest, -} from '../relay/Web3Request'; -import { - EthereumAddressFromSignedMessageResponse, - SignEthereumMessageResponse, - SignEthereumTransactionResponse, - SubmitEthereumTransactionResponse, -} from '../relay/Web3Response'; +import {} from '../relay/Web3Request'; +import {} from '../relay/Web3Response'; import { WalletUI, WalletUIOptions } from './WalletUI'; export class WalletLinkRelayUI implements WalletUI { @@ -66,45 +56,13 @@ export class WalletLinkRelayUI implements WalletUI { } /* istanbul ignore next */ - addEthereumChain(_options: { - onCancel: ErrorHandler; - onApprove: () => void; - chainId: string; - rpcUrls: string[]; - blockExplorerUrls?: string[]; - chainName?: string; - iconUrls?: string[]; - nativeCurrency?: { - name: string; - symbol: string; - decimals: number; - }; - }) { - // no-op - } + addEthereumChain() {} // no-op /* istanbul ignore next */ - watchAsset(_options: { - onCancel: ErrorHandler; - onApprove: () => void; - type: string; - address: string; - symbol?: string; - decimals?: number; - image?: string; - }) { - // no-op - } + watchAsset() {} // no-op /* istanbul ignore next */ - switchEthereumChain(_options: { - onCancel: ErrorHandler; - onApprove: () => void; - chainId: string; - address?: string; - }) { - // no-op - } + switchEthereumChain() {} // no-op requestEthereumAccounts(options: { onCancel: ErrorHandler }): void { this.linkFlow.open({ onCancel: options.onCancel }); @@ -115,39 +73,16 @@ export class WalletLinkRelayUI implements WalletUI { } /* istanbul ignore next */ - signEthereumMessage(_: { - request: SignEthereumMessageRequest; - onSuccess: (response: SignEthereumMessageResponse) => void; - onCancel: ErrorHandler; - }): void { - // No-op - } + signEthereumMessage() {} // no-op /* istanbul ignore next */ - signEthereumTransaction(_: { - request: SignEthereumTransactionRequest; - onSuccess: (response: SignEthereumTransactionResponse) => void; - onCancel: ErrorHandler; - }): void { - // No-op - } + signEthereumTransaction() {} // no-op /* istanbul ignore next */ - submitEthereumTransaction(_: { - request: SubmitEthereumTransactionRequest; - onSuccess: (response: SubmitEthereumTransactionResponse) => void; - onCancel: ErrorHandler; - }): void { - // No-op - } + submitEthereumTransaction() {} // no-op /* istanbul ignore next */ - ethereumAddressFromSignedMessage(_: { - request: EthereumAddressFromSignedMessageRequest; - onSuccess: (response: EthereumAddressFromSignedMessageResponse) => void; - }): void { - // No-op - } + ethereumAddressFromSignedMessage() {} // no-op showConnecting(options: { isUnlinkedErrorState?: boolean; @@ -214,7 +149,7 @@ export class WalletLinkRelayUI implements WalletUI { } /* istanbul ignore next */ - inlineAddEthereumChain(_chainId: string): boolean { + inlineAddEthereumChain(): boolean { return false; } diff --git a/packages/wallet-sdk/src/provider/WalletUI.ts b/packages/wallet-sdk/src/provider/WalletUI.ts index f86c51d184..820607dd45 100644 --- a/packages/wallet-sdk/src/provider/WalletUI.ts +++ b/packages/wallet-sdk/src/provider/WalletUI.ts @@ -1,17 +1,7 @@ import { ErrorHandler } from '../errors'; import { Session } from '../relay/Session'; -import { - EthereumAddressFromSignedMessageRequest, - SignEthereumMessageRequest, - SignEthereumTransactionRequest, - SubmitEthereumTransactionRequest, -} from '../relay/Web3Request'; -import { - EthereumAddressFromSignedMessageResponse, - SignEthereumMessageResponse, - SignEthereumTransactionResponse, - SubmitEthereumTransactionResponse, -} from '../relay/Web3Response'; +import { Web3Request } from '../relay/Web3Request'; +import { Web3Response } from '../relay/Web3Response'; import { AddressString, ProviderType } from '../types'; export interface WalletUIOptions { @@ -76,26 +66,26 @@ export interface WalletUI { }): void; signEthereumMessage(options: { - request: SignEthereumMessageRequest; - onSuccess: (response: SignEthereumMessageResponse) => void; + request: Web3Request<'signEthereumMessage'>; + onSuccess: (response: Web3Response<'signEthereumMessage'>) => void; onCancel: ErrorHandler; }): void; signEthereumTransaction(options: { - request: SignEthereumTransactionRequest; - onSuccess: (response: SignEthereumTransactionResponse) => void; + request: Web3Request<'signEthereumTransaction'>; + onSuccess: (response: Web3Response<'signEthereumTransaction'>) => void; onCancel: ErrorHandler; }): void; submitEthereumTransaction(options: { - request: SubmitEthereumTransactionRequest; - onSuccess: (response: SubmitEthereumTransactionResponse) => void; + request: Web3Request<'submitEthereumTransaction'>; + onSuccess: (response: Web3Response<'submitEthereumTransaction'>) => void; onCancel: ErrorHandler; }): void; ethereumAddressFromSignedMessage(options: { - request: EthereumAddressFromSignedMessageRequest; - onSuccess: (response: EthereumAddressFromSignedMessageResponse) => void; + request: Web3Request<'ethereumAddressFromSignedMessage'>; + onSuccess: (response: Web3Response<'ethereumAddressFromSignedMessage'>) => void; }): void; /** diff --git a/packages/wallet-sdk/src/relay/MobileRelay.ts b/packages/wallet-sdk/src/relay/MobileRelay.ts index 25458bb8e3..d9e6c43b46 100644 --- a/packages/wallet-sdk/src/relay/MobileRelay.ts +++ b/packages/wallet-sdk/src/relay/MobileRelay.ts @@ -1,11 +1,10 @@ import { MobileRelayUI } from '../provider/MobileRelayUI'; import { getLocation } from '../util'; +import { WalletLinkResponseEventData } from './RelayMessage'; import { WalletLinkRelay, WalletLinkRelayOptions } from './WalletLinkRelay'; import { CancelablePromise } from './WalletSDKRelayAbstract'; -import { Web3Method } from './Web3Method'; -import { ConnectAndSignInRequest, Web3Request } from './Web3Request'; -import { ConnectAndSignInResponse, RequestEthereumAccountsResponse } from './Web3Response'; -import { Web3ResponseMessage } from './Web3ResponseMessage'; +import { Web3Request } from './Web3Request'; +import { Web3Response } from './Web3Response'; export class MobileRelay extends WalletLinkRelay { private _enableMobileWalletLink: boolean; @@ -16,7 +15,7 @@ export class MobileRelay extends WalletLinkRelay { } // override - public requestEthereumAccounts(): CancelablePromise { + public requestEthereumAccounts(): CancelablePromise> { if (this._enableMobileWalletLink) { return super.requestEthereumAccounts(); } @@ -41,12 +40,12 @@ export class MobileRelay extends WalletLinkRelay { // For mobile relay requests, open the Coinbase Wallet app switch (request.method) { - case Web3Method.requestEthereumAccounts: - case Web3Method.connectAndSignIn: + case 'requestEthereumAccounts': + case 'connectAndSignIn': navigatedToCBW = true; this.ui.openCoinbaseWalletDeeplink(this.getQRCodeUrl()); break; - case Web3Method.switchEthereumChain: + case 'switchEthereumChain': // switchEthereumChain doesn't need to open the app return; default: @@ -75,7 +74,7 @@ export class MobileRelay extends WalletLinkRelay { } // override - handleWeb3ResponseMessage(message: Web3ResponseMessage) { + handleWeb3ResponseMessage(message: WalletLinkResponseEventData) { super.handleWeb3ResponseMessage(message); if (this._enableMobileWalletLink && this.ui instanceof MobileRelayUI) { @@ -87,13 +86,13 @@ export class MobileRelay extends WalletLinkRelay { nonce: string; statement?: string; resources?: string[]; - }): CancelablePromise { + }): CancelablePromise> { if (!this._enableMobileWalletLink) { throw new Error('connectAndSignIn is supported only when enableMobileWalletLink is on'); } - return this.sendRequest({ - method: Web3Method.connectAndSignIn, + return this.sendRequest({ + method: 'connectAndSignIn', params: { appName: this.appName, appLogoUrl: this.appLogoUrl, diff --git a/packages/wallet-sdk/src/relay/RelayMessage.ts b/packages/wallet-sdk/src/relay/RelayMessage.ts index 2507a14f37..323e3f6146 100644 --- a/packages/wallet-sdk/src/relay/RelayMessage.ts +++ b/packages/wallet-sdk/src/relay/RelayMessage.ts @@ -1,16 +1,33 @@ // Copyright (c) 2018-2023 Coinbase, Inc. // Licensed under the Apache License, version 2.0 -export enum RelayMessageType { - SESSION_ID_REQUEST = 'SESSION_ID_REQUEST', - SESSION_ID_RESPONSE = 'SESSION_ID_RESPONSE', - LINKED = 'LINKED', - UNLINKED = 'UNLINKED', - WEB3_REQUEST = 'WEB3_REQUEST', - WEB3_REQUEST_CANCELED = 'WEB3_REQUEST_CANCELED', - WEB3_RESPONSE = 'WEB3_RESPONSE', -} +import { Web3Request } from './Web3Request'; +import { Web3Response } from './Web3Response'; -export interface RelayMessage { - type: T; -} +type WalletLinkEventType = + | 'SESSION_ID_REQUEST' + | 'SESSION_ID_RESPONSE' + | 'LINKED' + | 'UNLINKED' + | 'WEB3_REQUEST' + | 'WEB3_REQUEST_CANCELED' + | 'WEB3_RESPONSE'; + +export type WalletLinkEventData = { + type: WalletLinkEventType; + id: string; +} & ( + | { + type: 'WEB3_RESPONSE'; + response: Web3Response; + } + | { + type: 'WEB3_REQUEST'; + request: Web3Request; + } + | { + type: 'WEB3_REQUEST_CANCELED'; + } +); + +export type WalletLinkResponseEventData = Extract; diff --git a/packages/wallet-sdk/src/relay/WalletLinkRelay.test.ts b/packages/wallet-sdk/src/relay/WalletLinkRelay.test.ts index 44c67904d2..a43a8da526 100644 --- a/packages/wallet-sdk/src/relay/WalletLinkRelay.test.ts +++ b/packages/wallet-sdk/src/relay/WalletLinkRelay.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ServerMessageEvent } from '../connection/ServerMessage'; +import { ServerMessage } from '../connection/ServerMessage'; import { SessionConfig } from '../connection/SessionConfig'; import { WalletLinkConnection } from '../connection/WalletLinkConnection'; import { WalletLinkConnectionCipher } from '../connection/WalletLinkConnectionCipher'; @@ -10,11 +10,6 @@ import { WalletLinkRelay, WalletLinkRelayOptions } from './WalletLinkRelay'; import { WALLET_USER_NAME_KEY } from './WalletSDKRelayAbstract'; import { WalletSDKRelayEventManager } from './WalletSDKRelayEventManager'; -// mock isWeb3ResponseMessage to return true -jest.mock('./Web3ResponseMessage', () => ({ - isWeb3ResponseMessage: jest.fn().mockReturnValue(true), -})); - const decryptMock = jest.fn().mockImplementation((text) => Promise.resolve(`"decrypted ${text}"`)); jest.spyOn(WalletLinkConnectionCipher.prototype, 'decrypt').mockImplementation(decryptMock); @@ -50,7 +45,7 @@ describe('WalletLinkRelay', () => { describe('subscribe', () => { it('should call handleIncomingEvent', async () => { - const serverMessageEvent: ServerMessageEvent = { + const serverMessageEvent: ServerMessage = { type: 'Event', sessionId: 'sessionId', eventId: 'eventId', @@ -58,6 +53,13 @@ describe('WalletLinkRelay', () => { data: 'data', }; + jest.spyOn(JSON, 'parse').mockImplementation(() => { + return { + type: 'WEB3_RESPONSE', + data: 'decrypted data', + }; + }); + const relay = new WalletLinkRelay(options); const handleWeb3ResponseMessageSpy = jest.spyOn(relay, 'handleWeb3ResponseMessage'); diff --git a/packages/wallet-sdk/src/relay/WalletLinkRelay.ts b/packages/wallet-sdk/src/relay/WalletLinkRelay.ts index 30beb7eac7..12d10a67dd 100644 --- a/packages/wallet-sdk/src/relay/WalletLinkRelay.ts +++ b/packages/wallet-sdk/src/relay/WalletLinkRelay.ts @@ -19,7 +19,7 @@ import { WalletUI, WalletUIOptions } from '../provider/WalletUI'; import { AddressString, IntNumber, ProviderType, RegExpString } from '../types'; import { bigIntStringFromBN, createQrUrl, hexStringFromBuffer, randomBytesHex } from '../util'; import { EthereumTransactionParams } from './EthereumTransactionParams'; -import { RelayMessage } from './RelayMessage'; +import { WalletLinkEventData, WalletLinkResponseEventData } from './RelayMessage'; import { Session } from './Session'; import { CancelablePromise, @@ -28,35 +28,8 @@ import { } from './WalletSDKRelayAbstract'; import { WalletSDKRelayEventManager } from './WalletSDKRelayEventManager'; import { Web3Method } from './Web3Method'; -import { - EthereumAddressFromSignedMessageRequest, - GenericRequest, - ScanQRCodeRequest, - SignEthereumMessageRequest, - SignEthereumTransactionRequest, - SubmitEthereumTransactionRequest, - Web3Request, -} from './Web3Request'; -import { Web3RequestCanceledMessage } from './Web3RequestCanceledMessage'; -import { Web3RequestMessage } from './Web3RequestMessage'; -import { - AddEthereumChainResponse, - EthereumAddressFromSignedMessageResponse, - GenericResponse, - isErrorResponse, - isRequestEthereumAccountsResponse, - RequestEthereumAccountsResponse, - ScanQRCodeResponse, - SelectProviderResponse, - SignEthereumMessageResponse, - SignEthereumTransactionResponse, - SubmitEthereumTransactionResponse, - SwitchEthereumChainResponse, - WatchAssetReponse, - WatchAssetResponse, - Web3Response, -} from './Web3Response'; -import { Web3ResponseMessage } from './Web3ResponseMessage'; +import { SupportedWeb3Method, Web3Request } from './Web3Request'; +import { isErrorResponse, Web3Response } from './Web3Response'; export interface WalletLinkRelayOptions { linkAPIUrl: string; @@ -197,10 +170,14 @@ export class WalletLinkRelay // reason we don't get a response via an explicit web3 message // we can still fulfill the eip1102 request. Array.from(WalletLinkRelay.accountRequestCallbackIds.values()).forEach((id) => { - const message = Web3ResponseMessage({ + const message: WalletLinkEventData = { + type: 'WEB3_RESPONSE', id, - response: RequestEthereumAccountsResponse([selectedAddress as AddressString]), - }); + response: { + method: 'requestEthereumAccounts', + result: [selectedAddress as AddressString], + }, + }; this.invokeCallback({ ...message, id }); }); WalletLinkRelay.accountRequestCallbackIds.clear(); @@ -296,9 +273,9 @@ export class WalletLinkRelay address: AddressString, addPrefix: boolean, typedDataJson?: string | null - ): CancelablePromise { - return this.sendRequest({ - method: Web3Method.signEthereumMessage, + ) { + return this.sendRequest({ + method: 'signEthereumMessage', params: { message: hexStringFromBuffer(message, true), address, @@ -308,16 +285,9 @@ export class WalletLinkRelay }); } - public ethereumAddressFromSignedMessage( - message: Buffer, - signature: Buffer, - addPrefix: boolean - ): CancelablePromise { - return this.sendRequest< - EthereumAddressFromSignedMessageRequest, - EthereumAddressFromSignedMessageResponse - >({ - method: Web3Method.ethereumAddressFromSignedMessage, + public ethereumAddressFromSignedMessage(message: Buffer, signature: Buffer, addPrefix: boolean) { + return this.sendRequest({ + method: 'ethereumAddressFromSignedMessage', params: { message: hexStringFromBuffer(message, true), signature: hexStringFromBuffer(signature, true), @@ -326,11 +296,9 @@ export class WalletLinkRelay }); } - public signEthereumTransaction( - params: EthereumTransactionParams - ): CancelablePromise { - return this.sendRequest({ - method: Web3Method.signEthereumTransaction, + public signEthereumTransaction(params: EthereumTransactionParams) { + return this.sendRequest({ + method: 'signEthereumTransaction', params: { fromAddress: params.fromAddress, toAddress: params.toAddress, @@ -349,11 +317,9 @@ export class WalletLinkRelay }); } - public signAndSubmitEthereumTransaction( - params: EthereumTransactionParams - ): CancelablePromise { - return this.sendRequest({ - method: Web3Method.signEthereumTransaction, + public signAndSubmitEthereumTransaction(params: EthereumTransactionParams) { + return this.sendRequest<'signEthereumTransaction', 'submitEthereumTransaction'>({ + method: 'signEthereumTransaction', params: { fromAddress: params.fromAddress, toAddress: params.toAddress, @@ -372,12 +338,9 @@ export class WalletLinkRelay }); } - public submitEthereumTransaction( - signedTransaction: Buffer, - chainId: IntNumber - ): CancelablePromise { - return this.sendRequest({ - method: Web3Method.submitEthereumTransaction, + public submitEthereumTransaction(signedTransaction: Buffer, chainId: IntNumber) { + return this.sendRequest({ + method: 'submitEthereumTransaction', params: { signedTransaction: hexStringFromBuffer(signedTransaction, true), chainId, @@ -385,10 +348,12 @@ export class WalletLinkRelay }); } - public scanQRCode(regExp: RegExpString): CancelablePromise { - return this.sendRequest({ - method: Web3Method.scanQRCode, - params: { regExp }, + public scanQRCode(regExp: RegExpString) { + return this.sendRequest({ + method: 'scanQRCode', + params: { + regExp, + }, }); } @@ -403,9 +368,9 @@ export class WalletLinkRelay ); } - public genericRequest(data: object, action: string): CancelablePromise { - return this.sendRequest({ - method: Web3Method.generic, + public genericRequest(data: object, action: string) { + return this.sendRequest({ + method: 'generic', params: { action, data, @@ -413,13 +378,17 @@ export class WalletLinkRelay }); } - public sendGenericMessage(request: GenericRequest): CancelablePromise { + public sendGenericMessage( + request: Web3Request<'generic'> + ): CancelablePromise> { return this.sendRequest(request); } - public sendRequest( - request: T - ): CancelablePromise { + public sendRequest< + RequestMethod extends SupportedWeb3Method, + ResponseMethod extends SupportedWeb3Method = RequestMethod, + Response = Web3Response, + >(request: Web3Request): CancelablePromise { let hideSnackbarItem: (() => void) | null = null; const id = randomBytesHex(8); @@ -429,7 +398,7 @@ export class WalletLinkRelay hideSnackbarItem?.(); }; - const promise = new Promise((resolve, reject) => { + const promise = new Promise((resolve, reject) => { if (!this.ui.isStandalone()) { hideSnackbarItem = this.ui.showConnecting({ isUnlinkedErrorState: this.isUnlinkedErrorState, @@ -440,11 +409,11 @@ export class WalletLinkRelay this.relayEventManager.callbacks.set(id, (response) => { hideSnackbarItem?.(); - if (response.errorMessage) { + if (isErrorResponse(response)) { return reject(new Error(response.errorMessage)); } - resolve(response as U); + resolve(response as Response); }); if (this.ui.isStandalone()) { @@ -479,11 +448,11 @@ export class WalletLinkRelay } protected publishWeb3RequestEvent(id: string, request: Web3Request): void { - const message = Web3RequestMessage({ id, request }); + const message: WalletLinkEventData = { type: 'WEB3_REQUEST', id, request }; const storedSession = Session.load(this.storage); this.diagnostic?.log(EVENTS.WEB3_REQUEST, { eventId: message.id, - method: `relay::${message.request.method}`, + method: `relay::${request.method}`, sessionIdHash: this.getSessionIdHash(), storedSessionIdHash: storedSession ? Session.hash(storedSession.id) : '', isSessionMismatched: (storedSession?.id !== this._session.id).toString(), @@ -493,46 +462,49 @@ export class WalletLinkRelay .then((_) => { this.diagnostic?.log(EVENTS.WEB3_REQUEST_PUBLISHED, { eventId: message.id, - method: `relay::${message.request.method}`, + method: `relay::${request.method}`, sessionIdHash: this.getSessionIdHash(), storedSessionIdHash: storedSession ? Session.hash(storedSession.id) : '', isSessionMismatched: (storedSession?.id !== this._session.id).toString(), }); }) .catch((err) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id: message.id, - response: { - method: message.request.method, - errorMessage: err.message, - }, - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id: message.id, + response: { + method: request.method, + errorMessage: err.message, + }, + }); }); } private publishWeb3RequestCanceledEvent(id: string) { - const message = Web3RequestCanceledMessage(id); + const message: WalletLinkEventData = { + type: 'WEB3_REQUEST_CANCELED', + id, + }; this.publishEvent('Web3RequestCanceled', message, false).then(); } protected publishEvent( event: string, - message: RelayMessage, + message: WalletLinkEventData, callWebhook: boolean ): Promise { return this.connection.publishEvent(event, message, callWebhook); } - handleWeb3ResponseMessage(message: Web3ResponseMessage) { + handleWeb3ResponseMessage(message: WalletLinkResponseEventData) { const { response } = message; + this.diagnostic?.log(EVENTS.WEB3_RESPONSE, { eventId: message.id, method: `relay::${response.method}`, sessionIdHash: this.getSessionIdHash(), }); - if (isRequestEthereumAccountsResponse(response)) { + if (response.method === 'requestEthereumAccounts') { WalletLinkRelay.accountRequestCallbackIds.forEach((id) => this.invokeCallback({ ...message, id }) ); @@ -550,19 +522,18 @@ export class WalletLinkRelay errorCode?: number ) { const errorMessage = error?.message ?? getMessageFromCode(errorCode); - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: { - method, - errorMessage, - errorCode, - }, - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { + method, + errorMessage, + errorCode, + }, + }); } - private invokeCallback(message: Web3ResponseMessage) { + private invokeCallback(message: WalletLinkResponseEventData) { const callback = this.relayEventManager.callbacks.get(message.id); if (callback) { callback(message.response); @@ -570,9 +541,9 @@ export class WalletLinkRelay } } - public requestEthereumAccounts(): CancelablePromise { + public requestEthereumAccounts() { const request: Web3Request = { - method: Web3Method.requestEthereumAccounts, + method: 'requestEthereumAccounts', params: { appName: this.appName, appLogoUrl: this.appLogoUrl || null, @@ -590,27 +561,26 @@ export class WalletLinkRelay hideSnackbarItem?.(); }; - const promise = new Promise((resolve, reject) => { + const promise = new Promise>((resolve, reject) => { this.relayEventManager.callbacks.set(id, (response) => { this.ui.hideRequestEthereumAccounts(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore hideSnackbarItem?.(); - if (response.errorMessage) { + if (isErrorResponse(response)) { return reject(new Error(response.errorMessage)); } - resolve(response as RequestEthereumAccountsResponse); + resolve(response as Web3Response<'requestEthereumAccounts'>); }); if (this.ui.inlineAccountsResponse()) { const onAccounts = (accounts: [AddressString]) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: RequestEthereumAccountsResponse(accounts), - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { method: 'requestEthereumAccounts', result: accounts }, + }); }; this.ui.requestEthereumAccounts({ @@ -637,9 +607,9 @@ export class WalletLinkRelay return { promise, cancel }; } - selectProvider(providerOptions: ProviderType[]): CancelablePromise { + selectProvider(providerOptions: ProviderType[]) { const request: Web3Request = { - method: Web3Method.selectProvider, + method: 'selectProvider', params: { providerOptions, }, @@ -652,30 +622,28 @@ export class WalletLinkRelay this.handleErrorResponse(id, request.method, error); }; - const promise = new Promise((resolve, reject) => { + const promise = new Promise>((resolve, reject) => { this.relayEventManager.callbacks.set(id, (response) => { - if (response.errorMessage) { + if (isErrorResponse(response)) { return reject(new Error(response.errorMessage)); } - resolve(response as SelectProviderResponse); + resolve(response as Web3Response<'selectProvider'>); }); const _cancel = (_error?: ErrorType) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: SelectProviderResponse(ProviderType.Unselected), - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { method: 'selectProvider', result: ProviderType.Unselected }, + }); }; const approve = (selectedProviderKey: ProviderType) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: SelectProviderResponse(selectedProviderKey), - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { method: 'selectProvider', result: selectedProviderKey }, + }); }; if (this.ui.selectProvider) @@ -696,9 +664,9 @@ export class WalletLinkRelay decimals?: number, image?: string, chainId?: string - ): CancelablePromise { + ): CancelablePromise> { const request: Web3Request = { - method: Web3Method.watchAsset, + method: 'watchAsset', params: { type, options: { @@ -728,32 +696,36 @@ export class WalletLinkRelay }); } - const promise = new Promise((resolve, reject) => { + const promise = new Promise>((resolve, reject) => { this.relayEventManager.callbacks.set(id, (response) => { hideSnackbarItem?.(); - if (response.errorMessage) { + if (isErrorResponse(response)) { return reject(new Error(response.errorMessage)); } - resolve(response as WatchAssetResponse); + resolve(response as Web3Response<'watchAsset'>); }); const _cancel = (_error?: ErrorType) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: WatchAssetReponse(false), - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { + method: 'watchAsset', + result: false, + }, + }); }; const approve = () => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: WatchAssetReponse(true), - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { + method: 'watchAsset', + result: true, + }, + }); }; if (this.ui.inlineWatchAsset()) { @@ -790,7 +762,7 @@ export class WalletLinkRelay } ) { const request: Web3Request = { - method: Web3Method.addEthereumChain, + method: 'addEthereumChain', params: { chainId, rpcUrls, @@ -818,35 +790,42 @@ export class WalletLinkRelay }); } - const promise = new Promise((resolve, reject) => { + const promise = new Promise>((resolve, reject) => { this.relayEventManager.callbacks.set(id, (response) => { hideSnackbarItem?.(); - if (response.errorMessage) { + if (isErrorResponse(response)) { return reject(new Error(response.errorMessage)); } - resolve(response as AddEthereumChainResponse); + resolve(response as Web3Response<'addEthereumChain'>); }); const _cancel = (_error?: ErrorType) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: AddEthereumChainResponse({ + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { + method: 'addEthereumChain', + result: { isApproved: false, rpcUrl: '', - }), - }) - ); + }, + }, + }); }; const approve = (rpcUrl: string) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: AddEthereumChainResponse({ isApproved: true, rpcUrl }), - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { + method: 'addEthereumChain', + result: { + isApproved: true, + rpcUrl, + }, + }, + }); }; if (this.ui.inlineAddEthereumChain(chainId)) { @@ -873,9 +852,9 @@ export class WalletLinkRelay switchEthereumChain( chainId: string, address?: string - ): CancelablePromise { + ): CancelablePromise> { const request: Web3Request = { - method: Web3Method.switchEthereumChain, + method: 'switchEthereumChain', params: { chainId, ...{ address }, @@ -889,7 +868,7 @@ export class WalletLinkRelay this.handleErrorResponse(id, request.method, error); }; - const promise = new Promise((resolve, reject) => { + const promise = new Promise>((resolve, reject) => { this.relayEventManager.callbacks.set(id, (response) => { if (isErrorResponse(response) && response.errorCode) { return reject( @@ -898,11 +877,11 @@ export class WalletLinkRelay message: `Unrecognized chain ID. Try adding the chain using addEthereumChain first.`, }) ); - } else if (response.errorMessage) { + } else if (isErrorResponse(response)) { return reject(new Error(response.errorMessage)); } - resolve(response as SwitchEthereumChainResponse); + resolve(response as Web3Response<'switchEthereumChain'>); }); const _cancel = (error?: ErrorType | number) => { @@ -912,33 +891,37 @@ export class WalletLinkRelay this.handleErrorResponse( id, - Web3Method.switchEthereumChain, + 'switchEthereumChain', error instanceof Error ? error : standardErrors.provider.unsupportedChain(chainId), errorCode ); } else { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: SwitchEthereumChainResponse({ + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { + method: 'switchEthereumChain', + result: { isApproved: false, rpcUrl: '', - }), - }) - ); + }, + }, + }); } }; const approve = (rpcUrl: string) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response: SwitchEthereumChainResponse({ + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response: { + method: 'switchEthereumChain', + result: { isApproved: true, rpcUrl, - }), - }) - ); + }, + }, + }); }; this.ui.switchEthereumChain({ @@ -970,43 +953,43 @@ export class WalletLinkRelay }; const onSuccess = ( - response: - | SignEthereumMessageResponse - | SignEthereumTransactionResponse - | SubmitEthereumTransactionResponse - | EthereumAddressFromSignedMessageResponse + response: Web3Response< + | 'signEthereumMessage' + | 'signEthereumTransaction' + | 'submitEthereumTransaction' + | 'ethereumAddressFromSignedMessage' + > ) => { - this.handleWeb3ResponseMessage( - Web3ResponseMessage({ - id, - response, - }) - ); + this.handleWeb3ResponseMessage({ + type: 'WEB3_RESPONSE', + id, + response, + }); }; switch (request.method) { - case Web3Method.signEthereumMessage: + case 'signEthereumMessage': this.ui.signEthereumMessage({ request, onSuccess, onCancel: _cancel, }); break; - case Web3Method.signEthereumTransaction: + case 'signEthereumTransaction': this.ui.signEthereumTransaction({ request, onSuccess, onCancel: _cancel, }); break; - case Web3Method.submitEthereumTransaction: + case 'submitEthereumTransaction': this.ui.submitEthereumTransaction({ request, onSuccess, onCancel: _cancel, }); break; - case Web3Method.ethereumAddressFromSignedMessage: + case 'ethereumAddressFromSignedMessage': this.ui.ethereumAddressFromSignedMessage({ request, onSuccess, diff --git a/packages/wallet-sdk/src/relay/WalletSDKRelayAbstract.ts b/packages/wallet-sdk/src/relay/WalletSDKRelayAbstract.ts index 5e85dc5f32..0a8080fb00 100644 --- a/packages/wallet-sdk/src/relay/WalletSDKRelayAbstract.ts +++ b/packages/wallet-sdk/src/relay/WalletSDKRelayAbstract.ts @@ -3,21 +3,8 @@ import { JSONRPCRequest, JSONRPCResponse } from '../provider/JSONRPC'; import { AddressString, IntNumber, ProviderType, RegExpString } from '../types'; import { EthereumTransactionParams } from './EthereumTransactionParams'; import { Session } from './Session'; -import { Web3Request } from './Web3Request'; -import { - AddEthereumChainResponse, - EthereumAddressFromSignedMessageResponse, - GenericResponse, - RequestEthereumAccountsResponse, - ScanQRCodeResponse, - SelectProviderResponse, - SignEthereumMessageResponse, - SignEthereumTransactionResponse, - SubmitEthereumTransactionResponse, - SwitchEthereumChainResponse, - WatchAssetResponse, - Web3Response, -} from './Web3Response'; +import { SupportedWeb3Method, Web3Request } from './Web3Request'; +import { Web3Response } from './Web3Response'; export const WALLET_USER_NAME_KEY = 'walletUsername'; export const LOCAL_STORAGE_ADDRESSES_KEY = 'Addresses'; @@ -31,7 +18,7 @@ export type CancelablePromise = { export abstract class WalletSDKRelayAbstract { abstract resetAndReload(): void; - abstract requestEthereumAccounts(): CancelablePromise; + abstract requestEthereumAccounts(): CancelablePromise>; abstract addEthereumChain( chainId: string, @@ -44,7 +31,7 @@ export abstract class WalletSDKRelayAbstract { symbol: string; decimals: number; } - ): CancelablePromise; + ): CancelablePromise>; abstract watchAsset( type: string, @@ -53,50 +40,51 @@ export abstract class WalletSDKRelayAbstract { decimals?: number, image?: string, chainId?: string - ): CancelablePromise; + ): CancelablePromise>; abstract selectProvider( providerOptions: ProviderType[] - ): CancelablePromise; + ): CancelablePromise>; abstract switchEthereumChain( chainId: string, address?: string - ): CancelablePromise; + ): CancelablePromise>; abstract signEthereumMessage( message: Buffer, address: AddressString, addPrefix: boolean, typedDataJson?: string | null - ): CancelablePromise; + ): CancelablePromise>; abstract ethereumAddressFromSignedMessage( message: Buffer, signature: Buffer, addPrefix: boolean - ): CancelablePromise; + ): CancelablePromise>; abstract signEthereumTransaction( params: EthereumTransactionParams - ): CancelablePromise; + ): CancelablePromise>; abstract signAndSubmitEthereumTransaction( params: EthereumTransactionParams - ): CancelablePromise; + ): CancelablePromise>; abstract submitEthereumTransaction( signedTransaction: Buffer, chainId: IntNumber - ): CancelablePromise; + ): CancelablePromise>; - abstract scanQRCode(regExp: RegExpString): CancelablePromise; + abstract scanQRCode(regExp: RegExpString): CancelablePromise>; - abstract genericRequest(data: object, action: string): CancelablePromise; + abstract genericRequest(data: object, action: string): CancelablePromise>; - abstract sendRequest( - request: T - ): CancelablePromise; + abstract sendRequest< + RequestMethod extends SupportedWeb3Method, + ResponseMethod extends SupportedWeb3Method = RequestMethod, + >(request: Web3Request): CancelablePromise>; abstract setAppInfo(appName: string, appLogoUrl: string | null): void; diff --git a/packages/wallet-sdk/src/relay/Web3Method.ts b/packages/wallet-sdk/src/relay/Web3Method.ts index 4f951ae48e..cdfbad8102 100644 --- a/packages/wallet-sdk/src/relay/Web3Method.ts +++ b/packages/wallet-sdk/src/relay/Web3Method.ts @@ -1,19 +1,18 @@ // Copyright (c) 2018-2023 Coinbase, Inc. // Licensed under the Apache License, version 2.0 -export enum Web3Method { - requestEthereumAccounts = 'requestEthereumAccounts', - signEthereumMessage = 'signEthereumMessage', - signEthereumTransaction = 'signEthereumTransaction', - submitEthereumTransaction = 'submitEthereumTransaction', - ethereumAddressFromSignedMessage = 'ethereumAddressFromSignedMessage', - scanQRCode = 'scanQRCode', - generic = 'generic', - childRequestEthereumAccounts = 'childRequestEthereumAccounts', - addEthereumChain = 'addEthereumChain', - switchEthereumChain = 'switchEthereumChain', - makeEthereumJSONRPCRequest = 'makeEthereumJSONRPCRequest', - watchAsset = 'watchAsset', - selectProvider = 'selectProvider', - connectAndSignIn = 'connectAndSignIn', -} +export type Web3Method = + | 'requestEthereumAccounts' + | 'signEthereumMessage' + | 'signEthereumTransaction' + | 'submitEthereumTransaction' + | 'ethereumAddressFromSignedMessage' + | 'scanQRCode' + | 'generic' + | 'childRequestEthereumAccounts' + | 'addEthereumChain' + | 'switchEthereumChain' + | 'makeEthereumJSONRPCRequest' + | 'watchAsset' + | 'selectProvider' + | 'connectAndSignIn'; diff --git a/packages/wallet-sdk/src/relay/Web3Request.ts b/packages/wallet-sdk/src/relay/Web3Request.ts index 01ba56a970..ef193be66a 100644 --- a/packages/wallet-sdk/src/relay/Web3Request.ts +++ b/packages/wallet-sdk/src/relay/Web3Request.ts @@ -11,164 +11,128 @@ import { } from '../types'; import { Web3Method } from './Web3Method'; -interface BaseWeb3Request< - Method extends Web3Method, - Params extends object = Record, -> { - method: Method; - params: Params; -} - -export type RequestEthereumAccountsRequest = BaseWeb3Request< - Web3Method.requestEthereumAccounts, - RequestEthereumAccountsBaseParams ->; - -type RequestEthereumAccountsBaseParams = { - appName: string; - appLogoUrl: string | null; -}; - -export type ConnectAndSignInRequest = BaseWeb3Request< - Web3Method.connectAndSignIn, - RequestEthereumAccountsBaseParams & { - domain: string; - aud: string; - version: string; - type: string; - nonce: string; - iat: string; - chainId: string; - statement?: string; - resources?: string[]; - } ->; - -export type AddEthereumChainRequest = BaseWeb3Request< - Web3Method.addEthereumChain, - { - chainId: string; - blockExplorerUrls?: string[]; - chainName?: string; - iconUrls?: string[]; - rpcUrls: string[]; - nativeCurrency?: { - name: string; - symbol: string; - decimals: number; +export type Web3Request = Extract<_Web3Request, { method: M }>; + +export type SupportedWeb3Method = Extract; + +type _Web3Request = + | { + method: 'requestEthereumAccounts'; + params: { + appName: string; + appLogoUrl: string | null; + }; + } + | { + method: 'connectAndSignIn'; + params: { + appName: string; + appLogoUrl: string | null; + domain: string; + aud: string; + version: string; + type: string; + nonce: string; + iat: string; + chainId: string; + statement?: string; + resources?: string[]; + }; + } + | { + method: 'addEthereumChain'; + params: { + chainId: string; + blockExplorerUrls?: string[]; + chainName?: string; + iconUrls?: string[]; + rpcUrls: string[]; + nativeCurrency?: { + name: string; + symbol: string; + decimals: number; + }; + }; + } + | { + method: 'switchEthereumChain'; + params: { + chainId: string; + address?: string; + }; + } + | { + method: 'signEthereumMessage'; + params: { + message: HexString; + address: AddressString; + addPrefix: boolean; + typedDataJson: string | null; + }; + } + | { + method: 'signEthereumTransaction'; + params: { + fromAddress: AddressString; + toAddress: AddressString | null; + weiValue: BigIntString; + data: HexString; + nonce: IntNumber | null; + gasPriceInWei: BigIntString | null; + maxFeePerGas: BigIntString | null; // in wei + maxPriorityFeePerGas: BigIntString | null; // in wei + gasLimit: BigIntString | null; + chainId: IntNumber; + shouldSubmit: boolean; + }; + } + | { + method: 'submitEthereumTransaction'; + params: { + signedTransaction: HexString; + chainId: IntNumber; + }; + } + | { + method: 'ethereumAddressFromSignedMessage'; + params: { + message: HexString; + signature: HexString; + addPrefix: boolean; + }; + } + | { + method: 'scanQRCode'; + params: { + regExp: RegExpString; + }; + } + | { + method: 'generic'; + params: { + action: string; + data: object; + }; + } + | { method: 'selectProvider'; params: { providerOptions: ProviderType[] } } + | { + method: 'makeEthereumJSONRPCRequest'; + params: { + rpcMethod: string; + rpcParams: unknown[]; + chainId: string; + }; + } + | { + method: 'watchAsset'; + params: { + type: string; + options: { + address: string; + symbol?: string; + decimals?: number; + image?: string; + }; + chainId?: string; + }; }; - } ->; - -export type SwitchEthereumChainRequest = BaseWeb3Request< - Web3Method.switchEthereumChain, - { - chainId: string; - address?: string; - } ->; - -export type SignEthereumMessageRequest = BaseWeb3Request< - Web3Method.signEthereumMessage, - { - message: HexString; - address: AddressString; - addPrefix: boolean; - typedDataJson: string | null; - } ->; - -export type SignEthereumTransactionRequest = BaseWeb3Request< - Web3Method.signEthereumTransaction, - { - fromAddress: AddressString; - toAddress: AddressString | null; - weiValue: BigIntString; - data: HexString; - nonce: IntNumber | null; - gasPriceInWei: BigIntString | null; - maxFeePerGas: BigIntString | null; // in wei - maxPriorityFeePerGas: BigIntString | null; // in wei - gasLimit: BigIntString | null; - chainId: IntNumber; - shouldSubmit: boolean; - } ->; - -export type SubmitEthereumTransactionRequest = BaseWeb3Request< - Web3Method.submitEthereumTransaction, - { - signedTransaction: HexString; - chainId: IntNumber; - } ->; - -export type EthereumAddressFromSignedMessageRequest = BaseWeb3Request< - Web3Method.ethereumAddressFromSignedMessage, - { - message: HexString; - signature: HexString; - addPrefix: boolean; - } ->; - -export type ScanQRCodeRequest = BaseWeb3Request< - Web3Method.scanQRCode, - { - regExp: RegExpString; - } ->; - -export type GenericRequest = BaseWeb3Request< - Web3Method.generic, - { - action: string; - data: object; - } ->; - -export type SelectProviderRequest = BaseWeb3Request< - Web3Method.selectProvider, - { providerOptions: ProviderType[] } ->; - -export type MakeEthereumJSONRPCRequest = BaseWeb3Request< - Web3Method.makeEthereumJSONRPCRequest, - { - rpcMethod: string; - rpcParams: unknown[]; - chainId: string; - } ->; - -type WatchAssetRequestBaseParams = { - type: string; - options: { - address: string; - symbol?: string; - decimals?: number; - image?: string; - }; -}; - -export type WatchAssetRequest = BaseWeb3Request< - Web3Method.watchAsset, - WatchAssetRequestBaseParams & { - chainId?: string; - } ->; - -export type Web3Request = - | RequestEthereumAccountsRequest - | ConnectAndSignInRequest - | SignEthereumMessageRequest - | SignEthereumTransactionRequest - | SubmitEthereumTransactionRequest - | EthereumAddressFromSignedMessageRequest - | ScanQRCodeRequest - | GenericRequest - | AddEthereumChainRequest - | SwitchEthereumChainRequest - | MakeEthereumJSONRPCRequest - | WatchAssetRequest - | SelectProviderRequest; diff --git a/packages/wallet-sdk/src/relay/Web3RequestCanceledMessage.ts b/packages/wallet-sdk/src/relay/Web3RequestCanceledMessage.ts deleted file mode 100644 index e200f2a045..0000000000 --- a/packages/wallet-sdk/src/relay/Web3RequestCanceledMessage.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2018-2023 Coinbase, Inc. -// Licensed under the Apache License, version 2.0 - -import { RelayMessage, RelayMessageType } from './RelayMessage'; - -export interface Web3RequestCanceledMessage - extends RelayMessage { - id: string; -} - -export function Web3RequestCanceledMessage(id: string): Web3RequestCanceledMessage { - return { type: RelayMessageType.WEB3_REQUEST_CANCELED, id }; -} diff --git a/packages/wallet-sdk/src/relay/Web3RequestMessage.ts b/packages/wallet-sdk/src/relay/Web3RequestMessage.ts deleted file mode 100644 index 0ca2fd9b75..0000000000 --- a/packages/wallet-sdk/src/relay/Web3RequestMessage.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2018-2023 Coinbase, Inc. -// Licensed under the Apache License, version 2.0 - -import { RelayMessage, RelayMessageType } from './RelayMessage'; -import { Web3Request } from './Web3Request'; - -export interface Web3RequestMessage extends RelayMessage { - id: string; - request: Web3Request; -} - -export function Web3RequestMessage(params: Omit): Web3RequestMessage { - return { type: RelayMessageType.WEB3_REQUEST, ...params }; -} diff --git a/packages/wallet-sdk/src/relay/Web3Response.ts b/packages/wallet-sdk/src/relay/Web3Response.ts index e27d0545a5..f255c04f4b 100644 --- a/packages/wallet-sdk/src/relay/Web3Response.ts +++ b/packages/wallet-sdk/src/relay/Web3Response.ts @@ -4,137 +4,81 @@ import { AddressString, HexString, ProviderType } from '../types'; import { Web3Method } from './Web3Method'; -interface BaseWeb3Response { - method: Web3Method; - errorMessage?: string | null; - result?: Result; -} +export type Web3Response = + | Extract<_Web3Response, { method: M }> + | ErrorResponse; -export interface ErrorResponse extends BaseWeb3Response { +type ErrorResponse = { + method: unknown; errorCode?: number; errorMessage: string; -} - -export function isErrorResponse(response: unknown): response is ErrorResponse { - return ( - (response as ErrorResponse)?.method !== undefined && - (response as ErrorResponse)?.errorMessage !== undefined - ); -} - -export type RequestEthereumAccountsResponse = BaseWeb3Response< - AddressString[] // an array of ethereum addresses ->; - -export type ConnectAndSignInResponse = BaseWeb3Response<{ - accounts: AddressString[]; - message: HexString; - signature: HexString; -}>; - -export type AddEthereumChainResponse = BaseWeb3Response; // was request approved - -export type WatchAssetResponse = BaseWeb3Response; - -export type SelectProviderResponse = BaseWeb3Response; - -export type AddResponse = { - isApproved: boolean; - rpcUrl: string; -}; - -export function AddEthereumChainResponse(addResponse: AddResponse): SwitchEthereumChainResponse { - return { - method: Web3Method.addEthereumChain, - result: addResponse, - }; -} - -export type SwitchEthereumChainResponse = BaseWeb3Response; // was request approved - -export type SwitchResponse = { - isApproved: boolean; - rpcUrl: string; }; -export function SwitchEthereumChainResponse( - switchResponse: SwitchResponse -): SwitchEthereumChainResponse { - return { - method: Web3Method.switchEthereumChain, - result: switchResponse, - }; -} - -export function RequestEthereumAccountsResponse( - addresses: AddressString[] -): RequestEthereumAccountsResponse { - return { method: Web3Method.requestEthereumAccounts, result: addresses }; -} - -export function WatchAssetReponse(success: boolean): WatchAssetResponse { - return { method: Web3Method.watchAsset, result: success }; -} - -export function SelectProviderResponse(selectedProviderKey: ProviderType): SelectProviderResponse { - return { method: Web3Method.selectProvider, result: selectedProviderKey }; -} - -export function isRequestEthereumAccountsResponse( - res: any -): res is RequestEthereumAccountsResponse { - return res && res.method === Web3Method.requestEthereumAccounts; -} - -export function SignEthereumMessageResponse(signature: HexString): SignEthereumMessageResponse { - return { method: Web3Method.signEthereumMessage, result: signature }; -} - -export type SignEthereumMessageResponse = BaseWeb3Response; // signature - -export function SignEthereumTransactionResponse( - signedData: HexString -): SignEthereumTransactionResponse { - return { method: Web3Method.signEthereumTransaction, result: signedData }; -} - -export type SignEthereumTransactionResponse = BaseWeb3Response; // signed transaction - -export function SubmitEthereumTransactionResponse( - txHash: HexString -): SubmitEthereumTransactionResponse { - return { method: Web3Method.submitEthereumTransaction, result: txHash }; -} - -export type SubmitEthereumTransactionResponse = BaseWeb3Response; // transaction hash - -export function EthereumAddressFromSignedMessageResponse( - address: AddressString -): EthereumAddressFromSignedMessageResponse { - return { - method: Web3Method.ethereumAddressFromSignedMessage, - result: address, - }; +// TODO: revisit if this is still needed +export function isErrorResponse(response: unknown): response is ErrorResponse { + return (response as ErrorResponse).errorMessage !== undefined; } -export type EthereumAddressFromSignedMessageResponse = BaseWeb3Response; // ethereum address - -export type ScanQRCodeResponse = BaseWeb3Response; // scanned string - -export type GenericResponse = BaseWeb3Response; // response data - -export type MakeEthereumJSONRPCResponse = BaseWeb3Response; - -export type Web3Response = - | ErrorResponse - | RequestEthereumAccountsResponse - | ConnectAndSignInResponse - | SignEthereumMessageResponse - | SignEthereumTransactionResponse - | SubmitEthereumTransactionResponse - | EthereumAddressFromSignedMessageResponse - | ScanQRCodeResponse - | GenericResponse - | AddEthereumChainResponse - | SwitchEthereumChainResponse - | MakeEthereumJSONRPCResponse; +type _Web3Response = + | { + method: 'connectAndSignIn'; + result: { + accounts: AddressString[]; + message: HexString; + signature: HexString; + }; + } + | { + method: 'addEthereumChain'; + result: { + isApproved: boolean; + rpcUrl: string; + }; + } + | { + method: 'switchEthereumChain'; + result: { + isApproved: boolean; + rpcUrl: string; + }; + } + | { + method: 'requestEthereumAccounts'; + result: AddressString[]; + } + | { + method: 'watchAsset'; + result: boolean; + } + | { + method: 'selectProvider'; + result: ProviderType; + } + | { + method: 'signEthereumMessage'; + result: HexString; + } + | { + method: 'signEthereumTransaction'; + result: HexString; + } + | { + method: 'submitEthereumTransaction'; + result: HexString; + } + | { + method: 'ethereumAddressFromSignedMessage'; + result: AddressString; + } + | { + method: 'scanQRCode'; + result: string; + } + | { + method: 'generic'; + result: string; + } + | { + method: 'makeEthereumJSONRPCRequest'; + result: unknown; + }; diff --git a/packages/wallet-sdk/src/relay/Web3ResponseMessage.ts b/packages/wallet-sdk/src/relay/Web3ResponseMessage.ts deleted file mode 100644 index dd1be200d6..0000000000 --- a/packages/wallet-sdk/src/relay/Web3ResponseMessage.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2018-2023 Coinbase, Inc. -// Licensed under the Apache License, version 2.0 - -import { RelayMessage, RelayMessageType } from './RelayMessage'; -import { Web3Response } from './Web3Response'; - -export interface Web3ResponseMessage extends RelayMessage { - type: RelayMessageType.WEB3_RESPONSE; - id: string; - response: Web3Response; -} - -export function Web3ResponseMessage( - params: Omit -): Web3ResponseMessage { - return { type: RelayMessageType.WEB3_RESPONSE, ...params }; -} - -export function isWeb3ResponseMessage(msg: any): msg is Web3ResponseMessage { - return msg && msg.type === RelayMessageType.WEB3_RESPONSE; -}