Skip to content

Commit 1620f45

Browse files
committed
Remove getSupportedAssets API from RampPlugin
1 parent 55581a6 commit 1620f45

File tree

7 files changed

+320
-293
lines changed

7 files changed

+320
-293
lines changed

docs/ramp-plugin-architecture.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# Ramp Plugin Architecture
2+
3+
This document describes the simplified ramp plugin architecture for Edge React GUI, focusing on the streamlined approach after removing the `getSupportedAssets` method.
4+
5+
## Overview
6+
7+
The ramp plugin system provides a unified interface for integrating fiat on/off ramp providers (buy/sell cryptocurrency). The architecture has been simplified to reduce complexity and improve performance.
8+
9+
## Architecture Flow
10+
11+
### Previous Architecture (Complex)
12+
1. User selects crypto/fiat pair
13+
2. UI calls `useSupportedPlugins` hook
14+
3. Hook calls `getSupportedAssets` on each plugin
15+
4. UI filters to only supported plugins
16+
5. UI calls `fetchQuote` on supported plugins only
17+
6. Display quotes to user
18+
19+
### Current Architecture (Simplified)
20+
1. User selects crypto/fiat pair
21+
2. UI gets all plugins from Redux
22+
3. UI calls `fetchQuote` on all plugins in parallel
23+
4. Plugins return empty array if unsupported
24+
5. Display quotes to user
25+
26+
## Plugin Interface
27+
28+
```typescript
29+
export interface RampPlugin {
30+
readonly pluginId: string
31+
readonly rampInfo: RampInfo
32+
33+
readonly fetchQuote: (
34+
request: RampQuoteRequest,
35+
opts?: unknown
36+
) => Promise<RampQuoteResult[]>
37+
}
38+
```
39+
40+
### Key Changes
41+
- Removed `getSupportedAssets` method
42+
- All support checking happens inside `fetchQuote`
43+
- Return empty array `[]` for unsupported requests
44+
- Only throw errors for actual failures
45+
46+
## Implementation Guide
47+
48+
### Creating a Ramp Plugin
49+
50+
```typescript
51+
export const myRampPlugin: RampPluginFactory = (config: RampPluginConfig) => {
52+
const { account, navigation, onLogEvent, disklet } = config
53+
54+
const plugin: RampPlugin = {
55+
pluginId: 'myplugin',
56+
rampInfo: {
57+
partnerIcon: 'https://example.com/icon.png',
58+
pluginDisplayName: 'My Plugin'
59+
},
60+
61+
fetchQuote: async (request: RampQuoteRequest): Promise<RampQuoteResult[]> => {
62+
const {
63+
direction,
64+
regionCode,
65+
fiatCurrencyCode,
66+
displayCurrencyCode,
67+
tokenId,
68+
pluginId: currencyPluginId
69+
} = request
70+
71+
// 1. Check region support
72+
if (!isRegionSupported(regionCode)) {
73+
return [] // Not supported
74+
}
75+
76+
// 2. Check asset support
77+
if (!isAssetSupported(currencyPluginId, tokenId)) {
78+
return [] // Not supported
79+
}
80+
81+
// 3. Check fiat support
82+
if (!isFiatSupported(fiatCurrencyCode)) {
83+
return [] // Not supported
84+
}
85+
86+
// 4. Fetch quotes from provider
87+
try {
88+
const quotes = await fetchFromProvider(request)
89+
return quotes.map(quote => convertToRampQuoteResult(quote))
90+
} catch (error) {
91+
// Only throw for actual errors
92+
console.error(`Failed to fetch quotes: ${error}`)
93+
throw error
94+
}
95+
}
96+
}
97+
98+
return plugin
99+
}
100+
```
101+
102+
## UI Integration
103+
104+
### TradeCreateScene
105+
106+
```typescript
107+
export const TradeCreateScene = () => {
108+
// Get all plugins directly from Redux
109+
const rampPlugins = useSelector(state => state.rampPlugins.plugins)
110+
const isPluginsLoading = useSelector(state => state.rampPlugins.isLoading)
111+
112+
// Create quote request
113+
const rampQuoteRequest: RampQuoteRequest = {
114+
// ... request parameters
115+
}
116+
117+
// Fetch quotes from all plugins
118+
const { quotes, isLoading, errors } = useRampQuotes({
119+
rampQuoteRequest,
120+
plugins: rampPlugins
121+
})
122+
123+
// Render UI
124+
return (
125+
// ... UI components
126+
)
127+
}
128+
```
129+
130+
### useRampQuotes Hook
131+
132+
The hook handles:
133+
- Parallel quote fetching from all plugins
134+
- Filtering out empty results (unsupported)
135+
- Error handling and retry logic
136+
- Quote expiration and refresh
137+
- Result caching and deduplication
138+
139+
## Benefits of Simplified Architecture
140+
141+
1. **Performance**: All plugins check support and fetch quotes in a single parallel operation
142+
2. **Simplicity**: One method to implement instead of two
143+
3. **Reduced Latency**: No sequential support check followed by quote fetch
144+
4. **Better Error Handling**: Clear distinction between "not supported" (empty array) and "error" (exception)
145+
5. **Easier Testing**: Only one method to test per plugin
146+
6. **Less Code**: Removed entire `useSupportedPlugins` hook and related logic
147+
148+
## Migration from Legacy Architecture
149+
150+
See [Ramp Plugin Migration Guide](./ramp-plugin-migration-guide.md) for detailed migration instructions.
151+
152+
## Best Practices
153+
154+
1. **Return Empty Array**: Always return `[]` for unsupported requests, never throw
155+
2. **Fast Fail**: Check support conditions early in `fetchQuote` to avoid unnecessary API calls
156+
3. **Parallel Processing**: The UI fetches from all plugins in parallel, so optimize for quick responses
157+
4. **Error Logging**: Log support check failures for debugging but don't throw
158+
5. **Cache Support Data**: Cache any expensive support checks within the plugin if needed
159+
160+
## Example Quote Flow
161+
162+
```mermaid
163+
sequenceDiagram
164+
participant User
165+
participant UI
166+
participant Redux
167+
participant Plugin1
168+
participant Plugin2
169+
170+
User->>UI: Select crypto/fiat pair
171+
UI->>Redux: Get all plugins
172+
Redux->>UI: Return plugins
173+
174+
par Parallel Execution
175+
UI->>Plugin1: fetchQuote(request)
176+
UI->>Plugin2: fetchQuote(request)
177+
end
178+
179+
Plugin1->>UI: Return quotes or []
180+
Plugin2->>UI: Return quotes or []
181+
182+
UI->>User: Display available quotes
183+
```
184+
185+
## Plugin State Management
186+
187+
Plugins are initialized once when the app starts and stored in Redux:
188+
189+
```typescript
190+
interface RampPluginState {
191+
readonly isLoading: boolean
192+
readonly plugins: Record<string, RampPlugin>
193+
}
194+
```
195+
196+
The `RampPluginManager` component handles:
197+
- Loading plugin factories
198+
- Initializing plugins with configuration
199+
- Updating Redux state when ready
200+
201+
## Future Considerations
202+
203+
1. **Plugin Discovery**: Dynamic plugin loading based on user region
204+
2. **Quote Caching**: Shared quote cache across plugins
205+
3. **WebSocket Support**: Real-time quote updates
206+
4. **Plugin Versioning**: Support for multiple plugin versions
207+
5. **Analytics**: Unified analytics across all plugins

docs/ramp-plugin-migration-guide.md

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -376,37 +376,92 @@ const assets = await plugin.getSupportedAssets({
376376

377377
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.
378378

379-
## Asset Discovery Integration
379+
## Removing getSupportedAssets
380380

381-
The ramp plugin architecture includes a `getSupportedAssets` method that allows the UI to check which plugins support specific crypto/fiat/region combinations:
381+
The ramp plugin architecture has been simplified by removing the separate `getSupportedAssets` method. All support checking is now done within the `fetchQuote` method.
382382

383+
### Migration Steps
384+
385+
1. **Move all asset support logic from `getSupportedAssets` into `fetchQuote`**
386+
2. **Return an empty array `[]` from `fetchQuote` if the requested assets are not supported**
387+
3. **Only throw errors for actual failures (network issues, API errors, etc.)**
388+
4. **Remove the `getSupportedAssets` method entirely**
389+
390+
### Example
391+
392+
**Before:**
393+
```typescript
394+
getSupportedAssets: async (request) => {
395+
const { direction, paymentTypes, regionCode } = request
396+
397+
// Validate region
398+
validateRegion(pluginId, regionCode, SUPPORTED_REGIONS)
399+
400+
// Check country restrictions
401+
if (regionCode.countryCode === 'GB') {
402+
throw new FiatProviderError({ errorType: 'assetUnsupported' })
403+
}
404+
405+
// Return supported assets
406+
return assetMap
407+
},
408+
409+
fetchQuote: async (request) => {
410+
// Fetch quotes...
411+
}
412+
```
413+
414+
**After:**
415+
```typescript
416+
fetchQuote: async (request) => {
417+
const { regionCode, direction } = request
418+
419+
// Validate region
420+
try {
421+
validateRegion(pluginId, regionCode, SUPPORTED_REGIONS)
422+
} catch (error) {
423+
return [] // Return empty array for unsupported regions
424+
}
425+
426+
// Check country restrictions
427+
if (regionCode.countryCode === 'GB') {
428+
return [] // Return empty array for unsupported countries
429+
}
430+
431+
// Check if assets are supported
432+
if (!isAssetSupported(request)) {
433+
return [] // Return empty array for unsupported assets
434+
}
435+
436+
// Fetch quotes...
437+
}
438+
```
439+
440+
### UI Integration
441+
442+
The UI no longer needs a separate hook to check plugin support. Instead, it passes all plugins to `useRampQuotes`:
443+
444+
**Before:**
383445
```typescript
384-
// Usage in TradeCreateScene via custom hook
385446
import { useSupportedPlugins } from '../../hooks/useSupportedPlugins'
386447

387-
const {
388-
data: supportedPlugins = [],
389-
isLoading: isCheckingSupport
390-
} = useSupportedPlugins({
391-
selectedWallet,
392-
selectedCrypto,
393-
selectedCryptoCurrencyCode,
394-
selectedFiatCurrencyCode,
395-
countryCode,
396-
stateProvinceCode
397-
})
448+
const { data: supportedPlugins } = useSupportedPlugins({ ... })
449+
const quotes = useRampQuotes({ plugins: supportedPlugins })
450+
```
398451

399-
// Only query supported plugins for quotes
400-
const quotePromises = supportedPlugins.map(async (plugin) => {
401-
return await plugin.fetchQuote(rampQuoteRequest)
402-
})
452+
**After:**
453+
```typescript
454+
const rampPlugins = useSelector(state => state.rampPlugins.plugins)
455+
const quotes = useRampQuotes({ plugins: rampPlugins })
403456
```
404457

405-
The `useSupportedPlugins` hook:
406-
- Checks all payment types for comprehensive support
407-
- Caches results for 5 minutes to avoid excessive API calls
408-
- Filters plugins based on crypto/fiat/region support
409-
- Returns loading state for UI feedback
458+
### Benefits
459+
460+
1. **Simplified Architecture**: Removes one entire method and hook from the system
461+
2. **Reduced Network Calls**: No separate support check before fetching quotes
462+
3. **Better Performance**: All plugins check support in parallel during quote fetching
463+
4. **Cleaner Code**: Less abstraction and indirection
464+
5. **Easier Plugin Development**: Plugin authors only need to implement one method
410465

411466
## Benefits
412467

0 commit comments

Comments
 (0)