Skip to content

Commit

Permalink
fix: infinite ethers/ethers5 network requests
Browse files Browse the repository at this point in the history
  • Loading branch information
magiziz committed Oct 15, 2024
1 parent 185ff63 commit 343d20e
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 12 deletions.
7 changes: 5 additions & 2 deletions packages/adapters/ethers/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,9 +631,12 @@ export class EthersAdapter {
if (provider) {
const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(provider)
const firstAddress = addresses?.[0]
const caipAddress = `${this.chainNamespace}:${chainId}:${firstAddress}` as CaipAddress
const caipNetwork = this.caipNetworks.find(c => c.id === chainId) ?? this.caipNetworks[0]
const caipAddress =
`${this.chainNamespace}:${caipNetwork?.id}:${firstAddress}` as CaipAddress

if (firstAddress && chainId) {
if (firstAddress && caipNetwork) {
this.appKit?.setCaipNetwork(caipNetwork)
this.appKit?.setCaipAddress(caipAddress, this.chainNamespace)
ProviderUtil.setProviderId('eip155', providerId)
ProviderUtil.setProvider<Provider>('eip155', provider)
Expand Down
68 changes: 64 additions & 4 deletions packages/adapters/ethers/src/tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
mainnet as AppkitMainnet,
polygon as AppkitPolygon,
optimism as AppkitOptimism,
bsc as AppkitBsc
bsc as AppkitBsc,
harmonyOne as AppkitHarmonyOne
} from '@reown/appkit/networks'
import { ProviderUtil, type ProviderIdType } from '@reown/appkit/store'
import { SafeLocalStorage, SafeLocalStorageKeys } from '@reown/appkit-common'
Expand Down Expand Up @@ -107,17 +108,40 @@ vi.mock('ethers', async () => {
}
})

vi.mock('@reown/appkit-common', async importOriginal => {
const actual = await importOriginal()
return {
// @ts-expect-error - actual is not typed
...actual,
SafeLocalStorage: {
getItem: vi.fn(key => {
const values = {
'@appkit/wallet_id': 'injected'
}
return values[key as keyof typeof values]
}),
setItem: vi.fn(),
removeItem: vi.fn()
}
}
})

describe('EthersAdapter', () => {
let client: EthersAdapter

beforeEach(() => {
vi.clearAllMocks()
const ethersConfig = mockCreateEthersConfig()
client = new EthersAdapter()
vi.spyOn(client as any, 'createEthersConfig').mockImplementation(() => ({
metadata: ethersConfig.metadata,
injected: ethersConfig.injected
}))
const optionsWithEthersConfig = {
...mockOptions,
networks: caipNetworks,
defaultNetwork: undefined,
ethersConfig: mockCreateEthersConfig()
ethersConfig
}
client.construct(mockAppKit, optionsWithEthersConfig)
})
Expand Down Expand Up @@ -650,17 +674,23 @@ describe('EthersAdapter', () => {
describe('EthersClient - syncAccount', () => {
beforeEach(() => {
vi.spyOn(client as any, 'syncConnectedWalletInfo').mockImplementation(() => {})
vi.spyOn(client as any, 'setupProviderListeners').mockImplementation(() => {})
vi.spyOn(client as any, 'setProvider').mockImplementation(() => Promise.resolve())
vi.spyOn(client as any, 'syncProfile').mockImplementation(() => Promise.resolve())
vi.spyOn(client as any, 'syncBalance').mockImplementation(() => Promise.resolve())
vi.spyOn(mockAppKit, 'getIsConnectedState').mockReturnValue(true)
vi.spyOn(mockAppKit, 'getPreferredAccountType').mockReturnValue('eoa')
})

it('should sync account when connected and address is provided', async () => {
const mockAddress = '0x1234567890123456789012345678901234567890'
const mockCaipNetwork = mainnet

vi.spyOn(mockAppKit, 'getIsConnectedState').mockReturnValue(true)
vi.spyOn(mockAppKit, 'getCaipNetwork').mockReturnValue(mockCaipNetwork)
vi.spyOn(mockAppKit, 'getPreferredAccountType').mockReturnValue('eoa')
vi.spyOn(EthersHelpersUtil, 'getUserInfo').mockResolvedValue({
addresses: ['0x1234567890123456789012345678901234567890'],
chainId: 1
})

await client['syncAccount']({ address: mockAddress })

Expand All @@ -671,9 +701,39 @@ describe('EthersAdapter', () => {
)
expect(client['syncConnectedWalletInfo']).toHaveBeenCalled()
expect(client['syncProfile']).toHaveBeenCalledWith(mockAddress)
expect(client['setupProviderListeners']).toHaveBeenCalledOnce()
expect(client['setProvider']).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipAddress).toHaveBeenCalledTimes(2)
expect(mockAppKit.setCaipAddress).toHaveBeenCalledWith(
`eip155:${mainnet.id}:${mockAddress}`,
'eip155'
)
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledWith(mainnet)
expect(mockAppKit.setApprovedCaipNetworksData).toHaveBeenCalledWith('eip155')
})

it('it should fallback to first available chain if current chain is unsupported', async () => {
const mockAddress = '0x1234567890123456789012345678901234567890'

vi.spyOn(EthersHelpersUtil, 'getUserInfo').mockResolvedValue({
addresses: [mockAddress],
chainId: AppkitHarmonyOne.id as number
})

await client['syncAccount']({ address: mockAddress })

expect(client['setupProviderListeners']).toHaveBeenCalledOnce()
expect(client['setProvider']).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipAddress).toHaveBeenCalledTimes(2)
expect(mockAppKit.setCaipAddress).toHaveBeenCalledWith(
`eip155:${mainnet.id}:${mockAddress}`,
'eip155'
)
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledWith(mainnet)
})

it('should reset connection when not connected', async () => {
vi.spyOn(mockAppKit, 'getIsConnectedState').mockReturnValue(false)

Expand Down
7 changes: 5 additions & 2 deletions packages/adapters/ethers5/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,9 +607,12 @@ export class Ethers5Adapter {
if (provider) {
const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(provider)
const firstAddress = addresses?.[0]
const caipAddress = `${this.chainNamespace}:${chainId}:${firstAddress}` as CaipAddress
const caipNetwork = this.caipNetworks.find(c => c.id === chainId) ?? this.caipNetworks[0]
const caipAddress =
`${this.chainNamespace}:${caipNetwork?.id}:${firstAddress}` as CaipAddress

if (firstAddress && chainId) {
if (firstAddress && caipNetwork) {
this.appKit?.setCaipNetwork(caipNetwork)
this.appKit?.setCaipAddress(caipAddress, this.chainNamespace)
ProviderUtil.setProviderId('eip155', providerId)
ProviderUtil.setProvider<Provider>('eip155', provider)
Expand Down
68 changes: 64 additions & 4 deletions packages/adapters/ethers5/src/tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
mainnet as AppkitMainnet,
polygon as AppkitPolygon,
optimism as AppkitOptimism,
bsc as AppkitBsc
bsc as AppkitBsc,
harmonyOne as AppkitHarmonyOne
} from '@reown/appkit/networks'
import { ProviderUtil, type ProviderIdType } from '@reown/appkit/store'
import { SafeLocalStorage, SafeLocalStorageKeys } from '@reown/appkit-common'
Expand Down Expand Up @@ -113,17 +114,40 @@ vi.mock('ethers', async () => {
}
})

vi.mock('@reown/appkit-common', async importOriginal => {
const actual = await importOriginal()
return {
// @ts-expect-error - actual is not typed
...actual,
SafeLocalStorage: {
getItem: vi.fn(key => {
const values = {
'@appkit/wallet_id': 'injected'
}
return values[key as keyof typeof values]
}),
setItem: vi.fn(),
removeItem: vi.fn()
}
}
})

describe('EthersAdapter', () => {
let client: Ethers5Adapter

beforeEach(() => {
vi.clearAllMocks()
const ethersConfig = mockCreateEthersConfig()
client = new Ethers5Adapter()
vi.spyOn(client as any, 'createEthersConfig').mockImplementation(() => ({
metadata: ethersConfig.metadata,
injected: ethersConfig.injected
}))
const optionsWithEthersConfig = {
...mockOptions,
networks: caipNetworks,
defaultNetwork: undefined,
ethersConfig: mockCreateEthersConfig()
ethersConfig
}
client.construct(mockAppKit, optionsWithEthersConfig)
})
Expand Down Expand Up @@ -656,17 +680,23 @@ describe('EthersAdapter', () => {
describe('EthersClient - syncAccount', () => {
beforeEach(() => {
vi.spyOn(client as any, 'syncConnectedWalletInfo').mockImplementation(() => {})
vi.spyOn(client as any, 'setupProviderListeners').mockImplementation(() => {})
vi.spyOn(client as any, 'setProvider').mockImplementation(() => Promise.resolve())
vi.spyOn(client as any, 'syncProfile').mockImplementation(() => Promise.resolve())
vi.spyOn(client as any, 'syncBalance').mockImplementation(() => Promise.resolve())
vi.spyOn(mockAppKit, 'getIsConnectedState').mockReturnValue(true)
vi.spyOn(mockAppKit, 'getPreferredAccountType').mockReturnValue('eoa')
})

it('should sync account when connected and address is provided', async () => {
const mockAddress = '0x1234567890123456789012345678901234567890'
const mockCaipNetwork = mainnet

vi.spyOn(mockAppKit, 'getIsConnectedState').mockReturnValue(true)
vi.spyOn(mockAppKit, 'getCaipNetwork').mockReturnValue(mockCaipNetwork)
vi.spyOn(mockAppKit, 'getPreferredAccountType').mockReturnValue('eoa')
vi.spyOn(EthersHelpersUtil, 'getUserInfo').mockResolvedValue({
addresses: ['0x1234567890123456789012345678901234567890'],
chainId: 1
})

await client['syncAccount']({ address: mockAddress })

Expand All @@ -677,9 +707,39 @@ describe('EthersAdapter', () => {
)
expect(client['syncConnectedWalletInfo']).toHaveBeenCalled()
expect(client['syncProfile']).toHaveBeenCalledWith(mockAddress)
expect(client['setupProviderListeners']).toHaveBeenCalledOnce()
expect(client['setProvider']).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipAddress).toHaveBeenCalledTimes(2)
expect(mockAppKit.setCaipAddress).toHaveBeenCalledWith(
`eip155:${mainnet.id}:${mockAddress}`,
'eip155'
)
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledWith(mainnet)
expect(mockAppKit.setApprovedCaipNetworksData).toHaveBeenCalledWith('eip155')
})

it('it should fallback to first available chain if current chain is unsupported', async () => {
const mockAddress = '0x1234567890123456789012345678901234567890'

vi.spyOn(EthersHelpersUtil, 'getUserInfo').mockResolvedValue({
addresses: [mockAddress],
chainId: AppkitHarmonyOne.id as number
})

await client['syncAccount']({ address: mockAddress })

expect(client['setupProviderListeners']).toHaveBeenCalledOnce()
expect(client['setProvider']).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipAddress).toHaveBeenCalledTimes(2)
expect(mockAppKit.setCaipAddress).toHaveBeenCalledWith(
`eip155:${mainnet.id}:${mockAddress}`,
'eip155'
)
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledOnce()
expect(mockAppKit.setCaipNetwork).toHaveBeenCalledWith(mainnet)
})

it('should reset connection when not connected', async () => {
vi.spyOn(mockAppKit, 'getIsConnectedState').mockReturnValue(false)

Expand Down

0 comments on commit 343d20e

Please sign in to comment.