@subwallet/extension-base 1.3.53-0 → 1.3.54-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 (52) hide show
  1. package/background/KoniTypes.d.ts +10 -3
  2. package/cjs/koni/background/handlers/Extension.js +4 -4
  3. package/cjs/koni/background/handlers/State.js +1 -0
  4. package/cjs/packageInfo.js +1 -1
  5. package/cjs/services/balance-service/transfer/xcm/snowBridge.js +21 -10
  6. package/cjs/services/chain-online-service/index.js +168 -15
  7. package/cjs/services/chain-service/index.js +39 -10
  8. package/cjs/services/chain-service/utils/patch.js +2 -2
  9. package/cjs/services/earning-service/constants/chains.js +2 -4
  10. package/cjs/services/earning-service/handlers/base.js +1 -1
  11. package/cjs/services/earning-service/handlers/native-staking/base.js +4 -2
  12. package/cjs/services/earning-service/handlers/native-staking/dtao.js +9 -3
  13. package/cjs/services/earning-service/handlers/native-staking/relay-chain.js +1 -10
  14. package/cjs/services/earning-service/handlers/native-staking/tao.js +24 -14
  15. package/cjs/services/earning-service/service.js +2 -2
  16. package/cjs/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +6 -2
  17. package/cjs/services/migration-service/scripts/databases/MigrateAssetSetting.js +6 -2
  18. package/cjs/services/migration-service/scripts/index.js +2 -2
  19. package/cjs/services/swap-service/index.js +24 -18
  20. package/cjs/services/transaction-service/utils.js +25 -16
  21. package/koni/background/handlers/Extension.d.ts +1 -1
  22. package/koni/background/handlers/Extension.js +4 -4
  23. package/koni/background/handlers/State.js +1 -0
  24. package/package.json +8 -6
  25. package/packageInfo.js +1 -1
  26. package/services/balance-service/transfer/xcm/snowBridge.js +21 -10
  27. package/services/chain-online-service/index.d.ts +4 -1
  28. package/services/chain-online-service/index.js +168 -15
  29. package/services/chain-service/index.js +39 -10
  30. package/services/chain-service/utils/patch.d.ts +1 -1
  31. package/services/chain-service/utils/patch.js +2 -2
  32. package/services/earning-service/constants/chains.d.ts +0 -1
  33. package/services/earning-service/constants/chains.js +1 -2
  34. package/services/earning-service/handlers/base.d.ts +3 -3
  35. package/services/earning-service/handlers/base.js +1 -1
  36. package/services/earning-service/handlers/native-staking/base.js +4 -2
  37. package/services/earning-service/handlers/native-staking/dtao.d.ts +4 -3
  38. package/services/earning-service/handlers/native-staking/dtao.js +9 -3
  39. package/services/earning-service/handlers/native-staking/relay-chain.d.ts +0 -2
  40. package/services/earning-service/handlers/native-staking/relay-chain.js +2 -11
  41. package/services/earning-service/handlers/native-staking/tao.d.ts +1 -0
  42. package/services/earning-service/handlers/native-staking/tao.js +24 -14
  43. package/services/earning-service/service.d.ts +3 -3
  44. package/services/earning-service/service.js +2 -2
  45. package/services/migration-service/scripts/MigrateTransactionHistoryBySymbol.js +6 -2
  46. package/services/migration-service/scripts/databases/MigrateAssetSetting.js +6 -2
  47. package/services/migration-service/scripts/index.js +2 -2
  48. package/services/swap-service/index.js +24 -18
  49. package/services/transaction-service/utils.js +25 -16
  50. package/types/yield/actions/join/submit.d.ts +5 -0
  51. package/types/yield/actions/others.d.ts +7 -2
  52. package/types/yield/info/account/info.d.ts +1 -0
@@ -2,10 +2,11 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { AssetLogoMap, ChainLogoMap } from '@subwallet/chain-list';
5
+ import { _ChainStatus } from '@subwallet/chain-list/types';
5
6
  import { LATEST_CHAIN_PATCH_FETCHING_INTERVAL, md5HashChainAsset, md5HashChainInfo } from '@subwallet/extension-base/services/chain-online-service/constants';
6
7
  import { filterAssetInfoMap } from '@subwallet/extension-base/services/chain-service';
7
8
  import { _ChainConnectionStatus } from '@subwallet/extension-base/services/chain-service/types';
8
- import { fetchPatchData, randomizeProvider } from '@subwallet/extension-base/services/chain-service/utils';
9
+ import { _isCustomAsset, _isCustomChain, _isEqualSmartContractAsset, fetchPatchData, randomizeProvider } from '@subwallet/extension-base/services/chain-service/utils';
9
10
  export class ChainOnlineService {
10
11
  constructor(chainService, settingService, eventService, dbService) {
11
12
  this.chainService = chainService;
@@ -14,6 +15,9 @@ export class ChainOnlineService {
14
15
  this.dbService = dbService;
15
16
  this.firstApplied = false;
16
17
  }
18
+ resetFirstApplied() {
19
+ this.firstApplied = false;
20
+ }
17
21
  validatePatchWithHash(latestPatch) {
18
22
  const {
19
23
  ChainAsset,
@@ -61,7 +65,7 @@ export class ChainOnlineService {
61
65
  providers: _providers,
62
66
  ...info
63
67
  } = _info;
64
- const providers = Object.assign(((_rs$slug = rs[slug]) === null || _rs$slug === void 0 ? void 0 : _rs$slug.providers) || {}, _providers);
68
+ const providers = rs[slug] ? (_rs$slug = rs[slug]) === null || _rs$slug === void 0 ? void 0 : _rs$slug.providers : _providers;
65
69
  rs[slug] = {
66
70
  ...info,
67
71
  providers
@@ -69,6 +73,26 @@ export class ChainOnlineService {
69
73
  }
70
74
  return rs;
71
75
  }
76
+ checkExistedPredefinedChain(latestChainInfoMap, genesisHash, evmChainId) {
77
+ let duplicatedSlug = '';
78
+ if (genesisHash) {
79
+ for (const chainInfo of Object.values(latestChainInfoMap)) {
80
+ if (chainInfo.substrateInfo && chainInfo.substrateInfo.genesisHash === genesisHash) {
81
+ duplicatedSlug = chainInfo.slug;
82
+ }
83
+ }
84
+ } else if (evmChainId) {
85
+ for (const chainInfo of Object.values(latestChainInfoMap)) {
86
+ if (chainInfo.evmInfo && chainInfo.evmInfo.evmChainId === evmChainId) {
87
+ duplicatedSlug = chainInfo.slug;
88
+ }
89
+ }
90
+ }
91
+ return duplicatedSlug;
92
+ }
93
+ generateSlugForSmartContractAsset(originChain, assetType, symbol, contractAddress) {
94
+ return `${originChain}-${assetType}-${symbol}-${contractAddress}`;
95
+ }
72
96
  async handleLatestPatch(latestPatch) {
73
97
  try {
74
98
  var _await$this$settingSe;
@@ -88,31 +112,151 @@ export class ChainOnlineService {
88
112
  let assetRegistry = structuredClone(this.chainService.getAssetRegistry());
89
113
  const currentChainStateMap = structuredClone(this.chainService.getChainStateMap());
90
114
  const currentChainStatusMap = structuredClone(this.chainService.getChainStatusMap());
115
+ const assetSetting = structuredClone(await this.chainService.getAssetSettings());
116
+ const migratedAssetSetting = {};
91
117
  let addedChain = [];
118
+ const customChains = [];
119
+ const deprecatedChains = [];
120
+ const deprecatedChainMap = {};
121
+ const deprecatedAssets = [];
92
122
  if (isSafePatch && (!this.firstApplied || currentPatchVersion !== latestPatchVersion)) {
93
123
  this.firstApplied = true;
94
124
 
95
125
  // 2. merge data map
96
126
  if (latestChainInfo && Object.keys(latestChainInfo).length > 0) {
127
+ const storedChainSettings = await this.dbService.getAllChainStore();
128
+ const storedChainSettingMap = {};
129
+ storedChainSettings.forEach(chainStoredSetting => {
130
+ storedChainSettingMap[chainStoredSetting.slug] = chainStoredSetting;
131
+ });
132
+ if (storedChainSettings.length > 0) {
133
+ for (const [storedSlug, storedChainInfo] of Object.entries(storedChainSettingMap)) {
134
+ if (_isCustomChain(storedSlug)) {
135
+ var _storedChainInfo$subs, _storedChainInfo$evmI;
136
+ // Check if this custom chain duplicates any of the latest chainInfo from patch based on genesisHash (for Substrate) or EVM chain ID.
137
+ const duplicatedDefaultSlug = this.checkExistedPredefinedChain(latestChainInfo, (_storedChainInfo$subs = storedChainInfo.substrateInfo) === null || _storedChainInfo$subs === void 0 ? void 0 : _storedChainInfo$subs.genesisHash, (_storedChainInfo$evmI = storedChainInfo.evmInfo) === null || _storedChainInfo$evmI === void 0 ? void 0 : _storedChainInfo$evmI.evmChainId);
138
+ if (duplicatedDefaultSlug.length > 0) {
139
+ // Add the old custom chain slug to the list of deprecated chains.
140
+ deprecatedChainMap[storedSlug] = duplicatedDefaultSlug;
141
+ deprecatedChains.push(storedSlug);
142
+ const storedChainState = currentChainStateMap[storedSlug];
143
+ const storedChainStatus = currentChainStatusMap[storedSlug];
144
+
145
+ // Update the current chain state to use the new chain slug, inheriting the active/inactive status from custom chain and randomly assigning a provider
146
+ currentChainStateMap[duplicatedDefaultSlug] = {
147
+ slug: duplicatedDefaultSlug,
148
+ active: storedChainState.active,
149
+ currentProvider: randomizeProvider(latestChainInfo[duplicatedDefaultSlug].providers).providerKey,
150
+ manualTurnOff: storedChainState.manualTurnOff
151
+ };
152
+
153
+ // Update the current chain status to use the new chain slug, inheriting the connection status from custom chain and updating the last updated timestamp
154
+ currentChainStatusMap[duplicatedDefaultSlug] = {
155
+ slug: duplicatedDefaultSlug,
156
+ connectionStatus: storedChainStatus.connectionStatus,
157
+ lastUpdated: Date.now()
158
+ };
159
+ customChains.push(duplicatedDefaultSlug);
160
+
161
+ // Remove the deprecated custom chain's info from the old chain info map and the current state/status maps.
162
+ delete oldChainInfoMap[storedSlug];
163
+ delete currentChainStateMap[storedSlug];
164
+ delete currentChainStatusMap[storedSlug];
165
+ }
166
+ }
167
+ }
168
+ }
97
169
  chainInfoMap = this.mergeChainList(oldChainInfoMap, latestChainInfo);
98
170
  const [currentChainStateKey, newChainKey] = [Object.keys(currentChainStateMap), Object.keys(chainInfoMap)];
99
- addedChain = newChainKey.filter(chain => !currentChainStateKey.includes(chain));
171
+ addedChain = newChainKey.filter(chain => !currentChainStateKey.includes(chain) || customChains.includes(chain));
100
172
  addedChain.forEach(key => {
101
- currentChainStateMap[key] = {
102
- active: false,
103
- currentProvider: randomizeProvider(chainInfoMap[key].providers).providerKey,
104
- manualTurnOff: false,
105
- slug: key
106
- };
107
- currentChainStatusMap[key] = {
108
- slug: key,
109
- connectionStatus: _ChainConnectionStatus.DISCONNECTED,
110
- lastUpdated: Date.now()
111
- };
173
+ if (!currentChainStateMap[key] && !currentChainStatusMap[key]) {
174
+ currentChainStateMap[key] = {
175
+ active: false,
176
+ currentProvider: randomizeProvider(chainInfoMap[key].providers).providerKey,
177
+ manualTurnOff: false,
178
+ slug: key
179
+ };
180
+ currentChainStatusMap[key] = {
181
+ slug: key,
182
+ connectionStatus: _ChainConnectionStatus.DISCONNECTED,
183
+ lastUpdated: Date.now()
184
+ };
185
+ }
112
186
  });
113
187
  }
114
188
  if (latestAssetInfo && Object.keys(latestAssetInfo).length > 0) {
115
- assetRegistry = filterAssetInfoMap(oldChainInfoMap, Object.assign({}, oldAssetRegistry, latestAssetInfo), addedChain);
189
+ // Get all previously stored asset registry entries from the database.
190
+ const storedAssetRegistry = await this.dbService.getAllAssetStore();
191
+ const availableChains = Object.values(chainInfoMap).filter(info => info.chainStatus === _ChainStatus.ACTIVE).map(chainInfo => chainInfo.slug);
192
+ let finalAssetRegistry;
193
+ if (storedAssetRegistry.length === 0) {
194
+ finalAssetRegistry = oldAssetRegistry;
195
+ } else {
196
+ const mergedAssetRegistry = oldAssetRegistry;
197
+ const parsedStoredAssetRegistry = {};
198
+
199
+ // Update custom assets of merged custom chains
200
+ for (const storedAsset of Object.values(storedAssetRegistry)) {
201
+ var _storedAsset$metadata;
202
+ // If the stored asset is a custom asset and its origin chain is marked as deprecated, and its assetType is ERC20
203
+ if (_isCustomAsset(storedAsset.slug) && Object.keys(deprecatedChainMap).includes(storedAsset.originChain) && (_storedAsset$metadata = storedAsset.metadata) !== null && _storedAsset$metadata !== void 0 && _storedAsset$metadata.contractAddress) {
204
+ const newOriginChain = deprecatedChainMap[storedAsset.originChain];
205
+ // const newSlug = this.generateSlugForSmartContractAsset(newOriginChain, storedAsset.assetType, storedAsset.symbol, storedAsset.metadata?.contractAddress);
206
+
207
+ // Mark the old custom asset slug as deprecated.
208
+ deprecatedAssets.push(storedAsset.slug);
209
+ parsedStoredAssetRegistry[storedAsset.slug] = {
210
+ ...storedAsset,
211
+ originChain: newOriginChain
212
+ };
213
+ } else {
214
+ parsedStoredAssetRegistry[storedAsset.slug] = storedAsset;
215
+ }
216
+ }
217
+ for (const storedAssetInfo of Object.values(parsedStoredAssetRegistry)) {
218
+ let duplicated = false;
219
+ let deprecated = false;
220
+ let defaultSlugForMigration;
221
+ for (const defaultChainAsset of Object.values(latestAssetInfo)) {
222
+ // case: the stored asset is the same to a smart contract asset from patch
223
+ if (_isEqualSmartContractAsset(storedAssetInfo, defaultChainAsset)) {
224
+ duplicated = true;
225
+ defaultSlugForMigration = defaultChainAsset.slug;
226
+ break;
227
+ }
228
+
229
+ // case: the origin chain of the stored asset is no longer active (Exp: custom chain is deprecated)
230
+ if (availableChains.indexOf(storedAssetInfo.originChain) === -1) {
231
+ deprecated = true;
232
+ defaultSlugForMigration = defaultChainAsset.slug;
233
+ break;
234
+ }
235
+ }
236
+
237
+ // If the stored asset is a duplicate of a default asset or its origin chain is deprecated.
238
+ if (duplicated || deprecated) {
239
+ if (Object.keys(assetSetting).includes(storedAssetInfo.slug)) {
240
+ const isVisible = assetSetting[storedAssetInfo.slug].visible;
241
+
242
+ // Migrate assetSetting from custom token to token from patch
243
+ if (defaultSlugForMigration) {
244
+ migratedAssetSetting[defaultSlugForMigration] = {
245
+ visible: isVisible
246
+ };
247
+ delete assetSetting[storedAssetInfo.slug];
248
+ }
249
+ }
250
+ delete mergedAssetRegistry[storedAssetInfo.slug];
251
+ deprecatedAssets.push(storedAssetInfo.slug);
252
+ } else {
253
+ // If the stored asset is not a duplicate and its origin chain is active, keep it in the merged registry.
254
+ mergedAssetRegistry[storedAssetInfo.slug] = storedAssetInfo;
255
+ }
256
+ }
257
+ finalAssetRegistry = mergedAssetRegistry;
258
+ }
259
+ assetRegistry = filterAssetInfoMap(oldChainInfoMap, Object.assign({}, finalAssetRegistry, latestAssetInfo), addedChain);
116
260
  }
117
261
 
118
262
  // 3. validate data before write
@@ -130,6 +274,15 @@ export class ChainOnlineService {
130
274
  this.chainService.setChainStateMap(currentChainStateMap);
131
275
  this.chainService.subscribeChainStateMap().next(currentChainStateMap);
132
276
  this.chainService.subscribeChainStatusMap().next(currentChainStatusMap);
277
+ // Migrate assetSetting
278
+ this.chainService.setAssetSettings({
279
+ ...assetSetting,
280
+ ...migratedAssetSetting
281
+ });
282
+
283
+ // Remove all custom chains and custom tokens that is duplicated from chains or tokens in patch
284
+ await this.dbService.removeFromChainStore(deprecatedChains);
285
+ await this.dbService.removeFromAssetStore(deprecatedAssets);
133
286
  const storedChainInfoList = Object.keys(chainInfoMap).map(chainSlug => {
134
287
  return {
135
288
  ...chainInfoMap[chainSlug],
@@ -1229,8 +1229,11 @@ export class ChainService {
1229
1229
  async initAssetRegistry(deprecatedCustomChainMap) {
1230
1230
  const storedAssetRegistry = await this.dbService.getAllAssetStore();
1231
1231
  const latestAssetRegistry = filterAssetInfoMap(this.getChainInfoMap(), ChainAssetMap);
1232
+ const assetSetting = await this.getAssetSettings();
1232
1233
  const availableChains = Object.values(this.dataMap.chainInfoMap).filter(info => info.chainStatus === _ChainStatus.ACTIVE).map(chainInfo => chainInfo.slug);
1234
+ const customChains = Object.keys(deprecatedCustomChainMap).filter(chain => _isCustomChain(chain));
1233
1235
  let finalAssetRegistry = {};
1236
+ const migratedAssetSetting = {};
1234
1237
  if (storedAssetRegistry.length === 0) {
1235
1238
  finalAssetRegistry = latestAssetRegistry;
1236
1239
  } else {
@@ -1240,15 +1243,18 @@ export class ChainService {
1240
1243
 
1241
1244
  // Update custom assets of merged custom chains
1242
1245
  Object.values(storedAssetRegistry).forEach(storedAsset => {
1243
- if (_isCustomAsset(storedAsset.slug) && Object.keys(deprecatedCustomChainMap).includes(storedAsset.originChain)) {
1244
- var _storedAsset$metadata;
1246
+ var _storedAsset$metadata;
1247
+ const isFromCustomChain = customChains ? customChains.includes(storedAsset.originChain) : false;
1248
+
1249
+ // If the stored asset is a custom asset, from a deprecated custom chain, and has a contract address.
1250
+ if (_isCustomAsset(storedAsset.slug) && isFromCustomChain && (_storedAsset$metadata = storedAsset.metadata) !== null && _storedAsset$metadata !== void 0 && _storedAsset$metadata.contractAddress) {
1245
1251
  const newOriginChain = deprecatedCustomChainMap[storedAsset.originChain];
1246
- const newSlug = this.generateSlugForSmartContractAsset(newOriginChain, storedAsset.assetType, storedAsset.symbol, (_storedAsset$metadata = storedAsset.metadata) === null || _storedAsset$metadata === void 0 ? void 0 : _storedAsset$metadata.contractAddress);
1252
+ // const newSlug = this.generateSlugForSmartContractAsset(newOriginChain, storedAsset.assetType, storedAsset.symbol, storedAsset.metadata?.contractAddress);
1253
+
1247
1254
  deprecatedAssets.push(storedAsset.slug);
1248
- parsedStoredAssetRegistry[newSlug] = {
1255
+ parsedStoredAssetRegistry[storedAsset.slug] = {
1249
1256
  ...storedAsset,
1250
- originChain: newOriginChain,
1251
- slug: newSlug
1257
+ originChain: newOriginChain
1252
1258
  };
1253
1259
  } else {
1254
1260
  parsedStoredAssetRegistry[storedAsset.slug] = storedAsset;
@@ -1257,25 +1263,48 @@ export class ChainService {
1257
1263
  for (const storedAssetInfo of Object.values(parsedStoredAssetRegistry)) {
1258
1264
  let duplicated = false;
1259
1265
  let deprecated = false;
1266
+ let defaultSlugForMigration;
1260
1267
  for (const defaultChainAsset of Object.values(latestAssetRegistry)) {
1261
- // case merge custom asset with default asset
1268
+ // Case: The stored asset is the same to a smart contract asset from new stable chainlist
1262
1269
  if (_isEqualSmartContractAsset(storedAssetInfo, defaultChainAsset)) {
1263
1270
  duplicated = true;
1271
+ defaultSlugForMigration = defaultChainAsset.slug;
1264
1272
  break;
1265
1273
  }
1274
+
1275
+ // Case: If the origin chain of the stored asset is no longer active. (custom chain is deprecated)
1266
1276
  if (availableChains.indexOf(storedAssetInfo.originChain) === -1) {
1267
1277
  deprecated = true;
1278
+ defaultSlugForMigration = defaultChainAsset.slug;
1268
1279
  break;
1269
1280
  }
1281
+
1282
+ // Case: If a default asset already exists with the same slug as the stored asset (from patch)
1270
1283
  if (defaultChainAsset.slug === storedAssetInfo.slug) {
1271
1284
  duplicated = true;
1285
+ defaultSlugForMigration = defaultChainAsset.slug;
1272
1286
  break;
1273
1287
  }
1274
1288
  }
1275
- if (!duplicated && !deprecated) {
1276
- mergedAssetRegistry[storedAssetInfo.slug] = storedAssetInfo;
1277
- } else {
1289
+
1290
+ // If the stored asset is a duplicate of a default asset or its origin chain is deprecated.
1291
+ if (duplicated || deprecated) {
1292
+ if (Object.keys(assetSetting).includes(storedAssetInfo.slug)) {
1293
+ const isVisible = assetSetting[storedAssetInfo.slug].visible;
1294
+
1295
+ // Migrate assetSetting from custom token to new default token
1296
+ if (defaultSlugForMigration) {
1297
+ migratedAssetSetting[defaultSlugForMigration] = {
1298
+ visible: isVisible
1299
+ };
1300
+ delete assetSetting[storedAssetInfo.slug];
1301
+ }
1302
+ }
1303
+ delete mergedAssetRegistry[storedAssetInfo.slug];
1278
1304
  deprecatedAssets.push(storedAssetInfo.slug);
1305
+ } else {
1306
+ // If the stored asset is not a duplicate and its origin chain is active, keep it in the merged registry.
1307
+ mergedAssetRegistry[storedAssetInfo.slug] = storedAssetInfo;
1279
1308
  }
1280
1309
  }
1281
1310
  finalAssetRegistry = mergedAssetRegistry;
@@ -1,5 +1,5 @@
1
1
  import { _ChainAsset, _ChainInfo, _MultiChainAsset } from '@subwallet/chain-list/types';
2
- export declare const ChainListVersion = "0.2.111";
2
+ export declare const ChainListVersion = "0.2.112";
3
3
  export interface PatchInfo {
4
4
  patchVersion: string;
5
5
  appliedVersion: string;
@@ -3,9 +3,9 @@
3
3
 
4
4
  const PRODUCTION_BRANCHES = ['master', 'webapp', 'webapp-dev'];
5
5
  const branchName = process.env.BRANCH_NAME || 'subwallet-dev';
6
- const fetchDomain = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'https://chain-list-assets.subwallet.app' : 'https://dev.sw-chain-list-assets.pages.dev';
6
+ const fetchDomain = process.env.PATCH_CHAIN_LIST_URL || (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
- export const ChainListVersion = '0.2.111'; // update this when build chain-list
8
+ export const ChainListVersion = '0.2.112'; // update this when build chain-list
9
9
 
10
10
  // todo: move this interface to chainlist
11
11
 
@@ -23,4 +23,3 @@ export declare const MANTA_VALIDATOR_POINTS_PER_BLOCK = 20;
23
23
  export declare const MANTA_MIN_DELEGATION = 500;
24
24
  export declare const CHANNEL_ID = 7;
25
25
  export declare const STAKING_IDENTITY_API_SLUG: Record<string, string>;
26
- export declare const _SUPPORT_CHANGE_VALIDATOR_CHAIN: string[];
@@ -32,5 +32,4 @@ export const CHANNEL_ID = 7;
32
32
  export const STAKING_IDENTITY_API_SLUG = {
33
33
  polkadot: 'polkadot_people',
34
34
  kusama: 'peopleKusama'
35
- };
36
- export const _SUPPORT_CHANGE_VALIDATOR_CHAIN = ['polkadot', 'kusama'];
35
+ };
@@ -3,8 +3,8 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
3
3
  import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
4
4
  import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
5
5
  import { _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
6
- import { BasePoolInfo, BaseYieldPoolMetadata, EarningRewardHistoryItem, EarningRewardItem, HandleYieldStepData, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, RequestEarningSlippage, ResponseEarlyValidateYield, StakeCancelWithdrawalParams, SubmitChangeValidatorStaking, SubmitYieldJoinData, TransactionData, UnstakingInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPoolTarget, YieldPoolType, YieldPositionInfo, YieldStepBaseInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
7
- import { EarningSlippageResult } from './native-staking/dtao';
6
+ import { BasePoolInfo, BaseYieldPoolMetadata, EarningRewardHistoryItem, EarningRewardItem, HandleYieldStepData, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, RequestEarningImpact, ResponseEarlyValidateYield, StakeCancelWithdrawalParams, SubmitChangeValidatorStaking, SubmitYieldJoinData, TransactionData, UnstakingInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPoolTarget, YieldPoolType, YieldPositionInfo, YieldStepBaseInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
7
+ import { EarningImpactResult } from './native-staking/dtao';
8
8
  /**
9
9
  * @class BasePoolHandler
10
10
  * @description Base pool handler
@@ -120,5 +120,5 @@ export default abstract class BasePoolHandler {
120
120
  abstract handleChangeEarningValidator(data: SubmitChangeValidatorStaking): Promise<TransactionData>;
121
121
  /** Check handler can handle slug */
122
122
  canHandleSlug(slug: string): boolean;
123
- getEarningSlippage(params: RequestEarningSlippage): Promise<EarningSlippageResult>;
123
+ getEarningImpact(params: RequestEarningImpact): Promise<EarningImpactResult>;
124
124
  }
@@ -280,7 +280,7 @@ export default class BasePoolHandler {
280
280
  canHandleSlug(slug) {
281
281
  return this.slug === slug;
282
282
  }
283
- getEarningSlippage(params) {
283
+ getEarningImpact(params) {
284
284
  return Promise.resolve({
285
285
  slippage: 0,
286
286
  rate: 1
@@ -131,7 +131,8 @@ export default class BaseNativeStakingPoolHandler extends BasePoolHandler {
131
131
  address,
132
132
  amount,
133
133
  selectedValidators,
134
- slug
134
+ slug,
135
+ subnetData
135
136
  } = data;
136
137
  const positionInfo = await this.getPoolPosition(address, slug);
137
138
  const [extrinsic] = await this.createJoinExtrinsic(data, positionInfo);
@@ -140,7 +141,8 @@ export default class BaseNativeStakingPoolHandler extends BasePoolHandler {
140
141
  slug: this.slug,
141
142
  amount,
142
143
  address,
143
- selectedValidators
144
+ selectedValidators,
145
+ subnetData
144
146
  };
145
147
  return {
146
148
  txChain: this.chain,
@@ -1,6 +1,6 @@
1
1
  import { _ChainInfo } from '@subwallet/chain-list/types';
2
2
  import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
3
- import { BaseYieldPositionInfo, RequestEarningSlippage, YieldPoolInfo, YieldPoolType, YieldPositionInfo } from '@subwallet/extension-base/types';
3
+ import { BaseYieldPositionInfo, RequestEarningImpact, YieldPoolInfo, YieldPoolType, YieldPositionInfo } from '@subwallet/extension-base/types';
4
4
  import TaoNativeStakingPoolHandler, { TaoStakingStakeOption } from './tao';
5
5
  export interface SubnetData {
6
6
  netuid: number;
@@ -20,9 +20,10 @@ export interface RawDelegateState {
20
20
  stake: string;
21
21
  }>;
22
22
  }
23
- export interface EarningSlippageResult {
23
+ export interface EarningImpactResult {
24
24
  slippage: number;
25
25
  rate: number;
26
+ stakingTaoFee?: string;
26
27
  }
27
28
  export default class SubnetTaoStakingPoolHandler extends TaoNativeStakingPoolHandler {
28
29
  readonly type = YieldPoolType.SUBNET_STAKING;
@@ -33,7 +34,7 @@ export default class SubnetTaoStakingPoolHandler extends TaoNativeStakingPoolHan
33
34
  private isInit;
34
35
  constructor(state: KoniState, chain: string);
35
36
  canHandleSlug(slug: string): boolean;
36
- getEarningSlippage(params: RequestEarningSlippage): Promise<EarningSlippageResult>;
37
+ getEarningImpact(params: RequestEarningImpact): Promise<EarningImpactResult>;
37
38
  private init;
38
39
  protected getDescription(): string;
39
40
  subscribePoolInfo(callback: (data: YieldPoolInfo) => void): Promise<VoidFunction>;
@@ -41,7 +41,7 @@ export default class SubnetTaoStakingPoolHandler extends TaoNativeStakingPoolHan
41
41
  canHandleSlug(slug) {
42
42
  return slug.startsWith(`${this.slug}__`);
43
43
  }
44
- async getEarningSlippage(params) {
44
+ async getEarningImpact(params) {
45
45
  const substrateApi = await this.substrateApi.isReady;
46
46
  const subnetInfo = (await substrateApi.api.call.subnetInfoRuntimeApi.getDynamicInfo(params.netuid)).toJSON();
47
47
  const alphaIn = new BigN((subnetInfo === null || subnetInfo === void 0 ? void 0 : subnetInfo.alphaIn) || 0);
@@ -49,15 +49,18 @@ export default class SubnetTaoStakingPoolHandler extends TaoNativeStakingPoolHan
49
49
  const k = alphaIn.multipliedBy(taoIn);
50
50
  const value = new BigN(params.value);
51
51
  const rate = taoIn.dividedBy(alphaIn);
52
+ const feeRate = await this.bittensorCache.fetchSubnetFeeRate(params.netuid);
52
53
  if (params.type === ExtrinsicType.STAKING_BOND) {
53
54
  const newTaoIn = taoIn.plus(value);
54
55
  const newAlphaIn = k.dividedBy(newTaoIn);
55
56
  const alphaReturned = alphaIn.minus(newAlphaIn);
56
57
  const alphaIdeal = value.multipliedBy(alphaIn).dividedBy(taoIn);
57
58
  const slippage = alphaIdeal.minus(alphaReturned).dividedBy(alphaIdeal);
59
+ const bnStakingTaoFee = value.multipliedBy(feeRate);
58
60
  return {
59
61
  slippage: slippage.plus(0.0001).toNumber(),
60
- rate: rate.toNumber()
62
+ rate: rate.toNumber(),
63
+ stakingTaoFee: bnStakingTaoFee.toString()
61
64
  };
62
65
  } else if (params.type === ExtrinsicType.STAKING_UNBOND) {
63
66
  const newAlphaIn = alphaIn.plus(value);
@@ -65,9 +68,12 @@ export default class SubnetTaoStakingPoolHandler extends TaoNativeStakingPoolHan
65
68
  const taoReturned = taoIn.minus(newTaoReserve);
66
69
  const taoIdeal = value.multipliedBy(taoIn).dividedBy(alphaIn);
67
70
  const slippage = taoIdeal.minus(taoReturned).dividedBy(taoIdeal);
71
+ const taoAmount = value.multipliedBy(rate);
72
+ const bnStakingTaoFee = taoAmount.multipliedBy(feeRate);
68
73
  return {
69
74
  slippage: slippage.plus(0.0001).toNumber(),
70
- rate: rate.toNumber()
75
+ rate: rate.toNumber(),
76
+ stakingTaoFee: bnStakingTaoFee.toString()
71
77
  };
72
78
  }
73
79
  return {
@@ -2,14 +2,12 @@
2
2
  import { _ChainInfo } from '@subwallet/chain-list/types';
3
3
  import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
4
4
  import { ExtrinsicType, NominationInfo, UnstakingInfo } from '@subwallet/extension-base/background/KoniTypes';
5
- import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
6
5
  import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
7
6
  import { BaseYieldPositionInfo, OptimalYieldPath, PalletStakingNominations, PalletStakingStakingLedger, StakeCancelWithdrawalParams, SubmitChangeValidatorStaking, SubmitJoinNativeStaking, SubmitYieldJoinData, TransactionData, ValidatorInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPositionInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
8
7
  import { BN } from '@polkadot/util';
9
8
  import BaseNativeStakingPoolHandler from './base';
10
9
  export default class RelayNativeStakingPoolHandler extends BaseNativeStakingPoolHandler {
11
10
  availableMethod: YieldPoolMethodInfo;
12
- constructor(state: KoniState, chain: string);
13
11
  subscribePoolInfo(callback: (data: YieldPoolInfo) => void): Promise<VoidFunction>;
14
12
  parseNominatorMetadata(chainInfo: _ChainInfo, address: string, substrateApi: _SubstrateApi, ledger: PalletStakingStakingLedger, currentEra: string, minStake: BN): Promise<Omit<YieldPositionInfo, keyof BaseYieldPositionInfo>>;
15
13
  handleNominationsList(substrateApi: _SubstrateApi, chain: string, nominations: PalletStakingNominations, currentEra: string, address: string, maxNominatorRewardedPerValidator: number | undefined): Promise<NominationInfo[]>;
@@ -6,7 +6,7 @@ import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
6
6
  import { calculateAlephZeroValidatorReturn, calculateChainStakedReturnV2, calculateInflation, calculateTernoaValidatorReturn, calculateValidatorStakedReturn, getAvgValidatorEraReward, getCommission, getMaxValidatorErrorMessage, getMinStakeErrorMessage, getRelayBlockedValidatorList, getRelayEraRewardMap, getRelayMaxNominations, getRelayTopValidatorByPoints, getRelayValidatorPointsMap, getRelayWaitingValidatorList, getSupportedDaysByHistoryDepth } from '@subwallet/extension-base/koni/api/staking/bonding/utils';
7
7
  import { _STAKING_ERA_LENGTH_MAP } from '@subwallet/extension-base/services/chain-service/constants';
8
8
  import { _getChainSubstrateAddressPrefix } from '@subwallet/extension-base/services/chain-service/utils';
9
- import { _STAKING_CHAIN_GROUP, _SUPPORT_CHANGE_VALIDATOR_CHAIN, MaxEraRewardPointsEras } from '@subwallet/extension-base/services/earning-service/constants';
9
+ import { _STAKING_CHAIN_GROUP, MaxEraRewardPointsEras } from '@subwallet/extension-base/services/earning-service/constants';
10
10
  import { applyDecimal, parseIdentity } from '@subwallet/extension-base/services/earning-service/utils';
11
11
  import { BasicTxErrorType, EarningStatus, StakingTxErrorType, UnstakingStatus } from '@subwallet/extension-base/types';
12
12
  import { balanceFormatter, formatNumber, reformatAddress } from '@subwallet/extension-base/utils';
@@ -22,17 +22,8 @@ export default class RelayNativeStakingPoolHandler extends BaseNativeStakingPool
22
22
  cancelUnstake: true,
23
23
  withdraw: true,
24
24
  claimReward: false,
25
- changeValidator: false
25
+ changeValidator: true
26
26
  };
27
- constructor(state, chain) {
28
- super(state, chain);
29
- if (_SUPPORT_CHANGE_VALIDATOR_CHAIN.includes(chain)) {
30
- this.availableMethod = {
31
- ...this.availableMethod,
32
- changeValidator: true
33
- };
34
- }
35
- }
36
27
  /* Subscribe pool info */
37
28
 
38
29
  async subscribePoolInfo(callback) {
@@ -80,6 +80,7 @@ export declare class BittensorCache {
80
80
  get(): Promise<ValidatorResponse>;
81
81
  private fetchData;
82
82
  fetchApr(netuid: number): Promise<ValidatorAprResponse>;
83
+ fetchSubnetFeeRate(netuid: number): Promise<string>;
83
84
  }
84
85
  export declare const getAlphaToTaoRate: (substrateApi: _SubstrateApi, netuid: number) => Promise<string>;
85
86
  export default class TaoNativeStakingPoolHandler extends BaseParaStakingPoolHandler {
@@ -10,6 +10,7 @@ import BaseParaStakingPoolHandler from '@subwallet/extension-base/services/earni
10
10
  import { BasicTxErrorType, EarningStatus, StakingTxErrorType, YieldPoolType } from '@subwallet/extension-base/types';
11
11
  import { ProxyServiceRoute } from '@subwallet/extension-base/types/environment';
12
12
  import { fetchFromProxyService, formatNumber, reformatAddress } from '@subwallet/extension-base/utils';
13
+ import { fetchStaticCache } from '@subwallet/extension-base/utils/fetchStaticCache';
13
14
  import BigN from 'bignumber.js';
14
15
  import { t } from 'i18next';
15
16
  import { BehaviorSubject, combineLatest } from 'rxjs';
@@ -44,21 +45,12 @@ export class BittensorCache {
44
45
  }
45
46
  async fetchData() {
46
47
  try {
47
- const resp = await fetchFromProxyService(ProxyServiceRoute.BITTENSOR, '/dtao/validator/latest/v1?limit=100', {
48
- method: 'GET',
49
- headers: {
50
- 'Content-Type': 'application/json'
51
- }
48
+ const fetchData = await fetchStaticCache('earning/dtao/validator.json', {
49
+ data: {}
52
50
  });
53
- if (!resp.ok) {
54
- console.error('Fetch bittensor delegates fail:', resp.status);
55
- return this.cache || {
56
- data: []
57
- };
58
- }
59
- const rawData = await resp.json();
51
+ const validators = Object.values(fetchData.data);
60
52
  const data = {
61
- data: rawData.data.filter(validator => parseFloat(validator.root_stake) > 0)
53
+ data: validators.filter(validator => parseFloat(validator.root_stake) > 0)
62
54
  };
63
55
  this.cache = data;
64
56
  this.promise = null;
@@ -71,7 +63,8 @@ export class BittensorCache {
71
63
  this.cache = newData;
72
64
  }
73
65
  }).catch(console.error);
74
- }, 60 * 2000);
66
+ }, 60 * 1000); // Cache 1 minute
67
+
75
68
  return data;
76
69
  } catch (error) {
77
70
  console.error(error);
@@ -102,6 +95,23 @@ export class BittensorCache {
102
95
  };
103
96
  }
104
97
  }
98
+ async fetchSubnetFeeRate(netuid) {
99
+ try {
100
+ var _subnet$fee_rate;
101
+ const resp = await fetchFromProxyService(ProxyServiceRoute.BITTENSOR, '/subnet/latest/v1', {
102
+ method: 'GET',
103
+ headers: {
104
+ 'Content-Type': 'application/json'
105
+ }
106
+ });
107
+ const rawData = await resp.json();
108
+ const subnet = rawData.data.find(item => item.netuid === netuid);
109
+ return (_subnet$fee_rate = subnet === null || subnet === void 0 ? void 0 : subnet.fee_rate) !== null && _subnet$fee_rate !== void 0 ? _subnet$fee_rate : '0.0005';
110
+ } catch (error) {
111
+ console.error(error);
112
+ return '0.0005'; // Default fee rate if fetch fails
113
+ }
114
+ }
105
115
  }
106
116
 
107
117
  // export async function fetchTaoDelegateState (address: string): Promise<RawDelegateState> {
@@ -3,10 +3,10 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
3
3
  import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
4
4
  import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
5
5
  import { PersistDataServiceInterface, ServiceStatus, StoppableServiceInterface } from '@subwallet/extension-base/services/base/types';
6
- import { EarningRewardHistoryItem, EarningRewardItem, EarningRewardJson, HandleYieldStepData, HandleYieldStepParams, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, RequestEarningSlippage, RequestStakeCancelWithdrawal, RequestStakeClaimReward, RequestYieldLeave, RequestYieldWithdrawal, ResponseEarlyValidateYield, SubmitChangeValidatorStaking, TransactionData, ValidateYieldProcessParams, YieldPoolInfo, YieldPoolTarget, YieldPositionInfo } from '@subwallet/extension-base/types';
6
+ import { EarningRewardHistoryItem, EarningRewardItem, EarningRewardJson, HandleYieldStepData, HandleYieldStepParams, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, RequestEarningImpact, RequestStakeCancelWithdrawal, RequestStakeClaimReward, RequestYieldLeave, RequestYieldWithdrawal, ResponseEarlyValidateYield, SubmitChangeValidatorStaking, TransactionData, ValidateYieldProcessParams, YieldPoolInfo, YieldPoolTarget, YieldPositionInfo } from '@subwallet/extension-base/types';
7
7
  import { PromiseHandler } from '@subwallet/extension-base/utils';
8
8
  import { BehaviorSubject } from 'rxjs';
9
- import { EarningSlippageResult } from './handlers/native-staking/dtao';
9
+ import { EarningImpactResult } from './handlers/native-staking/dtao';
10
10
  import { BasePoolHandler } from './handlers';
11
11
  export declare const fetchPoolsData: () => Promise<Record<string, YieldPoolInfo>>;
12
12
  export default class EarningService implements StoppableServiceInterface, PersistDataServiceInterface {
@@ -107,7 +107,7 @@ export default class EarningService implements StoppableServiceInterface, Persis
107
107
  handleYieldWithdraw(params: RequestYieldWithdrawal): Promise<TransactionData>;
108
108
  handleYieldCancelUnstake(params: RequestStakeCancelWithdrawal): Promise<TransactionData>;
109
109
  handleYieldClaimReward(params: RequestStakeClaimReward): Promise<TransactionData>;
110
- yieldGetEarningSlippage(params: RequestEarningSlippage): Promise<EarningSlippageResult>;
110
+ yieldGetEarningImpact(params: RequestEarningImpact): Promise<EarningImpactResult>;
111
111
  handleYieldChangeValidator(params: SubmitChangeValidatorStaking): Promise<TransactionData>;
112
112
  resetWallet(): Promise<void>;
113
113
  }