diff --git a/src/background/messaging/rpc-message-handler.ts b/src/background/messaging/rpc-message-handler.ts index 4f1a365323..e132203d70 100644 --- a/src/background/messaging/rpc-message-handler.ts +++ b/src/background/messaging/rpc-message-handler.ts @@ -13,6 +13,7 @@ import { rpcSendTransfer } from './rpc-methods/send-transfer'; import { rpcSignMessage } from './rpc-methods/sign-message'; import { rpcSignPsbt } from './rpc-methods/sign-psbt'; import { rpcSignStacksMessage } from './rpc-methods/sign-stacks-message'; +import { rpcStxGetAddresses } from './rpc-methods/stx-get-addresses'; import { rpcSupportedMethods } from './rpc-methods/supported-methods'; export async function rpcMessageHandler(message: WalletRequests, port: chrome.runtime.Port) { @@ -62,6 +63,11 @@ export async function rpcMessageHandler(message: WalletRequests, port: chrome.ru break; } + case 'stx_getAddresses': { + await rpcStxGetAddresses(message, port); + break; + } + default: chrome.tabs.sendMessage( getTabIdFromPort(port), diff --git a/src/background/messaging/rpc-methods/get-addresses.ts b/src/background/messaging/rpc-methods/get-addresses.ts index 4f9fea7ec8..a8e8307e22 100644 --- a/src/background/messaging/rpc-methods/get-addresses.ts +++ b/src/background/messaging/rpc-methods/get-addresses.ts @@ -1,4 +1,4 @@ -import { type GetAddressesRequest, RpcErrorCode } from '@leather.io/rpc'; +import { type LeatherRpcMethodMap, type MethodNames, RpcErrorCode } from '@leather.io/rpc'; import { RouteUrls } from '@shared/route-urls'; import { makeRpcErrorResponse } from '@shared/rpc/rpc-methods'; @@ -10,20 +10,26 @@ import { } from '../messaging-utils'; import { trackRpcRequestSuccess } from '../rpc-message-handler'; -export async function rpcGetAddresses(message: GetAddressesRequest, port: chrome.runtime.Port) { - const { urlParams, tabId } = makeSearchParamsWithDefaults(port, [['requestId', message.id]]); - const { id } = await triggerRequestWindowOpen(RouteUrls.RpcGetAddresses, urlParams); - void trackRpcRequestSuccess({ endpoint: message.method }); +export function makeRpcAddressesMessageListerner( + eventName: 'getAddresses' | 'stx_getAddresses' +) { + return async (message: LeatherRpcMethodMap[T]['request'], port: chrome.runtime.Port) => { + const { urlParams, tabId } = makeSearchParamsWithDefaults(port, [['requestId', message.id]]); + const { id } = await triggerRequestWindowOpen(RouteUrls.RpcGetAddresses, urlParams); + void trackRpcRequestSuccess({ endpoint: message.method }); - listenForPopupClose({ - tabId, - id, - response: makeRpcErrorResponse('getAddresses', { - id: message.id, - error: { - code: RpcErrorCode.USER_REJECTION, - message: 'User rejected request to get addresses', - }, - }), - }); + listenForPopupClose({ + tabId, + id, + response: makeRpcErrorResponse(eventName, { + id: message.id, + error: { + code: RpcErrorCode.USER_REJECTION, + message: 'User rejected request to get addresses', + }, + }), + }); + }; } + +export const rpcGetAddresses = makeRpcAddressesMessageListerner('getAddresses'); diff --git a/src/background/messaging/rpc-methods/stx-get-addresses.ts b/src/background/messaging/rpc-methods/stx-get-addresses.ts new file mode 100644 index 0000000000..2c9eef77c4 --- /dev/null +++ b/src/background/messaging/rpc-methods/stx-get-addresses.ts @@ -0,0 +1,4 @@ +import { makeRpcAddressesMessageListerner } from './get-addresses'; + +// Method simply clones behaviour of getAddresses action +export const rpcStxGetAddresses = makeRpcAddressesMessageListerner('stx_getAddresses'); diff --git a/tests/specs/rpc-get-addresses/get-addresses.spec.ts b/tests/specs/rpc-get-addresses/get-addresses.spec.ts index 0ef6fc8ebb..17cd0c0f68 100644 --- a/tests/specs/rpc-get-addresses/get-addresses.spec.ts +++ b/tests/specs/rpc-get-addresses/get-addresses.spec.ts @@ -6,6 +6,9 @@ import type { SupportedBlockchains } from '@leather.io/models'; import { test } from '../../fixtures/fixtures'; +const getAddressesMethods = ['getAddresses', 'stx_getAddresses'] as const; +type GetAddressesMethods = (typeof getAddressesMethods)[number]; + function softwareBeforeEach() { return () => test.beforeEach( @@ -62,8 +65,11 @@ async function interceptRequestPopup(context: BrowserContext) { return context.waitForEvent('page'); } -async function initiateGetAddresses(page: Page) { - return page.evaluate(async () => (window as any).LeatherProvider?.request('getAddresses')); +async function initiateGetAddresses(page: Page, eventName: GetAddressesMethods) { + return page.evaluate( + async eventName => (window as any).LeatherProvider?.request(eventName), + eventName + ); } async function clickConnectLeatherButton(popup: Page) { @@ -72,93 +78,96 @@ async function clickConnectLeatherButton(popup: Page) { await button.click(); } -test.describe('Rpc: GetAddresses', () => { - test.beforeEach( - async ({ extensionId, globalPage }) => await globalPage.setupAndUseApiCalls(extensionId) - ); - - const specs = { - softwareWallet: { - beforeEach: softwareBeforeEach(), - expectedResult: getExpectedResponseForKeys(['bitcoin', 'stacks']), - }, - ledgerWithBitcoinAndStacksKey: { - beforeEach: ledgerBeforeEach(makeLedgerTestAccountWalletState(['bitcoin', 'stacks'])), - expectedResult: getExpectedResponseForKeys(['bitcoin', 'stacks']), - }, - ledgerWithStacksKeysOnly: { - beforeEach: ledgerBeforeEach(makeLedgerTestAccountWalletState(['stacks'])), - expectedResult: getExpectedResponseForKeys(['stacks']), - }, - ledgerWithBitcoinKeysOnly: { - beforeEach: ledgerBeforeEach(makeLedgerTestAccountWalletState(['bitcoin'])), - expectedResult: getExpectedResponseForKeys(['bitcoin']), - }, - } as const; - - for (const [walletPreset, { beforeEach, expectedResult }] of Object.entries(specs)) { - test.describe(`${walletPreset} `, () => { - beforeEach(); - - test('the promise resolves with addresses successfully', async ({ page, context }) => { - await page.goto('localhost:3000'); - const getAddressesPromise = initiateGetAddresses(page); - - const popup = await interceptRequestPopup(context); - await clickConnectLeatherButton(popup); - - const result = await getAddressesPromise; - if (!result) throw new Error('Expected result'); - const { id, ...payloadWithoutId } = result; - - test.expect(payloadWithoutId).toEqual(expectedResult); - }); - - test('the promise rejects when user closes popup window', async ({ page, context }) => { - await page.goto('localhost:3000'); - const getAddressesPromise = initiateGetAddresses(page); - const popup = await interceptRequestPopup(context); - await popup.close(); - await test.expect(getAddressesPromise).rejects.toThrow(); - }); +// +getAddressesMethods.forEach(method => { + test.describe(`Rpc: ${method}`, () => { + test.beforeEach( + async ({ extensionId, globalPage }) => await globalPage.setupAndUseApiCalls(extensionId) + ); - if (walletPreset === 'softwareWallet') { - test('it redirects back to get addresses flow when wallet is locked', async ({ - homePage, - page, - context, - }) => { - await homePage.lock(); + const specs = { + softwareWallet: { + beforeEach: softwareBeforeEach(), + expectedResult: getExpectedResponseForKeys(['bitcoin', 'stacks']), + }, + ledgerWithBitcoinAndStacksKey: { + beforeEach: ledgerBeforeEach(makeLedgerTestAccountWalletState(['bitcoin', 'stacks'])), + expectedResult: getExpectedResponseForKeys(['bitcoin', 'stacks']), + }, + ledgerWithStacksKeysOnly: { + beforeEach: ledgerBeforeEach(makeLedgerTestAccountWalletState(['stacks'])), + expectedResult: getExpectedResponseForKeys(['stacks']), + }, + ledgerWithBitcoinKeysOnly: { + beforeEach: ledgerBeforeEach(makeLedgerTestAccountWalletState(['bitcoin'])), + expectedResult: getExpectedResponseForKeys(['bitcoin']), + }, + } as const; + + Object.entries(specs).forEach(([walletPreset, { beforeEach, expectedResult }]) => { + test.describe(`${walletPreset} `, () => { + beforeEach(); + + test('the promise resolves with addresses successfully', async ({ page, context }) => { await page.goto('localhost:3000'); - const getAddressesPromise = initiateGetAddresses(page); + const getAddressesPromise = initiateGetAddresses(page, method); + const popup = await interceptRequestPopup(context); - await popup.getByRole('textbox').fill(TEST_PASSWORD); - await popup.getByRole('button', { name: 'Continue' }).click(); - await popup.getByText('Connect').isVisible(); - await test.expect(popup.getByTestId('get-addresses-approve-button')).toBeVisible(); await clickConnectLeatherButton(popup); - await test.expect(getAddressesPromise).resolves.toMatchObject(expectedResult); + + const result = await getAddressesPromise; + if (!result) throw new Error('Expected result'); + const { id, ...payloadWithoutId } = result; + + test.expect(payloadWithoutId).toEqual(expectedResult); }); - test('it returns the second accounts data after changing account', async ({ - page, - context, - }) => { + test('the promise rejects when user closes popup window', async ({ page, context }) => { await page.goto('localhost:3000'); - const getAddressesPromise = initiateGetAddresses(page); + const getAddressesPromise = initiateGetAddresses(page, method); const popup = await interceptRequestPopup(context); - const switchAccountButton = popup.getByTestId('switch-account-item-0'); - await switchAccountButton.click(); - const secondAccountInListButton = popup.getByTestId('switch-account-item-1'); - await secondAccountInListButton.click(); - await test.expect(popup.getByText('Account 2')).toBeVisible(); - await clickConnectLeatherButton(popup); - const result = await getAddressesPromise; - test - .expect(result.result.addresses[0].address) - .toEqual('bc1qr9wmz342txkcmxxq5ysmfqrd5rvmxtu6nldjgp'); + await popup.close(); + await test.expect(getAddressesPromise).rejects.toThrow(); }); - } + + if (walletPreset === 'softwareWallet') { + test('it redirects back to get addresses flow when wallet is locked', async ({ + homePage, + page, + context, + }) => { + await homePage.lock(); + await page.goto('localhost:3000'); + const getAddressesPromise = initiateGetAddresses(page, method); + const popup = await interceptRequestPopup(context); + await popup.getByRole('textbox').fill(TEST_PASSWORD); + await popup.getByRole('button', { name: 'Continue' }).click(); + await popup.getByText('Connect').isVisible(); + await test.expect(popup.getByTestId('get-addresses-approve-button')).toBeVisible(); + await clickConnectLeatherButton(popup); + await test.expect(getAddressesPromise).resolves.toMatchObject(expectedResult); + }); + + test('it returns the second accounts data after changing account', async ({ + page, + context, + }) => { + await page.goto('localhost:3000'); + const getAddressesPromise = initiateGetAddresses(page, method); + const popup = await interceptRequestPopup(context); + const switchAccountButton = popup.getByTestId('switch-account-item-0'); + await switchAccountButton.click(); + const secondAccountInListButton = popup.getByTestId('switch-account-item-1'); + await secondAccountInListButton.click(); + await test.expect(popup.getByText('Account 2')).toBeVisible(); + await clickConnectLeatherButton(popup); + const result = await getAddressesPromise; + test + .expect(result.result.addresses[0].address) + .toEqual('bc1qr9wmz342txkcmxxq5ysmfqrd5rvmxtu6nldjgp'); + }); + } + }); }); - } + }); });