UNPKG

@reown/appkit-controllers

Version:

#### 🔗 [Website](https://reown.com/appkit)

348 lines • 15 kB
import { proxy } from 'valtio/vanilla'; import { subscribeKey as subKey } from 'valtio/vanilla/utils'; import { ConstantsUtil } from '@reown/appkit-common'; import { AssetUtil } from '../utils/AssetUtil.js'; import { CoreHelperUtil } from '../utils/CoreHelperUtil.js'; import { FetchUtil } from '../utils/FetchUtil.js'; import { CUSTOM_DEEPLINK_WALLETS } from '../utils/MobileWallet.js'; import { StorageUtil } from '../utils/StorageUtil.js'; import { AssetController } from './AssetController.js'; import { ChainController } from './ChainController.js'; import { ConnectorController } from './ConnectorController.js'; import { EventsController } from './EventsController.js'; import { OptionsController } from './OptionsController.js'; // -- Helpers ------------------------------------------- // const baseUrl = CoreHelperUtil.getApiUrl(); export const api = new FetchUtil({ baseUrl, clientId: null }); const entries = 40; const recommendedEntries = 4; const imageCountToFetch = 20; // -- State --------------------------------------------- // const state = proxy({ promises: {}, page: 1, count: 0, featured: [], allFeatured: [], recommended: [], allRecommended: [], wallets: [], filteredWallets: [], search: [], isAnalyticsEnabled: false, excludedWallets: [], isFetchingRecommendedWallets: false }); // -- Controller ---------------------------------------- // export const ApiController = { state, subscribeKey(key, callback) { return subKey(state, key, callback); }, _getSdkProperties() { const { projectId, sdkType, sdkVersion } = OptionsController.state; return { projectId, st: sdkType || 'appkit', sv: sdkVersion || 'html-wagmi-4.2.2' }; }, _filterOutExtensions(wallets) { if (OptionsController.state.isUniversalProvider) { return wallets.filter(w => Boolean(w.mobile_link || w.desktop_link || w.webapp_link)); } return wallets; }, async _fetchWalletImage(imageId) { const imageUrl = `${api.baseUrl}/getWalletImage/${imageId}`; const blob = await api.getBlob({ path: imageUrl, params: ApiController._getSdkProperties() }); AssetController.setWalletImage(imageId, URL.createObjectURL(blob)); }, async _fetchNetworkImage(imageId) { const imageUrl = `${api.baseUrl}/public/getAssetImage/${imageId}`; const blob = await api.getBlob({ path: imageUrl, params: ApiController._getSdkProperties() }); AssetController.setNetworkImage(imageId, URL.createObjectURL(blob)); }, async _fetchConnectorImage(imageId) { const imageUrl = `${api.baseUrl}/public/getAssetImage/${imageId}`; const blob = await api.getBlob({ path: imageUrl, params: ApiController._getSdkProperties() }); AssetController.setConnectorImage(imageId, URL.createObjectURL(blob)); }, async _fetchCurrencyImage(countryCode) { const imageUrl = `${api.baseUrl}/public/getCurrencyImage/${countryCode}`; const blob = await api.getBlob({ path: imageUrl, params: ApiController._getSdkProperties() }); AssetController.setCurrencyImage(countryCode, URL.createObjectURL(blob)); }, async _fetchTokenImage(symbol) { const imageUrl = `${api.baseUrl}/public/getTokenImage/${symbol}`; const blob = await api.getBlob({ path: imageUrl, params: ApiController._getSdkProperties() }); AssetController.setTokenImage(symbol, URL.createObjectURL(blob)); }, _filterWalletsByPlatform(wallets) { const filteredWallets = CoreHelperUtil.isMobile() ? wallets?.filter(w => { if (w.mobile_link) { return true; } if (w.id === CUSTOM_DEEPLINK_WALLETS.COINBASE.id) { return true; } const isSolana = ChainController.state.activeChain === 'solana'; return (isSolana && (w.id === CUSTOM_DEEPLINK_WALLETS.SOLFLARE.id || w.id === CUSTOM_DEEPLINK_WALLETS.PHANTOM.id)); }) : wallets; return filteredWallets; }, async fetchProjectConfig() { const response = await api.get({ path: '/appkit/v1/config', params: ApiController._getSdkProperties() }); return response.features; }, async fetchAllowedOrigins() { try { const { allowedOrigins } = await api.get({ path: '/projects/v1/origins', params: ApiController._getSdkProperties() }); return allowedOrigins; } catch (error) { if (error instanceof Error && error.cause instanceof Response) { const status = error.cause.status; if (status === ConstantsUtil.HTTP_STATUS_CODES.TOO_MANY_REQUESTS) { throw new Error('RATE_LIMITED', { cause: error }); } if (status >= ConstantsUtil.HTTP_STATUS_CODES.SERVER_ERROR && status < 600) { throw new Error('SERVER_ERROR', { cause: error }); } return []; } return []; } }, async fetchNetworkImages() { const requestedCaipNetworks = ChainController.getAllRequestedCaipNetworks(); const ids = requestedCaipNetworks ?.map(({ assets }) => assets?.imageId) .filter(Boolean) .filter(imageId => !AssetUtil.getNetworkImageById(imageId)); if (ids) { await Promise.allSettled(ids.map(id => ApiController._fetchNetworkImage(id))); } }, async fetchConnectorImages() { const { connectors } = ConnectorController.state; const ids = connectors.map(({ imageId }) => imageId).filter(Boolean); await Promise.allSettled(ids.map(id => ApiController._fetchConnectorImage(id))); }, async fetchCurrencyImages(currencies = []) { await Promise.allSettled(currencies.map(currency => ApiController._fetchCurrencyImage(currency))); }, async fetchTokenImages(tokens = []) { await Promise.allSettled(tokens.map(token => ApiController._fetchTokenImage(token))); }, async fetchWallets(params) { const exclude = params.exclude ?? []; const sdkProperties = ApiController._getSdkProperties(); if (sdkProperties.sv.startsWith('html-core-')) { exclude.push(...Object.values(CUSTOM_DEEPLINK_WALLETS).map(w => w.id)); } const wallets = await api.get({ path: '/getWallets', params: { ...ApiController._getSdkProperties(), ...params, page: String(params.page), entries: String(params.entries), include: params.include?.join(','), exclude: exclude.join(',') } }); const filteredWallets = ApiController._filterWalletsByPlatform(wallets?.data); return { data: filteredWallets || [], // Keep original count for display on main page count: wallets?.count }; }, async fetchFeaturedWallets() { const { featuredWalletIds } = OptionsController.state; if (featuredWalletIds?.length) { const params = { ...ApiController._getSdkProperties(), page: 1, entries: featuredWalletIds?.length ?? recommendedEntries, include: featuredWalletIds }; const { data } = await ApiController.fetchWallets(params); const sortedData = [...data].sort((a, b) => featuredWalletIds.indexOf(a.id) - featuredWalletIds.indexOf(b.id)); const images = sortedData.map(d => d.image_id).filter(Boolean); await Promise.allSettled(images.map(id => ApiController._fetchWalletImage(id))); state.featured = sortedData; state.allFeatured = sortedData; } }, async fetchRecommendedWallets() { try { state.isFetchingRecommendedWallets = true; const { includeWalletIds, excludeWalletIds, featuredWalletIds } = OptionsController.state; const exclude = [...(excludeWalletIds ?? []), ...(featuredWalletIds ?? [])].filter(Boolean); const chains = ChainController.getRequestedCaipNetworkIds().join(','); const params = { page: 1, entries: recommendedEntries, include: includeWalletIds, exclude, chains }; const { data, count } = await ApiController.fetchWallets(params); const recent = StorageUtil.getRecentWallets(); const recommendedImages = data.map(d => d.image_id).filter(Boolean); const recentImages = recent.map(r => r.image_id).filter(Boolean); await Promise.allSettled([...recommendedImages, ...recentImages].map(id => ApiController._fetchWalletImage(id))); state.recommended = data; state.allRecommended = data; state.count = count ?? 0; } catch { // Catch silently } finally { state.isFetchingRecommendedWallets = false; } }, async fetchWalletsByPage({ page }) { const { includeWalletIds, excludeWalletIds, featuredWalletIds } = OptionsController.state; const chains = ChainController.getRequestedCaipNetworkIds().join(','); const exclude = [ ...state.recommended.map(({ id }) => id), ...(excludeWalletIds ?? []), ...(featuredWalletIds ?? []) ].filter(Boolean); const params = { page, entries, include: includeWalletIds, exclude, chains }; const { data, count } = await ApiController.fetchWallets(params); const images = data .slice(0, imageCountToFetch) .map(w => w.image_id) .filter(Boolean); await Promise.allSettled(images.map(id => ApiController._fetchWalletImage(id))); state.wallets = CoreHelperUtil.uniqueBy([...state.wallets, ...ApiController._filterOutExtensions(data)], 'id').filter(w => w.chains?.some(chain => chains.includes(chain))); state.count = count > state.count ? count : state.count; state.page = page; }, async initializeExcludedWallets({ ids }) { const params = { page: 1, entries: ids.length, include: ids }; const { data } = await ApiController.fetchWallets(params); if (data) { data.forEach(wallet => { state.excludedWallets.push({ rdns: wallet.rdns, name: wallet.name }); }); } }, async searchWallet({ search, badge }) { const { includeWalletIds, excludeWalletIds } = OptionsController.state; const chains = ChainController.getRequestedCaipNetworkIds().join(','); state.search = []; const params = { page: 1, entries: 100, search: search?.trim(), badge_type: badge, include: includeWalletIds, exclude: excludeWalletIds, chains }; const { data } = await ApiController.fetchWallets(params); EventsController.sendEvent({ type: 'track', event: 'SEARCH_WALLET', properties: { badge: badge ?? '', search: search ?? '' } }); const images = data.map(w => w.image_id).filter(Boolean); await Promise.allSettled([ ...images.map(id => ApiController._fetchWalletImage(id)), CoreHelperUtil.wait(300) ]); state.search = ApiController._filterOutExtensions(data); }, initPromise(key, fetchFn) { const existingPromise = state.promises[key]; if (existingPromise) { return existingPromise; } return (state.promises[key] = fetchFn()); }, prefetch({ fetchConnectorImages = true, fetchFeaturedWallets = true, fetchRecommendedWallets = true, fetchNetworkImages = true } = {}) { const promises = [ fetchConnectorImages && ApiController.initPromise('connectorImages', ApiController.fetchConnectorImages), fetchFeaturedWallets && ApiController.initPromise('featuredWallets', ApiController.fetchFeaturedWallets), fetchRecommendedWallets && ApiController.initPromise('recommendedWallets', ApiController.fetchRecommendedWallets), fetchNetworkImages && ApiController.initPromise('networkImages', ApiController.fetchNetworkImages) ].filter(Boolean); return Promise.allSettled(promises); }, prefetchAnalyticsConfig() { if (OptionsController.state.features?.analytics) { ApiController.fetchAnalyticsConfig(); } }, async fetchAnalyticsConfig() { try { const { isAnalyticsEnabled } = await api.get({ path: '/getAnalyticsConfig', params: ApiController._getSdkProperties() }); OptionsController.setFeatures({ analytics: isAnalyticsEnabled }); } catch (error) { OptionsController.setFeatures({ analytics: false }); } }, filterByNamespaces(namespaces) { if (!namespaces?.length) { state.featured = state.allFeatured; state.recommended = state.allRecommended; return; } const caipNetworkIds = ChainController.getRequestedCaipNetworkIds().join(','); state.featured = state.allFeatured.filter(wallet => wallet.chains?.some(chain => caipNetworkIds.includes(chain))); state.recommended = state.allRecommended.filter(wallet => wallet.chains?.some(chain => caipNetworkIds.includes(chain))); state.filteredWallets = state.wallets.filter(wallet => wallet.chains?.some(chain => caipNetworkIds.includes(chain))); }, clearFilterByNamespaces() { state.filteredWallets = []; }, setFilterByNamespace(namespace) { if (!namespace) { state.featured = state.allFeatured; state.recommended = state.allRecommended; return; } const caipNetworkIds = ChainController.getRequestedCaipNetworkIds().join(','); state.featured = state.allFeatured.filter(wallet => wallet.chains?.some(chain => caipNetworkIds.includes(chain))); state.recommended = state.allRecommended.filter(wallet => wallet.chains?.some(chain => caipNetworkIds.includes(chain))); state.filteredWallets = state.wallets.filter(wallet => wallet.chains?.some(chain => caipNetworkIds.includes(chain))); } }; //# sourceMappingURL=ApiController.js.map