@ledgerhq/live-common 34.54.0 → 34.54.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 (125) hide show
  1. package/lib/e2e/speculosCI.d.ts.map +1 -1
  2. package/lib/e2e/speculosCI.js +1 -6
  3. package/lib/e2e/speculosCI.js.map +1 -1
  4. package/lib/exchange/swap/postSwapState.d.ts.map +1 -1
  5. package/lib/exchange/swap/postSwapState.js +10 -6
  6. package/lib/exchange/swap/postSwapState.js.map +1 -1
  7. package/lib/exchange/swap/transactionStrategies.d.ts +2 -1
  8. package/lib/exchange/swap/transactionStrategies.d.ts.map +1 -1
  9. package/lib/exchange/swap/transactionStrategies.js +7 -6
  10. package/lib/exchange/swap/transactionStrategies.js.map +1 -1
  11. package/lib/exchange/swap/types.d.ts +3 -1
  12. package/lib/exchange/swap/types.d.ts.map +1 -1
  13. package/lib/hw/connectAppEventMapper.d.ts.map +1 -1
  14. package/lib/hw/connectAppEventMapper.js +21 -2
  15. package/lib/hw/connectAppEventMapper.js.map +1 -1
  16. package/lib/hw/getAppAndVersion.d.ts +3 -1
  17. package/lib/hw/getAppAndVersion.d.ts.map +1 -1
  18. package/lib/hw/getAppAndVersion.js +2 -2
  19. package/lib/hw/getAppAndVersion.js.map +1 -1
  20. package/lib/wallet-api/Exchange/SwapError.d.ts +93 -0
  21. package/lib/wallet-api/Exchange/SwapError.d.ts.map +1 -0
  22. package/lib/wallet-api/Exchange/SwapError.js +142 -0
  23. package/lib/wallet-api/Exchange/SwapError.js.map +1 -0
  24. package/lib/wallet-api/Exchange/handleSwapErrors.d.ts +40 -0
  25. package/lib/wallet-api/Exchange/handleSwapErrors.d.ts.map +1 -0
  26. package/lib/wallet-api/Exchange/handleSwapErrors.js +112 -0
  27. package/lib/wallet-api/Exchange/handleSwapErrors.js.map +1 -0
  28. package/lib/wallet-api/Exchange/index.d.ts +4 -0
  29. package/lib/wallet-api/Exchange/index.d.ts.map +1 -0
  30. package/lib/wallet-api/Exchange/index.js +27 -0
  31. package/lib/wallet-api/Exchange/index.js.map +1 -0
  32. package/lib/wallet-api/Exchange/parser.d.ts +46 -0
  33. package/lib/wallet-api/Exchange/parser.d.ts.map +1 -0
  34. package/lib/wallet-api/Exchange/parser.js +97 -0
  35. package/lib/wallet-api/Exchange/parser.js.map +1 -0
  36. package/lib/wallet-api/Exchange/server.d.ts.map +1 -1
  37. package/lib/wallet-api/Exchange/server.js +227 -174
  38. package/lib/wallet-api/Exchange/server.js.map +1 -1
  39. package/lib/wallet-api/Exchange/tracking.d.ts +7 -6
  40. package/lib/wallet-api/Exchange/tracking.d.ts.map +1 -1
  41. package/lib/wallet-api/Exchange/tracking.js +52 -13
  42. package/lib/wallet-api/Exchange/tracking.js.map +1 -1
  43. package/lib/wallet-api/logic.d.ts +1 -1
  44. package/lib/wallet-api/logic.d.ts.map +1 -1
  45. package/lib/wallet-api/logic.js +5 -5
  46. package/lib/wallet-api/logic.js.map +1 -1
  47. package/lib/wallet-api/react.d.ts.map +1 -1
  48. package/lib/wallet-api/react.js +9 -6
  49. package/lib/wallet-api/react.js.map +1 -1
  50. package/lib/wallet-api/tracking.d.ts +5 -5
  51. package/lib/wallet-api/tracking.d.ts.map +1 -1
  52. package/lib/wallet-api/tracking.js +30 -10
  53. package/lib/wallet-api/tracking.js.map +1 -1
  54. package/lib-es/e2e/speculosCI.d.ts.map +1 -1
  55. package/lib-es/e2e/speculosCI.js +1 -6
  56. package/lib-es/e2e/speculosCI.js.map +1 -1
  57. package/lib-es/exchange/swap/postSwapState.d.ts.map +1 -1
  58. package/lib-es/exchange/swap/postSwapState.js +10 -6
  59. package/lib-es/exchange/swap/postSwapState.js.map +1 -1
  60. package/lib-es/exchange/swap/transactionStrategies.d.ts +2 -1
  61. package/lib-es/exchange/swap/transactionStrategies.d.ts.map +1 -1
  62. package/lib-es/exchange/swap/transactionStrategies.js +7 -6
  63. package/lib-es/exchange/swap/transactionStrategies.js.map +1 -1
  64. package/lib-es/exchange/swap/types.d.ts +3 -1
  65. package/lib-es/exchange/swap/types.d.ts.map +1 -1
  66. package/lib-es/hw/connectAppEventMapper.d.ts.map +1 -1
  67. package/lib-es/hw/connectAppEventMapper.js +23 -4
  68. package/lib-es/hw/connectAppEventMapper.js.map +1 -1
  69. package/lib-es/hw/getAppAndVersion.d.ts +3 -1
  70. package/lib-es/hw/getAppAndVersion.d.ts.map +1 -1
  71. package/lib-es/hw/getAppAndVersion.js +2 -2
  72. package/lib-es/hw/getAppAndVersion.js.map +1 -1
  73. package/lib-es/wallet-api/Exchange/SwapError.d.ts +93 -0
  74. package/lib-es/wallet-api/Exchange/SwapError.d.ts.map +1 -0
  75. package/lib-es/wallet-api/Exchange/SwapError.js +128 -0
  76. package/lib-es/wallet-api/Exchange/SwapError.js.map +1 -0
  77. package/lib-es/wallet-api/Exchange/handleSwapErrors.d.ts +40 -0
  78. package/lib-es/wallet-api/Exchange/handleSwapErrors.d.ts.map +1 -0
  79. package/lib-es/wallet-api/Exchange/handleSwapErrors.js +106 -0
  80. package/lib-es/wallet-api/Exchange/handleSwapErrors.js.map +1 -0
  81. package/lib-es/wallet-api/Exchange/index.d.ts +4 -0
  82. package/lib-es/wallet-api/Exchange/index.d.ts.map +1 -0
  83. package/lib-es/wallet-api/Exchange/index.js +7 -0
  84. package/lib-es/wallet-api/Exchange/index.js.map +1 -0
  85. package/lib-es/wallet-api/Exchange/parser.d.ts +46 -0
  86. package/lib-es/wallet-api/Exchange/parser.d.ts.map +1 -0
  87. package/lib-es/wallet-api/Exchange/parser.js +90 -0
  88. package/lib-es/wallet-api/Exchange/parser.js.map +1 -0
  89. package/lib-es/wallet-api/Exchange/server.d.ts.map +1 -1
  90. package/lib-es/wallet-api/Exchange/server.js +224 -174
  91. package/lib-es/wallet-api/Exchange/server.js.map +1 -1
  92. package/lib-es/wallet-api/Exchange/tracking.d.ts +7 -6
  93. package/lib-es/wallet-api/Exchange/tracking.d.ts.map +1 -1
  94. package/lib-es/wallet-api/Exchange/tracking.js +52 -13
  95. package/lib-es/wallet-api/Exchange/tracking.js.map +1 -1
  96. package/lib-es/wallet-api/logic.d.ts +1 -1
  97. package/lib-es/wallet-api/logic.d.ts.map +1 -1
  98. package/lib-es/wallet-api/logic.js +5 -5
  99. package/lib-es/wallet-api/logic.js.map +1 -1
  100. package/lib-es/wallet-api/react.d.ts.map +1 -1
  101. package/lib-es/wallet-api/react.js +9 -6
  102. package/lib-es/wallet-api/react.js.map +1 -1
  103. package/lib-es/wallet-api/tracking.d.ts +5 -5
  104. package/lib-es/wallet-api/tracking.d.ts.map +1 -1
  105. package/lib-es/wallet-api/tracking.js +30 -10
  106. package/lib-es/wallet-api/tracking.js.map +1 -1
  107. package/package.json +68 -68
  108. package/src/e2e/speculosCI.ts +1 -6
  109. package/src/exchange/swap/postSwapState.ts +10 -5
  110. package/src/exchange/swap/transactionStrategies.ts +8 -7
  111. package/src/exchange/swap/types.ts +3 -1
  112. package/src/hw/connectAppEventMapper.ts +28 -4
  113. package/src/hw/getAppAndVersion.ts +2 -1
  114. package/src/wallet-api/Exchange/SwapError.test.ts +126 -0
  115. package/src/wallet-api/Exchange/SwapError.ts +159 -0
  116. package/src/wallet-api/Exchange/handleSwapErrors.test.ts +46 -0
  117. package/src/wallet-api/Exchange/handleSwapErrors.ts +161 -0
  118. package/src/wallet-api/Exchange/index.ts +26 -0
  119. package/src/wallet-api/Exchange/parser.test.ts +86 -0
  120. package/src/wallet-api/Exchange/parser.ts +119 -0
  121. package/src/wallet-api/Exchange/server.ts +289 -232
  122. package/src/wallet-api/Exchange/tracking.ts +56 -13
  123. package/src/wallet-api/logic.ts +5 -4
  124. package/src/wallet-api/react.ts +10 -5
  125. package/src/wallet-api/tracking.ts +30 -10
@@ -53,6 +53,12 @@ import { getSwapStepFromError } from "../../exchange/error";
53
53
  import { postSwapCancelled } from "../../exchange/swap";
54
54
  import { DeviceModelId } from "@ledgerhq/types-devices";
55
55
  import { setBroadcastTransaction } from "../../exchange/swap/setBroadcastTransaction";
56
+ import { Transaction as EvmTransaction } from "@ledgerhq/coin-evm/types/index";
57
+ import { padHexString } from "@ledgerhq/hw-app-eth";
58
+ import { createStepError, StepError, toError } from "./parser";
59
+ import { handleErrors } from "./handleSwapErrors";
60
+ import get from "lodash/get";
61
+ import { SwapError } from "./SwapError";
56
62
 
57
63
  export { ExchangeType };
58
64
 
@@ -374,237 +380,288 @@ export const handlers = ({
374
380
  );
375
381
  }),
376
382
  "custom.exchange.swap": customWrapper<ExchangeSwapParams, SwapResult>(async params => {
377
- if (!params) {
378
- tracking.startExchangeNoParams(manifest);
379
- throw new ServerError(createUnknownError({ message: "params is undefined" }));
380
- }
383
+ try {
384
+ if (!params) {
385
+ tracking.startExchangeNoParams(manifest);
386
+ throw new ServerError(createUnknownError({ message: "params is undefined" }));
387
+ }
381
388
 
382
- const {
383
- provider,
384
- fromAmount,
385
- fromAmountAtomic,
386
- quoteId,
387
- toNewTokenId,
388
- customFeeConfig,
389
- swapAppVersion,
390
- sponsored,
391
- } = params;
392
-
393
- const trackingParams = {
394
- provider: params.provider,
395
- exchangeType: params.exchangeType,
396
- };
397
-
398
- tracking.startExchangeRequested(trackingParams);
399
-
400
- const exchangeStartParams: ExchangeStartParamsUiRequest = (await extractSwapStartParam(
401
- params,
402
- accounts,
403
- )) as SwapStartParamsUiRequest;
404
-
405
- const {
406
- fromCurrency,
407
- fromAccount,
408
- fromParentAccount,
409
- toCurrency,
410
- toAccount,
411
- toParentAccount,
412
- } = exchangeStartParams.exchange;
413
-
414
- if (!fromAccount || !fromCurrency) {
415
- throw new ServerError(createAccountNotFound(params.fromAccountId));
416
- }
389
+ const {
390
+ provider,
391
+ fromAmount,
392
+ fromAmountAtomic,
393
+ quoteId,
394
+ toNewTokenId,
395
+ customFeeConfig,
396
+ swapAppVersion,
397
+ sponsored,
398
+ isEmbedded,
399
+ } = params;
417
400
 
418
- const fromAccountAddress = fromParentAccount
419
- ? fromParentAccount.freshAddress
420
- : (fromAccount as Account).freshAddress;
421
-
422
- const toAccountAddress = toParentAccount
423
- ? toParentAccount.freshAddress
424
- : (toAccount as Account).freshAddress;
425
-
426
- // Step 1: Open the drawer and open exchange app
427
- const startExchange = async () => {
428
- return new Promise<{ transactionId: string; device?: ExchangeStartResult["device"] }>(
429
- (resolve, reject) => {
430
- uiExchangeStart({
431
- exchangeParams: exchangeStartParams,
432
- onSuccess: (nonce, device) => {
433
- tracking.startExchangeSuccess(trackingParams);
434
- resolve({ transactionId: nonce, device });
435
- },
436
- onCancel: error => {
437
- tracking.startExchangeFail(trackingParams);
438
- reject(error);
439
- },
440
- });
441
- },
442
- );
443
- };
444
-
445
- const { transactionId, device: deviceInfo } = await startExchange();
446
-
447
- const {
448
- binaryPayload,
449
- signature,
450
- payinAddress,
451
- swapId,
452
- payinExtraId,
453
- extraTransactionParameters,
454
- } = await retrieveSwapPayload({
455
- provider,
456
- deviceTransactionId: transactionId,
457
- fromAccountAddress,
458
- toAccountAddress,
459
- fromAccountCurrency: fromCurrency!.id,
460
- toAccountCurrency: toCurrency!.id,
461
- amount: fromAmount,
462
- amountInAtomicUnit: fromAmountAtomic,
463
- quoteId,
464
- toNewTokenId,
465
- }).catch((error: Error) => {
466
- throw error;
467
- });
468
-
469
- // Complete Swap
470
- const trackingCompleteParams = {
471
- provider: params.provider,
472
- exchangeType: params.exchangeType,
473
- };
474
- tracking.completeExchangeRequested(trackingCompleteParams);
475
-
476
- const strategyData = {
477
- recipient: payinAddress,
478
- amount: fromAmountAtomic,
479
- currency: fromCurrency as CryptoOrTokenCurrency,
480
- customFeeConfig: customFeeConfig ?? {},
481
- payinExtraId,
482
- extraTransactionParameters,
483
- sponsored,
484
- };
485
-
486
- const transaction: Transaction = await getStrategy(strategyData, "swap").catch(
487
- async error => {
488
- throw error;
489
- },
490
- );
401
+ const trackingParams = {
402
+ provider: params.provider,
403
+ exchangeType: params.exchangeType,
404
+ isEmbeddedSwap: isEmbedded,
405
+ };
491
406
 
492
- const mainFromAccount = getMainAccount(fromAccount, fromParentAccount);
407
+ tracking.startExchangeRequested(trackingParams);
493
408
 
494
- if (transaction.family !== mainFromAccount.currency.family) {
495
- return Promise.reject(
496
- new Error(
497
- `Account and transaction must be from the same family. Account family: ${mainFromAccount.currency.family}, Transaction family: ${transaction.family}`,
498
- ),
499
- );
500
- }
409
+ const exchangeStartParams: ExchangeStartParamsUiRequest = (await extractSwapStartParam(
410
+ params,
411
+ accounts,
412
+ )) as SwapStartParamsUiRequest;
413
+
414
+ const {
415
+ fromCurrency,
416
+ fromAccount,
417
+ fromParentAccount,
418
+ toCurrency,
419
+ toAccount,
420
+ toParentAccount,
421
+ } = exchangeStartParams.exchange;
422
+
423
+ if (!fromAccount || !fromCurrency) {
424
+ throw new ServerError(createAccountNotFound(params.fromAccountId));
425
+ }
501
426
 
502
- const accountBridge = getAccountBridge(fromAccount, fromParentAccount);
503
-
504
- /**
505
- * 'subAccountId' is used for ETH and it's ERC-20 tokens.
506
- * This field is ignored for BTC
507
- */
508
- const subAccountId =
509
- fromParentAccount && fromParentAccount.id !== fromAccount.id ? fromAccount.id : undefined;
510
-
511
- const bridgeTx = accountBridge.createTransaction(fromAccount);
512
- /**
513
- * We append the `recipient` to the tx created from `createTransaction`
514
- * to avoid having userGasLimit reset to null for ETH txs
515
- * cf. libs/ledger-live-common/src/families/ethereum/updateTransaction.ts
516
- */
517
- const tx = accountBridge.updateTransaction(
518
- {
519
- ...bridgeTx,
520
- recipient: transaction.recipient,
521
- },
522
- {
523
- ...transaction,
524
- feesStrategy: params.feeStrategy.toLowerCase(),
525
- subAccountId,
526
- },
527
- );
427
+ const fromAccountAddress = fromParentAccount
428
+ ? fromParentAccount.freshAddress
429
+ : (fromAccount as Account).freshAddress;
430
+
431
+ const toAccountAddress = toParentAccount
432
+ ? toParentAccount.freshAddress
433
+ : (toAccount as Account).freshAddress;
434
+
435
+ // Step 1: Open the drawer and open exchange app
436
+ const startExchange = async () => {
437
+ return new Promise<{ transactionId: string; device?: ExchangeStartResult["device"] }>(
438
+ (resolve, reject) => {
439
+ uiExchangeStart({
440
+ exchangeParams: exchangeStartParams,
441
+ onSuccess: (nonce, device) => {
442
+ tracking.startExchangeSuccess(trackingParams);
443
+ resolve({ transactionId: nonce, device });
444
+ },
445
+ onCancel: error => {
446
+ tracking.startExchangeFail(trackingParams);
447
+ reject(error);
448
+ },
449
+ });
450
+ },
451
+ );
452
+ };
528
453
 
529
- // Get amountExpectedTo and magnitudeAwareRate from binary payload
530
- const decodePayload = await decodeSwapPayload(binaryPayload);
531
- const amountExpectedTo = new BigNumber(decodePayload.amountToWallet.toString());
532
- const magnitudeAwareRate = tx.amount && amountExpectedTo.dividedBy(tx.amount);
533
- const refundAddress = decodePayload.refundAddress;
534
- const payoutAddress = decodePayload.payoutAddress;
454
+ let transactionId: string;
455
+ let deviceInfo: ExchangeStartResult["device"];
456
+
457
+ try {
458
+ const result = await startExchange();
459
+ transactionId = result.transactionId;
460
+ deviceInfo = result.device;
461
+ } catch (error) {
462
+ const rawError = get(error, "response.data.error", error);
463
+ const wrappedError = createStepError({
464
+ error: toError(rawError),
465
+ step: StepError.NONCE,
466
+ });
467
+ throw wrappedError;
468
+ }
535
469
 
536
- // tx.amount should be BigNumber
537
- tx.amount = new BigNumber(tx.amount);
470
+ const {
471
+ binaryPayload,
472
+ signature,
473
+ payinAddress,
474
+ swapId,
475
+ payinExtraId,
476
+ extraTransactionParameters,
477
+ } = await retrieveSwapPayload({
478
+ provider,
479
+ deviceTransactionId: transactionId,
480
+ fromAccountAddress,
481
+ toAccountAddress,
482
+ fromAccountCurrency: fromCurrency!.id,
483
+ toAccountCurrency: toCurrency!.id,
484
+ amount: fromAmount,
485
+ amountInAtomicUnit: fromAmountAtomic,
486
+ quoteId,
487
+ toNewTokenId,
488
+ }).catch((error: Error) => {
489
+ const wrappedError = createStepError({
490
+ error: get(error, "response.data.error", error),
491
+ step: StepError.PAYLOAD,
492
+ });
493
+
494
+ throw wrappedError;
495
+ });
538
496
 
539
- return new Promise((resolve, reject) =>
540
- uiSwap({
541
- exchangeParams: {
542
- exchangeType: ExchangeType.SWAP,
543
- provider: params.provider,
544
- transaction: tx,
545
- signature: signature,
546
- binaryPayload: binaryPayload,
547
- exchange: {
548
- fromAccount,
549
- fromParentAccount,
550
- toAccount,
551
- toParentAccount,
552
- fromCurrency: fromCurrency!,
553
- toCurrency: toCurrency!,
554
- },
555
- feesStrategy: params.feeStrategy,
556
- swapId: swapId,
557
- amountExpectedTo: amountExpectedTo.toNumber(),
558
- magnitudeAwareRate,
559
- refundAddress,
560
- payoutAddress,
561
- sponsored,
562
- },
563
- onSuccess: ({ operationHash, swapId }: { operationHash: string; swapId: string }) => {
564
- tracking.completeExchangeSuccess({
565
- ...trackingParams,
566
- currency: transaction.family,
567
- });
497
+ // Complete Swap
498
+ const trackingCompleteParams = {
499
+ provider: params.provider,
500
+ exchangeType: params.exchangeType,
501
+ isEmbeddedSwap: isEmbedded,
502
+ };
503
+ tracking.completeExchangeRequested(trackingCompleteParams);
504
+
505
+ const strategyData = {
506
+ recipient: payinAddress,
507
+ amount: fromAmountAtomic,
508
+ currency: fromCurrency as CryptoOrTokenCurrency,
509
+ customFeeConfig: customFeeConfig ?? {},
510
+ payinExtraId,
511
+ extraTransactionParameters,
512
+ sponsored,
513
+ };
568
514
 
569
- setBroadcastTransaction({
570
- provider,
571
- result: { operation: operationHash, swapId },
572
- sourceCurrencyId: fromCurrency.id,
573
- targetCurrencyId: toCurrency?.id,
574
- hardwareWalletType: deviceInfo?.modelId as DeviceModelId,
575
- swapAppVersion,
576
- fromAccountAddress,
577
- toAccountAddress,
578
- fromAmount,
579
- });
515
+ const transaction: Transaction = await getStrategy(strategyData, "swap");
516
+
517
+ const mainFromAccount = getMainAccount(fromAccount, fromParentAccount);
518
+
519
+ if (transaction.family !== mainFromAccount.currency.family) {
520
+ return Promise.reject(
521
+ new Error(
522
+ `Account and transaction must be from the same family. Account family: ${mainFromAccount.currency.family}, Transaction family: ${transaction.family}`,
523
+ ),
524
+ );
525
+ }
526
+
527
+ const accountBridge = getAccountBridge(fromAccount, fromParentAccount);
528
+
529
+ /**
530
+ * 'subAccountId' is used for ETH and it's ERC-20 tokens.
531
+ * This field is ignored for BTC
532
+ */
533
+ const subAccountId =
534
+ fromParentAccount && fromParentAccount.id !== fromAccount.id ? fromAccount.id : undefined;
580
535
 
581
- resolve({ operationHash, swapId });
536
+ const bridgeTx = accountBridge.createTransaction(fromAccount);
537
+ /**
538
+ * We append the `recipient` to the tx created from `createTransaction`
539
+ * to avoid having userGasLimit reset to null for ETH txs
540
+ * cf. libs/ledger-live-common/src/families/ethereum/updateTransaction.ts
541
+ */
542
+ const tx = accountBridge.updateTransaction(
543
+ {
544
+ ...bridgeTx,
545
+ recipient: transaction.recipient,
582
546
  },
583
- onCancel: error => {
584
- postSwapCancelled({
585
- provider: provider,
547
+ {
548
+ ...transaction,
549
+ feesStrategy: params.feeStrategy.toLowerCase(),
550
+ subAccountId,
551
+ },
552
+ );
553
+
554
+ // Get amountExpectedTo and magnitudeAwareRate from binary payload
555
+ const decodePayload = await decodeSwapPayload(binaryPayload);
556
+ const amountExpectedTo = new BigNumber(decodePayload.amountToWallet.toString());
557
+ const magnitudeAwareRate = tx.amount && amountExpectedTo.dividedBy(tx.amount);
558
+ const refundAddress = decodePayload.refundAddress;
559
+ const payoutAddress = decodePayload.payoutAddress;
560
+
561
+ // tx.amount should be BigNumber
562
+ tx.amount = new BigNumber(tx.amount);
563
+
564
+ return new Promise((resolve, reject) =>
565
+ uiSwap({
566
+ exchangeParams: {
567
+ exchangeType: ExchangeType.SWAP,
568
+ provider: params.provider,
569
+ transaction: tx,
570
+ signature: signature,
571
+ binaryPayload: binaryPayload,
572
+ exchange: {
573
+ fromAccount,
574
+ fromParentAccount,
575
+ toAccount,
576
+ toParentAccount,
577
+ fromCurrency: fromCurrency!,
578
+ toCurrency: toCurrency!,
579
+ },
580
+ feesStrategy: params.feeStrategy,
586
581
  swapId: swapId,
587
- swapStep: getSwapStepFromError(error),
588
- statusCode: error.name,
589
- errorMessage: error.message,
590
- sourceCurrencyId: fromCurrency.id,
591
- targetCurrencyId: toCurrency?.id,
592
- hardwareWalletType: deviceInfo?.modelId as DeviceModelId,
593
- swapType: quoteId ? "fixed" : "float",
594
- swapAppVersion,
595
- fromAccountAddress,
596
- toAccountAddress,
582
+ amountExpectedTo: amountExpectedTo.toNumber(),
583
+ magnitudeAwareRate,
597
584
  refundAddress,
598
585
  payoutAddress,
599
- fromAmount,
600
- seedIdFrom: mainFromAccount.seedIdentifier,
601
- seedIdTo: toParentAccount?.seedIdentifier || (toAccount as Account)?.seedIdentifier,
586
+ sponsored,
587
+ },
588
+ onSuccess: ({ operationHash, swapId }: { operationHash: string; swapId: string }) => {
589
+ tracking.completeExchangeSuccess({
590
+ ...trackingParams,
591
+ currency: transaction.family,
592
+ });
593
+
594
+ setBroadcastTransaction({
595
+ provider,
596
+ result: { operation: operationHash, swapId },
597
+ sourceCurrencyId: fromCurrency.id,
598
+ targetCurrencyId: toCurrency?.id,
599
+ hardwareWalletType: deviceInfo?.modelId as DeviceModelId,
600
+ swapAppVersion,
601
+ fromAccountAddress,
602
+ toAccountAddress,
603
+ fromAmount,
604
+ });
605
+
606
+ resolve({ operationHash, swapId });
607
+ },
608
+ onCancel: error => {
609
+ postSwapCancelled({
610
+ provider: provider,
611
+ swapId: swapId,
612
+ swapStep: getSwapStepFromError(error),
613
+ statusCode: error.name,
614
+ errorMessage: error.message,
615
+ sourceCurrencyId: fromCurrency.id,
616
+ targetCurrencyId: toCurrency?.id,
617
+ hardwareWalletType: deviceInfo?.modelId as DeviceModelId,
618
+ swapType: quoteId ? "fixed" : "float",
619
+ swapAppVersion,
620
+ fromAccountAddress,
621
+ toAccountAddress,
622
+ refundAddress,
623
+ payoutAddress,
624
+ fromAmount,
625
+ seedIdFrom: mainFromAccount.seedIdentifier,
626
+ seedIdTo: toParentAccount?.seedIdentifier || (toAccount as Account)?.seedIdentifier,
627
+ data: (transaction as EvmTransaction).data
628
+ ? `0x${padHexString((transaction as EvmTransaction).data?.toString("hex") || "")}`
629
+ : "0x",
630
+ });
631
+
632
+ reject(createStepError({ error, step: StepError.SIGNATURE }));
633
+ },
634
+ }),
635
+ );
636
+ } catch (error) {
637
+ // Skip DrawerClosedError
638
+ // do not redirect to the error screen
639
+ if (isDrawerClosedError(error)) {
640
+ throw error;
641
+ }
642
+
643
+ // Global catch for any errors during the swap process
644
+ // moved out as sonarcloud suggested to avoid 4 level nested functions
645
+ const createErrorRejector = (error: SwapError, reject: (error: SwapError) => void) => {
646
+ return () => reject(error);
647
+ };
648
+
649
+ const displayError = (error: SwapError): Promise<void> =>
650
+ new Promise((resolve, reject) => {
651
+ const rejectWithError = createErrorRejector(error, reject);
652
+ uiError({
653
+ error,
654
+ onSuccess: rejectWithError,
655
+ onCancel: rejectWithError,
602
656
  });
657
+ });
603
658
 
604
- reject(error);
605
- },
606
- }),
607
- );
659
+ await handleErrors(error, {
660
+ onDisplayError: displayError,
661
+ });
662
+
663
+ throw error;
664
+ }
608
665
  }),
609
666
 
610
667
  "custom.isReady": customWrapper<void, void>(async () => {
@@ -826,21 +883,21 @@ async function getStrategy(
826
883
  }
827
884
  }
828
885
 
829
- try {
830
- return await strategy({
831
- family,
832
- amount: new BigNumber(amount),
833
- recipient,
834
- customFeeConfig: convertedCustomFeeConfig,
835
- payinExtraId,
836
- extraTransactionParameters,
837
- customErrorType,
838
- sponsored,
839
- });
840
- } catch (error) {
841
- const errorMessage = error instanceof Error ? error.message : String(error);
842
- throw new Error(
843
- `Failed to execute transaction strategy for family: ${family}. Reason: ${errorMessage}`,
844
- );
845
- }
886
+ return strategy({
887
+ family,
888
+ amount: new BigNumber(amount),
889
+ recipient,
890
+ customFeeConfig: convertedCustomFeeConfig,
891
+ payinExtraId,
892
+ extraTransactionParameters,
893
+ customErrorType,
894
+ sponsored,
895
+ });
896
+ }
897
+
898
+ function isDrawerClosedError(error: unknown) {
899
+ if (!error || typeof error !== "object") return false;
900
+ return (
901
+ get(error, "name") === "DrawerClosedError" || get(error, "cause.name") === "DrawerClosedError"
902
+ );
846
903
  }
@@ -14,8 +14,15 @@ type TrackExchange = (
14
14
  interface TrackEventPayload {
15
15
  exchangeType: "SELL" | "FUND" | "SWAP";
16
16
  provider: string;
17
+ isEmbeddedSwap?: boolean;
17
18
  }
18
19
 
20
+ /**
21
+ * Converts isEmbeddedSwap boolean to string for analytics consistency
22
+ */
23
+ const formatIsEmbeddedSwap = (isEmbeddedSwap?: boolean): string | undefined =>
24
+ isEmbeddedSwap !== undefined ? String(isEmbeddedSwap) : undefined;
25
+
19
26
  function getEventData(manifest: AppManifest) {
20
27
  return { walletAPI: manifest.name };
21
28
  }
@@ -29,24 +36,46 @@ function getEventData(manifest: AppManifest) {
29
36
  // in order to get the exact type matching the tracking wrapper API
30
37
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
31
38
  export default function trackingWrapper(trackCall: TrackExchange) {
32
- const track = (event: string, properties: Record<string, string> | null) => {
33
- return trackCall(event, properties, null);
39
+ const track = (event: string, properties: Record<string, string | undefined> | null) => {
40
+ // Filter out undefined values before passing to trackCall
41
+ if (!properties) {
42
+ return trackCall(event, null, null);
43
+ }
44
+ const filteredProperties: Record<string, string> = {};
45
+ for (const [key, value] of Object.entries(properties)) {
46
+ if (value !== undefined) {
47
+ filteredProperties[key] = value;
48
+ }
49
+ }
50
+ return trackCall(event, filteredProperties, null);
34
51
  };
35
52
 
36
53
  return {
37
54
  // Generate Exchange nonce modal open
38
- startExchangeRequested: ({ provider, exchangeType }: TrackEventPayload) => {
39
- track(`Starts Exchange ${exchangeType} Nonce request`, { provider, exchangeType });
55
+ startExchangeRequested: ({ provider, exchangeType, isEmbeddedSwap }: TrackEventPayload) => {
56
+ track(`Starts Exchange ${exchangeType} Nonce request`, {
57
+ provider,
58
+ exchangeType,
59
+ isEmbeddedSwap: formatIsEmbeddedSwap(isEmbeddedSwap),
60
+ });
40
61
  },
41
62
 
42
63
  // Successfully generated an Exchange app nonce
43
- startExchangeSuccess: ({ provider, exchangeType }: TrackEventPayload) => {
44
- track(`Starts Exchange ${exchangeType} Nonce success`, { provider, exchangeType });
64
+ startExchangeSuccess: ({ provider, exchangeType, isEmbeddedSwap }: TrackEventPayload) => {
65
+ track(`Starts Exchange ${exchangeType} Nonce success`, {
66
+ provider,
67
+ exchangeType,
68
+ isEmbeddedSwap: formatIsEmbeddedSwap(isEmbeddedSwap),
69
+ });
45
70
  },
46
71
 
47
72
  // Failed to generate an Exchange app nonce
48
- startExchangeFail: ({ provider, exchangeType }: TrackEventPayload) => {
49
- track(`Starts Exchange ${exchangeType} Nonce fail`, { provider, exchangeType });
73
+ startExchangeFail: ({ provider, exchangeType, isEmbeddedSwap }: TrackEventPayload) => {
74
+ track(`Starts Exchange ${exchangeType} Nonce fail`, {
75
+ provider,
76
+ exchangeType,
77
+ isEmbeddedSwap: formatIsEmbeddedSwap(isEmbeddedSwap),
78
+ });
50
79
  },
51
80
 
52
81
  // No Params to generate an Exchange app nonce
@@ -54,8 +83,12 @@ export default function trackingWrapper(trackCall: TrackExchange) {
54
83
  track("Starts Exchange no params", getEventData(manifest));
55
84
  },
56
85
 
57
- completeExchangeRequested: ({ provider, exchangeType }: TrackEventPayload) => {
58
- track(`Completes Exchange ${exchangeType} requested`, { provider, exchangeType });
86
+ completeExchangeRequested: ({ provider, exchangeType, isEmbeddedSwap }: TrackEventPayload) => {
87
+ track(`Completes Exchange ${exchangeType} requested`, {
88
+ provider,
89
+ exchangeType,
90
+ isEmbeddedSwap: formatIsEmbeddedSwap(isEmbeddedSwap),
91
+ });
59
92
  },
60
93
 
61
94
  // Successfully completed an Exchange
@@ -63,13 +96,23 @@ export default function trackingWrapper(trackCall: TrackExchange) {
63
96
  provider,
64
97
  exchangeType,
65
98
  currency,
99
+ isEmbeddedSwap,
66
100
  }: TrackEventPayload & { currency: string }) => {
67
- track(`Completes Exchange ${exchangeType} success`, { provider, exchangeType, currency });
101
+ track(`Completes Exchange ${exchangeType} success`, {
102
+ provider,
103
+ exchangeType,
104
+ currency,
105
+ isEmbeddedSwap: formatIsEmbeddedSwap(isEmbeddedSwap),
106
+ });
68
107
  },
69
108
 
70
109
  // Failed to complete an Exchange
71
- completeExchangeFail: ({ provider, exchangeType }: TrackEventPayload) => {
72
- track(`Completes Exchange ${exchangeType} Nonce fail`, { provider, exchangeType });
110
+ completeExchangeFail: ({ provider, exchangeType, isEmbeddedSwap }: TrackEventPayload) => {
111
+ track(`Completes Exchange ${exchangeType} Nonce fail`, {
112
+ provider,
113
+ exchangeType,
114
+ isEmbeddedSwap: formatIsEmbeddedSwap(isEmbeddedSwap),
115
+ });
73
116
  },
74
117
 
75
118
  // No Params to complete an Exchange