@subwallet/extension-base 1.3.30-0 → 1.3.31-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.
Files changed (73) hide show
  1. package/background/KoniTypes.d.ts +12 -0
  2. package/cjs/constants/paraspell-chain-map.js +13 -0
  3. package/cjs/core/logic-validation/transfer.js +13 -1
  4. package/cjs/core/substrate/xcm-parser.js +5 -1
  5. package/cjs/core/utils.js +36 -15
  6. package/cjs/koni/background/handlers/Extension.js +136 -90
  7. package/cjs/koni/background/handlers/State.js +8 -1
  8. package/cjs/packageInfo.js +1 -1
  9. package/cjs/services/balance-service/helpers/process.js +27 -0
  10. package/cjs/services/balance-service/index.js +9 -0
  11. package/cjs/services/balance-service/transfer/xcm/acrossBridge/index.js +229 -0
  12. package/cjs/services/balance-service/transfer/xcm/index.js +96 -7
  13. package/cjs/services/balance-service/transfer/xcm/utils.js +213 -0
  14. package/cjs/services/chain-service/constants.js +2 -4
  15. package/cjs/services/chain-service/index.js +71 -17
  16. package/cjs/services/chain-service/utils/patch.js +1 -1
  17. package/cjs/services/earning-service/handlers/special.js +18 -9
  18. package/cjs/services/keyring-service/context/handlers/Ledger.js +1 -1
  19. package/cjs/services/keyring-service/context/state.js +3 -0
  20. package/cjs/services/migration-service/scripts/DisableZeroBalanceTokens.js +60 -0
  21. package/cjs/services/migration-service/scripts/EnableChain.js +1 -1
  22. package/cjs/services/migration-service/scripts/index.js +3 -2
  23. package/cjs/services/swap-service/handler/base-handler.js +24 -11
  24. package/cjs/services/transaction-service/utils.js +38 -14
  25. package/cjs/utils/fee/transfer.js +52 -28
  26. package/cjs/utils/staticData/index.js +7 -2
  27. package/constants/paraspell-chain-map.d.ts +1 -0
  28. package/constants/paraspell-chain-map.js +7 -0
  29. package/core/logic-validation/transfer.d.ts +1 -0
  30. package/core/logic-validation/transfer.js +12 -1
  31. package/core/substrate/xcm-parser.d.ts +1 -0
  32. package/core/substrate/xcm-parser.js +4 -1
  33. package/core/utils.d.ts +2 -2
  34. package/core/utils.js +36 -15
  35. package/koni/background/handlers/Extension.d.ts +1 -0
  36. package/koni/background/handlers/Extension.js +60 -15
  37. package/koni/background/handlers/State.d.ts +1 -0
  38. package/koni/background/handlers/State.js +7 -1
  39. package/package.json +23 -7
  40. package/packageInfo.js +1 -1
  41. package/services/balance-service/helpers/process.d.ts +2 -1
  42. package/services/balance-service/helpers/process.js +26 -0
  43. package/services/balance-service/index.js +11 -2
  44. package/services/balance-service/transfer/xcm/acrossBridge/index.d.ts +15 -0
  45. package/services/balance-service/transfer/xcm/acrossBridge/index.js +216 -0
  46. package/services/balance-service/transfer/xcm/index.d.ts +5 -1
  47. package/services/balance-service/transfer/xcm/index.js +85 -1
  48. package/services/balance-service/transfer/xcm/utils.d.ts +11 -0
  49. package/services/balance-service/transfer/xcm/utils.js +208 -0
  50. package/services/chain-service/constants.d.ts +0 -1
  51. package/services/chain-service/constants.js +1 -2
  52. package/services/chain-service/index.d.ts +9 -2
  53. package/services/chain-service/index.js +72 -18
  54. package/services/chain-service/utils/patch.js +1 -1
  55. package/services/earning-service/handlers/special.js +19 -10
  56. package/services/keyring-service/context/handlers/Ledger.js +1 -1
  57. package/services/keyring-service/context/state.d.ts +1 -0
  58. package/services/keyring-service/context/state.js +3 -0
  59. package/services/migration-service/scripts/DisableZeroBalanceTokens.d.ts +4 -0
  60. package/services/migration-service/scripts/DisableZeroBalanceTokens.js +51 -0
  61. package/services/migration-service/scripts/EnableChain.js +1 -1
  62. package/services/migration-service/scripts/index.js +3 -2
  63. package/services/swap-service/handler/base-handler.js +25 -12
  64. package/services/transaction-service/types.d.ts +3 -2
  65. package/services/transaction-service/utils.d.ts +1 -0
  66. package/services/transaction-service/utils.js +38 -15
  67. package/types/balance/transfer.d.ts +1 -0
  68. package/types/transaction/request.d.ts +7 -0
  69. package/utils/fee/transfer.d.ts +1 -0
  70. package/utils/fee/transfer.js +54 -30
  71. package/utils/staticData/index.d.ts +4 -1
  72. package/utils/staticData/index.js +5 -1
  73. package/utils/staticData/paraSpellChainMap.json +1 -0
@@ -1,7 +1,215 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
5
+ import { fetchParaSpellChainMap } from '@subwallet/extension-base/constants/paraspell-chain-map';
6
+ import { BasicTxErrorType } from '@subwallet/extension-base/types';
7
+ import { assert, compactToU8a, isHex, u8aConcat, u8aEq } from '@polkadot/util';
8
+ const paraSpellEndpoint = 'https://api.lightspell.xyz';
9
+ const paraSpellApi = {
10
+ buildXcm: `${paraSpellEndpoint}/x-transfer`,
11
+ dryRunXcm: `${paraSpellEndpoint}/dry-run`
12
+ };
13
+ const paraSpellKey = process.env.PARASPELL_API_KEY || '';
14
+ function txHexToSubmittableExtrinsic(api, hex) {
15
+ try {
16
+ assert(isHex(hex), 'Expected a hex-encoded call');
17
+ let extrinsicCall;
18
+ let extrinsicPayload = null;
19
+ let decoded = null;
20
+ try {
21
+ // attempt to decode with api.tx
22
+ const tx = api.tx(hex);
23
+
24
+ // ensure that the full data matches here
25
+ assert(tx.toHex() === hex, 'Cannot decode data as extrinsic, length mismatch');
26
+ decoded = tx;
27
+ extrinsicCall = api.createType('Call', decoded.method);
28
+ } catch {
29
+ try {
30
+ // attempt to decode as Call
31
+ extrinsicCall = api.createType('Call', hex);
32
+ const callHex = extrinsicCall.toHex();
33
+ if (callHex === hex) {
34
+ // ok
35
+ } else if (hex.startsWith(callHex)) {
36
+ // this could be an un-prefixed payload...
37
+ const prefixed = u8aConcat(compactToU8a(extrinsicCall.encodedLength), hex);
38
+ extrinsicPayload = api.createType('ExtrinsicPayload', prefixed);
39
+ assert(u8aEq(extrinsicPayload.toU8a(), prefixed), 'Unable to decode data as un-prefixed ExtrinsicPayload');
40
+ extrinsicCall = api.createType('Call', extrinsicPayload.method.toHex());
41
+ } else {
42
+ console.error('Unable to decode data as Call, length mismatch in supplied data');
43
+ }
44
+ } catch {
45
+ // final attempt, we try this as-is as a (prefixed) payload
46
+ extrinsicPayload = api.createType('ExtrinsicPayload', hex);
47
+ assert(extrinsicPayload.toHex() === hex, 'Unable to decode input data as Call, Extrinsic or ExtrinsicPayload');
48
+ extrinsicCall = api.createType('Call', extrinsicPayload.method.toHex());
49
+ }
50
+ }
51
+ const {
52
+ method,
53
+ section
54
+ } = api.registry.findMetaCall(extrinsicCall.callIndex);
55
+ const extrinsicFn = api.tx[section][method];
56
+ if (!decoded) {
57
+ decoded = extrinsicFn(...extrinsicCall.args);
58
+ }
59
+ return decoded;
60
+ } catch (e) {
61
+ console.error('Failed to decode extrinsic hex', e);
62
+ throw new Error('Failed to decode extrinsic hex');
63
+ }
64
+ }
65
+ export async function buildXcm(request) {
66
+ var _originTokenInfo$meta, _originTokenInfo$meta2;
67
+ const {
68
+ destinationChain,
69
+ originChain,
70
+ originTokenInfo,
71
+ recipient,
72
+ sendingValue,
73
+ substrateApi
74
+ } = request;
75
+ if (!substrateApi) {
76
+ return Promise.reject(new Error('Substrate API is not available'));
77
+ }
78
+ const psAssetType = (_originTokenInfo$meta = originTokenInfo.metadata) === null || _originTokenInfo$meta === void 0 ? void 0 : _originTokenInfo$meta.paraSpellAssetType;
79
+ const psAssetValue = (_originTokenInfo$meta2 = originTokenInfo.metadata) === null || _originTokenInfo$meta2 === void 0 ? void 0 : _originTokenInfo$meta2.paraSpellValue;
80
+ if (!psAssetType || !psAssetValue) {
81
+ throw new Error('Token is not support XCM at this time');
82
+ }
83
+ const paraSpellChainMap = await fetchParaSpellChainMap();
84
+ const bodyData = {
85
+ address: recipient,
86
+ from: paraSpellChainMap[originChain.slug],
87
+ to: paraSpellChainMap[destinationChain.slug],
88
+ currency: createParaSpellCurrency(psAssetType, psAssetValue, sendingValue)
89
+ };
90
+ const response = await fetch(paraSpellApi.buildXcm, {
91
+ method: 'POST',
92
+ body: JSON.stringify(bodyData),
93
+ headers: {
94
+ 'Content-Type': 'application/json',
95
+ Accept: 'application/json',
96
+ 'X-API-KEY': paraSpellKey
97
+ }
98
+ });
99
+ if (!response.ok) {
100
+ const error = await response.json();
101
+ throw new Error(error.message);
102
+ }
103
+ const extrinsicHex = await response.text();
104
+ const chainApi = await substrateApi.isReady;
105
+ return txHexToSubmittableExtrinsic(chainApi.api, extrinsicHex);
106
+ }
107
+
108
+ // dry run can fail due to sender address & amount token
109
+ export async function dryRunXcm(request) {
110
+ var _originTokenInfo$meta3, _originTokenInfo$meta4;
111
+ const {
112
+ destinationChain,
113
+ originChain,
114
+ originTokenInfo,
115
+ recipient,
116
+ sender,
117
+ sendingValue
118
+ } = request;
119
+ const paraSpellChainMap = await fetchParaSpellChainMap();
120
+ const psAssetType = (_originTokenInfo$meta3 = originTokenInfo.metadata) === null || _originTokenInfo$meta3 === void 0 ? void 0 : _originTokenInfo$meta3.paraSpellAssetType;
121
+ const psAssetValue = (_originTokenInfo$meta4 = originTokenInfo.metadata) === null || _originTokenInfo$meta4 === void 0 ? void 0 : _originTokenInfo$meta4.paraSpellValue;
122
+ if (!psAssetType || !psAssetValue) {
123
+ throw new Error('Token is not support XCM at this time'); // todo: content
124
+ }
125
+
126
+ let dryRunInfo;
127
+ try {
128
+ const bodyData = {
129
+ senderAddress: sender,
130
+ address: recipient,
131
+ from: paraSpellChainMap[originChain.slug],
132
+ to: paraSpellChainMap[destinationChain.slug],
133
+ currency: createParaSpellCurrency(psAssetType, psAssetValue, sendingValue)
134
+ };
135
+ const response = await fetch(paraSpellApi.dryRunXcm, {
136
+ method: 'POST',
137
+ body: JSON.stringify(bodyData),
138
+ headers: {
139
+ 'Content-Type': 'application/json',
140
+ Accept: 'application/json',
141
+ 'X-API-KEY': paraSpellKey
142
+ }
143
+ });
144
+ dryRunInfo = await response.json();
145
+ } catch (e) {
146
+ console.error('Unable to dry run', e);
147
+ }
148
+ if (!dryRunInfo || !dryRunInfo.success) {
149
+ throw new TransactionError(BasicTxErrorType.UNABLE_TO_SEND, 'Unable to perform transaction. Select another token or destination chain and try again');
150
+ }
151
+ return dryRunInfo;
152
+ }
153
+ export async function dryRunXcmV2(request) {
154
+ var _originTokenInfo$meta5, _originTokenInfo$meta6;
155
+ const {
156
+ destinationChain,
157
+ originChain,
158
+ originTokenInfo,
159
+ recipient,
160
+ sender,
161
+ sendingValue
162
+ } = request;
163
+ const paraSpellChainMap = await fetchParaSpellChainMap();
164
+ const psAssetType = (_originTokenInfo$meta5 = originTokenInfo.metadata) === null || _originTokenInfo$meta5 === void 0 ? void 0 : _originTokenInfo$meta5.paraSpellAssetType;
165
+ const psAssetValue = (_originTokenInfo$meta6 = originTokenInfo.metadata) === null || _originTokenInfo$meta6 === void 0 ? void 0 : _originTokenInfo$meta6.paraSpellValue;
166
+ if (!psAssetType || !psAssetValue) {
167
+ throw new Error('Token is not support XCM at this time');
168
+ }
169
+ const bodyData = {
170
+ senderAddress: sender,
171
+ address: recipient,
172
+ from: paraSpellChainMap[originChain.slug],
173
+ to: paraSpellChainMap[destinationChain.slug],
174
+ currency: createParaSpellCurrency(psAssetType, psAssetValue, sendingValue)
175
+ };
176
+ const response = await fetch(paraSpellApi.dryRunXcm, {
177
+ method: 'POST',
178
+ body: JSON.stringify(bodyData),
179
+ headers: {
180
+ 'Content-Type': 'application/json',
181
+ Accept: 'application/json',
182
+ 'X-API-KEY': paraSpellKey
183
+ }
184
+ });
185
+ if (!response.ok) {
186
+ const error = await response.json();
187
+ throw new Error(error.message);
188
+ }
189
+ return await response.json();
190
+ }
191
+ function createParaSpellCurrency(assetType, assetValue, amount) {
192
+ // todo: handle complex conditions for asset has same symbol in a chain: Id, Multi-location, ...
193
+ return {
194
+ [assetType]: assetValue,
195
+ amount
196
+ };
197
+ }
198
+ export function isChainNotSupportPolkadotApi(str) {
199
+ const regex = /(?=.*not yet supported)(?=.*Polkadot API).*/i; // Example: The node Interlay is not yet supported by the Polkadot API.
200
+
201
+ return regex.test(str);
202
+ }
203
+ export function isChainNotSupportDryRun(str) {
204
+ const regex = /(?=.*DryRunApi)(?=.*not available).*/i; // Example: DryRunApi is not available on node Acala
205
+
206
+ return regex.test(str);
207
+ }
208
+
209
+ // todo: remove
4
210
  export const STABLE_XCM_VERSION = 3;
211
+
212
+ // todo: remove
5
213
  export function isUseTeleportProtocol(originChainInfo, destChainInfo, tokenSlug) {
6
214
  const relayChainToSystemChain = ['polkadot'].includes(originChainInfo.slug) && ['statemint'].includes(destChainInfo.slug) || ['kusama'].includes(originChainInfo.slug) && ['statemine'].includes(destChainInfo.slug) || ['rococo'].includes(originChainInfo.slug) && ['rococo_assethub'].includes(destChainInfo.slug) || ['westend'].includes(originChainInfo.slug) && ['westend_assethub'].includes(destChainInfo.slug);
7
215
  const systemChainToRelayChain = ['polkadot'].includes(destChainInfo.slug) && ['statemint'].includes(originChainInfo.slug) || ['kusama'].includes(destChainInfo.slug) && ['statemine'].includes(originChainInfo.slug) || ['rococo'].includes(destChainInfo.slug) && ['rococo_assethub'].includes(originChainInfo.slug) || ['westend'].includes(destChainInfo.slug) && ['westend_assethub'].includes(originChainInfo.slug);
@@ -83,7 +83,6 @@ export declare const _XCM_CHAIN_GROUP: {
83
83
  polkadotXcmSpecialCases: string[];
84
84
  xcmPallet: string[];
85
85
  };
86
- export declare const SUFFICIENT_CHAIN: string[];
87
86
  export declare const _XCM_TYPE: {
88
87
  RP: string;
89
88
  PP: string;
@@ -29,7 +29,7 @@ export const _BALANCE_CHAIN_GROUP = {
29
29
  kintsugi: ['kintsugi', 'interlay', 'kintsugi_test', 'mangatax_para'],
30
30
  genshiro: ['genshiro_testnet', 'genshiro'],
31
31
  equilibrium_parachain: ['equilibrium_parachain'],
32
- bifrost: ['bifrost', 'acala', 'karura', 'acala_testnet', 'pioneer', 'bitcountry', 'bifrost_dot', 'hydradx_main', 'hydradx_rococo', 'pendulum', 'amplitude', 'continuum_network'],
32
+ bifrost: ['bifrost', 'acala', 'karura', 'acala_testnet', 'pioneer', 'bitcountry', 'bifrost_dot', 'hydradx_main', 'hydradx_rococo', 'pendulum', 'amplitude', 'continuum_network', 'truth_network'],
33
33
  statemine: ['statemine', 'astar', 'shiden', 'statemint', 'moonbeam', 'moonbase', 'moonriver', 'crabParachain', 'darwinia2', 'parallel', 'calamari', 'manta_network', 'rococo_assethub', 'liberlandTest', 'liberland', 'dentnet', 'pangolin', 'crust', 'phala', 'shibuya', 'dbcchain', 'westend_assethub'],
34
34
  kusama: ['kusama', 'kintsugi', 'kintsugi_test', 'interlay', 'acala', 'statemint', 'karura', 'bifrost'],
35
35
  // perhaps there are some runtime updates
@@ -278,7 +278,6 @@ export const _XCM_CHAIN_GROUP = {
278
278
  // default is xTokens pallet
279
279
  };
280
280
 
281
- export const SUFFICIENT_CHAIN = ['astar', 'calamari', 'parallel', 'darwinia2', 'crabParachain', 'pangolin', 'statemint', 'moonriver', 'shiden', 'moonbeam', 'statemine', 'liberland', 'dentnet', 'phala', 'crust', 'dbcchain', 'rococo_assethub'];
282
281
  export const _XCM_TYPE = {
283
282
  RP: `${_SubstrateChainType.RELAYCHAIN}-${_SubstrateChainType.PARACHAIN}`,
284
283
  // DMP
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _MultiChainAsset } from '@subwallet/chain-list/types';
3
- import { AssetSetting, MetadataItem, TokenPriorityDetails, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes';
3
+ import { AssetSetting, MetadataItem, SufficientChainsDetails, TokenPriorityDetails, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes';
4
4
  import { MantaPrivateHandler } from '@subwallet/extension-base/services/chain-service/handler/manta/MantaPrivateHandler';
5
5
  import { _ChainApiStatus, _ChainConnectionStatus, _ChainState, _NetworkUpsertParams, _SubstrateApi, _ValidateCustomAssetRequest, _ValidateCustomAssetResponse } from '@subwallet/extension-base/services/chain-service/types';
6
6
  import { EventService } from '@subwallet/extension-base/services/event-service';
@@ -32,6 +32,7 @@ export declare class ChainService {
32
32
  private chainLogoMapSubject;
33
33
  private ledgerGenericAllowChainsSubject;
34
34
  private priorityTokensSubject;
35
+ private sufficientChainsSubject;
35
36
  private store;
36
37
  private assetSettingSubject;
37
38
  private logger;
@@ -39,10 +40,12 @@ export declare class ChainService {
39
40
  get value(): {
40
41
  readonly ledgerGenericAllowChains: string[];
41
42
  readonly priorityTokens: TokenPriorityDetails;
43
+ readonly sufficientChains: SufficientChainsDetails;
42
44
  };
43
45
  get observable(): {
44
46
  readonly ledgerGenericAllowChains: import("rxjs").Observable<string[]>;
45
47
  readonly priorityTokens: import("rxjs").Observable<TokenPriorityDetails>;
48
+ readonly sufficientChains: import("rxjs").Observable<SufficientChainsDetails>;
46
49
  };
47
50
  subscribeSwapRefMap(): Subject<Record<string, _AssetRef>>;
48
51
  get xcmRefMap(): Record<string, _AssetRef>;
@@ -111,8 +114,10 @@ export declare class ChainService {
111
114
  stopCheckLatestChainData(): void;
112
115
  handleLatestChainData(latestChainInfo: _ChainInfo[]): void;
113
116
  autoEnableTokens(): Promise<void>;
117
+ enablePopularTokens(): Promise<void>;
114
118
  handleLatestLedgerGenericAllowChains(latestledgerGenericAllowChains: string[]): void;
115
119
  handleLatestPriorityTokens(latestPriorityTokens: TokenPriorityDetails): void;
120
+ handleLatestSufficientChains(latestSufficientChains: SufficientChainsDetails): void;
116
121
  handleLatestData(): void;
117
122
  private initApis;
118
123
  initSingleApi(slug: string): Promise<boolean>;
@@ -127,6 +132,7 @@ export declare class ChainService {
127
132
  private fetchLatestPriceIdsData;
128
133
  private fetchLatestLedgerGenericAllowChains;
129
134
  private fetchLatestPriorityTokens;
135
+ private fetchLatestSufficientChains;
130
136
  private initChains;
131
137
  private initAssetRegistry;
132
138
  private updateChainStateMapSubscription;
@@ -148,13 +154,14 @@ export declare class ChainService {
148
154
  refreshEvmApi(slug: string): void;
149
155
  stopAllChainApis(): Promise<void>;
150
156
  resumeAllChainApis(): Promise<void>;
151
- initAssetSettings(): Promise<void>;
157
+ initAssetSettings(): void;
152
158
  setAssetSettings(assetSettings: Record<string, AssetSetting>, emitEvent?: boolean): void;
153
159
  setMantaZkAssetSettings(visible: boolean): void;
154
160
  getStoreAssetSettings(): Promise<Record<string, AssetSetting>>;
155
161
  getAssetSettings(): Promise<Record<string, AssetSetting>>;
156
162
  updateAssetSetting(assetSlug: string, assetSetting: AssetSetting, autoEnableNativeToken?: boolean): Promise<boolean | undefined>;
157
163
  updateAssetSettingByChain(chainSlug: string, visible: boolean): Promise<void>;
164
+ updatePriorityAssetsByChain(chainSlug: string, visible: boolean): Promise<void>;
158
165
  subscribeAssetSettings(): BehaviorSubject<Record<string, AssetSetting>>;
159
166
  getAssetLogoMap(): Record<string, string>;
160
167
  subscribeAssetLogoMap(): BehaviorSubject<Record<string, string>>;
@@ -11,7 +11,7 @@ import { SubstrateChainHandler } from '@subwallet/extension-base/services/chain-
11
11
  import { TonChainHandler } from '@subwallet/extension-base/services/chain-service/handler/TonChainHandler';
12
12
  import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chain-service/handler/types';
13
13
  import { _ChainConnectionStatus, _CUSTOM_PREFIX, _NFT_CONTRACT_STANDARDS, _SMART_CONTRACT_STANDARDS } from '@subwallet/extension-base/services/chain-service/types';
14
- import { _getAssetOriginChain, _getTokenOnChainAssetId, _isAssetAutoEnable, _isAssetCanPayTxFee, _isAssetFungibleToken, _isChainEnabled, _isCustomAsset, _isCustomChain, _isCustomProvider, _isEqualContractAddress, _isEqualSmartContractAsset, _isLocalToken, _isMantaZkAsset, _isPureEvmChain, _isPureSubstrateChain, _parseAssetRefKey, randomizeProvider, updateLatestChainInfo } from '@subwallet/extension-base/services/chain-service/utils';
14
+ import { _getAssetOriginChain, _getTokenOnChainAssetId, _isAssetAutoEnable, _isAssetCanPayTxFee, _isAssetFungibleToken, _isChainEnabled, _isCustomAsset, _isCustomChain, _isCustomProvider, _isEqualContractAddress, _isEqualSmartContractAsset, _isLocalToken, _isMantaZkAsset, _isNativeToken, _isPureEvmChain, _isPureSubstrateChain, _parseAssetRefKey, randomizeProvider, updateLatestChainInfo } from '@subwallet/extension-base/services/chain-service/utils';
15
15
  import { MYTHOS_MIGRATION_KEY } from '@subwallet/extension-base/services/migration-service/scripts';
16
16
  import AssetSettingStore from '@subwallet/extension-base/stores/AssetSetting';
17
17
  import { addLazy, calculateMetadataHash, fetchStaticData, filterAssetsByChainAndType, getShortMetadata, MODULE_SUPPORT } from '@subwallet/extension-base/utils';
@@ -50,6 +50,7 @@ export class ChainService {
50
50
  chainLogoMapSubject = new BehaviorSubject(ChainLogoMap);
51
51
  ledgerGenericAllowChainsSubject = new BehaviorSubject([]);
52
52
  priorityTokensSubject = new BehaviorSubject({});
53
+ sufficientChainsSubject = new BehaviorSubject({});
53
54
 
54
55
  // Todo: Update to new store indexed DB
55
56
  store = new AssetSettingStore();
@@ -75,24 +76,32 @@ export class ChainService {
75
76
  get value() {
76
77
  const ledgerGenericAllowChains = this.ledgerGenericAllowChainsSubject;
77
78
  const priorityTokens = this.priorityTokensSubject;
79
+ const sufficientChains = this.sufficientChainsSubject;
78
80
  return {
79
81
  get ledgerGenericAllowChains() {
80
82
  return ledgerGenericAllowChains.value;
81
83
  },
82
84
  get priorityTokens() {
83
85
  return priorityTokens.value;
86
+ },
87
+ get sufficientChains() {
88
+ return sufficientChains.value;
84
89
  }
85
90
  };
86
91
  }
87
92
  get observable() {
88
93
  const ledgerGenericAllowChains = this.ledgerGenericAllowChainsSubject;
89
94
  const priorityTokens = this.priorityTokensSubject;
95
+ const sufficientChains = this.sufficientChainsSubject;
90
96
  return {
91
97
  get ledgerGenericAllowChains() {
92
98
  return ledgerGenericAllowChains.asObservable();
93
99
  },
94
100
  get priorityTokens() {
95
101
  return priorityTokens.asObservable();
102
+ },
103
+ get sufficientChains() {
104
+ return sufficientChains.asObservable();
96
105
  }
97
106
  };
98
107
  }
@@ -526,7 +535,7 @@ export class ChainService {
526
535
  this.initAssetRefMap();
527
536
  this.xcmRefMapSubject.next(this.xcmRefMap);
528
537
  await this.initApis();
529
- await this.initAssetSettings();
538
+ this.initAssetSettings();
530
539
  await this.autoEnableTokens();
531
540
  }
532
541
  initAssetRefMap() {
@@ -605,14 +614,47 @@ export class ChainService {
605
614
  }
606
615
  }
607
616
  }
617
+ async enablePopularTokens() {
618
+ const assetSettings = this.assetSettingSubject.value;
619
+ const chainStateMap = this.getChainStateMap();
620
+ const priorityTokensMap = this.priorityTokensSubject.value || {};
621
+ const priorityTokensList = priorityTokensMap.token && typeof priorityTokensMap.token === 'object' ? Object.keys(priorityTokensMap.token) : [];
622
+ for (const assetSlug of priorityTokensList) {
623
+ const assetState = assetSettings[assetSlug];
624
+ const assetInfo = this.getAssetBySlug(assetSlug);
625
+ const chainState = chainStateMap[assetInfo.originChain];
626
+ if (!assetState) {
627
+ // If this asset not has asset setting, this token is not enabled before (not turned off before)
628
+ if (!chainState || !chainState.manualTurnOff) {
629
+ await this.updateAssetSetting(assetSlug, {
630
+ visible: true
631
+ }, true);
632
+ }
633
+ }
634
+ }
635
+ }
608
636
  handleLatestLedgerGenericAllowChains(latestledgerGenericAllowChains) {
609
637
  this.ledgerGenericAllowChainsSubject.next(latestledgerGenericAllowChains);
610
638
  this.eventService.emit('ledger.ready', true);
611
639
  this.logger.log('Finished updating latest ledger generic allow chains');
612
640
  }
613
641
  handleLatestPriorityTokens(latestPriorityTokens) {
642
+ const currentTokens = this.priorityTokensSubject.value || {};
614
643
  this.priorityTokensSubject.next(latestPriorityTokens);
615
644
  this.logger.log('Finished updating latest popular tokens');
645
+ const currentTokenKeys = Object.keys(currentTokens.token || {}); // Extract keys from current tokens
646
+ const newTokenKeys = Object.keys(latestPriorityTokens.token || {}); // Extract keys from new tokens
647
+
648
+ if (JSON.stringify(currentTokenKeys) !== JSON.stringify(newTokenKeys)) {
649
+ // Check if token keys have changed
650
+ this.enablePopularTokens().then(() => this.logger.log('Popular tokens enabled due to priority tokens change')) // Log success after enabling tokens
651
+ .catch(e => console.error('Error enabling popular tokens:', e)); // Log error if enabling fails
652
+ }
653
+ }
654
+
655
+ handleLatestSufficientChains(latestSufficientChains) {
656
+ this.sufficientChainsSubject.next(latestSufficientChains);
657
+ this.logger.log('Finished updating latest supported sufficient chains');
616
658
  }
617
659
  handleLatestData() {
618
660
  this.fetchLatestChainData().then(latestChainInfo => {
@@ -634,6 +676,9 @@ export class ChainService {
634
676
  this.fetchLatestPriorityTokens().then(latestPriorityTokens => {
635
677
  this.handleLatestPriorityTokens(latestPriorityTokens);
636
678
  }).catch(console.error);
679
+ this.fetchLatestSufficientChains().then(latestSufficientChains => {
680
+ this.handleLatestSufficientChains(latestSufficientChains);
681
+ }).catch(console.error);
637
682
  }
638
683
  async initApis() {
639
684
  const chainInfoMap = this.getChainInfoMap();
@@ -913,6 +958,9 @@ export class ChainService {
913
958
  token: {}
914
959
  };
915
960
  }
961
+ async fetchLatestSufficientChains() {
962
+ return (await fetchStaticData('chains/supported-sufficient-chains')) || [];
963
+ }
916
964
  async initChains() {
917
965
  const storedChainSettings = await this.dbService.getAllChainStore();
918
966
  const defaultChainInfoMap = filterChainInfoMap(ChainInfoMap, ignoredList);
@@ -1616,22 +1664,7 @@ export class ChainService {
1616
1664
  await Promise.all([this.substrateChainHandler.wakeUp(), this.evmChainHandler.wakeUp(), this.tonChainHandler.wakeUp(), this.cardanoChainHandler.wakeUp()]);
1617
1665
  this.checkLatestData();
1618
1666
  }
1619
- async initAssetSettings() {
1620
- const assetSettings = await this.getAssetSettings();
1621
- const activeChainSlugs = this.getActiveChainSlugs();
1622
- const assetRegistry = this.getAssetRegistry();
1623
- Object.values(assetRegistry).forEach(assetInfo => {
1624
- const isSettingExisted = (assetInfo.slug in assetSettings);
1625
-
1626
- // Set visible for every enabled chains
1627
- if (activeChainSlugs.includes(assetInfo.originChain) && !isSettingExisted) {
1628
- // Setting only exist when set either by chain settings or user
1629
- assetSettings[assetInfo.slug] = {
1630
- visible: true
1631
- };
1632
- }
1633
- });
1634
- this.setAssetSettings(assetSettings, false);
1667
+ initAssetSettings() {
1635
1668
  this.eventService.emit('asset.ready', true);
1636
1669
  }
1637
1670
  setAssetSettings(assetSettings, emitEvent = true) {
@@ -1719,6 +1752,27 @@ export class ChainService {
1719
1752
  });
1720
1753
  this.setAssetSettings(assetSettings);
1721
1754
  }
1755
+ async updatePriorityAssetsByChain(chainSlug, visible) {
1756
+ const currentAssetSettings = await this.getAssetSettings();
1757
+ const assetsByChain = this.getFungibleTokensByChain(chainSlug);
1758
+ const priorityTokensMap = this.priorityTokensSubject.value || {};
1759
+ const priorityTokensList = priorityTokensMap.token && typeof priorityTokensMap.token === 'object' ? Object.keys(priorityTokensMap.token) : [];
1760
+ for (const asset of Object.values(assetsByChain)) {
1761
+ if (visible) {
1762
+ const isPriorityToken = priorityTokensList.includes(asset.slug);
1763
+ if (isPriorityToken || _isNativeToken(asset)) {
1764
+ currentAssetSettings[asset.slug] = {
1765
+ visible: true
1766
+ };
1767
+ }
1768
+ } else {
1769
+ currentAssetSettings[asset.slug] = {
1770
+ visible: false
1771
+ };
1772
+ }
1773
+ }
1774
+ this.setAssetSettings(currentAssetSettings);
1775
+ }
1722
1776
  subscribeAssetSettings() {
1723
1777
  return this.assetSettingSubject;
1724
1778
  }
@@ -5,7 +5,7 @@ const PRODUCTION_BRANCHES = ['master', 'webapp', 'webapp-dev'];
5
5
  const branchName = process.env.BRANCH_NAME || 'subwallet-dev';
6
6
  const fetchDomain = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'https://chain-list-assets.subwallet.app' : 'https://dev.sw-chain-list-assets.pages.dev';
7
7
  const fetchFile = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'list.json' : 'preview.json';
8
- const ChainListVersion = '0.2.102'; // update this when build chainlist
8
+ const ChainListVersion = '0.2.103'; // update this when build chainlist
9
9
 
10
10
  // todo: move this interface to chainlist
11
11
 
@@ -5,11 +5,12 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
5
5
  import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
6
6
  import { ALL_ACCOUNT_KEY, XCM_FEE_RATIO, XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants';
7
7
  import { YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
8
- import { createXcmExtrinsic } from '@subwallet/extension-base/services/balance-service/transfer/xcm';
8
+ import { createXcmExtrinsicV2, dryRunXcmExtrinsicV2 } from '@subwallet/extension-base/services/balance-service/transfer/xcm';
9
9
  import { _getAssetDecimals, _getAssetExistentialDeposit, _getAssetName, _getAssetSymbol, _getChainNativeTokenSlug, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
10
10
  import { BasicTxErrorType, YieldStepType, YieldValidationStatus } from '@subwallet/extension-base/types';
11
11
  import { createPromiseHandler, formatNumber } from '@subwallet/extension-base/utils';
12
12
  import { getId } from '@subwallet/extension-base/utils/getId';
13
+ import BigN from 'bignumber.js';
13
14
  import { t } from 'i18next';
14
15
  import { BN, BN_TEN, BN_ZERO, noop } from '@polkadot/util';
15
16
  import BasePoolHandler from "./base.js";
@@ -219,7 +220,7 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
219
220
  const xcmOriginSubstrateApi = await this.state.getSubstrateApi(altInputTokenInfo.originChain).isReady;
220
221
  const id = getId();
221
222
  const feeInfo = await this.state.feeService.subscribeChainFee(id, altChainInfo.slug, 'substrate');
222
- const xcmTransfer = await createXcmExtrinsic({
223
+ const xcmRequest = {
223
224
  sender: address,
224
225
  originTokenInfo: altInputTokenInfo,
225
226
  destinationTokenInfo: inputTokenInfo,
@@ -229,16 +230,20 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
229
230
  originChain: altChainInfo,
230
231
  substrateApi: xcmOriginSubstrateApi,
231
232
  feeInfo
232
- });
233
- const _xcmFeeInfo = await xcmTransfer.paymentInfo(address);
234
- const xcmFeeInfo = _xcmFeeInfo.toPrimitive();
235
- // TODO: calculate fee for destination chain
233
+ };
236
234
 
235
+ // TODO: calculate fee for destination chain
236
+ let xcmFee;
237
+ const xcmFeeByDryRun = await dryRunXcmExtrinsicV2(xcmRequest);
238
+ if (xcmFeeByDryRun.fee) {
239
+ xcmFee = BigN(xcmFeeByDryRun.fee).multipliedBy(XCM_MIN_AMOUNT_RATIO).toFixed(0, 1);
240
+ } else {
241
+ throw new Error('Error estimating XCM fee');
242
+ }
237
243
  const fee = {
238
244
  slug: altInputTokenSlug,
239
- amount: Math.round(xcmFeeInfo.partialFee * XCM_MIN_AMOUNT_RATIO).toString() // TODO
245
+ amount: xcmFee
240
246
  };
241
-
242
247
  let bnTransferAmount = bnAmount.sub(bnInputTokenBalance);
243
248
  if (_isNativeToken(altInputTokenInfo)) {
244
249
  const bnXcmFee = new BN(fee.amount || 0); // xcm fee is paid in native token but swap token is not always native token
@@ -442,7 +447,7 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
442
447
  const bnTotalAmount = bnAmount.sub(bnInputTokenBalance).add(bnXcmFee);
443
448
  const id = getId();
444
449
  const feeInfo = await this.state.feeService.subscribeChainFee(id, originChainInfo.slug, 'substrate');
445
- const extrinsic = await createXcmExtrinsic({
450
+ const xcmRequest = {
446
451
  destinationTokenInfo,
447
452
  originTokenInfo,
448
453
  recipient: address,
@@ -452,7 +457,11 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
452
457
  originChain: originChainInfo,
453
458
  destinationChain: this.chainInfo,
454
459
  feeInfo
455
- });
460
+ };
461
+ const extrinsic = await createXcmExtrinsicV2(xcmRequest);
462
+ if (!extrinsic) {
463
+ throw new Error('Error handling XCM extrinsic');
464
+ }
456
465
  const xcmData = {
457
466
  originNetworkKey: originChainInfo.slug,
458
467
  destinationNetworkKey: destinationTokenInfo.originChain,
@@ -150,7 +150,7 @@ export class AccountLedgerHandler extends AccountBaseHandler {
150
150
  }
151
151
  if (Object.keys(slugMap).length) {
152
152
  for (const chainSlug of Object.keys(slugMap)) {
153
- this.state.enableChain(chainSlug);
153
+ this.state.enableChainWithPriorityAssets(chainSlug);
154
154
  }
155
155
  }
156
156
  return true;
@@ -88,6 +88,7 @@ export declare class AccountState {
88
88
  updateMetadataForProxy(): void;
89
89
  findNetworkKeyByGenesisHash(genesisHash?: string): string | undefined;
90
90
  enableChain(slug: string): void;
91
+ enableChainWithPriorityAssets(slug: string): void;
91
92
  resetWallet(): void;
92
93
  }
93
94
  export {};
@@ -619,6 +619,9 @@ export class AccountState {
619
619
  enableChain(slug) {
620
620
  this.koniState.enableChain(slug, true).catch(console.error);
621
621
  }
622
+ enableChainWithPriorityAssets(slug) {
623
+ this.koniState.enableChainWithPriorityAssets(slug, true).catch(console.error);
624
+ }
622
625
 
623
626
  /* Others */
624
627
 
@@ -0,0 +1,4 @@
1
+ import BaseMigrationJob from '@subwallet/extension-base/services/migration-service/Base';
2
+ export default class DisableZeroBalanceTokens extends BaseMigrationJob {
3
+ run(): Promise<void>;
4
+ }
@@ -0,0 +1,51 @@
1
+ // Copyright 2019-2022 @subwallet/extension-koni authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { _isAssetAutoEnable } from '@subwallet/extension-base/services/chain-service/utils';
5
+ import BaseMigrationJob from '@subwallet/extension-base/services/migration-service/Base';
6
+ import { fetchStaticData } from '@subwallet/extension-base/utils';
7
+ // Usage:
8
+ // 1. Disable tokens with a balance of 0
9
+ // 2. Exclude tokens that belong to the popular list
10
+ // 3. Exclude tokens with the "auto enable" attribute
11
+
12
+ export default class DisableZeroBalanceTokens extends BaseMigrationJob {
13
+ async run() {
14
+ const state = this.state;
15
+ try {
16
+ const rawBalanceMap = await state.dbService.getStoredBalance();
17
+ const tokensList = await state.chainService.getAssetSettings();
18
+ const filteredEnabledTokens = Object.entries(tokensList).reduce((acc, [key, value]) => {
19
+ if (value.visible) {
20
+ acc[key] = value;
21
+ }
22
+ return acc;
23
+ }, {});
24
+ const balanceNonZero = rawBalanceMap.filter(item => {
25
+ return BigInt(item.free) + BigInt(item.locked) > 0;
26
+ });
27
+ const priorityTokensMap = (await fetchStaticData('chain-assets/priority-tokens')) || {
28
+ tokenGroup: {},
29
+ token: {}
30
+ };
31
+ const priorityTokensList = priorityTokensMap.token && typeof priorityTokensMap.token === 'object' ? Object.keys(priorityTokensMap.token) : [];
32
+ const autoEnableTokenSlugs = Object.values(this.state.chainService.getAssetRegistry()).filter(asset => _isAssetAutoEnable(asset)).map(asset => asset.slug);
33
+ // Extract the slugs of tokens with balance > 0
34
+ const nonZeroBalanceSlugs = new Set(balanceNonZero.map(item => item.tokenSlug));
35
+ const updatedSettings = structuredClone(tokensList);
36
+ Object.keys(filteredEnabledTokens).forEach(slug => {
37
+ const hasBalance = nonZeroBalanceSlugs.has(slug);
38
+ const isPopularToken = priorityTokensList.includes(slug);
39
+ const isAutoEnableToken = autoEnableTokenSlugs.includes(slug);
40
+ if (!hasBalance && !isPopularToken && !isAutoEnableToken) {
41
+ updatedSettings[slug] = {
42
+ visible: false
43
+ };
44
+ }
45
+ });
46
+ state.chainService.setAssetSettings(updatedSettings);
47
+ } catch (error) {
48
+ console.error(error);
49
+ }
50
+ }
51
+ }
@@ -5,6 +5,6 @@ import BaseMigrationJob from '@subwallet/extension-base/services/migration-servi
5
5
  export default class EnableChain extends BaseMigrationJob {
6
6
  async run() {
7
7
  const state = this.state;
8
- await state.enableChain(this.slug, true);
8
+ await state.enableChainWithPriorityAssets(this.slug, true);
9
9
  }
10
10
  }