@rabbitio/ui-kit 1.0.0-beta.11 → 1.0.0-beta.111
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.example +1 -0
- package/.gitlab-ci.yml +29 -0
- package/.husky/commit-msg +19 -0
- package/.husky/pre-push +1 -0
- package/README.md +14 -4
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +17666 -0
- package/coverage/coverage-final.json +119 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +1001 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/rabbit-ui-kit/index.html +116 -0
- package/coverage/rabbit-ui-kit/index.js.html +88 -0
- package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js.html +151 -0
- package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js.html +172 -0
- package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js.html +199 -0
- package/coverage/rabbit-ui-kit/src/common-apis/adapters/axiosAdapter.js.html +190 -0
- package/coverage/rabbit-ui-kit/src/common-apis/adapters/index.html +131 -0
- package/coverage/rabbit-ui-kit/src/common-apis/adapters/qrUtils.js.html +139 -0
- package/coverage/rabbit-ui-kit/src/common-apis/external-apis/apiGroups.js.html +253 -0
- package/coverage/rabbit-ui-kit/src/common-apis/external-apis/emailAPI.js.html +133 -0
- package/coverage/rabbit-ui-kit/src/common-apis/external-apis/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/common-apis/external-apis/ipAddressProviders.js.html +391 -0
- package/coverage/rabbit-ui-kit/src/common-apis/globalConstants.jsx.html +94 -0
- package/coverage/rabbit-ui-kit/src/common-apis/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/common-apis/models/blockchain.js.html +115 -0
- package/coverage/rabbit-ui-kit/src/common-apis/models/coin.js.html +829 -0
- package/coverage/rabbit-ui-kit/src/common-apis/models/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/common-apis/models/protocol.js.html +100 -0
- package/coverage/rabbit-ui-kit/src/common-apis/services/fiatCurrenciesService.js.html +544 -0
- package/coverage/rabbit-ui-kit/src/common-apis/services/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/amountUtils.js.html +1234 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/cache.js.html +811 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/errorUtils.js.html +211 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/index.html +191 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/index.html +131 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/logger.js.html +211 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/logsStorage.js.html +268 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/postponeExecution.js.html +118 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/rabbitTicker.js.html +157 -0
- package/coverage/rabbit-ui-kit/src/common-apis/utils/safeStringify.js.html +235 -0
- package/coverage/rabbit-ui-kit/src/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/index.js.html +382 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js.html +1660 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/cachedRobustExternalApiCallerService.js.html +526 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/cancelProcessing.js.html +172 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/concurrentCalculationsMetadataHolder.js.html +310 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/externalApiProvider.js.html +553 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/externalServicesStatsCollector.js.html +319 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/index.html +206 -0
- package/coverage/rabbit-ui-kit/src/robust-api-caller/robustExternalAPICallerService.js.html +997 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/exolixSwapProvider.js.html +1891 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/goexmeSwapProvider.js.html +2077 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/index.html +191 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/letsExchangeSwapProvider.js.html +1633 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +1825 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +1924 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/utils.js.html +163 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +319 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwap.js.html +514 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +529 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/index.html +176 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/partner.js.html +166 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/swapProviderCoinInfo.js.html +331 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/services/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/services/publicSwapService.js.html +1570 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/utils/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/utils/swapUtils.js.html +706 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowIcon.jsx.html +124 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowTosca.jsx.html +127 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowWhite.jsx.html +127 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/darkRectangle.jsx.html +106 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/determinedError.jsx.html +439 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx.html +202 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/index.html +281 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/infoIcon.jsx.html +133 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/messageIcon.jsx.html +346 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx.html +247 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx.html +163 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx.html +268 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/walletIcon.jsx.html +151 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx.html +256 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetIcon/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx.html +289 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetSelection/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx.html +187 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/BackgroundTitle/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx.html +238 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/InformationMessage/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Input/Input.jsx.html +634 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Input/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/LoadingDots/LoadingDots.jsx.html +196 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/LoadingDots/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx.html +277 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/NoticeIcon/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/QrCode/QrCode.jsx.html +199 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/QrCode/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx.html +175 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/RateSelector/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/SupportChat/SupportChat.jsx.html +217 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/SupportChat/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Textarea/Textarea.jsx.html +529 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Textarea/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx.html +508 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TitleBox/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx.html +316 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Tooltip/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx.html +313 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Validation/Validation.jsx.html +202 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Validation/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Button/Button.jsx.html +733 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Button/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Close/Close.jsx.html +259 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Close/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx.html +433 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx.html +415 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx.html +1429 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/AmountInput/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx.html +1474 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/CoinPicker/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx.html +241 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/ColoredNotice/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx.html +190 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LineWithIconLink/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx.html +307 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LogoCarousel/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx.html +427 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx.html +181 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx.html +283 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/Dialog.jsx.html +1567 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx.html +481 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx.html +1747 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx.html +4228 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/SwapForm/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx.html +559 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx.html +316 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/hooks/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useCallHandlingErrors.js.html +166 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useIsHydrated.js.html +121 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useReferredState.js.html +157 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/utils/index.html +176 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/utils/inputValueProviders.js.html +235 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/utils/searchCoins.js.html +259 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/utils/textUtils.js.html +139 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/utils/uiUtils.js.html +121 -0
- package/coverage/rabbit-ui-kit/src/ui-kit/utils/urlQueryUtils.js.html +271 -0
- package/coverage/rabbit-ui-kit/stories/atoms/BackgroundTitle.stories.jsx.html +202 -0
- package/coverage/rabbit-ui-kit/stories/atoms/LinesOfText.stories.jsx.html +283 -0
- package/coverage/rabbit-ui-kit/stories/atoms/LoadingDots.stories.jsx.html +226 -0
- package/coverage/rabbit-ui-kit/stories/atoms/QrCode.stories.jsx.html +175 -0
- package/coverage/rabbit-ui-kit/stories/atoms/RateSelector.stories.jsx.html +136 -0
- package/coverage/rabbit-ui-kit/stories/atoms/Validation.stories.jsx.html +178 -0
- package/coverage/rabbit-ui-kit/stories/atoms/buttons/Button.stories.jsx.html +883 -0
- package/coverage/rabbit-ui-kit/stories/atoms/buttons/Close.stories.jsx.html +211 -0
- package/coverage/rabbit-ui-kit/stories/atoms/buttons/LinkButton.stories.jsx.html +301 -0
- package/coverage/rabbit-ui-kit/stories/atoms/buttons/index.html +146 -0
- package/coverage/rabbit-ui-kit/stories/atoms/index.html +191 -0
- package/coverage/rabbit-ui-kit/stories/molecules/AmountInput.stories.jsx.html +289 -0
- package/coverage/rabbit-ui-kit/stories/molecules/CoinPicker.stories.jsx.html +322 -0
- package/coverage/rabbit-ui-kit/stories/molecules/ColoredNotice.stories.jsx.html +178 -0
- package/coverage/rabbit-ui-kit/stories/molecules/LineWithIconLink.stories.jsx.html +154 -0
- package/coverage/rabbit-ui-kit/stories/molecules/LogoCarousel.stories.jsx.html +235 -0
- package/coverage/rabbit-ui-kit/stories/molecules/TitledLineWithIconLink.stories.jsx.html +160 -0
- package/coverage/rabbit-ui-kit/stories/molecules/index.html +191 -0
- package/coverage/rabbit-ui-kit/stories/organisms/Dialog/Dialog.stories.jsx.html +523 -0
- package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/DialogButtons.stories.jsx.html +328 -0
- package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/index.html +116 -0
- package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/DialogStep.stories.jsx.html +337 -0
- package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/index.html +116 -0
- package/coverage/rabbit-ui-kit/stories/organisms/Dialog/index.html +116 -0
- package/coverage/rabbit-ui-kit/stories/organisms/WaitlistSubscription.stories.jsx.html +151 -0
- package/coverage/rabbit-ui-kit/stories/organisms/index.html +116 -0
- package/coverage/rabbit-ui-kit/stories/stubs/coins.jsx.html +6880 -0
- package/coverage/rabbit-ui-kit/stories/stubs/exampleContent.jsx.html +145 -0
- package/coverage/rabbit-ui-kit/stories/stubs/index.html +131 -0
- package/coverage/rabbit-ui-kit/stories/templates/DeterminedErrorDialogStep.stories.jsx.html +190 -0
- package/coverage/rabbit-ui-kit/stories/templates/index.html +116 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/dist/global.css +197 -0
- package/dist/global.css.map +1 -0
- package/dist/index.cjs +13003 -1052
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +2350 -8491
- package/dist/index.css.map +1 -1
- package/dist/index.modern.js +9951 -847
- package/dist/index.modern.js.map +1 -1
- package/dist/index.module.js +12930 -1066
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +12994 -1055
- package/dist/index.umd.js.map +1 -1
- package/package.json +35 -9
- package/raw +1000 -0
- package/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js +22 -0
- package/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js +29 -0
- package/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js +38 -0
- package/src/common-apis/adapters/axiosAdapter.js +35 -0
- package/src/common-apis/adapters/qrUtils.js +18 -0
- package/src/common-apis/external-apis/apiGroups.js +56 -0
- package/src/common-apis/external-apis/emailAPI.js +16 -0
- package/src/common-apis/external-apis/ipAddressProviders.js +102 -0
- package/src/common-apis/globalConstants.jsx +3 -0
- package/src/{common → common-apis}/models/coin.js +98 -7
- package/src/{common → common-apis/services}/fiatCurrenciesService.js +4 -12
- package/src/common-apis/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +18 -0
- package/src/common-apis/tests/units/utils/amountUtils/composeRateText.test.js +152 -0
- package/src/{common → common-apis/utils}/amountUtils.js +100 -140
- package/src/{common → common-apis}/utils/cache.js +15 -41
- package/src/{common → common-apis/utils}/errorUtils.js +15 -0
- package/src/{common → common-apis}/utils/logging/logger.js +7 -13
- package/src/common-apis/utils/postponeExecution.js +11 -0
- package/src/common-apis/utils/rabbitTicker.js +24 -0
- package/src/index.js +90 -14
- package/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js +525 -0
- package/src/robust-api-caller/cachedRobustExternalApiCallerService.js +147 -0
- package/src/robust-api-caller/cancelProcessing.js +29 -0
- package/src/robust-api-caller/concurrentCalculationsMetadataHolder.js +75 -0
- package/src/robust-api-caller/externalApiProvider.js +156 -0
- package/src/robust-api-caller/externalServicesStatsCollector.js +78 -0
- package/src/robust-api-caller/robustExternalAPICallerService.js +304 -0
- package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/_performCallAttempt.test.js +533 -0
- package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/callExternalAPI.test.js +532 -0
- package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/constructor.test.js +19 -0
- package/src/swaps-lib/external-apis/exolixSwapProvider.js +602 -0
- package/src/swaps-lib/external-apis/goexmeSwapProvider.js +664 -0
- package/src/swaps-lib/external-apis/letsExchangeSwapProvider.js +516 -0
- package/src/swaps-lib/external-apis/swapProvider.js +435 -24
- package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +613 -0
- package/src/swaps-lib/external-apis/utils.js +26 -0
- package/src/swaps-lib/models/baseSwapCreationInfo.js +78 -0
- package/src/swaps-lib/models/existingSwap.js +101 -16
- package/src/swaps-lib/models/existingSwapWithFiatData.js +148 -0
- package/src/swaps-lib/models/partner.js +27 -0
- package/src/swaps-lib/models/swapProviderCoinInfo.js +82 -0
- package/src/swaps-lib/services/publicSwapService.js +495 -0
- package/src/swaps-lib/test/external-apis/exolixSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +34 -0
- package/src/swaps-lib/test/external-apis/exolixSwapProvider/createSwap.test.js +1043 -0
- package/src/swaps-lib/test/external-apis/exolixSwapProvider/getSwapInfo.test.js +611 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +148 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/createSwap.test.js +115 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/getSwapInfo.test.js +149 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/PairSupport.int.test.js +97 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/_fetchSupportedCurrenciesIfNeeded.int.test.js +171 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/createSwap.int.test.js +239 -0
- package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/getSwapInfo.int.test.js +141 -0
- package/src/swaps-lib/test/external-apis/swapProvider/getAllSupportedCurrencies.test.js +63 -0
- package/src/swaps-lib/test/external-apis/swapProvider/getDepositCurrencies.test.js +73 -0
- package/src/swaps-lib/test/external-apis/swapProvider/getWithdrawalCurrencies.test.js +102 -0
- package/src/swaps-lib/test/external-apis/swapProvider/removeProtocolNameFromCoinName.test.js +152 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +536 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/createSwap.test.js +1214 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getSwapInfo.test.js +1743 -0
- package/src/swaps-lib/test/utils/swapUtils/safeHandleRequestsLimitExceeding.test.js +80 -0
- package/src/swaps-lib/utils/swapUtils.js +207 -0
- package/{styles → src/ui-kit/assets/styles}/_functions.scss +5 -0
- package/{styles → src/ui-kit/assets/styles}/_mixins.scss +2 -2
- package/{styles → src/ui-kit/assets/styles}/_placeholder.scss +3 -3
- package/{styles → src/ui-kit/assets/styles}/_variables.scss +17 -15
- package/src/ui-kit/assets/styles/fonts/NunitoSans-Bold.ttf +0 -0
- package/src/ui-kit/assets/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
- package/src/ui-kit/assets/styles/fonts/NunitoSans-Light.ttf +0 -0
- package/src/ui-kit/assets/styles/fonts/NunitoSans-Regular.ttf +0 -0
- package/src/ui-kit/assets/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
- package/src/ui-kit/assets/styles/global.scss +171 -0
- package/src/ui-kit/assets/styles/index.scss +10 -0
- package/src/ui-kit/assets/wrappedImages/arrowIcon.jsx +13 -0
- package/src/ui-kit/assets/wrappedImages/arrowTosca.jsx +14 -0
- package/src/ui-kit/assets/wrappedImages/arrowWhite.jsx +14 -0
- package/src/ui-kit/assets/wrappedImages/darkRectangle.jsx +7 -0
- package/src/ui-kit/assets/wrappedImages/determinedError.jsx +118 -0
- package/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx +39 -0
- package/src/ui-kit/assets/wrappedImages/infoIcon.jsx +16 -0
- package/src/ui-kit/assets/wrappedImages/messageIcon.jsx +87 -0
- package/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx +54 -0
- package/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx +26 -0
- package/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx +61 -0
- package/src/ui-kit/assets/wrappedImages/walletIcon.jsx +22 -0
- package/src/{components → ui-kit/components}/atoms/AssetIcon/AssetIcon.jsx +16 -14
- package/src/{components → ui-kit/components}/atoms/AssetIcon/asset-icon.module.scss +1 -1
- package/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx +68 -0
- package/src/ui-kit/components/atoms/AssetSelection/asset-selection.module.scss +56 -0
- package/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx +34 -0
- package/src/ui-kit/components/atoms/BackgroundTitle/background-title.module.scss +52 -0
- package/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx +51 -0
- package/src/ui-kit/components/atoms/InformationMessage/information-message.module.scss +38 -0
- package/src/ui-kit/components/atoms/Input/Input.jsx +183 -0
- package/src/ui-kit/components/atoms/Input/input.module.scss +107 -0
- package/src/{components → ui-kit/components}/atoms/LoadingDots/LoadingDots.jsx +8 -28
- package/src/{components → ui-kit/components}/atoms/LoadingDots/LoadingDots.module.scss +3 -2
- package/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx +64 -0
- package/src/ui-kit/components/atoms/NoticeIcon/notice-icon.module.scss +14 -0
- package/src/ui-kit/components/atoms/QrCode/QrCode.jsx +38 -0
- package/src/ui-kit/components/atoms/QrCode/qr-code.module.scss +8 -0
- package/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx +30 -0
- package/src/ui-kit/components/atoms/RateSelector/rate-selector.module.scss +47 -0
- package/src/{components → ui-kit/components}/atoms/SupportChat/SupportChat.jsx +5 -1
- package/src/ui-kit/components/atoms/Textarea/Textarea.jsx +148 -0
- package/src/ui-kit/components/atoms/Textarea/textarea.module.scss +71 -0
- package/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx +141 -0
- package/src/ui-kit/components/atoms/TitleBox/title-box.module.scss +32 -0
- package/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx +77 -0
- package/src/ui-kit/components/atoms/Tooltip/tooltip.module.scss +237 -0
- package/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx +76 -0
- package/src/ui-kit/components/atoms/TwoLinesOfText/lines-of-text.module.scss +65 -0
- package/src/ui-kit/components/atoms/Validation/Validation.jsx +39 -0
- package/src/ui-kit/components/atoms/Validation/validation.module.scss +19 -0
- package/src/{components → ui-kit/components}/atoms/buttons/Button/Button.jsx +26 -45
- package/src/{components → ui-kit/components}/atoms/buttons/Button/Button.module.scss +1 -1
- package/src/ui-kit/components/atoms/buttons/Close/Close.jsx +58 -0
- package/src/ui-kit/components/atoms/buttons/Close/close.module.scss +75 -0
- package/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx +116 -0
- package/src/ui-kit/components/atoms/buttons/LinkButton/link-button.module.scss +53 -0
- package/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx +110 -0
- package/src/ui-kit/components/atoms/buttons/RadioButtonWithText/radio-button-with-text.module.scss +86 -0
- package/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx +448 -0
- package/src/ui-kit/components/molecules/AmountInput/amount-input.module.scss +233 -0
- package/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx +463 -0
- package/src/ui-kit/components/molecules/CoinPicker/coin-picker.module.scss +207 -0
- package/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx +52 -0
- package/src/ui-kit/components/molecules/ColoredNotice/colored-notice.module.scss +36 -0
- package/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx +35 -0
- package/src/ui-kit/components/molecules/LineWithIconLink/line-with-icon-link.module.scss +25 -0
- package/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx +74 -0
- package/src/ui-kit/components/molecules/LogoCarousel/logo-carousel.module.scss +106 -0
- package/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx +114 -0
- package/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx +32 -0
- package/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx +66 -0
- package/src/ui-kit/components/organisms/Dialog/Dialog.jsx +494 -0
- package/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx +132 -0
- package/src/ui-kit/components/organisms/Dialog/DialogButtons/dialog-buttons.module.scss +25 -0
- package/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx +554 -0
- package/src/ui-kit/components/organisms/Dialog/DialogStep/dialog-step.module.scss +382 -0
- package/src/ui-kit/components/organisms/Dialog/dialog.module.scss +226 -0
- package/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx +1381 -0
- package/src/ui-kit/components/organisms/SwapForm/swap-form.module.scss +134 -0
- package/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx +158 -0
- package/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx +77 -0
- package/src/ui-kit/hooks/useCallHandlingErrors.js +27 -0
- package/src/ui-kit/hooks/useIsHydrated.js +12 -0
- package/src/ui-kit/hooks/useReferredState.js +24 -0
- package/src/ui-kit/tests/utils/inputValueProviders/provideFormatOfFloatValueByInputString.test.js +146 -0
- package/src/ui-kit/tests/utils/urlQueryUtils/getQueryParameterValues.test.js +65 -0
- package/src/ui-kit/tests/utils/urlQueryUtils/saveQueryParameterAndValues.test.js +104 -0
- package/src/ui-kit/utils/inputValueProviders.js +50 -0
- package/src/ui-kit/utils/searchCoins.js +58 -0
- package/src/ui-kit/utils/textUtils.js +18 -0
- package/src/ui-kit/utils/uiUtils.js +12 -0
- package/src/ui-kit/utils/urlQueryUtils.js +62 -0
- package/stories/font.scss +40 -0
- package/stories/stubs/coins.jsx +2266 -0
- package/stories/stubs/exampleContent.jsx +20 -0
- package/styles/_global-classes.scss +0 -433
- package/styles/fonts/NunitoSans-Bold.ttf +0 -0
- package/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
- package/styles/fonts/NunitoSans-Light.ttf +0 -0
- package/styles/fonts/NunitoSans-Regular.ttf +0 -0
- package/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
- package/styles/global-styles-index.scss +0 -74
- package/styles/index.scss +0 -33
- /package/src/{common → common-apis}/models/blockchain.js +0 -0
- /package/src/{common → common-apis}/models/protocol.js +0 -0
- /package/src/{common → common-apis}/utils/logging/logsStorage.js +0 -0
- /package/src/{common → common-apis}/utils/safeStringify.js +0 -0
- /package/{styles → src/ui-kit/assets/styles}/colors/_light-colors.scss +0 -0
- /package/{styles → src/ui-kit/assets/styles}/colors/_solid-colors.scss +0 -0
- /package/{styles → src/ui-kit/assets/styles}/size/_margin-size.scss +0 -0
- /package/{styles → src/ui-kit/assets/styles}/size/_padding-size.scss +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Logger } from "../../utils/logging/logger.js";
|
|
2
|
+
import { safeStringify } from "../../utils/safeStringify.js";
|
|
3
|
+
|
|
4
|
+
export class GoogleAnalyticsAdapter {
|
|
5
|
+
static sendEvent(eventName, parameters) {
|
|
6
|
+
// eslint-disable-next-line no-console
|
|
7
|
+
console.log("Sending ga event: ", parameters, ["event", eventName, parameters]);
|
|
8
|
+
this.doActionOnGTag(["event", eventName, parameters]);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static doActionOnGTag(parameters) {
|
|
12
|
+
try {
|
|
13
|
+
if (window.gtag) {
|
|
14
|
+
window.gtag(...parameters);
|
|
15
|
+
} else {
|
|
16
|
+
Logger.logError(null, "doActionOnGTag", "No gtag found");
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
Logger.logError(e, "doActionOnGTag", "Failed to do gtag action: " + safeStringify(parameters));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Logger } from "../../utils/logging/logger.js";
|
|
2
|
+
|
|
3
|
+
export class MetrikaAdapter {
|
|
4
|
+
/**
|
|
5
|
+
* Sends reachGoal to Metrika
|
|
6
|
+
*
|
|
7
|
+
* @param METRIKA_ID {string}
|
|
8
|
+
* @param goalName {string}
|
|
9
|
+
* @param [usdPrice] {string|number}
|
|
10
|
+
*/
|
|
11
|
+
static callMetrikaReachGoal(METRIKA_ID, goalName, usdPrice = null) {
|
|
12
|
+
try {
|
|
13
|
+
if (window.ym) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.log("Sending metrika goal reached: ", goalName, String(usdPrice));
|
|
16
|
+
window.ym(
|
|
17
|
+
METRIKA_ID,
|
|
18
|
+
"reachGoal",
|
|
19
|
+
goalName,
|
|
20
|
+
usdPrice != null ? { order_price: usdPrice, currency: "USD" } : {}
|
|
21
|
+
);
|
|
22
|
+
} else {
|
|
23
|
+
Logger.logError(null, "callMetrikaReachGoal", "No metrika found");
|
|
24
|
+
}
|
|
25
|
+
} catch (e) {
|
|
26
|
+
Logger.logError(e, "callMetrikaReachGoal", "Failed to call metrika: " + goalName + " " + usdPrice);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Logger } from "../../utils/logging/logger.js";
|
|
2
|
+
import { safeStringify } from "../../utils/safeStringify.js";
|
|
3
|
+
|
|
4
|
+
export class MixpanelAdapter {
|
|
5
|
+
static sendEvent(eventName, parameters) {
|
|
6
|
+
try {
|
|
7
|
+
if (window.mixpanel) {
|
|
8
|
+
const result = window.mixpanel.track(eventName, parameters);
|
|
9
|
+
// eslint-disable-next-line no-console
|
|
10
|
+
console.log("Mixpanel sendEvent result: " + result);
|
|
11
|
+
} else {
|
|
12
|
+
Logger.logError(null, "sendEvent", "No mixpanel found");
|
|
13
|
+
}
|
|
14
|
+
} catch (e) {
|
|
15
|
+
Logger.logError(
|
|
16
|
+
e,
|
|
17
|
+
"sendEvent",
|
|
18
|
+
"Failed to do mixpanel action: " + eventName + " - " + safeStringify(parameters)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static identify(uniqueId) {
|
|
24
|
+
try {
|
|
25
|
+
if (window.mixpanel) {
|
|
26
|
+
if (uniqueId) {
|
|
27
|
+
const result = window.mixpanel.identify(uniqueId);
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log("Mixpanel identify result: " + result);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
Logger.logError(null, "identify", "No mixpanel found");
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
Logger.logError(e, "identify", "Failed to identify.");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
export class AxiosAdapter {
|
|
4
|
+
static async call(method, ...args) {
|
|
5
|
+
return await axios[method](...args);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static async get(...args) {
|
|
9
|
+
return await axios.get(...args);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static async post(...args) {
|
|
13
|
+
return await axios.post(...args);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static async put(...args) {
|
|
17
|
+
return await axios.put(...args);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static async delete(...args) {
|
|
21
|
+
return await axios.delete(...args);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static async patch(...args) {
|
|
25
|
+
return await axios.patch(...args);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static async options(...args) {
|
|
29
|
+
return await axios.options(...args);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static async head(...args) {
|
|
33
|
+
return await axios.head(...args);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import QRCode from "qrcode";
|
|
2
|
+
|
|
3
|
+
import { improveAndRethrow } from "../utils/errorUtils.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates QR as svg xml string.
|
|
7
|
+
* Note that you should care about the element size by your self - the generated QR will just fill all available space
|
|
8
|
+
*
|
|
9
|
+
* @param encodingString {string} to be encoded as QR-code image
|
|
10
|
+
* @return {Promise<string>} xml string of generated svg image
|
|
11
|
+
*/
|
|
12
|
+
export async function generateQrAndShowInCanvas(encodingString) {
|
|
13
|
+
try {
|
|
14
|
+
return await QRCode.toString(encodingString, { type: "svg" });
|
|
15
|
+
} catch (e) {
|
|
16
|
+
improveAndRethrow(e, "generateQrAndShowInCanvas");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Models a group of APIs provided by the same owner and used for different services in our app.
|
|
3
|
+
* It means we need to mention RPS several times for each usage and also have some holder of last call timestamp per
|
|
4
|
+
* api group. So this concept allows to use it for exact ExternalApiProvider and make sure that you use the same
|
|
5
|
+
* RPS value and make decisions on base of the same timestamp of last call to the API group owner.
|
|
6
|
+
*/
|
|
7
|
+
export class ApiGroup {
|
|
8
|
+
constructor(id, rps, backendProxyIdGenerator = null) {
|
|
9
|
+
this.id = id;
|
|
10
|
+
this.rps = rps;
|
|
11
|
+
this.lastCalledTimestamp = null;
|
|
12
|
+
this.backendProxyIdGenerator = backendProxyIdGenerator;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isRpsExceeded() {
|
|
16
|
+
return (this.lastCalledTimestamp ?? 0) + Math.floor(1000 / this.rps) > Date.now();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
actualizeLastCalledTimestamp() {
|
|
20
|
+
this.lastCalledTimestamp = Date.now();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ApiGroups = {
|
|
25
|
+
/**
|
|
26
|
+
* Currently we use free version of etherscan provider with 0.2 RPS. But we have API key with 100k requests free
|
|
27
|
+
* per month. So we can add it if not enough current RPS.
|
|
28
|
+
*/
|
|
29
|
+
ETHERSCAN: new ApiGroup("etherscan", 0.2, networkKey => `etherscan-${networkKey}`),
|
|
30
|
+
ALCHEMY: new ApiGroup("alchemy", 0.3, networkKey => `alchemy-${networkKey}`),
|
|
31
|
+
BLOCKSTREAM: new ApiGroup("blockstream", 0.2),
|
|
32
|
+
BLOCKCHAIN_INFO: new ApiGroup("blockchain.info", 1),
|
|
33
|
+
BLOCKNATIVE: new ApiGroup("blocknative", 0.5),
|
|
34
|
+
ETHGASSTATION: new ApiGroup("ethgasstation", 0.5),
|
|
35
|
+
TRONGRID: new ApiGroup("trongrid", 0.3, networkKey => `trongrid-${networkKey}`),
|
|
36
|
+
TRONSCAN: new ApiGroup("tronscan", 0.3),
|
|
37
|
+
GETBLOCK: new ApiGroup("getblock", 0.3),
|
|
38
|
+
COINCAP: new ApiGroup("coincap", 0.5), // 200 per minute without API key
|
|
39
|
+
COINGECKO: new ApiGroup("coingecko", 0.9), // actually 0.13-0.5 according to the docs but we use smaller due to expirienced frequent abuses
|
|
40
|
+
MESSARI: new ApiGroup("messari", 0.2),
|
|
41
|
+
BTCCOM: new ApiGroup("btccom", 0.2),
|
|
42
|
+
BITAPS: new ApiGroup("bitaps", 0.25), // Docs say that RPS is 3 but using it causes frequent 429 HTTP errors
|
|
43
|
+
CEX: new ApiGroup("cex", 0.5), // Just assumption for RPS
|
|
44
|
+
BIGDATACLOUD: new ApiGroup("bigdatacloud", 1), // Just assumption for RPS
|
|
45
|
+
TRACKIP: new ApiGroup("trackip", 1), // Just assumption for RPS
|
|
46
|
+
IPIFY: new ApiGroup("ipify", 1), // Just assumption for RPS
|
|
47
|
+
WHATISMYIPADDRESS: new ApiGroup("whatismyipaddress", 1), // Just assumption for RPS
|
|
48
|
+
EXCHANGERATE: new ApiGroup("exchangerate", 1), // Just assumption for RPS
|
|
49
|
+
FRANKFURTER: new ApiGroup("frankfurter", 1), // Just assumption for RPS
|
|
50
|
+
BITGO: new ApiGroup("bitgo", 1), // Just assumption for RPS
|
|
51
|
+
BITCOINER: new ApiGroup("bitcoiner", 1), // Just assumption for RPS
|
|
52
|
+
BITCORE: new ApiGroup("bitcore", 1), // Just assumption for RPS
|
|
53
|
+
// BLOCKCHAIR: new ApiGroup("blockchair", 0.04), // this provider require API key for commercial use (10usd 10000 reqs), we will add it later
|
|
54
|
+
MEMPOOL: new ApiGroup("mempool", 0.2), // Just assumption for RPS
|
|
55
|
+
SWAP_FACADE: new ApiGroup("swapfacade", 50),
|
|
56
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
import { improveAndRethrow } from "../utils/errorUtils.js";
|
|
4
|
+
|
|
5
|
+
export class EmailsApi {
|
|
6
|
+
static serverEndpointEntity = "emails";
|
|
7
|
+
|
|
8
|
+
static async sendEmail(subject, body) {
|
|
9
|
+
try {
|
|
10
|
+
const url = `${window.location.protocol + "//" + window.location.host}/api/v1/${this.serverEndpointEntity}`;
|
|
11
|
+
await axios.post(url, { subject, body });
|
|
12
|
+
} catch (e) {
|
|
13
|
+
improveAndRethrow(e, "sendEmail", subject + body);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import EventBusInstance from "eventbusjs";
|
|
2
|
+
import { ExternalApiProvider } from "../../robust-api-caller/externalApiProvider.js";
|
|
3
|
+
import { ApiGroups } from "./apiGroups.js";
|
|
4
|
+
import { CachedRobustExternalApiCallerService } from "../../robust-api-caller/cachedRobustExternalApiCallerService.js";
|
|
5
|
+
import { Cache } from "../utils/cache.js";
|
|
6
|
+
import { improveAndRethrow } from "../utils/errorUtils.js";
|
|
7
|
+
|
|
8
|
+
class BigdatacloudIpAddressProvider extends ExternalApiProvider {
|
|
9
|
+
constructor() {
|
|
10
|
+
super("https://api.bigdatacloud.net/data/client-ip", "get", 15000, ApiGroups.BIGDATACLOUD);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getDataByResponse(response, params = [], subRequestIndex = 0, iterationsData = []) {
|
|
14
|
+
return response?.data && response.data?.ipString;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class SwapFacadeIpAddressProvider extends ExternalApiProvider {
|
|
19
|
+
constructor(url) {
|
|
20
|
+
super(url + "/clientIp", "get", 15000, ApiGroups.SWAP_FACADE);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getDataByResponse(response, params = [], subRequestIndex = 0, iterationsData = []) {
|
|
24
|
+
return response?.data?.responseObject?.ip ?? null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class TrackipIpAddressProvider extends ExternalApiProvider {
|
|
29
|
+
constructor() {
|
|
30
|
+
super("https://www.trackip.net/ip", "get", 15000, ApiGroups.TRACKIP);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getDataByResponse(response, params = [], subRequestIndex = 0, iterationsData = []) {
|
|
34
|
+
return response?.data;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class IpifyV6IpAddressProvider extends ExternalApiProvider {
|
|
39
|
+
constructor() {
|
|
40
|
+
super("https://api6.ipify.org/?format=json", "get", 15000, ApiGroups.IPIFY);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getDataByResponse(response, params = [], subRequestIndex = 0, iterationsData = []) {
|
|
44
|
+
return response?.data && response.data?.ip;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class IpifyIpAddressProvider extends ExternalApiProvider {
|
|
49
|
+
constructor() {
|
|
50
|
+
super("https://api.ipify.org/?format=json", "get", 15000, ApiGroups.IPIFY);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getDataByResponse(response, params = [], subRequestIndex = 0, iterationsData = []) {
|
|
54
|
+
return response?.data && response.data?.ip;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class WhatismyipaddressIpAddressProvider extends ExternalApiProvider {
|
|
59
|
+
constructor() {
|
|
60
|
+
super("http://bot.whatismyipaddress.com/", "get", 15000, ApiGroups.WHATISMYIPADDRESS);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getDataByResponse(response, params = [], subRequestIndex = 0, iterationsData = []) {
|
|
64
|
+
return response?.data;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class IpAddressProvider {
|
|
69
|
+
constructor(swapFacadeURL) {
|
|
70
|
+
this.externalIPAddressAPICaller = new CachedRobustExternalApiCallerService(
|
|
71
|
+
"externalIPAddressAPICaller",
|
|
72
|
+
new Cache(EventBusInstance),
|
|
73
|
+
[
|
|
74
|
+
new SwapFacadeIpAddressProvider(swapFacadeURL),
|
|
75
|
+
new BigdatacloudIpAddressProvider(),
|
|
76
|
+
new TrackipIpAddressProvider(),
|
|
77
|
+
new IpifyV6IpAddressProvider(),
|
|
78
|
+
new IpifyIpAddressProvider(),
|
|
79
|
+
new WhatismyipaddressIpAddressProvider(),
|
|
80
|
+
],
|
|
81
|
+
300_000
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns current public IP address identified by one of external services.
|
|
87
|
+
*
|
|
88
|
+
* It is easier than manual identification and also (as ip needed for server side to check it) it saves us from
|
|
89
|
+
* issues related to changes of infrastructure configurations (like adding proxies etc.) so we should not configure
|
|
90
|
+
* anything on server side to get correct client's IP.
|
|
91
|
+
*
|
|
92
|
+
* @returns {Promise<String>} IP address
|
|
93
|
+
* @throws {Error} if fails to retrieve IP address from all the services
|
|
94
|
+
*/
|
|
95
|
+
async getClientIpAddress() {
|
|
96
|
+
try {
|
|
97
|
+
return await this.externalIPAddressAPICaller.callExternalAPICached([], 7000);
|
|
98
|
+
} catch (e) {
|
|
99
|
+
improveAndRethrow(e, "getClientIpAddress");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { improveAndRethrow } from "
|
|
1
|
+
import { improveAndRethrow } from "../utils/errorUtils.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The model for cryptocurrency coins.
|
|
@@ -6,6 +6,101 @@ import { improveAndRethrow } from "./../errorUtils.js";
|
|
|
6
6
|
* WARNING: this class should not be instantiated directly. Use only predefined singleton Coin (or descendants) instances.
|
|
7
7
|
*/
|
|
8
8
|
export class Coin {
|
|
9
|
+
/**
|
|
10
|
+
* @type {string}
|
|
11
|
+
*/
|
|
12
|
+
latinName;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @type {string}
|
|
16
|
+
*/
|
|
17
|
+
ticker;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @type {string}
|
|
21
|
+
*/
|
|
22
|
+
tickerPrintable;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @type {number}
|
|
26
|
+
*/
|
|
27
|
+
digits;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @type {number|null}
|
|
31
|
+
*/
|
|
32
|
+
maxValue;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @type {string}
|
|
36
|
+
*/
|
|
37
|
+
atomName;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @type {Network}
|
|
41
|
+
*/
|
|
42
|
+
mainnet;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @type {Network}
|
|
46
|
+
*/
|
|
47
|
+
testnet;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @type {number}
|
|
51
|
+
*/
|
|
52
|
+
minConfirmations;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @type {string|null}
|
|
56
|
+
*/
|
|
57
|
+
payableEntityStringForFeeRate;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @type {string[]}
|
|
61
|
+
*/
|
|
62
|
+
feeOptionsTimeStringsSortedDesc;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @type {number}
|
|
66
|
+
*/
|
|
67
|
+
feeRatesExpirationTimeMs;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @type {Blockchain}
|
|
71
|
+
*/
|
|
72
|
+
blockchain;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @type {Protocol|null}
|
|
76
|
+
*/
|
|
77
|
+
protocol;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @type {string|null}
|
|
81
|
+
*/
|
|
82
|
+
tokenAddress;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @type {boolean}
|
|
86
|
+
*/
|
|
87
|
+
doesUseLowerCaseAddresses;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @type {boolean}
|
|
91
|
+
*/
|
|
92
|
+
doesUseOutputs;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @type {Coin}
|
|
96
|
+
*/
|
|
97
|
+
feeCoin;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @type {number}
|
|
101
|
+
*/
|
|
102
|
+
_significantDigits;
|
|
103
|
+
|
|
9
104
|
/**
|
|
10
105
|
* Creates new coin
|
|
11
106
|
*
|
|
@@ -130,9 +225,7 @@ export class Coin {
|
|
|
130
225
|
* @param coinAtomsString {string} coin atoms positive integer amount
|
|
131
226
|
* @return {string} string of coin amount and fee rate units
|
|
132
227
|
*/
|
|
133
|
-
coinAtomsFeeRateToCommonlyUsedAmountFormatWithDenominationString(
|
|
134
|
-
coinAtomsString
|
|
135
|
-
) {
|
|
228
|
+
coinAtomsFeeRateToCommonlyUsedAmountFormatWithDenominationString(coinAtomsString) {
|
|
136
229
|
throw new Error("Not implemented in base Coin");
|
|
137
230
|
}
|
|
138
231
|
|
|
@@ -147,9 +240,7 @@ export class Coin {
|
|
|
147
240
|
|
|
148
241
|
tickerAndProtocol() {
|
|
149
242
|
try {
|
|
150
|
-
return `${this.tickerPrintable}${
|
|
151
|
-
this.protocol ? " " + this.protocol.protocol ?? "" : ""
|
|
152
|
-
}`;
|
|
243
|
+
return `${this.tickerPrintable}${this.protocol ? " " + this.protocol.protocol ?? "" : ""}`;
|
|
153
244
|
} catch (e) {
|
|
154
245
|
improveAndRethrow(e, "tickerAndProtocol");
|
|
155
246
|
}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
export class FiatCurrenciesService {
|
|
2
2
|
static getFullCurrencyNameByCode(code = "") {
|
|
3
|
-
const data = fiatCurrenciesList.find(
|
|
4
|
-
(currencyData) => currencyData[0] === code.toUpperCase()
|
|
5
|
-
);
|
|
3
|
+
const data = fiatCurrenciesList.find(currencyData => currencyData[0] === code.toUpperCase());
|
|
6
4
|
return (data && data[2]) || null;
|
|
7
5
|
}
|
|
8
6
|
|
|
9
7
|
static isCodeValid(code) {
|
|
10
|
-
return !!fiatCurrenciesList.find(
|
|
11
|
-
(currenciesData) => currenciesData[0] === code
|
|
12
|
-
);
|
|
8
|
+
return !!fiatCurrenciesList.find(currenciesData => currenciesData[0] === code);
|
|
13
9
|
}
|
|
14
10
|
|
|
15
11
|
/**
|
|
@@ -19,9 +15,7 @@ export class FiatCurrenciesService {
|
|
|
19
15
|
* @return {string|null} code or null if there is no symbol for the currency
|
|
20
16
|
*/
|
|
21
17
|
static getCurrencySymbolByCode(code = "") {
|
|
22
|
-
const data = fiatCurrenciesList.find(
|
|
23
|
-
(currencyData) => currencyData[0] === code.toUpperCase()
|
|
24
|
-
);
|
|
18
|
+
const data = fiatCurrenciesList.find(currencyData => currencyData[0] === code.toUpperCase());
|
|
25
19
|
return data?.[1] ?? null;
|
|
26
20
|
}
|
|
27
21
|
|
|
@@ -30,9 +24,7 @@ export class FiatCurrenciesService {
|
|
|
30
24
|
* @return {number|null}
|
|
31
25
|
*/
|
|
32
26
|
static getCurrencyDecimalCountByCode(code = "") {
|
|
33
|
-
const data = fiatCurrenciesList.find(
|
|
34
|
-
(currencyData) => currencyData[0] === code.toUpperCase()
|
|
35
|
-
);
|
|
27
|
+
const data = fiatCurrenciesList.find(currencyData => currencyData[0] === code.toUpperCase());
|
|
36
28
|
return data?.[3] ?? null;
|
|
37
29
|
}
|
|
38
30
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import should from "should";
|
|
2
|
+
|
|
3
|
+
import { describe, it } from "vitest";
|
|
4
|
+
import { IpAddressProvider } from "../../../../external-apis/ipAddressProviders.js";
|
|
5
|
+
|
|
6
|
+
describe("ipAddressProviders", function () {
|
|
7
|
+
describe("#getClientIpAddress", function () {
|
|
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
|
+
);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import sinon from "sinon";
|
|
2
|
+
import should from "should";
|
|
3
|
+
|
|
4
|
+
import { beforeEach, afterEach, describe, it } from "vitest";
|
|
5
|
+
import BigNumber from "bignumber.js";
|
|
6
|
+
import { AmountUtils } from "../../../../utils/amountUtils.js";
|
|
7
|
+
|
|
8
|
+
describe("AmountUtils", function () {
|
|
9
|
+
let toIntegerStringStub;
|
|
10
|
+
|
|
11
|
+
beforeEach(function () {
|
|
12
|
+
toIntegerStringStub = sinon.stub(AmountUtils, "toIntegerString").callsFake(number => number.toFixed(0));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(function () {
|
|
16
|
+
toIntegerStringStub.restore();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("composeRateText", function () {
|
|
20
|
+
it("Should handle simple rate calculation", function () {
|
|
21
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.1");
|
|
22
|
+
|
|
23
|
+
result.should.equal("1 BTC ~ 0.10000000 ETH");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("Should handle rate with strictRate set to true", function () {
|
|
27
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.1", 8, true);
|
|
28
|
+
|
|
29
|
+
result.should.equal("1 BTC = 0.10000000 ETH");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("Should handle rate with high precision", function () {
|
|
33
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.12345678903422", 8);
|
|
34
|
+
|
|
35
|
+
result.should.equal("1 BTC ~ 0.12345678 ETH");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("Should handle very small rates by scaling up", function () {
|
|
39
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.000000000000001", 8);
|
|
40
|
+
|
|
41
|
+
result.should.equal("100000000 BTC ~ 0.00000010 ETH");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("Should handle large rates", function () {
|
|
45
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "1000000000000000", 8);
|
|
46
|
+
|
|
47
|
+
result.should.equal("1 BTC ~ 1000000000000000.00000000 ETH");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("Should return '?' for invalid rate input", function () {
|
|
51
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "invalid");
|
|
52
|
+
|
|
53
|
+
result.should.equal("1 BTC ~ ? ETH");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("Should return correct format for null rate", function () {
|
|
57
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", null);
|
|
58
|
+
|
|
59
|
+
result.should.equal("1 BTC ~ ? ETH");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("Should ensure that toIntegerString is called with correct parameter", function () {
|
|
63
|
+
AmountUtils.composeRateText("BTC", "ETH", "0.1");
|
|
64
|
+
|
|
65
|
+
toIntegerStringStub.calledWith(BigNumber("1")).should.be.true();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("Should handle rate with different rightCurrencyDigitsAfterDots", function () {
|
|
69
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.123456789", 5);
|
|
70
|
+
|
|
71
|
+
result.should.equal("1 BTC ~ 0.12345 ETH");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("Should handle rate with max rightCurrencyDigitsAfterDots", function () {
|
|
75
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.123456789", 10);
|
|
76
|
+
|
|
77
|
+
result.should.equal("1 BTC ~ 0.1234567890 ETH");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("Should ensure proper rounding of right amount", function () {
|
|
81
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.123456789", 4);
|
|
82
|
+
|
|
83
|
+
result.should.equal("1 BTC ~ 0.1234 ETH");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("Should handle great rightCurrencyDigitsAfterDots", function () {
|
|
87
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.123456789", 20);
|
|
88
|
+
|
|
89
|
+
result.should.equal("1 BTC ~ 0.12345678900000000000 ETH");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("Should handle empty rate", function () {
|
|
93
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "");
|
|
94
|
+
|
|
95
|
+
result.should.equal("1 BTC ~ ? ETH");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("Should handle negative rate", function () {
|
|
99
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "-0.1");
|
|
100
|
+
|
|
101
|
+
result.should.equal("1 BTC ~ -0.10000000 ETH");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("Should return proper result for rate equal to zero", function () {
|
|
105
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0");
|
|
106
|
+
|
|
107
|
+
result.should.equal("1 BTC ~ 0.00000000 ETH");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("Should ensure that toIntegerString is called the correct number of times", function () {
|
|
111
|
+
AmountUtils.composeRateText("BTC", "ETH", "0.1");
|
|
112
|
+
|
|
113
|
+
toIntegerStringStub.callCount.should.equal(1);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("Should handle rate with exponential notation", function () {
|
|
117
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "1e-8");
|
|
118
|
+
|
|
119
|
+
result.should.equal("1 BTC ~ 0.00000001 ETH");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("Should return correct format when rate has a lot of trailing zeros", function () {
|
|
123
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.1000000000000000000000");
|
|
124
|
+
|
|
125
|
+
result.should.equal("1 BTC ~ 0.10000000 ETH");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("Should handle zero rightCurrencyDigitsAfterDots", function () {
|
|
129
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.123456789", 0);
|
|
130
|
+
|
|
131
|
+
result.should.equal("100 BTC ~ 12 ETH");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("Should handle negative rightCurrencyDigitsAfterDots", function () {
|
|
135
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "0.123456789", -1);
|
|
136
|
+
|
|
137
|
+
result.should.equal("1 BTC ~ 0.12345678 ETH");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("Should handle rate input with trailing spaces", function () {
|
|
141
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", " 0.1 ");
|
|
142
|
+
|
|
143
|
+
result.should.equal("1 BTC ~ 0.10000000 ETH");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("Should handle rate input in scientific notation", function () {
|
|
147
|
+
const result = AmountUtils.composeRateText("BTC", "ETH", "1e-5");
|
|
148
|
+
|
|
149
|
+
result.should.equal("1 BTC ~ 0.00001000 ETH");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|