Skip to content

Commit a7646ab

Browse files
tomiirclaude
andauthored
feat: expose full API params in fetchWallets hook (#5526)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 00479a1 commit a7646ab

4 files changed

Lines changed: 267 additions & 20 deletions

File tree

.changeset/four-pans-attend.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@reown/appkit-adapter-wagmi': patch
3+
'@reown/appkit-utils': patch
4+
'@reown/appkit-experimental': patch
5+
'@reown/appkit-controllers': patch
6+
'@reown/appkit': patch
7+
'@reown/appkit-common': patch
8+
'@reown/appkit-siwe': patch
9+
'@reown/appkit-siwx': patch
10+
'@reown/appkit-cdn': patch
11+
'@reown/appkit-pay': patch
12+
---
13+
14+
Adds more options to fetchWallets method on useAppKitWallets

packages/controllers/exports/react.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { ConnectorControllerUtil } from '../src/utils/ConnectorControllerUtil.js
2424
import { CoreHelperUtil } from '../src/utils/CoreHelperUtil.js'
2525
import { MobileWalletUtil } from '../src/utils/MobileWallet.js'
2626
import type {
27+
BadgeType,
2728
NamespaceTypeMap,
2829
UseAppKitAccountReturn,
2930
UseAppKitNetworkReturn,
@@ -301,6 +302,23 @@ export function useAppKitConnection({ namespace, onSuccess, onError }: UseAppKit
301302
}
302303
}
303304

305+
export interface FetchWalletsOptions {
306+
/** Page number to fetch (default: 1) */
307+
page?: number
308+
/** @deprecated Use `search` instead */
309+
query?: string
310+
/** Search query to filter wallets. When provided, switches to search mode. */
311+
search?: string
312+
/** Number of entries per page. Defaults to 40 for list mode, 100 for search mode. */
313+
entries?: number
314+
/** Filter wallets by badge type ('none' | 'certified') */
315+
badge?: BadgeType
316+
/** Wallet IDs to include. Overrides the global includeWalletIds config when provided. */
317+
include?: string[]
318+
/** Wallet IDs to exclude. Overrides the default exclude list when provided. */
319+
exclude?: string[]
320+
}
321+
304322
export interface UseAppKitWalletsReturn {
305323
/**
306324
* List of wallets for the initial connect view including WalletConnect wallet and injected wallets together. If user doesn't have any injected wallets, it'll fill the list with most ranked WalletConnect wallets.
@@ -351,10 +369,8 @@ export interface UseAppKitWalletsReturn {
351369
/**
352370
* Function to fetch WalletConnect wallets from the explorer API. Allows to list, search and paginate through the wallets.
353371
* @param options - Options for fetching wallets
354-
* @param options.page - Page number to fetch (default: 1)
355-
* @param options.query - Search query to filter wallets (default: '')
356372
*/
357-
fetchWallets: (options?: { page?: number; query?: string }) => Promise<void>
373+
fetchWallets: (options?: FetchWalletsOptions) => Promise<void>
358374

359375
/**
360376
* Function to connect to a wallet.
@@ -438,16 +454,17 @@ export function useAppKitWallets(): UseAppKitWalletsReturn {
438454
await ConnectionController.connectWalletConnect({ cache: 'auto' })
439455
}
440456

441-
async function fetchWallets(fetchOptions?: { page?: number; query?: string }) {
457+
async function fetchWallets(fetchOptions?: FetchWalletsOptions) {
442458
setIsFetchingWallets(true)
443459
try {
444-
if (fetchOptions?.query) {
445-
await ApiController.searchWallet({ search: fetchOptions?.query })
460+
const { query, ...options } = fetchOptions ?? {}
461+
const search = options.search ?? query
462+
463+
if (search) {
464+
await ApiController.searchWallet({ ...options, search })
446465
} else {
447466
ApiController.state.search = []
448-
await ApiController.fetchWalletsByPage({
449-
page: fetchOptions?.page ?? 1
450-
})
467+
await ApiController.fetchWalletsByPage({ page: 1, ...options })
451468
}
452469
} catch (error) {
453470
// eslint-disable-next-line no-console

packages/controllers/src/controllers/ApiController.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
ApiGetUsageResponse,
1717
ApiGetWalletsRequest,
1818
ApiGetWalletsResponse,
19+
BadgeType,
1920
ProjectLimits,
2021
Tier,
2122
WcWallet
@@ -385,19 +386,31 @@ export const ApiController = {
385386
}
386387
},
387388

388-
async fetchWalletsByPage({ page }: Pick<ApiGetWalletsRequest, 'page'>) {
389+
async fetchWalletsByPage({
390+
page,
391+
entries: entriesOverride,
392+
badge,
393+
include: includeOverride,
394+
exclude: excludeOverride
395+
}: Pick<ApiGetWalletsRequest, 'page'> & {
396+
entries?: number
397+
badge?: BadgeType
398+
include?: string[]
399+
exclude?: string[]
400+
}) {
389401
const { includeWalletIds, excludeWalletIds, featuredWalletIds } = OptionsController.state
390402
const chains = ChainController.getRequestedCaipNetworkIds().join(',')
391-
const exclude = [
403+
const defaultExclude = [
392404
...state.recommended.map(({ id }) => id),
393405
...(excludeWalletIds ?? []),
394406
...(featuredWalletIds ?? [])
395407
].filter(Boolean)
396408
const params = {
397409
page,
398-
entries,
399-
include: includeWalletIds,
400-
exclude,
410+
entries: entriesOverride ?? entries,
411+
include: includeOverride ?? includeWalletIds,
412+
exclude: excludeOverride ?? defaultExclude,
413+
badge_type: badge,
401414
chains
402415
}
403416
const { data, count, mobileFilteredOutWalletsLength } = await ApiController.fetchWallets(params)
@@ -435,18 +448,30 @@ export const ApiController = {
435448
}
436449
},
437450

438-
async searchWallet({ search, badge }: Pick<ApiGetWalletsRequest, 'search' | 'badge'>) {
451+
async searchWallet({
452+
search,
453+
badge,
454+
entries: entriesOverride,
455+
page: pageOverride,
456+
include: includeOverride,
457+
exclude: excludeOverride
458+
}: Pick<ApiGetWalletsRequest, 'search' | 'badge'> & {
459+
entries?: number
460+
page?: number
461+
include?: string[]
462+
exclude?: string[]
463+
}) {
439464
const { includeWalletIds, excludeWalletIds } = OptionsController.state
440465
const chains = ChainController.getRequestedCaipNetworkIds().join(',')
441466
state.search = []
442467

443468
const params = {
444-
page: 1,
445-
entries: 100,
469+
page: pageOverride ?? 1,
470+
entries: entriesOverride ?? 100,
446471
search: search?.trim(),
447472
badge_type: badge,
448-
include: includeWalletIds,
449-
exclude: excludeWalletIds,
473+
include: includeOverride ?? includeWalletIds,
474+
exclude: excludeOverride ?? excludeWalletIds,
450475
chains
451476
}
452477

packages/controllers/tests/hooks/react.test.ts

Lines changed: 192 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ describe('useAppKitWallets', () => {
938938
await result.fetchWallets()
939939

940940
expect(setIsFetchingWallets).toHaveBeenCalledWith(true)
941-
expect(fetchWalletsByPageSpy).toHaveBeenCalledWith({ page: 1, entries: undefined })
941+
expect(fetchWalletsByPageSpy).toHaveBeenCalledWith({ page: 1 })
942942
expect(ApiController.state.search).toEqual([])
943943
expect(setIsFetchingWallets).toHaveBeenCalledWith(false)
944944
})
@@ -1058,6 +1058,197 @@ describe('useAppKitWallets', () => {
10581058
consoleErrorSpy.mockRestore()
10591059
})
10601060

1061+
it('should fetch wallets with search param', async () => {
1062+
const setIsFetchingWallets = vi.fn()
1063+
mockedReact.useState.mockReturnValue([false, setIsFetchingWallets])
1064+
1065+
useSnapshot
1066+
.mockReturnValueOnce({
1067+
features: { headless: true },
1068+
remoteFeatures: { headless: true }
1069+
})
1070+
.mockReturnValueOnce({
1071+
wcUri: undefined,
1072+
wcFetchingUri: false
1073+
})
1074+
.mockReturnValueOnce({
1075+
wallets: [],
1076+
search: [],
1077+
page: 1,
1078+
count: 0
1079+
})
1080+
.mockReturnValueOnce({
1081+
initialized: true,
1082+
connectingWallet: undefined
1083+
})
1084+
1085+
vi.spyOn(ConnectUtil, 'getInitialWallets').mockReturnValue([])
1086+
vi.spyOn(ConnectUtil, 'getWalletConnectWallets').mockReturnValue([])
1087+
const searchWalletSpy = vi.spyOn(ApiController, 'searchWallet').mockResolvedValue(undefined)
1088+
1089+
const result = useAppKitWallets()
1090+
1091+
await result.fetchWallets({ search: 'phantom' })
1092+
1093+
expect(searchWalletSpy).toHaveBeenCalledWith({ search: 'phantom' })
1094+
})
1095+
1096+
it('should use search over query when both provided', async () => {
1097+
const setIsFetchingWallets = vi.fn()
1098+
mockedReact.useState.mockReturnValue([false, setIsFetchingWallets])
1099+
1100+
useSnapshot
1101+
.mockReturnValueOnce({
1102+
features: { headless: true },
1103+
remoteFeatures: { headless: true }
1104+
})
1105+
.mockReturnValueOnce({
1106+
wcUri: undefined,
1107+
wcFetchingUri: false
1108+
})
1109+
.mockReturnValueOnce({
1110+
wallets: [],
1111+
search: [],
1112+
page: 1,
1113+
count: 0
1114+
})
1115+
.mockReturnValueOnce({
1116+
initialized: true,
1117+
connectingWallet: undefined
1118+
})
1119+
1120+
vi.spyOn(ConnectUtil, 'getInitialWallets').mockReturnValue([])
1121+
vi.spyOn(ConnectUtil, 'getWalletConnectWallets').mockReturnValue([])
1122+
const searchWalletSpy = vi.spyOn(ApiController, 'searchWallet').mockResolvedValue(undefined)
1123+
1124+
const result = useAppKitWallets()
1125+
1126+
await result.fetchWallets({ search: 'phantom', query: 'metamask' })
1127+
1128+
expect(searchWalletSpy).toHaveBeenCalledWith({ search: 'phantom' })
1129+
})
1130+
1131+
it('should pass entries and badge to fetchWalletsByPage', async () => {
1132+
const setIsFetchingWallets = vi.fn()
1133+
mockedReact.useState.mockReturnValue([false, setIsFetchingWallets])
1134+
1135+
useSnapshot
1136+
.mockReturnValueOnce({
1137+
features: { headless: true },
1138+
remoteFeatures: { headless: true }
1139+
})
1140+
.mockReturnValueOnce({
1141+
wcUri: undefined,
1142+
wcFetchingUri: false
1143+
})
1144+
.mockReturnValueOnce({
1145+
wallets: [],
1146+
search: [],
1147+
page: 1,
1148+
count: 0
1149+
})
1150+
.mockReturnValueOnce({
1151+
initialized: true,
1152+
connectingWallet: undefined
1153+
})
1154+
1155+
vi.spyOn(ConnectUtil, 'getInitialWallets').mockReturnValue([])
1156+
vi.spyOn(ConnectUtil, 'getWalletConnectWallets').mockReturnValue([])
1157+
const fetchWalletsByPageSpy = vi
1158+
.spyOn(ApiController, 'fetchWalletsByPage')
1159+
.mockResolvedValue(undefined)
1160+
1161+
const result = useAppKitWallets()
1162+
1163+
await result.fetchWallets({ entries: 20, badge: 'certified' })
1164+
1165+
expect(fetchWalletsByPageSpy).toHaveBeenCalledWith({
1166+
page: 1,
1167+
entries: 20,
1168+
badge: 'certified'
1169+
})
1170+
})
1171+
1172+
it('should pass badge and entries to searchWallet', async () => {
1173+
const setIsFetchingWallets = vi.fn()
1174+
mockedReact.useState.mockReturnValue([false, setIsFetchingWallets])
1175+
1176+
useSnapshot
1177+
.mockReturnValueOnce({
1178+
features: { headless: true },
1179+
remoteFeatures: { headless: true }
1180+
})
1181+
.mockReturnValueOnce({
1182+
wcUri: undefined,
1183+
wcFetchingUri: false
1184+
})
1185+
.mockReturnValueOnce({
1186+
wallets: [],
1187+
search: [],
1188+
page: 1,
1189+
count: 0
1190+
})
1191+
.mockReturnValueOnce({
1192+
initialized: true,
1193+
connectingWallet: undefined
1194+
})
1195+
1196+
vi.spyOn(ConnectUtil, 'getInitialWallets').mockReturnValue([])
1197+
vi.spyOn(ConnectUtil, 'getWalletConnectWallets').mockReturnValue([])
1198+
const searchWalletSpy = vi.spyOn(ApiController, 'searchWallet').mockResolvedValue(undefined)
1199+
1200+
const result = useAppKitWallets()
1201+
1202+
await result.fetchWallets({ query: 'safe', badge: 'certified', entries: 50 })
1203+
1204+
expect(searchWalletSpy).toHaveBeenCalledWith({
1205+
search: 'safe',
1206+
badge: 'certified',
1207+
entries: 50
1208+
})
1209+
})
1210+
1211+
it('should pass include and exclude to fetchWalletsByPage', async () => {
1212+
const setIsFetchingWallets = vi.fn()
1213+
mockedReact.useState.mockReturnValue([false, setIsFetchingWallets])
1214+
1215+
useSnapshot
1216+
.mockReturnValueOnce({
1217+
features: { headless: true },
1218+
remoteFeatures: { headless: true }
1219+
})
1220+
.mockReturnValueOnce({
1221+
wcUri: undefined,
1222+
wcFetchingUri: false
1223+
})
1224+
.mockReturnValueOnce({
1225+
wallets: [],
1226+
search: [],
1227+
page: 1,
1228+
count: 0
1229+
})
1230+
.mockReturnValueOnce({
1231+
initialized: true,
1232+
connectingWallet: undefined
1233+
})
1234+
1235+
vi.spyOn(ConnectUtil, 'getInitialWallets').mockReturnValue([])
1236+
vi.spyOn(ConnectUtil, 'getWalletConnectWallets').mockReturnValue([])
1237+
const fetchWalletsByPageSpy = vi
1238+
.spyOn(ApiController, 'fetchWalletsByPage')
1239+
.mockResolvedValue(undefined)
1240+
1241+
const result = useAppKitWallets()
1242+
1243+
await result.fetchWallets({ include: ['wallet-1'], exclude: ['wallet-2'] })
1244+
1245+
expect(fetchWalletsByPageSpy).toHaveBeenCalledWith({
1246+
page: 1,
1247+
include: ['wallet-1'],
1248+
exclude: ['wallet-2']
1249+
})
1250+
})
1251+
10611252
it('should connect to injected wallet', async () => {
10621253
const mockConnector = {
10631254
id: 'test-connector',

0 commit comments

Comments
 (0)