diff --git a/.changeset/nine-chicken-join.md b/.changeset/nine-chicken-join.md
new file mode 100644
index 0000000000..e34abb16fc
--- /dev/null
+++ b/.changeset/nine-chicken-join.md
@@ -0,0 +1,23 @@
+---
+'@reown/appkit': patch
+'@reown/appkit-core': patch
+'@reown/appkit-adapter-bitcoin': 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 AppKit client to handle syncBalance call for unsupported networks as expected
diff --git a/.changeset/selfish-rivers-shout.md b/.changeset/selfish-rivers-shout.md
new file mode 100644
index 0000000000..29764b5c42
--- /dev/null
+++ b/.changeset/selfish-rivers-shout.md
@@ -0,0 +1,23 @@
+---
+'@reown/appkit-adapter-wagmi': patch
+'@reown/appkit-adapter-bitcoin': patch
+'@reown/appkit-adapter-ethers': patch
+'@reown/appkit-adapter-ethers5': patch
+'@reown/appkit-adapter-solana': 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
+---
+
+Fixed an issue where `walletProvider` from the `useAppKitProvider` hook was `undefined` when the wallet was connected. This issue occurred only when using wagmi adapter.
diff --git a/.changeset/serious-ravens-appear.md b/.changeset/serious-ravens-appear.md
new file mode 100644
index 0000000000..bc77407a11
--- /dev/null
+++ b/.changeset/serious-ravens-appear.md
@@ -0,0 +1,23 @@
+---
+'@reown/appkit-siwx': patch
+'@reown/appkit-adapter-bitcoin': 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-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-ui': patch
+'@reown/appkit-wallet': patch
+'@reown/appkit-wallet-button': patch
+---
+
+Fixes issue where 1CA session would not be found because of non-cased addresses mismatching.'
diff --git a/.changeset/sweet-sloths-sneeze.md b/.changeset/sweet-sloths-sneeze.md
new file mode 100644
index 0000000000..550c1983ad
--- /dev/null
+++ b/.changeset/sweet-sloths-sneeze.md
@@ -0,0 +1,23 @@
+---
+'@reown/appkit-scaffold-ui': patch
+'@reown/appkit-adapter-bitcoin': 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-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-siwe': patch
+'@reown/appkit-siwx': patch
+'@reown/appkit-ui': patch
+'@reown/appkit-wallet': patch
+'@reown/appkit-wallet-button': patch
+---
+
+Removes duplicated all wallets button on AppKit Basic
diff --git a/.changeset/thin-bananas-try.md b/.changeset/thin-bananas-try.md
new file mode 100644
index 0000000000..da5a7e8b02
--- /dev/null
+++ b/.changeset/thin-bananas-try.md
@@ -0,0 +1,23 @@
+---
+'@reown/appkit-scaffold-ui': patch
+'@reown/appkit-core': patch
+'@reown/appkit-adapter-bitcoin': 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-utils': patch
+'@reown/appkit-cdn': patch
+'@reown/appkit-cli': patch
+'@reown/appkit-common': patch
+'@reown/appkit-experimental': patch
+'@reown/appkit-polyfills': patch
+'@reown/appkit-siwe': patch
+'@reown/appkit-siwx': patch
+'@reown/appkit-ui': patch
+'@reown/appkit-wallet': patch
+'@reown/appkit-wallet-button': patch
+---
+
+Fixes issue where onramp and activity were enabled in non-supported networks'
diff --git a/apps/builder/providers/appkit-provider.tsx b/apps/builder/providers/appkit-provider.tsx
index ff127109d6..836d288407 100644
--- a/apps/builder/providers/appkit-provider.tsx
+++ b/apps/builder/providers/appkit-provider.tsx
@@ -24,7 +24,7 @@ if (!projectId) {
const metadata = {
name: 'AppKit Builder',
description: 'The full stack toolkit to build onchain app UX',
- url: 'https://github.com/0xonerb/next-reown-appkit-ssr', // origin must match your domain & subdomain
+ url: window?.origin || 'https://demo.reown.com', // origin must match your domain & subdomain
icons: ['https://avatars.githubusercontent.com/u/179229932']
}
diff --git a/apps/laboratory/src/components/Ethers/Ethers5WriteContractTest.tsx b/apps/laboratory/src/components/Ethers/Ethers5WriteContractTest.tsx
index fb0064155c..3853577053 100644
--- a/apps/laboratory/src/components/Ethers/Ethers5WriteContractTest.tsx
+++ b/apps/laboratory/src/components/Ethers/Ethers5WriteContractTest.tsx
@@ -32,7 +32,7 @@ export function Ethers5WriteContractTest() {
const signer = provider.getSigner(address)
const contract = new ethers.Contract(donutAddress, abi, signer)
// @ts-expect-error ethers types are correct
- const tx = await contract.purchase(1, { value: ethers.parseEther('0.0001') })
+ const tx = await contract.purchase(1, { value: ethers.parseEther('0.00001') })
toast({
title: 'Success',
description: tx.hash,
diff --git a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx
index 70f34a22ea..35dd221aa5 100644
--- a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx
+++ b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx
@@ -31,7 +31,7 @@ export function EthersWriteContractTest() {
const signer = new JsonRpcSigner(provider, address)
const contract = new ethers.Contract(donutAddress, abi, signer)
// @ts-expect-error ethers types are correct
- const tx = await contract.purchase(1, { value: ethers.parseEther('0.0001') })
+ const tx = await contract.purchase(1, { value: ethers.parseEther('0.00001') })
toast({
title: 'Success',
description: tx.hash,
diff --git a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx
index a0a42549ab..4ddaf07f65 100644
--- a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx
+++ b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx
@@ -71,7 +71,7 @@ function ConnectedTestContent({
const purchaseDonutCallDataExecution = [
{
to: donutContractaddress as `0x${string}`,
- value: parseEther('0.0001'),
+ value: parseEther('0.00001'),
data: purchaseDonutCallData
}
]
diff --git a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx
index c200920a0e..15e1e1a2ee 100644
--- a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx
+++ b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx
@@ -69,7 +69,7 @@ function ConnectedTestContent({
const purchaseDonutCallDataExecution = [
{
to: donutContractaddress as `0x${string}`,
- value: parseEther('0.0001'),
+ value: parseEther('0.00001'),
data: purchaseDonutCallData
}
]
diff --git a/apps/laboratory/src/components/Wagmi/WagmiSendCallsWithPaymasterServiceTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSendCallsWithPaymasterServiceTest.tsx
index 64c3b80fb5..278a24fd46 100644
--- a/apps/laboratory/src/components/Wagmi/WagmiSendCallsWithPaymasterServiceTest.tsx
+++ b/apps/laboratory/src/components/Wagmi/WagmiSendCallsWithPaymasterServiceTest.tsx
@@ -8,7 +8,12 @@ import { useSendCalls } from 'wagmi/experimental'
import { useAppKitAccount } from '@reown/appkit/react'
import { useWagmiAvailableCapabilities } from '../../hooks/useWagmiActiveCapabilities'
-import { abi as donutContractAbi, address as donutContractaddress } from '../../utils/DonutContract'
+import {
+ abi as donutContractAbi,
+ donutContractSupportedChains,
+ donutContractSupportedChainsName,
+ address as donutContractaddress
+} from '../../utils/DonutContract'
import { EIP_5792_RPC_METHODS, WALLET_CAPABILITIES } from '../../utils/EIP5792Utils'
import { useChakraToast } from '../Toast'
@@ -20,7 +25,7 @@ const purchaseDonutCallData = encodeFunctionData({
const TEST_TX = {
to: donutContractaddress as `0x${string}`,
- value: parseEther('0.0001'),
+ value: parseEther('0.00001'),
data: purchaseDonutCallData
}
@@ -38,16 +43,33 @@ const BICONOMY_PAYMASTER_CONTEXT = {
}
export function WagmiSendCallsWithPaymasterServiceTest() {
- const { provider, supportedChains, supportedChainsName, currentChainsInfo, supported } =
- useWagmiAvailableCapabilities({
- capability: WALLET_CAPABILITIES.PAYMASTER_SERVICE,
- method: EIP_5792_RPC_METHODS.WALLET_SEND_CALLS
- })
+ const {
+ provider,
+ supportedChains: capabilitySupportedChains,
+ currentChainsInfo,
+ supported
+ } = useWagmiAvailableCapabilities({
+ capability: WALLET_CAPABILITIES.PAYMASTER_SERVICE,
+ method: EIP_5792_RPC_METHODS.WALLET_SEND_CALLS
+ })
const { address } = useAppKitAccount()
const { status } = useAccount()
const isConnected = status === 'connected'
+ const isFeatureSupported = useMemo(
+ () =>
+ currentChainsInfo &&
+ donutContractSupportedChains.some(chain => chain.id === currentChainsInfo.chainId),
+ [currentChainsInfo]
+ )
+
+ const doWalletSupportCapability = useMemo(
+ () =>
+ currentChainsInfo &&
+ capabilitySupportedChains.some(chain => chain.chainId === currentChainsInfo.chainId),
+ [capabilitySupportedChains]
+ )
if (!isConnected || !provider || !address) {
return (
@@ -65,18 +87,18 @@ export function WagmiSendCallsWithPaymasterServiceTest() {
)
}
- if (supportedChains.length === 0) {
+ if (!isFeatureSupported) {
return (
- Account does not support paymaster service feature
+ Switch to {donutContractSupportedChainsName} to test this feature
)
}
- if (!currentChainsInfo) {
+ if (!doWalletSupportCapability) {
return (
- Switch to {supportedChainsName} to test this feature
+ Account does not support paymaster service feature on {currentChainsInfo?.chainName}
)
}
diff --git a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx
index 1d5c8377ff..3127b16378 100644
--- a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx
+++ b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx
@@ -49,7 +49,7 @@ function AvailableTestContent({ accountAddress }: { accountAddress: string | und
abi,
address,
functionName: 'purchase',
- value: parseEther('0.0001'),
+ value: parseEther('0.00001'),
args: [1],
query: {
enabled: false
diff --git a/apps/laboratory/src/utils/DonutContract.ts b/apps/laboratory/src/utils/DonutContract.ts
index 70d7f17faa..029fa344b5 100644
--- a/apps/laboratory/src/utils/DonutContract.ts
+++ b/apps/laboratory/src/utils/DonutContract.ts
@@ -1,3 +1,5 @@
+import { base, baseSepolia, optimism, sepolia } from 'viem/chains'
+
export const abi = [
{
inputs: [
@@ -70,4 +72,10 @@ export const abi = [
}
]
-export const address = '0x2E65BAfA07238666c3b239E94F32DaD3cDD6498D'
+export const address = '0x59d6578C1D8FEb2f892f68D4f76f98511F831c6F'
+
+// Deployed on sepolia, base-seoplia, base-mainnet, optimism-mainnet.
+export const donutContractSupportedChains = [sepolia, baseSepolia, base, optimism]
+export const donutContractSupportedChainsName = donutContractSupportedChains
+ .map(chain => chain.name)
+ .join(', ')
diff --git a/packages/adapters/wagmi/src/client.ts b/packages/adapters/wagmi/src/client.ts
index 32354a0ef7..3af275215a 100644
--- a/packages/adapters/wagmi/src/client.ts
+++ b/packages/adapters/wagmi/src/client.ts
@@ -410,7 +410,7 @@ export class WagmiAdapter extends AdapterBlueprint {
return formatUnits(params.value, params.decimals)
}
- private addWagmiConnector(connector: Connector, options: AppKitOptions) {
+ private async addWagmiConnector(connector: Connector, options: AppKitOptions) {
/*
* We don't need to set auth connector or walletConnect connector
* from wagmi since we already set it in chain adapter blueprint
@@ -422,6 +422,8 @@ export class WagmiAdapter extends AdapterBlueprint {
return
}
+ const provider = (await connector.getProvider().catch(() => undefined)) as Provider | undefined
+
this.addConnector({
id: connector.id,
explorerId: PresetsUtil.ConnectorExplorerIds[connector.id],
@@ -433,17 +435,20 @@ export class WagmiAdapter extends AdapterBlueprint {
connector.id === CommonConstantsUtil.CONNECTOR_ID.INJECTED
? undefined
: { rdns: connector.id },
+ provider,
chain: this.namespace as ChainNamespace,
chains: []
})
}
- public syncConnectors(options: AppKitOptions, appKit: AppKit) {
+ public async syncConnectors(options: AppKitOptions, appKit: AppKit) {
// Add wagmi connectors
this.addWagmiConnectors(options, appKit)
// Add current wagmi connectors to chain adapter blueprint
- this.wagmiConfig.connectors.forEach(connector => this.addWagmiConnector(connector, options))
+ await Promise.all(
+ this.wagmiConfig.connectors.map(connector => this.addWagmiConnector(connector, options))
+ )
/*
* Watch for new connectors. This is needed because some EIP6963
diff --git a/packages/adapters/wagmi/src/tests/client.test.ts b/packages/adapters/wagmi/src/tests/client.test.ts
index 8cee2c208d..b70c70e415 100644
--- a/packages/adapters/wagmi/src/tests/client.test.ts
+++ b/packages/adapters/wagmi/src/tests/client.test.ts
@@ -66,7 +66,10 @@ const mockCaipNetworks = CaipNetworksUtil.extendCaipNetworks(mockNetworks, {
const mockWagmiConfig = {
connectors: [
{
- id: 'test-connector'
+ id: 'test-connector',
+ getProvider() {
+ return Promise.resolve({ connect: vi.fn(), request: vi.fn() })
+ }
}
],
_internal: {
@@ -101,10 +104,13 @@ describe('WagmiAdapter', () => {
expect(adapter.namespace).toBe('eip155')
})
- it('should set wagmi connectors', () => {
+ it('should set wagmi connectors', async () => {
vi.spyOn(wagmiCore, 'watchConnectors').mockImplementation(vi.fn())
- adapter.syncConnectors({ networks: [mainnet], projectId: 'YOUR_PROJECT_ID' }, mockAppKit)
+ await adapter.syncConnectors(
+ { networks: [mainnet], projectId: 'YOUR_PROJECT_ID' },
+ mockAppKit
+ )
expect(adapter.connectors).toStrictEqual([
{
@@ -115,6 +121,10 @@ describe('WagmiAdapter', () => {
imageId: undefined,
imageUrl: undefined,
info: { rdns: 'test-connector' },
+ provider: {
+ connect: expect.any(Function),
+ request: expect.any(Function)
+ },
name: undefined,
type: 'EXTERNAL'
}
@@ -156,6 +166,37 @@ describe('WagmiAdapter', () => {
`https://cloudflare-eth.com`
)
})
+
+ it('should add connector with provider', async () => {
+ const mockConnector = {
+ id: 'injected',
+ name: 'Injected Wallet',
+ type: 'injected',
+ getProvider() {
+ return Promise.resolve({ connect: vi.fn(), request: vi.fn() })
+ }
+ } as unknown as wagmiCore.Connector
+
+ await (adapter as any).addWagmiConnector(mockConnector)
+
+ expect(adapter.connectors).toStrictEqual([
+ {
+ chain: 'eip155',
+ chains: [],
+ explorerId: undefined,
+ id: 'injected',
+ imageId: '07ba87ed-43aa-4adf-4540-9e6a2b9cae00',
+ imageUrl: undefined,
+ info: undefined,
+ name: 'Browser Wallet',
+ provider: {
+ connect: expect.any(Function),
+ request: expect.any(Function)
+ },
+ type: 'INJECTED'
+ }
+ ])
+ })
})
describe('WagmiAdapter - signMessage', () => {
diff --git a/packages/appkit/src/client.ts b/packages/appkit/src/client.ts
index 6d8eb75939..f25b4b97a6 100644
--- a/packages/appkit/src/client.ts
+++ b/packages/appkit/src/client.ts
@@ -1593,25 +1593,26 @@ export class AppKit {
)
}
- const network = (caipNetwork || fallbackCaipNetwork) as CaipNetwork
- if (network.chainNamespace === ChainController.state.activeChain) {
+ const network = caipNetwork || fallbackCaipNetwork
+
+ if (network?.chainNamespace === ChainController.state.activeChain) {
this.setCaipNetwork(network)
}
this.syncConnectedWalletInfo(chainNamespace)
- await this.syncBalance({ address, chainId: network.id, chainNamespace })
+ await this.syncBalance({ address, chainId: network?.id, chainNamespace })
}
}
private async syncBalance(params: {
address: string
- chainId: string | number
+ chainId: string | number | undefined
chainNamespace: ChainNamespace
}) {
const caipNetwork = NetworkUtil.getNetworksByNamespace(
this.caipNetworks,
params.chainNamespace
- ).find(n => n.id.toString() === params.chainId.toString())
+ ).find(n => n.id.toString() === params.chainId?.toString())
if (!caipNetwork) {
return
diff --git a/packages/appkit/tests/appkit.test.ts b/packages/appkit/tests/appkit.test.ts
index 0239e15c78..43446f03fe 100644
--- a/packages/appkit/tests/appkit.test.ts
+++ b/packages/appkit/tests/appkit.test.ts
@@ -5,7 +5,6 @@ import {
type AppKitNetwork,
type Balance,
type CaipNetwork,
- type CaipNetworkId,
type ChainNamespace,
Emitter,
NetworkUtil,
@@ -40,13 +39,25 @@ import { CaipNetworksUtil, ErrorUtil } from '@reown/appkit-utils'
import type { AdapterBlueprint } from '../src/adapters/ChainAdapterBlueprint'
import { AppKit } from '../src/client'
-import { base, mainnet, polygon, sepolia, solana } from '../src/networks/index.js'
+import {
+ base as baseNetwork,
+ mainnet as mainnetNetwork,
+ polygon as polygonNetwork,
+ sepolia as sepoliaNetwork,
+ solana as solanaNetwork
+} from '../src/networks/index.js'
import { ProviderUtil } from '../src/store'
import { UniversalAdapter } from '../src/universal-adapter/client'
import mockUniversalAdapter from './mocks/Adapter'
import { mockOptions } from './mocks/Options'
import mockProvider from './mocks/UniversalProvider'
+// Extend networks as CAIP networks
+const [base, mainnet, polygon, sepolia, solana] = CaipNetworksUtil.extendCaipNetworks(
+ [baseNetwork, mainnetNetwork, polygonNetwork, sepoliaNetwork, solanaNetwork],
+ { customNetworkImageUrls: {}, projectId: 'test-project-id' }
+) as [CaipNetwork, CaipNetwork, CaipNetwork, CaipNetwork, CaipNetwork]
+
// Mock all controllers and UniversalAdapterClient
vi.mock('@reown/appkit-core')
vi.mock('../src/universal-adapter/client')
@@ -75,7 +86,6 @@ describe('Base', () => {
beforeEach(() => {
vi.mocked(ConnectorController).getConnectors = vi.fn().mockReturnValue([])
- vi.mocked(CaipNetworksUtil).extendCaipNetworks = vi.fn().mockReturnValue([])
appKit = new AppKit(mockOptions)
@@ -109,10 +119,14 @@ describe('Base', () => {
})
expect(ChainController.initialize).toHaveBeenCalledOnce()
- expect(ChainController.initialize).toHaveBeenCalledWith(mockOptions.adapters, [], {
- connectionControllerClient: expect.any(Object),
- networkControllerClient: expect.any(Object)
- })
+ expect(ChainController.initialize).toHaveBeenCalledWith(
+ mockOptions.adapters,
+ [mainnet, sepolia, solana],
+ {
+ connectionControllerClient: expect.any(Object),
+ networkControllerClient: expect.any(Object)
+ }
+ )
})
it('should set EIP6963 enabled by default', () => {
@@ -350,14 +364,12 @@ describe('Base', () => {
})
it('should set CAIP address', () => {
- // First mock AccountController.setCaipAddress to update ChainController state
- vi.spyOn(AccountController, 'setCaipAddress').mockImplementation(() => {
- vi.spyOn(ChainController, 'state', 'get').mockReturnValueOnce({
- ...ChainController.state,
- activeCaipAddress: 'eip155:1:0x123',
- chains: new Map([['eip155', { namespace: 'eip155' }]])
- }) as any
- })
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ ...ChainController.state,
+ activeChain: 'eip155',
+ activeCaipAddress: 'eip155:1:0x123',
+ chains: new Map([['eip155', { namespace: 'eip155' }]])
+ } as unknown as ChainControllerState)
appKit.setCaipAddress('eip155:1:0x123', 'eip155')
expect(AccountController.setCaipAddress).toHaveBeenCalledWith('eip155:1:0x123', 'eip155')
@@ -396,21 +408,21 @@ describe('Base', () => {
})
it('should set CAIP network', () => {
- const caipNetwork = { id: 'eip155:1', name: 'Ethereum' } as unknown as CaipNetwork
+ const caipNetwork = mainnet
appKit.setCaipNetwork(caipNetwork)
expect(ChainController.setActiveCaipNetwork).toHaveBeenCalledWith(caipNetwork)
})
it('should get CAIP network', () => {
vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
- activeCaipNetwork: { id: 'eip155:1', name: 'Ethereum' },
+ activeCaipNetwork: mainnet,
chains: new Map([['eip155', { namespace: 'eip155' }]])
} as any)
- expect(appKit.getCaipNetwork()).toEqual({ id: 'eip155:1', name: 'Ethereum' })
+ expect(appKit.getCaipNetwork()).toEqual(mainnet)
})
it('should set requested CAIP networks', () => {
- const requestedNetworks = [{ id: 'eip155:1', name: 'Ethereum' }] as unknown as CaipNetwork[]
+ const requestedNetworks = [mainnet] as unknown as CaipNetwork[]
appKit.setRequestedCaipNetworks(requestedNetworks, 'eip155')
expect(ChainController.setRequestedCaipNetworks).toHaveBeenCalledWith(
requestedNetworks,
@@ -647,15 +659,11 @@ describe('Base', () => {
})
it('should switch network when requested', async () => {
- vi.mocked(CaipNetworksUtil).extendCaipNetworks = vi
- .fn()
- .mockReturnValue([{ id: mainnet.id, name: mainnet.name }])
-
const mockAppKit = new AppKit(mockOptions)
vi.mocked(ChainController.switchActiveNetwork).mockResolvedValue(undefined)
- await mockAppKit.switchNetwork(mainnet)
+ mockAppKit.switchNetwork(mainnet)
expect(ChainController.switchActiveNetwork).toHaveBeenCalledWith(
expect.objectContaining({
@@ -664,15 +672,12 @@ describe('Base', () => {
})
)
- await mockAppKit.switchNetwork(polygon)
+ mockAppKit.switchNetwork(polygon)
expect(ChainController.switchActiveNetwork).toHaveBeenCalledTimes(1)
})
it('should use the correct network when syncing account if is does not allow all networks and network is not allowed', async () => {
- vi.spyOn(NetworkUtil, 'getNetworksByNamespace').mockReturnValue([
- mainnet as unknown as CaipNetwork
- ])
vi.spyOn(AccountController, 'fetchTokenBalance').mockResolvedValue([
{
quantity: { numeric: '0.00', decimals: '18' },
@@ -680,39 +685,30 @@ describe('Base', () => {
symbol: 'ETH'
} as Balance
])
- vi.mocked(ChainController.getAllApprovedCaipNetworkIds).mockReturnValue(['eip155:1'])
+ vi.mocked(ChainController.getAllApprovedCaipNetworkIds).mockReturnValue([
+ 'eip155:1',
+ 'eip155:11155111'
+ ])
vi.spyOn(ChainController, 'getNetworkProp').mockReturnValue(false)
vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValueOnce('eip155')
- vi.mocked(appKit as any).caipNetworks = [
- {
- id: '1',
- chainNamespace: 'eip155',
- caipNetworkId: 'eip155:1' as CaipNetworkId
- }
- ]
+ vi.spyOn(StorageUtil, 'getActiveNetworkProps').mockReturnValueOnce({
+ namespace: mainnet.chainNamespace,
+ chainId: mainnet.id,
+ caipNetworkId: mainnet.caipNetworkId
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValueOnce({
+ allowUnsupportedChain: false
+ } as any)
const mockAccountData = {
address: '0x123',
chainId: '2',
chainNamespace: 'eip155' as const
}
- vi.spyOn(StorageUtil, 'getActiveNetworkProps').mockReturnValueOnce({
- namespace: 'eip155',
- chainId: '1',
- caipNetworkId: 'eip155:1'
- })
-
- OptionsController.state.allowUnsupportedChain = undefined
- vi.spyOn(OptionsController.state, 'allowUnsupportedChain', 'get').mockReturnValueOnce(false)
-
await appKit['syncAccount'](mockAccountData)
expect(ChainController.getNetworkProp).toHaveBeenCalledWith('supportsAllNetworks', 'eip155')
- expect(ChainController.setActiveCaipNetwork).toHaveBeenCalledWith({
- id: '1',
- chainNamespace: 'eip155',
- caipNetworkId: 'eip155:1'
- })
+ expect(ChainController.setActiveCaipNetwork).toHaveBeenCalledWith(mainnet)
})
it('should set connected wallet info when syncing account', async () => {
@@ -731,13 +727,7 @@ describe('Base', () => {
} as Balance
])
vi.spyOn(ChainController, 'getAllApprovedCaipNetworkIds').mockReturnValue(['eip155:1'])
- vi.mocked(appKit as any).caipNetworks = [
- {
- id: '1',
- chainNamespace: 'eip155',
- caipNetworkId: 'eip155:1' as CaipNetworkId
- }
- ]
+ vi.mocked(appKit as any).caipNetworks = [mainnet]
// Mock the connector data
const mockConnector = {
id: 'test-wallet'
@@ -745,15 +735,15 @@ describe('Base', () => {
vi.mocked(ConnectorController.getConnectors).mockReturnValue([mockConnector])
vi.mocked(StorageUtil.getActiveNetworkProps).mockReturnValue({
- namespace: 'eip155',
- chainId: '1',
- caipNetworkId: '1'
+ namespace: mainnet.chainNamespace,
+ chainId: mainnet.id,
+ caipNetworkId: mainnet.caipNetworkId
})
const mockAccountData = {
address: '0x123',
- chainId: '1',
- chainNamespace: 'eip155' as const
+ chainId: mainnet.id,
+ chainNamespace: mainnet.chainNamespace
}
vi.spyOn(StorageUtil, 'getConnectedConnectorId').mockReturnValue(mockConnector.id)
@@ -784,23 +774,17 @@ describe('Base', () => {
} as Balance
])
vi.spyOn(ChainController, 'getAllApprovedCaipNetworkIds').mockReturnValue(['eip155:1'])
- vi.mocked(appKit as any).caipNetworks = [
- {
- id: '1',
- chainNamespace: 'eip155',
- caipNetworkId: 'eip155:1' as CaipNetworkId
- }
- ]
+ vi.mocked(appKit as any).caipNetworks = [mainnet]
const mockAccountData = {
address: '0x123',
- chainId: '1',
- chainNamespace: 'eip155' as const
+ chainId: mainnet.id,
+ chainNamespace: mainnet.chainNamespace
}
vi.spyOn(StorageUtil, 'getActiveNetworkProps').mockReturnValue({
- namespace: 'eip155',
- chainId: '1',
- caipNetworkId: '1'
+ namespace: mainnet.chainNamespace,
+ chainId: mainnet.id,
+ caipNetworkId: mainnet.caipNetworkId
})
vi.mocked(BlockchainApiController.fetchIdentity).mockResolvedValue({
@@ -835,22 +819,16 @@ describe('Base', () => {
} as Balance
])
vi.spyOn(ChainController, 'getAllApprovedCaipNetworkIds').mockReturnValue(['solana:1'])
- vi.mocked(appKit as any).caipNetworks = [
- {
- id: '1',
- chainNamespace: 'solana',
- caipNetworkId: 'solana:1' as CaipNetworkId
- }
- ]
+ vi.mocked(appKit as any).caipNetworks = [solana]
const mockAccountData = {
address: '0x123',
- chainId: '1',
- chainNamespace: 'solana' as const
+ chainId: solana.id,
+ chainNamespace: solana.chainNamespace
}
vi.spyOn(StorageUtil, 'getActiveNetworkProps').mockReturnValueOnce({
- namespace: 'solana',
- chainId: '1',
- caipNetworkId: 'solana:1'
+ namespace: solana.chainNamespace,
+ chainId: solana.id,
+ caipNetworkId: solana.caipNetworkId
})
await appKit['syncAccount'](mockAccountData)
@@ -874,15 +852,7 @@ describe('Base', () => {
} as Balance
])
vi.spyOn(ChainController, 'getAllApprovedCaipNetworkIds').mockReturnValue(['eip155:11155111'])
- vi.mocked(appKit as any).caipNetworks = [
- {
- ...sepolia,
- nativeCurrency: { symbol: 'sETH' },
- chainNamespace: 'eip155',
- caipNetworkId: 'eip155:11155111',
- testnet: true
- } as unknown as CaipNetwork
- ]
+ vi.mocked(appKit as any).caipNetworks = [sepolia]
const mockAccountData = {
address: '0x123',
chainId: '11155111',
@@ -914,25 +884,18 @@ describe('Base', () => {
])
vi.spyOn(ChainController, 'getAllApprovedCaipNetworkIds').mockReturnValue(['eip155:1'])
vi.spyOn(StorageUtil, 'getActiveNetworkProps').mockReturnValueOnce({
- namespace: 'eip155',
- chainId: '1',
- caipNetworkId: 'eip155:1'
+ namespace: mainnet.chainNamespace,
+ chainId: mainnet.id,
+ caipNetworkId: mainnet.caipNetworkId
})
const mockAccountData = {
address: '0x123',
- chainId: '1',
- chainNamespace: 'eip155' as const
+ chainId: mainnet.id,
+ chainNamespace: mainnet.chainNamespace
}
vi.spyOn(AccountController, 'state', 'get').mockReturnValue(mockAccountData as any)
- vi.spyOn(CaipNetworksUtil, 'extendCaipNetworks').mockReturnValue([
- {
- id: '1',
- chainNamespace: 'eip155',
- caipNetworkId: 'eip155:1' as CaipNetworkId
- } as CaipNetwork
- ])
appKit = new AppKit({ ...mockOptions })
@@ -969,16 +932,6 @@ describe('Base', () => {
caipNetworkId: 'eip155:11155111'
})
- vi.spyOn(CaipNetworksUtil, 'extendCaipNetworks').mockReturnValueOnce([
- {
- id: '11155111',
- chainNamespace: 'eip155',
- caipNetworkId: 'eip155:11155111' as CaipNetworkId,
- testnet: true,
- nativeCurrency: { symbol: 'sETH' }
- } as CaipNetwork
- ])
-
vi.spyOn(AccountController, 'state', 'get').mockReturnValue(mockAccountData as any)
appKit = new AppKit({ ...mockOptions })
@@ -990,10 +943,6 @@ describe('Base', () => {
})
it('should disconnect correctly', async () => {
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork
- ])
-
vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
chains: new Map([['eip155', { namespace: 'eip155' }]]),
activeChain: 'eip155'
@@ -1006,7 +955,6 @@ describe('Base', () => {
const appKit = new AppKit({
...mockOptions,
networks: [base],
- projectId: 'YOUR_PROJECT_ID',
adapters: [mockUniversalAdapter]
})
@@ -1021,7 +969,7 @@ describe('Base', () => {
chains: new Map([['eip155', { namespace: 'eip155' }]]),
activeChain: 'eip155'
} as any)
- ;(appKit as any).caipNetworks = [{ id: 'eip155:1', chainNamespace: 'eip155' }]
+ ;(appKit as any).caipNetworks = [mainnet]
OptionsController.state.allowUnsupportedChain = undefined
vi.spyOn(OptionsController.state, 'allowUnsupportedChain', 'get').mockResolvedValueOnce(true)
@@ -1099,17 +1047,17 @@ describe('Base', () => {
activeCaipNetwork: { id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork
} as ChainControllerState)
vi.mocked(StorageUtil.getActiveNetworkProps).mockReturnValue({
- namespace: 'eip155',
- chainId: '1',
- caipNetworkId: '1'
+ namespace: mainnet.chainNamespace,
+ chainId: mainnet.id,
+ caipNetworkId: mainnet.caipNetworkId
})
const mockAdapter = {
getAccounts: vi.fn().mockResolvedValue({ accounts: [{ address: '0x123', type: 'eoa' }] }),
syncConnection: vi.fn().mockResolvedValue({
address: '0x123',
- chainId: '1',
- chainNamespace: 'eip155',
+ chainId: mainnet.id,
+ chainNamespace: mainnet.chainNamespace,
accounts: [{ address: '0x123', type: 'eoa' }]
}),
on: vi.fn(),
@@ -1169,8 +1117,8 @@ describe('Base', () => {
vi.spyOn(ProviderUtil, 'setProviderId').mockImplementation(vi.fn())
vi.spyOn(StorageUtil, 'getActiveNetworkProps').mockReturnValue({
namespace: 'eip155',
- chainId: '1',
- caipNetworkId: '1'
+ chainId: mainnet.id,
+ caipNetworkId: mainnet.caipNetworkId
})
vi.spyOn(StorageUtil, 'getConnectedNamespaces').mockReturnValueOnce(['eip155', 'solana'])
vi.spyOn(StorageUtil, 'getConnectedConnectorId').mockImplementation(namespace => {
@@ -1184,8 +1132,8 @@ describe('Base', () => {
getAccounts: vi.fn().mockResolvedValue({ accounts: [{ address: '0x123', type: 'eoa' }] }),
syncConnection: vi.fn().mockResolvedValue({
address: '0x123',
- chainId: '1',
- chainNamespace: 'eip155',
+ chainId: mainnet.id,
+ chainNamespace: mainnet.chainNamespace,
accounts: [{ address: '0x123', type: 'eoa' }],
type: 'EXTERNAL',
id: 'evm-connector'
@@ -1197,8 +1145,8 @@ describe('Base', () => {
getAccounts: vi.fn().mockResolvedValue({ accounts: [{ address: 'Hgbsh1', type: 'eoa' }] }),
syncConnection: vi.fn().mockResolvedValue({
address: 'Hgbsh1',
- chainId: '1',
- chainNamespace: 'solana',
+ chainId: solana.id,
+ chainNamespace: solana.chainNamespace,
accounts: [{ address: 'Hgbsh1', type: 'eoa' }],
type: 'EXTERNAL',
id: 'solana-connector'
@@ -1271,14 +1219,9 @@ describe('Base', () => {
})
it('should call syncConnectors when initializing adapters', async () => {
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork
- ])
-
const appKit = new AppKit({
...mockOptions,
networks: [base],
- projectId: 'YOUR_PROJECT_ID',
adapters: [mockAdapter]
})
@@ -1293,14 +1236,9 @@ describe('Base', () => {
})
it('should create UniversalAdapter when no blueprint is provided for namespace', async () => {
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork
- ])
-
const appKit = new AppKit({
...mockOptions,
- networks: [mainnet],
- projectId: 'YOUR_PROJECT_ID',
+ networks: [mainnetNetwork],
adapters: [mockAdapter]
})
@@ -1321,14 +1259,11 @@ describe('Base', () => {
expect(UniversalAdapter).toHaveBeenCalledWith({
namespace: 'eip155',
- networks: [{ id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork]
+ networks: [mainnet]
})
})
it('should initialize UniversalProvider when not provided in options', () => {
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork
- ])
vi.spyOn(CoreHelperUtil, 'isClient').mockReturnValue(true)
const upSpy = vi.spyOn(UniversalProvider, 'init')
@@ -1345,9 +1280,6 @@ describe('Base', () => {
})
it('should not initialize UniversalProvider when provided in options', async () => {
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork
- ])
vi.spyOn(CoreHelperUtil, 'isClient').mockReturnValue(true)
const upSpy = vi.spyOn(UniversalProvider, 'init')
@@ -1365,11 +1297,6 @@ describe('Base', () => {
})
it('should initialize multiple adapters for different namespaces', async () => {
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: '1', chainNamespace: 'eip155' } as CaipNetwork,
- { id: 'solana', chainNamespace: 'solana' } as CaipNetwork
- ])
-
const mockSolanaAdapter = {
namespace: 'solana',
construct: vi.fn(),
@@ -1385,7 +1312,6 @@ describe('Base', () => {
const appKit = new AppKit({
...mockOptions,
networks: [mainnet, solana],
- projectId: 'YOUR_PROJECT_ID',
adapters: [mockSolanaAdapter, mockAdapter]
})
@@ -1402,14 +1328,9 @@ describe('Base', () => {
})
it('should set universal provider and auth provider for each adapter', async () => {
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: '1', chainNamespace: 'eip155' } as CaipNetwork
- ])
-
const appKit = new AppKit({
...mockOptions,
networks: [mainnet],
- projectId: 'YOUR_PROJECT_ID',
adapters: [mockAdapter]
})
@@ -1480,19 +1401,16 @@ describe('Listeners', () => {
vi.spyOn(AccountController, 'state', 'get').mockReturnValue({
address: '0x'
} as unknown as typeof AccountController.state)
- vi.spyOn(CaipNetworksUtil, 'extendCaipNetworks').mockReturnValueOnce([
- { id: '1', chainNamespace: 'eip155' } as CaipNetwork
- ])
vi.spyOn(StorageUtil, 'getActiveNetworkProps').mockReturnValueOnce({
- namespace: 'eip155',
- chainId: '1',
- caipNetworkId: '1'
+ namespace: mainnet.chainNamespace,
+ chainId: mainnet.id,
+ caipNetworkId: mainnet.caipNetworkId
})
const mockAccount = {
address: '0x123',
- chainId: '1',
- chainNamespace: 'eip155'
+ chainId: mainnet.id,
+ chainNamespace: mainnet.chainNamespace
}
vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
@@ -1519,7 +1437,6 @@ describe('Listeners', () => {
const appKit = new AppKit({
...mockOptions,
networks: [mainnet],
- projectId: 'YOUR_PROJECT_ID',
features: {
email: false,
socials: []
@@ -1555,7 +1472,6 @@ describe('Listeners', () => {
describe('Adapter Management', () => {
let appKit: AppKit
let mockAdapter: AdapterBlueprint
- let mockNetwork: AppKitNetwork
beforeEach(() => {
vi.spyOn(OptionsController, 'getSnapshot').mockReturnValue({ ...OptionsController.state })
@@ -1574,18 +1490,9 @@ describe('Adapter Management', () => {
removeAllEventListeners: vi.fn()
} as unknown as AdapterBlueprint
- mockNetwork = {
- id: 'eip155:1',
- name: 'Ethereum'
- } as unknown as AppKitNetwork
-
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValue([
- { id: 'eip155:1', chainNamespace: 'eip155' } as CaipNetwork
- ])
-
appKit = new AppKit({
...mockOptions,
- networks: [mockNetwork],
+ networks: [mainnet],
adapters: [mockAdapter]
})
@@ -1608,16 +1515,7 @@ describe('Adapter Management', () => {
emit: vi.fn()
} as unknown as ChainAdapter
- const newNetwork = {
- id: 'solana:1',
- name: 'Solana'
- } as unknown as AppKitNetwork
-
- vi.mocked(CaipNetworksUtil.extendCaipNetworks).mockReturnValueOnce([
- { id: 'solana:1', chainNamespace: 'solana' } as CaipNetwork
- ])
-
- appKit.addAdapter(newAdapter, [newNetwork])
+ appKit.addAdapter(newAdapter, [solana])
expect(appKit.chainAdapters?.solana).toBeDefined()
expect(appKit.chainNamespaces).toContain('solana')
@@ -1636,16 +1534,11 @@ describe('Adapter Management', () => {
namespace: 'solana'
} as unknown as ChainAdapter
- const newNetwork = {
- id: 'solana:1',
- name: 'Solana'
- } as unknown as AppKitNetwork
-
// Remove clients
;(appKit as any).connectionControllerClient = undefined
;(appKit as any).networkControllerClient = undefined
- appKit.addAdapter(newAdapter, [newNetwork])
+ appKit.addAdapter(newAdapter, [solana])
expect(appKit.chainAdapters?.solana).toBeUndefined()
})
@@ -1659,15 +1552,10 @@ describe('Adapter Management', () => {
namespace: 'solana'
} as unknown as ChainAdapter
- const newNetwork = {
- id: 'solana:1',
- name: 'Solana'
- } as unknown as AppKitNetwork
-
// Remove chainAdapters
;(appKit as any).chainAdapters = undefined
- appKit.addAdapter(newAdapter, [newNetwork])
+ appKit.addAdapter(newAdapter, [solana])
expect((appKit as any).createAdapter).not.toHaveBeenCalled()
expect((appKit as any).initChainAdapter).not.toHaveBeenCalled()
@@ -1737,20 +1625,23 @@ describe('Adapter Management', () => {
describe('Balance sync', () => {
beforeEach(() => {
vi.resetAllMocks()
+ vi.spyOn(OptionsController, 'getSnapshot').mockReturnValue({ ...OptionsController.state })
+ vi.spyOn(ThemeController, 'getSnapshot').mockReturnValue({ ...ThemeController.state })
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({ ...ChainController.state })
})
it('should not sync balance if theres no matching caipNetwork', async () => {
- vi.spyOn(NetworkUtil, 'getNetworksByNamespace').mockReturnValue([])
-
const appKit = new AppKit({
...mockOptions,
networks: [mainnet]
})
+ vi.spyOn(NetworkUtil, 'getNetworksByNamespace').mockReturnValue([])
+
await appKit['syncBalance']({
address: '0x123',
- chainId: '1',
- chainNamespace: 'eip155' as const
+ chainId: sepolia.id,
+ chainNamespace: sepolia.chainNamespace
})
expect(NetworkUtil.getNetworksByNamespace).toHaveBeenCalled()
@@ -1759,9 +1650,7 @@ describe('Balance sync', () => {
})
it('should set empty balance on testnet', async () => {
- vi.spyOn(NetworkUtil, 'getNetworksByNamespace').mockReturnValue([
- { ...sepolia, caipNetworkId: 'eip155:11155111', chainNamespace: 'eip155' }
- ])
+ vi.spyOn(NetworkUtil, 'getNetworksByNamespace').mockReturnValue([sepolia])
const appKit = new AppKit({
...mockOptions,
diff --git a/packages/appkit/tests/mocks/Options.ts b/packages/appkit/tests/mocks/Options.ts
index 16eddf4d59..ad4fde2276 100644
--- a/packages/appkit/tests/mocks/Options.ts
+++ b/packages/appkit/tests/mocks/Options.ts
@@ -3,7 +3,7 @@ import { vi } from 'vitest'
import type { ChainAdapter } from '@reown/appkit-core'
import type { SdkVersion } from '@reown/appkit-core'
-import { mainnet, solana } from '../../src/networks/index.js'
+import { mainnet, sepolia, solana } from '../../src/networks/index.js'
import type { AppKitOptions } from '../../src/utils/index.js'
export const mockOptions: AppKitOptions & {
@@ -29,7 +29,7 @@ export const mockOptions: AppKitOptions & {
emit: vi.fn()
} as unknown as ChainAdapter
],
- networks: [mainnet, solana],
+ networks: [mainnet, sepolia, solana],
metadata: {
name: 'Test App',
description: 'Test App Description',
diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts
index 7132bb1445..58f8d6beaf 100644
--- a/packages/core/src/controllers/ConnectionController.ts
+++ b/packages/core/src/controllers/ConnectionController.ts
@@ -106,13 +106,9 @@ export const ConnectionController = {
StorageUtil.setConnectedConnectorId(namespace, ConstantsUtil.CONNECTOR_ID.WALLET_CONNECT)
})
- if (CoreHelperUtil.isTelegram()) {
+ if (CoreHelperUtil.isTelegram() || (CoreHelperUtil.isSafari() && CoreHelperUtil.isIos())) {
if (wcConnectionPromise) {
- try {
- await wcConnectionPromise
- } catch (error) {
- /* Empty */
- }
+ await wcConnectionPromise
wcConnectionPromise = undefined
return
@@ -124,10 +120,9 @@ export const ConnectionController = {
return
}
- wcConnectionPromise = new Promise(async (resolve, reject) => {
- await this._getClient()?.connectWalletConnect?.().catch(reject)
- resolve()
- })
+ wcConnectionPromise = this._getClient()
+ ?.connectWalletConnect?.()
+ .catch(() => undefined)
this.state.status = 'connecting'
await wcConnectionPromise
wcConnectionPromise = undefined
diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts
index 9d7aa88128..4bcfe2358c 100644
--- a/packages/core/src/utils/ConstantsUtil.ts
+++ b/packages/core/src/utils/ConstantsUtil.ts
@@ -201,7 +201,8 @@ export const ConstantsUtil = {
],
NAMES_SUPPORTED_CHAIN_NAMESPACES: ['eip155'] as ChainNamespace[],
-
+ ONRAMP_SUPPORTED_CHAIN_NAMESPACES: ['eip155', 'solana'] as ChainNamespace[],
+ ACTIVITY_ENABLED_CHAIN_NAMESPACES: ['eip155', 'solana'] as ChainNamespace[],
NATIVE_TOKEN_ADDRESS: {
eip155: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
solana: 'So11111111111111111111111111111111111111111',
diff --git a/packages/core/src/utils/CoreHelperUtil.ts b/packages/core/src/utils/CoreHelperUtil.ts
index 6fccf5e298..a629fd5c60 100644
--- a/packages/core/src/utils/CoreHelperUtil.ts
+++ b/packages/core/src/utils/CoreHelperUtil.ts
@@ -10,7 +10,7 @@ type OpenTarget = '_blank' | '_self' | 'popupWindow' | '_top'
export const CoreHelperUtil = {
isMobile() {
- if (typeof window !== 'undefined') {
+ if (this.isClient()) {
return Boolean(
window.matchMedia('(pointer:coarse)').matches ||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini/u.test(navigator.userAgent)
@@ -25,15 +25,33 @@ export const CoreHelperUtil = {
},
isAndroid() {
+ if (!this.isMobile()) {
+ return false
+ }
+
const ua = window.navigator.userAgent.toLowerCase()
return CoreHelperUtil.isMobile() && ua.includes('android')
},
isIos() {
+ if (!this.isMobile()) {
+ return false
+ }
+
+ const ua = window.navigator.userAgent.toLowerCase()
+
+ return ua.includes('iphone') || ua.includes('ipad')
+ },
+
+ isSafari() {
+ if (!this.isClient()) {
+ return false
+ }
+
const ua = window.navigator.userAgent.toLowerCase()
- return CoreHelperUtil.isMobile() && (ua.includes('iphone') || ua.includes('ipad'))
+ return ua.includes('safari')
},
isClient() {
diff --git a/packages/core/src/utils/StorageUtil.ts b/packages/core/src/utils/StorageUtil.ts
index 47872f9a28..ecdc351188 100644
--- a/packages/core/src/utils/StorageUtil.ts
+++ b/packages/core/src/utils/StorageUtil.ts
@@ -12,16 +12,21 @@ import type { ConnectionStatus, SocialProvider, WcWallet } from './TypeUtil.js'
// -- Utility -----------------------------------------------------------------
export const StorageUtil = {
getActiveNetworkProps() {
- const activeNamespace = StorageUtil.getActiveNamespace()
- const activeCaipNetworkId = StorageUtil.getActiveCaipNetworkId()
- const caipNetworkIdFromStorage = activeCaipNetworkId
- ? activeCaipNetworkId.split(':')[1]
+ const namespace = StorageUtil.getActiveNamespace()
+ const caipNetworkId = StorageUtil.getActiveCaipNetworkId() as CaipNetworkId | undefined
+ const stringChainId = caipNetworkId ? caipNetworkId.split(':')[1] : undefined
+
+ // eslint-disable-next-line no-nested-ternary
+ const chainId = stringChainId
+ ? isNaN(Number(stringChainId))
+ ? stringChainId
+ : Number(stringChainId)
: undefined
return {
- namespace: activeNamespace,
- caipNetworkId: activeCaipNetworkId,
- chainId: caipNetworkIdFromStorage
+ namespace,
+ caipNetworkId,
+ chainId
}
},
diff --git a/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts b/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts
index bcbb0a526d..4c000b1da2 100644
--- a/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts
+++ b/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts
@@ -7,7 +7,6 @@ import {
AccountController,
type AccountType,
ChainController,
- ConstantsUtil as CommonConstantsUtil,
ConnectionController,
ConnectorController,
ConstantsUtil as CoreConstantsUtil,
@@ -132,10 +131,16 @@ export class W3mAccountDefaultWidget extends LitElement {
// -- Private ------------------------------------------- //
private onrampTemplate() {
+ if (!this.namespace) {
+ return null
+ }
+
const onramp = this.features?.onramp
- const isBitcoin = ChainController.state.activeChain === 'bip122'
+ const hasNetworkSupport = CoreConstantsUtil.ONRAMP_SUPPORTED_CHAIN_NAMESPACES.includes(
+ this.namespace
+ )
- if (!onramp || isBitcoin) {
+ if (!onramp || !hasNetworkSupport) {
return null
}
@@ -171,19 +176,30 @@ export class W3mAccountDefaultWidget extends LitElement {
}
private activityTemplate() {
- const isSolana = ChainController.state.activeChain === ConstantsUtil.CHAIN.SOLANA
+ if (!this.namespace) {
+ return null
+ }
- return html`
- Activity
- ${isSolana ? html`Coming soon` : ''}
- `
+ const isSolana = ChainController.state.activeChain === ConstantsUtil.CHAIN.SOLANA
+ const isEnabled =
+ this.features?.history &&
+ CoreConstantsUtil.ACTIVITY_ENABLED_CHAIN_NAMESPACES.includes(this.namespace)
+
+ return isEnabled
+ ? html`
+
+ Activity
+
+ ${isSolana ? html`Coming soon` : ''}
+ `
+ : null
}
private swapsTemplate() {
@@ -234,7 +250,7 @@ export class W3mAccountDefaultWidget extends LitElement {
if (
!authConnector ||
connectorId !== ConstantsUtil.CONNECTOR_ID.AUTH ||
- origin.includes(CommonConstantsUtil.SECURE_SITE)
+ origin.includes(CoreConstantsUtil.SECURE_SITE)
) {
return null
}
diff --git a/packages/scaffold-ui/src/partials/w3m-alertbar/index.ts b/packages/scaffold-ui/src/partials/w3m-alertbar/index.ts
index d979906df0..91c6c062cc 100644
--- a/packages/scaffold-ui/src/partials/w3m-alertbar/index.ts
+++ b/packages/scaffold-ui/src/partials/w3m-alertbar/index.ts
@@ -7,7 +7,7 @@ import { customElement } from '@reown/appkit-ui'
import styles from './styles.js'
// -- Helpers ------------------------------------------- //
-const presets = {
+export const presets = {
info: {
backgroundColor: 'fg-350',
iconColor: 'fg-325',
diff --git a/packages/scaffold-ui/src/partials/w3m-connecting-wc-mobile/index.ts b/packages/scaffold-ui/src/partials/w3m-connecting-wc-mobile/index.ts
index 331b9fb214..3e5ee0342d 100644
--- a/packages/scaffold-ui/src/partials/w3m-connecting-wc-mobile/index.ts
+++ b/packages/scaffold-ui/src/partials/w3m-connecting-wc-mobile/index.ts
@@ -20,8 +20,6 @@ export class W3mConnectingWcMobile extends W3mConnectingWidget {
}
this.secondaryBtnLabel = undefined
this.secondaryLabel = ConstantsUtil.CONNECT_LABELS.MOBILE
- this.onConnect = this.onConnectProxy.bind(this)
- this.onRender = this.onRenderProxy.bind(this)
document.addEventListener('visibilitychange', this.onBuffering.bind(this))
EventsController.sendEvent({
type: 'track',
@@ -45,14 +43,14 @@ export class W3mConnectingWcMobile extends W3mConnectingWidget {
}
// -- Private ------------------------------------------- //
- private onRenderProxy() {
+ protected override onRender = () => {
if (!this.ready && this.uri) {
this.ready = true
this.onConnect?.()
}
}
- private onConnectProxy() {
+ protected override onConnect = () => {
if (this.wallet?.mobile_link && this.uri) {
try {
this.error = false
@@ -89,6 +87,13 @@ export class W3mConnectingWcMobile extends W3mConnectingWidget {
}, 5000)
}
}
+
+ protected override onTryAgain() {
+ if (!this.buffering) {
+ ConnectionController.setWcError(false)
+ this.onConnect()
+ }
+ }
}
declare global {
diff --git a/packages/scaffold-ui/src/partials/w3m-connecting-wc-qrcode/index.ts b/packages/scaffold-ui/src/partials/w3m-connecting-wc-qrcode/index.ts
index e964bcc9f3..fe1cdd6704 100644
--- a/packages/scaffold-ui/src/partials/w3m-connecting-wc-qrcode/index.ts
+++ b/packages/scaffold-ui/src/partials/w3m-connecting-wc-qrcode/index.ts
@@ -1,12 +1,10 @@
import { html } from 'lit'
-import { state } from 'lit/decorators.js'
import { ifDefined } from 'lit/directives/if-defined.js'
import {
AssetUtil,
ConnectionController,
EventsController,
- OptionsController,
ThemeController
} from '@reown/appkit-core'
import { customElement } from '@reown/appkit-ui'
@@ -18,18 +16,9 @@ import styles from './styles.js'
export class W3mConnectingWcQrcode extends W3mConnectingWidget {
public static override styles = styles
- // -- State & Properties -------------------------------- //
- @state() private useInjectedUniversalProvider =
- OptionsController.state.useInjectedUniversalProvider
-
public constructor() {
super()
window.addEventListener('resize', this.forceUpdate)
- this.unsubscribe.push(
- OptionsController.subscribeKey('useInjectedUniversalProvider', () => {
- this.useInjectedUniversalProvider = OptionsController.state.useInjectedUniversalProvider
- })
- )
EventsController.sendEvent({
type: 'track',
@@ -63,11 +52,6 @@ export class W3mConnectingWcQrcode extends W3mConnectingWidget {
${this.copyTemplate()}
- ${this.useInjectedUniversalProvider
- ? html`
-
- `
- : null}
`
}
diff --git a/packages/scaffold-ui/src/utils/w3m-connecting-widget/index.ts b/packages/scaffold-ui/src/utils/w3m-connecting-widget/index.ts
index 25da04281c..a01ee2fc1b 100644
--- a/packages/scaffold-ui/src/utils/w3m-connecting-widget/index.ts
+++ b/packages/scaffold-ui/src/utils/w3m-connecting-widget/index.ts
@@ -78,7 +78,11 @@ export class W3mConnectingWidget extends LitElement {
]
)
// The uri should be preloaded in the tg ios context so we can safely init as the subscribeKey won't trigger
- if (CoreHelperUtil.isTelegram() && CoreHelperUtil.isIos() && ConnectionController.state.wcUri) {
+ if (
+ (CoreHelperUtil.isTelegram() || CoreHelperUtil.isSafari()) &&
+ CoreHelperUtil.isIos() &&
+ ConnectionController.state.wcUri
+ ) {
this.onConnect?.()
}
}
@@ -187,7 +191,7 @@ export class W3mConnectingWidget extends LitElement {
}
}
- private onTryAgain() {
+ protected onTryAgain() {
if (!this.buffering) {
ConnectionController.setWcError(false)
if (this.onRetry) {
diff --git a/packages/scaffold-ui/src/views/w3m-connect-view/index.ts b/packages/scaffold-ui/src/views/w3m-connect-view/index.ts
index b3647dcbf9..ea97d3ac6a 100644
--- a/packages/scaffold-ui/src/views/w3m-connect-view/index.ts
+++ b/packages/scaffold-ui/src/views/w3m-connect-view/index.ts
@@ -89,26 +89,26 @@ export class W3mConnectView extends LitElement {
public override render() {
const { termsConditionsUrl, privacyPolicyUrl } = OptionsController.state
- const legalCheckbox = OptionsController.state.features?.legalCheckbox
+ const isLegalCheckbox = OptionsController.state.features?.legalCheckbox
const legalUrl = termsConditionsUrl || privacyPolicyUrl
- const showLegalCheckbox =
- Boolean(legalUrl) && Boolean(legalCheckbox) && this.walletGuide === 'get-started'
+ const isShowLegalCheckbox =
+ Boolean(legalUrl) && Boolean(isLegalCheckbox) && this.walletGuide === 'get-started'
- const disabled = showLegalCheckbox && !this.checked
+ const isDisabled = isShowLegalCheckbox && !this.checked
const classes = {
connect: true,
- disabled
+ disabled: isDisabled
}
- const enableWalletGuide = OptionsController.state.enableWalletGuide
+ const isEnableWalletGuide = OptionsController.state.enableWalletGuide
- const enableWallets = this.enableWallets
+ const isEnableWallets = this.enableWallets
const socialOrEmailLoginEnabled = this.isSocialEnabled || this.authConnector
- const tabIndex = disabled ? -1 : undefined
+ const tabIndex = isDisabled ? -1 : undefined
return html`
@@ -123,8 +123,8 @@ export class W3mConnectView extends LitElement {
flexDirection="column"
gap="s"
.padding=${socialOrEmailLoginEnabled &&
- enableWallets &&
- enableWalletGuide &&
+ isEnableWallets &&
+ isEnableWalletGuide &&
this.walletGuide === 'get-started'
? ['3xs', 's', '0', 's']
: ['3xs', 's', 's', 's']}
@@ -132,7 +132,7 @@ export class W3mConnectView extends LitElement {
${this.renderConnectMethod(tabIndex)}
- ${this.guideTemplate(disabled)}
+ ${this.guideTemplate(isDisabled)}
`
@@ -248,16 +248,16 @@ export class W3mConnectView extends LitElement {
}
private walletListTemplate(tabIndex?: number) {
- const enableWallets = this.enableWallets
- const collapseWalletsOldProp = this.features?.emailShowWallets === false
- const collapseWallets = this.features?.collapseWallets
- const shouldCollapseWallets = collapseWalletsOldProp || collapseWallets
+ const isEnableWallets = this.enableWallets
+ const isCollapseWalletsOldProp = this.features?.emailShowWallets === false
+ const isCollapseWallets = this.features?.collapseWallets
+ const shouldCollapseWallets = isCollapseWalletsOldProp || isCollapseWallets
- if (!enableWallets) {
+ if (!isEnableWallets) {
return null
}
// In tg ios context, we have to preload the connection uri so we can use it to deeplink on user click
- if (CoreHelperUtil.isTelegram() && CoreHelperUtil.isIos()) {
+ if ((CoreHelperUtil.isTelegram() || CoreHelperUtil.isSafari()) && CoreHelperUtil.isIos()) {
ConnectionController.connectWalletConnect().catch(_e => ({}))
}
@@ -280,9 +280,9 @@ export class W3mConnectView extends LitElement {
}
private guideTemplate(disabled = false) {
- const enableWalletGuide = OptionsController.state.enableWalletGuide
+ const isEnableWalletGuide = OptionsController.state.enableWalletGuide
- if (!enableWalletGuide) {
+ if (!isEnableWalletGuide) {
return null
}
diff --git a/packages/scaffold-ui/test/partials/w3m-alertbar.test.ts b/packages/scaffold-ui/test/partials/w3m-alertbar.test.ts
new file mode 100644
index 0000000000..dec936cf05
--- /dev/null
+++ b/packages/scaffold-ui/test/partials/w3m-alertbar.test.ts
@@ -0,0 +1,108 @@
+import { elementUpdated, fixture } from '@open-wc/testing'
+import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
+
+import { html } from 'lit'
+
+import { AlertController } from '@reown/appkit-core'
+import type { WuiAlertBar } from '@reown/appkit-ui'
+
+import { W3mAlertBar, presets } from '../../src/partials/w3m-alertbar'
+import { HelpersUtil } from '../utils/HelpersUtil'
+
+// --- Constants ---------------------------------------------------- //
+const ALERBAR = 'wui-alertbar'
+const ALERT_VARIANTS = [
+ {
+ variant: 'info',
+ message: 'info message',
+ preset: presets.info
+ },
+ {
+ variant: 'success',
+ message: 'success message',
+ preset: presets.success
+ },
+ {
+ variant: 'warning',
+ message: 'warning message',
+ preset: presets.warning
+ },
+ {
+ variant: 'error',
+ message: 'error message',
+ preset: presets.error
+ }
+] as const
+
+describe('W3mAlertBar', () => {
+ beforeAll(() => {
+ Element.prototype.animate = vi.fn()
+ })
+
+ afterEach(() => {
+ vi.clearAllMocks()
+ })
+
+ it('it should display the correct alert based on the variant', async () => {
+ for (const { variant, message, preset } of ALERT_VARIANTS) {
+ AlertController.state.message = message
+ AlertController.state.variant = variant
+
+ const alertBar: W3mAlertBar = await fixture(html``)
+ const {
+ message: alertMessage,
+ backgroundColor,
+ icon,
+ iconColor
+ } = HelpersUtil.querySelect(alertBar, ALERBAR) as WuiAlertBar
+
+ expect(alertMessage).toBe(message)
+ expect(backgroundColor).toBe(preset.backgroundColor)
+ expect(icon).toBe(preset.icon)
+ expect(iconColor).toBe(preset.iconColor)
+ }
+ })
+
+ it('it should apply correct animations and styles based on open state', async () => {
+ AlertController.state.open = false
+
+ const alertBar: W3mAlertBar = await fixture(html``)
+ const animateSpy = vi.spyOn(alertBar, 'animate')
+
+ AlertController.state.open = true
+
+ await alertBar.requestUpdate()
+ await elementUpdated(alertBar)
+
+ expect(animateSpy).toHaveBeenCalledWith(
+ [
+ { opacity: 0, transform: 'scale(0.85)' },
+ { opacity: 1, transform: 'scale(1)' }
+ ],
+ {
+ duration: 150,
+ fill: 'forwards',
+ easing: 'ease'
+ }
+ )
+ expect(alertBar.style.cssText).toContain('pointer-events: auto')
+
+ AlertController.state.open = false
+
+ await alertBar.requestUpdate()
+ await elementUpdated(alertBar)
+
+ expect(animateSpy).toHaveBeenCalledWith(
+ [
+ { opacity: 1, transform: 'scale(1)' },
+ { opacity: 0, transform: 'scale(0.85)' }
+ ],
+ {
+ duration: 150,
+ fill: 'forwards',
+ easing: 'ease'
+ }
+ )
+ expect(alertBar.style.cssText).toContain('pointer-events: none')
+ })
+})
diff --git a/packages/scaffold-ui/test/partials/w3m-all-wallets-widget.test.ts b/packages/scaffold-ui/test/partials/w3m-all-wallets-widget.test.ts
new file mode 100644
index 0000000000..0a2e88fb94
--- /dev/null
+++ b/packages/scaffold-ui/test/partials/w3m-all-wallets-widget.test.ts
@@ -0,0 +1,158 @@
+import { fixture } from '@open-wc/testing'
+import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
+
+import { html } from 'lit'
+
+import {
+ ApiController,
+ type ApiControllerState,
+ ConnectorController,
+ type ConnectorControllerState,
+ type ConnectorWithProviders,
+ CoreHelperUtil,
+ EventsController,
+ OptionsController,
+ type OptionsControllerState,
+ RouterController,
+ type SdkVersion
+} from '@reown/appkit-core'
+
+import type { OptionsControllerStateInternal } from '../../../core/dist/types/src/controllers/OptionsController'
+import { W3mAllWalletsWidget } from '../../src/partials/w3m-all-wallets-widget'
+import { HelpersUtil } from '../utils/HelpersUtil'
+
+// --- Constants ---------------------------------------------------- //
+const ALL_WALLETS_TEST_ID = 'all-wallets'
+const WALLET_CONNECT_ID = 'walletConnect'
+
+const mockConnectorState: ConnectorControllerState = {
+ connectors: [],
+ activeConnector: undefined,
+ allConnectors: []
+}
+
+const mockOptionsState: OptionsControllerState & OptionsControllerStateInternal = {
+ allWallets: 'SHOW' as const,
+ projectId: 'test-project-id',
+ sdkVersion: '1.0.0' as SdkVersion,
+ sdkType: 'appkit' as const,
+ defaultAccountTypes: []
+} as unknown as OptionsControllerState & OptionsControllerStateInternal
+
+const mockConnector: ConnectorWithProviders = {
+ id: WALLET_CONNECT_ID,
+ type: 'WALLET_CONNECT',
+ name: 'WalletConnect',
+ chain: 'eip155'
+}
+
+const mockApiState: ApiControllerState = {
+ page: 1,
+ count: 8,
+ featured: [
+ {
+ id: '1',
+ name: 'Test Wallet',
+ rdns: 'io.test',
+ homepage: 'https://test.com',
+ image_url: 'https://test.com/image.png'
+ }
+ ],
+ recommended: [],
+ wallets: [],
+ search: [],
+ isAnalyticsEnabled: false,
+ excludedRDNS: []
+}
+
+describe('W3mAllWalletsWidget', () => {
+ beforeAll(() => {
+ vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false)
+ })
+
+ afterEach(() => {
+ vi.resetAllMocks()
+ })
+
+ it('should not render if WalletConnect connector is not found', async () => {
+ vi.spyOn(ConnectorController, 'state', 'get').mockReturnValue(mockConnectorState)
+
+ const element: W3mAllWalletsWidget = await fixture(
+ html``
+ )
+
+ expect(HelpersUtil.getByTestId(element, ALL_WALLETS_TEST_ID)).toBeNull()
+ })
+
+ it('should not render if allWallets option is HIDE', async () => {
+ vi.spyOn(ConnectorController, 'state', 'get').mockReturnValue({
+ ...mockConnectorState,
+ connectors: [mockConnector]
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ allWallets: 'HIDE' as const
+ } as unknown as OptionsControllerState & OptionsControllerStateInternal)
+
+ const element: W3mAllWalletsWidget = await fixture(
+ html``
+ )
+
+ expect(HelpersUtil.getByTestId(element, ALL_WALLETS_TEST_ID)).toBeNull()
+ })
+
+ it('should not render if allWallets is ONLY_MOBILE and not on mobile', async () => {
+ vi.spyOn(ConnectorController, 'state', 'get').mockReturnValue({
+ ...mockConnectorState,
+ connectors: [mockConnector]
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ allWallets: 'ONLY_MOBILE' as const
+ } as unknown as OptionsControllerState & OptionsControllerStateInternal)
+ vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false)
+
+ const element: W3mAllWalletsWidget = await fixture(
+ html``
+ )
+
+ expect(HelpersUtil.getByTestId(element, ALL_WALLETS_TEST_ID)).toBeNull()
+ })
+
+ it('should render with correct wallet count tag', async () => {
+ vi.spyOn(ConnectorController, 'state', 'get').mockReturnValue({
+ ...mockConnectorState,
+ connectors: [mockConnector]
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue(mockOptionsState)
+ vi.spyOn(ApiController, 'state', 'get').mockReturnValue(mockApiState)
+
+ const element: W3mAllWalletsWidget = await fixture(
+ html``
+ )
+
+ const walletList = HelpersUtil.getByTestId(element, ALL_WALLETS_TEST_ID)
+ expect(walletList).not.toBeNull()
+ expect(walletList.getAttribute('tagLabel')).toBe('9')
+ })
+
+ it('should navigate to AllWallets view and track event on click', async () => {
+ vi.spyOn(ConnectorController, 'state', 'get').mockReturnValue({
+ ...mockConnectorState,
+ connectors: [mockConnector]
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue(mockOptionsState)
+ vi.spyOn(ApiController, 'state', 'get').mockReturnValue(mockApiState)
+
+ const routerPushSpy = vi.spyOn(RouterController, 'push')
+ const sendEventSpy = vi.spyOn(EventsController, 'sendEvent')
+
+ const element: W3mAllWalletsWidget = await fixture(
+ html``
+ )
+
+ const walletList = HelpersUtil.getByTestId(element, ALL_WALLETS_TEST_ID)
+ walletList.click()
+
+ expect(sendEventSpy).toHaveBeenCalledWith({ type: 'track', event: 'CLICK_ALL_WALLETS' })
+ expect(routerPushSpy).toHaveBeenCalledWith('AllWallets')
+ })
+})
diff --git a/packages/scaffold-ui/test/partials/w3m-connecting-wc-mobile.test.ts b/packages/scaffold-ui/test/partials/w3m-connecting-wc-mobile.test.ts
index 35520b41f5..b9328f26a6 100644
--- a/packages/scaffold-ui/test/partials/w3m-connecting-wc-mobile.test.ts
+++ b/packages/scaffold-ui/test/partials/w3m-connecting-wc-mobile.test.ts
@@ -31,7 +31,7 @@ describe('W3mConnectingWcMobile', () => {
const el: W3mConnectingWcMobile = await fixture(
html``
)
- el['onConnectProxy']()
+ el['onConnect']()
expect(openHrefSpy).toHaveBeenCalledWith(`link://wc?uri=${WC_URI}`, '_self')
})
@@ -46,7 +46,7 @@ describe('W3mConnectingWcMobile', () => {
const el: W3mConnectingWcMobile = await fixture(
html``
)
- el['onConnectProxy']()
+ el['onConnect']()
expect(openHrefSpy).toHaveBeenCalledWith(`link://wc?uri=${WC_URI}`, '_top')
} finally {
diff --git a/packages/scaffold-ui/test/partials/w3m-connecting-wc-qrcode.test.ts b/packages/scaffold-ui/test/partials/w3m-connecting-wc-qrcode.test.ts
index 41949ca3f3..1841e12ef8 100644
--- a/packages/scaffold-ui/test/partials/w3m-connecting-wc-qrcode.test.ts
+++ b/packages/scaffold-ui/test/partials/w3m-connecting-wc-qrcode.test.ts
@@ -7,7 +7,6 @@ import {
ConnectionController,
CoreHelperUtil,
EventsController,
- OptionsController,
RouterController,
type WcWallet
} from '@reown/appkit-core'
@@ -16,7 +15,6 @@ import type { WuiQrCode } from '@reown/appkit-ui'
import { HelpersUtil } from '../utils/HelpersUtil'
// -- Constants ------------------------------------------- //
-const ALL_WALLETS_WIDGET = 'w3m-all-wallets-widget'
const QR_CODE = 'wui-qr-code'
const WALLET = {
name: 'WalletConnect'
@@ -64,23 +62,4 @@ describe('W3mConnectingWcQrcode', () => {
properties: { name: WALLET.name, platform: 'qrcode' }
})
})
-
- it('it should use the injected universal provider when "OptionsController.useInjectedUniversalProvider" is true', async () => {
- vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
- ...OptionsController.state,
- useInjectedUniversalProvider: true
- })
-
- const connectingQrCode = await fixture(
- html``
- )
-
- // We display all wallets widget if we use injected universal provider
- expect(HelpersUtil.querySelect(connectingQrCode, ALL_WALLETS_WIDGET)).not.toBeNull()
- expect(EventsController.sendEvent).toHaveBeenCalledWith({
- type: 'track',
- event: 'SELECT_WALLET',
- properties: { name: WALLET.name, platform: 'qrcode' }
- })
- })
})
diff --git a/packages/scaffold-ui/test/partials/w3m-default-account-widget.test.ts b/packages/scaffold-ui/test/partials/w3m-default-account-widget.test.ts
new file mode 100644
index 0000000000..bc8d4ae296
--- /dev/null
+++ b/packages/scaffold-ui/test/partials/w3m-default-account-widget.test.ts
@@ -0,0 +1,301 @@
+import { fixture } from '@open-wc/testing'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { html } from 'lit'
+
+import { ConstantsUtil } from '@reown/appkit-common'
+import {
+ AccountController,
+ ChainController,
+ ConnectionController,
+ ConnectorController,
+ CoreHelperUtil,
+ EventsController,
+ ModalController,
+ OptionsController,
+ RouterController,
+ SnackController,
+ StorageUtil
+} from '@reown/appkit-core'
+import type {
+ AccountControllerState,
+ AuthConnector,
+ ChainControllerState
+} from '@reown/appkit-core'
+
+import type { W3mAccountDefaultWidget } from '../../exports'
+import { HelpersUtil } from '../utils/HelpersUtil'
+
+describe('W3mAccountDefaultWidget', () => {
+ const mockCaipAddress = 'eip155:1:0x123'
+ const mockAddress = '0x123'
+ const mockProfileName = 'Test Account'
+ const mockProfileImage = 'profile.jpg'
+
+ beforeEach(() => {
+ // Mock AccountController state
+ vi.spyOn(AccountController, 'state', 'get').mockReturnValue({
+ caipAddress: mockCaipAddress,
+ address: mockAddress,
+ profileName: mockProfileName,
+ profileImage: mockProfileImage,
+ balance: '100',
+ balanceSymbol: 'ETH',
+ allAccounts: [{ address: mockAddress, type: 'eoa' }],
+ addressExplorerUrl: 'https://etherscan.io',
+ addressLabels: new Map(),
+ preferredAccountType: 'eoa'
+ } as unknown as AccountControllerState)
+
+ // Mock ChainController state
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ activeChain: ConstantsUtil.CHAIN.EVM,
+ activeCaipNetwork: {
+ id: '1',
+ caipNetworkId: 'eip155:1'
+ }
+ } as unknown as ChainControllerState)
+
+ // Mock OptionsController state
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ features: {
+ onramp: true,
+ swaps: true,
+ send: true,
+ walletFeaturesOrder: ['onramp', 'swaps', 'send']
+ }
+ } as any)
+
+ // Mock other controllers
+ vi.spyOn(StorageUtil, 'getConnectedConnectorId').mockReturnValue('test')
+ vi.spyOn(ConnectorController, 'getAuthConnector').mockReturnValue(undefined)
+ vi.spyOn(CoreHelperUtil, 'copyToClopboard').mockImplementation(vi.fn())
+ vi.spyOn(CoreHelperUtil, 'openHref').mockImplementation(vi.fn())
+ vi.spyOn(RouterController, 'push').mockImplementation(vi.fn())
+ vi.spyOn(ModalController, 'close').mockImplementation(vi.fn())
+ vi.spyOn(EventsController, 'sendEvent').mockImplementation(vi.fn())
+ vi.spyOn(SnackController, 'showSuccess').mockImplementation(vi.fn())
+ vi.spyOn(SnackController, 'showError').mockImplementation(vi.fn())
+ })
+
+ afterEach(() => {
+ vi.clearAllMocks()
+ })
+
+ describe('Rendering', () => {
+ it('renders nothing when no caipAddress', async () => {
+ vi.spyOn(AccountController, 'state', 'get').mockReturnValue({
+ caipAddress: null
+ } as unknown as AccountControllerState)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ // Should only have styles tag
+ expect(element.shadowRoot?.children.length).toBe(1)
+ })
+
+ it('renders single account view for Solana chain', async () => {
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ activeChain: ConstantsUtil.CHAIN.SOLANA
+ } as unknown as ChainControllerState)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ expect(HelpersUtil.querySelect(element, '[data-testid="single-account-avatar"]')).toBeTruthy()
+ })
+
+ it('renders multi account view for EVM with multiple accounts', async () => {
+ vi.spyOn(AccountController, 'state', 'get').mockReturnValue({
+ ...AccountController.state,
+ allAccounts: [
+ { address: '0x123', type: 'eoa' },
+ { address: '0x456', type: 'eoa' }
+ ]
+ } as AccountControllerState)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ expect(HelpersUtil.querySelect(element, 'wui-profile-button-v2')).toBeTruthy()
+ })
+
+ it('renders BTC accounts template for bip122 namespace with multiple accounts', async () => {
+ vi.spyOn(AccountController, 'state', 'get').mockReturnValue({
+ ...AccountController.state,
+ allAccounts: [
+ { address: '0x123', type: 'eoa' },
+ { address: '0x456', type: 'eoa' }
+ ]
+ } as AccountControllerState)
+
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ activeChain: 'bip122',
+ activeCaipNetwork: { id: '1', caipNetworkId: 'bip122:1' }
+ } as unknown as ChainControllerState)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ expect(HelpersUtil.querySelect(element, 'wui-tabs')).toBeTruthy()
+ })
+ })
+
+ describe('Features', () => {
+ it('shows onramp button when enabled for supported chain', async () => {
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ expect(
+ HelpersUtil.querySelect(element, '[data-testid="w3m-account-default-onramp-button"]')
+ ).toBeTruthy()
+ })
+
+ it('should not show onramp button when disabled', async () => {
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ features: {
+ onramp: false
+ }
+ } as any)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ expect(HelpersUtil.getByTestId(element, 'w3m-account-default-onramp-button')).toBeFalsy()
+ })
+
+ it('should not show onramp button for non-enabled chain', async () => {
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ activeChain: ConstantsUtil.CHAIN.BITCOIN
+ } as unknown as ChainControllerState)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ expect(HelpersUtil.getByTestId(element, 'w3m-account-default-onramp-button')).toBeFalsy()
+ })
+
+ it('shows swap button for EVM chain', async () => {
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const text = HelpersUtil.querySelectAll(element, 'wui-text')
+ expect([...text].some(t => t.textContent?.includes('Swap'))).toBeTruthy()
+ })
+
+ it('does not show swap button when disabled', async () => {
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ features: {
+ swaps: false
+ }
+ } as any)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const texts = HelpersUtil.querySelectAll(element, 'wui-text')
+ expect([...texts].some(text => text?.textContent?.includes('Swap'))).toBeFalsy()
+ })
+
+ it('does not show swap button for non-EVM chain', async () => {
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ activeChain: ConstantsUtil.CHAIN.SOLANA
+ } as unknown as ChainControllerState)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const texts = HelpersUtil.querySelectAll(element, 'wui-text')
+ expect([...texts].some(text => text?.textContent?.includes('Swap'))).toBeFalsy()
+ })
+
+ it('shows auth card for AUTH connector', async () => {
+ vi.spyOn(StorageUtil, 'getConnectedConnectorId').mockReturnValue(
+ ConstantsUtil.CONNECTOR_ID.AUTH
+ )
+ vi.spyOn(ConnectorController, 'getAuthConnector').mockReturnValue({
+ id: 'auth',
+ provider: {
+ getEmail: () => 'email@email.com'
+ }
+ } as AuthConnector)
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ expect(
+ HelpersUtil.querySelect(element, '[data-testid="w3m-wallet-upgrade-card"]')
+ ).toBeTruthy()
+ })
+ })
+
+ describe('Interactions', () => {
+ it('copies address and shows success message', async () => {
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const copyButton = HelpersUtil.querySelect(element, 'wui-icon-link')
+ await copyButton?.click()
+
+ expect(CoreHelperUtil.copyToClopboard).toHaveBeenCalledWith(mockAddress)
+ expect(SnackController.showSuccess).toHaveBeenCalledWith('Address copied')
+ })
+
+ it('disconnects wallet successfully', async () => {
+ vi.spyOn(ConnectionController, 'disconnect').mockResolvedValue()
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const disconnectButton = HelpersUtil.querySelect(element, '[data-testid="disconnect-button"]')
+ await disconnectButton?.click()
+
+ expect(ConnectionController.disconnect).toHaveBeenCalled()
+ expect(EventsController.sendEvent).toHaveBeenCalledWith({
+ type: 'track',
+ event: 'DISCONNECT_SUCCESS'
+ })
+ expect(ModalController.close).toHaveBeenCalled()
+ })
+
+ it('handles disconnect failure', async () => {
+ vi.spyOn(ConnectionController, 'disconnect').mockRejectedValue(new Error())
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const disconnectButton = HelpersUtil.querySelect(element, '[data-testid="disconnect-button"]')
+ await disconnectButton?.click()
+
+ expect(EventsController.sendEvent).toHaveBeenCalledWith({
+ type: 'track',
+ event: 'DISCONNECT_ERROR'
+ })
+ expect(SnackController.showError).toHaveBeenCalledWith('Failed to disconnect')
+ })
+
+ it('navigates to explorer', async () => {
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const explorerButton = HelpersUtil.querySelect(element, 'wui-button')
+ await explorerButton?.click()
+
+ expect(CoreHelperUtil.openHref).toHaveBeenCalledWith('https://etherscan.io', '_blank')
+ })
+ })
+
+ describe('State Updates', () => {
+ it('cleans up subscriptions on disconnect', async () => {
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+ const unsubscribeSpy = vi.fn()
+ ;(element as any).unsubscribe = [unsubscribeSpy]
+
+ element.disconnectedCallback()
+ expect(unsubscribeSpy).toHaveBeenCalled()
+ })
+ })
+})
diff --git a/packages/siwx/src/configs/CloudAuthSIWX.ts b/packages/siwx/src/configs/CloudAuthSIWX.ts
index 123eff245f..d1f341a48a 100644
--- a/packages/siwx/src/configs/CloudAuthSIWX.ts
+++ b/packages/siwx/src/configs/CloudAuthSIWX.ts
@@ -72,7 +72,10 @@ export class CloudAuthSIWX implements SIWXConfig {
const siweCaipNetworkId = `eip155:${siweSession?.chainId}`
- if (!siweSession || siweCaipNetworkId !== chainId || siweSession.address !== address) {
+ const isSameAddress = siweSession?.address.toLowerCase() === address.toLowerCase()
+ const isSameNetwork = siweCaipNetworkId === chainId
+
+ if (!isSameAddress || !isSameNetwork) {
return []
}
diff --git a/packages/siwx/tests/configs/CloudAuthSIWX.test.ts b/packages/siwx/tests/configs/CloudAuthSIWX.test.ts
index c33487615b..bd4476657b 100644
--- a/packages/siwx/tests/configs/CloudAuthSIWX.test.ts
+++ b/packages/siwx/tests/configs/CloudAuthSIWX.test.ts
@@ -241,6 +241,33 @@ Issued At: 2024-12-05T16:02:32.905Z`)
)
})
+ it('gets sessions when address is not lowercased', async () => {
+ const fetchSpy = vi.spyOn(global, 'fetch')
+
+ fetchSpy.mockResolvedValueOnce(
+ mocks.mockFetchResponse({
+ address: '0x1234567890abcdef1234567890abcdef12345678',
+ chainId: 1
+ })
+ )
+
+ const sessions = await siwx.getSessions(
+ 'eip155:1',
+ '0x1234567890ABCDEF1234567890abcdef12345678'
+ )
+
+ expect(sessions).toEqual([
+ {
+ data: {
+ accountAddress: '0x1234567890abcdef1234567890abcdef12345678',
+ chainId: 'eip155:1'
+ },
+ message: '',
+ signature: ''
+ }
+ ])
+ })
+
it('returns empty array if session is not found', async () => {
const fetchSpy = vi.spyOn(global, 'fetch')