Skip to content

Commit f3d85d5

Browse files
committed
Add getSupportedPlugins API
1 parent aaa2d8d commit f3d85d5

File tree

5 files changed

+347
-36
lines changed

5 files changed

+347
-36
lines changed

docs/ramp-plugin-migration-guide.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,60 @@ if (deniedPermission) {
234234

235235
Note the inverted boolean logic: `!success` becomes `deniedPermission`.
236236

237+
### Asset Discovery
238+
239+
**Before:**
240+
```typescript
241+
const assets = await provider.getSupportedAssets({
242+
direction: 'buy',
243+
paymentTypes: ['credit', 'bank'],
244+
regionCode: { countryCode: 'US', stateCode: 'CA' }
245+
})
246+
```
247+
248+
**After:**
249+
```typescript
250+
const assets = await plugin.getSupportedAssets({
251+
direction: 'buy',
252+
paymentTypes: ['credit', 'bank'],
253+
regionCode: { countryCode: 'US', stateCode: 'CA' }
254+
})
255+
```
256+
257+
The API remains the same, maintaining compatibility with existing code. Note that the method returns the asset map for the first supported payment type from the provided array.
258+
259+
## Asset Discovery Integration
260+
261+
The ramp plugin architecture includes a `getSupportedAssets` method that allows the UI to check which plugins support specific crypto/fiat/region combinations:
262+
263+
```typescript
264+
// Usage in TradeCreateScene via custom hook
265+
import { useSupportedPlugins } from '../../hooks/useSupportedPlugins'
266+
267+
const {
268+
data: supportedPlugins = [],
269+
isLoading: isCheckingSupport
270+
} = useSupportedPlugins({
271+
selectedWallet,
272+
selectedCrypto,
273+
selectedCryptoCurrencyCode,
274+
selectedFiatCurrencyCode,
275+
countryCode,
276+
stateProvinceCode
277+
})
278+
279+
// Only query supported plugins for quotes
280+
const quotePromises = supportedPlugins.map(async (plugin) => {
281+
return await plugin.fetchQuote(rampQuoteRequest)
282+
})
283+
```
284+
285+
The `useSupportedPlugins` hook:
286+
- Checks all payment types for comprehensive support
287+
- Caches results for 5 minutes to avoid excessive API calls
288+
- Filters plugins based on crypto/fiat/region support
289+
- Returns loading state for UI feedback
290+
237291
## Benefits
238292

239293
- **Reduced abstraction**: Direct usage of APIs makes code easier to understand

src/components/scenes/TradeCreateScene.tsx

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { FLAG_LOGO_URL } from '../../constants/CdnConstants'
1111
import { COUNTRY_CODES, FIAT_COUNTRY } from '../../constants/CountryConstants'
1212
import { useHandler } from '../../hooks/useHandler'
1313
import { useRampQuotes } from '../../hooks/useRampQuotes'
14+
import { useSupportedPlugins } from '../../hooks/useSupportedPlugins'
1415
import { useWatch } from '../../hooks/useWatch'
1516
import { lstrings } from '../../locales/strings'
1617
import type { RampQuoteRequest } from '../../plugins/ramps/rampPluginTypes'
@@ -70,8 +71,6 @@ export const TradeCreateScene = (props: Props): React.ReactElement => {
7071
const [selectedFiatCurrencyCode, setSelectedFiatCurrencyCode] =
7172
useState<string>(defaultFiat)
7273

73-
const rampPlugins = useSelector(state => state.rampPlugins.plugins)
74-
7574
// Get first wallet as default if no forcedWalletResult
7675
const firstWallet = React.useMemo((): WalletListWalletResult | undefined => {
7776
const walletIds = Object.keys(currencyWallets)
@@ -125,6 +124,17 @@ export const TradeCreateScene = (props: Props): React.ReactElement => {
125124
const shouldShowRegionSelect =
126125
initialRegionCode == null && (countryCode === '' || countryData == null)
127126

127+
// Use the custom hook for supported plugins
128+
const { data: supportedPlugins = {}, isLoading: isCheckingSupport } =
129+
useSupportedPlugins({
130+
selectedWallet,
131+
selectedCrypto,
132+
selectedCryptoCurrencyCode,
133+
selectedFiatCurrencyCode,
134+
countryCode: countryCode || 'US',
135+
stateProvinceCode
136+
})
137+
128138
const getRegionText = (): string => {
129139
if (countryCode === '' || countryData == null) {
130140
return lstrings.buy_sell_crypto_select_country_button
@@ -204,7 +214,7 @@ export const TradeCreateScene = (props: Props): React.ReactElement => {
204214
errors: quoteErrors
205215
} = useRampQuotes({
206216
rampQuoteRequest,
207-
plugins: rampPlugins
217+
plugins: supportedPlugins
208218
})
209219

210220
// Get the best quote
@@ -531,8 +541,30 @@ export const TradeCreateScene = (props: Props): React.ReactElement => {
531541
</ExchangeRateContainer>
532542
)}
533543

534-
{/* Error Alert */}
535-
{!isLoadingQuotes &&
544+
{/* Support Check Loading */}
545+
{isCheckingSupport && selectedWallet != null && (
546+
<LoadingContainer>
547+
<ActivityIndicator size="small" color={theme.primaryText} />
548+
<EdgeText>{lstrings.loading}</EdgeText>
549+
</LoadingContainer>
550+
)}
551+
552+
{/* No Plugin Support Alert */}
553+
{!isCheckingSupport &&
554+
!isLoadingQuotes &&
555+
selectedWallet != null &&
556+
selectedCryptoCurrencyCode != null &&
557+
Object.keys(supportedPlugins).length === 0 ? (
558+
<AlertCardUi4
559+
type="warning"
560+
title={lstrings.trade_buy_unavailable_title}
561+
body={lstrings.buy_sell_crypto_no_provider_region}
562+
/>
563+
) : null}
564+
565+
{/* Error Alert for Failed Quotes */}
566+
{!isCheckingSupport &&
567+
!isLoadingQuotes &&
536568
quoteErrors.length > 0 &&
537569
sortedQuotes.length === 0 ? (
538570
<AlertCardUi4
@@ -556,6 +588,7 @@ export const TradeCreateScene = (props: Props): React.ReactElement => {
556588
selectedCryptoCurrencyCode == null ||
557589
userInput === '' ||
558590
lastUsedInput === null ||
591+
isCheckingSupport ||
559592
isLoadingQuotes ||
560593
sortedQuotes.length === 0
561594
}}

src/hooks/useSupportedPlugins.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { useQuery, type UseQueryResult } from '@tanstack/react-query'
2+
import type { EdgeCurrencyWallet } from 'edge-core-js'
3+
4+
import type { WalletListWalletResult } from '../components/modals/WalletListModal'
5+
import type { FiatPaymentType } from '../plugins/gui/fiatPluginTypes'
6+
import type {
7+
RampPlugin,
8+
RampSupportedAssetsRequest
9+
} from '../plugins/ramps/rampPluginTypes'
10+
import { useSelector } from '../types/reactRedux'
11+
12+
interface UseSupportedPluginsParams {
13+
selectedWallet: EdgeCurrencyWallet | undefined
14+
selectedCrypto: WalletListWalletResult | undefined
15+
selectedCryptoCurrencyCode: string | undefined
16+
selectedFiatCurrencyCode: string
17+
countryCode: string
18+
stateProvinceCode?: string
19+
}
20+
21+
export const useSupportedPlugins = ({
22+
selectedWallet,
23+
selectedCrypto,
24+
selectedCryptoCurrencyCode,
25+
selectedFiatCurrencyCode,
26+
countryCode,
27+
stateProvinceCode
28+
}: UseSupportedPluginsParams): UseQueryResult<Record<string, RampPlugin>> => {
29+
const rampPlugins = useSelector(state => state.rampPlugins.plugins)
30+
31+
// Get all possible payment types
32+
const allPaymentTypes: FiatPaymentType[] = [
33+
'ach',
34+
'applepay',
35+
'colombiabank',
36+
'credit',
37+
'directtobank',
38+
'fasterpayments',
39+
'googlepay',
40+
'iach',
41+
'ideal',
42+
'interac',
43+
'iobank',
44+
'mexicobank',
45+
'payid',
46+
'paypal',
47+
'pix',
48+
'pse',
49+
'revolut',
50+
'sepa',
51+
'spei',
52+
'turkishbank',
53+
'venmo',
54+
'wire'
55+
]
56+
57+
return useQuery({
58+
queryKey: [
59+
'supportedRampPlugins',
60+
selectedWallet?.id,
61+
selectedCrypto?.tokenId,
62+
selectedFiatCurrencyCode,
63+
countryCode,
64+
stateProvinceCode
65+
],
66+
queryFn: async (): Promise<Record<string, RampPlugin>> => {
67+
if (!selectedWallet || !selectedCryptoCurrencyCode || !selectedCrypto) {
68+
return {}
69+
}
70+
71+
const supportRequest: RampSupportedAssetsRequest = {
72+
direction: 'buy',
73+
paymentTypes: allPaymentTypes,
74+
regionCode: {
75+
countryCode: countryCode || 'US',
76+
stateProvinceCode
77+
}
78+
}
79+
80+
// Fetch support from all plugins in parallel
81+
const supportPromises = Object.entries(rampPlugins).map(
82+
async ([pluginId, plugin]) => {
83+
try {
84+
const assetMap = await plugin.getSupportedAssets(supportRequest)
85+
return { pluginId, plugin, assetMap }
86+
} catch (error) {
87+
console.error(
88+
`Failed to get supported assets for ${pluginId}:`,
89+
error
90+
)
91+
return { pluginId, plugin, assetMap: null }
92+
}
93+
}
94+
)
95+
96+
const results = await Promise.all(supportPromises)
97+
98+
// Filter to only supported plugins
99+
return results
100+
.filter(({ assetMap }) => {
101+
if (!assetMap) return false
102+
103+
// Check crypto support
104+
const pluginCurrencyCode = selectedWallet.currencyInfo.pluginId
105+
const cryptoSupported = assetMap.crypto[pluginCurrencyCode]?.some(
106+
token => token.tokenId === (selectedCrypto.tokenId ?? null)
107+
)
108+
109+
// Check fiat support
110+
const fiatSupported =
111+
assetMap.fiat[`iso:${selectedFiatCurrencyCode}`] === true
112+
113+
return cryptoSupported && fiatSupported
114+
})
115+
.reduce<Record<string, RampPlugin>>((plugins, { plugin }) => {
116+
plugins[plugin.pluginId] = plugin
117+
return plugins
118+
}, {})
119+
},
120+
enabled:
121+
!!selectedWallet &&
122+
!!selectedCryptoCurrencyCode &&
123+
Object.keys(rampPlugins).length > 0,
124+
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
125+
gcTime: 10 * 60 * 1000 // Keep in cache for 10 minutes
126+
})
127+
}

0 commit comments

Comments
 (0)