@subwallet/extension-base 1.1.58-0 → 1.1.60-0
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/background/KoniTypes.d.ts +18 -1
- package/cjs/constants/staking.js +2 -1
- package/cjs/constants/storage.js +4 -2
- package/cjs/koni/api/coingecko.js +58 -15
- package/cjs/koni/api/nft/config.js +27 -21
- package/cjs/koni/api/staking/bonding/utils.js +7 -3
- package/cjs/koni/background/handlers/Extension.js +130 -122
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +7 -1
- package/cjs/services/chain-service/constants.js +4 -2
- package/cjs/services/chain-service/index.js +6 -1
- package/cjs/services/chain-service/utils/index.js +34 -14
- package/cjs/services/earning-service/constants/chains.js +2 -2
- package/cjs/services/earning-service/handlers/native-staking/relay-chain.js +4 -4
- package/cjs/services/earning-service/handlers/nomination-pool/index.js +2 -2
- package/cjs/services/price-service/coingecko.js +54 -35
- package/cjs/services/price-service/index.js +83 -12
- package/cjs/services/setting-service/constants.js +4 -1
- package/cjs/services/storage-service/DatabaseService.js +2 -3
- package/cjs/utils/number.js +84 -1
- package/cjs/utils/staticData/index.js +6 -1
- package/constants/staking.js +2 -1
- package/constants/storage.d.ts +1 -0
- package/constants/storage.js +2 -1
- package/koni/api/coingecko.d.ts +2 -2
- package/koni/api/coingecko.js +58 -15
- package/koni/api/nft/config.js +28 -20
- package/koni/api/staking/bonding/utils.d.ts +1 -1
- package/koni/api/staking/bonding/utils.js +7 -3
- package/koni/background/handlers/Extension.d.ts +1 -0
- package/koni/background/handlers/Extension.js +7 -0
- package/package.json +7 -6
- package/packageInfo.js +1 -1
- package/services/balance-service/helpers/subscribe/substrate/index.js +8 -2
- package/services/chain-service/constants.js +4 -2
- package/services/chain-service/index.js +6 -1
- package/services/chain-service/utils/index.d.ts +13 -0
- package/services/chain-service/utils/index.js +32 -14
- package/services/earning-service/constants/chains.js +2 -2
- package/services/earning-service/handlers/native-staking/relay-chain.js +4 -4
- package/services/earning-service/handlers/nomination-pool/index.js +2 -2
- package/services/price-service/coingecko.d.ts +3 -2
- package/services/price-service/coingecko.js +50 -32
- package/services/price-service/index.d.ts +7 -2
- package/services/price-service/index.js +85 -15
- package/services/setting-service/constants.d.ts +1 -0
- package/services/setting-service/constants.js +2 -0
- package/services/storage-service/DatabaseService.d.ts +1 -1
- package/services/storage-service/DatabaseService.js +2 -3
- package/utils/number.d.ts +1 -0
- package/utils/number.js +82 -0
- package/utils/staticData/currencySymbol.json +11 -0
- package/utils/staticData/index.d.ts +3 -0
- package/utils/staticData/index.js +4 -0
package/constants/storage.js
CHANGED
package/koni/api/coingecko.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { PriceJson } from '@subwallet/extension-base/background/KoniTypes';
|
|
2
|
-
export declare const getTokenPrice: (priceIds:
|
|
1
|
+
import { CurrencyType, PriceJson } from '@subwallet/extension-base/background/KoniTypes';
|
|
2
|
+
export declare const getTokenPrice: (priceIds: Set<string>, currencyCode?: CurrencyType) => Promise<PriceJson>;
|
package/koni/api/coingecko.js
CHANGED
|
@@ -1,29 +1,72 @@
|
|
|
1
1
|
// Copyright 2019-2022 @subwallet/extension-koni authors & contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
+
import { staticData, StaticKey } from '@subwallet/extension-base/utils/staticData';
|
|
4
5
|
import axios from 'axios';
|
|
5
|
-
|
|
6
|
+
let useBackupApi = false;
|
|
7
|
+
export const getTokenPrice = async (priceIds, currencyCode = 'USD') => {
|
|
6
8
|
try {
|
|
7
|
-
|
|
8
|
-
const idStr = priceIds.join(',');
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
var _resMultiPromise$, _resMultiPromise$2;
|
|
10
|
+
const idStr = Array.from(priceIds).join(',');
|
|
11
|
+
const getPriceMap = async () => {
|
|
12
|
+
var _rs, _rs2;
|
|
13
|
+
let rs;
|
|
14
|
+
if (!useBackupApi) {
|
|
15
|
+
try {
|
|
16
|
+
rs = await axios.get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=${currencyCode.toLowerCase()}&per_page=250&ids=${idStr}`);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
useBackupApi = true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (useBackupApi || ((_rs = rs) === null || _rs === void 0 ? void 0 : _rs.status) !== 200) {
|
|
22
|
+
useBackupApi = true;
|
|
23
|
+
rs = await axios.get(`https://chain-data.subwallet.app/api/price/get?ids=${idStr}`);
|
|
24
|
+
}
|
|
25
|
+
if (((_rs2 = rs) === null || _rs2 === void 0 ? void 0 : _rs2.status) !== 200) {
|
|
26
|
+
console.warn('Failed to get token price');
|
|
27
|
+
}
|
|
28
|
+
return rs;
|
|
29
|
+
};
|
|
30
|
+
const getExchangeRate = async () => {
|
|
31
|
+
var _rs3;
|
|
32
|
+
let rs;
|
|
33
|
+
try {
|
|
34
|
+
rs = await axios.get('https://api-cache.subwallet.app/exchange-rate');
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.warn('Failed to get exchange rate');
|
|
37
|
+
}
|
|
38
|
+
if (((_rs3 = rs) === null || _rs3 === void 0 ? void 0 : _rs3.status) !== 200) {
|
|
39
|
+
console.warn('Failed to get exchange rate');
|
|
40
|
+
}
|
|
41
|
+
return rs;
|
|
42
|
+
};
|
|
43
|
+
const resMultiPromise = await Promise.all([getPriceMap(), getExchangeRate()]);
|
|
44
|
+
const responseDataPrice = ((_resMultiPromise$ = resMultiPromise[0]) === null || _resMultiPromise$ === void 0 ? void 0 : _resMultiPromise$.data) || [];
|
|
45
|
+
const responseDataExchangeRate = ((_resMultiPromise$2 = resMultiPromise[1]) === null || _resMultiPromise$2 === void 0 ? void 0 : _resMultiPromise$2.data) || {};
|
|
11
46
|
const priceMap = {};
|
|
12
47
|
const price24hMap = {};
|
|
13
|
-
|
|
48
|
+
const exchangeRateMap = Object.keys(responseDataExchangeRate.conversion_rates).reduce((map, exchangeKey) => {
|
|
49
|
+
if (!staticData[StaticKey.CURRENCY_SYMBOL][exchangeKey]) {
|
|
50
|
+
return map;
|
|
51
|
+
}
|
|
52
|
+
map[exchangeKey] = {
|
|
53
|
+
exchange: responseDataExchangeRate.conversion_rates[exchangeKey],
|
|
54
|
+
label: staticData[StaticKey.CURRENCY_SYMBOL][exchangeKey].label
|
|
55
|
+
};
|
|
56
|
+
return map;
|
|
57
|
+
}, {});
|
|
58
|
+
const currencyData = staticData[StaticKey.CURRENCY_SYMBOL][currencyCode];
|
|
59
|
+
responseDataPrice.forEach(val => {
|
|
14
60
|
const currentPrice = val.current_price || 0;
|
|
15
61
|
const price24h = currentPrice - (val.price_change_24h || 0);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// if (inverseMap[val.id]) {
|
|
20
|
-
// priceMap[inverseMap[val.id]] = currentPrice;
|
|
21
|
-
// price24hMap[inverseMap[val.id]] = price24h;
|
|
22
|
-
// }
|
|
62
|
+
const exchangeRate = exchangeRateMap[currencyCode] || 1;
|
|
63
|
+
priceMap[val.id] = currentPrice * exchangeRate.exchange;
|
|
64
|
+
price24hMap[val.id] = price24h * exchangeRate.exchange;
|
|
23
65
|
});
|
|
24
|
-
|
|
25
66
|
return {
|
|
26
|
-
currency,
|
|
67
|
+
currency: currencyCode,
|
|
68
|
+
currencyData,
|
|
69
|
+
exchangeRateMap,
|
|
27
70
|
priceMap,
|
|
28
71
|
price24hMap
|
|
29
72
|
};
|
package/koni/api/nft/config.js
CHANGED
|
@@ -88,6 +88,8 @@ export const TRANSFER_CHAIN_ID = {
|
|
|
88
88
|
[SUPPORTED_TRANSFER_EVM_CHAIN_NAME.shibuya]: 81
|
|
89
89
|
};
|
|
90
90
|
export let SUPPORTED_TRANSFER_SUBSTRATE_CHAIN_NAME;
|
|
91
|
+
|
|
92
|
+
// This is for localhost or http only
|
|
91
93
|
(function (SUPPORTED_TRANSFER_SUBSTRATE_CHAIN_NAME) {
|
|
92
94
|
SUPPORTED_TRANSFER_SUBSTRATE_CHAIN_NAME["statemine"] = "statemine";
|
|
93
95
|
SUPPORTED_TRANSFER_SUBSTRATE_CHAIN_NAME["acala"] = "acala";
|
|
@@ -110,33 +112,39 @@ if (isFirefox) {
|
|
|
110
112
|
weight: 5000
|
|
111
113
|
});
|
|
112
114
|
}
|
|
113
|
-
if (
|
|
115
|
+
if (RuntimeInfo.protocol && RuntimeInfo.protocol.startsWith('http')) {
|
|
116
|
+
// This is for https
|
|
117
|
+
if (RuntimeInfo.protocol.startsWith('https')) {
|
|
118
|
+
RANDOM_IPFS_GATEWAY_SETTING.push({
|
|
119
|
+
provider: IPFS_FLEEK,
|
|
120
|
+
weight: 4
|
|
121
|
+
}, {
|
|
122
|
+
provider: IPFS_GATEWAY_4EVERLAND,
|
|
123
|
+
weight: 2
|
|
124
|
+
}, {
|
|
125
|
+
provider: IPFS_W3S_LINK,
|
|
126
|
+
weight: 1
|
|
127
|
+
}, {
|
|
128
|
+
provider: CF_IPFS_GATEWAY,
|
|
129
|
+
weight: 4
|
|
130
|
+
}, {
|
|
131
|
+
provider: PINATA_IPFS_GATEWAY,
|
|
132
|
+
weight: 1 // Rate limit too low
|
|
133
|
+
}, {
|
|
134
|
+
provider: IPFS_IO,
|
|
135
|
+
weight: 5
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
// This is for extension env or other
|
|
114
140
|
RANDOM_IPFS_GATEWAY_SETTING.push({
|
|
115
|
-
provider: IPFS_FLEEK,
|
|
116
|
-
weight: 4
|
|
117
|
-
}, {
|
|
118
|
-
provider: IPFS_GATEWAY_4EVERLAND,
|
|
119
|
-
weight: 2
|
|
120
|
-
}, {
|
|
121
|
-
provider: IPFS_W3S_LINK,
|
|
122
|
-
weight: 1
|
|
123
|
-
}, {
|
|
124
|
-
provider: CF_IPFS_GATEWAY,
|
|
125
|
-
weight: 4
|
|
126
|
-
}, {
|
|
127
|
-
provider: PINATA_IPFS_GATEWAY,
|
|
128
|
-
weight: 1 // Rate limit too low
|
|
129
|
-
}, {
|
|
130
141
|
provider: NFT_STORAGE_GATEWAY,
|
|
131
142
|
weight: 50
|
|
132
|
-
}, {
|
|
133
|
-
provider: GATEWAY_IPFS_IO,
|
|
134
|
-
weight: 5
|
|
135
143
|
}, {
|
|
136
144
|
provider: DWEB_LINK,
|
|
137
145
|
weight: 5
|
|
138
146
|
}, {
|
|
139
|
-
provider:
|
|
147
|
+
provider: GATEWAY_IPFS_IO,
|
|
140
148
|
weight: 5
|
|
141
149
|
});
|
|
142
150
|
}
|
|
@@ -157,7 +157,7 @@ export declare function getWithdrawalInfo(nominatorMetadata: NominatorMetadata):
|
|
|
157
157
|
export declare function getEarningStatusByNominations(bnTotalActiveStake: BN, nominationList: NominationInfo[]): EarningStatus;
|
|
158
158
|
export declare function getValidatorLabel(chain: string): "dApp" | "Validator" | "Collator";
|
|
159
159
|
export declare function getAvgValidatorEraReward(supportedDays: number, eraRewardHistory: Codec[]): BigNumber;
|
|
160
|
-
export declare function getSupportedDaysByHistoryDepth(erasPerDay: number, maxSupportedEras: number):
|
|
160
|
+
export declare function getSupportedDaysByHistoryDepth(erasPerDay: number, maxSupportedEras: number, liveDay?: number): number;
|
|
161
161
|
export declare function getValidatorPointsMap(eraRewardMap: Record<string, PalletStakingEraRewardPoints>): Record<string, BigNumber>;
|
|
162
162
|
export declare function getTopValidatorByPoints(validatorPointsList: Record<string, BigNumber>): string[];
|
|
163
163
|
export declare const getMinStakeErrorMessage: (chainInfo: _ChainInfo, bnMinStake: BN) => string;
|
|
@@ -343,11 +343,15 @@ export function getAvgValidatorEraReward(supportedDays, eraRewardHistory) {
|
|
|
343
343
|
}
|
|
344
344
|
return sumEraReward.dividedBy(new BigNumber(supportedDays - failEra));
|
|
345
345
|
}
|
|
346
|
-
export function getSupportedDaysByHistoryDepth(erasPerDay, maxSupportedEras) {
|
|
347
|
-
|
|
346
|
+
export function getSupportedDaysByHistoryDepth(erasPerDay, maxSupportedEras, liveDay) {
|
|
347
|
+
const maxSupportDay = maxSupportedEras / erasPerDay;
|
|
348
|
+
if (liveDay && liveDay <= 30) {
|
|
349
|
+
return Math.min(liveDay - 1, maxSupportDay);
|
|
350
|
+
}
|
|
351
|
+
if (maxSupportDay > 30) {
|
|
348
352
|
return 30;
|
|
349
353
|
} else {
|
|
350
|
-
return
|
|
354
|
+
return maxSupportDay;
|
|
351
355
|
}
|
|
352
356
|
}
|
|
353
357
|
export function getValidatorPointsMap(eraRewardMap) {
|
|
@@ -1024,6 +1024,11 @@ export default class KoniExtension {
|
|
|
1024
1024
|
async getPrice() {
|
|
1025
1025
|
return this.#koniState.priceService.getPrice();
|
|
1026
1026
|
}
|
|
1027
|
+
async setPriceCurrency({
|
|
1028
|
+
currency
|
|
1029
|
+
}) {
|
|
1030
|
+
return await this.#koniState.priceService.setPriceCurrency(currency);
|
|
1031
|
+
}
|
|
1027
1032
|
subscribePrice(id, port) {
|
|
1028
1033
|
const cb = createSubscription(id, port);
|
|
1029
1034
|
const priceSubscription = this.#koniState.priceService.getPriceSubject().subscribe(rs => {
|
|
@@ -4158,6 +4163,8 @@ export default class KoniExtension {
|
|
|
4158
4163
|
return await this.getPrice();
|
|
4159
4164
|
case 'pri(price.getSubscription)':
|
|
4160
4165
|
return await this.subscribePrice(id, port);
|
|
4166
|
+
case 'pri(settings.savePriceCurrency)':
|
|
4167
|
+
return await this.setPriceCurrency(request);
|
|
4161
4168
|
case 'pri(balance.getBalance)':
|
|
4162
4169
|
return await this.getBalance();
|
|
4163
4170
|
case 'pri(balance.getSubscription)':
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"./cjs/detectPackage.js"
|
|
18
18
|
],
|
|
19
19
|
"type": "module",
|
|
20
|
-
"version": "1.1.
|
|
20
|
+
"version": "1.1.60-0",
|
|
21
21
|
"main": "./cjs/index.js",
|
|
22
22
|
"module": "./index.js",
|
|
23
23
|
"types": "./index.d.ts",
|
|
@@ -1829,6 +1829,7 @@
|
|
|
1829
1829
|
"./utils/staticData/buyTokenConfigs.json": "./utils/staticData/buyTokenConfigs.json",
|
|
1830
1830
|
"./utils/staticData/chains.json": "./utils/staticData/chains.json",
|
|
1831
1831
|
"./utils/staticData/crowdloanFunds.json": "./utils/staticData/crowdloanFunds.json",
|
|
1832
|
+
"./utils/staticData/currencySymbol.json": "./utils/staticData/currencySymbol.json",
|
|
1832
1833
|
"./utils/staticData/marketingCampaigns.json": "./utils/staticData/marketingCampaigns.json",
|
|
1833
1834
|
"./utils/staticData/termAndCondition.json": "./utils/staticData/termAndCondition.json",
|
|
1834
1835
|
"./utils/swap": {
|
|
@@ -1873,11 +1874,11 @@
|
|
|
1873
1874
|
"@reduxjs/toolkit": "^1.9.1",
|
|
1874
1875
|
"@sora-substrate/type-definitions": "^1.17.7",
|
|
1875
1876
|
"@substrate/connect": "^0.8.9",
|
|
1876
|
-
"@subwallet/chain-list": "0.2.
|
|
1877
|
-
"@subwallet/extension-base": "^1.1.
|
|
1878
|
-
"@subwallet/extension-chains": "^1.1.
|
|
1879
|
-
"@subwallet/extension-dapp": "^1.1.
|
|
1880
|
-
"@subwallet/extension-inject": "^1.1.
|
|
1877
|
+
"@subwallet/chain-list": "0.2.58",
|
|
1878
|
+
"@subwallet/extension-base": "^1.1.60-0",
|
|
1879
|
+
"@subwallet/extension-chains": "^1.1.60-0",
|
|
1880
|
+
"@subwallet/extension-dapp": "^1.1.60-0",
|
|
1881
|
+
"@subwallet/extension-inject": "^1.1.60-0",
|
|
1881
1882
|
"@subwallet/keyring": "^0.1.5",
|
|
1882
1883
|
"@subwallet/ui-keyring": "^0.1.5",
|
|
1883
1884
|
"@walletconnect/sign-client": "^2.8.4",
|
package/packageInfo.js
CHANGED
|
@@ -7,5 +7,5 @@ export const packageInfo = {
|
|
|
7
7
|
name: '@subwallet/extension-base',
|
|
8
8
|
path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto',
|
|
9
9
|
type: 'esm',
|
|
10
|
-
version: '1.1.
|
|
10
|
+
version: '1.1.60-0'
|
|
11
11
|
};
|
|
@@ -7,7 +7,7 @@ import { SUB_TOKEN_REFRESH_BALANCE_INTERVAL } from '@subwallet/extension-base/co
|
|
|
7
7
|
import { getPSP22ContractPromise } from '@subwallet/extension-base/koni/api/tokens/wasm';
|
|
8
8
|
import { getDefaultWeightV2 } from '@subwallet/extension-base/koni/api/tokens/wasm/utils';
|
|
9
9
|
import { _BALANCE_CHAIN_GROUP, _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
|
|
10
|
-
import { _checkSmartContractSupportByChain, _getChainNativeTokenSlug, _getContractAddressOfToken, _getTokenOnChainAssetId, _getTokenOnChainInfo, _getXcmAssetMultilocation, _isBridgedToken, _isChainEvmCompatible, _isSubstrateRelayChain } from '@subwallet/extension-base/services/chain-service/utils';
|
|
10
|
+
import { _checkSmartContractSupportByChain, _getChainNativeTokenSlug, _getContractAddressOfToken, _getTokenOnChainAssetId, _getTokenOnChainInfo, _getTokenTypesSupportedByChain, _getXcmAssetMultilocation, _isBridgedToken, _isChainEvmCompatible, _isSubstrateRelayChain } from '@subwallet/extension-base/services/chain-service/utils';
|
|
11
11
|
import { filterAssetsByChainAndType } from '@subwallet/extension-base/utils';
|
|
12
12
|
import { combineLatest, Observable } from 'rxjs';
|
|
13
13
|
import { BN, BN_ZERO } from '@polkadot/util';
|
|
@@ -54,7 +54,13 @@ export const subscribeSubstrateBalance = async (addresses, chainInfo, assetMap,
|
|
|
54
54
|
if (_BALANCE_CHAIN_GROUP.supportBridged.includes(chain)) {
|
|
55
55
|
unsubBridgedToken = await subscribeBridgedBalance(substrateParams);
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Some substrate chain use evm account format but not have evm connection and support ERC20 contract,
|
|
60
|
+
* so we need to check if the chain is compatible with EVM and support ERC20
|
|
61
|
+
* */
|
|
62
|
+
if (_isChainEvmCompatible(chainInfo) && _getTokenTypesSupportedByChain(chainInfo).includes(_AssetType.ERC20)) {
|
|
63
|
+
// Get sub-token for EVM-compatible chains
|
|
58
64
|
unsubEvmContractToken = subscribeERC20Interval({
|
|
59
65
|
...baseParams,
|
|
60
66
|
evmApi: evmApi
|
|
@@ -90,7 +90,8 @@ export const _STAKING_ERA_LENGTH_MAP = {
|
|
|
90
90
|
manta_network: 6,
|
|
91
91
|
krest_network: 4,
|
|
92
92
|
polimec: 6,
|
|
93
|
-
enjin_relaychain: 24
|
|
93
|
+
enjin_relaychain: 24,
|
|
94
|
+
availTuringTest: 24
|
|
94
95
|
};
|
|
95
96
|
export const _EXPECTED_BLOCK_TIME = {
|
|
96
97
|
// in seconds
|
|
@@ -119,7 +120,8 @@ export const _EXPECTED_BLOCK_TIME = {
|
|
|
119
120
|
calamari: 12,
|
|
120
121
|
calamari_test: 12,
|
|
121
122
|
manta_network: 12,
|
|
122
|
-
enjin_relaychain: 6
|
|
123
|
+
enjin_relaychain: 6,
|
|
124
|
+
availTuringTest: 20
|
|
123
125
|
};
|
|
124
126
|
export const _PARACHAIN_INFLATION_DISTRIBUTION = {
|
|
125
127
|
moonbeam: {
|
|
@@ -645,7 +645,12 @@ export class ChainService {
|
|
|
645
645
|
this.substrateChainHandler.setSubstrateApi(chainInfo.slug, chainApi);
|
|
646
646
|
}
|
|
647
647
|
}
|
|
648
|
-
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* To check if the chain is EVM chain, we need to check if the chain has evmInfo and evmChainId is not -1
|
|
651
|
+
* (fake evm chain to connect to substrate chain)
|
|
652
|
+
* */
|
|
653
|
+
if (chainInfo.evmInfo !== null && chainInfo.evmInfo !== undefined && chainInfo.evmInfo.evmChainId !== -1) {
|
|
649
654
|
const chainApi = await this.evmChainHandler.initApi(chainInfo.slug, endpoint, {
|
|
650
655
|
providerName,
|
|
651
656
|
onUpdateStatus
|
|
@@ -11,6 +11,19 @@ export declare function _isPureEvmChain(chainInfo: _ChainInfo): boolean;
|
|
|
11
11
|
export declare function _isPureSubstrateChain(chainInfo: _ChainInfo): boolean;
|
|
12
12
|
export declare function _getOriginChainOfAsset(assetSlug: string): string;
|
|
13
13
|
export declare function _getContractAddressOfToken(tokenInfo: _ChainAsset): string;
|
|
14
|
+
/**
|
|
15
|
+
* @function _isNativeTokenTransferredByEvm
|
|
16
|
+
* @description Check if the native token is transferred by EVM, some token is only transferred by Substrate, need to check disableEvmTransfer flag
|
|
17
|
+
* @param {_ChainAsset} tokenInfo - The token info object
|
|
18
|
+
* @returns {boolean} - Return true if the native token can transfer by EVM
|
|
19
|
+
* */
|
|
20
|
+
export declare function _isNativeTokenTransferredByEvm(tokenInfo: _ChainAsset): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* @function _isTokenTransferredByEvm
|
|
23
|
+
* @description Check if the token is transferred by EVM
|
|
24
|
+
* @param {_ChainAsset} tokenInfo - The token info object
|
|
25
|
+
* @returns {boolean} - Return true if the token can transfer by EVM
|
|
26
|
+
* */
|
|
14
27
|
export declare function _isTokenTransferredByEvm(tokenInfo: _ChainAsset): boolean;
|
|
15
28
|
export declare function _checkSmartContractSupportByChain(chainInfo: _ChainInfo, contractType: _AssetType): boolean;
|
|
16
29
|
export declare function _getTokenOnChainAssetId(tokenInfo: _ChainAsset): string;
|
|
@@ -67,9 +67,27 @@ export function _getContractAddressOfToken(tokenInfo) {
|
|
|
67
67
|
var _tokenInfo$metadata;
|
|
68
68
|
return ((_tokenInfo$metadata = tokenInfo.metadata) === null || _tokenInfo$metadata === void 0 ? void 0 : _tokenInfo$metadata.contractAddress) || '';
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @function _isNativeTokenTransferredByEvm
|
|
73
|
+
* @description Check if the native token is transferred by EVM, some token is only transferred by Substrate, need to check disableEvmTransfer flag
|
|
74
|
+
* @param {_ChainAsset} tokenInfo - The token info object
|
|
75
|
+
* @returns {boolean} - Return true if the native token can transfer by EVM
|
|
76
|
+
* */
|
|
77
|
+
export function _isNativeTokenTransferredByEvm(tokenInfo) {
|
|
71
78
|
var _tokenInfo$metadata2;
|
|
72
|
-
return
|
|
79
|
+
return !((_tokenInfo$metadata2 = tokenInfo.metadata) !== null && _tokenInfo$metadata2 !== void 0 && _tokenInfo$metadata2.disableEvmTransfer);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @function _isTokenTransferredByEvm
|
|
84
|
+
* @description Check if the token is transferred by EVM
|
|
85
|
+
* @param {_ChainAsset} tokenInfo - The token info object
|
|
86
|
+
* @returns {boolean} - Return true if the token can transfer by EVM
|
|
87
|
+
* */
|
|
88
|
+
export function _isTokenTransferredByEvm(tokenInfo) {
|
|
89
|
+
var _tokenInfo$metadata3;
|
|
90
|
+
return !!((_tokenInfo$metadata3 = tokenInfo.metadata) !== null && _tokenInfo$metadata3 !== void 0 && _tokenInfo$metadata3.contractAddress) || _isNativeToken(tokenInfo) && _isNativeTokenTransferredByEvm(tokenInfo);
|
|
73
91
|
}
|
|
74
92
|
export function _checkSmartContractSupportByChain(chainInfo, contractType) {
|
|
75
93
|
// EVM chains support smart contract by default so just checking Substrate chains
|
|
@@ -81,16 +99,16 @@ export function _checkSmartContractSupportByChain(chainInfo, contractType) {
|
|
|
81
99
|
|
|
82
100
|
// Utils for balance functions
|
|
83
101
|
export function _getTokenOnChainAssetId(tokenInfo) {
|
|
84
|
-
var _tokenInfo$
|
|
85
|
-
return ((_tokenInfo$
|
|
102
|
+
var _tokenInfo$metadata4;
|
|
103
|
+
return ((_tokenInfo$metadata4 = tokenInfo.metadata) === null || _tokenInfo$metadata4 === void 0 ? void 0 : _tokenInfo$metadata4.assetId) || '-1';
|
|
86
104
|
}
|
|
87
105
|
export function _getTokenOnChainInfo(tokenInfo) {
|
|
88
|
-
var _tokenInfo$
|
|
89
|
-
return (_tokenInfo$
|
|
106
|
+
var _tokenInfo$metadata5;
|
|
107
|
+
return (_tokenInfo$metadata5 = tokenInfo.metadata) === null || _tokenInfo$metadata5 === void 0 ? void 0 : _tokenInfo$metadata5.onChainInfo;
|
|
90
108
|
}
|
|
91
109
|
export function _isBridgedToken(tokenInfo) {
|
|
92
|
-
var _tokenInfo$
|
|
93
|
-
return (_tokenInfo$
|
|
110
|
+
var _tokenInfo$metadata6;
|
|
111
|
+
return (_tokenInfo$metadata6 = tokenInfo.metadata) === null || _tokenInfo$metadata6 === void 0 ? void 0 : _tokenInfo$metadata6.isBridged;
|
|
94
112
|
}
|
|
95
113
|
export function _getTokenMinAmount(tokenInfo) {
|
|
96
114
|
return tokenInfo.minAmount || '0';
|
|
@@ -244,16 +262,16 @@ export function _isXcmPathSupported(originTokenSlug, destinationTokenSlug, asset
|
|
|
244
262
|
return assetRef.path === _AssetRefPath.XCM;
|
|
245
263
|
}
|
|
246
264
|
export function _getXcmAssetType(tokenInfo) {
|
|
247
|
-
var _tokenInfo$
|
|
248
|
-
return ((_tokenInfo$
|
|
265
|
+
var _tokenInfo$metadata7;
|
|
266
|
+
return ((_tokenInfo$metadata7 = tokenInfo.metadata) === null || _tokenInfo$metadata7 === void 0 ? void 0 : _tokenInfo$metadata7.assetType) || '';
|
|
249
267
|
}
|
|
250
268
|
export function _getXcmAssetId(tokenInfo) {
|
|
251
|
-
var _tokenInfo$
|
|
252
|
-
return ((_tokenInfo$
|
|
269
|
+
var _tokenInfo$metadata8;
|
|
270
|
+
return ((_tokenInfo$metadata8 = tokenInfo.metadata) === null || _tokenInfo$metadata8 === void 0 ? void 0 : _tokenInfo$metadata8.assetId) || '-1';
|
|
253
271
|
}
|
|
254
272
|
export function _getXcmAssetMultilocation(tokenInfo) {
|
|
255
|
-
var _tokenInfo$
|
|
256
|
-
return (_tokenInfo$
|
|
273
|
+
var _tokenInfo$metadata9;
|
|
274
|
+
return (_tokenInfo$metadata9 = tokenInfo.metadata) === null || _tokenInfo$metadata9 === void 0 ? void 0 : _tokenInfo$metadata9.multilocation;
|
|
257
275
|
}
|
|
258
276
|
export function _getXcmTransferType(originChainInfo, destinationChainInfo) {
|
|
259
277
|
var _originChainInfo$subs, _destinationChainInfo;
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
export const _STAKING_CHAIN_GROUP = {
|
|
5
|
-
relay: ['polkadot', 'kusama', 'aleph', 'polkadex', 'ternoa', 'alephTest', 'polkadexTest', 'westend', 'kate', 'edgeware', 'creditcoin', 'vara_network', 'goldberg_testnet'],
|
|
5
|
+
relay: ['polkadot', 'kusama', 'aleph', 'polkadex', 'ternoa', 'alephTest', 'polkadexTest', 'westend', 'kate', 'edgeware', 'creditcoin', 'vara_network', 'goldberg_testnet', 'availTuringTest'],
|
|
6
6
|
para: ['moonbeam', 'moonriver', 'moonbase', 'turing', 'turingStaging', 'bifrost', 'bifrost_testnet', 'calamari_test', 'calamari', 'manta_network', 'polimec'],
|
|
7
7
|
astar: ['astar', 'shiden', 'shibuya'],
|
|
8
8
|
amplitude: ['amplitude', 'amplitude_test', 'kilt', 'kilt_peregrine', 'pendulum', 'krest_network'],
|
|
9
9
|
// amplitude and kilt only share some common logic
|
|
10
10
|
kilt: ['kilt', 'kilt_peregrine'],
|
|
11
|
-
nominationPool: ['polkadot', 'kusama', 'westend', 'alephTest', 'aleph', 'kate', 'vara_network', 'goldberg_testnet'],
|
|
11
|
+
nominationPool: ['polkadot', 'kusama', 'westend', 'alephTest', 'aleph', 'kate', 'vara_network', 'goldberg_testnet', 'availTuringTest'],
|
|
12
12
|
bifrost: ['bifrost', 'bifrost_testnet'],
|
|
13
13
|
aleph: ['aleph', 'alephTest'],
|
|
14
14
|
// A0 has distinct tokenomics
|
|
@@ -57,9 +57,9 @@ export default class RelayNativeStakingPoolHandler extends BaseNativeStakingPool
|
|
|
57
57
|
const maxUnlockingChunks = substrateApi.api.consts.staking.maxUnlockingChunks.toString();
|
|
58
58
|
const unlockingEras = substrateApi.api.consts.staking.bondingDuration.toString();
|
|
59
59
|
const maxSupportedEras = substrateApi.api.consts.staking.historyDepth.toString();
|
|
60
|
-
const erasPerDay = 24 / _STAKING_ERA_LENGTH_MAP[chainInfo.slug]; // Can be exactly calculate from epochDuration
|
|
60
|
+
const erasPerDay = 24 / _STAKING_ERA_LENGTH_MAP[chainInfo.slug]; // Can be exactly calculate from babe.epochDuration * blockTime * staking.sessionsPerEra
|
|
61
61
|
|
|
62
|
-
const supportedDays = getSupportedDaysByHistoryDepth(erasPerDay, parseInt(maxSupportedEras));
|
|
62
|
+
const supportedDays = getSupportedDaysByHistoryDepth(erasPerDay, parseInt(maxSupportedEras), parseInt(currentEra) / erasPerDay);
|
|
63
63
|
const startEra = parseInt(currentEra) - supportedDays * erasPerDay;
|
|
64
64
|
const [_EraStakeInfo, _totalIssuance, _auctionCounter, _minNominatorBond, _counterForNominators, _minimumActiveStake, ..._eraReward] = await Promise.all([substrateApi.api.query.staking.erasTotalStake.multi([parseInt(currentEra), parseInt(currentEra) - 1]), substrateApi.api.query.balances.totalIssuance(), (_substrateApi$api$que2 = substrateApi.api.query.auctions) === null || _substrateApi$api$que2 === void 0 ? void 0 : _substrateApi$api$que2.auctionCounter(), substrateApi.api.query.staking.minNominatorBond(), substrateApi.api.query.staking.counterForNominators(), ((_substrateApi$api$que3 = substrateApi.api.query) === null || _substrateApi$api$que3 === void 0 ? void 0 : (_substrateApi$api$que4 = _substrateApi$api$que3.staking) === null || _substrateApi$api$que4 === void 0 ? void 0 : _substrateApi$api$que4.minimumActiveStake) && ((_substrateApi$api$que5 = substrateApi.api.query) === null || _substrateApi$api$que5 === void 0 ? void 0 : (_substrateApi$api$que6 = _substrateApi$api$que5.staking) === null || _substrateApi$api$que6 === void 0 ? void 0 : _substrateApi$api$que6.minimumActiveStake()), substrateApi.api.query.staking.erasValidatorReward.multi([...Array(supportedDays).keys()].map(i => i + startEra))]);
|
|
65
65
|
const [_totalEraStake, _lastTotalStaked] = _EraStakeInfo;
|
|
@@ -144,7 +144,7 @@ export default class RelayNativeStakingPoolHandler extends BaseNativeStakingPool
|
|
|
144
144
|
let nominationStatus = EarningStatus.NOT_EARNING;
|
|
145
145
|
let eraStakerOtherList = [];
|
|
146
146
|
let identity;
|
|
147
|
-
if (['kusama', 'polkadot', 'westend'].includes(this.chain)) {
|
|
147
|
+
if (['kusama', 'polkadot', 'westend', 'availTuringTest'].includes(this.chain)) {
|
|
148
148
|
// todo: review all relaychains later
|
|
149
149
|
const [[_identity], _eraStaker] = await Promise.all([parseIdentity(substrateApi, validatorAddress), substrateApi.api.query.staking.erasStakersPaged.entries(currentEra, validatorAddress)]);
|
|
150
150
|
identity = _identity;
|
|
@@ -298,7 +298,7 @@ export default class RelayNativeStakingPoolHandler extends BaseNativeStakingPool
|
|
|
298
298
|
const endEraForPoints = parseInt(activeEra) - 1;
|
|
299
299
|
let startEraForPoints = endEraForPoints - maxEraRewardPointsEras + 1;
|
|
300
300
|
let _eraStakersPromise;
|
|
301
|
-
if (['kusama', 'polkadot', 'westend'].includes(this.chain)) {
|
|
301
|
+
if (['kusama', 'polkadot', 'westend', 'availTuringTest'].includes(this.chain)) {
|
|
302
302
|
// todo: review all relaychains later
|
|
303
303
|
_eraStakersPromise = chainApi.api.query.staking.erasStakersOverview.entries(parseInt(currentEra));
|
|
304
304
|
} else {
|
|
@@ -81,7 +81,7 @@ export default class NominationPoolHandler extends BasePoolHandler {
|
|
|
81
81
|
const maxSupportedEras = substrateApi.api.consts.staking.historyDepth.toString();
|
|
82
82
|
const erasPerDay = 24 / _STAKING_ERA_LENGTH_MAP[chainInfo.slug]; // Can be exactly calculate from epochDuration, blockTime, sessionsPerEra
|
|
83
83
|
|
|
84
|
-
const supportedDays = getSupportedDaysByHistoryDepth(erasPerDay, parseInt(maxSupportedEras));
|
|
84
|
+
const supportedDays = getSupportedDaysByHistoryDepth(erasPerDay, parseInt(maxSupportedEras), parseInt(currentEra) / erasPerDay);
|
|
85
85
|
const startEra = parseInt(currentEra) - supportedDays * erasPerDay;
|
|
86
86
|
const [_EraStakeInfo, _totalIssuance, _auctionCounter, _minPoolJoin, ..._eraReward] = await Promise.all([substrateApi.api.query.staking.erasTotalStake.multi([parseInt(currentEra), parseInt(currentEra) - 1]), substrateApi.api.query.balances.totalIssuance(), (_substrateApi$api$que2 = substrateApi.api.query.auctions) === null || _substrateApi$api$que2 === void 0 ? void 0 : _substrateApi$api$que2.auctionCounter(), (_substrateApi$api$que3 = substrateApi.api.query) === null || _substrateApi$api$que3 === void 0 ? void 0 : (_substrateApi$api$que4 = _substrateApi$api$que3.nominationPools) === null || _substrateApi$api$que4 === void 0 ? void 0 : _substrateApi$api$que4.minJoinBond(), substrateApi.api.query.staking.erasValidatorReward.multi([...Array(supportedDays).keys()].map(i => i + startEra))]);
|
|
87
87
|
const [_totalEraStake, _lastTotalStaked] = _EraStakeInfo;
|
|
@@ -159,7 +159,7 @@ export default class NominationPoolHandler extends BasePoolHandler {
|
|
|
159
159
|
const validatorList = nominations.targets;
|
|
160
160
|
await Promise.all(validatorList.map(async validatorAddress => {
|
|
161
161
|
let eraStakerOtherList = [];
|
|
162
|
-
if (['kusama', 'polkadot', 'westend'].includes(this.chain)) {
|
|
162
|
+
if (['kusama', 'polkadot', 'westend', 'availTuringTest'].includes(this.chain)) {
|
|
163
163
|
// todo: review all relaychains later
|
|
164
164
|
const _eraStaker = await substrateApi.api.query.staking.erasStakersPaged.entries(currentEra, validatorAddress);
|
|
165
165
|
eraStakerOtherList = _eraStaker.flatMap(paged => paged[1].toPrimitive().others);
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { PriceJson } from '@subwallet/extension-base/background/KoniTypes';
|
|
2
|
-
export declare const
|
|
1
|
+
import { CurrencyType, ExchangeRateJSON, PriceJson } from '@subwallet/extension-base/background/KoniTypes';
|
|
2
|
+
export declare const getExchangeRateMap: () => Promise<Record<CurrencyType, ExchangeRateJSON>>;
|
|
3
|
+
export declare const getPriceMap: (priceIds: Set<string>, currency?: CurrencyType) => Promise<Omit<PriceJson, 'exchangeRateMap'>>;
|
|
@@ -1,43 +1,61 @@
|
|
|
1
1
|
// Copyright 2019-2022 @subwallet/extension-koni authors & contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
+
import { staticData, StaticKey } from '@subwallet/extension-base/utils/staticData';
|
|
4
5
|
import axios from 'axios';
|
|
6
|
+
const DEFAULT_CURRENCY = 'USD';
|
|
5
7
|
let useBackupApi = false;
|
|
6
|
-
export const
|
|
8
|
+
export const getExchangeRateMap = async () => {
|
|
7
9
|
try {
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
res = await axios.get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=${currency}&per_page=250&ids=${idStr}`);
|
|
14
|
-
} catch (err) {
|
|
15
|
-
useBackupApi = true;
|
|
10
|
+
const responseDataExchangeRate = (await axios.get('https://api-cache.subwallet.app/exchange-rate')).data || {};
|
|
11
|
+
const exchangeRateMap = Object.keys(responseDataExchangeRate.conversion_rates).reduce((map, exchangeKey) => {
|
|
12
|
+
if (!staticData[StaticKey.CURRENCY_SYMBOL][exchangeKey]) {
|
|
13
|
+
return map;
|
|
16
14
|
}
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
map[exchangeKey] = {
|
|
16
|
+
exchange: responseDataExchangeRate.conversion_rates[exchangeKey],
|
|
17
|
+
label: staticData[StaticKey.CURRENCY_SYMBOL][exchangeKey].label
|
|
18
|
+
};
|
|
19
|
+
return map;
|
|
20
|
+
}, {});
|
|
21
|
+
return exchangeRateMap;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.warn('Failed to get exchange rate');
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export const getPriceMap = async (priceIds, currency = 'USD') => {
|
|
28
|
+
var _rs, _rs2, _rs3;
|
|
29
|
+
const idStr = Array.from(priceIds).join(',');
|
|
30
|
+
let rs;
|
|
31
|
+
if (!useBackupApi) {
|
|
32
|
+
try {
|
|
33
|
+
rs = await axios.get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=${currency.toLowerCase()}&per_page=250&ids=${idStr}`);
|
|
34
|
+
} catch (err) {
|
|
19
35
|
useBackupApi = true;
|
|
20
|
-
res = await axios.get(`https://chain-data.subwallet.app/api/price/get?ids=${idStr}`);
|
|
21
|
-
}
|
|
22
|
-
if (res.status !== 200) {
|
|
23
|
-
console.warn('Failed to get token price');
|
|
24
36
|
}
|
|
25
|
-
const responseData = res.data || [];
|
|
26
|
-
const priceMap = {};
|
|
27
|
-
const price24hMap = {};
|
|
28
|
-
responseData.forEach(val => {
|
|
29
|
-
const currentPrice = val.current_price || 0;
|
|
30
|
-
const price24h = currentPrice - (val.price_change_24h || 0);
|
|
31
|
-
priceMap[val.id] = currentPrice;
|
|
32
|
-
price24hMap[val.id] = price24h;
|
|
33
|
-
});
|
|
34
|
-
return {
|
|
35
|
-
currency,
|
|
36
|
-
priceMap,
|
|
37
|
-
price24hMap
|
|
38
|
-
};
|
|
39
|
-
} catch (err) {
|
|
40
|
-
console.error(err);
|
|
41
|
-
throw err;
|
|
42
37
|
}
|
|
38
|
+
if (useBackupApi || ((_rs = rs) === null || _rs === void 0 ? void 0 : _rs.status) !== 200) {
|
|
39
|
+
useBackupApi = true;
|
|
40
|
+
rs = await axios.get(`https://chain-data.subwallet.app/api/price/get?ids=${idStr}`);
|
|
41
|
+
}
|
|
42
|
+
if (((_rs2 = rs) === null || _rs2 === void 0 ? void 0 : _rs2.status) !== 200) {
|
|
43
|
+
console.warn('Failed to get token price');
|
|
44
|
+
}
|
|
45
|
+
const responseDataPrice = ((_rs3 = rs) === null || _rs3 === void 0 ? void 0 : _rs3.data) || [];
|
|
46
|
+
const currencyData = staticData[StaticKey.CURRENCY_SYMBOL][currency || DEFAULT_CURRENCY];
|
|
47
|
+
const priceMap = {};
|
|
48
|
+
const price24hMap = {};
|
|
49
|
+
responseDataPrice.forEach(val => {
|
|
50
|
+
const currentPrice = val.current_price || 0;
|
|
51
|
+
const price24h = currentPrice - (val.price_change_24h || 0);
|
|
52
|
+
priceMap[val.id] = currentPrice;
|
|
53
|
+
price24hMap[val.id] = price24h;
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
currency,
|
|
57
|
+
currencyData,
|
|
58
|
+
priceMap,
|
|
59
|
+
price24hMap
|
|
60
|
+
};
|
|
43
61
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PriceJson } from '@subwallet/extension-base/background/KoniTypes';
|
|
1
|
+
import { CurrencyType, PriceJson } from '@subwallet/extension-base/background/KoniTypes';
|
|
2
2
|
import { CronServiceInterface, PersistDataServiceInterface, ServiceStatus, StoppableServiceInterface } from '@subwallet/extension-base/services/base/types';
|
|
3
3
|
import { ChainService } from '@subwallet/extension-base/services/chain-service';
|
|
4
4
|
import { EventService } from '@subwallet/extension-base/services/event-service';
|
|
@@ -10,13 +10,18 @@ export declare class PriceService implements StoppableServiceInterface, PersistD
|
|
|
10
10
|
private eventService;
|
|
11
11
|
private chainService;
|
|
12
12
|
private priceSubject;
|
|
13
|
+
private rawPriceSubject;
|
|
14
|
+
private rawExchangeRateMap;
|
|
13
15
|
private refreshTimeout;
|
|
14
16
|
private priceIds;
|
|
17
|
+
private currency;
|
|
15
18
|
constructor(dbService: DatabaseService, eventService: EventService, chainService: ChainService);
|
|
19
|
+
private getTokenPrice;
|
|
16
20
|
getPrice(): Promise<PriceJson>;
|
|
17
21
|
getPriceSubject(): BehaviorSubject<PriceJson>;
|
|
18
22
|
getPriceIds(): Set<string>;
|
|
19
|
-
|
|
23
|
+
setPriceCurrency(newCurrencyCode: CurrencyType): Promise<boolean>;
|
|
24
|
+
refreshPriceData(priceIds?: Set<string>, resolve?: (rs: boolean) => void, reject?: (e: boolean) => void): void;
|
|
20
25
|
init(): Promise<void>;
|
|
21
26
|
loadData(): Promise<void>;
|
|
22
27
|
persistData(): Promise<void>;
|