From a9d7686eac8a95d8a1235504a302e8ae153ebf5d Mon Sep 17 00:00:00 2001 From: Felipe Mendes Date: Thu, 12 Dec 2024 11:16:39 -0300 Subject: [PATCH] fix: correct return of chain when connecting to bitcoin (#3450) --- .changeset/tender-moles-notice.md | 22 ++++++++++++++ packages/adapters/bitcoin/src/adapter.ts | 8 ++++- .../src/connectors/SatsConnectConnector.ts | 2 +- .../src/connectors/WalletStandardConnector.ts | 14 ++++++++- .../bitcoin/tests/BitcoinAdapter.test.ts | 30 +++++++++++++++++++ .../connectors/SatsConnectConnector.test.ts | 13 +++++--- .../WalletStandardConnector.test.ts | 25 ++++++++++++---- 7 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 .changeset/tender-moles-notice.md diff --git a/.changeset/tender-moles-notice.md b/.changeset/tender-moles-notice.md new file mode 100644 index 0000000000..bbdac6a1dd --- /dev/null +++ b/.changeset/tender-moles-notice.md @@ -0,0 +1,22 @@ +--- +'@reown/appkit-adapter-ethers': patch +'@reown/appkit-adapter-ethers5': patch +'@reown/appkit-adapter-solana': patch +'@reown/appkit-adapter-wagmi': patch +'@reown/appkit': patch +'@reown/appkit-utils': patch +'@reown/appkit-cdn': patch +'@reown/appkit-cli': patch +'@reown/appkit-common': patch +'@reown/appkit-core': patch +'@reown/appkit-experimental': patch +'@reown/appkit-polyfills': patch +'@reown/appkit-scaffold-ui': patch +'@reown/appkit-siwe': patch +'@reown/appkit-siwx': patch +'@reown/appkit-ui': patch +'@reown/appkit-wallet': patch +'@reown/appkit-wallet-button': patch +--- + +Fix the chainId response when connecting to bitcoin with multichain adapters diff --git a/packages/adapters/bitcoin/src/adapter.ts b/packages/adapters/bitcoin/src/adapter.ts index ebe63f6adf..044c775e42 100644 --- a/packages/adapters/bitcoin/src/adapter.ts +++ b/packages/adapters/bitcoin/src/adapter.ts @@ -63,11 +63,17 @@ export class BitcoinAdapter extends AdapterBlueprint { this.connector = connector this.bindEvents(this.connector) + const chain = connector.chains.find(c => c.id === params.chainId) || connector.chains[0] + + if (!chain) { + throw new Error('The connector does not support any of the requested chains') + } + return { id: connector.id, type: connector.type, address, - chainId: this.networks[0]?.id || '', + chainId: chain.id, provider: connector.provider } } diff --git a/packages/adapters/bitcoin/src/connectors/SatsConnectConnector.ts b/packages/adapters/bitcoin/src/connectors/SatsConnectConnector.ts index 28295a9a2e..912c59ff5a 100644 --- a/packages/adapters/bitcoin/src/connectors/SatsConnectConnector.ts +++ b/packages/adapters/bitcoin/src/connectors/SatsConnectConnector.ts @@ -52,7 +52,7 @@ export class SatsConnectConnector extends ProviderEventEmitter implements Bitcoi } public get chains() { - return this.requestedChains + return this.requestedChains.filter(chain => chain.chainNamespace === 'bip122') } async request(args: RequestArguments) { diff --git a/packages/adapters/bitcoin/src/connectors/WalletStandardConnector.ts b/packages/adapters/bitcoin/src/connectors/WalletStandardConnector.ts index 38467245a3..5997c4717f 100644 --- a/packages/adapters/bitcoin/src/connectors/WalletStandardConnector.ts +++ b/packages/adapters/bitcoin/src/connectors/WalletStandardConnector.ts @@ -6,6 +6,7 @@ import type { BitcoinFeatures } from '../utils/wallet-standard/WalletFeatures.js import type { Provider, RequestArguments } from '@reown/appkit-core' import { ProviderEventEmitter } from '../utils/ProviderEventEmitter.js' import { MethodNotSupportedError } from '../errors/MethodNotSupportedError.js' +import { bitcoin, bitcoinTestnet } from '@reown/appkit/networks' export class WalletStandardConnector extends ProviderEventEmitter implements BitcoinConnector { public readonly chain = 'bip122' @@ -39,7 +40,18 @@ export class WalletStandardConnector extends ProviderEventEmitter implements Bit public get chains() { return this.wallet.chains - .map(chainId => this.requestedChains.find(chain => chain.caipNetworkId === chainId)) + .map(chainId => + this.requestedChains.find(chain => { + switch (chainId) { + case 'bitcoin:mainnet': + return chain.caipNetworkId === bitcoin.caipNetworkId + case 'bitcoin:testnet': + return chain.caipNetworkId === bitcoinTestnet.caipNetworkId + default: + return chain.caipNetworkId === chainId + } + }) + ) .filter(Boolean) as CaipNetwork[] } diff --git a/packages/adapters/bitcoin/tests/BitcoinAdapter.test.ts b/packages/adapters/bitcoin/tests/BitcoinAdapter.test.ts index e2177010fc..a4ee6627d0 100644 --- a/packages/adapters/bitcoin/tests/BitcoinAdapter.test.ts +++ b/packages/adapters/bitcoin/tests/BitcoinAdapter.test.ts @@ -3,6 +3,8 @@ import { BitcoinAdapter } from '../src' import type { BitcoinApi } from '../src/utils/BitcoinApi' import { bitcoin, mainnet } from '@reown/appkit/networks' import { mockUTXO } from './mocks/mockUTXO' +import { SatsConnectConnector } from '../src/connectors/SatsConnectConnector' +import { mockSatsConnectProvider } from './mocks/mockSatsConnect' function mockBitcoinApi(): { [K in keyof BitcoinApi.Interface]: Mock } { return { @@ -19,6 +21,34 @@ describe('BitcoinAdapter', () => { adapter = new BitcoinAdapter({ api }) }) + describe('connect', () => { + it('should return the chainId of the available chain from connector', async () => { + const connector = new SatsConnectConnector({ + provider: mockSatsConnectProvider().provider, + requestedChains: [bitcoin], + getActiveNetwork: () => bitcoin + }) + vi.spyOn(connector, 'connect').mockResolvedValueOnce('mock_address') + + adapter.connectors.push(connector) + + const result = await adapter.connect({ + id: connector.id, + chainId: 'bitcoin:any_chain_id', + provider: connector.provider, + type: 'mock_type' + }) + + expect(result).toEqual({ + id: connector.id, + type: connector.type, + address: 'mock_address', + chainId: bitcoin.id, + provider: connector.provider + }) + }) + }) + describe('getBalance', () => { it('should return the balance', async () => { api.getUTXOs.mockResolvedValueOnce([ diff --git a/packages/adapters/bitcoin/tests/connectors/SatsConnectConnector.test.ts b/packages/adapters/bitcoin/tests/connectors/SatsConnectConnector.test.ts index d5780753ba..9b178eff11 100644 --- a/packages/adapters/bitcoin/tests/connectors/SatsConnectConnector.test.ts +++ b/packages/adapters/bitcoin/tests/connectors/SatsConnectConnector.test.ts @@ -3,7 +3,7 @@ import { SatsConnectConnector } from '../../src/connectors/SatsConnectConnector' import { mockSatsConnectProvider } from '../mocks/mockSatsConnect' import type { CaipNetwork } from '@reown/appkit-common' import { MessageSigningProtocols } from 'sats-connect' -import { bitcoin } from '@reown/appkit/networks' +import { bitcoin, bitcoinTestnet, mainnet } from '@reown/appkit/networks' describe('SatsConnectConnector', () => { let connector: SatsConnectConnector @@ -12,7 +12,12 @@ describe('SatsConnectConnector', () => { let getActiveNetwork: Mock<() => CaipNetwork | undefined> beforeEach(() => { - requestedChains = [] + // requested chains may contain not bip122 chains + requestedChains = [ + { ...mainnet, caipNetworkId: 'eip155:1', chainNamespace: 'eip155' }, + bitcoin, + bitcoinTestnet + ] mocks = mockSatsConnectProvider() getActiveNetwork = vi.fn(() => bitcoin) connector = new SatsConnectConnector({ @@ -39,7 +44,7 @@ describe('SatsConnectConnector', () => { expect(connector.id).toBe(mocks.provider.name) expect(connector.name).toBe(mocks.provider.name) expect(connector.imageUrl).toBe(mocks.provider.icon) - expect(connector.chains).toEqual(requestedChains) + expect(connector.chains).toEqual([bitcoin, bitcoinTestnet]) }) it('should disconnect correctly', async () => { @@ -309,7 +314,7 @@ describe('SatsConnectConnector', () => { await callback?.({ type: 'networkChange' }) - expect(emitSpy).toHaveBeenCalledWith('chainChanged', requestedChains) + expect(emitSpy).toHaveBeenCalledWith('chainChanged', [bitcoin, bitcoinTestnet]) }) }) }) diff --git a/packages/adapters/bitcoin/tests/connectors/WalletStandardConnector.test.ts b/packages/adapters/bitcoin/tests/connectors/WalletStandardConnector.test.ts index 024215bdd4..3400133295 100644 --- a/packages/adapters/bitcoin/tests/connectors/WalletStandardConnector.test.ts +++ b/packages/adapters/bitcoin/tests/connectors/WalletStandardConnector.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import type { CaipNetwork } from '@reown/appkit-common' -import { bitcoin, bitcoinTestnet } from '@reown/appkit/networks' +import { bitcoin, bitcoinTestnet, mainnet } from '@reown/appkit/networks' import { WalletStandardConnector } from '../../src/connectors/WalletStandardConnector' import { mockWalletStandardProvider } from '../mocks/mockWalletStandard' import { MethodNotSupportedError } from '../../src/errors/MethodNotSupportedError' @@ -20,7 +20,12 @@ describe('WalletStandardConnector', () => { let requestedChains: CaipNetwork[] beforeEach(() => { - requestedChains = [bitcoin, bitcoinTestnet] + // requested chains may contain not bip122 chains + requestedChains = [ + { ...mainnet, caipNetworkId: 'eip155:1', chainNamespace: 'eip155' }, + bitcoin, + bitcoinTestnet + ] wallet = mockWalletStandardProvider() connector = new WalletStandardConnector({ wallet, @@ -40,10 +45,6 @@ describe('WalletStandardConnector', () => { expect(connector.imageUrl).toBe(wallet.icon) }) - it('should map correctly only chains that are requested and the wallet supports', async () => { - expect(connector.chains).toEqual([bitcoin]) - }) - it('should throw if feature is not available', async () => { wallet = mockWalletStandardProvider({ features: {} @@ -55,6 +56,18 @@ describe('WalletStandardConnector', () => { await expect(connector.connect()).rejects.toThrow(MethodNotSupportedError) }) + describe('chains', () => { + it('should map correctly only chains that are requested and the wallet supports', async () => { + expect(connector.chains).toEqual([bitcoin]) + }) + + it('should map network id aliases', async () => { + vi.spyOn(wallet, 'chains', 'get').mockReturnValueOnce(['bitcoin:mainnet', 'bitcoin:testnet']) + + expect(connector.chains).toEqual([bitcoin, bitcoinTestnet]) + }) + }) + describe('watchWallets', () => { it('should get wallets using the callback', async () => { const callbackMock = vi.fn((...args) => {