@reown/appkit-core-react-native 1.2.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/lib/commonjs/controllers/BlockchainApiController.js +65 -1
  2. package/lib/commonjs/controllers/BlockchainApiController.js.map +1 -1
  3. package/lib/commonjs/controllers/EnsController.js +4 -4
  4. package/lib/commonjs/controllers/EnsController.js.map +1 -1
  5. package/lib/commonjs/controllers/EventsController.js +4 -4
  6. package/lib/commonjs/controllers/EventsController.js.map +1 -1
  7. package/lib/commonjs/controllers/ModalController.js.map +1 -1
  8. package/lib/commonjs/controllers/OnRampController.js +475 -0
  9. package/lib/commonjs/controllers/OnRampController.js.map +1 -0
  10. package/lib/commonjs/controllers/OptionsController.js +3 -0
  11. package/lib/commonjs/controllers/OptionsController.js.map +1 -1
  12. package/lib/commonjs/controllers/RouterController.js +3 -2
  13. package/lib/commonjs/controllers/RouterController.js.map +1 -1
  14. package/lib/commonjs/controllers/SendController.js +5 -5
  15. package/lib/commonjs/controllers/SendController.js.map +1 -1
  16. package/lib/commonjs/controllers/TransactionsController.js +3 -3
  17. package/lib/commonjs/controllers/TransactionsController.js.map +1 -1
  18. package/lib/commonjs/index.js +7 -0
  19. package/lib/commonjs/index.js.map +1 -1
  20. package/lib/commonjs/utils/ConstantsUtil.js +69 -5
  21. package/lib/commonjs/utils/ConstantsUtil.js.map +1 -1
  22. package/lib/commonjs/utils/CoreHelperUtil.js +28 -0
  23. package/lib/commonjs/utils/CoreHelperUtil.js.map +1 -1
  24. package/lib/commonjs/utils/FetchUtil.js +48 -13
  25. package/lib/commonjs/utils/FetchUtil.js.map +1 -1
  26. package/lib/commonjs/utils/StorageUtil.js +185 -1
  27. package/lib/commonjs/utils/StorageUtil.js.map +1 -1
  28. package/lib/commonjs/utils/TypeUtil.js +29 -0
  29. package/lib/commonjs/utils/TypeUtil.js.map +1 -1
  30. package/lib/module/controllers/BlockchainApiController.js +64 -0
  31. package/lib/module/controllers/BlockchainApiController.js.map +1 -1
  32. package/lib/module/controllers/EnsController.js +2 -2
  33. package/lib/module/controllers/EnsController.js.map +1 -1
  34. package/lib/module/controllers/EventsController.js +1 -1
  35. package/lib/module/controllers/EventsController.js.map +1 -1
  36. package/lib/module/controllers/ModalController.js.map +1 -1
  37. package/lib/module/controllers/OnRampController.js +470 -0
  38. package/lib/module/controllers/OnRampController.js.map +1 -0
  39. package/lib/module/controllers/OptionsController.js +3 -0
  40. package/lib/module/controllers/OptionsController.js.map +1 -1
  41. package/lib/module/controllers/RouterController.js +3 -2
  42. package/lib/module/controllers/RouterController.js.map +1 -1
  43. package/lib/module/controllers/SendController.js +2 -2
  44. package/lib/module/controllers/SendController.js.map +1 -1
  45. package/lib/module/controllers/TransactionsController.js +1 -1
  46. package/lib/module/controllers/TransactionsController.js.map +1 -1
  47. package/lib/module/index.js +1 -0
  48. package/lib/module/index.js.map +1 -1
  49. package/lib/module/utils/ConstantsUtil.js +68 -4
  50. package/lib/module/utils/ConstantsUtil.js.map +1 -1
  51. package/lib/module/utils/CoreHelperUtil.js +26 -0
  52. package/lib/module/utils/CoreHelperUtil.js.map +1 -1
  53. package/lib/module/utils/FetchUtil.js +48 -13
  54. package/lib/module/utils/FetchUtil.js.map +1 -1
  55. package/lib/module/utils/StorageUtil.js +186 -1
  56. package/lib/module/utils/StorageUtil.js.map +1 -1
  57. package/lib/module/utils/TypeUtil.js +27 -1
  58. package/lib/module/utils/TypeUtil.js.map +1 -1
  59. package/lib/typescript/controllers/BlockchainApiController.d.ts +16 -1
  60. package/lib/typescript/controllers/BlockchainApiController.d.ts.map +1 -1
  61. package/lib/typescript/controllers/ModalController.d.ts +1 -1
  62. package/lib/typescript/controllers/ModalController.d.ts.map +1 -1
  63. package/lib/typescript/controllers/OnRampController.d.ts +55 -0
  64. package/lib/typescript/controllers/OnRampController.d.ts.map +1 -0
  65. package/lib/typescript/controllers/OptionsController.d.ts +2 -0
  66. package/lib/typescript/controllers/OptionsController.d.ts.map +1 -1
  67. package/lib/typescript/controllers/RouterController.d.ts +4 -3
  68. package/lib/typescript/controllers/RouterController.d.ts.map +1 -1
  69. package/lib/typescript/index.d.ts +1 -0
  70. package/lib/typescript/index.d.ts.map +1 -1
  71. package/lib/typescript/utils/ConstantsUtil.d.ts +55 -0
  72. package/lib/typescript/utils/ConstantsUtil.d.ts.map +1 -1
  73. package/lib/typescript/utils/CoreHelperUtil.d.ts +3 -0
  74. package/lib/typescript/utils/CoreHelperUtil.d.ts.map +1 -1
  75. package/lib/typescript/utils/FetchUtil.d.ts +4 -4
  76. package/lib/typescript/utils/FetchUtil.d.ts.map +1 -1
  77. package/lib/typescript/utils/StorageUtil.d.ts +16 -2
  78. package/lib/typescript/utils/StorageUtil.d.ts.map +1 -1
  79. package/lib/typescript/utils/TypeUtil.d.ts +172 -0
  80. package/lib/typescript/utils/TypeUtil.d.ts.map +1 -1
  81. package/package.json +5 -4
  82. package/src/controllers/BlockchainApiController.ts +87 -1
  83. package/src/controllers/EnsController.ts +2 -2
  84. package/src/controllers/EventsController.ts +1 -1
  85. package/src/controllers/ModalController.ts +1 -2
  86. package/src/controllers/OnRampController.ts +663 -0
  87. package/src/controllers/OptionsController.ts +5 -0
  88. package/src/controllers/RouterController.ts +16 -3
  89. package/src/controllers/SendController.ts +2 -2
  90. package/src/controllers/TransactionsController.ts +1 -1
  91. package/src/index.ts +1 -0
  92. package/src/utils/ConstantsUtil.ts +49 -4
  93. package/src/utils/CoreHelperUtil.ts +30 -0
  94. package/src/utils/FetchUtil.ts +51 -17
  95. package/src/utils/StorageUtil.ts +225 -3
  96. package/src/utils/TypeUtil.ts +201 -1
@@ -0,0 +1,663 @@
1
+ import { subscribeKey as subKey } from 'valtio/vanilla/utils';
2
+ import { proxy, subscribe as sub } from 'valtio/vanilla';
3
+ import {
4
+ type OnRampPaymentMethod,
5
+ type OnRampCountry,
6
+ type OnRampFiatCurrency,
7
+ type OnRampQuote,
8
+ type OnRampFiatLimit,
9
+ type OnRampCryptoCurrency,
10
+ type OnRampServiceProvider,
11
+ type OnRampError,
12
+ type OnRampErrorTypeValues,
13
+ type OnRampCountryDefaults,
14
+ BlockchainOnRampError
15
+ } from '../utils/TypeUtil';
16
+
17
+ import { CoreHelperUtil } from '../utils/CoreHelperUtil';
18
+ import { NetworkController } from './NetworkController';
19
+ import { AccountController } from './AccountController';
20
+ import { OptionsController } from './OptionsController';
21
+ import { ConstantsUtil, OnRampErrorType } from '../utils/ConstantsUtil';
22
+ import { StorageUtil } from '../utils/StorageUtil';
23
+ import { SnackController } from './SnackController';
24
+ import { EventsController } from './EventsController';
25
+ import { BlockchainApiController, EXCLUDED_ONRAMP_PROVIDERS } from './BlockchainApiController';
26
+
27
+ // -- Helpers ------------------------------------------- //
28
+
29
+ let quotesAbortController: AbortController | null = null;
30
+
31
+ // -- Utils --------------------------------------------- //
32
+
33
+ const mapErrorMessage = (errorCode: string): OnRampError => {
34
+ const errorMap: Record<string, { type: OnRampErrorTypeValues; message: string }> = {
35
+ [OnRampErrorType.AMOUNT_TOO_LOW]: {
36
+ type: OnRampErrorType.AMOUNT_TOO_LOW,
37
+ message: 'The amount is too low'
38
+ },
39
+ [OnRampErrorType.AMOUNT_TOO_HIGH]: {
40
+ type: OnRampErrorType.AMOUNT_TOO_HIGH,
41
+ message: 'The amount is too high'
42
+ },
43
+ [OnRampErrorType.INVALID_AMOUNT]: {
44
+ type: OnRampErrorType.INVALID_AMOUNT,
45
+ message: 'Enter a valid amount'
46
+ },
47
+ [OnRampErrorType.INCOMPATIBLE_REQUEST]: {
48
+ type: OnRampErrorType.INCOMPATIBLE_REQUEST,
49
+ message: 'Enter a valid amount'
50
+ },
51
+ [OnRampErrorType.BAD_REQUEST]: {
52
+ type: OnRampErrorType.BAD_REQUEST,
53
+ message: 'Enter a valid amount'
54
+ },
55
+ [OnRampErrorType.NO_VALID_QUOTES]: {
56
+ type: OnRampErrorType.NO_VALID_QUOTES,
57
+ message: 'No quotes available'
58
+ }
59
+ };
60
+
61
+ return (
62
+ errorMap[errorCode] || {
63
+ type: OnRampErrorType.UNKNOWN,
64
+ message: 'Something went wrong. Please try again'
65
+ }
66
+ );
67
+ };
68
+
69
+ // -- Types --------------------------------------------- //
70
+ export interface OnRampControllerState {
71
+ countries: OnRampCountry[];
72
+ countriesDefaults?: OnRampCountryDefaults[];
73
+ selectedCountry?: OnRampCountry;
74
+ serviceProviders: OnRampServiceProvider[];
75
+ selectedServiceProvider?: OnRampServiceProvider;
76
+ paymentMethods: OnRampPaymentMethod[];
77
+ selectedPaymentMethod?: OnRampPaymentMethod;
78
+ purchaseCurrency?: OnRampCryptoCurrency;
79
+ purchaseCurrencies?: OnRampCryptoCurrency[];
80
+ paymentAmount?: number;
81
+ paymentCurrency?: OnRampFiatCurrency;
82
+ paymentCurrencies?: OnRampFiatCurrency[];
83
+ paymentCurrenciesLimits?: OnRampFiatLimit[];
84
+ quotes?: OnRampQuote[];
85
+ selectedQuote?: OnRampQuote;
86
+ widgetUrl?: string;
87
+ error?: OnRampError;
88
+ initialLoading?: boolean;
89
+ loading?: boolean;
90
+ quotesLoading: boolean;
91
+ }
92
+
93
+ type StateKey = keyof OnRampControllerState;
94
+
95
+ const defaultState = {
96
+ quotesLoading: false,
97
+ countries: [],
98
+ paymentMethods: [],
99
+ serviceProviders: [],
100
+ paymentAmount: undefined
101
+ };
102
+
103
+ // -- State --------------------------------------------- //
104
+ const state = proxy<OnRampControllerState>(defaultState);
105
+
106
+ // -- Controller ---------------------------------------- //
107
+ export const OnRampController = {
108
+ state,
109
+
110
+ subscribe(callback: (newState: OnRampControllerState) => void) {
111
+ return sub(state, () => callback(state));
112
+ },
113
+
114
+ subscribeKey<K extends StateKey>(key: K, callback: (value: OnRampControllerState[K]) => void) {
115
+ return subKey(state, key, callback);
116
+ },
117
+
118
+ async setSelectedCountry(country: OnRampCountry, updateCurrency = true) {
119
+ try {
120
+ state.selectedCountry = country;
121
+ state.loading = true;
122
+
123
+ if (updateCurrency) {
124
+ const currencyCode =
125
+ state.countriesDefaults?.find(d => d.countryCode === country.countryCode)
126
+ ?.defaultCurrencyCode || 'USD';
127
+
128
+ const currency = state.paymentCurrencies?.find(c => c.currencyCode === currencyCode);
129
+
130
+ if (currency) {
131
+ this.setPaymentCurrency(currency);
132
+ }
133
+ }
134
+
135
+ await Promise.all([this.fetchPaymentMethods(), this.fetchCryptoCurrencies()]);
136
+ this.clearQuotes();
137
+
138
+ state.loading = false;
139
+
140
+ StorageUtil.setOnRampPreferredCountry(country);
141
+ } catch (error) {
142
+ state.loading = false;
143
+ state.error = {
144
+ type: OnRampErrorType.FAILED_TO_LOAD_COUNTRIES,
145
+ message: 'Failed to load countries'
146
+ };
147
+ }
148
+ },
149
+
150
+ setSelectedPaymentMethod(paymentMethod: OnRampPaymentMethod) {
151
+ state.selectedPaymentMethod = paymentMethod;
152
+ },
153
+
154
+ setPurchaseCurrency(currency: OnRampCryptoCurrency) {
155
+ state.purchaseCurrency = currency;
156
+
157
+ EventsController.sendEvent({
158
+ type: 'track',
159
+ event: 'SELECT_BUY_ASSET',
160
+ properties: {
161
+ asset: currency.currencyCode
162
+ }
163
+ });
164
+
165
+ this.clearQuotes();
166
+ },
167
+
168
+ setPaymentCurrency(currency: OnRampFiatCurrency, updateAmount = true) {
169
+ state.paymentCurrency = currency;
170
+
171
+ StorageUtil.setOnRampPreferredFiatCurrency(currency);
172
+
173
+ if (updateAmount) {
174
+ state.paymentAmount = undefined;
175
+ }
176
+
177
+ this.clearQuotes();
178
+ this.clearError();
179
+ },
180
+
181
+ setPaymentAmount(amount?: number | string) {
182
+ state.paymentAmount = amount ? Number(amount) : undefined;
183
+ },
184
+
185
+ setSelectedQuote(quote?: OnRampQuote) {
186
+ state.selectedQuote = quote;
187
+ },
188
+
189
+ updateSelectedPurchaseCurrency() {
190
+ let selectedCurrency;
191
+ if (NetworkController.state.caipNetwork?.id) {
192
+ const defaultCurrency =
193
+ ConstantsUtil.NETWORK_DEFAULT_CURRENCIES[
194
+ NetworkController.state.caipNetwork
195
+ ?.id as keyof typeof ConstantsUtil.NETWORK_DEFAULT_CURRENCIES
196
+ ];
197
+ selectedCurrency = state.purchaseCurrencies?.find(c => c.currencyCode === defaultCurrency);
198
+ }
199
+
200
+ state.purchaseCurrency = selectedCurrency ?? state.purchaseCurrencies?.[0] ?? undefined;
201
+ },
202
+
203
+ getServiceProviderImage(serviceProviderName?: string) {
204
+ if (!serviceProviderName) return undefined;
205
+
206
+ const provider = state.serviceProviders.find(p => p.serviceProvider === serviceProviderName);
207
+
208
+ return provider?.logos?.lightShort;
209
+ },
210
+
211
+ getCurrencyLimit(currency: OnRampFiatCurrency) {
212
+ return state.paymentCurrenciesLimits?.find(l => l.currencyCode === currency.currencyCode);
213
+ },
214
+
215
+ async fetchCountries() {
216
+ try {
217
+ let countries = await StorageUtil.getOnRampCountries();
218
+
219
+ if (!countries.length) {
220
+ countries = (await BlockchainApiController.fetchOnRampCountries()) ?? [];
221
+
222
+ if (countries.length) {
223
+ StorageUtil.setOnRampCountries(countries);
224
+ }
225
+ }
226
+
227
+ state.countries = countries;
228
+
229
+ const preferredCountry = await StorageUtil.getOnRampPreferredCountry();
230
+
231
+ if (preferredCountry) {
232
+ state.selectedCountry = preferredCountry;
233
+ } else {
234
+ const countryCode = CoreHelperUtil.getCountryFromTimezone();
235
+
236
+ state.selectedCountry =
237
+ countries.find(c => c.countryCode === countryCode) || countries[0] || undefined;
238
+ }
239
+ } catch (error) {
240
+ state.error = {
241
+ type: OnRampErrorType.FAILED_TO_LOAD_COUNTRIES,
242
+ message: 'Failed to load countries'
243
+ };
244
+ }
245
+ },
246
+
247
+ async fetchCountriesDefaults() {
248
+ try {
249
+ let countriesDefaults = await StorageUtil.getOnRampCountriesDefaults();
250
+
251
+ if (!countriesDefaults.length) {
252
+ countriesDefaults = (await BlockchainApiController.fetchOnRampCountriesDefaults()) ?? [];
253
+
254
+ if (countriesDefaults.length) {
255
+ StorageUtil.setOnRampCountriesDefaults(countriesDefaults);
256
+ }
257
+ }
258
+
259
+ state.countriesDefaults = countriesDefaults;
260
+ } catch (error) {
261
+ state.error = {
262
+ type: OnRampErrorType.FAILED_TO_LOAD_COUNTRIES,
263
+ message: 'Failed to load countries defaults'
264
+ };
265
+ }
266
+ },
267
+
268
+ async fetchServiceProviders() {
269
+ try {
270
+ let serviceProviders = await StorageUtil.getOnRampServiceProviders();
271
+
272
+ if (!serviceProviders.length) {
273
+ serviceProviders = (await BlockchainApiController.fetchOnRampServiceProviders()) ?? [];
274
+
275
+ if (serviceProviders.length) {
276
+ StorageUtil.setOnRampServiceProviders(serviceProviders);
277
+ }
278
+ }
279
+
280
+ state.serviceProviders = serviceProviders || [];
281
+ } catch (error) {
282
+ state.error = {
283
+ type: OnRampErrorType.FAILED_TO_LOAD_PROVIDERS,
284
+ message: 'Failed to load service providers'
285
+ };
286
+ }
287
+ },
288
+
289
+ async fetchPaymentMethods() {
290
+ try {
291
+ const paymentMethods = await BlockchainApiController.fetchOnRampPaymentMethods({
292
+ countries: state.selectedCountry?.countryCode
293
+ });
294
+
295
+ const defaultCountryPaymentMethods =
296
+ state.countriesDefaults?.find(d => d.countryCode === state.selectedCountry?.countryCode)
297
+ ?.defaultPaymentMethods || [];
298
+
299
+ state.paymentMethods =
300
+ paymentMethods?.sort((a, b) => {
301
+ const aIndex = defaultCountryPaymentMethods?.indexOf(a.paymentMethod);
302
+ const bIndex = defaultCountryPaymentMethods?.indexOf(b.paymentMethod);
303
+
304
+ if (aIndex === -1 && bIndex === -1) return 0;
305
+ if (aIndex === -1) return 1;
306
+ if (bIndex === -1) return -1;
307
+
308
+ return aIndex - bIndex;
309
+ }) || [];
310
+
311
+ state.selectedPaymentMethod = state.paymentMethods[0];
312
+ } catch (error) {
313
+ state.error = {
314
+ type: OnRampErrorType.FAILED_TO_LOAD_METHODS,
315
+ message: 'Failed to load payment methods'
316
+ };
317
+ state.paymentMethods = [];
318
+ state.selectedPaymentMethod = undefined;
319
+ }
320
+ },
321
+
322
+ async fetchCryptoCurrencies() {
323
+ try {
324
+ const cryptoCurrencies = await BlockchainApiController.fetchOnRampCryptoCurrencies({
325
+ countries: state.selectedCountry?.countryCode
326
+ });
327
+
328
+ state.purchaseCurrencies = cryptoCurrencies || [];
329
+
330
+ let selectedCurrency;
331
+ if (NetworkController.state.caipNetwork?.id) {
332
+ const defaultCurrency =
333
+ ConstantsUtil.NETWORK_DEFAULT_CURRENCIES[
334
+ NetworkController.state.caipNetwork
335
+ ?.id as keyof typeof ConstantsUtil.NETWORK_DEFAULT_CURRENCIES
336
+ ] || 'ETH';
337
+ selectedCurrency = state.purchaseCurrencies?.find(c => c.currencyCode === defaultCurrency);
338
+ }
339
+
340
+ state.purchaseCurrency = selectedCurrency || cryptoCurrencies?.[0] || undefined;
341
+ } catch (error) {
342
+ state.error = {
343
+ type: OnRampErrorType.FAILED_TO_LOAD_CURRENCIES,
344
+ message: 'Failed to load crypto currencies'
345
+ };
346
+ state.purchaseCurrencies = [];
347
+ state.purchaseCurrency = undefined;
348
+ }
349
+ },
350
+
351
+ async fetchFiatCurrencies() {
352
+ try {
353
+ let fiatCurrencies = await StorageUtil.getOnRampFiatCurrencies();
354
+ let currencyCode = 'USD';
355
+ const countryCode = state.selectedCountry?.countryCode;
356
+
357
+ if (!fiatCurrencies.length) {
358
+ fiatCurrencies = (await BlockchainApiController.fetchOnRampFiatCurrencies()) ?? [];
359
+
360
+ if (fiatCurrencies.length) {
361
+ StorageUtil.setOnRampFiatCurrencies(fiatCurrencies);
362
+ }
363
+ }
364
+
365
+ state.paymentCurrencies = fiatCurrencies || [];
366
+
367
+ if (countryCode) {
368
+ currencyCode =
369
+ state.countriesDefaults?.find(d => d.countryCode === countryCode)?.defaultCurrencyCode ||
370
+ 'USD';
371
+ }
372
+
373
+ const preferredCurrency = await StorageUtil.getOnRampPreferredFiatCurrency();
374
+
375
+ const defaultCurrency =
376
+ preferredCurrency ||
377
+ fiatCurrencies?.find(c => c.currencyCode === currencyCode) ||
378
+ fiatCurrencies?.[0] ||
379
+ undefined;
380
+
381
+ if (defaultCurrency) {
382
+ this.setPaymentCurrency(defaultCurrency);
383
+ }
384
+ } catch (error) {
385
+ state.error = {
386
+ type: OnRampErrorType.FAILED_TO_LOAD_CURRENCIES,
387
+ message: 'Failed to load fiat currencies'
388
+ };
389
+ state.paymentCurrencies = [];
390
+ state.paymentCurrency = undefined;
391
+ }
392
+ },
393
+
394
+ abortGetQuotes(clearState = true) {
395
+ if (quotesAbortController) {
396
+ quotesAbortController.abort();
397
+ quotesAbortController = null;
398
+ }
399
+
400
+ if (clearState) {
401
+ this.clearQuotes();
402
+ state.quotesLoading = false;
403
+ state.error = undefined;
404
+ }
405
+ },
406
+
407
+ async getQuotes() {
408
+ if (!this.canGenerateQuote()) {
409
+ this.clearQuotes();
410
+
411
+ return;
412
+ }
413
+
414
+ this.abortGetQuotes(false);
415
+ quotesAbortController = new AbortController();
416
+ const currentSignal = quotesAbortController.signal;
417
+
418
+ try {
419
+ if (
420
+ !state.selectedCountry?.countryCode ||
421
+ !state.purchaseCurrency?.currencyCode ||
422
+ !state.paymentCurrency?.currencyCode ||
423
+ !AccountController.state.address
424
+ ) {
425
+ throw new BlockchainOnRampError(OnRampErrorType.UNKNOWN, 'Invalid quote parameters');
426
+ }
427
+
428
+ state.quotesLoading = true;
429
+ state.selectedQuote = undefined;
430
+ state.selectedServiceProvider = undefined;
431
+ state.error = undefined;
432
+
433
+ const body = {
434
+ countryCode: state.selectedCountry.countryCode,
435
+ destinationCurrencyCode: state.purchaseCurrency.currencyCode,
436
+ sourceAmount: state.paymentAmount!,
437
+ sourceCurrencyCode: state.paymentCurrency.currencyCode,
438
+ walletAddress: AccountController.state.address,
439
+ excludeProviders: EXCLUDED_ONRAMP_PROVIDERS
440
+ };
441
+
442
+ const response = await BlockchainApiController.getOnRampQuotes(body, currentSignal);
443
+
444
+ if (!response || !response.length) {
445
+ throw new BlockchainOnRampError(OnRampErrorType.NO_VALID_QUOTES, 'No valid quotes');
446
+ }
447
+
448
+ const quotes = response.sort((a, b) => b.customerScore - a.customerScore);
449
+
450
+ state.quotes = quotes;
451
+
452
+ //Replace payment method if it's not in the quotes
453
+ const isValidPaymentMethod =
454
+ state.selectedPaymentMethod &&
455
+ quotes.some(
456
+ quote => quote.paymentMethodType === state.selectedPaymentMethod?.paymentMethod
457
+ );
458
+
459
+ if (!isValidPaymentMethod) {
460
+ const countryMethods =
461
+ state.countriesDefaults?.find(d => d.countryCode === state.selectedCountry?.countryCode)
462
+ ?.defaultPaymentMethods || [];
463
+
464
+ const availableQuoteMethods = new Set(quotes.map(q => q.paymentMethodType));
465
+
466
+ let newPaymentMethodType: string | undefined;
467
+ for (const dpm of countryMethods) {
468
+ if (availableQuoteMethods.has(dpm)) {
469
+ newPaymentMethodType = dpm;
470
+ break;
471
+ }
472
+ }
473
+
474
+ if (newPaymentMethodType) {
475
+ state.selectedPaymentMethod =
476
+ state.paymentMethods.find(m => m.paymentMethod === newPaymentMethodType) ||
477
+ state.paymentMethods.find(
478
+ method => method.paymentMethod === quotes[0]?.paymentMethodType
479
+ );
480
+ } else {
481
+ state.selectedPaymentMethod = state.paymentMethods.find(
482
+ method => method.paymentMethod === quotes[0]?.paymentMethodType
483
+ );
484
+ }
485
+ }
486
+
487
+ state.selectedQuote = quotes.find(
488
+ quote => quote.paymentMethodType === state.selectedPaymentMethod?.paymentMethod
489
+ );
490
+
491
+ state.selectedServiceProvider = state.serviceProviders.find(
492
+ sp => sp.serviceProvider === state.selectedQuote?.serviceProvider
493
+ );
494
+ } catch (error: any) {
495
+ if (error.name === 'AbortError') {
496
+ // Do nothing, another request was made
497
+ return;
498
+ }
499
+
500
+ EventsController.sendEvent({
501
+ type: 'track',
502
+ event: 'BUY_FAIL',
503
+ properties: {
504
+ message: error?.message ?? error?.code ?? 'Error getting quotes'
505
+ }
506
+ });
507
+
508
+ this.clearQuotes();
509
+ state.error = mapErrorMessage(error?.code || 'UNKNOWN_ERROR');
510
+ } finally {
511
+ if (!currentSignal.aborted) {
512
+ state.quotesLoading = false;
513
+ }
514
+ }
515
+ },
516
+
517
+ canGenerateQuote(): boolean {
518
+ return !!(
519
+ state.selectedCountry?.countryCode &&
520
+ state.selectedPaymentMethod?.paymentMethod &&
521
+ state.purchaseCurrency?.currencyCode &&
522
+ state.paymentAmount &&
523
+ state.paymentAmount > 0 &&
524
+ state.paymentCurrency?.currencyCode &&
525
+ state.selectedCountry &&
526
+ !state.loading &&
527
+ AccountController.state.address
528
+ );
529
+ },
530
+
531
+ async fetchFiatLimits() {
532
+ try {
533
+ let limits = await StorageUtil.getOnRampFiatLimits();
534
+
535
+ if (!limits.length) {
536
+ limits = (await BlockchainApiController.fetchOnRampFiatLimits()) ?? [];
537
+
538
+ if (limits.length) {
539
+ StorageUtil.setOnRampFiatLimits(limits);
540
+ }
541
+ }
542
+
543
+ state.paymentCurrenciesLimits = limits;
544
+ } catch (error) {
545
+ state.error = {
546
+ type: OnRampErrorType.FAILED_TO_LOAD_LIMITS,
547
+ message: 'Failed to load fiat limits'
548
+ };
549
+ state.paymentCurrenciesLimits = [];
550
+ }
551
+ },
552
+
553
+ async generateWidget({ quote }: { quote: OnRampQuote }) {
554
+ const metadata = OptionsController.state.metadata;
555
+ const eventProperties = {
556
+ asset: quote.destinationCurrencyCode,
557
+ network: state.purchaseCurrency?.chainName ?? '',
558
+ amount: quote.destinationAmount.toString(),
559
+ currency: quote.destinationCurrencyCode,
560
+ paymentMethod: quote.paymentMethodType,
561
+ provider: 'MELD',
562
+ serviceProvider: quote.serviceProvider
563
+ };
564
+
565
+ try {
566
+ if (!quote) {
567
+ throw new Error('Invalid quote');
568
+ }
569
+
570
+ const body = {
571
+ countryCode: quote.countryCode,
572
+ destinationCurrencyCode: quote.destinationCurrencyCode,
573
+ paymentMethodType: quote.paymentMethodType,
574
+ serviceProvider: quote.serviceProvider,
575
+ sourceAmount: quote.sourceAmount,
576
+ sourceCurrencyCode: quote.sourceCurrencyCode,
577
+ walletAddress: AccountController.state.address!,
578
+ redirectUrl: metadata?.redirect?.universal ?? metadata?.redirect?.native
579
+ };
580
+
581
+ const widget = await BlockchainApiController.getOnRampWidget(body);
582
+
583
+ if (!widget || !widget.widgetUrl) {
584
+ throw new Error('Invalid widget response');
585
+ }
586
+
587
+ EventsController.sendEvent({
588
+ type: 'track',
589
+ event: 'BUY_SUBMITTED',
590
+ properties: eventProperties
591
+ });
592
+
593
+ state.widgetUrl = widget.widgetUrl;
594
+
595
+ return widget;
596
+ } catch (e: any) {
597
+ EventsController.sendEvent({
598
+ type: 'track',
599
+ event: 'BUY_FAIL',
600
+ properties: {
601
+ ...eventProperties,
602
+ message: e?.message ?? e?.code ?? 'Error generating widget url'
603
+ }
604
+ });
605
+
606
+ state.error = mapErrorMessage(e?.code || 'UNKNOWN_ERROR');
607
+ SnackController.showInternalError({
608
+ shortMessage: 'Error creating purchase URL',
609
+ longMessage: e?.message ?? e?.code
610
+ });
611
+
612
+ return undefined;
613
+ }
614
+ },
615
+
616
+ clearError() {
617
+ state.error = undefined;
618
+ },
619
+
620
+ clearQuotes() {
621
+ state.quotes = [];
622
+ state.selectedQuote = undefined;
623
+ state.selectedServiceProvider = undefined;
624
+ },
625
+
626
+ async loadOnRampData() {
627
+ state.initialLoading = true;
628
+ state.error = undefined;
629
+
630
+ try {
631
+ await this.fetchCountries();
632
+ await this.fetchServiceProviders();
633
+
634
+ await Promise.all([
635
+ this.fetchCountriesDefaults(),
636
+ this.fetchPaymentMethods(),
637
+ this.fetchFiatLimits(),
638
+ this.fetchCryptoCurrencies(),
639
+ this.fetchFiatCurrencies()
640
+ ]);
641
+ } catch (error) {
642
+ if (!state.error) {
643
+ state.error = {
644
+ type: OnRampErrorType.FAILED_TO_LOAD,
645
+ message: 'Failed to load onramp data'
646
+ };
647
+ }
648
+ } finally {
649
+ state.initialLoading = false;
650
+ }
651
+ },
652
+
653
+ resetState() {
654
+ state.error = undefined;
655
+ state.quotesLoading = false;
656
+ state.quotes = [];
657
+ state.selectedQuote = undefined;
658
+ state.selectedServiceProvider = undefined;
659
+ state.widgetUrl = undefined;
660
+ state.paymentAmount = undefined;
661
+ this.updateSelectedPurchaseCurrency();
662
+ }
663
+ };
@@ -28,6 +28,7 @@ export interface OptionsControllerState {
28
28
  sdkVersion: SdkVersion;
29
29
  metadata?: Metadata;
30
30
  isSiweEnabled?: boolean;
31
+ isOnRampEnabled?: boolean;
31
32
  features?: Features;
32
33
  debug?: boolean;
33
34
  }
@@ -97,6 +98,10 @@ export const OptionsController = {
97
98
  state.debug = debug;
98
99
  },
99
100
 
101
+ setIsOnRampEnabled(isOnRampEnabled: OptionsControllerState['isOnRampEnabled']) {
102
+ state.isOnRampEnabled = isOnRampEnabled;
103
+ },
104
+
100
105
  isClipboardAvailable() {
101
106
  return !!state._clipboardClient;
102
107
  },