@subwallet/extension-base 1.1.23-0 → 1.1.24-1

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 (82) hide show
  1. package/background/KoniTypes.d.ts +12 -26
  2. package/cjs/constants/index.js +9 -3
  3. package/cjs/koni/api/staking/bonding/index.js +1 -0
  4. package/cjs/koni/api/tokens/evm/balance.js +5 -1
  5. package/cjs/koni/api/tokens/evm/transfer.js +8 -4
  6. package/cjs/koni/background/cron.js +2 -2
  7. package/cjs/koni/background/handlers/Extension.js +14 -5
  8. package/cjs/koni/background/handlers/Mobile.js +32 -0
  9. package/cjs/koni/background/handlers/State.js +126 -45
  10. package/cjs/koni/background/subscription.js +2 -2
  11. package/cjs/packageInfo.js +1 -1
  12. package/cjs/services/balance-service/helpers/group.js +53 -0
  13. package/cjs/services/balance-service/helpers/subscribe/balance.js +111 -0
  14. package/cjs/services/balance-service/helpers/subscribe/evm.js +95 -0
  15. package/cjs/services/balance-service/helpers/subscribe/substrate/equilibrium.js +113 -0
  16. package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +324 -0
  17. package/cjs/services/balance-service/index.js +41 -16
  18. package/cjs/services/history-service/helpers/subscan-extrinsic-parser-helper.js +7 -1
  19. package/cjs/services/history-service/index.js +12 -8
  20. package/cjs/services/migration-service/scripts/MigrateProvider.js +1 -1
  21. package/cjs/services/storage-service/DatabaseService.js +47 -2
  22. package/cjs/services/storage-service/db-stores/Balance.js +9 -9
  23. package/cjs/services/subscan-service/index.js +66 -22
  24. package/cjs/services/transaction-service/index.js +4 -3
  25. package/cjs/types/balance.js +1 -0
  26. package/cjs/types/index.js +11 -0
  27. package/cjs/utils/{address.js → account.js} +32 -2
  28. package/cjs/utils/eth.js +7 -2
  29. package/cjs/utils/index.js +12 -0
  30. package/constants/index.d.ts +2 -0
  31. package/constants/index.js +2 -0
  32. package/koni/api/staking/bonding/index.js +1 -0
  33. package/koni/api/tokens/evm/balance.js +5 -1
  34. package/koni/api/tokens/evm/transfer.d.ts +1 -1
  35. package/koni/api/tokens/evm/transfer.js +8 -4
  36. package/koni/background/cron.js +3 -3
  37. package/koni/background/handlers/Extension.js +14 -5
  38. package/koni/background/handlers/Mobile.d.ts +5 -1
  39. package/koni/background/handlers/Mobile.js +31 -0
  40. package/koni/background/handlers/State.d.ts +6 -4
  41. package/koni/background/handlers/State.js +114 -34
  42. package/koni/background/subscription.js +2 -2
  43. package/package.json +40 -14
  44. package/packageInfo.js +1 -1
  45. package/services/balance-service/helpers/group.d.ts +9 -0
  46. package/services/balance-service/helpers/group.js +46 -0
  47. package/services/balance-service/helpers/subscribe/balance.d.ts +4 -0
  48. package/services/balance-service/helpers/subscribe/balance.js +103 -0
  49. package/services/balance-service/helpers/subscribe/evm.d.ts +5 -0
  50. package/services/balance-service/helpers/subscribe/evm.js +87 -0
  51. package/services/balance-service/helpers/subscribe/substrate/equilibrium.d.ts +4 -0
  52. package/services/balance-service/helpers/subscribe/substrate/equilibrium.js +105 -0
  53. package/services/balance-service/helpers/subscribe/substrate/index.d.ts +4 -0
  54. package/services/balance-service/helpers/subscribe/substrate/index.js +316 -0
  55. package/services/balance-service/index.d.ts +24 -5
  56. package/services/balance-service/index.js +40 -14
  57. package/services/history-service/helpers/subscan-extrinsic-parser-helper.js +7 -1
  58. package/services/history-service/index.js +12 -8
  59. package/services/migration-service/scripts/MigrateProvider.js +1 -1
  60. package/services/storage-service/DatabaseService.d.ts +7 -2
  61. package/services/storage-service/DatabaseService.js +47 -2
  62. package/services/storage-service/databases/index.d.ts +2 -1
  63. package/services/storage-service/db-stores/Balance.d.ts +2 -2
  64. package/services/storage-service/db-stores/Balance.js +9 -9
  65. package/services/subscan-service/index.d.ts +11 -5
  66. package/services/subscan-service/index.js +66 -26
  67. package/services/subscan-service/types.d.ts +4 -0
  68. package/services/transaction-service/index.js +5 -4
  69. package/types/balance.d.ts +40 -0
  70. package/types/balance.js +1 -0
  71. package/types/index.d.ts +1 -0
  72. package/types/index.js +1 -0
  73. package/utils/account.d.ts +15 -0
  74. package/utils/{address.js → account.js} +28 -0
  75. package/utils/eth.d.ts +1 -0
  76. package/utils/eth.js +4 -0
  77. package/utils/index.d.ts +1 -0
  78. package/utils/index.js +1 -0
  79. package/cjs/koni/api/dotsama/balance.js +0 -464
  80. package/koni/api/dotsama/balance.d.ts +0 -6
  81. package/koni/api/dotsama/balance.js +0 -451
  82. package/utils/address.d.ts +0 -5
@@ -0,0 +1,105 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { _AssetType } from '@subwallet/chain-list/types';
5
+ import { APIItemState } from '@subwallet/extension-base/background/KoniTypes';
6
+ import { state } from '@subwallet/extension-base/koni/background/handlers';
7
+ import { _getTokenOnChainAssetId } from '@subwallet/extension-base/services/chain-service/utils';
8
+ import BigN from 'bignumber.js';
9
+ import { BN, BN_ZERO } from '@polkadot/util';
10
+ export async function subscribeEquilibriumTokenBalance(addresses, chain, api, callBack, includeNativeToken) {
11
+ const tokenTypes = includeNativeToken ? [_AssetType.NATIVE, _AssetType.LOCAL] : [_AssetType.LOCAL];
12
+ const tokenMap = state.getAssetByChainAndAsset(chain, tokenTypes);
13
+ try {
14
+ const unsub = await api.query.system.account.multi(addresses, balances => {
15
+ // Equilibrium customizes the SystemAccount pallet
16
+ Object.values(tokenMap).forEach(tokenInfo => {
17
+ const assetId = _getTokenOnChainAssetId(tokenInfo);
18
+ const items = balances.map((balance, index) => {
19
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
20
+ const balancesData = JSON.parse(balance.data.toString());
21
+ const balanceList = balancesData.v0.balance;
22
+
23
+ // @ts-ignore
24
+ const freeTokenBalance = balanceList.find(data => data[0] === parseInt(assetId));
25
+ const bnFreeTokenBalance = freeTokenBalance ? new BN(new BigN(freeTokenBalance[1].positive).toString()) : BN_ZERO;
26
+ return {
27
+ address: addresses[index],
28
+ free: bnFreeTokenBalance.toString(),
29
+ locked: '0',
30
+ // Equilibrium doesn't show locked balance
31
+ state: APIItemState.READY,
32
+ tokenSlug: tokenInfo.slug
33
+ };
34
+ });
35
+ callBack(items);
36
+ });
37
+ });
38
+ return () => {
39
+ unsub();
40
+ };
41
+ } catch (e) {
42
+ Object.values(tokenMap).forEach(tokenInfo => {
43
+ const items = addresses.map(address => {
44
+ return {
45
+ address: address,
46
+ free: '0',
47
+ locked: '0',
48
+ // Equilibrium doesn't show locked balance
49
+ state: APIItemState.READY,
50
+ tokenSlug: tokenInfo.slug
51
+ };
52
+ });
53
+ callBack(items);
54
+ });
55
+ return () => {
56
+ // Empty
57
+ };
58
+ }
59
+ }
60
+
61
+ // eslint-disable-next-line @typescript-eslint/require-await
62
+ export async function subscribeEqBalanceAccountPallet(addresses, chain, api, callBack, includeNativeToken) {
63
+ const tokenTypes = includeNativeToken ? [_AssetType.NATIVE, _AssetType.LOCAL] : [_AssetType.LOCAL];
64
+ const tokenMap = state.getAssetByChainAndAsset(chain, tokenTypes);
65
+ const unsubList = Object.values(tokenMap).map(async tokenInfo => {
66
+ try {
67
+ const assetId = _getTokenOnChainAssetId(tokenInfo);
68
+ const unsub = await api.query.eqBalances.account.multi(addresses.map(address => [address, [assetId]]), balances => {
69
+ const items = balances.map((balance, index) => {
70
+ return {
71
+ address: addresses[index],
72
+ free: balance.asPositive.toString(),
73
+ locked: '0',
74
+ // Equilibrium doesn't show locked balance
75
+ state: APIItemState.READY,
76
+ tokenSlug: tokenInfo.slug
77
+ };
78
+ });
79
+ callBack(items);
80
+ });
81
+ return unsub;
82
+ } catch (err) {
83
+ console.warn(err);
84
+ const items = addresses.map(address => {
85
+ return {
86
+ address: address,
87
+ free: '0',
88
+ locked: '0',
89
+ // Equilibrium doesn't show locked balance
90
+ state: APIItemState.READY,
91
+ tokenSlug: tokenInfo.slug
92
+ };
93
+ });
94
+ callBack(items);
95
+ return undefined;
96
+ }
97
+ });
98
+ return () => {
99
+ unsubList.forEach(subProm => {
100
+ subProm.then(unsub => {
101
+ unsub && unsub();
102
+ }).catch(console.error);
103
+ });
104
+ };
105
+ }
@@ -0,0 +1,4 @@
1
+ import { _ChainInfo } from '@subwallet/chain-list/types';
2
+ import { _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
3
+ import { BalanceItem } from '@subwallet/extension-base/types';
4
+ export declare function subscribeSubstrateBalance(addresses: string[], chainInfo: _ChainInfo, chain: string, networkAPI: _SubstrateApi, evmApiMap: Record<string, _EvmApi>, callBack: (rs: BalanceItem[]) => void): Promise<() => void>;
@@ -0,0 +1,316 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { _AssetType } from '@subwallet/chain-list/types';
5
+ import { APIItemState } from '@subwallet/extension-base/background/KoniTypes';
6
+ import { SUB_TOKEN_REFRESH_BALANCE_INTERVAL } from '@subwallet/extension-base/constants';
7
+ import { getPSP22ContractPromise } from '@subwallet/extension-base/koni/api/tokens/wasm';
8
+ import { getDefaultWeightV2 } from '@subwallet/extension-base/koni/api/tokens/wasm/utils';
9
+ import { state } from '@subwallet/extension-base/koni/background/handlers';
10
+ import { subscribeERC20Interval } from '@subwallet/extension-base/services/balance-service/helpers/subscribe/evm';
11
+ import { _BALANCE_CHAIN_GROUP, _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
12
+ import { _checkSmartContractSupportByChain, _getChainNativeTokenSlug, _getContractAddressOfToken, _getTokenOnChainAssetId, _getTokenOnChainInfo, _isChainEvmCompatible, _isSubstrateRelayChain } from '@subwallet/extension-base/services/chain-service/utils';
13
+ import { BN, BN_ZERO } from '@polkadot/util';
14
+ import { subscribeEqBalanceAccountPallet, subscribeEquilibriumTokenBalance } from "./equilibrium.js";
15
+ export async function subscribeSubstrateBalance(addresses, chainInfo, chain, networkAPI, evmApiMap, callBack) {
16
+ let unsubNativeToken;
17
+ if (!_BALANCE_CHAIN_GROUP.kintsugi.includes(chain) && !_BALANCE_CHAIN_GROUP.genshiro.includes(chain) && !_BALANCE_CHAIN_GROUP.equilibrium_parachain.includes(chain)) {
18
+ unsubNativeToken = await subscribeWithSystemAccountPallet(addresses, chainInfo, networkAPI.api, callBack);
19
+ }
20
+ let unsubLocalToken;
21
+ let unsubEvmContractToken;
22
+ let unsubWasmContractToken;
23
+ try {
24
+ if (_BALANCE_CHAIN_GROUP.bifrost.includes(chain)) {
25
+ unsubLocalToken = await subscribeTokensAccountsPallet(addresses, chain, networkAPI.api, callBack);
26
+ } else if (_BALANCE_CHAIN_GROUP.kintsugi.includes(chain)) {
27
+ unsubLocalToken = await subscribeTokensAccountsPallet(addresses, chain, networkAPI.api, callBack, true);
28
+ } else if (_BALANCE_CHAIN_GROUP.statemine.includes(chain)) {
29
+ unsubLocalToken = await subscribeAssetsAccountPallet(addresses, chain, networkAPI.api, callBack);
30
+ } else if (_BALANCE_CHAIN_GROUP.genshiro.includes(chain)) {
31
+ unsubLocalToken = await subscribeEqBalanceAccountPallet(addresses, chain, networkAPI.api, callBack, true);
32
+ } else if (_BALANCE_CHAIN_GROUP.equilibrium_parachain.includes(chain)) {
33
+ unsubLocalToken = await subscribeEquilibriumTokenBalance(addresses, chain, networkAPI.api, callBack, true);
34
+ } else if (_BALANCE_CHAIN_GROUP.centrifuge.includes(chain)) {
35
+ unsubLocalToken = await subscribeOrmlTokensPallet(addresses, chain, networkAPI.api, callBack);
36
+ }
37
+ if (_isChainEvmCompatible(chainInfo)) {
38
+ unsubEvmContractToken = subscribeERC20Interval(addresses, chain, evmApiMap, callBack);
39
+ }
40
+ if (_checkSmartContractSupportByChain(chainInfo, _AssetType.PSP22)) {
41
+ // Get sub-token for substrate-based chains
42
+ unsubWasmContractToken = subscribePSP22Balance(addresses, chain, networkAPI.api, callBack);
43
+ }
44
+ } catch (err) {
45
+ console.warn(err);
46
+ }
47
+ return () => {
48
+ unsubNativeToken && unsubNativeToken();
49
+ unsubLocalToken && unsubLocalToken();
50
+ unsubEvmContractToken && unsubEvmContractToken();
51
+ unsubWasmContractToken && unsubWasmContractToken();
52
+ };
53
+ }
54
+
55
+ // handler according to different logic
56
+ async function subscribeWithSystemAccountPallet(addresses, chainInfo, networkAPI, callBack) {
57
+ const chainNativeTokenSlug = _getChainNativeTokenSlug(chainInfo);
58
+
59
+ // TODO: Need handle case error
60
+ const unsub = await networkAPI.query.system.account.multi(addresses, async balances => {
61
+ const pooledStakingBalances = [];
62
+ if (_isSubstrateRelayChain(chainInfo) && networkAPI.query.nominationPools) {
63
+ var _networkAPI$query$nom;
64
+ const poolMemberDatas = await ((_networkAPI$query$nom = networkAPI.query.nominationPools.poolMembers) === null || _networkAPI$query$nom === void 0 ? void 0 : _networkAPI$query$nom.multi(addresses));
65
+ if (poolMemberDatas) {
66
+ for (const _poolMemberData of poolMemberDatas) {
67
+ const poolMemberData = _poolMemberData.toPrimitive();
68
+ if (poolMemberData) {
69
+ let pooled = new BN(poolMemberData.points.toString());
70
+ Object.entries(poolMemberData.unbondingEras).forEach(([, amount]) => {
71
+ pooled = pooled.add(new BN(amount));
72
+ });
73
+ pooledStakingBalances.push(pooled);
74
+ } else {
75
+ pooledStakingBalances.push(BN_ZERO);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ const items = balances.map((balance, index) => {
81
+ var _balance$data, _balance$data$free, _balance$data2, _balance$data2$reserv, _balance$data3, _balance$data3$miscFr, _balance$data4, _balance$data4$frozen, _balance$data5, _balance$data5$feeFro;
82
+ let total = ((_balance$data = balance.data) === null || _balance$data === void 0 ? void 0 : (_balance$data$free = _balance$data.free) === null || _balance$data$free === void 0 ? void 0 : _balance$data$free.toBn()) || new BN(0);
83
+ const reserved = ((_balance$data2 = balance.data) === null || _balance$data2 === void 0 ? void 0 : (_balance$data2$reserv = _balance$data2.reserved) === null || _balance$data2$reserv === void 0 ? void 0 : _balance$data2$reserv.toBn()) || new BN(0);
84
+ // @ts-ignore
85
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
86
+ const miscFrozen = ((_balance$data3 = balance.data) === null || _balance$data3 === void 0 ? void 0 : (_balance$data3$miscFr = _balance$data3.miscFrozen) === null || _balance$data3$miscFr === void 0 ? void 0 : _balance$data3$miscFr.toBn()) || (balance === null || balance === void 0 ? void 0 : (_balance$data4 = balance.data) === null || _balance$data4 === void 0 ? void 0 : (_balance$data4$frozen = _balance$data4.frozen) === null || _balance$data4$frozen === void 0 ? void 0 : _balance$data4$frozen.toBn()) || new BN(0);
87
+ const feeFrozen = ((_balance$data5 = balance.data) === null || _balance$data5 === void 0 ? void 0 : (_balance$data5$feeFro = _balance$data5.feeFrozen) === null || _balance$data5$feeFro === void 0 ? void 0 : _balance$data5$feeFro.toBn()) || new BN(0);
88
+ let locked = reserved.add(miscFrozen);
89
+ total = total.add(reserved);
90
+ const pooledStakingBalance = pooledStakingBalances[index] || BN_ZERO;
91
+ if (pooledStakingBalance.gt(BN_ZERO)) {
92
+ total = total.add(pooledStakingBalance);
93
+ locked = locked.add(pooledStakingBalance);
94
+ }
95
+ const free = total.sub(locked);
96
+ return {
97
+ address: addresses[index],
98
+ tokenSlug: chainNativeTokenSlug,
99
+ free: free.gte(BN_ZERO) ? free.toString() : '0',
100
+ locked: locked.toString(),
101
+ state: APIItemState.READY,
102
+ substrateInfo: {
103
+ miscFrozen: miscFrozen.toString(),
104
+ reserved: reserved.toString(),
105
+ feeFrozen: feeFrozen.toString()
106
+ }
107
+ };
108
+ });
109
+ callBack(items);
110
+ });
111
+ return () => {
112
+ unsub();
113
+ };
114
+ }
115
+ function subscribePSP22Balance(addresses, chain, api, callBack) {
116
+ let tokenList = {};
117
+ const psp22ContractMap = {};
118
+ const getTokenBalances = () => {
119
+ Object.values(tokenList).map(async tokenInfo => {
120
+ try {
121
+ const contract = psp22ContractMap[tokenInfo.slug];
122
+ const balances = await Promise.all(addresses.map(async address => {
123
+ try {
124
+ var _balanceOf$output, _ref;
125
+ const _balanceOf = await contract.query['psp22::balanceOf'](address, {
126
+ gasLimit: getDefaultWeightV2(api)
127
+ }, address);
128
+ const balanceObj = _balanceOf === null || _balanceOf === void 0 ? void 0 : (_balanceOf$output = _balanceOf.output) === null || _balanceOf$output === void 0 ? void 0 : _balanceOf$output.toPrimitive();
129
+ return {
130
+ address: address,
131
+ tokenSlug: tokenInfo.slug,
132
+ free: _balanceOf.output ? (_ref = balanceObj.ok) !== null && _ref !== void 0 ? _ref : balanceObj.Ok : '0',
133
+ locked: '0',
134
+ state: APIItemState.READY
135
+ };
136
+ } catch (err) {
137
+ console.error(`Error on get balance of account ${address} for token ${tokenInfo.slug}`, err);
138
+ return {
139
+ address: address,
140
+ tokenSlug: tokenInfo.slug,
141
+ free: '0',
142
+ locked: '0',
143
+ state: APIItemState.READY
144
+ };
145
+ }
146
+ }));
147
+ callBack(balances);
148
+ } catch (err) {
149
+ console.warn(tokenInfo.slug, err); // TODO: error createType
150
+ }
151
+ });
152
+ };
153
+
154
+ tokenList = state.getAssetByChainAndAsset(chain, [_AssetType.PSP22]);
155
+ Object.entries(tokenList).forEach(([slug, tokenInfo]) => {
156
+ psp22ContractMap[slug] = getPSP22ContractPromise(api, _getContractAddressOfToken(tokenInfo));
157
+ });
158
+ getTokenBalances();
159
+ const interval = setInterval(getTokenBalances, SUB_TOKEN_REFRESH_BALANCE_INTERVAL);
160
+ return () => {
161
+ clearInterval(interval);
162
+ };
163
+ }
164
+ async function subscribeTokensAccountsPallet(addresses, chain, api, callBack, includeNativeToken) {
165
+ const tokenTypes = includeNativeToken ? [_AssetType.NATIVE, _AssetType.LOCAL] : [_AssetType.LOCAL];
166
+ const tokenMap = state.getAssetByChainAndAsset(chain, tokenTypes);
167
+ const unsubList = await Promise.all(Object.values(tokenMap).map(async tokenInfo => {
168
+ try {
169
+ const onChainInfo = _getTokenOnChainInfo(tokenInfo);
170
+ const assetId = _getTokenOnChainAssetId(tokenInfo);
171
+
172
+ // Get Token Balance
173
+ // @ts-ignore
174
+ return await api.query.tokens.accounts.multi(addresses.map(address => [address, onChainInfo || assetId]), balances => {
175
+ const items = balances.map((balance, index) => {
176
+ const tokenBalance = {
177
+ reserved: balance.reserved || new BN(0),
178
+ frozen: balance.frozen || new BN(0),
179
+ free: balance.free || new BN(0) // free is actually total balance
180
+ };
181
+
182
+ const freeBalance = tokenBalance.free.sub(tokenBalance.frozen);
183
+ const lockedBalance = tokenBalance.frozen.add(tokenBalance.reserved);
184
+ return {
185
+ address: addresses[index],
186
+ tokenSlug: tokenInfo.slug,
187
+ state: APIItemState.READY,
188
+ free: freeBalance.toString(),
189
+ locked: lockedBalance.toString(),
190
+ substrateInfo: {
191
+ reserved: tokenBalance.reserved.toString(),
192
+ miscFrozen: tokenBalance.frozen.toString()
193
+ }
194
+ };
195
+ });
196
+ callBack(items);
197
+ });
198
+ } catch (err) {
199
+ console.warn(err);
200
+ }
201
+ return undefined;
202
+ }));
203
+ return () => {
204
+ unsubList.forEach(unsub => {
205
+ unsub && unsub();
206
+ });
207
+ };
208
+ }
209
+ async function subscribeAssetsAccountPallet(addresses, chain, api, callBack) {
210
+ const tokenMap = state.getAssetByChainAndAsset(chain, [_AssetType.LOCAL]);
211
+ Object.values(tokenMap).forEach(token => {
212
+ if (_MANTA_ZK_CHAIN_GROUP.includes(token.originChain) && token.symbol.startsWith(_ZK_ASSET_PREFIX)) {
213
+ delete tokenMap[token.slug];
214
+ }
215
+ });
216
+ const unsubList = await Promise.all(Object.values(tokenMap).map(async tokenInfo => {
217
+ try {
218
+ const assetIndex = _getTokenOnChainAssetId(tokenInfo);
219
+
220
+ // Get Token Balance
221
+ return await api.query.assets.account.multi(addresses.map(address => [assetIndex, address]), balances => {
222
+ const items = balances.map((balance, index) => {
223
+ // @ts-ignore
224
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment
225
+ const bdata = balance === null || balance === void 0 ? void 0 : balance.toHuman();
226
+ let frozen = BN_ZERO;
227
+ let total = BN_ZERO;
228
+ if (bdata) {
229
+ // @ts-ignore
230
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument
231
+ const addressBalance = new BN(String(bdata === null || bdata === void 0 ? void 0 : bdata.balance).replaceAll(',', '') || '0');
232
+
233
+ // @ts-ignore
234
+ if (bdata !== null && bdata !== void 0 && bdata.isFrozen) {
235
+ frozen = addressBalance;
236
+ } else {
237
+ total = addressBalance;
238
+ }
239
+ }
240
+ const free = total.sub(frozen);
241
+ return {
242
+ address: addresses[index],
243
+ tokenSlug: tokenInfo.slug,
244
+ free: free.toString(),
245
+ locked: frozen.toString(),
246
+ state: APIItemState.READY,
247
+ substrateInfo: {
248
+ miscFrozen: frozen.toString(),
249
+ reserved: '0'
250
+ }
251
+ };
252
+ });
253
+ callBack(items);
254
+ });
255
+ } catch (err) {
256
+ console.warn(err);
257
+ }
258
+ return undefined;
259
+ }));
260
+ return () => {
261
+ unsubList.forEach(unsub => {
262
+ unsub && unsub();
263
+ });
264
+ };
265
+ }
266
+
267
+ // eslint-disable-next-line @typescript-eslint/require-await
268
+ async function subscribeOrmlTokensPallet(addresses, chain, api, callBack) {
269
+ const tokenTypes = [_AssetType.LOCAL];
270
+ const tokenMap = state.getAssetByChainAndAsset(chain, tokenTypes);
271
+ const unsubList = Object.values(tokenMap).map(async tokenInfo => {
272
+ try {
273
+ const onChainInfo = _getTokenOnChainInfo(tokenInfo);
274
+
275
+ // Get Token Balance
276
+ // @ts-ignore
277
+ const unsub = await api.query.ormlTokens.accounts.multi(addresses.map(address => [address, onChainInfo]), balances => {
278
+ const items = balances.map((balance, index) => {
279
+ const tokenBalance = {
280
+ reserved: balance.reserved || new BN(0),
281
+ frozen: balance.frozen || new BN(0),
282
+ free: balance.free || new BN(0) // free is actually total balance
283
+ };
284
+
285
+ // free balance = total balance - frozen misc
286
+ // locked balance = reserved + frozen misc
287
+ const freeBalance = tokenBalance.free.sub(tokenBalance.frozen);
288
+ const lockedBalance = tokenBalance.frozen.add(tokenBalance.reserved);
289
+ return {
290
+ address: addresses[index],
291
+ tokenSlug: tokenInfo.slug,
292
+ state: APIItemState.READY,
293
+ free: freeBalance.toString(),
294
+ locked: lockedBalance.toString(),
295
+ substrateInfo: {
296
+ reserved: tokenBalance.reserved.toString(),
297
+ miscFrozen: tokenBalance.frozen.toString()
298
+ }
299
+ };
300
+ });
301
+ callBack(items);
302
+ });
303
+ return unsub;
304
+ } catch (err) {
305
+ console.warn(err);
306
+ return undefined;
307
+ }
308
+ });
309
+ return () => {
310
+ unsubList.forEach(subProm => {
311
+ subProm.then(unsub => {
312
+ unsub && unsub();
313
+ }).catch(console.error);
314
+ });
315
+ };
316
+ }
@@ -1,9 +1,28 @@
1
- import { AmountData, BalanceItem } from '@subwallet/extension-base/background/KoniTypes';
2
- import { ChainService } from '@subwallet/extension-base/services/chain-service';
1
+ import { AmountData } from '@subwallet/extension-base/background/KoniTypes';
2
+ import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
3
+ import { BalanceItem } from '@subwallet/extension-base/types';
4
+ /**
5
+ * Balance service
6
+ * @class
7
+ */
3
8
  export declare class BalanceService {
4
- private chainService;
5
- constructor(chainService: ChainService);
9
+ private state;
10
+ /**
11
+ * @constructor
12
+ * @param {KoniState} state - The state of extension.
13
+ */
14
+ constructor(state: KoniState);
6
15
  subscribeTokenFreeBalance(address: string, chain: string, tokenSlug: string | undefined, callback?: (rs: AmountData) => void): Promise<[() => void, AmountData]>;
16
+ /**
17
+ * @public
18
+ * @async
19
+ * @function getTokenFreeBalance
20
+ * @desc Fetch free balance on chain
21
+ * @param {string} address - Address
22
+ * @param {string} chain - Slug of chain
23
+ * @param {string} [tokenSlug] - Slug of token
24
+ * @return {Promise<AmountData>} - Free token balance of address on chain
25
+ */
7
26
  getTokenFreeBalance(address: string, chain: string, tokenSlug?: string): Promise<AmountData>;
8
- subscribeBalance(addresses: string[], chains: string[] | null, callback: (rs: BalanceItem) => void): () => void;
27
+ subscribeBalance(addresses: string[], chains: string[] | null, _callback: (rs: BalanceItem) => void): () => void;
9
28
  }
@@ -3,16 +3,25 @@
3
3
 
4
4
  import { BalanceError } from '@subwallet/extension-base/background/errors/BalanceError';
5
5
  import { BalanceErrorType } from '@subwallet/extension-base/background/KoniTypes';
6
- import { subscribeEVMBalance, subscribeSubstrateBalance } from '@subwallet/extension-base/koni/api/dotsama/balance';
7
- import { state } from '@subwallet/extension-base/koni/background/handlers';
6
+ import { groupBalance } from '@subwallet/extension-base/services/balance-service/helpers/group';
7
+ import { subscribeEVMBalance } from '@subwallet/extension-base/services/balance-service/helpers/subscribe/evm';
8
+ import { subscribeSubstrateBalance } from '@subwallet/extension-base/services/balance-service/helpers/subscribe/substrate';
8
9
  import { _PURE_EVM_CHAINS } from '@subwallet/extension-base/services/chain-service/constants';
9
10
  import { _getChainNativeTokenSlug, _isChainEvmCompatible, _isPureEvmChain } from '@subwallet/extension-base/services/chain-service/utils';
10
11
  import { categoryAddresses } from '@subwallet/extension-base/utils';
11
12
  import { t } from 'i18next';
12
- export class BalanceService {
13
- constructor(chainService) {
14
- this.chainService = chainService;
15
13
 
14
+ /**
15
+ * Balance service
16
+ * @class
17
+ */
18
+ export class BalanceService {
19
+ /**
20
+ * @constructor
21
+ * @param {KoniState} state - The state of extension.
22
+ */
23
+ constructor(state) {
24
+ this.state = state;
16
25
  // Todo: Load data from db to balanceSubject
17
26
  // Todo: Start subscribe balance and data
18
27
  // Todo: Listen change and apply to balanceSubject
@@ -23,9 +32,10 @@ export class BalanceService {
23
32
  // Todo: Move everything of fetching balance to this service
24
33
  }
25
34
 
35
+ /* Subscribe token free balance on chain */
26
36
  async subscribeTokenFreeBalance(address, chain, tokenSlug, callback) {
27
- const chainInfo = this.chainService.getChainInfoByKey(chain);
28
- const chainState = this.chainService.getChainStateByKey(chain);
37
+ const chainInfo = this.state.chainService.getChainInfoByKey(chain);
38
+ const chainState = this.state.chainService.getChainStateByKey(chain);
29
39
  if (!chainInfo || !chainState || !chainState.active) {
30
40
  return Promise.reject(new BalanceError(BalanceErrorType.NETWORK_ERROR, t('{{chain}} is inactive. Please enable network', {
31
41
  replace: {
@@ -34,7 +44,7 @@ export class BalanceService {
34
44
  })));
35
45
  }
36
46
  const tSlug = tokenSlug || _getChainNativeTokenSlug(chainInfo);
37
- const tokenInfo = this.chainService.getAssetBySlug(tSlug);
47
+ const tokenInfo = this.state.chainService.getAssetBySlug(tSlug);
38
48
  if (!tokenInfo) {
39
49
  return Promise.reject(new BalanceError(BalanceErrorType.TOKEN_ERROR, t('Transfer is currently not available for this token: {{tSlug}}', {
40
50
  replace: {
@@ -69,28 +79,44 @@ export class BalanceService {
69
79
  }, 9999);
70
80
  });
71
81
  }
82
+
83
+ /**
84
+ * @public
85
+ * @async
86
+ * @function getTokenFreeBalance
87
+ * @desc Fetch free balance on chain
88
+ * @param {string} address - Address
89
+ * @param {string} chain - Slug of chain
90
+ * @param {string} [tokenSlug] - Slug of token
91
+ * @return {Promise<AmountData>} - Free token balance of address on chain
92
+ */
72
93
  async getTokenFreeBalance(address, chain, tokenSlug) {
73
94
  const [, balance] = await this.subscribeTokenFreeBalance(address, chain, tokenSlug);
74
95
  return balance;
75
96
  }
76
- subscribeBalance(addresses, chains, callback) {
97
+ subscribeBalance(addresses, chains, _callback) {
77
98
  const [substrateAddresses, evmAddresses] = categoryAddresses(addresses);
78
- const chainInfoMap = this.chainService.getChainInfoMap();
79
- const chainStateMap = this.chainService.getChainStateMap();
80
- const substrateApiMap = this.chainService.getSubstrateApiMap();
81
- const evmApiMap = this.chainService.getEvmApiMap();
99
+ const chainInfoMap = this.state.chainService.getChainInfoMap();
100
+ const chainStateMap = this.state.chainService.getChainStateMap();
101
+ const substrateApiMap = this.state.chainService.getSubstrateApiMap();
102
+ const evmApiMap = this.state.chainService.getEvmApiMap();
82
103
 
83
104
  // Get data from chain or all chains
84
105
  const chainList = chains || Object.keys(chainInfoMap);
85
106
  // Filter active chain only
86
107
  const useChainInfos = chainList.filter(c => chainStateMap[c] && chainStateMap[c].active).map(c => chainInfoMap[c]);
108
+ const callback = items => {
109
+ if (items.length) {
110
+ _callback(groupBalance(items, 'GROUPED', items[0].tokenSlug));
111
+ }
112
+ };
87
113
 
88
114
  // Looping over each chain
89
115
  const unsubList = useChainInfos.map(async chainInfo => {
90
116
  const chainSlug = chainInfo.slug;
91
117
  const useAddresses = _isChainEvmCompatible(chainInfo) ? evmAddresses : substrateAddresses;
92
118
  if (_isPureEvmChain(chainInfo)) {
93
- const nativeTokenInfo = state.getNativeTokenInfo(chainSlug);
119
+ const nativeTokenInfo = this.state.getNativeTokenInfo(chainSlug);
94
120
  return subscribeEVMBalance(chainSlug, useAddresses, evmApiMap, callback, nativeTokenInfo);
95
121
  }
96
122
  if (!useAddresses || useAddresses.length === 0 || _PURE_EVM_CHAINS.indexOf(chainSlug) > -1) {
@@ -13,6 +13,12 @@ function paramJsonParse(item) {
13
13
  return [];
14
14
  }
15
15
  }
16
+ function autoAddPublicKeyPrefix(publicKey) {
17
+ if (!publicKey.startsWith('0x')) {
18
+ return `0x${publicKey}`;
19
+ }
20
+ return publicKey;
21
+ }
16
22
  function balanceTransferParserFunction(item) {
17
23
  const params = paramJsonParse(item);
18
24
  params.forEach(p => {
@@ -20,7 +26,7 @@ function balanceTransferParserFunction(item) {
20
26
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
21
27
  const toPublicKey = p.value.id || p.value.Id;
22
28
  if (toPublicKey) {
23
- item.to = encodeAddress(toPublicKey, 42);
29
+ item.to = encodeAddress(autoAddPublicKeyPrefix(toPublicKey), 42);
24
30
  }
25
31
  } else if (p.name === 'value') {
26
32
  if (item.amount) {
@@ -76,7 +76,7 @@ export class HistoryService {
76
76
  const excludeExtrinsicParserKeys = ['balances.transfer_all'];
77
77
 
78
78
  // Note: fetchAllPossibleExtrinsicItems and fetchAllPossibleTransferItems-receive can run parallelly
79
- // Hover, fetchAllPossibleTransferItems-sent must run after fetchAllPossibleExtrinsicItems,
79
+ // However, fetchAllPossibleTransferItems-sent must run after fetchAllPossibleExtrinsicItems,
80
80
  // to avoid "duplicate Extrinsic Hash between items" problem
81
81
 
82
82
  this.subscanService.fetchAllPossibleExtrinsicItems(chain, address, extrinsicItems => {
@@ -97,11 +97,12 @@ export class HistoryService {
97
97
  excludeTransferExtrinsicHash.push(x.extrinsic_hash);
98
98
  }
99
99
  });
100
- this.subscanService.fetchAllPossibleTransferItems(chain, address, 'sent', transferItems => {
100
+ this.subscanService.fetchAllPossibleTransferItems(chain, address, 'sent').then(rsMap => {
101
101
  const result = [];
102
- transferItems.forEach(t => {
103
- if (!excludeTransferExtrinsicHash.includes(t.hash)) {
104
- result.push(parseSubscanTransferData(address, t, chainInfo));
102
+ Object.keys(rsMap).forEach(hash => {
103
+ // only push item that does not have same hash with another item
104
+ if (!excludeTransferExtrinsicHash.includes(hash) && rsMap[hash].length === 1) {
105
+ result.push(parseSubscanTransferData(address, rsMap[hash][0], chainInfo));
105
106
  }
106
107
  });
107
108
  this.addHistoryItems(result).catch(e => {
@@ -113,10 +114,13 @@ export class HistoryService {
113
114
  }).catch(e => {
114
115
  console.log('fetchAllPossibleExtrinsicItems error', e);
115
116
  });
116
- this.subscanService.fetchAllPossibleTransferItems(chain, address, 'received', transferItems => {
117
+ this.subscanService.fetchAllPossibleTransferItems(chain, address, 'received').then(rsMap => {
117
118
  const result = [];
118
- transferItems.forEach(t => {
119
- result.push(parseSubscanTransferData(address, t, chainInfo));
119
+ Object.keys(rsMap).forEach(hash => {
120
+ // only push item that does not have same hash with another item
121
+ if (rsMap[hash].length === 1) {
122
+ result.push(parseSubscanTransferData(address, rsMap[hash][0], chainInfo));
123
+ }
120
124
  });
121
125
  this.addHistoryItems(result).catch(e => {
122
126
  console.log('addHistoryItems in fetchAllPossibleTransferItems-receive error', e);
@@ -7,7 +7,7 @@ export default class MigrateProvider extends BaseMigrationJob {
7
7
  const state = this.state;
8
8
  const chainState = state.getChainStateByKey(this.slug);
9
9
  const chainInfo = state.getChainInfo(this.slug);
10
- if (chainState.active && chainState.currentProvider === this.oldProvider) {
10
+ if (chainState && chainState.active && chainState.currentProvider === this.oldProvider) {
11
11
  await state.upsertChainInfo({
12
12
  mode: 'update',
13
13
  chainEditInfo: {