@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.
- package/.env +1 -1
- package/.env.example +1 -1
- package/coverage/clover.xml +1411 -1410
- package/coverage/coverage-final.json +18 -18
- package/coverage/index.html +17 -17
- package/coverage/ui-kit/index.html +1 -1
- package/coverage/ui-kit/index.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/adapters/axiosAdapter.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/adapters/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/adapters/qrUtils.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/external-apis/apiGroups.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/external-apis/emailAPI.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/external-apis/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/external-apis/ipAddressProviders.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/globalConstants.jsx.html +1 -1
- package/coverage/ui-kit/src/common-apis/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/models/blockchain.js.html +4 -4
- package/coverage/ui-kit/src/common-apis/models/coin.js.html +221 -221
- package/coverage/ui-kit/src/common-apis/models/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/models/protocol.js.html +3 -3
- package/coverage/ui-kit/src/common-apis/services/fiatCurrenciesService.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/services/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/utils/amountUtils.js.html +21 -21
- package/coverage/ui-kit/src/common-apis/utils/cache.js.html +6 -6
- package/coverage/ui-kit/src/common-apis/utils/errorUtils.js.html +13 -13
- package/coverage/ui-kit/src/common-apis/utils/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/utils/logging/index.html +1 -1
- package/coverage/ui-kit/src/common-apis/utils/logging/logger.js.html +22 -22
- package/coverage/ui-kit/src/common-apis/utils/logging/logsStorage.js.html +3 -3
- package/coverage/ui-kit/src/common-apis/utils/postponeExecution.js.html +1 -1
- package/coverage/ui-kit/src/common-apis/utils/rabbitTicker.js.html +10 -10
- package/coverage/ui-kit/src/common-apis/utils/safeStringify.js.html +27 -27
- package/coverage/ui-kit/src/index.html +1 -1
- package/coverage/ui-kit/src/index.js.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/cachedRobustExternalApiCallerService.js.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/cancelProcessing.js.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/concurrentCalculationsMetadataHolder.js.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/externalApiProvider.js.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/externalServicesStatsCollector.js.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/index.html +1 -1
- package/coverage/ui-kit/src/robust-api-caller/robustExternalAPICallerService.js.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/external-apis/exolixSwapProvider.js.html +66 -45
- package/coverage/ui-kit/src/swaps-lib/external-apis/goexmeSwapProvider.js.html +521 -545
- package/coverage/ui-kit/src/swaps-lib/external-apis/index.html +27 -27
- package/coverage/ui-kit/src/swaps-lib/external-apis/letsExchangeSwapProvider.js.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +328 -328
- package/coverage/ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +76 -52
- package/coverage/ui-kit/src/swaps-lib/external-apis/utils.js.html +14 -14
- package/coverage/ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/models/existingSwap.js.html +141 -141
- package/coverage/ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/models/index.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/models/partner.js.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/models/swapProviderCoinInfo.js.html +73 -73
- package/coverage/ui-kit/src/swaps-lib/services/index.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/services/publicSwapService.js.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/utils/index.html +1 -1
- package/coverage/ui-kit/src/swaps-lib/utils/swapUtils.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowTosca.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowWhite.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/darkRectangle.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/determinedError.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/infoIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/messageIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/walletIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/AssetIcon/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/AssetSelection/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/BackgroundTitle/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/InformationMessage/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Input/Input.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Input/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/LoadingDots/LoadingDots.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/LoadingDots/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/NoticeIcon/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/QrCode/QrCode.jsx.html +7 -25
- package/coverage/ui-kit/src/ui-kit/components/atoms/QrCode/index.html +5 -5
- package/coverage/ui-kit/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/RateSelector/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/SupportChat/SupportChat.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/SupportChat/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Textarea/Textarea.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Textarea/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/TitleBox/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Tooltip/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Validation/Validation.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/Validation/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Button/Button.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Button/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Close/Close.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Close/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/AmountInput/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/CoinPicker/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/ColoredNotice/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/LineWithIconLink/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/LogoCarousel/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/Dialog.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/SwapForm/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx.html +1 -1
- package/coverage/ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/hooks/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/hooks/useCallHandlingErrors.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/hooks/useIsHydrated.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/hooks/useReferredState.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/utils/index.html +1 -1
- package/coverage/ui-kit/src/ui-kit/utils/inputValueProviders.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/utils/searchCoins.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/utils/textUtils.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/utils/uiUtils.js.html +1 -1
- package/coverage/ui-kit/src/ui-kit/utils/urlQueryUtils.js.html +1 -1
- package/coverage/ui-kit/stories/atoms/BackgroundTitle.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/LinesOfText.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/LoadingDots.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/QrCode.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/RateSelector.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/Validation.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/buttons/Button.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/buttons/Close.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/buttons/LinkButton.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/atoms/buttons/index.html +1 -1
- package/coverage/ui-kit/stories/atoms/index.html +1 -1
- package/coverage/ui-kit/stories/molecules/AmountInput.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/molecules/CoinPicker.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/molecules/ColoredNotice.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/molecules/LineWithIconLink.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/molecules/LogoCarousel.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/molecules/TitledLineWithIconLink.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/molecules/index.html +1 -1
- package/coverage/ui-kit/stories/organisms/Dialog/Dialog.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/organisms/Dialog/DialogButtons/DialogButtons.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/organisms/Dialog/DialogButtons/index.html +1 -1
- package/coverage/ui-kit/stories/organisms/Dialog/DialogStep/DialogStep.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/organisms/Dialog/DialogStep/index.html +1 -1
- package/coverage/ui-kit/stories/organisms/Dialog/index.html +1 -1
- package/coverage/ui-kit/stories/organisms/WaitlistSubscription.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/organisms/index.html +1 -1
- package/coverage/ui-kit/stories/stubs/coins.jsx.html +1 -1
- package/coverage/ui-kit/stories/stubs/exampleContent.jsx.html +1 -1
- package/coverage/ui-kit/stories/stubs/index.html +1 -1
- package/coverage/ui-kit/stories/templates/DeterminedErrorDialogStep.stories.jsx.html +1 -1
- package/coverage/ui-kit/stories/templates/index.html +1 -1
- package/dist/index.cjs +94 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +4 -12
- package/dist/index.css.map +1 -1
- package/dist/index.modern.js +82 -78
- package/dist/index.modern.js.map +1 -1
- package/dist/index.module.js +94 -92
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +94 -92
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/common-apis/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +9 -5
- package/src/swaps-lib/external-apis/exolixSwapProvider.js +46 -39
- package/src/swaps-lib/external-apis/goexmeSwapProvider.js +29 -37
- package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +54 -46
- package/src/swaps-lib/models/existingSwap.js +1 -1
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_convertCurrencyMeta.test.js +80 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_ensurePairsLoaded.test.js +77 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +50 -123
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/createSwap.test.js +184 -72
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/getExistingSwapsDetailsAndStatus.test.js +151 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/getSwapInfo.test.js +155 -104
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/helpers.test.js +26 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/PairSupport.int.test.js +57 -57
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/_fetchSupportedCurrenciesIfNeeded.int.test.js +289 -126
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/createSwap.int.test.js +320 -225
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/getSwapInfo.int.test.js +138 -127
- package/src/ui-kit/components/atoms/QrCode/QrCode.jsx +2 -8
- package/src/ui-kit/components/atoms/QrCode/qr-code.module.scss +4 -11
package/package.json
CHANGED
|
@@ -5,10 +5,14 @@ import { IpAddressProvider } from "../../../../external-apis/ipAddressProviders.
|
|
|
5
5
|
|
|
6
6
|
describe("ipAddressProviders", function () {
|
|
7
7
|
describe("#getClientIpAddress", function () {
|
|
8
|
-
it(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
-
*
|
|
411
|
-
* `
|
|
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
|
-
*
|
|
416
|
-
*
|
|
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
|
-
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
615
|
+
statusData.depositHash,
|
|
624
616
|
null,
|
|
625
617
|
toCoin,
|
|
626
618
|
toAmountTrimmed,
|
|
627
|
-
statusData.txId
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
.
|
|
534
|
-
.
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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 {
|
|
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
|
+
});
|