@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.
- package/lib/commonjs/controllers/BlockchainApiController.js +65 -1
- package/lib/commonjs/controllers/BlockchainApiController.js.map +1 -1
- package/lib/commonjs/controllers/EnsController.js +4 -4
- package/lib/commonjs/controllers/EnsController.js.map +1 -1
- package/lib/commonjs/controllers/EventsController.js +4 -4
- package/lib/commonjs/controllers/EventsController.js.map +1 -1
- package/lib/commonjs/controllers/ModalController.js.map +1 -1
- package/lib/commonjs/controllers/OnRampController.js +475 -0
- package/lib/commonjs/controllers/OnRampController.js.map +1 -0
- package/lib/commonjs/controllers/OptionsController.js +3 -0
- package/lib/commonjs/controllers/OptionsController.js.map +1 -1
- package/lib/commonjs/controllers/RouterController.js +3 -2
- package/lib/commonjs/controllers/RouterController.js.map +1 -1
- package/lib/commonjs/controllers/SendController.js +5 -5
- package/lib/commonjs/controllers/SendController.js.map +1 -1
- package/lib/commonjs/controllers/TransactionsController.js +3 -3
- package/lib/commonjs/controllers/TransactionsController.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/ConstantsUtil.js +69 -5
- package/lib/commonjs/utils/ConstantsUtil.js.map +1 -1
- package/lib/commonjs/utils/CoreHelperUtil.js +28 -0
- package/lib/commonjs/utils/CoreHelperUtil.js.map +1 -1
- package/lib/commonjs/utils/FetchUtil.js +48 -13
- package/lib/commonjs/utils/FetchUtil.js.map +1 -1
- package/lib/commonjs/utils/StorageUtil.js +185 -1
- package/lib/commonjs/utils/StorageUtil.js.map +1 -1
- package/lib/commonjs/utils/TypeUtil.js +29 -0
- package/lib/commonjs/utils/TypeUtil.js.map +1 -1
- package/lib/module/controllers/BlockchainApiController.js +64 -0
- package/lib/module/controllers/BlockchainApiController.js.map +1 -1
- package/lib/module/controllers/EnsController.js +2 -2
- package/lib/module/controllers/EnsController.js.map +1 -1
- package/lib/module/controllers/EventsController.js +1 -1
- package/lib/module/controllers/EventsController.js.map +1 -1
- package/lib/module/controllers/ModalController.js.map +1 -1
- package/lib/module/controllers/OnRampController.js +470 -0
- package/lib/module/controllers/OnRampController.js.map +1 -0
- package/lib/module/controllers/OptionsController.js +3 -0
- package/lib/module/controllers/OptionsController.js.map +1 -1
- package/lib/module/controllers/RouterController.js +3 -2
- package/lib/module/controllers/RouterController.js.map +1 -1
- package/lib/module/controllers/SendController.js +2 -2
- package/lib/module/controllers/SendController.js.map +1 -1
- package/lib/module/controllers/TransactionsController.js +1 -1
- package/lib/module/controllers/TransactionsController.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/ConstantsUtil.js +68 -4
- package/lib/module/utils/ConstantsUtil.js.map +1 -1
- package/lib/module/utils/CoreHelperUtil.js +26 -0
- package/lib/module/utils/CoreHelperUtil.js.map +1 -1
- package/lib/module/utils/FetchUtil.js +48 -13
- package/lib/module/utils/FetchUtil.js.map +1 -1
- package/lib/module/utils/StorageUtil.js +186 -1
- package/lib/module/utils/StorageUtil.js.map +1 -1
- package/lib/module/utils/TypeUtil.js +27 -1
- package/lib/module/utils/TypeUtil.js.map +1 -1
- package/lib/typescript/controllers/BlockchainApiController.d.ts +16 -1
- package/lib/typescript/controllers/BlockchainApiController.d.ts.map +1 -1
- package/lib/typescript/controllers/ModalController.d.ts +1 -1
- package/lib/typescript/controllers/ModalController.d.ts.map +1 -1
- package/lib/typescript/controllers/OnRampController.d.ts +55 -0
- package/lib/typescript/controllers/OnRampController.d.ts.map +1 -0
- package/lib/typescript/controllers/OptionsController.d.ts +2 -0
- package/lib/typescript/controllers/OptionsController.d.ts.map +1 -1
- package/lib/typescript/controllers/RouterController.d.ts +4 -3
- package/lib/typescript/controllers/RouterController.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/utils/ConstantsUtil.d.ts +55 -0
- package/lib/typescript/utils/ConstantsUtil.d.ts.map +1 -1
- package/lib/typescript/utils/CoreHelperUtil.d.ts +3 -0
- package/lib/typescript/utils/CoreHelperUtil.d.ts.map +1 -1
- package/lib/typescript/utils/FetchUtil.d.ts +4 -4
- package/lib/typescript/utils/FetchUtil.d.ts.map +1 -1
- package/lib/typescript/utils/StorageUtil.d.ts +16 -2
- package/lib/typescript/utils/StorageUtil.d.ts.map +1 -1
- package/lib/typescript/utils/TypeUtil.d.ts +172 -0
- package/lib/typescript/utils/TypeUtil.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/controllers/BlockchainApiController.ts +87 -1
- package/src/controllers/EnsController.ts +2 -2
- package/src/controllers/EventsController.ts +1 -1
- package/src/controllers/ModalController.ts +1 -2
- package/src/controllers/OnRampController.ts +663 -0
- package/src/controllers/OptionsController.ts +5 -0
- package/src/controllers/RouterController.ts +16 -3
- package/src/controllers/SendController.ts +2 -2
- package/src/controllers/TransactionsController.ts +1 -1
- package/src/index.ts +1 -0
- package/src/utils/ConstantsUtil.ts +49 -4
- package/src/utils/CoreHelperUtil.ts +30 -0
- package/src/utils/FetchUtil.ts +51 -17
- package/src/utils/StorageUtil.ts +225 -3
- 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
|
},
|