Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: exposed switch network method and hooks #3164

Merged
merged 30 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d649a83
feat: add switchNetwork and addNetwork functions to useAppKitNetwork …
enesozturk Oct 30, 2024
473b750
chore: linter issues
enesozturk Oct 30, 2024
c13ed22
fix: tests
enesozturk Oct 30, 2024
eea1cdc
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 30, 2024
f164e0b
chore: version const
enesozturk Oct 30, 2024
367898c
refactor: update method names
enesozturk Oct 30, 2024
e4e63ee
chore: add tests for appkit functions
enesozturk Oct 30, 2024
b2ccddc
refactor: remove add network logics
enesozturk Oct 30, 2024
168d8d0
chore: changeset file
enesozturk Oct 30, 2024
0696e1d
chore: linter issues
enesozturk Oct 30, 2024
b1462a0
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 30, 2024
33be069
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 31, 2024
beacd62
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 31, 2024
d8f1ea6
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 31, 2024
e2279fb
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 31, 2024
2e178b1
fix: lab hydration issue
enesozturk Oct 31, 2024
5a96859
chore: appkit hook hydration issue
enesozturk Oct 31, 2024
20afd35
chore: hooks comp hydration
enesozturk Oct 31, 2024
58506aa
chore: hydration
enesozturk Oct 31, 2024
ed766e2
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 31, 2024
6a25c80
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Oct 31, 2024
15a4eca
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
tomiir Oct 31, 2024
d3eda44
fix: disable typing network name to switch network button
enesozturk Oct 31, 2024
312b43c
refactor: swtich network button logics and styles
enesozturk Oct 31, 2024
9b46d07
Merge branch 'main' into refactor/exposed-switch-network-method-and-h…
enesozturk Nov 1, 2024
f88749b
chore: skip new test
enesozturk Nov 1, 2024
8635922
fix: wrong skipped test
enesozturk Nov 1, 2024
f020cad
chore: remove new hook button
enesozturk Nov 1, 2024
6e23957
chore: revert switch network button
enesozturk Nov 1, 2024
7801b98
chore: remove skip for test
enesozturk Nov 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .changeset/lazy-frogs-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'@reown/appkit-utils': patch
'@apps/laboratory': patch
'@reown/appkit': patch
'@reown/appkit-core': patch
'@apps/demo': patch
'@apps/gallery': patch
'@reown/appkit-adapter-ethers': patch
'@reown/appkit-adapter-ethers5': patch
'@reown/appkit-adapter-polkadot': patch
'@reown/appkit-adapter-solana': patch
'@reown/appkit-adapter-wagmi': patch
'@reown/appkit-cdn': 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
---

Adds switch network methods and hooks
38 changes: 4 additions & 34 deletions apps/laboratory/src/components/AppKitButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
import { useAppKit, useDisconnect, useAppKitAccount } from '@reown/appkit/react'
import {
Stack,
Card,
CardHeader,
Heading,
CardBody,
Box,
StackDivider,
Button
} from '@chakra-ui/react'
import { Stack, Card, CardHeader, Heading, CardBody, Box, StackDivider } from '@chakra-ui/react'
import { AppKitHooks } from './AppKitHooks'

export function AppKitButtons() {
const { open } = useAppKit()
const { isConnected } = useAppKitAccount()
const { disconnect } = useDisconnect()

return (
<Card marginTop={10}>
<CardHeader>
<Heading size="md">AppKit Interactions</Heading>
</CardHeader>

<CardBody>
<Stack divider={<StackDivider />} spacing="4">
<Stack divider={<StackDivider />} spacing="4" flexWrap="wrap">
<Box>
<Heading size="xs" textTransform="uppercase" pb="2">
Connect / Account Button
Expand All @@ -35,24 +22,7 @@ export function AppKitButtons() {
</Heading>
<w3m-network-button />
</Box>
<Box>
<Heading size="xs" textTransform="uppercase" pb="2">
Hooks Interactions
</Heading>
<Box display="flex" alignItems="center" columnGap={3}>
<Button data-testid="w3m-open-hook-button" onClick={() => open()}>
Open
</Button>

<Button
data-testid="disconnect-hook-button"
isDisabled={!isConnected}
onClick={() => disconnect()}
>
Disconnect
</Button>
</Box>
</Box>
<AppKitHooks />
</Stack>
</CardBody>
</Card>
Expand Down
49 changes: 49 additions & 0 deletions apps/laboratory/src/components/AppKitHooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useAppKit, useDisconnect, useAppKitAccount, useAppKitNetwork } from '@reown/appkit/react'
import { polygon, mainnet, solana, solanaTestnet, type AppKitNetwork } from '@reown/appkit/networks'
import { Heading, Box, Button } from '@chakra-ui/react'

export function AppKitHooks() {
const { open } = useAppKit()
const { isConnected } = useAppKitAccount()
const { caipNetwork, switchNetwork } = useAppKitNetwork()
const { disconnect } = useDisconnect()

function handleSwitchNetwork() {
const isEIPNamespace = caipNetwork?.chainNamespace === 'eip155'
// eslint-disable-next-line no-nested-ternary
const networkToSwitch: AppKitNetwork = isEIPNamespace
? caipNetwork?.id === polygon.id
? mainnet
: polygon
: caipNetwork?.id === solana.id
? solanaTestnet
: solana

switchNetwork(networkToSwitch)
}

return (
<Box>
<Heading size="xs" textTransform="uppercase" pb="2">
Hooks Interactions
</Heading>
<Box display="flex" alignItems="center" columnGap={3} flexWrap="wrap">
<Button data-testid="w3m-open-hook-button" onClick={() => open()}>
Open
</Button>

<Button
data-testid="disconnect-hook-button"
isDisabled={!isConnected}
onClick={() => disconnect()}
>
Disconnect
</Button>

<Button data-testid="switch-network-hook-button" onClick={handleSwitchNetwork}>
Switch Network
</Button>
</Box>
</Box>
)
}
4 changes: 4 additions & 0 deletions apps/laboratory/tests/shared/pages/ModalPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,4 +567,8 @@ export class ModalPage {

return signature as `0x${string}`
}

async switchNetworkWithHook() {
await this.page.getByTestId('switch-network-hook-button').click()
}
}
31 changes: 30 additions & 1 deletion apps/laboratory/tests/wallet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ sampleWalletTest('it should show disabled networks', async ({ library }) => {
})

sampleWalletTest('it should switch networks and sign', async ({ library }) => {
const chains = library === 'solana' ? ['Solana Testnet', 'Solana'] : ['Polygon', 'Ethereum']
const chains =
library === 'solana' ? [solanaTestnet.name, solana.name] : [polygon.name, mainnet.name]
const caipNetworkId =
library === 'solana' ? [solanaTestnet.id, solana.id] : [polygon.id, mainnet.id]

Expand Down Expand Up @@ -99,6 +100,34 @@ sampleWalletTest('it should switch networks and sign', async ({ library }) => {
await processChain(0)
})

sampleWalletTest('it should switch networks using hook', async ({ library }) => {
const chains = library === 'solana' ? ['Solana Testnet', 'Solana'] : ['Polygon', 'Ethereum']
enesozturk marked this conversation as resolved.
Show resolved Hide resolved
const caipNetworkId =
library === 'solana' ? [solanaTestnet.id, solana.id] : [polygon.id, mainnet.id]

async function processChain(index: number) {
if (index >= chains.length) {
return
}

const chainName = chains[index] ?? DEFAULT_CHAIN_NAME
// Switch network using hook button
await modalPage.switchNetworkWithHook()
await modalPage.openModal()
await modalPage.openNetworks()
await modalValidator.expectSwitchedNetwork(chainName)
await modalPage.closeModal()
await modalValidator.expectCaipAddressHaveCorrectNetworkId(
caipNetworkId[index] as CaipNetworkId
)

await processChain(index + 1)
}

// Start processing from the first chain
await processChain(0)
})

sampleWalletTest('it should show last connected network after refreshing', async ({ library }) => {
const chainName = library === 'solana' ? 'Solana Testnet' : 'Polygon'

Expand Down
5 changes: 5 additions & 0 deletions packages/appkit-utils/src/ErrorUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ export const ErrorUtil = {
UNAUTHORIZED_DOMAIN_NOT_ALLOWED: 'Unauthorized: origin not allowed'
},
ALERT_ERRORS: {
SWITCH_NETWORK_NOT_FOUND: {
shortMessage: 'Network Not Found',
longMessage:
"Network not found - please make sure it is included in 'networks' array in createAppKit function"
},
INVALID_APP_CONFIGURATION: {
shortMessage: 'Invalid App Configuration',
longMessage: () =>
Expand Down
20 changes: 19 additions & 1 deletion packages/appkit/exports/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { AppKitOptions } from '../src/utils/TypesUtil.js'
import { getAppKit } from '../src/library/react/index.js'
import { CoreHelperUtil } from '@reown/appkit-core'
import { PACKAGE_VERSION } from './constants.js'
import { useAppKitNetworkCore } from '@reown/appkit-core/react'
import type { AppKitNetwork } from '@reown/appkit/networks'

// -- Views ------------------------------------------------------------
export * from '@reown/appkit-scaffold-ui'
Expand Down Expand Up @@ -41,4 +43,20 @@ export type { AppKitOptions }

// -- Hooks ------------------------------------------------------------
export * from '../src/library/react/index.js'
export { useAppKitAccount, useAppKitNetwork } from '@reown/appkit-core/react'

export function useAppKitNetwork() {
const { caipNetwork, caipNetworkId, chainId } = useAppKitNetworkCore()

function switchNetwork(network: AppKitNetwork) {
modal?.switchNetwork(network)
}

return {
caipNetwork,
caipNetworkId,
chainId,
switchNetwork
}
}

export { useAppKitAccount } from '@reown/appkit-core/react'
17 changes: 17 additions & 0 deletions packages/appkit/exports/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { AppKitOptions } from '../src/utils/TypesUtil.js'
import { getAppKit } from '../src/library/vue/index.js'
import { CoreHelperUtil } from '@reown/appkit-core'
import { PACKAGE_VERSION } from './constants.js'
import { useAppKitNetworkCore } from '@reown/appkit-core/vue'
import type { AppKitNetwork } from '@reown/appkit/networks'

// -- Views ------------------------------------------------------------
export * from '@reown/appkit-scaffold-ui'
Expand Down Expand Up @@ -36,4 +38,19 @@ export { AppKit }
export type { AppKitOptions }

// -- Hooks ------------------------------------------------------------
export function useAppKitNetwork() {
const { caipNetwork, caipNetworkId, chainId } = useAppKitNetworkCore()

function switchNetwork(network: AppKitNetwork) {
modal?.switchNetwork(network)
}

return {
caipNetwork,
caipNetworkId,
chainId,
switchNetwork
}
}

export * from '../src/library/vue/index.js'
13 changes: 11 additions & 2 deletions packages/appkit/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { UniversalAdapterClient } from './universal-adapter/client.js'
import { CaipNetworksUtil, ErrorUtil } from '@reown/appkit-utils'
import type { W3mFrameTypes } from '@reown/appkit-wallet'
import { ProviderUtil } from './store/ProviderUtil.js'
import type { AppKitNetwork } from '@reown/appkit/networks'

// -- Export Controllers -------------------------------------------------------
export { AccountController }
Expand Down Expand Up @@ -116,8 +117,16 @@ export class AppKit {
return ChainController.state.activeCaipNetwork?.id
}

public switchNetwork(caipNetwork: CaipNetwork) {
return ChainController.switchActiveNetwork(caipNetwork)
public switchNetwork(appKitNetwork: AppKitNetwork) {
const network = this.caipNetworks.find(n => n.id === appKitNetwork.id)

if (!network) {
AlertController.open(ErrorUtil.ALERT_ERRORS.SWITCH_NETWORK_NOT_FOUND, 'error')

Comment on lines +123 to +125
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🙌

return
}

ChainController.switchActiveNetwork(network)
}

public getWalletProvider() {
Expand Down
18 changes: 18 additions & 0 deletions packages/appkit/src/tests/appkit.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { AppKit } from '../client'
import { mainnet, polygon } from '../networks/index.js'
import {
AccountController,
ModalController,
Expand Down Expand Up @@ -446,5 +447,22 @@ describe('Base', () => {
})
expect(result).toBe('connector-image-url')
})

it('should switch network when requested', async () => {
vi.mocked(ChainController.switchActiveNetwork).mockResolvedValue(undefined)

await appKit.switchNetwork(mainnet)

expect(ChainController.switchActiveNetwork).toHaveBeenCalledWith(
expect.objectContaining({
id: mainnet.id,
name: mainnet.name
})
)

await appKit.switchNetwork(polygon)

expect(ChainController.switchActiveNetwork).toHaveBeenCalledTimes(1)
})
enesozturk marked this conversation as resolved.
Show resolved Hide resolved
})
})
2 changes: 1 addition & 1 deletion packages/core/exports/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { CaipNetwork, CaipNetworkId } from '@reown/appkit-common'
import { ConnectionController } from '../src/controllers/ConnectionController.js'

// -- Hooks ------------------------------------------------------------
export function useAppKitNetwork(): {
export function useAppKitNetworkCore(): {
caipNetwork: CaipNetwork | undefined
chainId: number | string | undefined
caipNetworkId: CaipNetworkId | undefined
Expand Down
2 changes: 1 addition & 1 deletion packages/core/exports/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { onUnmounted } from 'vue'
import { ConnectionController } from '../src/controllers/ConnectionController.js'

// -- Hooks ------------------------------------------------------------
export function useAppKitNetwork(): {
export function useAppKitNetworkCore(): {
caipNetwork: CaipNetwork | undefined
chainId: number | string | undefined
caipNetworkId: CaipNetworkId | undefined
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/controllers/ChainController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ export const ChainController = {
}
},

addCaipNetwork(caipNetwork: AdapterNetworkState['caipNetwork']) {
if (!caipNetwork) {
return
}

state.chains.get(caipNetwork.chainNamespace)?.caipNetworks.push(caipNetwork)
},

async switchActiveNetwork(network: CaipNetwork) {
const networkControllerClient = this.getNetworkControllerClient(network.chainNamespace)

Expand Down
4 changes: 2 additions & 2 deletions packages/core/tests/hooks/react.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useAppKitNetwork, useAppKitAccount } from '../../exports/react'
import { useAppKitNetworkCore, useAppKitAccount } from '../../exports/react'

import type { CaipNetwork } from '@reown/appkit-common'
import { AccountController, ChainController } from '../../exports'
Expand Down Expand Up @@ -42,7 +42,7 @@ describe('useAppKitNetwork', () => {
activeCaipNetwork: mockNetwork
})

const { caipNetwork, chainId } = useAppKitNetwork()
const { caipNetwork, chainId } = useAppKitNetworkCore()

expect(caipNetwork).toBe(mockNetwork)
expect(chainId).toBe(1)
Expand Down
Loading