@rabbitio/ui-kit 1.0.0-beta.45 → 1.0.0-beta.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.husky/pre-push +1 -1
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/clover.xml +10957 -0
  5. package/coverage/coverage-final.json +67 -0
  6. package/coverage/favicon.png +0 -0
  7. package/coverage/index.html +701 -0
  8. package/coverage/prettify.css +1 -0
  9. package/coverage/prettify.js +2 -0
  10. package/coverage/rabbit-ui-kit/index.html +116 -0
  11. package/coverage/rabbit-ui-kit/index.js.html +88 -0
  12. package/coverage/rabbit-ui-kit/src/common/adapters/axiosAdapter.js.html +190 -0
  13. package/coverage/rabbit-ui-kit/src/common/adapters/index.html +116 -0
  14. package/coverage/rabbit-ui-kit/src/common/amountUtils.js.html +1393 -0
  15. package/coverage/rabbit-ui-kit/src/common/errorUtils.js.html +211 -0
  16. package/coverage/rabbit-ui-kit/src/common/external-apis/apiGroups.js.html +250 -0
  17. package/coverage/rabbit-ui-kit/src/common/external-apis/index.html +131 -0
  18. package/coverage/rabbit-ui-kit/src/common/external-apis/ipAddressProviders.js.html +499 -0
  19. package/coverage/rabbit-ui-kit/src/common/fiatCurrenciesService.js.html +568 -0
  20. package/coverage/rabbit-ui-kit/src/common/index.html +146 -0
  21. package/coverage/rabbit-ui-kit/src/common/models/blockchain.js.html +115 -0
  22. package/coverage/rabbit-ui-kit/src/common/models/coin.js.html +556 -0
  23. package/coverage/rabbit-ui-kit/src/common/models/index.html +146 -0
  24. package/coverage/rabbit-ui-kit/src/common/models/protocol.js.html +100 -0
  25. package/coverage/rabbit-ui-kit/src/common/utils/cache.js.html +889 -0
  26. package/coverage/rabbit-ui-kit/src/common/utils/emailAPI.js.html +139 -0
  27. package/coverage/rabbit-ui-kit/src/common/utils/index.html +161 -0
  28. package/coverage/rabbit-ui-kit/src/common/utils/logging/index.html +131 -0
  29. package/coverage/rabbit-ui-kit/src/common/utils/logging/logger.js.html +229 -0
  30. package/coverage/rabbit-ui-kit/src/common/utils/logging/logsStorage.js.html +268 -0
  31. package/coverage/rabbit-ui-kit/src/common/utils/postponeExecution.js.html +118 -0
  32. package/coverage/rabbit-ui-kit/src/common/utils/safeStringify.js.html +235 -0
  33. package/coverage/rabbit-ui-kit/src/components/atoms/AssetIcon/AssetIcon.jsx.html +250 -0
  34. package/coverage/rabbit-ui-kit/src/components/atoms/AssetIcon/index.html +116 -0
  35. package/coverage/rabbit-ui-kit/src/components/atoms/AssetSelection/AssetSelection.jsx.html +364 -0
  36. package/coverage/rabbit-ui-kit/src/components/atoms/AssetSelection/index.html +116 -0
  37. package/coverage/rabbit-ui-kit/src/components/atoms/BackgroundTitle/BackgroundTitle.jsx.html +217 -0
  38. package/coverage/rabbit-ui-kit/src/components/atoms/BackgroundTitle/index.html +116 -0
  39. package/coverage/rabbit-ui-kit/src/components/atoms/LoadingDots/LoadingDots.jsx.html +256 -0
  40. package/coverage/rabbit-ui-kit/src/components/atoms/LoadingDots/index.html +116 -0
  41. package/coverage/rabbit-ui-kit/src/components/atoms/NoticeIcon/NoticeIcon.jsx.html +298 -0
  42. package/coverage/rabbit-ui-kit/src/components/atoms/NoticeIcon/index.html +116 -0
  43. package/coverage/rabbit-ui-kit/src/components/atoms/SupportChat/SupportChat.jsx.html +229 -0
  44. package/coverage/rabbit-ui-kit/src/components/atoms/SupportChat/index.html +116 -0
  45. package/coverage/rabbit-ui-kit/src/components/atoms/TitleBox/TitleBox.jsx.html +574 -0
  46. package/coverage/rabbit-ui-kit/src/components/atoms/TitleBox/index.html +116 -0
  47. package/coverage/rabbit-ui-kit/src/components/atoms/Tooltip/Tooltip.jsx.html +370 -0
  48. package/coverage/rabbit-ui-kit/src/components/atoms/Tooltip/index.html +116 -0
  49. package/coverage/rabbit-ui-kit/src/components/atoms/Validation/Validation.jsx.html +475 -0
  50. package/coverage/rabbit-ui-kit/src/components/atoms/Validation/index.html +116 -0
  51. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Button/Button.jsx.html +802 -0
  52. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Button/index.html +116 -0
  53. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Close/Close.jsx.html +277 -0
  54. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Close/index.html +116 -0
  55. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/LinkButton/LinkButton.jsx.html +448 -0
  56. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/LinkButton/index.html +116 -0
  57. package/coverage/rabbit-ui-kit/src/components/hooks/index.html +131 -0
  58. package/coverage/rabbit-ui-kit/src/components/hooks/useCallHandlingErrors.js.html +163 -0
  59. package/coverage/rabbit-ui-kit/src/components/hooks/useReferredState.js.html +157 -0
  60. package/coverage/rabbit-ui-kit/src/components/molecules/AmountInput/AmountInput.jsx.html +1510 -0
  61. package/coverage/rabbit-ui-kit/src/components/molecules/AmountInput/index.html +116 -0
  62. package/coverage/rabbit-ui-kit/src/components/organisms/Dialog/Dialog.jsx.html +1630 -0
  63. package/coverage/rabbit-ui-kit/src/components/organisms/Dialog/DialogButtons/DialogButtons.jsx.html +451 -0
  64. package/coverage/rabbit-ui-kit/src/components/organisms/Dialog/DialogButtons/index.html +116 -0
  65. package/coverage/rabbit-ui-kit/src/components/organisms/Dialog/DialogStep/DialogStep.jsx.html +2077 -0
  66. package/coverage/rabbit-ui-kit/src/components/organisms/Dialog/DialogStep/index.html +116 -0
  67. package/coverage/rabbit-ui-kit/src/components/organisms/Dialog/index.html +116 -0
  68. package/coverage/rabbit-ui-kit/src/components/organisms/SwapForm/SwapForm.jsx.html +3538 -0
  69. package/coverage/rabbit-ui-kit/src/components/organisms/SwapForm/index.html +116 -0
  70. package/coverage/rabbit-ui-kit/src/components/utils/index.html +161 -0
  71. package/coverage/rabbit-ui-kit/src/components/utils/inputValueProviders.js.html +259 -0
  72. package/coverage/rabbit-ui-kit/src/components/utils/textUtils.js.html +139 -0
  73. package/coverage/rabbit-ui-kit/src/components/utils/uiUtils.js.html +127 -0
  74. package/coverage/rabbit-ui-kit/src/components/utils/urlQueryUtils.js.html +346 -0
  75. package/coverage/rabbit-ui-kit/src/constants/organisms/dialog/DialogStep/dialogStep.js.html +88 -0
  76. package/coverage/rabbit-ui-kit/src/constants/organisms/dialog/DialogStep/index.html +116 -0
  77. package/coverage/rabbit-ui-kit/src/constants/organisms/dialog/dialog.js.html +172 -0
  78. package/coverage/rabbit-ui-kit/src/constants/organisms/dialog/index.html +116 -0
  79. package/coverage/rabbit-ui-kit/src/index.html +116 -0
  80. package/coverage/rabbit-ui-kit/src/index.js.html +310 -0
  81. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cacheAndConcurrentRequestsResolver.js.html +1762 -0
  82. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cachedRobustExternalApiCallerService.js.html +649 -0
  83. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cancelProcessing.js.html +172 -0
  84. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/concurrentCalculationsMetadataHolder.js.html +394 -0
  85. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/externalApiProvider.js.html +553 -0
  86. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/externalServicesStatsCollector.js.html +331 -0
  87. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/index.html +206 -0
  88. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/robustExternalAPICallerService.js.html +1249 -0
  89. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/index.html +131 -0
  90. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +727 -0
  91. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +2899 -0
  92. package/coverage/rabbit-ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +214 -0
  93. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwap.js.html +304 -0
  94. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +487 -0
  95. package/coverage/rabbit-ui-kit/src/swaps-lib/models/index.html +146 -0
  96. package/coverage/rabbit-ui-kit/src/swaps-lib/services/index.html +116 -0
  97. package/coverage/rabbit-ui-kit/src/swaps-lib/services/publicSwapService.js.html +2191 -0
  98. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/index.html +116 -0
  99. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/swapUtils.js.html +742 -0
  100. package/coverage/rabbit-ui-kit/stories/atoms/BackgroundTitle.stories.jsx.html +202 -0
  101. package/coverage/rabbit-ui-kit/stories/atoms/LoadingDots.stories.jsx.html +226 -0
  102. package/coverage/rabbit-ui-kit/stories/atoms/Validation.stories.jsx.html +178 -0
  103. package/coverage/rabbit-ui-kit/stories/atoms/buttons/Button.stories.jsx.html +946 -0
  104. package/coverage/rabbit-ui-kit/stories/atoms/buttons/Close.stories.jsx.html +211 -0
  105. package/coverage/rabbit-ui-kit/stories/atoms/buttons/LinkButton.stories.jsx.html +298 -0
  106. package/coverage/rabbit-ui-kit/stories/atoms/buttons/index.html +146 -0
  107. package/coverage/rabbit-ui-kit/stories/atoms/index.html +146 -0
  108. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/Dialog.stories.jsx.html +574 -0
  109. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/DialogButtons.stories.jsx.html +328 -0
  110. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/index.html +116 -0
  111. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/DialogStep.stories.jsx.html +352 -0
  112. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/index.html +116 -0
  113. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/index.html +116 -0
  114. package/coverage/rabbit-ui-kit/stories/stubs/exampleContent.jsx.html +145 -0
  115. package/coverage/rabbit-ui-kit/stories/stubs/index.html +116 -0
  116. package/coverage/sort-arrow-sprite.png +0 -0
  117. package/coverage/sorter.js +196 -0
  118. package/dist/index.cjs +1634 -130
  119. package/dist/index.cjs.map +1 -1
  120. package/dist/index.css +17456 -110
  121. package/dist/index.css.map +1 -1
  122. package/dist/index.modern.js +1368 -111
  123. package/dist/index.modern.js.map +1 -1
  124. package/dist/index.module.js +1626 -131
  125. package/dist/index.module.js.map +1 -1
  126. package/dist/index.umd.js +1637 -134
  127. package/dist/index.umd.js.map +1 -1
  128. package/package.json +2 -1
  129. package/src/assets/image/icons/arrow-darker.svg +14 -0
  130. package/src/assets/image/icons/dark-rectangle.svg +3 -0
  131. package/src/assets/image/icons/wallet-gray-small.svg +6 -0
  132. package/src/components/atoms/AssetSelection/AssetSelection.jsx +93 -0
  133. package/src/components/atoms/AssetSelection/asset-selection.module.scss +55 -0
  134. package/src/components/atoms/NoticeIcon/NoticeIcon.jsx +71 -0
  135. package/src/components/atoms/NoticeIcon/notice-icon.module.scss +14 -0
  136. package/src/components/atoms/TitleBox/TitleBox.jsx +163 -0
  137. package/src/components/atoms/TitleBox/title-box.module.scss +30 -0
  138. package/src/components/atoms/Tooltip/Tooltip.jsx +95 -0
  139. package/src/components/atoms/Tooltip/tooltip.module.scss +231 -0
  140. package/src/components/atoms/Validation/Validation.jsx +1 -1
  141. package/src/components/molecules/AmountInput/AmountInput.jsx +475 -0
  142. package/src/components/molecules/AmountInput/amount-input.module.scss +189 -0
  143. package/src/components/organisms/SwapForm/SwapForm.jsx +1151 -0
  144. package/src/components/organisms/SwapForm/swap-form.module.scss +120 -0
  145. package/src/components/utils/textUtils.js +18 -0
  146. package/src/index.js +11 -1
  147. package/styles/index.scss +11 -10
@@ -0,0 +1,1151 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import React, { useEffect, useState } from "react";
3
+ import PropTypes from "prop-types";
4
+
5
+ import s from "./swap-form.module.scss";
6
+
7
+ import { useReferredState } from "../../hooks/useReferredState.js";
8
+ import { useCallHandlingErrors } from "../../hooks/useCallHandlingErrors.js";
9
+
10
+ import { AmountUtils } from "../../../common/amountUtils.js";
11
+ import { TitleBox } from "../../atoms/TitleBox/TitleBox.jsx";
12
+ import { AmountInput } from "../../molecules/AmountInput/AmountInput.jsx";
13
+ import { Validation } from "../../atoms/Validation/Validation.jsx";
14
+ import { Button } from "../../atoms/buttons/Button/Button.jsx";
15
+
16
+ /**
17
+ * This is type definition of a function that is used inside SwapForm to compose the URL for hero button.
18
+ *
19
+ * @function
20
+ * @name composeConfirmButtonTo
21
+ * @param [fromTicker] {string}
22
+ * @param [toTicker] {string}
23
+ * @param [fromAmount] {string}
24
+ * @return {string} full URL or relative URL that can be used for <a href="..." />
25
+ */
26
+
27
+ /**
28
+ * Common click handler without params and return value, just your logic execution.
29
+ *
30
+ * @function
31
+ * @name handleClick
32
+ * @return {void}
33
+ */
34
+
35
+ /**
36
+ * Form executes this function when swap creation info retrieval is finished successfully.
37
+ *
38
+ * @name setSwapCreationEstimation
39
+ * @function
40
+ * @param details {BaseSwapCreationInfo}
41
+ * @return {void}
42
+ */
43
+
44
+ /**
45
+ * Error handler for unexpected errors.
46
+ *
47
+ * @name handleUnexpectedError
48
+ * @function
49
+ * @param error {Error}
50
+ * @return {void}
51
+ */
52
+
53
+ /**
54
+ * Should implement the logic with rotating assets (if you want "to" to became "from" and "from" to became "to").
55
+ *
56
+ * @function
57
+ * @name rotateAssets
58
+ * @param sendingAssetTicker {string}
59
+ * @param receivingAssetTicker {string}
60
+ * @return {void}
61
+ */
62
+
63
+ /**
64
+ * Error handler for standard errors that we can recognize.
65
+ *
66
+ * @function
67
+ * @name handleSwapServiceError
68
+ * @param errorCode {string} error code that is being returned as "reason" from your handlers ...
69
+ * @param setValidationContent {function} accepts string to be displayed on form as validation error
70
+ * @param setBalanceValid {function} accepts single boolean parameter
71
+ * @param setMinimalAmountValid {function} accepts single boolean parameter
72
+ * @param setMaximumAmountValid {function} accepts single boolean parameter
73
+ * @param setIsPairSupported {function} accepts single boolean parameter
74
+ * @param setIsSameCoins {function} accepts single boolean parameter
75
+ * @return {void}
76
+ */
77
+
78
+ /**
79
+ * Async function that retrieves swap creation info by given params.
80
+ *
81
+ * @function
82
+ * @name retrieveSwapDetails
83
+ * @param sendingAssetTicker {string}
84
+ * @param receivingAssetTicker {string}
85
+ * @param amount {string}
86
+ * @param [isSwapAll=false] {boolean}
87
+ * @return {Promise<{
88
+ * result: false,
89
+ * reason: string,
90
+ * min: (string|null),
91
+ * max: (string|null),
92
+ * rate: (string|undefined),
93
+ * fiatMin: (number|null),
94
+ * fiatMax: (number|null)
95
+ * }|{
96
+ * result: true,
97
+ * swapCreationInfo: BaseSwapCreationInfo
98
+ * }>}
99
+ */
100
+
101
+ /**
102
+ * Async function that retrieves initial swap details for pair like whether the pair is available, min/max swappable amounts etc.
103
+ *
104
+ * @function
105
+ * @name retrieveInitialSwapData
106
+ * @param sendingAssetTicker {string}
107
+ * @param receivingAssetTicker {string}
108
+ * @return {Promise<{
109
+ * result: true,
110
+ * min: string,
111
+ * fiatMin: (number|null),
112
+ * max: string,
113
+ * fiatMax: (number|null),
114
+ * rate: (string|null)
115
+ * }|{
116
+ * result: false,
117
+ * reason: string
118
+ * }>}
119
+ */
120
+
121
+ /**
122
+ * Swap form provides basic functionality for entering to/from amounts both in crypto and fiat, switching assets,
123
+ * selecting assets, showing balance for assets.
124
+ * It gracefully handles not available fiat rates and can work without wallet environment (without balances).
125
+ *
126
+ * @param sendingAssetTicker {string} Rabbit format of ticker
127
+ * @param receivingAssetTicker {string} Rabbit format of ticker
128
+ * @param [sendingAssetDecimalCount=AmountUtils.significantDecimalCount] {number}
129
+ * @param [receivingAssetDecimalCount=AmountUtils.significantDecimalCount] {number}
130
+ * @param sendingAssetTickerPrintable {string} standard asset ticker
131
+ * @param receivingAssetTickerPrintable {string} standard asset ticker
132
+ * @param [sendingAssetProtocol] Protocol name to be displayed in text in the change asset button
133
+ * @param [receivingAssetProtocol] Protocol name to be displayed in text in the change asset button
134
+ * @param sendingAssetIconSrc {string} ready to use src
135
+ * @param [sendingAssetProtocolIconSrc=null] {string} optional ready to use src
136
+ * @param receivingAssetIconSrc {string} ready to use src
137
+ * @param [receivingAssetProtocolIconSrc=null] {string} optional ready to use src
138
+ * @param [fallBackAssetIconSrc=null] {string} optional ready to use src - used when some icon src resolution fails
139
+ * @param sendingAssetFeeCoinTickerPrintable {string} optional standard ticker for fee coin
140
+ * @param [sendingAssetBalance] {string}
141
+ * @param [receivingAssetBalance] {string}
142
+ * @param handleChangeSendingAssetClick {handleClick}
143
+ * @param handleChangeReceivingAssetClick {handleClick}
144
+ * @param handleConfirmButtonClick {handleClick}
145
+ * @param composeConfirmButtonTo {composeConfirmButtonTo}
146
+ * @param setSwapCreationEstimation {setSwapCreationEstimation}
147
+ * @param handleUnexpectedError {handleUnexpectedError}
148
+ * @param rotateAssets {rotateAssets}
149
+ * @param [preservedAmount=null] {string}
150
+ * @param handleSwapServiceError {handleSwapServiceError}
151
+ * @param [formHasBalance] {boolean}
152
+ * @param retrieveSwapDetails {retrieveSwapDetails}
153
+ * @param retrieveInitialSwapData {retrieveInitialSwapData}
154
+ * @param triggerDataUpdateResetting {number} convey changed number to reset interval updating the data periodically
155
+ * @param fromAssetSelectionButtonRef {React.Ref}
156
+ * @param toAssetSelectionButtonRef {React.Ref}
157
+ * @param [sendingAssetToFiatRate] {number|string}
158
+ * @param [receivingAssetToFiatRate] {number|string}
159
+ * @param [fiatCurrencyCode] {string}
160
+ * @param [fiatCurrencyDecimals] {number}
161
+ * @param formHasFiat {boolean}
162
+ * @param [translations] {object} default English texts will be used if the property is omitted
163
+ * @param swapSeparatorIconSrc {string} ready to use src
164
+ * @param swapButtonAlwaysActive {boolean} this setting allows to the form state in terms of validity to go
165
+ * to the next step. If this setting is set to true then user always can click the hero button
166
+ * @return {JSX.Element}
167
+ * @constructor
168
+ */
169
+ export const SwapForm = ({
170
+ sendingAssetTicker,
171
+ receivingAssetTicker,
172
+ sendingAssetDecimalCount = AmountUtils.significantDecimalCount,
173
+ receivingAssetDecimalCount = AmountUtils.significantDecimalCount,
174
+ sendingAssetTickerPrintable,
175
+ receivingAssetTickerPrintable,
176
+ sendingAssetProtocol,
177
+ receivingAssetProtocol,
178
+ sendingAssetIconSrc,
179
+ sendingAssetProtocolIconSrc = null,
180
+ receivingAssetIconSrc,
181
+ receivingAssetProtocolIconSrc = null,
182
+ fallBackAssetIconSrc = null,
183
+ sendingAssetFeeCoinTickerPrintable,
184
+ sendingAssetBalance,
185
+ receivingAssetBalance,
186
+ handleChangeSendingAssetClick,
187
+ handleChangeReceivingAssetClick,
188
+ handleConfirmButtonClick = () => {},
189
+ composeConfirmButtonTo = (fromTicker, toTicker, fromAmount) => "",
190
+ setSwapCreationEstimation = (details) => {},
191
+ handleUnexpectedError = (error) => {},
192
+ rotateAssets = (fromTicker, toTicker) => {},
193
+ preservedAmount = null,
194
+ handleSwapServiceError = (
195
+ errorCode,
196
+ setValidationContent,
197
+ setBalanceValid,
198
+ setMinimalAmountValid,
199
+ setMaximumAmountValid,
200
+ setIsPairSupported,
201
+ setIsSameCoins
202
+ ) => {},
203
+ formHasBalance = false,
204
+ retrieveSwapDetails = async (
205
+ sendingAssetTicker,
206
+ receivingAssetTicker,
207
+ amount,
208
+ isSwapAll = false
209
+ ) => {},
210
+ retrieveInitialSwapData = async (
211
+ sendingAssetTicker,
212
+ receivingAssetTicker
213
+ ) => {},
214
+ triggerDataUpdateResetting = 0,
215
+ fromAssetSelectionButtonRef = null,
216
+ toAssetSelectionButtonRef = null,
217
+ sendingAssetToFiatRate = null,
218
+ receivingAssetToFiatRate = null,
219
+ fiatCurrencyCode = null,
220
+ fiatCurrencyDecimals = null,
221
+ formHasFiat = true,
222
+ translations = {
223
+ swapAllButtonTitles: {
224
+ enable: "Swap all",
225
+ cancel: "Cancel",
226
+ },
227
+ input: {
228
+ fiatPlaceholder: "Enter fiat amount",
229
+ balanceLoaderText: "Loading balance...",
230
+ },
231
+ consents: {
232
+ consentTextRate: "Rate varies with the amount.",
233
+ consentText: 'By clicking "Swap" you agree with Rabbit Swap\'s',
234
+ termsOfUse: "Terms of Use",
235
+ and: "and",
236
+ privacyPolicy: "Privacy Policy",
237
+ },
238
+ informationBlock: {
239
+ minimumAmount: "Minimal amount: ",
240
+ maximumAmount: "Maximum amount: ",
241
+ transactionFee: "Network fee: ",
242
+ calculatingNetworkFee: "Calculating network fee..",
243
+ loadingMinimalAmount: "Loading minimal amount..",
244
+ swapRate: "Rate: ",
245
+ calculatingSwapRates: "Calculating swap rates..",
246
+ pairNotAvailable:
247
+ "Pair is not available now. Please, try again later or choose another pair.",
248
+ },
249
+ confirmButtonText: "Swap",
250
+ },
251
+ swapSeparatorIconSrc,
252
+ swapButtonAlwaysActive = false,
253
+ }) => {
254
+ const DETAIL_REFRESH_INTERVAL_MS = 1.5 * 60000;
255
+
256
+ // Updating the states below will insert the new value into the send or receive input field
257
+ const [updateSendInputTo, setUpdateSendInputTo] = useState(
258
+ preservedAmount ?? null
259
+ );
260
+ const [updateReceiveInputTo, setUpdateReceiveInputTo] = useState(null);
261
+
262
+ const [sendAssetAmount, setSendAssetAmount] = useReferredState(
263
+ preservedAmount == null || preservedAmount === ""
264
+ ? null
265
+ : preservedAmount
266
+ );
267
+
268
+ const [swapRate, setSwapRate] = useState();
269
+ const [minimalAmount, setMinimalAmount] = useReferredState(null);
270
+ const [maximumAmount, setMaximumAmount] = useReferredState(null);
271
+ const [validationContent, setValidationContent] = useState();
272
+
273
+ const [minimalAmountValid, setMinimalAmountValid] = useState(true); // Whether the amount is above the minimal amount
274
+ const [maximumAmountValid, setMaximumAmountValid] = useState(true);
275
+ const [isPairSupported, setIsPairSupported] = useState(true);
276
+ const [isSameCoins, setIsSameCoins] = useState(false);
277
+ const [isSwapCalculated, setIsSwapCalculated] = useState(false);
278
+ const [readyToSwap, setReadyToSwap] = useState(false); // Basically a param that enables/disables the "Next" button
279
+ // TODO: [refactoring, moderate] instead of handling preservedAmount at a lot of cases below, just
280
+ // write single useEffect setting sendAssetAmount to the=is passed value and simplify the logic below. task_id=6453251e49b04c5e88a3cc771479ffb5
281
+ const [isAmountZero, setIsAmountZero] = useState(
282
+ preservedAmount == null || String(preservedAmount) === "0"
283
+ );
284
+ const [isLoading, setIsLoading] = useReferredState(false); // Whether the form is in the progress of loading some data (new rates, for example)
285
+ const [confirmButtonTo, setConfirmButtonTo] = useState(
286
+ composeConfirmButtonTo(
287
+ sendingAssetTicker,
288
+ receivingAssetTicker,
289
+ preservedAmount
290
+ )
291
+ );
292
+
293
+ const [transactionFee, setTransactionFee] = useState();
294
+ const [balanceValid, setBalanceValid] = useState(true); // Whether the amount is less than total balance
295
+ const [isSwapAll, setIsSwapAll] = useReferredState(null);
296
+ const [swapAllButtonLoaderReSetter, setSwapAllButtonLoaderReSetter] =
297
+ useState([]);
298
+
299
+ const [dataUpdateTimeoutId, setDataUpdateTimeoutId] =
300
+ useReferredState(null);
301
+ const [idleDataUpdateTimeoutId, setIdleDataUpdateTimeoutId] =
302
+ useReferredState(null);
303
+
304
+ const callHandlingErrors = useCallHandlingErrors();
305
+
306
+ const recalculationDelayOnTyping = 1000;
307
+ const showConsent = !formHasBalance; // We show TOS&PP consent only when working outside wallet
308
+
309
+ const handleSendAssetAmountChange = (amount) => {
310
+ if (amount != null && amount !== "") {
311
+ setUpdateReceiveInputTo(
312
+ AmountUtils.trim(
313
+ BigNumber(amount).times(swapRate),
314
+ receivingAssetDecimalCount
315
+ )
316
+ );
317
+ } else {
318
+ setUpdateReceiveInputTo("");
319
+ }
320
+ setSendAssetAmount(amount);
321
+ };
322
+
323
+ const handleReceiveAssetAmountChange = (amount) => {
324
+ let sendAssetAmount = "";
325
+ if (amount != null && amount !== "") {
326
+ sendAssetAmount = AmountUtils.trim(
327
+ BigNumber(amount).div(swapRate),
328
+ sendingAssetDecimalCount
329
+ );
330
+ }
331
+ setUpdateSendInputTo(sendAssetAmount);
332
+ setSendAssetAmount(sendAssetAmount);
333
+ };
334
+
335
+ const handleMinimalAmountClick = () => {
336
+ if (!minimalAmount.current) return;
337
+
338
+ setUpdateSendInputTo(minimalAmount.current?.crypto);
339
+ handleSendAssetAmountChange(minimalAmount.current?.crypto);
340
+ };
341
+
342
+ const handleMaximumAmountClick = () => {
343
+ if (!maximumAmount.current) return;
344
+
345
+ setUpdateSendInputTo(maximumAmount.current?.crypto);
346
+ handleSendAssetAmountChange(maximumAmount.current?.crypto);
347
+ };
348
+
349
+ const handleSwapAllClick = (resetButtonLoader) => {
350
+ setIsSwapAll((prev) => {
351
+ if (prev) {
352
+ // Setting amount inputs to empty string if we are handling the disabling of previously enabled swap all
353
+ setUpdateSendInputTo("");
354
+ setUpdateReceiveInputTo("");
355
+ setSendAssetAmount(null);
356
+ }
357
+ return !prev;
358
+ });
359
+ setSwapAllButtonLoaderReSetter([resetButtonLoader]);
360
+ };
361
+
362
+ const processMinMaxAmounts = (amount) => {
363
+ if (amount === null) return;
364
+
365
+ setMaximumAmountValid(
366
+ !maximumAmount.current ||
367
+ BigNumber(amount).eq("0") ||
368
+ BigNumber(amount).lte(maximumAmount.current.crypto)
369
+ );
370
+ setMinimalAmountValid(
371
+ !minimalAmount.current ||
372
+ BigNumber(amount).eq("0") ||
373
+ BigNumber(amount).gte(minimalAmount.current?.crypto)
374
+ );
375
+ };
376
+
377
+ const requestDataRefresh = (amount, immediately = false) => {
378
+ try {
379
+ clearTimeout(dataUpdateTimeoutId.current);
380
+
381
+ if (!amount) {
382
+ return;
383
+ }
384
+
385
+ if (BigNumber(amount).eq("0")) {
386
+ if (immediately) {
387
+ loadMinimalAmountAndSwapRate(true);
388
+ } else {
389
+ setDataUpdateTimeoutId(
390
+ setTimeout(
391
+ () => loadMinimalAmountAndSwapRate(true),
392
+ recalculationDelayOnTyping
393
+ )
394
+ );
395
+ }
396
+ } else {
397
+ if (immediately) {
398
+ loadFullEstimation(
399
+ amount,
400
+ preservedAmount && String(preservedAmount) !== "0"
401
+ ? true
402
+ : undefined
403
+ );
404
+ } else {
405
+ setDataUpdateTimeoutId(
406
+ setTimeout(
407
+ () => loadFullEstimation(amount),
408
+ recalculationDelayOnTyping
409
+ )
410
+ );
411
+ }
412
+ }
413
+ } catch (e) {
414
+ handleUnexpectedError(e);
415
+ }
416
+ };
417
+
418
+ const setAmountLimitsAndRate = (details) => {
419
+ setSwapRate(details?.rate ?? null);
420
+ setMinimalAmount(
421
+ details.min == null
422
+ ? null
423
+ : { crypto: details.min, fiat: details.fiatMin }
424
+ );
425
+ setMaximumAmount(
426
+ details.max == null
427
+ ? null
428
+ : { crypto: details.max, fiat: details.fiatMax }
429
+ );
430
+ if (details.feeCoins != null) {
431
+ setTransactionFee({
432
+ crypto: details.feeCoins,
433
+ fiat: details.feeFiat,
434
+ });
435
+ } else {
436
+ setTransactionFee(null);
437
+ }
438
+ };
439
+
440
+ const loadFullEstimation = (amount, isForPreserved = false) => {
441
+ (async () => {
442
+ try {
443
+ const dataMemento = {
444
+ rate: swapRate,
445
+ min: minimalAmount.current?.crypto,
446
+ max: maximumAmount.current?.crypto,
447
+ feeCoins: transactionFee?.crypto,
448
+ feeFiat: transactionFee?.fiat,
449
+ };
450
+ setIsLoading(true);
451
+ setMinimalAmount(null);
452
+ setMaximumAmount(null);
453
+ setTransactionFee(null);
454
+ setValidationContent(null);
455
+ setSwapCreationEstimation(null);
456
+ setIsSameCoins(false);
457
+ setIsPairSupported(true);
458
+ setIsSwapCalculated(false);
459
+
460
+ const response = await retrieveSwapDetails(
461
+ sendingAssetTicker,
462
+ receivingAssetTicker,
463
+ amount,
464
+ isSwapAll.current
465
+ );
466
+
467
+ if (
468
+ !isSwapAll.current &&
469
+ String(amount) !==
470
+ String(
471
+ isForPreserved && sendAssetAmount.current == null
472
+ ? preservedAmount
473
+ : sendAssetAmount.current
474
+ )
475
+ ) {
476
+ // Means amount changed and we no more need to do this exact calculation
477
+ return;
478
+ }
479
+
480
+ if (response.result) {
481
+ const swapCreationInfo = response.swapCreationInfo;
482
+
483
+ setUpdateSendInputTo(swapCreationInfo.fromAmountCoins);
484
+ setUpdateReceiveInputTo(swapCreationInfo.toAmountCoins);
485
+
486
+ if (isSwapAll.current) {
487
+ setIsAmountZero(
488
+ BigNumber("0").eq(swapCreationInfo.fromAmountCoins)
489
+ );
490
+ }
491
+
492
+ setAmountLimitsAndRate(swapCreationInfo);
493
+ setSwapCreationEstimation(swapCreationInfo);
494
+ processMinMaxAmounts(
495
+ isSwapAll.current
496
+ ? swapCreationInfo.fromAmountCoins
497
+ : amount
498
+ );
499
+ setIsSwapCalculated(true);
500
+ } else {
501
+ handleSwapServiceError(
502
+ response.reason,
503
+ setValidationContent,
504
+ setBalanceValid,
505
+ setMinimalAmountValid,
506
+ setMaximumAmountValid,
507
+ setIsPairSupported,
508
+ setIsSameCoins
509
+ );
510
+ if (isSwapAll.current) {
511
+ /* We disable swap all if swap all details retrieval fails and set
512
+ * previous limits and rate. We set null to avoid triggering reloading of initial data.
513
+ */
514
+ setIsSwapAll(null);
515
+ setAmountLimitsAndRate(dataMemento);
516
+ } else {
517
+ /* We set returned amount limits and rate only if the failed details retrieval is not
518
+ * for the swap All case.
519
+ */
520
+ setAmountLimitsAndRate(response);
521
+ }
522
+ }
523
+ setIsLoading(false);
524
+ } catch (e) {
525
+ handleUnexpectedError(e);
526
+ }
527
+ })();
528
+ };
529
+
530
+ const loadMinimalAmountAndSwapRate = (isCalledForClearedInput = false) => {
531
+ (async () => {
532
+ try {
533
+ setIsLoading(true);
534
+ setMinimalAmount(null);
535
+ setMaximumAmount(null);
536
+ setSwapRate(null);
537
+ setValidationContent(null);
538
+ setIsSwapCalculated(false);
539
+
540
+ const swapData = await retrieveInitialSwapData(
541
+ sendingAssetTicker,
542
+ receivingAssetTicker
543
+ );
544
+
545
+ if (
546
+ sendAssetAmount.current != null &&
547
+ !isCalledForClearedInput
548
+ ) {
549
+ // Means user already entered amount after starting the form initialization
550
+ return;
551
+ }
552
+
553
+ if (swapData.result === true) {
554
+ setAmountLimitsAndRate(swapData);
555
+ } else {
556
+ handleSwapServiceError(
557
+ swapData.reason,
558
+ setValidationContent,
559
+ setBalanceValid,
560
+ setMinimalAmountValid,
561
+ setMaximumAmountValid,
562
+ setIsPairSupported,
563
+ setIsSameCoins
564
+ );
565
+ }
566
+
567
+ setIsLoading(false);
568
+ } catch (e) {
569
+ handleUnexpectedError(e);
570
+ }
571
+ })();
572
+ };
573
+
574
+ const handleChangeAssetsIconClick = (e) => {
575
+ callHandlingErrors(() => {
576
+ if (!isLoading.current) {
577
+ setIsSwapAll(null);
578
+ setMaximumAmountValid(true);
579
+ setMinimalAmountValid(true);
580
+ rotateAssets(sendingAssetTicker, receivingAssetTicker);
581
+ }
582
+ }, e);
583
+ };
584
+
585
+ useEffect(() => {
586
+ if (isSwapAll.current === true) {
587
+ requestDataRefresh(undefined, true);
588
+ } else if (isSwapAll.current === false) {
589
+ loadMinimalAmountAndSwapRate();
590
+ }
591
+ // eslint-disable-next-line react-hooks/exhaustive-deps
592
+ }, [isSwapAll.current]);
593
+
594
+ useEffect(() => {
595
+ setConfirmButtonTo(
596
+ composeConfirmButtonTo(
597
+ sendingAssetTicker,
598
+ receivingAssetTicker,
599
+ sendAssetAmount.current || preservedAmount || null
600
+ )
601
+ );
602
+ // eslint-disable-next-line react-hooks/exhaustive-deps
603
+ }, [sendingAssetTicker, receivingAssetTicker]);
604
+
605
+ useEffect(() => {
606
+ if (!sendingAssetTicker || !receivingAssetTicker) return;
607
+
608
+ const isCurrentAmountNotZero =
609
+ sendAssetAmount.current &&
610
+ !BigNumber("0").eq(sendAssetAmount.current);
611
+ const isPreservedAmountNotZero =
612
+ preservedAmount && !BigNumber("0").eq(preservedAmount);
613
+ if (isCurrentAmountNotZero) {
614
+ loadFullEstimation(sendAssetAmount.current);
615
+ } else if (isPreservedAmountNotZero) {
616
+ loadFullEstimation(preservedAmount, true);
617
+ } else {
618
+ loadMinimalAmountAndSwapRate();
619
+ }
620
+
621
+ // eslint-disable-next-line react-hooks/exhaustive-deps
622
+ }, [sendingAssetTicker, receivingAssetTicker]);
623
+
624
+ useEffect(() => {
625
+ if (!minimalAmountValid || isAmountZero) {
626
+ setTransactionFee(null);
627
+ }
628
+ if (isAmountZero) {
629
+ clearTimeout(dataUpdateTimeoutId.current);
630
+ }
631
+
632
+ setReadyToSwap(
633
+ swapButtonAlwaysActive ||
634
+ ((balanceValid || !formHasBalance) &&
635
+ minimalAmountValid &&
636
+ !isAmountZero &&
637
+ isSwapCalculated &&
638
+ (!isSameCoins || !formHasBalance) &&
639
+ isPairSupported)
640
+ );
641
+
642
+ // eslint-disable-next-line react-hooks/exhaustive-deps
643
+ }, [
644
+ balanceValid,
645
+ formHasBalance,
646
+ minimalAmountValid,
647
+ isAmountZero,
648
+ isSwapCalculated,
649
+ isSameCoins,
650
+ isPairSupported,
651
+ ]);
652
+
653
+ useEffect(() => {
654
+ // Here we set up auto recalculations for the swap details if the form is ready to swap but is idle for some time
655
+ let timeoutId = null;
656
+ if (readyToSwap) {
657
+ timeoutId = setTimeout(
658
+ () =>
659
+ requestDataRefresh(
660
+ sendAssetAmount.current ?? preservedAmount,
661
+ true
662
+ ),
663
+ DETAIL_REFRESH_INTERVAL_MS
664
+ );
665
+ setIdleDataUpdateTimeoutId(timeoutId);
666
+ } else {
667
+ clearTimeout(idleDataUpdateTimeoutId.current);
668
+ setIdleDataUpdateTimeoutId(null);
669
+ }
670
+ return () => {
671
+ timeoutId != null && clearTimeout(timeoutId);
672
+ };
673
+ // eslint-disable-next-line react-hooks/exhaustive-deps
674
+ }, [readyToSwap]);
675
+
676
+ // TODO: [refactoring, critical] this code looks like a hack related to task_id=6e328d39063142b7b9fa01d497e616da
677
+ useEffect(() => {
678
+ if (triggerDataUpdateResetting) {
679
+ clearTimeout(dataUpdateTimeoutId.current);
680
+ setDataUpdateTimeoutId(null);
681
+
682
+ clearTimeout(idleDataUpdateTimeoutId.current);
683
+ setIdleDataUpdateTimeoutId(null);
684
+ }
685
+ // eslint-disable-next-line react-hooks/exhaustive-deps
686
+ }, [triggerDataUpdateResetting]);
687
+
688
+ // Resets the passed "set value to" param to null, so it can be used multiple times with any value
689
+ useEffect(() => {
690
+ if (!!updateSendInputTo) setUpdateSendInputTo(null);
691
+ if (!!updateReceiveInputTo) setUpdateReceiveInputTo(null);
692
+ // eslint-disable-next-line react-hooks/exhaustive-deps
693
+ }, [updateSendInputTo, updateReceiveInputTo]);
694
+
695
+ useEffect(() => {
696
+ if (swapRate != null && sendAssetAmount.current != null) {
697
+ setUpdateReceiveInputTo(
698
+ AmountUtils.trim(
699
+ BigNumber(sendAssetAmount.current).times(swapRate),
700
+ receivingAssetDecimalCount
701
+ )
702
+ );
703
+ }
704
+ // eslint-disable-next-line react-hooks/exhaustive-deps
705
+ }, [swapRate]);
706
+
707
+ useEffect(() => {
708
+ if (
709
+ isLoading.current === false &&
710
+ swapAllButtonLoaderReSetter?.length
711
+ ) {
712
+ swapAllButtonLoaderReSetter[0]();
713
+ setSwapAllButtonLoaderReSetter([]);
714
+ }
715
+ // eslint-disable-next-line react-hooks/exhaustive-deps
716
+ }, [isLoading.current, swapAllButtonLoaderReSetter]);
717
+
718
+ // Handlers upon changing the asset amount
719
+ useEffect(() => {
720
+ setConfirmButtonTo(
721
+ composeConfirmButtonTo(
722
+ sendingAssetTicker,
723
+ receivingAssetTicker,
724
+ sendAssetAmount.current
725
+ )
726
+ );
727
+
728
+ if (sendAssetAmount.current == null) return;
729
+ let processingAmount =
730
+ sendAssetAmount.current === "" ? "0" : sendAssetAmount.current;
731
+ requestDataRefresh(processingAmount);
732
+ processMinMaxAmounts(processingAmount);
733
+ setIsAmountZero(BigNumber("0").eq(processingAmount));
734
+ setIsSwapCalculated(false);
735
+ // eslint-disable-next-line react-hooks/exhaustive-deps
736
+ }, [sendAssetAmount.current]);
737
+
738
+ return (
739
+ <div className={s["swap-dialog-form"]}>
740
+ <TitleBox
741
+ linkButtonClick={
742
+ BigNumber(sendingAssetBalance?.assetAmount).eq(0)
743
+ ? null
744
+ : (resetButtonLoader) =>
745
+ handleSwapAllClick(resetButtonLoader)
746
+ }
747
+ linkText={
748
+ !formHasBalance
749
+ ? ""
750
+ : isSwapAll.current
751
+ ? translations.swapAllButtonTitles.cancel
752
+ : translations.swapAllButtonTitles.enable
753
+ }
754
+ linkButtonLoader={true}
755
+ isLinkButtonDisabled={
756
+ isLoading.current ||
757
+ sendingAssetTicker === receivingAssetTicker ||
758
+ BigNumber(sendingAssetBalance?.assetAmount).eq(0)
759
+ }
760
+ // TODO: texts for copy button
761
+ >
762
+ <div className={s["swap-dialog-form-inputs"]}>
763
+ <AmountInput
764
+ ticker={sendingAssetTicker}
765
+ tickerPrintable={sendingAssetTickerPrintable}
766
+ assetDecimalPlaces={sendingAssetDecimalCount}
767
+ assetBalance={sendingAssetBalance}
768
+ assetIconSrc={sendingAssetIconSrc}
769
+ assetIconProtocolSrc={sendingAssetProtocolIconSrc}
770
+ fallbackAssetIconSrc={fallBackAssetIconSrc}
771
+ disabled={
772
+ isSwapAll.current ||
773
+ sendingAssetTicker === null ||
774
+ receivingAssetTicker === null
775
+ }
776
+ handleCoinAmountChange={handleSendAssetAmountChange}
777
+ handleChangeAssetClick={handleChangeSendingAssetClick}
778
+ handleBalanceValidationChange={(isValid) =>
779
+ setBalanceValid(!isValid)
780
+ }
781
+ updateAssetInputTo={updateSendInputTo}
782
+ showBalance={formHasBalance}
783
+ showBalanceValidation={formHasBalance}
784
+ showChangeAssetButton
785
+ changeAssetButtonProtocol={sendingAssetProtocol}
786
+ upperFormPosition
787
+ errorEncountered={!minimalAmountValid}
788
+ ref={fromAssetSelectionButtonRef}
789
+ isLoading={false}
790
+ cryptoAssetToFiatRate={sendingAssetToFiatRate}
791
+ fiatCurrencyCode={formHasFiat ? fiatCurrencyCode : null}
792
+ fiatCurrencyDecimals={fiatCurrencyDecimals}
793
+ balanceLoaderText={translations.input.balanceLoaderText}
794
+ fiatInputPlaceholderText={
795
+ translations.input.fiatPlaceholder
796
+ }
797
+ />
798
+ <div
799
+ className={
800
+ s["swap-dialog-form-inputs-separator"] +
801
+ " " +
802
+ (isLoading.current ||
803
+ sendingAssetTicker === null ||
804
+ receivingAssetTicker === null
805
+ ? s["disabled"]
806
+ : "")
807
+ }
808
+ >
809
+ <img
810
+ src={swapSeparatorIconSrc}
811
+ alt="swap icon"
812
+ draggable={false}
813
+ onClick={(e) =>
814
+ callHandlingErrors(
815
+ handleChangeAssetsIconClick,
816
+ e
817
+ )
818
+ }
819
+ />
820
+ </div>
821
+ <AmountInput
822
+ ticker={receivingAssetTicker}
823
+ tickerPrintable={receivingAssetTickerPrintable}
824
+ assetDecimalPlaces={receivingAssetDecimalCount}
825
+ assetBalance={receivingAssetBalance}
826
+ assetIconSrc={receivingAssetIconSrc}
827
+ assetIconProtocolSrc={receivingAssetProtocolIconSrc}
828
+ fallbackAssetIconSrc={fallBackAssetIconSrc}
829
+ disabled={
830
+ isSwapAll.current ||
831
+ sendingAssetTicker === null ||
832
+ receivingAssetTicker === null
833
+ }
834
+ handleCoinAmountChange={handleReceiveAssetAmountChange}
835
+ handleChangeAssetClick={handleChangeReceivingAssetClick}
836
+ updateAssetInputTo={updateReceiveInputTo}
837
+ showChangeAssetButton
838
+ changeAssetButtonProtocol={receivingAssetProtocol}
839
+ showBalance={formHasBalance}
840
+ lowerFormPosition
841
+ estimateAmount
842
+ ref={toAssetSelectionButtonRef}
843
+ isLoading={isLoading.current}
844
+ cryptoAssetToFiatRate={receivingAssetToFiatRate}
845
+ fiatCurrencyCode={formHasFiat ? fiatCurrencyCode : null}
846
+ fiatCurrencyDecimals={fiatCurrencyDecimals}
847
+ balanceLoaderText={translations.input.balanceLoaderText}
848
+ fiatInputPlaceholderText={
849
+ translations.input.fiatPlaceholder
850
+ }
851
+ />
852
+ </div>
853
+ <div className={s["swap-dialog-form-information-field"]}>
854
+ <p>
855
+ {!isPairSupported ? (
856
+ translations.informationBlock.pairNotAvailable
857
+ ) : transactionFee && minimalAmountValid ? (
858
+ <>
859
+ {translations.informationBlock.transactionFee}
860
+ <span>
861
+ {AmountUtils.crypto(
862
+ transactionFee?.crypto,
863
+ sendingAssetFeeCoinTickerPrintable
864
+ )}
865
+ </span>
866
+ {transactionFee?.fiat != null &&
867
+ transactionFee?.fiat !== "" ? (
868
+ <span className={s["semi-transparent"]}>
869
+ {" ~ " +
870
+ AmountUtils.fiat(
871
+ transactionFee?.fiat,
872
+ fiatCurrencyCode
873
+ )}
874
+ </span>
875
+ ) : (
876
+ ""
877
+ )}
878
+ </>
879
+ ) : isSwapCalculated ? (
880
+ <>
881
+ {translations.informationBlock.swapRate}
882
+ <span>
883
+ {AmountUtils.composeRateText(
884
+ sendingAssetTickerPrintable,
885
+ receivingAssetTickerPrintable,
886
+ swapRate
887
+ )}
888
+ </span>
889
+ </>
890
+ ) : minimalAmount.current ? (
891
+ !maximumAmountValid &&
892
+ maximumAmount.current != null ? (
893
+ <>
894
+ {
895
+ translations.informationBlock
896
+ .maximumAmount
897
+ }
898
+ <span
899
+ className={
900
+ s["interactable"] + " " + s["red"]
901
+ }
902
+ onClick={
903
+ isLoading.current
904
+ ? () => {}
905
+ : (e) =>
906
+ callHandlingErrors(
907
+ handleMaximumAmountClick,
908
+ e
909
+ )
910
+ }
911
+ >
912
+ {AmountUtils.crypto(
913
+ maximumAmount.current?.crypto,
914
+ sendingAssetTickerPrintable
915
+ )}
916
+ </span>
917
+ {maximumAmount.current?.fiat != null &&
918
+ maximumAmount.current?.fiat !== "" ? (
919
+ <span className={s["semi-transparent"]}>
920
+ {" ~ " +
921
+ AmountUtils.fiat(
922
+ maximumAmount.current.fiat,
923
+ fiatCurrencyCode
924
+ )}
925
+ </span>
926
+ ) : (
927
+ ""
928
+ )}
929
+ </>
930
+ ) : (
931
+ <>
932
+ {
933
+ translations.informationBlock
934
+ .minimumAmount
935
+ }
936
+ <span
937
+ className={
938
+ s["interactable"] +
939
+ " " +
940
+ (!minimalAmountValid
941
+ ? s["red"]
942
+ : "")
943
+ }
944
+ onClick={
945
+ isLoading.current
946
+ ? () => {}
947
+ : (e) =>
948
+ callHandlingErrors(
949
+ handleMinimalAmountClick,
950
+ e
951
+ )
952
+ }
953
+ >
954
+ {AmountUtils.crypto(
955
+ minimalAmount.current.crypto,
956
+ sendingAssetTickerPrintable
957
+ )}
958
+ </span>
959
+ {minimalAmount.current?.fiat != null &&
960
+ minimalAmount.current.fiat !== "" ? (
961
+ <span className={s["semi-transparent"]}>
962
+ {" ~ " +
963
+ AmountUtils.fiat(
964
+ minimalAmount.current.fiat,
965
+ fiatCurrencyCode
966
+ )}
967
+ </span>
968
+ ) : (
969
+ ""
970
+ )}
971
+ </>
972
+ )
973
+ ) : sendAssetAmount.current || isSwapAll.current ? (
974
+ translations.informationBlock[
975
+ formHasBalance
976
+ ? "calculatingNetworkFee"
977
+ : "calculatingSwapRates"
978
+ ]
979
+ ) : (
980
+ translations.informationBlock.loadingMinimalAmount
981
+ )}
982
+ {/* TODO: [feature, moderate] add "Please enter the amount you want to swap." */}
983
+ </p>
984
+ </div>
985
+ {validationContent ? (
986
+ <div className={s["swap-dialog-form-validation-text"]}>
987
+ <Validation text={validationContent} />
988
+ </div>
989
+ ) : (
990
+ ""
991
+ )}
992
+ <div className={s["swap-dialog-form-button-container"]}>
993
+ {formHasBalance ? null : (
994
+ <p
995
+ className={
996
+ s[
997
+ "swap-dialog-form-button-container-consent-text"
998
+ ]
999
+ }
1000
+ >
1001
+ {translations.consents.consentTextRate}
1002
+ </p>
1003
+ )}
1004
+ {showConsent ? null : (
1005
+ <p
1006
+ className={
1007
+ s[
1008
+ "swap-dialog-form-button-container-consent-text"
1009
+ ]
1010
+ }
1011
+ >
1012
+ {translations.consents.consentText + " "}
1013
+ <a
1014
+ // TODO: [dev] fix dox link - pass via property
1015
+ href="/docs/legal/swap-terms-of-use.html"
1016
+ className={
1017
+ s[
1018
+ "swap-dialog-form-button-container-consent-text-link"
1019
+ ]
1020
+ }
1021
+ target="_blank"
1022
+ >
1023
+ {translations.consents.termsOfUse}
1024
+ </a>
1025
+ {" " + translations.consents.and + " "}
1026
+ <a
1027
+ // TODO: [dev] fix dox link - pass via property
1028
+ href="/docs/legal/swap-privacy-policy.html"
1029
+ className={
1030
+ s[
1031
+ "swap-dialog-form-button-container-consent-text-link"
1032
+ ]
1033
+ }
1034
+ target="_blank"
1035
+ >
1036
+ {translations.consents.privacyPolicy}
1037
+ </a>
1038
+ .
1039
+ </p>
1040
+ )}
1041
+ <Button
1042
+ size="lg"
1043
+ mode="primary"
1044
+ content={translations.confirmButtonText}
1045
+ onClick={handleConfirmButtonClick}
1046
+ fullWidthOnMobiles
1047
+ isDisabled={!readyToSwap}
1048
+ to={formHasBalance ? "" : confirmButtonTo}
1049
+ loader={formHasBalance}
1050
+ handleError={callHandlingErrors}
1051
+ />
1052
+ </div>
1053
+ </TitleBox>
1054
+ </div>
1055
+ );
1056
+ };
1057
+
1058
+ SwapForm.propTypes = {
1059
+ sendingAssetTicker: PropTypes.string.isRequired,
1060
+ receivingAssetTicker: PropTypes.string.isRequired,
1061
+ sendingAssetDecimalCount: PropTypes.number,
1062
+ receivingAssetDecimalCount: PropTypes.number,
1063
+ sendingAssetTickerPrintable: PropTypes.string.isRequired,
1064
+ receivingAssetTickerPrintable: PropTypes.string.isRequired,
1065
+ sendingAssetProtocol: PropTypes.string,
1066
+ receivingAssetProtocol: PropTypes.string,
1067
+ sendingAssetIconSrc: PropTypes.string.isRequired,
1068
+ sendingAssetProtocolIconSrc: PropTypes.oneOfType([PropTypes.string, null]),
1069
+ receivingAssetIconSrc: PropTypes.string.isRequired,
1070
+ receivingAssetProtocolIconSrc: PropTypes.oneOfType([
1071
+ PropTypes.string,
1072
+ null,
1073
+ ]),
1074
+ fallBackAssetIconSrc: PropTypes.oneOfType([PropTypes.string, null]),
1075
+ sendingAssetFeeCoinTickerPrintable: PropTypes.string,
1076
+ sendingAssetBalance: PropTypes.string,
1077
+ receivingAssetBalance: PropTypes.string,
1078
+ handleChangeSendingAssetClick: PropTypes.func.isRequired,
1079
+ handleChangeReceivingAssetClick: PropTypes.func.isRequired,
1080
+ handleConfirmButtonClick: PropTypes.func,
1081
+ composeConfirmButtonTo: PropTypes.func,
1082
+ setSwapCreationEstimation: PropTypes.func,
1083
+ handleUnexpectedError: PropTypes.func,
1084
+ rotateAssets: PropTypes.func,
1085
+ preservedAmount: PropTypes.oneOfType([PropTypes.string, null]),
1086
+ handleSwapServiceError: PropTypes.func.isRequired,
1087
+ formHasBalance: PropTypes.bool,
1088
+ retrieveSwapDetails: PropTypes.func.isRequired,
1089
+ retrieveInitialSwapData: PropTypes.func.isRequired,
1090
+ triggerDataUpdateResetting: PropTypes.number,
1091
+ fromAssetSelectionButtonRef: PropTypes.any,
1092
+ toAssetSelectionButtonRef: PropTypes.any,
1093
+ sendingAssetToFiatRate: PropTypes.oneOfType([
1094
+ PropTypes.number,
1095
+ PropTypes.string,
1096
+ null,
1097
+ ]),
1098
+ receivingAssetToFiatRate: PropTypes.oneOfType([
1099
+ PropTypes.number,
1100
+ PropTypes.string,
1101
+ null,
1102
+ ]),
1103
+ fiatCurrencyCode: PropTypes.oneOfType([PropTypes.string, null]),
1104
+ fiatCurrencyDecimals: PropTypes.oneOfType([PropTypes.number, null]),
1105
+ formHasFiat: PropTypes.bool,
1106
+ translations: PropTypes.object,
1107
+ swapSeparatorIconSrc: PropTypes.string.isRequired,
1108
+ swapButtonAlwaysActive: PropTypes.bool,
1109
+ };
1110
+
1111
+ SwapForm.defaultProps = {
1112
+ sendingAssetDecimalCount: AmountUtils.significantDecimalCount,
1113
+ receivingAssetDecimalCount: AmountUtils.significantDecimalCount,
1114
+ sendingAssetProtocolIconSrc: null,
1115
+ receivingAssetProtocolIconSrc: null,
1116
+ fallBackAssetIconSrc: null,
1117
+ handleConfirmButtonClick: () => {},
1118
+ composeConfirmButtonTo: (fromTicker, toTicker, fromAmount) => "",
1119
+ setSwapCreationEstimation: (details) => {},
1120
+ handleUnexpectedError: (error) => {},
1121
+ rotateAssets: (fromTicker, toTicker) => {},
1122
+ preservedAmount: null,
1123
+ handleSwapServiceError: (
1124
+ errorCode,
1125
+ setValidationContent,
1126
+ setBalanceValid,
1127
+ setMinimalAmountValid,
1128
+ setMaximumAmountValid,
1129
+ setIsPairSupported,
1130
+ setIsSameCoins
1131
+ ) => {},
1132
+ retrieveSwapDetails: async (
1133
+ sendingAssetTicker,
1134
+ receivingAssetTicker,
1135
+ amount,
1136
+ isSwapAll = false
1137
+ ) => {},
1138
+ retrieveInitialSwapData: async (
1139
+ sendingAssetTicker,
1140
+ receivingAssetTicker
1141
+ ) => {},
1142
+ triggerDataUpdateResetting: 0,
1143
+ fromAssetSelectionButtonRef: null,
1144
+ toAssetSelectionButtonRef: null,
1145
+ sendingAssetToFiatRate: null,
1146
+ receivingAssetToFiatRate: null,
1147
+ fiatCurrencyCode: null,
1148
+ fiatCurrencyDecimals: null,
1149
+ formHasFiat: true,
1150
+ swapButtonAlwaysActive: false,
1151
+ };