@imtbl/sdk 1.4.0-alpha.2 → 1.4.1-alpha.1

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 (51) hide show
  1. package/dist/blockchain_data.js +2 -2
  2. package/dist/browser/checkout/BridgeWidget-cf957c15.js +1 -0
  3. package/dist/browser/checkout/OnRampWidget-83d73134.js +1 -0
  4. package/dist/browser/checkout/SaleWidget-ad8b7642.js +2 -0
  5. package/dist/browser/checkout/SpendingCapHero-0a0f9fd0.js +1 -0
  6. package/dist/browser/checkout/SwapWidget-7c0a1017.js +1 -0
  7. package/dist/browser/checkout/TopUpView-3b2e045d.js +1 -0
  8. package/dist/browser/checkout/WalletWidget-381ddb8f.js +1 -0
  9. package/dist/browser/checkout/{auto-track-1bf3a0ee.js → auto-track-8b6baf69.js} +1 -1
  10. package/dist/browser/checkout/balance-1493691b.js +1 -0
  11. package/dist/browser/checkout/index-1c33f972.js +1 -0
  12. package/dist/browser/checkout/{index-942c822d.js → index-287f0ec7.js} +1 -1
  13. package/dist/browser/checkout/{index-3272becd.js → index-531bae54.js} +1 -1
  14. package/dist/browser/checkout/{index-d9cfe478.js → index-696631eb.js} +9 -9
  15. package/dist/browser/checkout/{index-8562ab3b.js → index-b7b99467.js} +1 -1
  16. package/dist/browser/checkout/{index-9a5fbaa1.js → index-e7272f1c.js} +12 -12
  17. package/dist/browser/checkout/{index-6ee5fac7.js → index-feece362.js} +1 -1
  18. package/dist/browser/checkout/{index.umd-956dbfb2.js → index.umd-bcb0582f.js} +1 -1
  19. package/dist/browser/checkout/{retry-81ddd897.js → retry-86e4cf9a.js} +1 -1
  20. package/dist/browser/checkout/sdk.js +4 -4
  21. package/dist/browser/checkout/{useTransakEvents-8580b8ed.js → useTransakEvents-a01463cc.js} +2 -2
  22. package/dist/browser/checkout/widgets.js +1 -1
  23. package/dist/checkout.js +4 -4
  24. package/dist/config.js +1 -1
  25. package/dist/index.browser.js +4 -4
  26. package/dist/index.cjs +6 -6
  27. package/dist/index.js +6 -6
  28. package/dist/orderbook.js +1 -1
  29. package/dist/passport.js +3 -3
  30. package/dist/x.js +2 -2
  31. package/package.json +1 -1
  32. package/dist/browser/checkout/BridgeWidget-52ba2210.js +0 -2753
  33. package/dist/browser/checkout/BridgeWidget-fffec54f.js +0 -1
  34. package/dist/browser/checkout/OnRampWidget-04677711.js +0 -288
  35. package/dist/browser/checkout/OnRampWidget-b1c1cc52.js +0 -1
  36. package/dist/browser/checkout/SaleWidget-8b73504f.js +0 -1610
  37. package/dist/browser/checkout/SaleWidget-e9cc4cb2.js +0 -2
  38. package/dist/browser/checkout/SpendingCapHero-43c9cf8c.js +0 -9
  39. package/dist/browser/checkout/SpendingCapHero-af71a6f5.js +0 -1
  40. package/dist/browser/checkout/SwapWidget-45ff8dfa.js +0 -1
  41. package/dist/browser/checkout/SwapWidget-e52876ac.js +0 -1461
  42. package/dist/browser/checkout/TopUpView-590c2c4c.js +0 -365
  43. package/dist/browser/checkout/TopUpView-ff09517a.js +0 -1
  44. package/dist/browser/checkout/WalletWidget-9c4c62e4.js +0 -760
  45. package/dist/browser/checkout/WalletWidget-ed0bd774.js +0 -1
  46. package/dist/browser/checkout/balance-e5a26a9d.js +0 -1
  47. package/dist/browser/checkout/balance-e5d785aa.js +0 -182
  48. package/dist/browser/checkout/index-1405c4f4.js +0 -1
  49. package/dist/browser/checkout/index-4511315b.js +0 -5638
  50. package/dist/browser/checkout/retry-254e3478.js +0 -58
  51. package/dist/browser/checkout/useTransakEvents-54a554e5.js +0 -150
@@ -1,1610 +0,0 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { useState, useCallback, createContext, useRef, useContext, useEffect, useMemo, useReducer } from 'react';
3
- import { RoutingOutcomeType, ItemType, TransactionOrGasType, GasTokenType, FundingStepType, SalePaymentTypes, IMTBLWidgetEvents, ConnectTargetLayer, SwapEventType, BridgeEventType, ConnectEventType, ChainId, BlockExplorerService } from '@imtbl/checkout-sdk';
4
- import { Environment } from '@imtbl/config';
5
- import { useTranslation } from 'react-i18next';
6
- import { M as SaleErrorTypes, k as getL2ChainId, N as NATIVE, x as calculateCryptoToFiat, P as formatFiatString, Q as SmartCheckoutErrorTypes, V as ViewContext, b as ViewActions, R as SaleWidgetViews, T as FundWithSmartCheckoutSubViews, C as ConnectLoaderContext, X as eventTargetReducer, Y as initialEventTargetState, l as getL1ChainId, _ as EventTargetActions, E as EventTargetContext, L as LoadingView, $ as ConnectWidget, a0 as ConnectWidgetViews, a1 as ConnectLoaderActions, t as tokenValueFormat, a as SimpleLayout, H as HeaderNavigation, a2 as sendSaleWidgetCloseEvent, F as FooterLogo, u as useAnalytics, U as UserJourney, a3 as sendSaleSuccessEvent, a4 as sendSaleFailedEvent, a5 as sendSaleTransactionSuccessEvent, a6 as sendSalePaymentMethodEvent, a7 as CenteredBoxContent, a8 as LoadingBox, S as SharedViews, o as orchestrationEvents, f as StatusType, d as StatusView, a9 as widgetTheme, v as viewReducer, i as initialViewState } from './index-4511315b.js';
7
- import { BigNumber } from 'ethers';
8
- import { parseUnits } from 'ethers/lib/utils';
9
- import { MenuItem, Heading, HorizontalMenu, Button, Drawer, Box, Body, Banner, Link } from '@biom3/react';
10
- import { B as BridgeWidget, g as getChainNameById } from './BridgeWidget-52ba2210.js';
11
- import SwapWidget from './SwapWidget-e52876ac.js';
12
- import { C as CryptoFiatContext, a as CryptoFiatActions, b as CryptoFiatProvider } from './TopUpView-590c2c4c.js';
13
- import { u as useTransakEvents } from './useTransakEvents-54a554e5.js';
14
- import pako from 'pako';
15
- import '@biom3/design-tokens';
16
- import '@imtbl/react-analytics';
17
- import 'react-dom/client';
18
- import '@ethersproject/providers';
19
- import 'i18next';
20
- import 'i18next-browser-languagedetector';
21
- import '@imtbl/bridge-sdk';
22
- import 'axios';
23
- import './retry-254e3478.js';
24
- import './balance-e5d785aa.js';
25
- import '@rive-app/react-canvas';
26
- import '@imtbl/dex-sdk';
27
- import './SpendingCapHero-43c9cf8c.js';
28
- import '@imtbl/cryptofiat';
29
- import 'url';
30
-
31
- /* eslint-disable @typescript-eslint/naming-convention */
32
- const PRIMARY_SALES_API_BASE_URL = {
33
- [Environment.SANDBOX]: 'https://api.sandbox.immutable.com/v1/primary-sales',
34
- [Environment.PRODUCTION]: 'https://api.immutable.com/v1/primary-sales',
35
- };
36
- var SignCurrencyFilter;
37
- (function (SignCurrencyFilter) {
38
- SignCurrencyFilter["CONTRACT_ADDRESS"] = "contract_address";
39
- SignCurrencyFilter["CURRENCY_SYMBOL"] = "currency_symbol";
40
- })(SignCurrencyFilter || (SignCurrencyFilter = {}));
41
- const toSignedProduct = (product, currency, item) => ({
42
- productId: product.product_id,
43
- image: item?.image || '',
44
- qty: item?.qty || 1,
45
- name: item?.name || '',
46
- description: item?.description || '',
47
- currency,
48
- contractType: product.contract_type,
49
- collectionAddress: product.collection_address,
50
- amount: product.detail.map(({ amount }) => amount),
51
- tokenId: product.detail.map(({ token_id: tokenId }) => tokenId),
52
- });
53
- const toSignResponse = (signApiResponse, items) => {
54
- const { order, transactions } = signApiResponse;
55
- return {
56
- order: {
57
- currency: {
58
- name: order.currency.name,
59
- erc20Address: order.currency.erc20_address,
60
- },
61
- products: order.products
62
- .map((product) => toSignedProduct(product, order.currency.name, items.find((item) => item.productId === product.product_id)))
63
- .reduce((acc, product) => {
64
- const index = acc.findIndex((n) => n.name === product.name);
65
- if (index === -1) {
66
- acc.push({ ...product });
67
- }
68
- if (index > -1) {
69
- acc[index].amount = [...acc[index].amount, ...product.amount];
70
- acc[index].tokenId = [...acc[index].tokenId, ...product.tokenId];
71
- }
72
- return acc;
73
- }, []),
74
- totalAmount: Number(order.total_amount),
75
- },
76
- transactions: transactions.map((transaction) => ({
77
- tokenAddress: transaction.contract_address,
78
- gasEstimate: transaction.gas_estimate,
79
- methodCall: transaction.method_call,
80
- params: {
81
- reference: transaction.params.reference || '',
82
- amount: transaction.params.amount || 0,
83
- spender: transaction.params.spender || '',
84
- },
85
- rawData: transaction.raw_data,
86
- })),
87
- };
88
- };
89
- const useSignOrder = (input) => {
90
- const { provider, items, fromTokenAddress, recipientAddress, env, environmentId, } = input;
91
- const [signError, setSignError] = useState(undefined);
92
- const [signResponse, setSignResponse] = useState(undefined);
93
- const [executeResponse, setExecuteResponse] = useState({
94
- done: false,
95
- transactions: [],
96
- });
97
- const setExecuteTransactions = (transaction) => {
98
- setExecuteResponse((prev) => ({
99
- ...prev,
100
- transactions: [...prev.transactions, transaction],
101
- }));
102
- };
103
- const setExecuteDone = () => setExecuteResponse((prev) => ({ ...prev, done: true }));
104
- const setExecuteFailed = () => setExecuteResponse({
105
- done: false,
106
- transactions: [],
107
- });
108
- const sendTransaction = useCallback(async (to, data, gasLimit, method) => {
109
- let transactionHash;
110
- try {
111
- const signer = provider?.getSigner();
112
- const gasPrice = await provider?.getGasPrice();
113
- const txnResponse = await signer?.sendTransaction({
114
- to,
115
- data,
116
- gasPrice,
117
- gasLimit,
118
- });
119
- setExecuteTransactions({ method, hash: txnResponse?.hash });
120
- await txnResponse?.wait(1);
121
- transactionHash = txnResponse?.hash || '';
122
- return [transactionHash, undefined];
123
- }
124
- catch (err) {
125
- const reason = `${err?.reason || err?.message || ''}`.toLowerCase();
126
- transactionHash = err?.transactionHash;
127
- let errorType = SaleErrorTypes.WALLET_FAILED;
128
- if (reason.includes('rejected') && reason.includes('user')) {
129
- errorType = SaleErrorTypes.WALLET_REJECTED;
130
- }
131
- if (reason.includes('failed to submit')
132
- && reason.includes('highest gas limit')) {
133
- errorType = SaleErrorTypes.WALLET_REJECTED_NO_FUNDS;
134
- }
135
- if (reason.includes('status failed')
136
- || reason.includes('transaction failed')) {
137
- errorType = SaleErrorTypes.TRANSACTION_FAILED;
138
- }
139
- setSignError({
140
- type: errorType,
141
- data: { error: err },
142
- });
143
- return [undefined, err];
144
- }
145
- }, [provider]);
146
- const sign = useCallback(async (paymentType) => {
147
- try {
148
- const data = {
149
- recipient_address: recipientAddress,
150
- payment_type: paymentType,
151
- currency_filter: SignCurrencyFilter.CONTRACT_ADDRESS,
152
- currency_value: fromTokenAddress,
153
- products: items.map((item) => ({
154
- product_id: item.productId,
155
- quantity: item.qty,
156
- })),
157
- };
158
- const baseUrl = `${PRIMARY_SALES_API_BASE_URL[env]}/${environmentId}/order/sign`;
159
- const response = await fetch(baseUrl, {
160
- method: 'POST',
161
- headers: {
162
- 'Content-Type': 'application/json',
163
- },
164
- body: JSON.stringify(data),
165
- });
166
- const { ok, status } = response;
167
- if (!ok) {
168
- const { code } = (await response.json());
169
- let errorType;
170
- switch (status) {
171
- case 400:
172
- errorType = SaleErrorTypes.SERVICE_BREAKDOWN;
173
- break;
174
- case 404:
175
- if (code === 'insufficient_stock') {
176
- errorType = SaleErrorTypes.INSUFFICIENT_STOCK;
177
- }
178
- else {
179
- errorType = SaleErrorTypes.PRODUCT_NOT_FOUND;
180
- }
181
- break;
182
- case 429:
183
- case 500:
184
- errorType = SaleErrorTypes.DEFAULT;
185
- break;
186
- default:
187
- throw new Error('Unknown error');
188
- }
189
- setSignError({ type: errorType });
190
- return undefined;
191
- }
192
- const responseData = toSignResponse(await response.json(), items);
193
- setSignResponse(responseData);
194
- return responseData;
195
- }
196
- catch (e) {
197
- setSignError({ type: SaleErrorTypes.DEFAULT, data: { error: e } });
198
- }
199
- return undefined;
200
- }, [items, fromTokenAddress, recipientAddress, environmentId, env, provider]);
201
- const execute = async (signData, onTxnSuccess, onTxnError) => {
202
- if (!signData) {
203
- setSignError({
204
- type: SaleErrorTypes.DEFAULT,
205
- data: { reason: 'No sign data' },
206
- });
207
- return [];
208
- }
209
- let successful = true;
210
- const execTransactions = [];
211
- for (const transaction of signData.transactions) {
212
- const { tokenAddress: to, rawData: data, methodCall: method, gasEstimate, } = transaction;
213
- // eslint-disable-next-line no-await-in-loop
214
- const [hash, txnError] = await sendTransaction(to, data, gasEstimate, method);
215
- if (txnError || !hash) {
216
- successful = false;
217
- onTxnError(txnError, execTransactions);
218
- break;
219
- }
220
- execTransactions.push({ method, hash });
221
- onTxnSuccess({ method, hash });
222
- }
223
- (successful ? setExecuteDone : setExecuteFailed)();
224
- return execTransactions;
225
- };
226
- return {
227
- sign,
228
- signResponse,
229
- signError,
230
- execute,
231
- executeResponse,
232
- };
233
- };
234
-
235
- const MAX_GAS_LIMIT$1 = '30000000';
236
- const getItemRequirements = (amount, spenderAddress, tokenAddress) => [
237
- {
238
- type: ItemType.ERC20,
239
- tokenAddress,
240
- spenderAddress,
241
- amount,
242
- },
243
- ];
244
- const getGasEstimate = () => ({
245
- type: TransactionOrGasType.GAS,
246
- gasToken: {
247
- type: GasTokenType.NATIVE,
248
- limit: BigNumber.from(MAX_GAS_LIMIT$1),
249
- },
250
- });
251
- const isUserFractionalBalanceBlocked = async (walletAddress, tokenAddress, amount, checkout, provider) => {
252
- const chainId = getL2ChainId(checkout.config);
253
- const balanceResponse = await checkout.getAllBalances({ provider: provider, walletAddress, chainId });
254
- const zero = BigNumber.from('0');
255
- const purchaseBalance = balanceResponse.balances.find((balance) => balance.token.address === tokenAddress);
256
- if (!purchaseBalance) {
257
- return false;
258
- }
259
- const formattedAmount = parseUnits(amount, purchaseBalance.token.decimals);
260
- if (purchaseBalance.balance.gt(zero) && purchaseBalance.balance.lt(formattedAmount)) {
261
- return true;
262
- }
263
- const isPassport = !!provider?.provider?.isPassport;
264
- if (isPassport) {
265
- return false;
266
- }
267
- const imxBalance = balanceResponse.balances.find((balance) => balance.token.address === NATIVE);
268
- const imxBalanceAmount = imxBalance ? imxBalance.balance : BigNumber.from('0');
269
- if (imxBalanceAmount.gte(zero) && imxBalanceAmount.lt(BigNumber.from(MAX_GAS_LIMIT$1))) {
270
- return true;
271
- }
272
- return false;
273
- };
274
- const fundingRouteFees = (fundingRoute, conversions) => {
275
- const fees = [];
276
- for (const step of fundingRoute.steps) {
277
- switch (step.type) {
278
- case FundingStepType.BRIDGE:
279
- fees.push(step.fees.approvalGasFee);
280
- fees.push(...step.fees.bridgeFees);
281
- fees.push(step.fees.bridgeGasFee);
282
- break;
283
- case FundingStepType.SWAP:
284
- fees.push(step.fees.approvalGasFee);
285
- fees.push(...step.fees.swapFees);
286
- fees.push(step.fees.swapGasFee);
287
- break;
288
- }
289
- }
290
- let totalUsd = 0;
291
- for (const fee of fees) {
292
- if (fee.token) {
293
- const feeUsd = calculateCryptoToFiat(fee.formattedAmount, fee.token.symbol, conversions);
294
- totalUsd += parseFloat(feeUsd);
295
- }
296
- }
297
- return formatFiatString(totalUsd);
298
- };
299
- const smartCheckoutTokensList = (smartCheckoutResult) => {
300
- if (smartCheckoutResult.sufficient
301
- || smartCheckoutResult.router.routingOutcome.type !== RoutingOutcomeType.ROUTES_FOUND) {
302
- return [];
303
- }
304
- const tokenSymbols = [];
305
- for (const requirement of smartCheckoutResult.transactionRequirements) {
306
- const { token } = requirement.current;
307
- if (!tokenSymbols.includes(token.symbol)) {
308
- tokenSymbols.push(token.symbol);
309
- }
310
- }
311
- for (const fundingRoute of smartCheckoutResult.router.routingOutcome.fundingRoutes) {
312
- for (const step of fundingRoute.steps) {
313
- if (!tokenSymbols.includes(step.fundingItem.token.symbol)) {
314
- tokenSymbols.push(step.fundingItem.token.symbol);
315
- }
316
- }
317
- }
318
- return tokenSymbols;
319
- };
320
- const filterSmartCheckoutResult = (smartCheckoutResult) => {
321
- if (smartCheckoutResult.sufficient
322
- || smartCheckoutResult.router.routingOutcome.type !== RoutingOutcomeType.ROUTES_FOUND) {
323
- return smartCheckoutResult;
324
- }
325
- const filteredFundingRoutes = smartCheckoutResult.router.routingOutcome.fundingRoutes
326
- .filter((route) => !route.steps.some((step) => step.type !== FundingStepType.SWAP));
327
- let routingOutcome;
328
- if (filteredFundingRoutes.length === 0) {
329
- routingOutcome = {
330
- type: RoutingOutcomeType.NO_ROUTES_FOUND,
331
- message: 'Smart Checkout did not find any Swap routes to fulfill the transaction',
332
- };
333
- }
334
- else {
335
- routingOutcome = {
336
- type: RoutingOutcomeType.ROUTES_FOUND,
337
- fundingRoutes: filteredFundingRoutes,
338
- };
339
- }
340
- return {
341
- ...smartCheckoutResult,
342
- router: {
343
- ...smartCheckoutResult.router,
344
- routingOutcome,
345
- },
346
- };
347
- };
348
-
349
- const useSmartCheckout = ({ checkout, provider, items, amount, tokenAddress, }) => {
350
- const [smartCheckoutResult, setSmartCheckoutResult] = useState(undefined);
351
- const [smartCheckoutError, setSmartCheckoutError] = useState(undefined);
352
- const smartCheckout = useCallback(async () => {
353
- try {
354
- const signer = provider?.getSigner();
355
- const spenderAddress = await signer?.getAddress() || '';
356
- const userFractionalBalanceBlocked = await isUserFractionalBalanceBlocked(spenderAddress, tokenAddress, amount, checkout, provider);
357
- if (userFractionalBalanceBlocked) {
358
- throw new Error(SmartCheckoutErrorTypes.FRACTIONAL_BALANCE_BLOCKED);
359
- }
360
- const itemRequirements = getItemRequirements(amount, spenderAddress, tokenAddress);
361
- const gasEstimate = getGasEstimate();
362
- const res = await checkout?.smartCheckout({
363
- provider: provider,
364
- itemRequirements,
365
- transactionOrGasAmount: gasEstimate,
366
- });
367
- if (!res) {
368
- throw new Error();
369
- }
370
- const result = { ...res };
371
- const filteredSmartCheckoutResult = filterSmartCheckoutResult(result);
372
- setSmartCheckoutResult(filteredSmartCheckoutResult);
373
- return result;
374
- }
375
- catch (err) {
376
- setSmartCheckoutError({
377
- type: SaleErrorTypes.SMART_CHECKOUT_ERROR,
378
- data: { error: err },
379
- });
380
- }
381
- return undefined;
382
- }, [checkout, provider, items, amount, tokenAddress]);
383
- return {
384
- smartCheckout, smartCheckoutResult, smartCheckoutError,
385
- };
386
- };
387
-
388
- // eslint-disable-next-line @typescript-eslint/naming-convention
389
- const SaleContext = createContext({
390
- items: [],
391
- amount: '',
392
- fromTokenAddress: '',
393
- collectionName: '',
394
- provider: undefined,
395
- checkout: undefined,
396
- environmentId: '',
397
- env: '',
398
- recipientAddress: '',
399
- recipientEmail: '',
400
- sign: () => Promise.resolve(undefined),
401
- execute: () => Promise.resolve([]),
402
- signResponse: undefined,
403
- signError: undefined,
404
- executeResponse: undefined,
405
- passport: undefined,
406
- isPassportWallet: false,
407
- paymentMethod: undefined,
408
- setPaymentMethod: () => { },
409
- goBackToPaymentMethods: () => { },
410
- goToErrorView: () => { },
411
- goToSuccessView: () => { },
412
- config: {},
413
- querySmartCheckout: () => Promise.resolve(undefined),
414
- smartCheckoutResult: undefined,
415
- fundingRoutes: [],
416
- disabledPaymentTypes: [],
417
- invalidParameters: false,
418
- });
419
- SaleContext.displayName = 'SaleSaleContext';
420
- /** Max attemps to retry with same payment method */
421
- const MAX_ERROR_RETRIES = 1;
422
- function SaleContextProvider(props) {
423
- const { children, value: { config, env, environmentId, items, amount, fromTokenAddress, provider, checkout, passport, collectionName, }, } = props;
424
- const errorRetries = useRef(0);
425
- const { viewDispatch } = useContext(ViewContext);
426
- const [{ recipientEmail, recipientAddress }, setUserInfo] = useState({
427
- recipientEmail: '',
428
- recipientAddress: '',
429
- });
430
- const [paymentMethod, setPaymentMethod] = useState(undefined);
431
- const [fundingRoutes, setFundingRoutes] = useState([]);
432
- const [disabledPaymentTypes, setDisabledPaymentTypes] = useState([]);
433
- const [invalidParameters, setInvalidParameters] = useState(false);
434
- const goBackToPaymentMethods = useCallback((type, showInsufficientCoinsBanner) => {
435
- setPaymentMethod(type);
436
- viewDispatch({
437
- payload: {
438
- type: ViewActions.UPDATE_VIEW,
439
- view: {
440
- type: SaleWidgetViews.PAYMENT_METHODS,
441
- data: {
442
- showInsufficientCoinsBanner,
443
- },
444
- },
445
- },
446
- });
447
- }, []);
448
- useEffect(() => {
449
- const getUserInfo = async () => {
450
- const signer = provider?.getSigner();
451
- const address = (await signer?.getAddress()) || '';
452
- const email = (await passport?.getUserInfo())?.email || '';
453
- setUserInfo({ recipientEmail: email, recipientAddress: address });
454
- };
455
- getUserInfo();
456
- }, [provider]);
457
- const { sign: signOrder, execute, signResponse, signError, executeResponse, } = useSignOrder({
458
- items,
459
- provider,
460
- fromTokenAddress,
461
- recipientAddress,
462
- environmentId,
463
- env,
464
- });
465
- const sign = useCallback(async (type, callback) => {
466
- const response = await signOrder(type);
467
- if (!response)
468
- return undefined;
469
- callback?.(response);
470
- return response;
471
- }, [signOrder]);
472
- const goToErrorView = useCallback((errorType, data = {}) => {
473
- errorRetries.current += 1;
474
- if (errorRetries.current > MAX_ERROR_RETRIES) {
475
- errorRetries.current = 0;
476
- setPaymentMethod(undefined);
477
- }
478
- viewDispatch({
479
- payload: {
480
- type: ViewActions.UPDATE_VIEW,
481
- view: {
482
- type: SaleWidgetViews.SALE_FAIL,
483
- data: {
484
- ...data,
485
- errorType,
486
- paymentMethod,
487
- transactions: executeResponse.transactions,
488
- },
489
- },
490
- },
491
- });
492
- }, [paymentMethod, setPaymentMethod, executeResponse]);
493
- const goToSuccessView = useCallback((data) => {
494
- viewDispatch({
495
- payload: {
496
- type: ViewActions.UPDATE_VIEW,
497
- view: {
498
- type: SaleWidgetViews.SALE_SUCCESS,
499
- data: {
500
- paymentMethod,
501
- transactions: executeResponse.transactions,
502
- ...data,
503
- },
504
- },
505
- },
506
- });
507
- }, [[paymentMethod, executeResponse]]);
508
- useEffect(() => {
509
- if (!signError)
510
- return;
511
- goToErrorView(signError.type, signError.data);
512
- }, [signError]);
513
- const { smartCheckout, smartCheckoutResult, smartCheckoutError } = useSmartCheckout({
514
- provider,
515
- checkout,
516
- items,
517
- amount,
518
- tokenAddress: fromTokenAddress,
519
- });
520
- useEffect(() => {
521
- if (!smartCheckoutError)
522
- return;
523
- if (smartCheckoutError.data?.error?.message === SmartCheckoutErrorTypes.FRACTIONAL_BALANCE_BLOCKED) {
524
- setDisabledPaymentTypes([SalePaymentTypes.CRYPTO]);
525
- goBackToPaymentMethods(undefined, true);
526
- return;
527
- }
528
- goToErrorView(smartCheckoutError.type, smartCheckoutError.data);
529
- }, [smartCheckoutError]);
530
- const querySmartCheckout = useCallback(async (callback) => {
531
- const result = await smartCheckout();
532
- callback?.(result);
533
- return result;
534
- }, [smartCheckout]);
535
- useEffect(() => {
536
- if (!smartCheckoutResult) {
537
- return;
538
- }
539
- if (smartCheckoutResult.sufficient) {
540
- sign(SalePaymentTypes.CRYPTO);
541
- viewDispatch({
542
- payload: {
543
- type: ViewActions.UPDATE_VIEW,
544
- view: {
545
- type: SaleWidgetViews.PAY_WITH_COINS,
546
- },
547
- },
548
- });
549
- }
550
- if (!smartCheckoutResult.sufficient) {
551
- switch (smartCheckoutResult.router.routingOutcome.type) {
552
- case RoutingOutcomeType.ROUTES_FOUND:
553
- setFundingRoutes(smartCheckoutResult.router.routingOutcome.fundingRoutes);
554
- viewDispatch({
555
- payload: {
556
- type: ViewActions.UPDATE_VIEW,
557
- view: {
558
- type: SaleWidgetViews.FUND_WITH_SMART_CHECKOUT,
559
- subView: FundWithSmartCheckoutSubViews.FUNDING_ROUTE_SELECT,
560
- },
561
- },
562
- });
563
- break;
564
- case RoutingOutcomeType.NO_ROUTES_FOUND:
565
- case RoutingOutcomeType.NO_ROUTE_OPTIONS:
566
- default:
567
- setFundingRoutes([]);
568
- setPaymentMethod(undefined);
569
- setDisabledPaymentTypes([SalePaymentTypes.CRYPTO]);
570
- goBackToPaymentMethods(undefined, true);
571
- break;
572
- }
573
- }
574
- }, [smartCheckoutResult]);
575
- useEffect(() => {
576
- const invalidItems = !items || items.length === 0;
577
- const invalidAmount = !amount || amount === '0';
578
- const invalidFromTokenAddress = !fromTokenAddress || !fromTokenAddress.startsWith('0x');
579
- if (invalidItems || invalidAmount || invalidFromTokenAddress || !collectionName || !environmentId) {
580
- setInvalidParameters(true);
581
- }
582
- }, [items, amount, fromTokenAddress, collectionName, environmentId]);
583
- const values = useMemo(() => ({
584
- config,
585
- items,
586
- amount,
587
- fromTokenAddress,
588
- sign,
589
- signResponse,
590
- signError,
591
- execute,
592
- executeResponse,
593
- environmentId,
594
- collectionName,
595
- env,
596
- provider,
597
- checkout,
598
- recipientAddress,
599
- recipientEmail,
600
- paymentMethod,
601
- setPaymentMethod,
602
- goBackToPaymentMethods,
603
- goToErrorView,
604
- goToSuccessView,
605
- isPassportWallet: !!provider?.provider?.isPassport,
606
- querySmartCheckout,
607
- smartCheckoutResult,
608
- fundingRoutes,
609
- disabledPaymentTypes,
610
- invalidParameters,
611
- }), [
612
- config,
613
- env,
614
- environmentId,
615
- items,
616
- amount,
617
- fromTokenAddress,
618
- collectionName,
619
- provider,
620
- checkout,
621
- recipientAddress,
622
- recipientEmail,
623
- signResponse,
624
- signError,
625
- executeResponse,
626
- paymentMethod,
627
- goBackToPaymentMethods,
628
- goToErrorView,
629
- goToSuccessView,
630
- sign,
631
- querySmartCheckout,
632
- smartCheckoutResult,
633
- fundingRoutes,
634
- disabledPaymentTypes,
635
- invalidParameters,
636
- ]);
637
- return jsx(SaleContext.Provider, { value: values, children: children });
638
- }
639
- function useSaleContext() {
640
- return useContext(SaleContext);
641
- }
642
-
643
- var FundingRouteExecuteViews;
644
- (function (FundingRouteExecuteViews) {
645
- FundingRouteExecuteViews["LOADING"] = "LOADING";
646
- FundingRouteExecuteViews["EXECUTE_BRIDGE"] = "EXECUTE_BRIDGE";
647
- FundingRouteExecuteViews["EXECUTE_SWAP"] = "EXECUTE_SWAP";
648
- FundingRouteExecuteViews["SWITCH_NETWORK_ETH"] = "SWITCH_NETWORK_ETH";
649
- FundingRouteExecuteViews["SWITCH_NETWORK_ZKEVM"] = "SWITCH_NETWORK_ZKEVM";
650
- })(FundingRouteExecuteViews || (FundingRouteExecuteViews = {}));
651
- function FundingRouteExecute({ fundingRouteStep, onFundingRouteExecuted }) {
652
- const { t } = useTranslation();
653
- const { config, provider, checkout, fromTokenAddress: requiredTokenAddress, } = useSaleContext();
654
- const { viewDispatch } = useContext(ViewContext);
655
- const { connectLoaderDispatch } = useContext(ConnectLoaderContext);
656
- const [swapParams, setSwapParams] = useState(undefined);
657
- const [bridgeParams, setBridgeParams] = useState(undefined);
658
- const [view, setView] = useState(FundingRouteExecuteViews.LOADING);
659
- const nextView = useRef(false);
660
- const stepSuccess = useRef(undefined);
661
- const stepFailed = useRef(undefined);
662
- const [eventTargetState, eventTargetDispatch] = useReducer(eventTargetReducer, initialEventTargetState);
663
- const eventTargetReducerValues = useMemo(() => ({ eventTargetState, eventTargetDispatch }), [eventTargetState, eventTargetDispatch]);
664
- const eventTarget = new EventTarget();
665
- const sendFailEvent = (errorData) => {
666
- viewDispatch({
667
- payload: {
668
- type: ViewActions.UPDATE_VIEW,
669
- view: {
670
- type: SaleWidgetViews.SALE_FAIL,
671
- data: {
672
- errorType: SaleErrorTypes.SMART_CHECKOUT_EXECUTE_ERROR,
673
- errorData,
674
- },
675
- },
676
- },
677
- });
678
- };
679
- const handleStep = useCallback(async (step) => {
680
- if (!checkout || !provider) {
681
- throw new Error('checkout or provider not available.');
682
- }
683
- const network = await checkout.getNetworkInfo({
684
- provider,
685
- });
686
- if (step.type === FundingStepType.BRIDGE) {
687
- setBridgeParams({
688
- tokenAddress: step.fundingItem.token.address,
689
- amount: step.fundingItem.fundsRequired.formattedAmount,
690
- });
691
- if (network.chainId === getL1ChainId(checkout.config)) {
692
- setView(FundingRouteExecuteViews.EXECUTE_BRIDGE);
693
- return;
694
- }
695
- nextView.current = FundingRouteExecuteViews.EXECUTE_BRIDGE;
696
- setView(FundingRouteExecuteViews.SWITCH_NETWORK_ETH);
697
- }
698
- if (step.type === FundingStepType.SWAP) {
699
- setSwapParams({
700
- amount: step.fundingItem.fundsRequired.formattedAmount,
701
- fromTokenAddress: step.fundingItem.token.address,
702
- toTokenAddress: requiredTokenAddress,
703
- });
704
- if (network.chainId === getL2ChainId(checkout.config)) {
705
- setView(FundingRouteExecuteViews.EXECUTE_SWAP);
706
- return;
707
- }
708
- nextView.current = FundingRouteExecuteViews.EXECUTE_SWAP;
709
- setView(FundingRouteExecuteViews.SWITCH_NETWORK_ZKEVM);
710
- }
711
- }, [provider, checkout]);
712
- useEffect(() => {
713
- if (!fundingRouteStep) {
714
- return;
715
- }
716
- try {
717
- handleStep(fundingRouteStep);
718
- }
719
- catch (err) {
720
- sendFailEvent(err);
721
- }
722
- }, [fundingRouteStep]);
723
- const onCloseWidget = () => {
724
- // Need to check SUCCESS first, as it's possible for widget to emit both FAILED and SUCCESS.
725
- if (stepSuccess.current) {
726
- stepSuccess.current = undefined;
727
- stepFailed.current = undefined;
728
- onFundingRouteExecuted();
729
- }
730
- else {
731
- sendFailEvent(stepFailed.current);
732
- }
733
- };
734
- const handleCustomEvent = (event) => {
735
- switch (event.detail.type) {
736
- case BridgeEventType.TRANSACTION_SENT:
737
- case SwapEventType.SUCCESS: {
738
- const successEvent = event.detail.data;
739
- stepSuccess.current = successEvent;
740
- break;
741
- }
742
- case BridgeEventType.FAILURE:
743
- case SwapEventType.FAILURE: {
744
- // On FAILURE, widget will prompt user to try again.
745
- // We need to know if it failed though when they close the widget
746
- const failureEvent = event.detail.data;
747
- stepFailed.current = failureEvent;
748
- break;
749
- }
750
- case BridgeEventType.CLOSE_WIDGET:
751
- case SwapEventType.CLOSE_WIDGET: {
752
- onCloseWidget();
753
- break;
754
- }
755
- case SwapEventType.REJECTED:
756
- }
757
- };
758
- const handleConnectEvent = (event) => {
759
- switch (event.detail.type) {
760
- case ConnectEventType.SUCCESS: {
761
- const eventData = event.detail.data;
762
- connectLoaderDispatch({
763
- payload: {
764
- type: ConnectLoaderActions.SET_PROVIDER,
765
- provider: eventData.provider,
766
- },
767
- });
768
- if (nextView.current !== false) {
769
- setView(nextView.current);
770
- nextView.current = false;
771
- }
772
- break;
773
- }
774
- case ConnectEventType.FAILURE:
775
- case ConnectEventType.CLOSE_WIDGET:
776
- default:
777
- onCloseWidget();
778
- break;
779
- }
780
- };
781
- useEffect(() => {
782
- // Handle the connect event when switching networks
783
- eventTarget.addEventListener(IMTBLWidgetEvents.IMTBL_CONNECT_WIDGET_EVENT, handleConnectEvent);
784
- // Handle the other widget events
785
- eventTarget.addEventListener(IMTBLWidgetEvents.IMTBL_BRIDGE_WIDGET_EVENT, handleCustomEvent);
786
- eventTarget.addEventListener(IMTBLWidgetEvents.IMTBL_SWAP_WIDGET_EVENT, handleCustomEvent);
787
- // Remove the custom event listener when the component unmounts
788
- return () => {
789
- eventTarget.removeEventListener(IMTBLWidgetEvents.IMTBL_CONNECT_WIDGET_EVENT, handleConnectEvent);
790
- eventTarget.removeEventListener(IMTBLWidgetEvents.IMTBL_BRIDGE_WIDGET_EVENT, handleCustomEvent);
791
- eventTarget.removeEventListener(IMTBLWidgetEvents.IMTBL_SWAP_WIDGET_EVENT, handleCustomEvent);
792
- };
793
- }, []);
794
- useEffect(() => {
795
- eventTargetDispatch({
796
- payload: {
797
- type: EventTargetActions.SET_EVENT_TARGET,
798
- eventTarget,
799
- },
800
- });
801
- }, [checkout]);
802
- return (jsxs(EventTargetContext.Provider, { value: eventTargetReducerValues, children: [view === FundingRouteExecuteViews.LOADING && (jsx(LoadingView, { loadingText: t('views.FUND_WITH_SMART_CHECKOUT.loading.checkingBalances') })), view === FundingRouteExecuteViews.EXECUTE_BRIDGE && (jsx(BridgeWidget, { ...bridgeParams, config: config, checkout: checkout })), view === FundingRouteExecuteViews.EXECUTE_SWAP && (jsx(SwapWidget, { ...swapParams, config: config })), view === FundingRouteExecuteViews.SWITCH_NETWORK_ETH && (jsx(ConnectWidget, { config: config, targetLayer: ConnectTargetLayer.LAYER1, web3Provider: provider, checkout: checkout, deepLink: ConnectWidgetViews.SWITCH_NETWORK })), view === FundingRouteExecuteViews.SWITCH_NETWORK_ZKEVM && (jsx(ConnectWidget, { config: config, targetLayer: ConnectTargetLayer.LAYER2, web3Provider: provider, checkout: checkout, deepLink: ConnectWidgetViews.SWITCH_NETWORK }))] }));
803
- }
804
-
805
- // Taken from packages/checkout/widgets-lib/src/widgets/wallet/components/NetworkMenu/NetworkMenu.tsx
806
- const networkIcon = {
807
- [ChainId.IMTBL_ZKEVM_DEVNET]: 'Immutable',
808
- [ChainId.IMTBL_ZKEVM_MAINNET]: 'Immutable',
809
- [ChainId.IMTBL_ZKEVM_TESTNET]: 'Immutable',
810
- [ChainId.ETHEREUM]: 'EthToken',
811
- [ChainId.SEPOLIA]: 'EthToken',
812
- };
813
- const logoColour = {
814
- [ChainId.IMTBL_ZKEVM_DEVNET]: 'base.color.text.link.primary',
815
- [ChainId.IMTBL_ZKEVM_TESTNET]: 'base.color.text.link.primary',
816
- [ChainId.IMTBL_ZKEVM_MAINNET]: 'base.color.text.link.primary',
817
- [ChainId.ETHEREUM]: 'base.color.accent.5',
818
- [ChainId.SEPOLIA]: 'base.color.accent.5',
819
- };
820
- function FundingRouteMenuItem({ onClick, fundingRoute, toggleVisible, selected, size = 'small', }) {
821
- const { t } = useTranslation();
822
- const firstFundingStep = fundingRoute.steps[0];
823
- const { cryptoFiatState } = useContext(CryptoFiatContext);
824
- const { isPassportWallet } = useSaleContext();
825
- const [feesUsd, setFeesUsd] = useState(undefined);
826
- const [usdBalance, setUsdBalance] = useState(undefined);
827
- useEffect(() => {
828
- if (!cryptoFiatState.conversions) {
829
- return;
830
- }
831
- try {
832
- setFeesUsd(fundingRouteFees(fundingRoute, cryptoFiatState.conversions));
833
- setUsdBalance(calculateCryptoToFiat(firstFundingStep.fundingItem.userBalance.formattedBalance, firstFundingStep.fundingItem.token.symbol, cryptoFiatState.conversions));
834
- }
835
- catch {
836
- setFeesUsd(undefined);
837
- setUsdBalance(undefined);
838
- }
839
- }, [cryptoFiatState, fundingRoute]);
840
- const networkLabel = () => (jsxs(HorizontalMenu.Button, { rc: jsx("a", {}), sx: {
841
- pointerEvents: 'none',
842
- cursor: 'default',
843
- height: '100%',
844
- marginLeft: 'base.spacing.x2',
845
- fontSize: 'base.text.body.xxSmall.regular.fontSize',
846
- fontWeight: 'base.text.body.xxSmall.regular.fontWeight',
847
- color: 'base.color.brand.4',
848
- paddingLeft: 'base.spacing.x2',
849
- paddingRight: 'base.spacing.x2',
850
- }, size: "small", children: [jsx(Button.Icon, { icon: networkIcon[firstFundingStep.chainId], sx: {
851
- width: '14px',
852
- fill: logoColour[firstFundingStep.chainId],
853
- } }), getChainNameById(firstFundingStep.chainId)] }));
854
- return (jsxs(MenuItem, { emphasized: true, testId: "funding-route-menu-item", onClick: onClick, selected: selected, size: size, children: [toggleVisible && jsx(MenuItem.IntentIcon, { icon: "ChevronExpand" }), jsx(MenuItem.FramedIcon, { icon: "Coins", circularFrame: true }), jsx(MenuItem.PriceDisplay, { use: jsx(Heading, { size: "xSmall" }), fiatAmount: `${t('views.FUND_WITH_SMART_CHECKOUT.currency.usdEstimate')}${usdBalance}`, price: tokenValueFormat(firstFundingStep.fundingItem.userBalance.formattedBalance) }), jsxs(MenuItem.Label, { sx: { display: 'flex', wordBreak: 'default' }, children: [firstFundingStep.fundingItem.token.symbol, isPassportWallet ? null : networkLabel()] }), jsxs(MenuItem.Caption, { children: ["Fees \u2248 USD $", feesUsd] })] }));
855
- }
856
-
857
- function FundingRouteDrawer({ visible, onCloseDrawer, fundingRoutes, activeFundingRouteIndex, }) {
858
- const { t } = useTranslation();
859
- const onClickMenuItem = (selectedFundingRouteIndex) => {
860
- onCloseDrawer(selectedFundingRouteIndex);
861
- };
862
- return (jsx(Drawer, { size: "full", onCloseDrawer: () => onCloseDrawer(activeFundingRouteIndex), visible: visible, showHeaderBar: true, headerBarTitle: t('views.FUND_WITH_SMART_CHECKOUT.fundingRouteDrawer.header'), children: jsx(Drawer.Content, { children: fundingRoutes.map((fundingRoute, i) => (jsx(FundingRouteMenuItem, { onClick: () => onClickMenuItem(i), fundingRoute: fundingRoute, selected: activeFundingRouteIndex === i, size: "medium" }, fundingRoute.steps[0].fundingItem.type + fundingRoute.steps[0].fundingItem.token))) }) }));
863
- }
864
-
865
- function PurchaseMenuItem({ fundingRoute, collectionName }) {
866
- const { t } = useTranslation();
867
- const { items } = useSaleContext();
868
- const firstItem = items[0];
869
- const firstFundingStep = fundingRoute.steps[0];
870
- const { cryptoFiatState } = useContext(CryptoFiatContext);
871
- const [usdPurchaseAmount, setUsdPurchaseAmount] = useState(undefined);
872
- useEffect(() => {
873
- if (!cryptoFiatState.conversions) {
874
- return;
875
- }
876
- try {
877
- setUsdPurchaseAmount(calculateCryptoToFiat(firstFundingStep.fundingItem.fundsRequired.formattedAmount, firstFundingStep.fundingItem.token.symbol, cryptoFiatState.conversions));
878
- }
879
- catch {
880
- setUsdPurchaseAmount(undefined);
881
- }
882
- }, [cryptoFiatState, fundingRoute]);
883
- return (jsxs(MenuItem, { emphasized: true, testId: "funding-route-purchase-item", size: "medium", children: [jsx(MenuItem.FramedImage, { imageUrl: firstItem?.image }), jsx(MenuItem.PriceDisplay, { use: jsx(Heading, { size: "xSmall" }), price: `${firstFundingStep?.fundingItem.token.symbol}
884
- ${tokenValueFormat(firstFundingStep.fundingItem.fundsRequired.formattedAmount)}`, fiatAmount: `${t('views.FUND_WITH_SMART_CHECKOUT.currency.usdEstimate')}${usdPurchaseAmount}` }), jsxs(MenuItem.Label, { children: [firstItem?.name, firstItem?.qty > 1 ? ` x${firstItem.qty}` : null] }), jsx(MenuItem.Caption, { children: collectionName })] }, firstItem?.name));
885
- }
886
-
887
- function FundingRouteSelect({ fundingRoutes, collectionName, onFundingRouteSelected, }) {
888
- const { t } = useTranslation();
889
- const [smartCheckoutDrawerVisible, setSmartCheckoutDrawerVisible] = useState(false);
890
- const [activeFundingRouteIndex, setActiveFundingRouteIndex] = useState(0);
891
- const { viewDispatch } = useContext(ViewContext);
892
- const { eventTargetState: { eventTarget }, } = useContext(EventTargetContext);
893
- const { goBackToPaymentMethods } = useSaleContext();
894
- const onClickContinue = () => {
895
- onFundingRouteSelected(fundingRoutes[activeFundingRouteIndex]);
896
- viewDispatch({
897
- payload: {
898
- type: ViewActions.UPDATE_VIEW,
899
- view: {
900
- type: SaleWidgetViews.FUND_WITH_SMART_CHECKOUT,
901
- subView: FundWithSmartCheckoutSubViews.FUNDING_ROUTE_EXECUTE,
902
- },
903
- },
904
- });
905
- };
906
- const closeDrawer = (selectedFundingRouteIndex) => {
907
- setActiveFundingRouteIndex(selectedFundingRouteIndex);
908
- setSmartCheckoutDrawerVisible(false);
909
- };
910
- const onSmartCheckoutDropdownClick = () => {
911
- setSmartCheckoutDrawerVisible(true);
912
- };
913
- return (jsxs(SimpleLayout, { testId: "funding-route-select", header: (jsx(HeaderNavigation, { onCloseButtonClick: () => sendSaleWidgetCloseEvent(eventTarget) })), footer: jsx(FooterLogo, {}), children: [jsxs(Box, { sx: {
914
- display: 'flex',
915
- flexDirection: 'column',
916
- paddingX: 'base.spacing.x2',
917
- paddingY: 'base.spacing.x8',
918
- rowGap: 'base.spacing.x4',
919
- height: '100%',
920
- }, children: [jsx(Heading, { size: "small", children: t('views.FUND_WITH_SMART_CHECKOUT.fundingRouteSelect.heading') }), fundingRoutes.length === 0
921
- ? [
922
- jsx(Body, { size: "small", children: t('views.FUND_WITH_SMART_CHECKOUT.fundingRouteSelect.noRoutesAvailable') }, "noRoutesAvailableText"),
923
- jsx(Button, { variant: "tertiary", children: t('views.FUND_WITH_SMART_CHECKOUT.fundingRouteSelect.payWithCard') }, "payWithCardButton"),
924
- ]
925
- : [
926
- jsx(FundingRouteMenuItem, { "data-testid": "funding-route-select-selected-route", onClick: fundingRoutes.length > 1
927
- ? onSmartCheckoutDropdownClick
928
- : () => { }, fundingRoute: fundingRoutes[activeFundingRouteIndex], selected: true, toggleVisible: fundingRoutes.length > 1 }, "selectedFundingRouteMenuItem"),
929
- jsx(PurchaseMenuItem, { fundingRoute: fundingRoutes[activeFundingRouteIndex], collectionName: collectionName }, "purchaseMenuItem"),
930
- jsx(Button, { sx: { mt: 'auto' }, variant: "primary", onClick: onClickContinue, children: t('views.FUND_WITH_SMART_CHECKOUT.fundingRouteSelect.continue') }, "continueButton"),
931
- jsx(Button, { variant: "tertiary", onClick: () => goBackToPaymentMethods(), children: t('views.FUND_WITH_SMART_CHECKOUT.fundingRouteSelect.payWithCardInstead') }, "payWithCardButton"),
932
- ]] }), jsx(FundingRouteDrawer, { visible: smartCheckoutDrawerVisible, onCloseDrawer: closeDrawer, fundingRoutes: fundingRoutes, activeFundingRouteIndex: activeFundingRouteIndex })] }));
933
- }
934
-
935
- const toStringifyTransactions = (transactions) => transactions
936
- .map(({ method, hash }) => `${method}: ${hash}`).join(' | ');
937
- const toPascalCase = (str) => str
938
- .split('_')
939
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
940
- .join('');
941
-
942
- const useSaleEvent = () => {
943
- const { track, page } = useAnalytics();
944
- const { recipientAddress: userId, recipientEmail: email, signResponse, paymentMethod, } = useSaleContext();
945
- const { eventTargetState: { eventTarget }, } = useContext(EventTargetContext);
946
- const defaultView = SaleWidgetViews.PAYMENT_METHODS;
947
- const commonProps = {
948
- location: 'web',
949
- userJourney: UserJourney.SALE,
950
- };
951
- const userProps = {
952
- email,
953
- userId,
954
- paymentMethod,
955
- };
956
- const orderProps = {
957
- amount: signResponse?.order.totalAmount,
958
- currency: signResponse?.order.currency.name,
959
- };
960
- const sendCloseEvent = (screen = defaultView, controlType = 'Button', action = 'Pressed') => {
961
- track({
962
- ...commonProps,
963
- screen: toPascalCase(screen),
964
- control: 'Close',
965
- controlType,
966
- action,
967
- extras: {
968
- ...userProps,
969
- },
970
- });
971
- sendSaleWidgetCloseEvent(eventTarget);
972
- };
973
- const sendSuccessEvent = (screen = defaultView, transactions = [], details) => {
974
- track({
975
- ...commonProps,
976
- screen: toPascalCase(screen),
977
- control: 'Success',
978
- controlType: 'Event',
979
- action: 'Succeeded',
980
- extras: {
981
- ...details,
982
- ...userProps,
983
- transactions: toStringifyTransactions(transactions),
984
- ...orderProps,
985
- paymentMethod,
986
- },
987
- });
988
- sendSaleSuccessEvent(eventTarget, paymentMethod, transactions);
989
- };
990
- const sendFailedEvent = (reason, error, transactions = [], screen = defaultView) => {
991
- track({
992
- ...commonProps,
993
- screen: toPascalCase(screen),
994
- control: 'Fail',
995
- controlType: 'Event',
996
- action: 'Failed',
997
- extras: {
998
- transactions: toStringifyTransactions(transactions),
999
- ...error,
1000
- ...orderProps,
1001
- ...userProps,
1002
- paymentMethod,
1003
- reason,
1004
- },
1005
- });
1006
- sendSaleFailedEvent(eventTarget, reason, error, paymentMethod, transactions);
1007
- };
1008
- const sendTransactionSuccessEvent = (transaction) => {
1009
- sendSaleTransactionSuccessEvent(eventTarget, paymentMethod, [transaction]);
1010
- };
1011
- const sendSelectedPaymentMethod = (type, screen) => {
1012
- track({
1013
- ...commonProps,
1014
- screen: toPascalCase(screen),
1015
- control: 'Select',
1016
- controlType: 'MenuItem',
1017
- extras: {
1018
- paymentMethod: type,
1019
- },
1020
- });
1021
- sendSalePaymentMethodEvent(eventTarget, type);
1022
- };
1023
- const sendPageView = (screen, data) => {
1024
- page({
1025
- ...commonProps,
1026
- screen: toPascalCase(screen),
1027
- action: 'Viewed',
1028
- ...data,
1029
- });
1030
- };
1031
- const sendOrderCreated = (screen, details) => {
1032
- track({
1033
- ...commonProps,
1034
- screen: toPascalCase(screen),
1035
- control: 'OrderCreated',
1036
- controlType: 'Event',
1037
- extras: {
1038
- ...details,
1039
- },
1040
- });
1041
- };
1042
- return {
1043
- track,
1044
- page,
1045
- sendPageView,
1046
- sendCloseEvent,
1047
- sendSuccessEvent,
1048
- sendFailedEvent,
1049
- sendTransactionSuccessEvent,
1050
- sendOrderCreated,
1051
- sendSelectedPaymentMethod,
1052
- };
1053
- };
1054
-
1055
- function FundWithSmartCheckout({ subView }) {
1056
- const { t } = useTranslation();
1057
- const { sendPageView } = useSaleEvent();
1058
- const { viewDispatch } = useContext(ViewContext);
1059
- const [selectedFundingRoute, setSelectedFundingRoute] = useState(undefined);
1060
- const [fundingRouteStepIndex, setFundingRouteStepIndex] = useState(0);
1061
- const { querySmartCheckout, fundingRoutes, smartCheckoutResult, collectionName, } = useSaleContext();
1062
- const { cryptoFiatDispatch } = useContext(CryptoFiatContext);
1063
- const smartCheckoutLoading = useRef(false);
1064
- const onFundingRouteSelected = (fundingRoute) => {
1065
- setSelectedFundingRoute(fundingRoute);
1066
- };
1067
- useEffect(() => {
1068
- if (subView === FundWithSmartCheckoutSubViews.INIT && !smartCheckoutLoading.current) {
1069
- smartCheckoutLoading.current = true;
1070
- try {
1071
- querySmartCheckout();
1072
- }
1073
- finally {
1074
- smartCheckoutLoading.current = false;
1075
- }
1076
- }
1077
- }, [subView]);
1078
- useEffect(() => {
1079
- if (!cryptoFiatDispatch || !smartCheckoutResult)
1080
- return;
1081
- const tokenSymbols = smartCheckoutTokensList(smartCheckoutResult);
1082
- cryptoFiatDispatch({
1083
- payload: {
1084
- type: CryptoFiatActions.SET_TOKEN_SYMBOLS,
1085
- tokenSymbols,
1086
- },
1087
- });
1088
- }, [cryptoFiatDispatch, smartCheckoutResult]);
1089
- const fundingRouteStep = useMemo(() => {
1090
- if (!selectedFundingRoute) {
1091
- return undefined;
1092
- }
1093
- return selectedFundingRoute.steps[fundingRouteStepIndex];
1094
- }, [selectedFundingRoute, fundingRouteStepIndex]);
1095
- const onFundingRouteExecuted = () => {
1096
- if (!selectedFundingRoute) {
1097
- return;
1098
- }
1099
- if (fundingRouteStepIndex === selectedFundingRoute.steps.length - 1) {
1100
- setFundingRouteStepIndex(0);
1101
- setSelectedFundingRoute(undefined);
1102
- // ! Recurse with SC to trigger another query and confirm they have the required balance
1103
- viewDispatch({
1104
- payload: {
1105
- type: ViewActions.UPDATE_VIEW,
1106
- view: {
1107
- type: SaleWidgetViews.FUND_WITH_SMART_CHECKOUT,
1108
- subView: FundWithSmartCheckoutSubViews.INIT,
1109
- },
1110
- },
1111
- });
1112
- }
1113
- else {
1114
- setFundingRouteStepIndex(fundingRouteStepIndex + 1);
1115
- }
1116
- };
1117
- useEffect(() => sendPageView(SaleWidgetViews.FUND_WITH_SMART_CHECKOUT, {
1118
- subView,
1119
- ...(!!fundingRouteStep && { fundingStep: fundingRouteStep.type }),
1120
- }), []);
1121
- return (jsxs(Box, { children: [subView === FundWithSmartCheckoutSubViews.INIT && (jsx(LoadingView, { loadingText: t('views.FUND_WITH_SMART_CHECKOUT.loading.checkingBalances') })), subView === FundWithSmartCheckoutSubViews.FUNDING_ROUTE_SELECT && (jsx(FundingRouteSelect, { fundingRoutes: fundingRoutes, collectionName: collectionName, onFundingRouteSelected: onFundingRouteSelected })), subView === FundWithSmartCheckoutSubViews.FUNDING_ROUTE_EXECUTE && (jsx(FundingRouteExecute, { onFundingRouteExecuted: onFundingRouteExecuted, fundingRouteStep: fundingRouteStep }))] }));
1122
- }
1123
-
1124
- const MAX_GAS_LIMIT = '30000000';
1125
- // TODO: Move to common config file inside Checkout SDK while refactoring onRamp
1126
- // const { baseUrl, apiKey } = checkout.fiatExchangeConfig('transak')
1127
- const TRANSAK_API_BASE_URL = {
1128
- [Environment.SANDBOX]: 'https://global-stg.transak.com',
1129
- [Environment.PRODUCTION]: 'https://global.transak.com/',
1130
- };
1131
- const useTransakIframe = (props) => {
1132
- const { type, ...transakParams } = props;
1133
- const [iframeSrc, setIframeSrc] = useState('');
1134
- // TODO: Get transak config from checkout SDK
1135
- // const { checkout, provider } = connectLoaderState;
1136
- // const { config } = checkout.fiatExchangeConfig('transak');
1137
- // const { environment, apiKey, network } = config
1138
- const environment = 'sandbox';
1139
- const apiKey = 'd14b44fb-0f84-4db5-affb-e044040d724b';
1140
- const network = 'immutablezkevm';
1141
- const getNFTCheckoutURL = useCallback(() => {
1142
- const { calldata, nftData: nfts, estimatedGasLimit, ...restTransakParams } = transakParams;
1143
- // FIXME: defaulting to first nft in the list
1144
- // as transak currently only supports on nft at a time
1145
- const nftData = nfts?.slice(0, 1);
1146
- const gasLimit = estimatedGasLimit > 0 ? estimatedGasLimit : MAX_GAS_LIMIT;
1147
- const params = {
1148
- apiKey,
1149
- network,
1150
- isNFT: 'true',
1151
- disableWalletAddressForm: 'true',
1152
- environment: 'STAGING',
1153
- calldata: btoa(String.fromCharCode.apply(null, pako.deflate(calldata))),
1154
- nftData: btoa(JSON.stringify(nftData)),
1155
- estimatedGasLimit: gasLimit.toString(),
1156
- ...restTransakParams,
1157
- themeColor: '0D0D0D',
1158
- };
1159
- const baseUrl = `${TRANSAK_API_BASE_URL[environment]}?`;
1160
- const queryParams = new URLSearchParams(params);
1161
- const widgetUrl = `${baseUrl}${queryParams.toString()}`;
1162
- return widgetUrl;
1163
- }, [props]);
1164
- useEffect(() => {
1165
- setIframeSrc(getNFTCheckoutURL());
1166
- }, [props]);
1167
- return { iframeSrc };
1168
- };
1169
-
1170
- function TransakIframe(props) {
1171
- const { id, type, email, walletAddress, isPassportWallet, nftData, calldata, cryptoCurrencyCode, estimatedGasLimit, exchangeScreenTitle, smartContractAddress, partnerOrderId, onOpen, onInit, onOrderCreated, onOrderProcessing, onOrderCompleted, onOrderFailed, onFailedToLoad, failedToLoadTimeoutInMs, loadingText, } = props;
1172
- const iframeRef = useRef(null);
1173
- const { onLoad, initialised } = useTransakEvents({
1174
- userJourney: UserJourney.SALE,
1175
- ref: iframeRef,
1176
- email,
1177
- userId: walletAddress,
1178
- isPassportWallet,
1179
- onOpen,
1180
- onOrderCreated,
1181
- onOrderProcessing,
1182
- onOrderCompleted,
1183
- onOrderFailed,
1184
- onInit,
1185
- failedToLoadTimeoutInMs,
1186
- onFailedToLoad,
1187
- });
1188
- const { iframeSrc } = useTransakIframe({
1189
- type,
1190
- nftData,
1191
- calldata,
1192
- cryptoCurrencyCode,
1193
- estimatedGasLimit,
1194
- exchangeScreenTitle,
1195
- smartContractAddress,
1196
- email,
1197
- walletAddress,
1198
- partnerOrderId,
1199
- });
1200
- return (jsxs(Fragment, { children: [!initialised && (jsx(CenteredBoxContent, { testId: "loading-view", children: jsx(LoadingBox, { loadingText: loadingText }) })), jsx("iframe", { ref: iframeRef, id: id, src: iframeSrc, title: "Transak-Iframe", allow: "camera;microphone;fullscreen;payment", style: {
1201
- height: '100%',
1202
- width: '100%',
1203
- border: 'none',
1204
- position: 'absolute',
1205
- top: 0,
1206
- left: 0,
1207
- opacity: initialised ? 1 : 0,
1208
- transition: 'opacity 0.5s ease-out',
1209
- }, onLoad: onLoad, onError: () => onFailedToLoad?.() })] }));
1210
- }
1211
-
1212
- function WithCard(props) {
1213
- const { t } = useTranslation();
1214
- const { onInit, onOpen, onOrderCreated, onOrderProcessing, onOrderCompleted, onOrderFailed, } = props;
1215
- const { recipientEmail, recipientAddress, isPassportWallet, signResponse, goToErrorView, } = useSaleContext();
1216
- const executeTxn = signResponse?.transactions.find((txn) => txn.methodCall.startsWith('execute'));
1217
- if (!signResponse || !executeTxn) {
1218
- return null;
1219
- }
1220
- const nftData = useMemo(() => signResponse.order.products.map((product) => ({
1221
- collectionAddress: product.collectionAddress,
1222
- imageURL: product.image,
1223
- nftName: product.name,
1224
- price: product.amount,
1225
- quantity: product.qty,
1226
- tokenID: product.tokenId,
1227
- nftType: product.contractType || 'ERC721',
1228
- })), [signResponse]);
1229
- const onFailedToLoad = () => {
1230
- goToErrorView(SaleErrorTypes.TRANSACTION_FAILED);
1231
- };
1232
- return (jsx(TransakIframe, { id: "transak-iframe", type: "nft-checkout", email: recipientEmail, walletAddress: recipientAddress, isPassportWallet: isPassportWallet, exchangeScreenTitle: t('views.PAY_WITH_CARD.screenTitle'), loadingText: t('views.PAY_WITH_CARD.loading'), nftData: nftData, calldata: executeTxn.rawData, cryptoCurrencyCode: signResponse.order.currency.name, estimatedGasLimit: executeTxn.gasEstimate, smartContractAddress: executeTxn.tokenAddress, partnerOrderId: executeTxn.params.reference, onInit: onInit, onOpen: onOpen, onOrderCreated: onOrderCreated, onOrderProcessing: onOrderProcessing, onOrderCompleted: onOrderCompleted, onOrderFailed: onOrderFailed, onFailedToLoad: onFailedToLoad }));
1233
- }
1234
-
1235
- function PayWithCard() {
1236
- const { sendPageView } = useSaleEvent();
1237
- const [initialised, setInitialised] = useState(false);
1238
- const { goBackToPaymentMethods, goToErrorView, goToSuccessView } = useSaleContext();
1239
- const { sendOrderCreated } = useSaleEvent();
1240
- const onInit = () => setInitialised(true);
1241
- const onOrderFailed = () => {
1242
- goToErrorView(SaleErrorTypes.TRANSAK_FAILED);
1243
- };
1244
- const onOrderProcessing = (data = {}) => {
1245
- const { id: orderId, status: orderStatus, cryptoAmount, cryptoCurrency, fiatAmount, fiatAmountInUsd, amountPaid, totalFeeInFiat, paymentOptionId: paymentOption, userId, userKycType, walletAddress, nftAssetInfo, } = data;
1246
- const { nftDataBase64, quantity } = nftAssetInfo || {};
1247
- goToSuccessView({
1248
- orderId,
1249
- orderStatus,
1250
- cryptoAmount,
1251
- cryptoCurrency,
1252
- fiatAmount,
1253
- fiatAmountInUsd,
1254
- amountPaid,
1255
- totalFeeInFiat,
1256
- paymentOption,
1257
- userId,
1258
- userKycType,
1259
- walletAddress,
1260
- nftDataBase64,
1261
- quantity,
1262
- });
1263
- };
1264
- const onOrderCreated = (data = {}) => {
1265
- const { id: orderId, status: orderStatus, cryptoAmount, cryptoCurrency, fiatAmount, fiatAmountInUsd, amountPaid, totalFeeInFiat, paymentOptionId: paymentOption, userId, userKycType, walletAddress, nftAssetInfo, } = data;
1266
- const { nftDataBase64, quantity } = nftAssetInfo || {};
1267
- sendOrderCreated(SaleWidgetViews.PAY_WITH_CARD, {
1268
- orderId,
1269
- orderStatus,
1270
- cryptoAmount,
1271
- cryptoCurrency,
1272
- fiatAmount,
1273
- fiatAmountInUsd,
1274
- amountPaid,
1275
- totalFeeInFiat,
1276
- paymentOption,
1277
- userId,
1278
- userKycType,
1279
- walletAddress,
1280
- nftDataBase64,
1281
- quantity,
1282
- });
1283
- };
1284
- useEffect(() => sendPageView(SaleWidgetViews.PAY_WITH_CARD), []);
1285
- return (jsx(SimpleLayout, { header: initialised && (jsx(HeaderNavigation, { onCloseButtonClick: () => goBackToPaymentMethods() })), footer: jsx(FooterLogo, { hideLogo: initialised }), children: jsx(Box, { style: {
1286
- display: 'block',
1287
- position: 'relative',
1288
- maxWidth: '420px',
1289
- height: '565px',
1290
- borderRadius: '1%',
1291
- overflow: 'hidden',
1292
- margin: '0 auto',
1293
- width: '100%',
1294
- }, children: jsx(WithCard, { onInit: onInit, onOrderFailed: onOrderFailed, onOrderCompleted: onOrderProcessing, onOrderCreated: onOrderCreated, onOrderProcessing: onOrderProcessing }) }) }));
1295
- }
1296
-
1297
- function PayWithCoins() {
1298
- const { t } = useTranslation();
1299
- const processing = useRef(false);
1300
- const { sendPageView, sendTransactionSuccessEvent, sendFailedEvent } = useSaleEvent();
1301
- const { execute, signResponse, executeResponse, goToSuccessView, } = useSaleContext();
1302
- const expectedTxns = signResponse?.transactions.length || 0;
1303
- const executedTxns = executeResponse?.transactions.length || 0;
1304
- let loadingText = t('views.PAYMENT_METHODS.loading.ready');
1305
- if (signResponse !== undefined) {
1306
- loadingText = t('views.PAYMENT_METHODS.loading.confirm');
1307
- }
1308
- else if (executedTxns > 0 && executedTxns === expectedTxns) {
1309
- loadingText = t('views.PAYMENT_METHODS.loading.processing');
1310
- }
1311
- if (signResponse !== undefined) {
1312
- loadingText = `${loadingText} ${executedTxns}/${expectedTxns}`;
1313
- }
1314
- const sendTransaction = async () => {
1315
- execute(signResponse, (txn) => {
1316
- sendTransactionSuccessEvent(txn);
1317
- }, (error, txns) => {
1318
- sendFailedEvent(error.toString(), error, txns);
1319
- });
1320
- };
1321
- useEffect(() => {
1322
- if (signResponse !== undefined && processing.current === false) {
1323
- processing.current = true;
1324
- sendTransaction();
1325
- }
1326
- }, [signResponse]);
1327
- useEffect(() => {
1328
- if (executeResponse?.done === true) {
1329
- goToSuccessView();
1330
- }
1331
- }, [executeResponse]);
1332
- useEffect(() => sendPageView(SaleWidgetViews.PAY_WITH_COINS), []);
1333
- return jsx(LoadingView, { loadingText: loadingText, showFooterLogo: true });
1334
- }
1335
-
1336
- function PaymentOption(props) {
1337
- const { t } = useTranslation();
1338
- const { type, onClick, disabled } = props;
1339
- // const optionText = options[type];
1340
- const icon = {
1341
- [SalePaymentTypes.CRYPTO]: 'Coins',
1342
- [SalePaymentTypes.FIAT]: 'BankCard',
1343
- };
1344
- const handleClick = () => onClick(type);
1345
- return (jsxs(MenuItem, { size: "medium", emphasized: true, onClick: disabled ? undefined : handleClick, sx: {
1346
- ...(disabled && { opacity: '0.5', cursor: 'not-allowed' }),
1347
- marginBottom: 'base.spacing.x1',
1348
- }, disabled: disabled, children: [jsx(MenuItem.FramedIcon, { icon: icon[type] }), jsx(MenuItem.Label, { size: "medium", children: t(`views.PAYMENT_METHODS.options.${type}.heading`) }), !disabled && jsx(MenuItem.IntentIcon, {}), jsx(MenuItem.Caption, { children: t(`views.PAYMENT_METHODS.options.${type}.${disabled ? 'disabledCaption' : 'caption'}`) })] }));
1349
- }
1350
-
1351
- const defaultPaymentOptions = [
1352
- SalePaymentTypes.FIAT,
1353
- SalePaymentTypes.CRYPTO,
1354
- ];
1355
- function PaymentOptions(props) {
1356
- const { disabledOptions = [], onClick } = props;
1357
- return (jsx(Box, { testId: "payment-options-list", sx: {
1358
- width: '100%',
1359
- display: 'flex',
1360
- flexDirection: 'column',
1361
- justifyContent: 'center',
1362
- alignItems: 'flex-start',
1363
- }, children: defaultPaymentOptions.map((type) => (jsx(PaymentOption, { disabled: disabledOptions.includes(type), onClick: onClick, type: type }, type))) }));
1364
- }
1365
-
1366
- function PaymentMethods() {
1367
- const { t } = useTranslation();
1368
- const { viewState, viewDispatch } = useContext(ViewContext);
1369
- const { sign, amount, fromTokenAddress: tokenAddress, goToErrorView, paymentMethod, setPaymentMethod, disabledPaymentTypes, invalidParameters, } = useSaleContext();
1370
- const { sendPageView, sendCloseEvent, sendSelectedPaymentMethod } = useSaleEvent();
1371
- const { eventTargetState: { eventTarget } } = useContext(EventTargetContext);
1372
- const handleOptionClick = (type) => setPaymentMethod(type);
1373
- useEffect(() => {
1374
- if (paymentMethod) {
1375
- sendSelectedPaymentMethod(paymentMethod, SaleWidgetViews.PAYMENT_METHODS);
1376
- }
1377
- if (paymentMethod === SalePaymentTypes.FIAT) {
1378
- sign(paymentMethod, () => {
1379
- viewDispatch({
1380
- payload: {
1381
- type: ViewActions.UPDATE_VIEW,
1382
- view: {
1383
- type: SaleWidgetViews.PAY_WITH_CARD,
1384
- },
1385
- },
1386
- });
1387
- });
1388
- viewDispatch({
1389
- payload: {
1390
- type: ViewActions.UPDATE_VIEW,
1391
- view: {
1392
- type: SharedViews.LOADING_VIEW,
1393
- data: { loadingText: t('views.PAYMENT_METHODS.loading.ready') },
1394
- },
1395
- },
1396
- });
1397
- }
1398
- if (paymentMethod === SalePaymentTypes.CRYPTO) {
1399
- viewDispatch({
1400
- payload: {
1401
- type: ViewActions.UPDATE_VIEW,
1402
- view: {
1403
- type: SaleWidgetViews.FUND_WITH_SMART_CHECKOUT,
1404
- subView: FundWithSmartCheckoutSubViews.INIT,
1405
- },
1406
- },
1407
- });
1408
- }
1409
- }, [paymentMethod]);
1410
- const insufficientCoinsBanner = (jsx(Box, { sx: { paddingX: 'base.spacing.x2' }, children: jsxs(Banner, { children: [jsx(Banner.Icon, { icon: "InformationCircle" }), jsxs(Banner.Caption, { children: [t('views.PAYMENT_METHODS.insufficientCoinsBanner.caption'), jsx(Link, { sx: { mx: 'base.spacing.x1' }, onClick: () => orchestrationEvents
1411
- .sendRequestBridgeEvent(eventTarget, IMTBLWidgetEvents.IMTBL_SALE_WIDGET_EVENT, {
1412
- amount, tokenAddress,
1413
- }), children: t('views.PAYMENT_METHODS.insufficientCoinsBanner.captionCTA') }), t('views.PAYMENT_METHODS.insufficientCoinsBanner.captionEnd')] })] }) }));
1414
- useEffect(() => sendPageView(SaleWidgetViews.PAYMENT_METHODS), []);
1415
- useEffect(() => {
1416
- if (!invalidParameters)
1417
- return;
1418
- goToErrorView(SaleErrorTypes.INVALID_PARAMETERS);
1419
- }, [invalidParameters]);
1420
- return (jsx(SimpleLayout, { testId: "payment-methods", header: (jsx(HeaderNavigation, { onCloseButtonClick: () => sendCloseEvent(SaleWidgetViews.PAYMENT_METHODS) })), footer: jsx(FooterLogo, {}), children: jsxs(Box, { sx: {
1421
- display: 'flex',
1422
- flexDirection: 'column',
1423
- paddingX: 'base.spacing.x2',
1424
- paddingY: 'base.spacing.x8',
1425
- rowGap: 'base.spacing.x4',
1426
- }, children: [jsx(Heading, { size: "small", sx: {
1427
- paddingX: 'base.spacing.x4',
1428
- }, children: t('views.PAYMENT_METHODS.header.heading') }), jsx(Box, { sx: { paddingX: 'base.spacing.x2' }, children: jsx(PaymentOptions, { disabledOptions: disabledPaymentTypes, onClick: handleOptionClick }) }), viewState.view.data?.showInsufficientCoinsBanner ? insufficientCoinsBanner : null] }) }));
1429
- }
1430
-
1431
- function SaleErrorView({ errorType = SaleErrorTypes.DEFAULT, transactionHash, blockExplorerLink, biomeTheme, }) {
1432
- const { t } = useTranslation();
1433
- const { goBackToPaymentMethods } = useSaleContext();
1434
- const { eventTargetState: { eventTarget } } = useContext(EventTargetContext);
1435
- const closeWidget = () => {
1436
- sendSaleWidgetCloseEvent(eventTarget);
1437
- };
1438
- const errorHandlersConfig = {
1439
- [SaleErrorTypes.TRANSACTION_FAILED]: {
1440
- onActionClick: goBackToPaymentMethods,
1441
- onSecondaryActionClick: transactionHash ? () => {
1442
- window.open(blockExplorerLink);
1443
- } : closeWidget,
1444
- statusType: StatusType.FAILURE,
1445
- statusIconStyles: {
1446
- fill: biomeTheme.color.status.destructive.dim,
1447
- },
1448
- },
1449
- [SaleErrorTypes.SERVICE_BREAKDOWN]: {
1450
- onSecondaryActionClick: closeWidget,
1451
- statusType: StatusType.INFORMATION,
1452
- statusIconStyles: {
1453
- fill: biomeTheme.color.status.fatal.dim,
1454
- },
1455
- },
1456
- [SaleErrorTypes.PRODUCT_NOT_FOUND]: {
1457
- onSecondaryActionClick: closeWidget,
1458
- statusType: StatusType.INFORMATION,
1459
- statusIconStyles: {
1460
- fill: biomeTheme.color.status.fatal.dim,
1461
- },
1462
- },
1463
- [SaleErrorTypes.INSUFFICIENT_STOCK]: {
1464
- onSecondaryActionClick: closeWidget,
1465
- statusType: StatusType.INFORMATION,
1466
- statusIconStyles: {
1467
- fill: biomeTheme.color.status.fatal.dim,
1468
- },
1469
- },
1470
- [SaleErrorTypes.TRANSAK_FAILED]: {
1471
- onActionClick: goBackToPaymentMethods,
1472
- onSecondaryActionClick: closeWidget,
1473
- statusType: StatusType.INFORMATION,
1474
- },
1475
- [SaleErrorTypes.WALLET_FAILED]: {
1476
- onActionClick: goBackToPaymentMethods,
1477
- onSecondaryActionClick: closeWidget,
1478
- statusType: StatusType.INFORMATION,
1479
- statusIconStyles: {
1480
- fill: biomeTheme.color.status.fatal.dim,
1481
- },
1482
- },
1483
- [SaleErrorTypes.WALLET_REJECTED_NO_FUNDS]: {
1484
- onActionClick: goBackToPaymentMethods,
1485
- onSecondaryActionClick: closeWidget,
1486
- statusType: StatusType.INFORMATION,
1487
- },
1488
- [SaleErrorTypes.WALLET_REJECTED]: {
1489
- onActionClick: () => {
1490
- goBackToPaymentMethods(SalePaymentTypes.CRYPTO);
1491
- },
1492
- onSecondaryActionClick: closeWidget,
1493
- statusType: StatusType.INFORMATION,
1494
- },
1495
- [SaleErrorTypes.SMART_CHECKOUT_ERROR]: {
1496
- onActionClick: goBackToPaymentMethods,
1497
- onSecondaryActionClick: closeWidget,
1498
- statusType: StatusType.INFORMATION,
1499
- },
1500
- [SaleErrorTypes.SMART_CHECKOUT_EXECUTE_ERROR]: {
1501
- onActionClick: goBackToPaymentMethods,
1502
- onSecondaryActionClick: closeWidget,
1503
- statusType: StatusType.INFORMATION,
1504
- },
1505
- [SaleErrorTypes.INVALID_PARAMETERS]: {
1506
- onSecondaryActionClick: closeWidget,
1507
- statusType: StatusType.ALERT,
1508
- statusIconStyles: {
1509
- fill: biomeTheme.color.status.attention.dim,
1510
- transform: 'none',
1511
- },
1512
- },
1513
- [SaleErrorTypes.DEFAULT]: {
1514
- onActionClick: goBackToPaymentMethods,
1515
- onSecondaryActionClick: closeWidget,
1516
- statusType: StatusType.INFORMATION,
1517
- },
1518
- };
1519
- const getErrorViewProps = () => {
1520
- const handlers = errorHandlersConfig[errorType] || {};
1521
- const secondaryActionText = errorType === SaleErrorTypes.TRANSACTION_FAILED && transactionHash
1522
- ? t(`views.SALE_FAIL.errors.${SaleErrorTypes.DEFAULT}.secondaryAction`)
1523
- : t(`views.SALE_FAIL.errors.${errorType}.secondaryAction`);
1524
- return {
1525
- testId: 'fail-view',
1526
- statusText: t(`views.SALE_FAIL.errors.${errorType}.description`),
1527
- actionText: t(`views.SALE_FAIL.errors.${errorType}.primaryAction`),
1528
- onActionClick: handlers?.onActionClick,
1529
- secondaryActionText,
1530
- onSecondaryActionClick: handlers?.onSecondaryActionClick,
1531
- onCloseClick: closeWidget,
1532
- statusType: handlers.statusType,
1533
- statusIconStyles: {
1534
- transform: 'rotate(180deg)',
1535
- fill: biomeTheme.color.status.guidance.dim,
1536
- ...handlers.statusIconStyles,
1537
- },
1538
- };
1539
- };
1540
- return (jsx(StatusView, { ...getErrorViewProps() }));
1541
- }
1542
-
1543
- function SaleSuccessView({ data }) {
1544
- const { t } = useTranslation();
1545
- const { sendCloseEvent, sendSuccessEvent } = useSaleEvent();
1546
- const closeWidget = () => {
1547
- sendCloseEvent(SaleWidgetViews.SALE_SUCCESS);
1548
- };
1549
- return (jsx(StatusView, { statusText: t('views.SALE_SUCCESS.text'), actionText: t('views.SALE_SUCCESS.actionText'), onRenderEvent: () => {
1550
- const { transactions, ...rest } = data;
1551
- sendSuccessEvent(SaleWidgetViews.SALE_SUCCESS, transactions, rest);
1552
- }, onActionClick: () => closeWidget(), statusType: StatusType.SUCCESS, testId: "success-view" }));
1553
- }
1554
-
1555
- function SaleWidget(props) {
1556
- const { t } = useTranslation();
1557
- const { config, amount, items, environmentId, fromTokenAddress, collectionName, } = props;
1558
- const { connectLoaderState } = useContext(ConnectLoaderContext);
1559
- const { checkout, provider } = connectLoaderState;
1560
- const chainId = useRef();
1561
- const { theme } = config;
1562
- const biomeTheme = useMemo(() => widgetTheme(theme), [theme]);
1563
- const [viewState, viewDispatch] = useReducer(viewReducer, initialViewState);
1564
- const viewReducerValues = useMemo(() => ({ viewState, viewDispatch }), [viewState, viewDispatch]);
1565
- const loadingText = viewState.view.data?.loadingText
1566
- || t('views.LOADING_VIEW.text');
1567
- useEffect(() => {
1568
- if (!checkout || !provider)
1569
- return;
1570
- (async () => {
1571
- const network = await checkout.getNetworkInfo({ provider });
1572
- chainId.current = network.chainId;
1573
- })();
1574
- }, [checkout, provider]);
1575
- const mounted = useRef(false);
1576
- const onMount = useCallback(() => {
1577
- if (!checkout || !provider)
1578
- return;
1579
- if (!mounted.current) {
1580
- mounted.current = true;
1581
- viewDispatch({
1582
- payload: {
1583
- type: ViewActions.UPDATE_VIEW,
1584
- view: {
1585
- type: SaleWidgetViews.PAYMENT_METHODS,
1586
- },
1587
- },
1588
- });
1589
- }
1590
- }, [checkout, provider]);
1591
- useEffect(() => {
1592
- if (!checkout || !provider)
1593
- return;
1594
- onMount();
1595
- }, [checkout, provider]);
1596
- return (jsx(ViewContext.Provider, { value: viewReducerValues, children: jsx(SaleContextProvider, { value: {
1597
- config,
1598
- items,
1599
- amount,
1600
- fromTokenAddress,
1601
- env: checkout.config.environment ?? Environment.SANDBOX,
1602
- environmentId,
1603
- provider,
1604
- checkout,
1605
- passport: checkout?.passport,
1606
- collectionName,
1607
- }, children: jsxs(CryptoFiatProvider, { environment: config.environment, children: [viewState.view.type === SharedViews.LOADING_VIEW && (jsx(LoadingView, { loadingText: loadingText })), viewState.view.type === SaleWidgetViews.PAYMENT_METHODS && (jsx(PaymentMethods, {})), viewState.view.type === SaleWidgetViews.PAY_WITH_CARD && (jsx(PayWithCard, {})), viewState.view.type === SaleWidgetViews.PAY_WITH_COINS && (jsx(PayWithCoins, {})), viewState.view.type === SaleWidgetViews.SALE_FAIL && (jsx(SaleErrorView, { biomeTheme: biomeTheme, errorType: viewState.view.data?.errorType, transactionHash: viewState.view.data?.transactionHash, blockExplorerLink: BlockExplorerService.getTransactionLink(chainId.current, viewState.view.data?.transactionHash) })), viewState.view.type === SaleWidgetViews.SALE_SUCCESS && provider && (jsx(SaleSuccessView, { data: viewState.view.data })), viewState.view.type === SaleWidgetViews.FUND_WITH_SMART_CHECKOUT && (jsx(FundWithSmartCheckout, { subView: viewState.view.subView }))] }) }) }));
1608
- }
1609
-
1610
- export { SaleWidget as default };