@rabbitio/ui-kit 1.0.0-beta.2 → 1.0.0-beta.21
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/CHANGELOG.md +0 -0
- package/README.md +23 -16
- package/dist/index.cjs +4404 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +8757 -1
- package/dist/index.css.map +1 -1
- package/dist/index.modern.js +3692 -1
- package/dist/index.modern.js.map +1 -1
- package/dist/index.module.js +4375 -1
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +4406 -1
- package/dist/index.umd.js.map +1 -1
- package/index.js +1 -1
- package/package.json +17 -24
- package/src/common/amountUtils.js +423 -0
- package/src/common/errorUtils.js +27 -0
- package/src/common/fiatCurrenciesService.js +161 -0
- package/src/common/models/blockchain.js +10 -0
- package/src/common/models/coin.js +157 -0
- package/src/common/models/protocol.js +5 -0
- package/src/common/utils/cache.js +268 -0
- package/src/common/utils/emailAPI.js +18 -0
- package/src/common/utils/logging/logger.js +48 -0
- package/src/common/utils/logging/logsStorage.js +61 -0
- package/src/common/utils/safeStringify.js +50 -0
- package/src/components/atoms/AssetIcon/AssetIcon.jsx +55 -0
- package/src/components/atoms/AssetIcon/asset-icon.module.scss +42 -0
- package/{stories → src/components}/atoms/LoadingDots/LoadingDots.module.scss +1 -1
- package/src/components/atoms/SupportChat/SupportChat.jsx +40 -0
- package/{stories → src/components}/atoms/buttons/Button/Button.jsx +6 -6
- package/{stories → src/components}/atoms/buttons/Button/Button.module.scss +6 -1
- package/src/components/hooks/useCallHandlingErrors.js +26 -0
- package/src/components/hooks/useReferredState.js +24 -0
- package/src/index.js +33 -0
- package/src/swaps-lib/external-apis/swapProvider.js +169 -0
- package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +812 -0
- package/src/swaps-lib/models/baseSwapCreationInfo.js +40 -0
- package/src/swaps-lib/models/existingSwap.js +58 -0
- package/src/swaps-lib/models/existingSwapWithFiatData.js +115 -0
- package/src/swaps-lib/services/publicSwapService.js +602 -0
- package/src/swaps-lib/utils/swapUtils.js +209 -0
- package/stories/index.js +0 -2
- /package/{stories → src/components}/atoms/LoadingDots/LoadingDots.jsx +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export class FiatCurrenciesService {
|
|
2
|
+
static getFullCurrencyNameByCode(code = "") {
|
|
3
|
+
const data = fiatCurrenciesList.find(
|
|
4
|
+
(currencyData) => currencyData[0] === code.toUpperCase()
|
|
5
|
+
);
|
|
6
|
+
return (data && data[2]) || null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static isCodeValid(code) {
|
|
10
|
+
return !!fiatCurrenciesList.find(
|
|
11
|
+
(currenciesData) => currenciesData[0] === code
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns currency symbol by code if present
|
|
17
|
+
*
|
|
18
|
+
* @param code {string} currency code
|
|
19
|
+
* @return {string|null} code or null if there is no symbol for the currency
|
|
20
|
+
*/
|
|
21
|
+
static getCurrencySymbolByCode(code = "") {
|
|
22
|
+
const data = fiatCurrenciesList.find(
|
|
23
|
+
(currencyData) => currencyData[0] === code.toUpperCase()
|
|
24
|
+
);
|
|
25
|
+
return data?.[1] ?? null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param code {string}
|
|
30
|
+
* @return {number|null}
|
|
31
|
+
*/
|
|
32
|
+
static getCurrencyDecimalCountByCode(code = "") {
|
|
33
|
+
const data = fiatCurrenciesList.find(
|
|
34
|
+
(currencyData) => currencyData[0] === code.toUpperCase()
|
|
35
|
+
);
|
|
36
|
+
return data?.[3] ?? null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fiatCurrenciesList = [
|
|
41
|
+
["USD", "$", "US Dollar", 2],
|
|
42
|
+
["CAD", "CA$", "Canadian Dollar", 2],
|
|
43
|
+
["EUR", "€", "Euro", 2],
|
|
44
|
+
["AED", "AED", "UAE Dirham", 2],
|
|
45
|
+
["AFN", "؋", "Afghan Afghani", 0],
|
|
46
|
+
["ALL", "ALL", "Albanian Lek", 0],
|
|
47
|
+
["AMD", "֏", "Armenian Dram", 0],
|
|
48
|
+
["ARS", "AR$", "Argentine Peso", 2],
|
|
49
|
+
["AUD", "AU$", "Australian Dollar", 2],
|
|
50
|
+
["AZN", "₼", "Azerbaijani Manat", 2],
|
|
51
|
+
["BAM", "KM", "Bosnia-Herzegovina Convertible Mark", 2],
|
|
52
|
+
["BDT", "Tk", "Bangladeshi Taka", 2],
|
|
53
|
+
["BGN", "BGN", "Bulgarian Lev", 2],
|
|
54
|
+
["BHD", "BD", "Bahraini Dinar", 3],
|
|
55
|
+
["BIF", "FBu", "Burundian Franc", 0],
|
|
56
|
+
["BND", "BN$", "Brunei Dollar", 2],
|
|
57
|
+
["BOB", "Bs", "Bolivian Boliviano", 2],
|
|
58
|
+
["BRL", "R$", "Brazilian Real", 2],
|
|
59
|
+
["BWP", "BWP", "Botswanan Pula", 2],
|
|
60
|
+
["BYN", "Br", "Belarusian Ruble", 2],
|
|
61
|
+
["BZD", "BZ$", "Belize Dollar", 2],
|
|
62
|
+
["CDF", "CDF", "Congolese Franc", 2],
|
|
63
|
+
["CHF", "CHF", "Swiss Franc", 2],
|
|
64
|
+
["CLP", "CL$", "Chilean Peso", 0],
|
|
65
|
+
["CNY", "CN¥", "Chinese Yuan", 2],
|
|
66
|
+
["COP", "CO$", "Colombian Peso", 0],
|
|
67
|
+
["CRC", "₡", "Costa Rican Colón", 0],
|
|
68
|
+
["CVE", "CV$", "Cape Verdean Escudo", 2],
|
|
69
|
+
["CZK", "Kč", "Czech Republic Koruna", 2],
|
|
70
|
+
["DJF", "Fdj", "Djiboutian Franc", 0],
|
|
71
|
+
["DKK", "Dkr", "Danish Krone", 2],
|
|
72
|
+
["DOP", "RD$", "Dominican Peso", 2],
|
|
73
|
+
["DZD", "DA", "Algerian Dinar", 2],
|
|
74
|
+
["EEK", "Ekr", "Estonian Kroon", 2],
|
|
75
|
+
["EGP", "EGP", "Egyptian Pound", 2],
|
|
76
|
+
["ERN", "Nfk", "Eritrean Nakfa", 2],
|
|
77
|
+
["ETB", "Br", "Ethiopian Birr", 2],
|
|
78
|
+
["GBP", "£", "British Pound Sterling", 2],
|
|
79
|
+
["GEL", "₾", "Georgian Lari", 2],
|
|
80
|
+
["GHS", "₵", "Ghanaian Cedi", 2],
|
|
81
|
+
["GNF", "FG", "Guinean Franc", 0],
|
|
82
|
+
["GTQ", "GTQ", "Guatemalan Quetzal", 2],
|
|
83
|
+
["HKD", "HK$", "Hong Kong Dollar", 2],
|
|
84
|
+
["HNL", "HNL", "Honduran Lempira", 2],
|
|
85
|
+
["HRK", "kn", "Croatian Kuna", 2],
|
|
86
|
+
["HUF", "Ft", "Hungarian Forint", 0],
|
|
87
|
+
["IDR", "Rp", "Indonesian Rupiah", 0],
|
|
88
|
+
["ILS", "₪", "Israeli New Sheqel", 2],
|
|
89
|
+
["INR", "₹", "Indian Rupee", 2],
|
|
90
|
+
["IQD", "IQD", "Iraqi Dinar", 0],
|
|
91
|
+
["IRR", "﷼", "Iranian Rial", 0],
|
|
92
|
+
["ISK", "Ikr", "Icelandic Króna", 0],
|
|
93
|
+
["JMD", "J$", "Jamaican Dollar", 2],
|
|
94
|
+
["JOD", "JD", "Jordanian Dinar", 3],
|
|
95
|
+
["JPY", "¥", "Japanese Yen", 0],
|
|
96
|
+
["KES", "Ksh", "Kenyan Shilling", 2],
|
|
97
|
+
["KHR", "KHR", "Cambodian Riel", 2],
|
|
98
|
+
["KMF", "CF", "Comorian Franc", 0],
|
|
99
|
+
["KRW", "₩", "South Korean Won", 0],
|
|
100
|
+
["KWD", "KD", "Kuwaiti Dinar", 3],
|
|
101
|
+
["KZT", "₸", "Kazakhstani Tenge", 2],
|
|
102
|
+
["LBP", "LB£", "Lebanese Pound", 0],
|
|
103
|
+
["LKR", "SLRs", "Sri Lankan Rupee", 2],
|
|
104
|
+
["LTL", "Lt", "Lithuanian Litas", 2],
|
|
105
|
+
["LVL", "Ls", "Latvian Lats", 2],
|
|
106
|
+
["LYD", "LD", "Libyan Dinar", 3],
|
|
107
|
+
["MAD", "MAD", "Moroccan Dirham", 2],
|
|
108
|
+
["MDL", "MDL", "Moldovan Leu", 2],
|
|
109
|
+
["MGA", "MGA", "Malagasy Ariary", 0],
|
|
110
|
+
["MKD", "MKD", "Macedonian Denar", 2],
|
|
111
|
+
["MMK", "MMK", "Myanma Kyat", 0],
|
|
112
|
+
["MNT", "₮", "Mongolian Tugrik", 0],
|
|
113
|
+
["MOP", "MOP$", "Macanese Pataca", 2],
|
|
114
|
+
["MUR", "MURs", "Mauritian Rupee", 0],
|
|
115
|
+
["MXN", "MX$", "Mexican Peso", 2],
|
|
116
|
+
["MYR", "RM", "Malaysian Ringgit", 2],
|
|
117
|
+
["MZN", "MTn", "Mozambican Metical", 2],
|
|
118
|
+
["NAD", "N$", "Namibian Dollar", 2],
|
|
119
|
+
["NGN", "₦", "Nigerian Naira", 2],
|
|
120
|
+
["NIO", "C$", "Nicaraguan Córdoba", 2],
|
|
121
|
+
["NOK", "Nkr", "Norwegian Krone", 2],
|
|
122
|
+
["NPR", "NPRs", "Nepalese Rupee", 2],
|
|
123
|
+
["NZD", "NZ$", "New Zealand Dollar", 2],
|
|
124
|
+
["OMR", "OMR", "Omani Rial", 3],
|
|
125
|
+
["PAB", "B/.", "Panamanian Balboa", 2],
|
|
126
|
+
["PEN", "S/.", "Peruvian Nuevo Sol", 2],
|
|
127
|
+
["PHP", "₱", "Philippine Peso", 2],
|
|
128
|
+
["PKR", "PKRs", "Pakistani Rupee", 0],
|
|
129
|
+
["PLN", "zł", "Polish Zloty", 2],
|
|
130
|
+
["PYG", "₲", "Paraguayan Guarani", 0],
|
|
131
|
+
["QAR", "QR", "Qatari Rial", 2],
|
|
132
|
+
["RON", "RON", "Romanian Leu", 2],
|
|
133
|
+
["RSD", "din.", "Serbian Dinar", 0],
|
|
134
|
+
["RUB", "₽", "Russian Ruble", 2],
|
|
135
|
+
["RWF", "RWF", "Rwandan Franc", 0],
|
|
136
|
+
["SAR", "SR", "Saudi Riyal", 2],
|
|
137
|
+
["SDG", "SDG", "Sudanese Pound", 2],
|
|
138
|
+
["SEK", "Skr", "Swedish Krona", 2],
|
|
139
|
+
["SGD", "S$", "Singapore Dollar", 2],
|
|
140
|
+
["SOS", "Ssh", "Somali Shilling", 0],
|
|
141
|
+
["SYP", "SY£", "Syrian Pound", 0],
|
|
142
|
+
["THB", "฿", "Thai Baht", 2],
|
|
143
|
+
["TND", "DT", "Tunisian Dinar", 3],
|
|
144
|
+
["TOP", "T$", "Tongan Paʻanga", 2],
|
|
145
|
+
["TRY", "₺", "Turkish Lira", 2],
|
|
146
|
+
["TTD", "TT$", "Trinidad and Tobago Dollar", 2],
|
|
147
|
+
["TWD", "NT$", "New Taiwan Dollar", 2],
|
|
148
|
+
["TZS", "TSh", "Tanzanian Shilling", 0],
|
|
149
|
+
["UAH", "₴", "Ukrainian Hryvnia", 2],
|
|
150
|
+
["UGX", "USh", "Ugandan Shilling", 0],
|
|
151
|
+
["UYU", "$U", "Uruguayan Peso", 2],
|
|
152
|
+
["UZS", "UZS", "Uzbekistan Som", 0],
|
|
153
|
+
["VEF", "Bs.F.", "Venezuelan Bolívar", 2],
|
|
154
|
+
["VND", "₫", "Vietnamese Dong", 0],
|
|
155
|
+
["XAF", "FCFA", "CFA Franc BEAC", 0],
|
|
156
|
+
["XOF", "CFA", "CFA Franc BCEAO", 0],
|
|
157
|
+
["YER", "﷼", "Yemeni Rial", 0],
|
|
158
|
+
["ZAR", "R", "South African Rand", 2],
|
|
159
|
+
["ZMK", "ZK", "Zambian Kwacha", 0],
|
|
160
|
+
["ZWL", "ZWL$", "Zimbabwean Dollar", 0],
|
|
161
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export class Blockchain {
|
|
2
|
+
/**
|
|
3
|
+
* @param name {string} latin printable name of blockchain
|
|
4
|
+
* @param supportedProtocols {Protocol[]}
|
|
5
|
+
*/
|
|
6
|
+
constructor(name, supportedProtocols = []) {
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.supportedProtocols = supportedProtocols;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { improveAndRethrow } from "./../errorUtils.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The model for cryptocurrency coins.
|
|
5
|
+
*
|
|
6
|
+
* WARNING: this class should not be instantiated directly. Use only predefined singleton Coin (or descendants) instances.
|
|
7
|
+
*/
|
|
8
|
+
export class Coin {
|
|
9
|
+
/**
|
|
10
|
+
* Creates new coin
|
|
11
|
+
*
|
|
12
|
+
* @param latinName {string} the coin name in latin symbols like "Bitcoin"
|
|
13
|
+
* @param ticker {string} the coin symbol/ticker/code like 'BTC'. Always upper case. A unique coin identifier
|
|
14
|
+
* @param tickerPrintable {string} ticker but in printable format. Useful for tokens based on external blockchains
|
|
15
|
+
* like ERC20 or TRC20. It is not friendly to display USDTERC20 or BUSDTRC20 - more neat options are just
|
|
16
|
+
* USDT and BUSD. Note that you should always care about user's understanding of what coin he/she is working
|
|
17
|
+
* with as printable ticker for USDTERC20 and USDTTRC20 are the same.
|
|
18
|
+
* @param digitsCountAfterComma {number} count of digits after the comma. E.g. 8 for bitcoin
|
|
19
|
+
* @param maxValue {number|null} max possible value for cryptocurrency. Null means that the currency has no max possible value
|
|
20
|
+
* @param atomName {string} name of the coin's atomic value. Like 'satoshi' for bitcoin
|
|
21
|
+
* @param mainnet {Network} main network for this coin
|
|
22
|
+
* @param testnet {Network} test network for this coin
|
|
23
|
+
* @param minConfirmations {number} min confirmations count to treat the coin's transaction confirmed
|
|
24
|
+
* @param payableEntityStringForFeeRate {string|null} the payable fee entity like byte for bitcoin or gas for ether if present
|
|
25
|
+
* @param feeOptionsTimeStringsSortedDesc {string[]} array of 4 strings for fee options when sending coins. Should be sorted from the highest time to the smallest
|
|
26
|
+
* @param feeRatesExpirationTimeMs {number} number of milliseconds to treat the fee rates as expired
|
|
27
|
+
* @param blockchain {Blockchain} blockchain object
|
|
28
|
+
* @param [protocol] {Protocol|null} token/coin protocol if relevant
|
|
29
|
+
* @param [tokenAddress] {string|null} address of contract of this token (if the coin is token)
|
|
30
|
+
* @param [doesUseLowerCaseAddresses] {boolean} flag to clarify whether we can use lower case addresses to ensure more robust comparisons
|
|
31
|
+
* @param [doesUseOutputs=false] {boolean} true if this coin uses inputs/outputs concept and false if it uses just balances
|
|
32
|
+
*/
|
|
33
|
+
constructor(
|
|
34
|
+
latinName,
|
|
35
|
+
ticker,
|
|
36
|
+
tickerPrintable,
|
|
37
|
+
digitsCountAfterComma,
|
|
38
|
+
maxValue,
|
|
39
|
+
atomName,
|
|
40
|
+
mainnet,
|
|
41
|
+
testnet,
|
|
42
|
+
minConfirmations,
|
|
43
|
+
payableEntityStringForFeeRate,
|
|
44
|
+
feeOptionsTimeStringsSortedDesc,
|
|
45
|
+
feeRatesExpirationTimeMs,
|
|
46
|
+
blockchain,
|
|
47
|
+
protocol = null,
|
|
48
|
+
tokenAddress = null,
|
|
49
|
+
doesUseLowerCaseAddresses = true,
|
|
50
|
+
doesUseOutputs = false
|
|
51
|
+
) {
|
|
52
|
+
this.latinName = latinName;
|
|
53
|
+
this.ticker = ticker;
|
|
54
|
+
this.tickerPrintable = tickerPrintable;
|
|
55
|
+
this.digits = digitsCountAfterComma;
|
|
56
|
+
this.maxValue = maxValue;
|
|
57
|
+
this.atomName = atomName;
|
|
58
|
+
this.mainnet = mainnet;
|
|
59
|
+
this.testnet = testnet;
|
|
60
|
+
this.minConfirmations = minConfirmations;
|
|
61
|
+
this.payableEntityStringForFeeRate = payableEntityStringForFeeRate;
|
|
62
|
+
this.feeOptionsTimeStringsSortedDesc = feeOptionsTimeStringsSortedDesc;
|
|
63
|
+
this.feeRatesExpirationTimeMs = feeRatesExpirationTimeMs;
|
|
64
|
+
this.protocol = protocol;
|
|
65
|
+
this.blockchain = blockchain;
|
|
66
|
+
// TODO: [bug, critical] use testnet property for testnet contract address as it blocks the app work in testnets
|
|
67
|
+
this.tokenAddress = tokenAddress;
|
|
68
|
+
this.feeCoin = this;
|
|
69
|
+
this._significantDigits = 8;
|
|
70
|
+
this.doesUseLowerCaseAddresses = doesUseLowerCaseAddresses;
|
|
71
|
+
this.doesUseOutputs = doesUseOutputs;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sets fee coin
|
|
76
|
+
*
|
|
77
|
+
* @param feeCoin {Coin} some tokens use another coin to charge transaction fee as they work on top of some external
|
|
78
|
+
* blockchain. So pass here the coin the token uses for fee charging. Like for ERC20 token the fee coin is ETH.
|
|
79
|
+
* By default, the creating coin will be set as a value for this field.
|
|
80
|
+
*/
|
|
81
|
+
setFeeCoin(feeCoin) {
|
|
82
|
+
this.feeCoin = feeCoin;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Checks whether this coin uses another coin (blockchain) to charge fee for transactions (means works on base of
|
|
87
|
+
* some external blockchain).
|
|
88
|
+
*
|
|
89
|
+
* @return {boolean} true if this coin uses external blockchain to perform transactions and charge fee
|
|
90
|
+
*/
|
|
91
|
+
doesUseDifferentCoinFee() {
|
|
92
|
+
return this.feeCoin !== this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Converts the given atoms string/number to string representing the same amount in coin itself - floating point number
|
|
97
|
+
*
|
|
98
|
+
* @param atoms {string} atoms positive integer amount
|
|
99
|
+
* @return {string} coin amount floating point number as a string
|
|
100
|
+
*/
|
|
101
|
+
atomsToCoinAmount(atoms) {
|
|
102
|
+
throw new Error("Not implemented in base Coin");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Converts the given coins amount string/number to string representing the same amount in coin atoms - integer number
|
|
107
|
+
*
|
|
108
|
+
* @param coinsAmount {string} coins positive floating point amount
|
|
109
|
+
* @return {string} coin atoms amount integer number as a string
|
|
110
|
+
*/
|
|
111
|
+
coinAmountToAtoms(coinsAmount) {
|
|
112
|
+
throw new Error("Not implemented in base Coin");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Composes URL to view the tx with given id in the external blockchain explorer
|
|
117
|
+
*
|
|
118
|
+
* @param txId {string} id of transaction
|
|
119
|
+
* @return {string} URL string
|
|
120
|
+
*/
|
|
121
|
+
composeUrlToTransactionExplorer(txId) {
|
|
122
|
+
throw new Error("Not implemented in base Coin");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Most of the cryptocurrencies has specific fee rate or fee price metric. This value usually has specific measure
|
|
127
|
+
* like satoshi/byte or gWei/gas. This function adds the described denomination string to the given amount
|
|
128
|
+
* as a suffix and returns the result string ready to be show to a user.
|
|
129
|
+
*
|
|
130
|
+
* @param coinAtomsString {string} coin atoms positive integer amount
|
|
131
|
+
* @return {string} string of coin amount and fee rate units
|
|
132
|
+
*/
|
|
133
|
+
coinAtomsFeeRateToCommonlyUsedAmountFormatWithDenominationString(
|
|
134
|
+
coinAtomsString
|
|
135
|
+
) {
|
|
136
|
+
throw new Error("Not implemented in base Coin");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check whether this coin support transaction prioritisation during the sending process.
|
|
141
|
+
*
|
|
142
|
+
* @return {boolean} true if support transaction prioritisation and false otherwise
|
|
143
|
+
*/
|
|
144
|
+
doesSupportTransactionPrioritisation() {
|
|
145
|
+
return Array.isArray(this.feeOptionsTimeStringsSortedDesc);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
tickerAndProtocol() {
|
|
149
|
+
try {
|
|
150
|
+
return `${this.tickerPrintable}${
|
|
151
|
+
this.protocol ? " " + this.protocol.protocol ?? "" : ""
|
|
152
|
+
}`;
|
|
153
|
+
} catch (e) {
|
|
154
|
+
improveAndRethrow(e, "tickerAndProtocol");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { improveAndRethrow } from "../errorUtils.js";
|
|
2
|
+
import { Logger } from "./logging/logger.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TODO: [tests, critical] Ued by payments logic
|
|
6
|
+
*
|
|
7
|
+
* Simple cache based on Map.
|
|
8
|
+
* Provides ability to store event-dependent data.
|
|
9
|
+
*/
|
|
10
|
+
export class Cache {
|
|
11
|
+
/**
|
|
12
|
+
* @param eventBus {EventBus} EventBus.js lib instance
|
|
13
|
+
* @param [noSessionEvents=[]] {string[]} array of events that will be treated as "no session"
|
|
14
|
+
*/
|
|
15
|
+
constructor(eventBus, noSessionEvents = []) {
|
|
16
|
+
this._cache = new Map();
|
|
17
|
+
this._eventDependentDataKeys = [];
|
|
18
|
+
this._noSessionEvents = noSessionEvents;
|
|
19
|
+
this._eventBus = eventBus;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
_setupIntervalClearingExpired() {
|
|
23
|
+
let cleaner = function () {
|
|
24
|
+
try {
|
|
25
|
+
for (const key of this._cache.keys()) {
|
|
26
|
+
const item = this._cache.get(key);
|
|
27
|
+
if (
|
|
28
|
+
item &&
|
|
29
|
+
item.ttlMs &&
|
|
30
|
+
item.addedMsTimestamp + item.ttlMs < Date.now()
|
|
31
|
+
) {
|
|
32
|
+
this._cache.delete(key);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
improveAndRethrow(e, "_intervalClearingExpiredCache");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
cleaner = cleaner.bind(this);
|
|
41
|
+
|
|
42
|
+
setInterval(cleaner, 1000);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Puts data to cache
|
|
47
|
+
*
|
|
48
|
+
* @param key {string} string key for this data
|
|
49
|
+
* @param data {any} any data
|
|
50
|
+
* @param ttlMs {number|null} optional milliseconds number for cache lifetime
|
|
51
|
+
* @throws {Error} when the data is null/undefined because these values for data are reserved for internal logic
|
|
52
|
+
*/
|
|
53
|
+
put(key, data, ttlMs = null) {
|
|
54
|
+
try {
|
|
55
|
+
if (typeof key !== "string" || data == null) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Trying to cache corrupted data: ${key}, ${data}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
this._cache.set(key, {
|
|
61
|
+
data: data,
|
|
62
|
+
addedMsTimestamp: Date.now(),
|
|
63
|
+
ttlMs: ttlMs,
|
|
64
|
+
});
|
|
65
|
+
} catch (e) {
|
|
66
|
+
improveAndRethrow(e, "cache.put");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
putSessionDependentData(key, data, ttlMs = null) {
|
|
71
|
+
this._putEventDependentData(key, data, this._noSessionEvents, ttlMs);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Puts data to cache and adds its key to list of keys that should be related by each of given events.
|
|
76
|
+
*
|
|
77
|
+
* @param key {string} key for cache
|
|
78
|
+
* @param data {any} any caching data
|
|
79
|
+
* @param events {string[]} list of events forcing putting data to be removed when triggered
|
|
80
|
+
* @param ttlMs {|null} optional time to live for this cache item
|
|
81
|
+
* @throws {Error} when the data is null/undefined because these values for data are reserved for internal logic
|
|
82
|
+
*/
|
|
83
|
+
putEventDependentData(key, data, events, ttlMs = null) {
|
|
84
|
+
this._putEventDependentData(key, data, events, ttlMs);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_putEventDependentData(key, data, events, ttlMs = null) {
|
|
88
|
+
try {
|
|
89
|
+
if (typeof key !== "string" || data == null) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Trying to cache corrupted data: ${key}, ${data}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
this._cache.set(key, {
|
|
95
|
+
data: data,
|
|
96
|
+
addedMsTimestamp: Date.now(),
|
|
97
|
+
ttlMs: ttlMs,
|
|
98
|
+
});
|
|
99
|
+
for (let event of events) {
|
|
100
|
+
const eventAndKeys = this._eventDependentDataKeys.find(
|
|
101
|
+
(item) => item[0] === event
|
|
102
|
+
);
|
|
103
|
+
if (eventAndKeys) {
|
|
104
|
+
eventAndKeys.push(key);
|
|
105
|
+
} else {
|
|
106
|
+
this._eventDependentDataKeys.push([event, key]);
|
|
107
|
+
this._eventBus.addEventListener(event, () => {
|
|
108
|
+
try {
|
|
109
|
+
const keys = this._eventDependentDataKeys.find(
|
|
110
|
+
(item) => item[0] === event
|
|
111
|
+
);
|
|
112
|
+
(keys ?? [event])
|
|
113
|
+
.slice(1)
|
|
114
|
+
.forEach((key) => this._cache.delete(key));
|
|
115
|
+
} catch (e) {
|
|
116
|
+
Logger.logError(
|
|
117
|
+
e,
|
|
118
|
+
"cache.removing-for-event",
|
|
119
|
+
`Event: ${event}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
improveAndRethrow(e, "cache.putEventDependentData");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// TODO: [feature, low] add clearing of expired data by schedule
|
|
131
|
+
get(key) {
|
|
132
|
+
try {
|
|
133
|
+
const item = this._cache.get(key);
|
|
134
|
+
if (item) {
|
|
135
|
+
if (
|
|
136
|
+
item.addedMsTimestamp &&
|
|
137
|
+
item.ttlMs !== null &&
|
|
138
|
+
item.addedMsTimestamp + item.ttlMs < Date.now()
|
|
139
|
+
) {
|
|
140
|
+
this._cache.delete(key);
|
|
141
|
+
return null;
|
|
142
|
+
} else {
|
|
143
|
+
return item.data;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return null;
|
|
148
|
+
} catch (e) {
|
|
149
|
+
improveAndRethrow(e, "cache.get");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getLastUpdateTimestamp(key) {
|
|
154
|
+
return this._cache.get(key)?.addedMsTimestamp ?? null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Updates the timestamp of the last update for specified key to the provided value.
|
|
159
|
+
* Can be useful when TTL is controlled outside this class.
|
|
160
|
+
*
|
|
161
|
+
* @param key {string}
|
|
162
|
+
* @param timestamp {number}
|
|
163
|
+
* @return {boolean}
|
|
164
|
+
*/
|
|
165
|
+
setLastUpdateTimestamp(key, timestamp) {
|
|
166
|
+
try {
|
|
167
|
+
const item = this._cache.get(key);
|
|
168
|
+
if (item != null && typeof timestamp === "number") {
|
|
169
|
+
this._cache.set(key, { ...item, addedTimestampMs: timestamp });
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
} catch (e) {
|
|
174
|
+
improveAndRethrow("cache.setLastUpdateTimestamp");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
invalidate(key) {
|
|
179
|
+
try {
|
|
180
|
+
this._cache.delete(key);
|
|
181
|
+
} catch (e) {
|
|
182
|
+
improveAndRethrow(e, "cache.invalidate");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
invalidateContaining(keyPart) {
|
|
187
|
+
if (typeof keyPart !== "string" || keyPart === "") {
|
|
188
|
+
throw new Error(
|
|
189
|
+
"Trying to invalidate containing wrong key or empty key: " +
|
|
190
|
+
keyPart
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const matchedKeys = Array.from(this._cache.keys()).filter(
|
|
196
|
+
(key) =>
|
|
197
|
+
typeof key === "string" && new RegExp(keyPart).test(key)
|
|
198
|
+
);
|
|
199
|
+
for (let i = 0; i < matchedKeys.length; ++i) {
|
|
200
|
+
this._cache.delete(matchedKeys[i]);
|
|
201
|
+
}
|
|
202
|
+
} catch (e) {
|
|
203
|
+
improveAndRethrow(e, "invalidateContaining");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
clear() {
|
|
208
|
+
this._cache.clear();
|
|
209
|
+
this._sessionDependentDataKeys = [];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Saves given data string to persistent cache.
|
|
214
|
+
* NOTE: we have no TTL here, implement if needed.
|
|
215
|
+
*
|
|
216
|
+
* WARNING: use only when really needed and don't store big data as we use localStorage
|
|
217
|
+
* under the hood and its capacity is restricted.
|
|
218
|
+
*
|
|
219
|
+
* @param uniqueKey {string} the key should be unique
|
|
220
|
+
* @param data {string} only string data allowed
|
|
221
|
+
*/
|
|
222
|
+
putClientPersistentData(uniqueKey, data) {
|
|
223
|
+
try {
|
|
224
|
+
if (typeof window !== "undefined") {
|
|
225
|
+
localStorage.setItem(uniqueKey, data);
|
|
226
|
+
}
|
|
227
|
+
} catch (e) {
|
|
228
|
+
improveAndRethrow(e, "cache.putClientPersistentData");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @param uniqueKey {string}
|
|
234
|
+
* @return {string|null}
|
|
235
|
+
*/
|
|
236
|
+
getClientPersistentData(uniqueKey) {
|
|
237
|
+
try {
|
|
238
|
+
if (typeof window !== "undefined") {
|
|
239
|
+
return localStorage.getItem(uniqueKey);
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
} catch (e) {
|
|
243
|
+
improveAndRethrow(e, "cache.getClientPersistentData");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Only makes effect if the TTL is not null.
|
|
249
|
+
*
|
|
250
|
+
* @param key {string}
|
|
251
|
+
* @param ttlMs {number|null}
|
|
252
|
+
*/
|
|
253
|
+
markCacheItemAsExpiredButDontRemove(key, ttlMs = null) {
|
|
254
|
+
try {
|
|
255
|
+
const item = this._cache.get(key);
|
|
256
|
+
const ttlFinalMs = ttlMs ?? item?.ttlMs;
|
|
257
|
+
if (item != null && ttlFinalMs) {
|
|
258
|
+
this._cache.set(key, {
|
|
259
|
+
data: item.data,
|
|
260
|
+
addedMsTimestamp: Date.now() - ttlFinalMs - 1,
|
|
261
|
+
ttlMs: ttlFinalMs,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
} catch (e) {
|
|
265
|
+
improveAndRethrow(e, "cache.markCacheItemAsExpiredButDontRemove");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
import { improveAndRethrow } from "../errorUtils.js";
|
|
4
|
+
|
|
5
|
+
export class EmailsApi {
|
|
6
|
+
static serverEndpointEntity = "emails";
|
|
7
|
+
|
|
8
|
+
static async sendEmail(subject, body) {
|
|
9
|
+
try {
|
|
10
|
+
const url = `${
|
|
11
|
+
window.location.protocol + "//" + window.location.host
|
|
12
|
+
}/api/v1/${this.serverEndpointEntity}`;
|
|
13
|
+
await axios.post(url, { subject, body });
|
|
14
|
+
} catch (e) {
|
|
15
|
+
improveAndRethrow(e, "sendEmail", subject + body);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { LogsStorage } from "./logsStorage.js";
|
|
2
|
+
import { safeStringify } from "../safeStringify.js";
|
|
3
|
+
|
|
4
|
+
export class Logger {
|
|
5
|
+
/**
|
|
6
|
+
* Logs to client logs storage.
|
|
7
|
+
*
|
|
8
|
+
* WARNING! this method should ce used carefully for critical logging as we have the restriction for storing logs
|
|
9
|
+
* on client side as we store them inside the local storage. Please see details inside storage.js
|
|
10
|
+
* @param logString {string} log string
|
|
11
|
+
* @param source {string} source of the log entry
|
|
12
|
+
*/
|
|
13
|
+
static log(logString, source) {
|
|
14
|
+
const timestamp = new Date().toISOString();
|
|
15
|
+
LogsStorage.saveLog(`${timestamp}|${source}:${logString}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static logError(
|
|
19
|
+
e,
|
|
20
|
+
settingFunction,
|
|
21
|
+
additionalMessage = "",
|
|
22
|
+
onlyToConsole = false
|
|
23
|
+
) {
|
|
24
|
+
let message = `\nFunction call ${
|
|
25
|
+
settingFunction ?? ""
|
|
26
|
+
} failed. Error message: ${e?.message}. ${additionalMessage} `;
|
|
27
|
+
message +=
|
|
28
|
+
`${e?.errorDescription ?? ""}${e?.howToFix ?? ""}` +
|
|
29
|
+
(e?.httpStatus === 403
|
|
30
|
+
? "Authentication has expired or was lost. "
|
|
31
|
+
: "");
|
|
32
|
+
|
|
33
|
+
if (e?.response) {
|
|
34
|
+
try {
|
|
35
|
+
const responseData = safeStringify({ response: e.response });
|
|
36
|
+
responseData && (message += `\n${responseData}. `);
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const finalErrorText = message + ". " + safeStringify(e);
|
|
41
|
+
// eslint-disable-next-line no-console
|
|
42
|
+
console.error(finalErrorText, e);
|
|
43
|
+
|
|
44
|
+
if (!onlyToConsole) {
|
|
45
|
+
this.log(finalErrorText, "logError");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|