@rabbitio/ui-kit 1.0.0-beta.110 → 1.0.0-beta.112

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 (212) hide show
  1. package/.env +1 -1
  2. package/.env.example +1 -1
  3. package/coverage/clover.xml +1411 -1410
  4. package/coverage/coverage-final.json +18 -18
  5. package/coverage/index.html +17 -17
  6. package/coverage/ui-kit/index.html +1 -1
  7. package/coverage/ui-kit/index.js.html +1 -1
  8. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js.html +1 -1
  9. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/index.html +1 -1
  10. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js.html +1 -1
  11. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js.html +1 -1
  12. package/coverage/ui-kit/src/common-apis/adapters/axiosAdapter.js.html +1 -1
  13. package/coverage/ui-kit/src/common-apis/adapters/index.html +1 -1
  14. package/coverage/ui-kit/src/common-apis/adapters/qrUtils.js.html +1 -1
  15. package/coverage/ui-kit/src/common-apis/external-apis/apiGroups.js.html +1 -1
  16. package/coverage/ui-kit/src/common-apis/external-apis/emailAPI.js.html +1 -1
  17. package/coverage/ui-kit/src/common-apis/external-apis/index.html +1 -1
  18. package/coverage/ui-kit/src/common-apis/external-apis/ipAddressProviders.js.html +1 -1
  19. package/coverage/ui-kit/src/common-apis/globalConstants.jsx.html +1 -1
  20. package/coverage/ui-kit/src/common-apis/index.html +1 -1
  21. package/coverage/ui-kit/src/common-apis/models/blockchain.js.html +4 -4
  22. package/coverage/ui-kit/src/common-apis/models/coin.js.html +221 -221
  23. package/coverage/ui-kit/src/common-apis/models/index.html +1 -1
  24. package/coverage/ui-kit/src/common-apis/models/protocol.js.html +3 -3
  25. package/coverage/ui-kit/src/common-apis/services/fiatCurrenciesService.js.html +1 -1
  26. package/coverage/ui-kit/src/common-apis/services/index.html +1 -1
  27. package/coverage/ui-kit/src/common-apis/utils/amountUtils.js.html +21 -21
  28. package/coverage/ui-kit/src/common-apis/utils/cache.js.html +6 -6
  29. package/coverage/ui-kit/src/common-apis/utils/errorUtils.js.html +13 -13
  30. package/coverage/ui-kit/src/common-apis/utils/index.html +1 -1
  31. package/coverage/ui-kit/src/common-apis/utils/logging/index.html +1 -1
  32. package/coverage/ui-kit/src/common-apis/utils/logging/logger.js.html +22 -22
  33. package/coverage/ui-kit/src/common-apis/utils/logging/logsStorage.js.html +3 -3
  34. package/coverage/ui-kit/src/common-apis/utils/postponeExecution.js.html +1 -1
  35. package/coverage/ui-kit/src/common-apis/utils/rabbitTicker.js.html +10 -10
  36. package/coverage/ui-kit/src/common-apis/utils/safeStringify.js.html +27 -27
  37. package/coverage/ui-kit/src/index.html +1 -1
  38. package/coverage/ui-kit/src/index.js.html +1 -1
  39. package/coverage/ui-kit/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js.html +1 -1
  40. package/coverage/ui-kit/src/robust-api-caller/cachedRobustExternalApiCallerService.js.html +1 -1
  41. package/coverage/ui-kit/src/robust-api-caller/cancelProcessing.js.html +1 -1
  42. package/coverage/ui-kit/src/robust-api-caller/concurrentCalculationsMetadataHolder.js.html +1 -1
  43. package/coverage/ui-kit/src/robust-api-caller/externalApiProvider.js.html +1 -1
  44. package/coverage/ui-kit/src/robust-api-caller/externalServicesStatsCollector.js.html +1 -1
  45. package/coverage/ui-kit/src/robust-api-caller/index.html +1 -1
  46. package/coverage/ui-kit/src/robust-api-caller/robustExternalAPICallerService.js.html +1 -1
  47. package/coverage/ui-kit/src/swaps-lib/external-apis/exolixSwapProvider.js.html +66 -45
  48. package/coverage/ui-kit/src/swaps-lib/external-apis/goexmeSwapProvider.js.html +521 -545
  49. package/coverage/ui-kit/src/swaps-lib/external-apis/index.html +27 -27
  50. package/coverage/ui-kit/src/swaps-lib/external-apis/letsExchangeSwapProvider.js.html +1 -1
  51. package/coverage/ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +328 -328
  52. package/coverage/ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +76 -52
  53. package/coverage/ui-kit/src/swaps-lib/external-apis/utils.js.html +14 -14
  54. package/coverage/ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +1 -1
  55. package/coverage/ui-kit/src/swaps-lib/models/existingSwap.js.html +141 -141
  56. package/coverage/ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +1 -1
  57. package/coverage/ui-kit/src/swaps-lib/models/index.html +1 -1
  58. package/coverage/ui-kit/src/swaps-lib/models/partner.js.html +1 -1
  59. package/coverage/ui-kit/src/swaps-lib/models/swapProviderCoinInfo.js.html +73 -73
  60. package/coverage/ui-kit/src/swaps-lib/services/index.html +1 -1
  61. package/coverage/ui-kit/src/swaps-lib/services/publicSwapService.js.html +1 -1
  62. package/coverage/ui-kit/src/swaps-lib/utils/index.html +1 -1
  63. package/coverage/ui-kit/src/swaps-lib/utils/swapUtils.js.html +1 -1
  64. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowIcon.jsx.html +1 -1
  65. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowTosca.jsx.html +1 -1
  66. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowWhite.jsx.html +1 -1
  67. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/darkRectangle.jsx.html +1 -1
  68. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/determinedError.jsx.html +1 -1
  69. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx.html +1 -1
  70. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/index.html +1 -1
  71. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/infoIcon.jsx.html +1 -1
  72. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/messageIcon.jsx.html +1 -1
  73. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx.html +1 -1
  74. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx.html +1 -1
  75. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx.html +1 -1
  76. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/walletIcon.jsx.html +1 -1
  77. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx.html +1 -1
  78. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetIcon/index.html +1 -1
  79. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx.html +1 -1
  80. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetSelection/index.html +1 -1
  81. package/coverage/ui-kit/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx.html +1 -1
  82. package/coverage/ui-kit/src/ui-kit/components/atoms/BackgroundTitle/index.html +1 -1
  83. package/coverage/ui-kit/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx.html +1 -1
  84. package/coverage/ui-kit/src/ui-kit/components/atoms/InformationMessage/index.html +1 -1
  85. package/coverage/ui-kit/src/ui-kit/components/atoms/Input/Input.jsx.html +1 -1
  86. package/coverage/ui-kit/src/ui-kit/components/atoms/Input/index.html +1 -1
  87. package/coverage/ui-kit/src/ui-kit/components/atoms/LoadingDots/LoadingDots.jsx.html +1 -1
  88. package/coverage/ui-kit/src/ui-kit/components/atoms/LoadingDots/index.html +1 -1
  89. package/coverage/ui-kit/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx.html +1 -1
  90. package/coverage/ui-kit/src/ui-kit/components/atoms/NoticeIcon/index.html +1 -1
  91. package/coverage/ui-kit/src/ui-kit/components/atoms/QrCode/QrCode.jsx.html +7 -25
  92. package/coverage/ui-kit/src/ui-kit/components/atoms/QrCode/index.html +5 -5
  93. package/coverage/ui-kit/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx.html +1 -1
  94. package/coverage/ui-kit/src/ui-kit/components/atoms/RateSelector/index.html +1 -1
  95. package/coverage/ui-kit/src/ui-kit/components/atoms/SupportChat/SupportChat.jsx.html +1 -1
  96. package/coverage/ui-kit/src/ui-kit/components/atoms/SupportChat/index.html +1 -1
  97. package/coverage/ui-kit/src/ui-kit/components/atoms/Textarea/Textarea.jsx.html +1 -1
  98. package/coverage/ui-kit/src/ui-kit/components/atoms/Textarea/index.html +1 -1
  99. package/coverage/ui-kit/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx.html +1 -1
  100. package/coverage/ui-kit/src/ui-kit/components/atoms/TitleBox/index.html +1 -1
  101. package/coverage/ui-kit/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx.html +1 -1
  102. package/coverage/ui-kit/src/ui-kit/components/atoms/Tooltip/index.html +1 -1
  103. package/coverage/ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx.html +1 -1
  104. package/coverage/ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/index.html +1 -1
  105. package/coverage/ui-kit/src/ui-kit/components/atoms/Validation/Validation.jsx.html +1 -1
  106. package/coverage/ui-kit/src/ui-kit/components/atoms/Validation/index.html +1 -1
  107. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Button/Button.jsx.html +1 -1
  108. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Button/index.html +1 -1
  109. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Close/Close.jsx.html +1 -1
  110. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Close/index.html +1 -1
  111. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx.html +1 -1
  112. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/index.html +1 -1
  113. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx.html +1 -1
  114. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/index.html +1 -1
  115. package/coverage/ui-kit/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx.html +1 -1
  116. package/coverage/ui-kit/src/ui-kit/components/molecules/AmountInput/index.html +1 -1
  117. package/coverage/ui-kit/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx.html +1 -1
  118. package/coverage/ui-kit/src/ui-kit/components/molecules/CoinPicker/index.html +1 -1
  119. package/coverage/ui-kit/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx.html +1 -1
  120. package/coverage/ui-kit/src/ui-kit/components/molecules/ColoredNotice/index.html +1 -1
  121. package/coverage/ui-kit/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx.html +1 -1
  122. package/coverage/ui-kit/src/ui-kit/components/molecules/LineWithIconLink/index.html +1 -1
  123. package/coverage/ui-kit/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx.html +1 -1
  124. package/coverage/ui-kit/src/ui-kit/components/molecules/LogoCarousel/index.html +1 -1
  125. package/coverage/ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx.html +1 -1
  126. package/coverage/ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/index.html +1 -1
  127. package/coverage/ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx.html +1 -1
  128. package/coverage/ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/index.html +1 -1
  129. package/coverage/ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx.html +1 -1
  130. package/coverage/ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/index.html +1 -1
  131. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/Dialog.jsx.html +1 -1
  132. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx.html +1 -1
  133. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/index.html +1 -1
  134. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx.html +1 -1
  135. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/index.html +1 -1
  136. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/index.html +1 -1
  137. package/coverage/ui-kit/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx.html +1 -1
  138. package/coverage/ui-kit/src/ui-kit/components/organisms/SwapForm/index.html +1 -1
  139. package/coverage/ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx.html +1 -1
  140. package/coverage/ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/index.html +1 -1
  141. package/coverage/ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx.html +1 -1
  142. package/coverage/ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/index.html +1 -1
  143. package/coverage/ui-kit/src/ui-kit/hooks/index.html +1 -1
  144. package/coverage/ui-kit/src/ui-kit/hooks/useCallHandlingErrors.js.html +1 -1
  145. package/coverage/ui-kit/src/ui-kit/hooks/useIsHydrated.js.html +1 -1
  146. package/coverage/ui-kit/src/ui-kit/hooks/useReferredState.js.html +1 -1
  147. package/coverage/ui-kit/src/ui-kit/utils/index.html +1 -1
  148. package/coverage/ui-kit/src/ui-kit/utils/inputValueProviders.js.html +1 -1
  149. package/coverage/ui-kit/src/ui-kit/utils/searchCoins.js.html +1 -1
  150. package/coverage/ui-kit/src/ui-kit/utils/textUtils.js.html +1 -1
  151. package/coverage/ui-kit/src/ui-kit/utils/uiUtils.js.html +1 -1
  152. package/coverage/ui-kit/src/ui-kit/utils/urlQueryUtils.js.html +1 -1
  153. package/coverage/ui-kit/stories/atoms/BackgroundTitle.stories.jsx.html +1 -1
  154. package/coverage/ui-kit/stories/atoms/LinesOfText.stories.jsx.html +1 -1
  155. package/coverage/ui-kit/stories/atoms/LoadingDots.stories.jsx.html +1 -1
  156. package/coverage/ui-kit/stories/atoms/QrCode.stories.jsx.html +1 -1
  157. package/coverage/ui-kit/stories/atoms/RateSelector.stories.jsx.html +1 -1
  158. package/coverage/ui-kit/stories/atoms/Validation.stories.jsx.html +1 -1
  159. package/coverage/ui-kit/stories/atoms/buttons/Button.stories.jsx.html +1 -1
  160. package/coverage/ui-kit/stories/atoms/buttons/Close.stories.jsx.html +1 -1
  161. package/coverage/ui-kit/stories/atoms/buttons/LinkButton.stories.jsx.html +1 -1
  162. package/coverage/ui-kit/stories/atoms/buttons/index.html +1 -1
  163. package/coverage/ui-kit/stories/atoms/index.html +1 -1
  164. package/coverage/ui-kit/stories/molecules/AmountInput.stories.jsx.html +1 -1
  165. package/coverage/ui-kit/stories/molecules/CoinPicker.stories.jsx.html +1 -1
  166. package/coverage/ui-kit/stories/molecules/ColoredNotice.stories.jsx.html +1 -1
  167. package/coverage/ui-kit/stories/molecules/LineWithIconLink.stories.jsx.html +1 -1
  168. package/coverage/ui-kit/stories/molecules/LogoCarousel.stories.jsx.html +1 -1
  169. package/coverage/ui-kit/stories/molecules/TitledLineWithIconLink.stories.jsx.html +1 -1
  170. package/coverage/ui-kit/stories/molecules/index.html +1 -1
  171. package/coverage/ui-kit/stories/organisms/Dialog/Dialog.stories.jsx.html +1 -1
  172. package/coverage/ui-kit/stories/organisms/Dialog/DialogButtons/DialogButtons.stories.jsx.html +1 -1
  173. package/coverage/ui-kit/stories/organisms/Dialog/DialogButtons/index.html +1 -1
  174. package/coverage/ui-kit/stories/organisms/Dialog/DialogStep/DialogStep.stories.jsx.html +1 -1
  175. package/coverage/ui-kit/stories/organisms/Dialog/DialogStep/index.html +1 -1
  176. package/coverage/ui-kit/stories/organisms/Dialog/index.html +1 -1
  177. package/coverage/ui-kit/stories/organisms/WaitlistSubscription.stories.jsx.html +1 -1
  178. package/coverage/ui-kit/stories/organisms/index.html +1 -1
  179. package/coverage/ui-kit/stories/stubs/coins.jsx.html +1 -1
  180. package/coverage/ui-kit/stories/stubs/exampleContent.jsx.html +1 -1
  181. package/coverage/ui-kit/stories/stubs/index.html +1 -1
  182. package/coverage/ui-kit/stories/templates/DeterminedErrorDialogStep.stories.jsx.html +1 -1
  183. package/coverage/ui-kit/stories/templates/index.html +1 -1
  184. package/dist/index.cjs +94 -92
  185. package/dist/index.cjs.map +1 -1
  186. package/dist/index.css +4 -12
  187. package/dist/index.css.map +1 -1
  188. package/dist/index.modern.js +82 -78
  189. package/dist/index.modern.js.map +1 -1
  190. package/dist/index.module.js +94 -92
  191. package/dist/index.module.js.map +1 -1
  192. package/dist/index.umd.js +94 -92
  193. package/dist/index.umd.js.map +1 -1
  194. package/package.json +1 -1
  195. package/src/common-apis/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +9 -5
  196. package/src/swaps-lib/external-apis/exolixSwapProvider.js +46 -39
  197. package/src/swaps-lib/external-apis/goexmeSwapProvider.js +29 -37
  198. package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +54 -46
  199. package/src/swaps-lib/models/existingSwap.js +1 -1
  200. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_convertCurrencyMeta.test.js +80 -0
  201. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_ensurePairsLoaded.test.js +77 -0
  202. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +50 -123
  203. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/createSwap.test.js +184 -72
  204. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/getExistingSwapsDetailsAndStatus.test.js +151 -0
  205. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/getSwapInfo.test.js +155 -104
  206. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/helpers.test.js +26 -0
  207. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/PairSupport.int.test.js +57 -57
  208. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/_fetchSupportedCurrenciesIfNeeded.int.test.js +289 -126
  209. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/createSwap.int.test.js +320 -225
  210. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/getSwapInfo.int.test.js +138 -127
  211. package/src/ui-kit/components/atoms/QrCode/QrCode.jsx +2 -8
  212. package/src/ui-kit/components/atoms/QrCode/qr-code.module.scss +4 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rabbitio/ui-kit",
3
- "version": "1.0.0-beta.110",
3
+ "version": "1.0.0-beta.112",
4
4
  "description": "Rabbit.io react.js components kit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -5,10 +5,14 @@ import { IpAddressProvider } from "../../../../external-apis/ipAddressProviders.
5
5
 
6
6
  describe("ipAddressProviders", function () {
7
7
  describe("#getClientIpAddress", function () {
8
- it("Should successfully retrieve IP address", async function () {
9
- (
10
- await new IpAddressProvider("https://plankton-app-k6der.ondigitalocean.app").getClientIpAddress()
11
- ).should.not.be.empty();
12
- });
8
+ it(
9
+ "Should successfully retrieve IP address",
10
+ async function () {
11
+ (
12
+ await new IpAddressProvider("https://plankton-app-k6der.ondigitalocean.app").getClientIpAddress()
13
+ ).should.not.be.empty();
14
+ },
15
+ { timeout: 100_000 }
16
+ );
13
17
  });
14
18
  });
@@ -501,46 +501,53 @@ export class ExolixSwapProvider extends SwapProvider {
501
501
  const swaps = fetchedSwaps
502
502
  .map(r => r.data)
503
503
  .map((swap, index) => {
504
- const fromCoin = this._supportedCoins.find(
505
- i => i.code === swap.coinFrom.coinCode && i.network === swap.coinFrom.network
506
- )?.coin;
507
- const toCoin = this._supportedCoins.find(
508
- i => i.code === swap.coinTo.coinCode && i.network === swap.coinTo.network
509
- )?.coin;
510
- if (!fromCoin || !toCoin) {
511
- return []; // We skip swaps with not supported coins for now
504
+ try {
505
+ const fromCoin = this._supportedCoins.find(
506
+ i => i.code === swap.coinFrom.coinCode && i.network === swap.coinFrom.network
507
+ )?.coin ?? { ticker: toRabbitTicker(swap.coinFrom.coinCode, swap.coinFrom.network), digits: 8 };
508
+ const toCoin = this._supportedCoins.find(
509
+ i => i.code === swap.coinTo.coinCode && i.network === swap.coinTo.network
510
+ )?.coin ?? { ticker: toRabbitTicker(swap.coinTo.coinCode, swap.coinTo.network), digits: 8 };
511
+ if (!fromCoin || !toCoin) {
512
+ return []; // We skip swaps with not supported coins for now
513
+ }
514
+
515
+ const toUtcTimestamp = timeStr =>
516
+ Date.parse(timeStr.match(/.+[Zz]$/) ? timeStr : `${timeStr}Z`);
517
+ const createdAt = toUtcTimestamp(swap.createdAt);
518
+ const expiresAt = createdAt + 25 * 60 * 1000; // Hack with hard-coded 25 minutes
519
+ const isExpiredByTime = expiresAt < Date.now();
520
+ const status = this._mapExolixStatusToRabbitStatus(swap.status, isExpiredByTime);
521
+ const toDigits =
522
+ status === SwapProvider.SWAP_STATUSES.REFUNDED ? fromCoin.digits : toCoin.digits;
523
+ return new ExistingSwap(
524
+ swapIds[index],
525
+ status,
526
+ createdAt,
527
+ expiresAt,
528
+ -1,
529
+ AmountUtils.trim(swap.rate, this._maxRateDigits),
530
+ swap?.rateType === "fixed",
531
+ swap.refundAddress,
532
+ swap.depositAddress,
533
+ fromCoin,
534
+ AmountUtils.trim(swap.amount, fromCoin.digits),
535
+ swap.hashIn.hash,
536
+ swap.hashIn.link,
537
+ toCoin,
538
+ AmountUtils.trim(swap.amountTo, toDigits),
539
+ swap.hashOut.hash,
540
+ swap.hashOut.link,
541
+ swap.withdrawalAddress,
542
+ this.id,
543
+ swap.depositExtraId ?? null,
544
+ swap.withdrawalExtraId ?? null,
545
+ swap.refundExtraId ?? null
546
+ );
547
+ } catch (e) {
548
+ console.log("Failed to parse loaded swap", e);
549
+ return [];
512
550
  }
513
-
514
- const toUtcTimestamp = timeStr => Date.parse(timeStr.match(/.+[Zz]$/) ? timeStr : `${timeStr}Z`);
515
- const createdAt = toUtcTimestamp(swap.createdAt);
516
- const expiresAt = createdAt + 25 * 60 * 1000; // Hack with hard-coded 25 minutes
517
- const isExpiredByTime = expiresAt < Date.now();
518
- const status = this._mapExolixStatusToRabbitStatus(swap.status, isExpiredByTime);
519
- const toDigits = status === SwapProvider.SWAP_STATUSES.REFUNDED ? fromCoin.digits : toCoin.digits;
520
- return new ExistingSwap(
521
- swapIds[index],
522
- status,
523
- createdAt,
524
- expiresAt,
525
- -1,
526
- AmountUtils.trim(swap.rate, this._maxRateDigits),
527
- swap?.rateType === "fixed",
528
- swap.refundAddress,
529
- swap.depositAddress,
530
- fromCoin,
531
- AmountUtils.trim(swap.amount, fromCoin.digits),
532
- swap.hashIn.hash,
533
- swap.hashIn.link,
534
- toCoin,
535
- AmountUtils.trim(swap.amountTo, toDigits),
536
- swap.hashOut.hash,
537
- swap.hashOut.link,
538
- swap.withdrawalAddress,
539
- this.id,
540
- swap.depositExtraId ?? null,
541
- swap.withdrawalExtraId ?? null,
542
- swap.refundExtraId ?? null
543
- );
544
551
  })
545
552
  .flat();
546
553
  Logger.log(`Swap details result ${safeStringify(swaps)}`, loggerSource);
@@ -361,10 +361,7 @@ export class GoexmeSwapProvider extends SwapProvider {
361
361
  };
362
362
  }
363
363
 
364
- /* --------------------------------------------------------
365
- * Convert amountTo → ratio so that downstream code can do
366
- * amountFrom * rate === amountTo
367
- * -------------------------------------------------------- */
364
+ // Convert amountTo → ratio so that downstream code can do amountFrom * rate === amountTo
368
365
  let rateRatio = null;
369
366
  if (response.data.rate != null && BigNumber(amountCoins).gt(0)) {
370
367
  rateRatio = AmountUtils.trim(
@@ -385,10 +382,6 @@ export class GoexmeSwapProvider extends SwapProvider {
385
382
  rawSwapData: response.data,
386
383
  /**
387
384
  * GoExme never requires a refund address.
388
- *
389
- * The official API documentation marks `destinationTag` / `refundTag` as **optional**.
390
- * Support chat confirmation (2025‑06‑27): "Yes, it's optional and can be omitted even for small amounts."
391
- *
392
385
  */
393
386
  isRefundAddressRequired: false,
394
387
  };
@@ -407,23 +400,28 @@ export class GoexmeSwapProvider extends SwapProvider {
407
400
  /**
408
401
  * Creates a swap on GoExme.
409
402
  *
410
- * We cannot rely on what upper layers put into
411
- * `proprietarySwapEstimationData.rate`:
412
- * • sometimes it is the **amountTo** (GoExme original value);
413
- * • sometimes it is already a **ratio** – but we cannot know.
403
+ * The `proprietarySwapEstimationData` object **must** carry two distinct values
404
+ * produced by a prior `/rate` call:
414
405
  *
415
- * The only safe approach: treat any numeric value we get from
416
- * estimation as amountTo, then derive the ratio ourselves.
406
+ * `rawSwapData.rate` **amountTo**
407
+ * (the exact number of coins GoExme will send to the user);
408
+ * • `rate` – **ratio**
409
+ * (`amountTo / amountFrom`, i.e. Rabbit‑style per‑coin rate).
417
410
  *
418
- * Steps:
419
- * 1. Pick the first non-null of
420
- * estimation.rawSwapData.rate
421
- * estimation.rate
422
- * (both are amountTo for GoExme).
423
- * 2. Store it as `toAmountCalculated`.
424
- * 3. Compute `rateRatio = toAmount / amountFrom`.
425
- * both mandatory fields (`rate`, `withdrawal.amount`)
426
- * are always present, avoiding schema validation errors.
411
+ * Processing steps
412
+ * ----------------
413
+ * 1. Determine **amountTo** (`rawSwapData.rate`).
414
+ * 2. Compute the canonical per‑coin **ratio**:
415
+ *
416
+ * ratio = amountTo / amountFrom
417
+ *
418
+ * 3. Return both numbers for downstream code:
419
+ * `rate` → ratio (Rabbit convention);
420
+ * • `toAmount` → amountTo (final coins the user receives).
421
+ *
422
+ * This guarantees the invariant:
423
+ *
424
+ * amountFrom * rate === toAmount
427
425
  */
428
426
  async createSwap(
429
427
  fromCoin,
@@ -474,6 +472,9 @@ export class GoexmeSwapProvider extends SwapProvider {
474
472
  };
475
473
 
476
474
  params.amount = amount;
475
+
476
+ // Add refundAddress only if extraId is present:
477
+ // GoExme documents these fields as optional, and support (2025‑06‑27) confirmed that.
477
478
  if (toCoinInfo.hasExtraId && toCurrencyExtraId) params.destinationTag = toCurrencyExtraId;
478
479
  if (refundAddress) {
479
480
  params.refundAddress = refundAddress;
@@ -494,9 +495,7 @@ export class GoexmeSwapProvider extends SwapProvider {
494
495
  throw new Error(errMsg);
495
496
  }
496
497
 
497
- /*------------------------------------------------------------
498
- * Extract amountTo from estimation (first non-null source).
499
- *-----------------------------------------------------------*/
498
+ // Extract amountTo from estimation (first non-null source).
500
499
  const amountToCandidate =
501
500
  proprietarySwapEstimationData?.rawSwapData?.rate ?? proprietarySwapEstimationData?.rate ?? null;
502
501
 
@@ -506,9 +505,7 @@ export class GoexmeSwapProvider extends SwapProvider {
506
505
 
507
506
  const toAmountCalculated = AmountUtils.trim(amountToCandidate, toCoin.digits);
508
507
 
509
- /*------------------------------------------------------------
510
- * Derive ratio so that amountFrom * ratio = amountTo.
511
- *-----------------------------------------------------------*/
508
+ // Derive ratio so that amountFrom * ratio = amountTo.
512
509
  const rateRatio = AmountUtils.trim(
513
510
  BigNumber(toAmountCalculated).div(amount).toString(),
514
511
  this._maxRateDigits
@@ -561,26 +558,23 @@ export class GoexmeSwapProvider extends SwapProvider {
561
558
 
562
559
  const statusResponses = await Promise.all(swapIds.map(fetchOne));
563
560
 
564
- /* helper: normalize coin code using CODE_TRANSLATIONS */
561
+ // helper: normalize coin code using CODE_TRANSLATIONS
565
562
  const normalize = code => CODE_TRANSLATIONS[code.toUpperCase()] ?? code.toUpperCase();
566
563
 
567
564
  const swaps = statusResponses
568
565
  .map((statusData, swapIndex) => {
569
566
  if (!statusData) return null;
570
567
 
571
- /* ------------------ coins ------------------ */
572
568
  const fromCoinTicker = toRabbitTicker(normalize(statusData.from), "");
573
569
  const toCoinTicker = toRabbitTicker(normalize(statusData.to), "");
574
570
 
575
571
  let fromCoin = this.getCoinByTickerIfPresent(fromCoinTicker);
576
572
  let toCoin = this.getCoinByTickerIfPresent(toCoinTicker);
577
573
 
578
- /* fallback: look up by raw provider code if ticker lookup failed */
579
574
  if (!fromCoin) {
580
575
  const byCode = this._supportedCoins.find(c => c.code === statusData.from);
581
576
  fromCoin = byCode?.coin ?? null;
582
577
  }
583
- /* fallback: look up by raw provider code if ticker lookup failed */
584
578
  if (!toCoin) {
585
579
  const byCode = this._supportedCoins.find(c => c.code === statusData.to);
586
580
  toCoin = byCode?.coin ?? null;
@@ -588,11 +582,9 @@ export class GoexmeSwapProvider extends SwapProvider {
588
582
 
589
583
  if (!fromCoin || !toCoin) return null;
590
584
 
591
- /* timestamps */
592
585
  const createdAt = GoexmeSwapProvider._parseGoexmeDate(statusData.date);
593
586
  const expiresAt = createdAt + this.SWAP_EXPIRATION_TIME_MS;
594
587
 
595
- /* amounts & rate */
596
588
  const amountFromEffective = statusData.amountFrom ?? statusData.expectedAmountFrom ?? "0";
597
589
  const amountToEffective = statusData.amountTo ?? statusData.expectedAmountTo ?? null;
598
590
  let rateVal = statusData.rate;
@@ -620,11 +612,11 @@ export class GoexmeSwapProvider extends SwapProvider {
620
612
  statusData.depositAddress,
621
613
  fromCoin,
622
614
  AmountUtils.trim(amountFromEffective, fromCoin.digits),
623
- null,
615
+ statusData.depositHash,
624
616
  null,
625
617
  toCoin,
626
618
  toAmountTrimmed,
627
- statusData.txId ?? null,
619
+ statusData.txId,
628
620
  null,
629
621
  statusData.destinationAddress,
630
622
  this.id,
@@ -509,56 +509,64 @@ export class SwapspaceSwapProvider extends SwapProvider {
509
509
  };
510
510
  const responses = await Promise.all(swapIds.map(swapId => getNotFailingOn404(swapId)));
511
511
  const wo404 = responses.flat();
512
- Logger.log("All swaps RAW: " + JSON.stringify(wo404.map(r => r.data)), loggerSource);
512
+ Logger.log("All swaps RAW swapspace: " + JSON.stringify(wo404.map(r => r.data)), loggerSource);
513
513
  const swaps = wo404
514
514
  .map(r => r.data)
515
515
  .map((swap, index) => {
516
- const fromCoin = this._supportedCoins.find(
517
- i => i.code === swap.from.code && i.network === swap.from.network
518
- )?.coin;
519
- const toCoin = this._supportedCoins.find(
520
- i => i.code === swap.to.code && i.network === swap.to.network
521
- )?.coin;
522
- if (!fromCoin || !toCoin) {
523
- return []; // We skip swaps with not supported coins for now
524
- }
516
+ try {
517
+ let fromCoin = this._supportedCoins.find(
518
+ i => i.code === swap.from.code && i.network === swap.from.network
519
+ )?.coin ?? { ticker: toRabbitTicker(swap.from.code, swap.from.network), digits: 8 };
520
+ let toCoin = this._supportedCoins.find(
521
+ i => i.code === swap.to.code && i.network === swap.to.network
522
+ )?.coin ?? { ticker: toRabbitTicker(swap.to.code, swap.to.network), digits: 8 };
523
+
524
+ if (!fromCoin || !toCoin) {
525
+ return []; // We skip swaps with not supported coins for now
526
+ }
525
527
 
526
- const toUtcTimestamp = timeStr => Date.parse(timeStr.match(/.+[Zz]$/) ? timeStr : `${timeStr}Z`);
527
- const expiresAt = toUtcTimestamp(swap.timestamps.expiresAt);
528
- const isExpiredByTime = expiresAt < Date.now();
529
- const status = this._mapSwapspaceStatusToRabbitStatus(swap.status, isExpiredByTime);
530
- const toDigits = status === SwapProvider.SWAP_STATUSES.REFUNDED ? fromCoin.digits : toCoin.digits;
531
- const addressToSendCoinsToSwapspace = swap.from.address;
532
- const toUrl = (swap.blockExplorerTransactionUrl.to ?? "")
533
- .replace(/from=swapspace&?/g, "")
534
- .replaceAll(/&&+/g, "&")
535
- .replace("?&", "?")
536
- .replace(/&$/, "")
537
- .replace(/\?$/, "");
538
- return new ExistingSwap(
539
- swapIds[index],
540
- status,
541
- toUtcTimestamp(swap.timestamps.createdAt),
542
- expiresAt,
543
- swap.confirmations,
544
- AmountUtils.trim(swap.rate, this._maxRateDigits),
545
- swap.fixed,
546
- swap.refundAddress,
547
- addressToSendCoinsToSwapspace,
548
- fromCoin,
549
- AmountUtils.trim(swap.from.amount, fromCoin.digits),
550
- swap.from.transactionHash,
551
- swap.blockExplorerTransactionUrl.from,
552
- toCoin,
553
- AmountUtils.trim(swap.to.amount, toDigits),
554
- swap.to.transactionHash,
555
- toUrl,
556
- swap.to.address,
557
- swap.partner,
558
- swap.from.extraId ?? null,
559
- swap.to.extraId ?? null,
560
- swap.refundExtraId ?? null
561
- );
528
+ const toUtcTimestamp = timeStr =>
529
+ Date.parse(timeStr.match(/.+[Zz]$/) ? timeStr : `${timeStr}Z`);
530
+ const expiresAt = toUtcTimestamp(swap.timestamps.expiresAt);
531
+ const isExpiredByTime = expiresAt < Date.now();
532
+ const status = this._mapSwapspaceStatusToRabbitStatus(swap.status, isExpiredByTime);
533
+ const toDigits =
534
+ status === SwapProvider.SWAP_STATUSES.REFUNDED ? fromCoin.digits : toCoin.digits;
535
+ const addressToSendCoinsToSwapspace = swap.from.address;
536
+ const toUrl = (swap.blockExplorerTransactionUrl.to ?? "")
537
+ .replace(/from=swapspace&?/g, "")
538
+ .replaceAll(/&&+/g, "&")
539
+ .replace("?&", "?")
540
+ .replace(/&$/, "")
541
+ .replace(/\?$/, "");
542
+ return new ExistingSwap(
543
+ swapIds[index],
544
+ status,
545
+ toUtcTimestamp(swap.timestamps.createdAt),
546
+ expiresAt,
547
+ swap.confirmations,
548
+ AmountUtils.trim(swap.rate, this._maxRateDigits),
549
+ swap.fixed,
550
+ swap.refundAddress,
551
+ addressToSendCoinsToSwapspace,
552
+ fromCoin,
553
+ AmountUtils.trim(swap.from.amount, fromCoin.digits),
554
+ swap.from.transactionHash,
555
+ swap.blockExplorerTransactionUrl.from,
556
+ toCoin,
557
+ AmountUtils.trim(swap.to.amount, toDigits),
558
+ swap.to.transactionHash,
559
+ toUrl,
560
+ swap.to.address,
561
+ swap.partner,
562
+ swap.from.extraId ?? null,
563
+ swap.to.extraId ?? null,
564
+ swap.refundExtraId ?? null
565
+ );
566
+ } catch (e) {
567
+ console.log("Failed to parse loaded swap", e);
568
+ return [];
569
+ }
562
570
  })
563
571
  .flat();
564
572
  Logger.log(`Swap details result ${safeStringify(swaps)}`, loggerSource);
@@ -76,7 +76,7 @@ export class ExistingSwap {
76
76
  * @param {number} expiresAt - The timestamp when the swap expires.
77
77
  * @param {number|null} confirmations - The number of confirmations the swap has received, or null.
78
78
  * @param {string} rate - The exchange rate for the swap.
79
- * @param {null} fixed - Indicates if the rate is fixed.
79
+ * @param {boolean} fixed - Indicates if the rate is fixed.
80
80
  * @param {string|null} refundAddress - The address to refund to in case of failure, or null.
81
81
  * @param {string} payToAddress - The address to send the funds to.
82
82
  * @param {Coin} fromCoin - The coin that is being swapped from.
@@ -0,0 +1,80 @@
1
+ import should from "should";
2
+ import { describe, it } from "vitest";
3
+
4
+ import { GoexmeSwapProvider } from "../../../external-apis/goexmeSwapProvider.js";
5
+ import { Cache } from "../../../../common-apis/utils/cache.js";
6
+ import { Coin } from "../../../../common-apis/models/coin.js";
7
+ import { Protocol } from "../../../../common-apis/models/protocol.js";
8
+
9
+ describe("goexmeSwapProvider", function () {
10
+ describe("_convertCurrencyMeta", function () {
11
+ it("Should return null for restricted set", () => {
12
+ const restrictedProvider = new GoexmeSwapProvider("/", new Cache(), () => null, true);
13
+ should(restrictedProvider._convertCurrencyMeta("abc", { coinName: "Abc" })).be.null();
14
+ });
15
+
16
+ it("Should auto‑construct Coin when builder is null and unrestricted", () => {
17
+ const unrestrictedProvider = new GoexmeSwapProvider("/", new Cache(), null, false);
18
+ const currencyInfo = unrestrictedProvider._convertCurrencyMeta("btc", {
19
+ coinName: "Bitcoin Test",
20
+ network: "",
21
+ available: true,
22
+ });
23
+ currencyInfo.should.have.properties({ code: "BTC", network: null });
24
+ currencyInfo.coin.should.be.instanceOf(Coin);
25
+ if (currencyInfo.coin.protocol) currencyInfo.coin.protocol.should.be.instanceOf(Protocol);
26
+ });
27
+
28
+ it("Should set hasExtraId=true and extraIdName when tagname present", () => {
29
+ const unrestrictedProvider = new GoexmeSwapProvider("/", new Cache(), null, false);
30
+ const currencyInfo = unrestrictedProvider._convertCurrencyMeta("xrp", {
31
+ coinName: "Ripple",
32
+ network: "",
33
+ available: true,
34
+ tagname: "DestinationTag",
35
+ });
36
+ currencyInfo.hasExtraId.should.be.true();
37
+ currencyInfo.extraIdName.should.equal("DestinationTag");
38
+ });
39
+
40
+ it("Should set hasExtraId=false and extraIdName=null when no tagname", () => {
41
+ const unrestrictedProvider = new GoexmeSwapProvider("/", new Cache(), null, false);
42
+ const currencyInfo = unrestrictedProvider._convertCurrencyMeta("ltc", {
43
+ coinName: "Litecoin",
44
+ network: "",
45
+ available: true,
46
+ });
47
+ currencyInfo.hasExtraId.should.be.false();
48
+ should(currencyInfo.extraIdName).be.null();
49
+ });
50
+ });
51
+
52
+ describe("constructor", function () {
53
+ it("Should default _URL to proxy path when apiKey empty", () => {
54
+ const provider = new GoexmeSwapProvider("/proxy", new Cache());
55
+ provider._URL.should.equal("/proxy/goexme");
56
+ });
57
+
58
+ it("Should set direct URL when apiKey passed", () => {
59
+ const providerWithKey = new GoexmeSwapProvider("/proxy", new Cache(), () => null, false, "key");
60
+ providerWithKey._URL.should.equal("https://goexme.io/v1/api");
61
+ });
62
+
63
+ it("Should not set Authorization header", () => {
64
+ const providerWithSecret = new GoexmeSwapProvider("/proxy", new Cache(), () => null, false, "mySecret");
65
+ should(providerWithSecret.headers).not.have.property("Authorization");
66
+ });
67
+
68
+ it("Should not set any unexpected headers", () => {
69
+ const providerWithSecret = new GoexmeSwapProvider("/proxy", new Cache(), () => null, false, "mySecret");
70
+ Object.keys(providerWithSecret.headers).should.eql([]);
71
+ });
72
+
73
+ it("Should allow only 200, 400 and 404 in validateStatus", () => {
74
+ const defaultProvider = new GoexmeSwapProvider("/", new Cache());
75
+ const allowedStatuses = [200, 400, 404];
76
+ allowedStatuses.forEach(status => defaultProvider.axiosPreset.validateStatus(status).should.be.true());
77
+ [199, 500].forEach(status => defaultProvider.axiosPreset.validateStatus(status).should.be.false());
78
+ });
79
+ });
80
+ });
@@ -0,0 +1,77 @@
1
+ import sinon from "sinon";
2
+ import axios from "axios";
3
+ import should from "should";
4
+ import { beforeEach, afterEach, describe, it } from "vitest";
5
+
6
+ import { GoexmeSwapProvider } from "../../../external-apis/goexmeSwapProvider.js";
7
+ import { Cache } from "../../../../common-apis/utils/cache.js";
8
+
9
+ describe("goexmeSwapProvider", function () {
10
+ describe("_ensurePairsLoaded", function () {
11
+ let provider, axiosGetStub;
12
+
13
+ beforeEach(() => {
14
+ provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
15
+ axiosGetStub = sinon.stub(axios, "get").callsFake(url => {
16
+ if (url.includes("/pairs")) {
17
+ return Promise.resolve({
18
+ status: 200,
19
+ data: { btc: ["eth", "ETH"] },
20
+ });
21
+ }
22
+ return Promise.resolve({ status: 200, data: {} });
23
+ });
24
+ });
25
+
26
+ afterEach(() => {
27
+ axiosGetStub.restore();
28
+ });
29
+
30
+ it("Should normalise codes to upper‑case", async () => {
31
+ await provider._ensurePairsLoaded();
32
+ provider._pairsCache.should.have.property("BTC");
33
+ });
34
+
35
+ it("Should remove duplicate tickers", async () => {
36
+ await provider._ensurePairsLoaded();
37
+ Array.from(provider._pairsCache.BTC).should.eql(["ETH"]);
38
+ });
39
+
40
+ it("Should not call axios again while pairs cache is fresh", async () => {
41
+ await provider._ensurePairsLoaded();
42
+ const calls = axiosGetStub.callCount;
43
+ await provider._ensurePairsLoaded();
44
+ axiosGetStub.callCount.should.equal(calls);
45
+ });
46
+
47
+ it("Should refresh pairs after expiration", async () => {
48
+ await provider._ensurePairsLoaded();
49
+ const calls = axiosGetStub.callCount;
50
+
51
+ provider._pairsCacheTimestamp -= provider.PAIRS_EXPIRATION_TIME_MS + 1;
52
+
53
+ await provider._ensurePairsLoaded();
54
+ axiosGetStub.callCount.should.equal(calls + 1);
55
+ });
56
+
57
+ it("Should reject when response status is not 200", async () => {
58
+ axiosGetStub.resetBehavior();
59
+ axiosGetStub.resolves({ status: 400, data: {} });
60
+
61
+ return provider._ensurePairsLoaded().should.be.rejected();
62
+ });
63
+
64
+ it("Should reject when data is not an object", async () => {
65
+ axiosGetStub.resetBehavior();
66
+ axiosGetStub.resolves({ status: 200, data: "not‑object" });
67
+
68
+ return provider._ensurePairsLoaded().should.be.rejected();
69
+ });
70
+
71
+ it("Should propagate axios error", async () => {
72
+ axiosGetStub.resetBehavior();
73
+ axiosGetStub.rejects(new Error("network"));
74
+ return provider._ensurePairsLoaded().should.be.rejectedWith(/network/);
75
+ });
76
+ });
77
+ });