Skip to content

Commit

Permalink
Merge branch 'main' into fix/universal-provider-accounts-changed-event
Browse files Browse the repository at this point in the history
  • Loading branch information
zoruka authored Dec 13, 2024
2 parents 982b26f + 444d1dd commit 7cc04a6
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 20 deletions.
22 changes: 22 additions & 0 deletions .changeset/cool-pumas-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@reown/appkit-utils': patch
'@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-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
---

Add Bitcoin network image id
22 changes: 22 additions & 0 deletions .changeset/ninety-kangaroos-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@reown/appkit': patch
'@reown/appkit-core': patch
'@reown/appkit-adapter-ethers': patch
'@reown/appkit-adapter-ethers5': patch
'@reown/appkit-adapter-solana': patch
'@reown/appkit-adapter-wagmi': patch
'@reown/appkit-utils': patch
'@reown/appkit-cdn': patch
'@reown/appkit-cli': patch
'@reown/appkit-common': 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
---

Refactors disconnect business logic for multiple adapter use cases
4 changes: 3 additions & 1 deletion packages/appkit-utils/src/PresetsUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ export const PresetsUtil = {
// Solana networks
'5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': 'a1b58899-f671-4276-6a5e-56ca5bd59700',
'4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'a1b58899-f671-4276-6a5e-56ca5bd59700',
EtWTRABZaYq6iMfeYKouRu166VU2xqa1: 'a1b58899-f671-4276-6a5e-56ca5bd59700'
EtWTRABZaYq6iMfeYKouRu166VU2xqa1: 'a1b58899-f671-4276-6a5e-56ca5bd59700',
// Bitcoin
'000000000019d6689c085ae165831e93': '21c895fa-e105-4829-9434-378bb54fa600'
} as Record<string, string>,

ConnectorImageIds: {
Expand Down
8 changes: 0 additions & 8 deletions packages/appkit/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -905,20 +905,12 @@ export class AppKit {
const provider = ProviderUtil.getProvider<UniversalProvider | Provider | W3mFrameProvider>(
ChainController.state.activeChain as ChainNamespace
)

const providerType =
ProviderUtil.state.providerIds[ChainController.state.activeChain as ChainNamespace]

await adapter?.disconnect({ provider, providerType })

this.setStatus('disconnected', ChainController.state.activeChain as ChainNamespace)

StorageUtil.deleteConnectedConnector()
StorageUtil.deleteActiveCaipNetworkId()

ChainController.state.chains.forEach(chain => {
this.resetAccount(chain.namespace as ChainNamespace)
})
},
checkInstalled: (ids?: string[]) => {
if (!ids) {
Expand Down
44 changes: 44 additions & 0 deletions packages/core/src/controllers/ChainController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { EventsController } from './EventsController.js'
import { RouterController } from './RouterController.js'
import { StorageUtil } from '../utils/StorageUtil.js'
import { OptionsController } from './OptionsController.js'
import { ConnectionController } from './ConnectionController.js'

// -- Constants ----------------------------------------- //
const accountState: AccountControllerState = {
Expand Down Expand Up @@ -519,5 +520,48 @@ export const ChainController = {
allAccounts: []
})
)
},

async disconnect() {
try {
const disconnectResults = await Promise.allSettled(
Array.from(state.chains.entries()).map(async ([namespace, adapter]) => {
try {
if (adapter.connectionControllerClient?.disconnect) {
await adapter.connectionControllerClient.disconnect()
}
this.resetAccount(namespace)
this.resetNetwork(namespace)
} catch (error) {
throw new Error(`Failed to disconnect chain ${namespace}: ${(error as Error).message}`)
}
})
)

const failures = disconnectResults.filter(
(result): result is PromiseRejectedResult => result.status === 'rejected'
)

if (failures.length > 0) {
throw new Error(failures.map(f => f.reason.message).join(', '))
}

StorageUtil.deleteConnectedConnector()
ConnectionController.resetWcConnection()
EventsController.sendEvent({
type: 'track',
event: 'DISCONNECT_SUCCESS'
})
} catch (error) {
// eslint-disable-next-line no-console
console.error((error as Error).message || 'Failed to disconnect chains')
EventsController.sendEvent({
type: 'track',
event: 'DISCONNECT_ERROR',
properties: {
message: (error as Error).message || 'Failed to disconnect chains'
}
})
}
}
}
6 changes: 1 addition & 5 deletions packages/core/src/controllers/ConnectionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,6 @@ export const ConnectionController = {

async disconnect() {
try {
const connectionControllerClient = this._getClient()

const siwx = OptionsController.state.siwx
if (siwx) {
const activeCaipNetwork = ChainController.getActiveCaipNetwork()
Expand All @@ -261,9 +259,7 @@ export const ConnectionController = {
}
}

await connectionControllerClient?.disconnect()

this.resetWcConnection()
await ChainController.disconnect()
} catch (error) {
throw new Error('Failed to disconnect')
}
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/utils/TypeUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ export type Event =
| {
type: 'track'
event: 'DISCONNECT_ERROR'
properties?: {
message: string
}
}
| {
type: 'track'
Expand Down
106 changes: 100 additions & 6 deletions packages/core/tests/controllers/ChainController.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { beforeAll, describe, expect, it, vi } from 'vitest'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
ConstantsUtil,
SafeLocalStorageKeys,
Expand All @@ -11,6 +11,9 @@ import { type ConnectionControllerClient } from '../../src/controllers/Connectio
import type { NetworkControllerClient } from '../../exports/index.js'
import { RouterController } from '../../src/controllers/RouterController.js'
import { SafeLocalStorage } from '@reown/appkit-common'
import { StorageUtil } from '../../src/utils/StorageUtil.js'
import { ConnectionController } from '../../src/controllers/ConnectionController.js'
import { EventsController } from '../../src/controllers/EventsController.js'

// -- Setup --------------------------------------------------------------------
const chainNamespace = 'eip155' as ChainNamespace
Expand Down Expand Up @@ -127,7 +130,8 @@ const solanaAdapter = {
caipNetworks: [solanaCaipNetwork] as unknown as CaipNetwork[]
}

beforeAll(() => {
beforeEach(() => {
ChainController.state.noAdapters = false
ChainController.initialize([evmAdapter], requestedCaipNetworks)
})

Expand All @@ -154,7 +158,6 @@ describe('ChainController', () => {
})

it('should update state correctly on getApprovedCaipNetworkIds()', async () => {
const namespace = 'eip155'
const networkController = { ...networkControllerClient }
const networkControllerSpy = vi
.spyOn(networkController, 'getApprovedCaipNetworksData')
Expand All @@ -163,17 +166,19 @@ describe('ChainController', () => {
supportsAllNetworks: false
})
const evmAdapter = {
chainNamespace,
namespace: chainNamespace,
connectionControllerClient,
networkControllerClient: networkController,
caipNetworks: [] as CaipNetwork[]
}

// Need to re-initialize to set the spy properly
ChainController.initialize([evmAdapter], requestedCaipNetworks)
await ChainController.setApprovedCaipNetworksData(namespace)
await ChainController.setApprovedCaipNetworksData(chainNamespace)

expect(ChainController.getApprovedCaipNetworkIds(namespace)).toEqual(approvedCaipNetworkIds)
expect(ChainController.getApprovedCaipNetworkIds(chainNamespace)).toEqual(
approvedCaipNetworkIds
)
expect(networkControllerSpy).toHaveBeenCalled()
})

Expand Down Expand Up @@ -346,4 +351,93 @@ describe('ChainController', () => {
ChainController.initialize([], requestedCaipNetworks)
expect(ChainController.state.noAdapters).toBe(true)
})

it('should properly handle disconnect', async () => {
const evmConnectionController = { ...connectionControllerClient }
const solanaConnectionController = { ...connectionControllerClient }
const disconnectSpy = vi.spyOn(evmConnectionController, 'disconnect')
const disconnectSpy2 = vi.spyOn(solanaConnectionController, 'disconnect')

const customEvmAdapter = {
...evmAdapter,
connectionControllerClient: evmConnectionController
}
const customSolanaAdapter = {
...solanaAdapter,
connectionControllerClient: solanaConnectionController
}

const resetAccountSpy = vi.spyOn(ChainController, 'resetAccount')
const resetNetworkSpy = vi.spyOn(ChainController, 'resetNetwork')
const deleteConnectorSpy = vi.spyOn(StorageUtil, 'deleteConnectedConnector')
const resetWcConnectionSpy = vi.spyOn(ConnectionController, 'resetWcConnection')
const sendEventSpy = vi.spyOn(EventsController, 'sendEvent')

ChainController.initialize([customEvmAdapter, customSolanaAdapter], requestedCaipNetworks)

await ChainController.disconnect()

expect(disconnectSpy).toHaveBeenCalled()
expect(disconnectSpy2).toHaveBeenCalled()

expect(resetAccountSpy).toHaveBeenCalledWith(ConstantsUtil.CHAIN.EVM)
expect(resetAccountSpy).toHaveBeenCalledWith(ConstantsUtil.CHAIN.SOLANA)
expect(resetNetworkSpy).toHaveBeenCalledWith(ConstantsUtil.CHAIN.EVM)
expect(resetNetworkSpy).toHaveBeenCalledWith(ConstantsUtil.CHAIN.SOLANA)

expect(deleteConnectorSpy).toHaveBeenCalled()
expect(resetWcConnectionSpy).toHaveBeenCalled()

expect(sendEventSpy).toHaveBeenCalledWith({
type: 'track',
event: 'DISCONNECT_SUCCESS'
})

resetAccountSpy.mockRestore()
resetNetworkSpy.mockRestore()
deleteConnectorSpy.mockRestore()
resetWcConnectionSpy.mockRestore()
sendEventSpy.mockRestore()
})

it('should handle disconnect errors gracefully', async () => {
const evmConnectionController = {
...connectionControllerClient,
disconnect: vi.fn().mockRejectedValue(new Error('EVM disconnect failed'))
}
const solanaConnectionController = {
...connectionControllerClient,
disconnect: vi.fn().mockRejectedValue(new Error('Solana disconnect failed'))
}
const customEvmAdapter = {
...evmAdapter,
connectionControllerClient: evmConnectionController
}
const customSolanaAdapter = {
...solanaAdapter,
connectionControllerClient: solanaConnectionController
}
const sendEventSpy = vi.spyOn(EventsController, 'sendEvent')
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})

ChainController.initialize([customEvmAdapter, customSolanaAdapter], requestedCaipNetworks)

await ChainController.disconnect()

expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('EVM disconnect failed'))
expect(sendEventSpy).toHaveBeenCalledWith({
type: 'track',
event: 'DISCONNECT_ERROR',
properties: {
message: expect.stringContaining('EVM disconnect failed')
}
})
expect(sendEventSpy).not.toHaveBeenCalledWith({
type: 'track',
event: 'DISCONNECT_SUCCESS'
})

sendEventSpy.mockRestore()
consoleSpy.mockRestore()
})
})

0 comments on commit 7cc04a6

Please sign in to comment.