@subwallet/extension-base 1.1.33-beta.0 → 1.1.33-beta.2

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 (47) hide show
  1. package/background/KoniTypes.d.ts +9 -0
  2. package/background/KoniTypes.js +3 -0
  3. package/cjs/background/KoniTypes.js +3 -0
  4. package/cjs/constants/staking.js +1 -1
  5. package/cjs/koni/api/yield/helper/utils.js +6 -3
  6. package/cjs/services/chain-service/constants.js +4 -2
  7. package/cjs/services/chain-service/handler/SubstrateApi.js +10 -5
  8. package/cjs/services/chain-service/index.js +144 -37
  9. package/cjs/services/chain-service/utils.js +68 -0
  10. package/cjs/services/earning-service/handlers/liquid-staking/base.js +2 -1
  11. package/cjs/services/earning-service/handlers/liquid-staking/bifrost-manta.js +140 -0
  12. package/cjs/services/earning-service/handlers/liquid-staking/bifrost.js +2 -1
  13. package/cjs/services/earning-service/handlers/liquid-staking/index.js +7 -0
  14. package/cjs/services/earning-service/handlers/special.js +7 -3
  15. package/cjs/services/earning-service/service.js +1 -0
  16. package/cjs/services/storage-service/DatabaseService.js +34 -0
  17. package/cjs/services/storage-service/db-stores/AssetRef.js +24 -0
  18. package/cjs/services/transaction-service/index.js +3 -0
  19. package/cjs/types/yield/actions/join/step.js +1 -0
  20. package/constants/staking.js +1 -1
  21. package/koni/api/yield/helper/utils.d.ts +1 -1
  22. package/koni/api/yield/helper/utils.js +5 -3
  23. package/package.json +12 -2
  24. package/services/chain-service/constants.d.ts +1 -0
  25. package/services/chain-service/constants.js +1 -0
  26. package/services/chain-service/handler/SubstrateApi.js +10 -5
  27. package/services/chain-service/index.d.ts +12 -1
  28. package/services/chain-service/index.js +140 -34
  29. package/services/chain-service/utils.d.ts +10 -1
  30. package/services/chain-service/utils.js +66 -1
  31. package/services/earning-service/handlers/liquid-staking/base.d.ts +1 -1
  32. package/services/earning-service/handlers/liquid-staking/base.js +2 -1
  33. package/services/earning-service/handlers/liquid-staking/bifrost-manta.d.ts +37 -0
  34. package/services/earning-service/handlers/liquid-staking/bifrost-manta.js +132 -0
  35. package/services/earning-service/handlers/liquid-staking/bifrost.d.ts +3 -3
  36. package/services/earning-service/handlers/liquid-staking/bifrost.js +2 -1
  37. package/services/earning-service/handlers/liquid-staking/index.d.ts +1 -0
  38. package/services/earning-service/handlers/liquid-staking/index.js +1 -0
  39. package/services/earning-service/handlers/special.js +7 -3
  40. package/services/earning-service/service.js +2 -1
  41. package/services/storage-service/DatabaseService.js +34 -0
  42. package/services/storage-service/databases/index.d.ts +4 -1
  43. package/services/storage-service/db-stores/AssetRef.d.ts +7 -0
  44. package/services/storage-service/db-stores/AssetRef.js +16 -0
  45. package/services/transaction-service/index.js +3 -0
  46. package/types/yield/actions/join/step.d.ts +1 -0
  47. package/types/yield/actions/join/step.js +1 -0
@@ -3,15 +3,15 @@
3
3
 
4
4
  import { AssetLogoMap, AssetRefMap, ChainAssetMap, ChainInfoMap, ChainLogoMap, MultiChainAssetMap } from '@subwallet/chain-list';
5
5
  import { _AssetRefPath, _AssetType, _ChainStatus, _SubstrateChainType } from '@subwallet/chain-list/types';
6
- import { _ASSET_LOGO_MAP_SRC, _ASSET_REF_SRC, _CHAIN_ASSET_SRC, _CHAIN_INFO_SRC, _CHAIN_LOGO_MAP_SRC, _DEFAULT_ACTIVE_CHAINS, _MANTA_ZK_CHAIN_GROUP, _MULTI_CHAIN_ASSET_SRC, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
6
+ import { _DEFAULT_ACTIVE_CHAINS, _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX, LATEST_CHAIN_DATA_FETCHING_INTERVAL } from '@subwallet/extension-base/services/chain-service/constants';
7
7
  import { EvmChainHandler } from '@subwallet/extension-base/services/chain-service/handler/EvmChainHandler';
8
8
  import { MantaPrivateHandler } from '@subwallet/extension-base/services/chain-service/handler/manta/MantaPrivateHandler';
9
9
  import { SubstrateChainHandler } from '@subwallet/extension-base/services/chain-service/handler/SubstrateChainHandler';
10
10
  import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chain-service/handler/types';
11
11
  import { _ChainConnectionStatus, _CUSTOM_PREFIX, _NFT_CONTRACT_STANDARDS, _SMART_CONTRACT_STANDARDS } from '@subwallet/extension-base/services/chain-service/types';
12
- import { _isAssetFungibleToken, _isChainEnabled, _isCustomAsset, _isCustomChain, _isCustomProvider, _isEqualContractAddress, _isEqualSmartContractAsset, _isMantaZkAsset, _isPureEvmChain, _isPureSubstrateChain, _parseAssetRefKey } from '@subwallet/extension-base/services/chain-service/utils';
12
+ import { _isAssetFungibleToken, _isChainEnabled, _isCustomAsset, _isCustomChain, _isCustomProvider, _isEqualContractAddress, _isEqualSmartContractAsset, _isMantaZkAsset, _isPureEvmChain, _isPureSubstrateChain, _parseAssetRefKey, randomizeProvider, updateLatestChainInfo } from '@subwallet/extension-base/services/chain-service/utils';
13
13
  import AssetSettingStore from '@subwallet/extension-base/stores/AssetSetting';
14
- import { MODULE_SUPPORT } from '@subwallet/extension-base/utils';
14
+ import { fetchStaticData, MODULE_SUPPORT } from '@subwallet/extension-base/utils';
15
15
  import { BehaviorSubject, Subject } from 'rxjs';
16
16
  import Web3 from 'web3';
17
17
  import { logger as createLogger } from '@polkadot/util/logger';
@@ -396,9 +396,11 @@ export class ChainService {
396
396
  await this.eventService.waitDatabaseReady;
397
397
 
398
398
  // TODO: reconsider the flow of initiation
399
- const [latestAssetRefMap, latestMultiChainAssetMap] = await Promise.all([this.fetchLatestData(_ASSET_REF_SRC, AssetRefMap), this.fetchLatestData(_MULTI_CHAIN_ASSET_SRC, MultiChainAssetMap)]);
400
- this.multiChainAssetMapSubject.next(latestMultiChainAssetMap);
401
- this.dataMap.assetRefMap = latestAssetRefMap;
399
+ this.multiChainAssetMapSubject.next(MultiChainAssetMap);
400
+ // const storedAssetRefMap = await this.dbService.getAssetRefMap();
401
+ //
402
+ // this.dataMap.assetRefMap = storedAssetRefMap && Object.values(storedAssetRefMap).length > 0 ? storedAssetRefMap : AssetRefMap;
403
+
402
404
  await this.initChains();
403
405
  this.chainInfoMapSubject.next(this.getChainInfoMap());
404
406
  this.updateChainStateMapSubscription();
@@ -406,7 +408,93 @@ export class ChainService {
406
408
  this.xcmRefMapSubject.next(this.dataMap.assetRefMap);
407
409
  await this.initApis();
408
410
  await this.initAssetSettings();
411
+ await this.initAssetRefMap();
412
+ this.checkLatestData();
413
+ }
414
+ async initAssetRefMap() {
415
+ try {
416
+ const fetchPromise = this.fetchLatestBlockedAssetRef();
417
+ const timeout = new Promise(resolve => {
418
+ const id = setTimeout(() => {
419
+ clearTimeout(id);
420
+ resolve(null);
421
+ }, 1000);
422
+ });
423
+ const disabledAssetRefs = (await Promise.race([timeout, fetchPromise])) || null;
424
+ if (disabledAssetRefs) {
425
+ this.handleLatestBlockedAssetRef(disabledAssetRefs);
426
+ } else {
427
+ this.dataMap.assetRefMap = AssetRefMap;
428
+ }
429
+ } catch (e) {
430
+ this.dataMap.assetRefMap = AssetRefMap;
431
+ }
432
+ }
433
+ checkLatestData() {
434
+ clearInterval(this.refreshLatestChainDataTimeOut);
435
+ this.handleLatestData();
436
+ this.handleLatestData();
437
+ this.handleLatestData();
438
+ this.refreshLatestChainDataTimeOut = setInterval(this.handleLatestData.bind(this), LATEST_CHAIN_DATA_FETCHING_INTERVAL);
439
+ }
440
+ stopCheckLatestChainData() {
441
+ clearInterval(this.refreshLatestChainDataTimeOut);
409
442
  }
443
+ handleLatestProviderData(latestChainInfo) {
444
+ try {
445
+ if (latestChainInfo && latestChainInfo.length > 0) {
446
+ const {
447
+ needUpdateChainApiList,
448
+ storedChainInfoList
449
+ } = updateLatestChainInfo(this.dataMap, latestChainInfo);
450
+ this.dbService.bulkUpdateChainStore(storedChainInfoList).catch(console.error);
451
+ this.updateChainSubscription();
452
+ needUpdateChainApiList.forEach(chainInfo => {
453
+ console.log('Updating chain API for', chainInfo.slug);
454
+ this.initApiForChain(chainInfo).catch(console.error);
455
+ });
456
+ this.logger.log('Finished updating latest RPC providers');
457
+ }
458
+ } catch (e) {
459
+ console.error('Error fetching latest chain data');
460
+ }
461
+ }
462
+ handleLatestBlockedAssetRef(latestBlockedAssetRefList) {
463
+ const updatedAssetRefMap = {
464
+ ...AssetRefMap
465
+ };
466
+ latestBlockedAssetRefList.forEach(blockedAssetRef => {
467
+ delete updatedAssetRefMap[blockedAssetRef];
468
+ });
469
+ this.dataMap.assetRefMap = updatedAssetRefMap;
470
+
471
+ // this.dbService.setAssetRef(this.dataMap.assetRefMap).catch(console.error);
472
+ this.xcmRefMapSubject.next(this.dataMap.assetRefMap);
473
+ this.logger.log('Finished updating latest asset ref');
474
+ }
475
+ handleLatestPriceId(latestPriceIds) {
476
+ Object.entries(latestPriceIds).forEach(([slug, priceId]) => {
477
+ if (this.dataMap.assetRegistry[slug]) {
478
+ this.dataMap.assetRegistry[slug].priceId = priceId;
479
+ }
480
+ });
481
+ this.assetRegistrySubject.next(this.dataMap.assetRegistry);
482
+ this.eventService.emit('asset.updateState', '');
483
+ this.logger.log('Finished updating latest price IDs');
484
+ }
485
+ handleLatestData() {
486
+ this.fetchLatestChainData().then(latestChainInfo => {
487
+ this.handleLatestProviderData(latestChainInfo);
488
+ }).catch(console.error);
489
+ this.fetchLatestBlockedAssetRef().then(latestAssetRef => {
490
+ this.handleLatestBlockedAssetRef(latestAssetRef);
491
+ }).catch(console.error);
492
+
493
+ // this.fetchLatestPriceIdsData().then((latestPriceIds) => {
494
+ // this.handleLatestPriceId(latestPriceIds);
495
+ // }).catch(console.error);
496
+ }
497
+
410
498
  async initApis() {
411
499
  const chainInfoMap = this.getChainInfoMap();
412
500
  const chainStateMap = this.getChainStateMap();
@@ -562,8 +650,8 @@ export class ChainService {
562
650
  }
563
651
  return duplicatedSlug;
564
652
  }
565
- async fetchLatestData(src, defaultValue) {
566
- return Promise.resolve(defaultValue);
653
+ async fetchLatestChainData() {
654
+ return await fetchStaticData('chains');
567
655
  // try {
568
656
  // const timeout = new Promise((resolve) => {
569
657
  // const id = setTimeout(() => {
@@ -600,9 +688,16 @@ export class ChainService {
600
688
  // }
601
689
  }
602
690
 
691
+ // @ts-ignore
692
+ async fetchLatestPriceIdsData() {
693
+ return await fetchStaticData('chain-assets/price-map');
694
+ }
695
+ async fetchLatestBlockedAssetRef() {
696
+ return await fetchStaticData('chain-assets/disabled-xcm-channels');
697
+ }
603
698
  async initChains() {
604
699
  const storedChainSettings = await this.dbService.getAllChainStore();
605
- const latestChainInfoMap = await this.fetchLatestData(_CHAIN_INFO_SRC, ChainInfoMap);
700
+ const defaultChainInfoMap = ChainInfoMap;
606
701
  const storedChainSettingMap = {};
607
702
  storedChainSettings.forEach(chainStoredSetting => {
608
703
  storedChainSettingMap[chainStoredSetting.slug] = chainStoredSetting;
@@ -611,10 +706,13 @@ export class ChainService {
611
706
  const deprecatedChains = [];
612
707
  const deprecatedChainMap = {};
613
708
  if (storedChainSettings.length === 0) {
614
- this.dataMap.chainInfoMap = latestChainInfoMap;
615
- Object.values(latestChainInfoMap).forEach(chainInfo => {
709
+ this.dataMap.chainInfoMap = defaultChainInfoMap;
710
+ Object.values(defaultChainInfoMap).forEach(chainInfo => {
711
+ const {
712
+ providerKey
713
+ } = randomizeProvider(chainInfo.providers);
616
714
  this.dataMap.chainStateMap[chainInfo.slug] = {
617
- currentProvider: Object.keys(chainInfo.providers)[0],
715
+ currentProvider: providerKey,
618
716
  slug: chainInfo.slug,
619
717
  connectionStatus: _ChainConnectionStatus.DISCONNECTED,
620
718
  active: _DEFAULT_ACTIVE_CHAINS.includes(chainInfo.slug)
@@ -624,15 +722,15 @@ export class ChainService {
624
722
  newStorageData.push({
625
723
  ...chainInfo,
626
724
  active: _DEFAULT_ACTIVE_CHAINS.includes(chainInfo.slug),
627
- currentProvider: Object.keys(chainInfo.providers)[0]
725
+ currentProvider: providerKey
628
726
  });
629
727
  });
630
728
  } else {
631
- const mergedChainInfoMap = latestChainInfoMap;
729
+ const mergedChainInfoMap = defaultChainInfoMap;
632
730
  for (const [storedSlug, storedChainInfo] of Object.entries(storedChainSettingMap)) {
633
- const chainInfo = latestChainInfoMap[storedSlug];
731
+ const chainInfo = defaultChainInfoMap[storedSlug];
634
732
 
635
- // Network is existed on change list
733
+ // Network existed on change list
636
734
  // check predefined chains first, keep setting for providers and currentProvider
637
735
  if (chainInfo) {
638
736
  // Keep customer provider only
@@ -647,23 +745,27 @@ export class ChainService {
647
745
  }
648
746
  }
649
747
  mergedChainInfoMap[storedSlug].providers = providers;
650
-
651
- // Merge current provider
652
- let currentProvider = storedChainInfo.currentProvider;
653
- const providerValue = storedChainInfo.providers[currentProvider] || '';
654
- if (!providers[currentProvider]) {
655
- currentProvider = Object.keys(providers)[0];
656
- for (const [key, value] of Object.entries(providers)) {
657
- if (providerValue === value) {
658
- currentProvider = key;
659
- break;
660
- }
748
+ const {
749
+ providerKey
750
+ } = randomizeProvider(providers);
751
+ let selectedProvider = providerKey;
752
+ const storedProviderKey = storedChainInfo.currentProvider;
753
+ const storedProviderValue = storedChainInfo.providers[storedProviderKey] || '';
754
+ if (storedProviderValue !== null && storedProviderValue !== void 0 && storedProviderValue.startsWith('light') || storedProviderKey !== null && storedProviderKey !== void 0 && storedProviderKey.startsWith(_CUSTOM_PREFIX)) {
755
+ const savedProviderKey = Object.keys(providers).find(key => providers[key] === storedProviderValue);
756
+ if (savedProviderKey) {
757
+ selectedProvider = savedProviderKey;
661
758
  }
662
759
  }
760
+
761
+ // Merge current provider
762
+ // let currentProvider = storedChainInfo.currentProvider;
763
+ // const providerValue = storedChainInfo.providers[selectedProvider] || '';
764
+
663
765
  const hasProvider = Object.values(providers).length > 0;
664
766
  const canActive = hasProvider && chainInfo.chainStatus === _ChainStatus.ACTIVE;
665
767
  this.dataMap.chainStateMap[storedSlug] = {
666
- currentProvider: currentProvider,
768
+ currentProvider: selectedProvider,
667
769
  slug: storedSlug,
668
770
  connectionStatus: _ChainConnectionStatus.DISCONNECTED,
669
771
  active: canActive && storedChainInfo.active
@@ -671,13 +773,13 @@ export class ChainService {
671
773
  newStorageData.push({
672
774
  ...mergedChainInfoMap[storedSlug],
673
775
  active: canActive && storedChainInfo.active,
674
- currentProvider: currentProvider
776
+ currentProvider: selectedProvider
675
777
  });
676
778
  } else if (_isCustomChain(storedSlug)) {
677
779
  var _storedChainInfo$subs, _storedChainInfo$evmI;
678
780
  // only custom chains are left
679
781
  // check custom chain duplicated with predefined chain => merge into predefined chain
680
- const duplicatedDefaultSlug = this.checkExistedPredefinedChain(latestChainInfoMap, (_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);
782
+ const duplicatedDefaultSlug = this.checkExistedPredefinedChain(defaultChainInfoMap, (_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);
681
783
  if (duplicatedDefaultSlug.length > 0) {
682
784
  // merge custom chain with existed chain
683
785
  mergedChainInfoMap[duplicatedDefaultSlug].providers = {
@@ -702,6 +804,7 @@ export class ChainService {
702
804
  slug: storedSlug,
703
805
  name: storedChainInfo.name,
704
806
  providers: storedChainInfo.providers,
807
+ // TODO: review
705
808
  evmInfo: storedChainInfo.evmInfo,
706
809
  substrateInfo: storedChainInfo.substrateInfo,
707
810
  isTestnet: storedChainInfo.isTestnet,
@@ -711,6 +814,7 @@ export class ChainService {
711
814
  };
712
815
  this.dataMap.chainStateMap[storedSlug] = {
713
816
  currentProvider: storedChainInfo.currentProvider,
817
+ // TODO: review
714
818
  slug: storedSlug,
715
819
  connectionStatus: _ChainConnectionStatus.DISCONNECTED,
716
820
  active: storedChainInfo.active
@@ -718,7 +822,7 @@ export class ChainService {
718
822
  newStorageData.push({
719
823
  ...mergedChainInfoMap[storedSlug],
720
824
  active: storedChainInfo.active,
721
- currentProvider: storedChainInfo.currentProvider
825
+ currentProvider: storedChainInfo.currentProvider // TODO: review
722
826
  });
723
827
  }
724
828
  } else {
@@ -750,7 +854,7 @@ export class ChainService {
750
854
  }
751
855
  async initAssetRegistry(deprecatedCustomChainMap) {
752
856
  const storedAssetRegistry = await this.dbService.getAllAssetStore();
753
- const latestAssetRegistry = await this.fetchLatestData(_CHAIN_ASSET_SRC, ChainAssetMap);
857
+ const latestAssetRegistry = ChainAssetMap;
754
858
  const availableChains = Object.values(this.dataMap.chainInfoMap).filter(info => info.chainStatus === _ChainStatus.ACTIVE).map(chainInfo => chainInfo.slug);
755
859
 
756
860
  // Fill out zk assets from latestAssetRegistry if not supported
@@ -1200,9 +1304,11 @@ export class ChainService {
1200
1304
  }
1201
1305
  async stopAllChainApis() {
1202
1306
  await Promise.all([this.substrateChainHandler.sleep(), this.evmChainHandler.sleep()]);
1307
+ this.stopCheckLatestChainData();
1203
1308
  }
1204
1309
  async resumeAllChainApis() {
1205
1310
  await Promise.all([this.substrateChainHandler.wakeUp(), this.evmChainHandler.wakeUp()]);
1311
+ this.checkLatestData();
1206
1312
  }
1207
1313
  checkAndUpdateStatusMapForChain(chainSlug) {
1208
1314
  const substrateApiMap = this.getSubstrateApiMap();
@@ -1337,10 +1443,10 @@ export class ChainService {
1337
1443
  return this.assetSettingSubject;
1338
1444
  }
1339
1445
  async getChainLogoMap() {
1340
- return await this.fetchLatestData(_CHAIN_LOGO_MAP_SRC, ChainLogoMap);
1446
+ return Promise.resolve(ChainLogoMap);
1341
1447
  }
1342
1448
  async getAssetLogoMap() {
1343
- return await this.fetchLatestData(_ASSET_LOGO_MAP_SRC, AssetLogoMap);
1449
+ return Promise.resolve(AssetLogoMap);
1344
1450
  }
1345
1451
  resetWallet(resetAll) {
1346
1452
  if (resetAll) {
@@ -1,6 +1,7 @@
1
1
  import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _MultiChainAsset } from '@subwallet/chain-list/types';
2
2
  import { BasicTokenInfo } from '@subwallet/extension-base/background/KoniTypes';
3
- import { _ChainState } from '@subwallet/extension-base/services/chain-service/types';
3
+ import { _ChainState, _DataMap } from '@subwallet/extension-base/services/chain-service/types';
4
+ import { IChain } from '@subwallet/extension-base/services/storage-service/databases';
4
5
  export declare function _isCustomChain(slug: string): boolean;
5
6
  export declare function _isCustomAsset(slug: string): boolean;
6
7
  export declare function _getCustomAssets(assetRegistry: Record<string, _ChainAsset>): Record<string, _ChainAsset>;
@@ -67,3 +68,11 @@ export declare function _generateCustomProviderKey(index: number): string;
67
68
  export declare const findChainInfoByHalfGenesisHash: (chainMap: Record<string, _ChainInfo>, halfGenesisHash?: string) => _ChainInfo | null;
68
69
  export declare const findChainInfoByChainId: (chainMap: Record<string, _ChainInfo>, chainId?: number) => _ChainInfo | null;
69
70
  export declare function _isMantaZkAsset(chainAsset: _ChainAsset): boolean;
71
+ export declare function randomizeProvider(providers: Record<string, string>, excludedKeys?: string[]): {
72
+ providerKey: string;
73
+ providerValue: string;
74
+ };
75
+ export declare function updateLatestChainInfo(currentDataMap: _DataMap, latestChainInfoList: _ChainInfo[]): {
76
+ storedChainInfoList: IChain[];
77
+ needUpdateChainApiList: _ChainInfo[];
78
+ };
@@ -1,7 +1,7 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { _AssetRefPath, _AssetType, _SubstrateChainType } from '@subwallet/chain-list/types';
4
+ import { _AssetRefPath, _AssetType, _ChainStatus, _SubstrateChainType } from '@subwallet/chain-list/types';
5
5
  import { _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
6
6
  import { _CUSTOM_PREFIX, _SMART_CONTRACT_STANDARDS } from '@subwallet/extension-base/services/chain-service/types';
7
7
  import { isEthereumAddress } from '@polkadot/util-crypto';
@@ -362,4 +362,69 @@ export const findChainInfoByChainId = (chainMap, chainId) => {
362
362
  };
363
363
  export function _isMantaZkAsset(chainAsset) {
364
364
  return _MANTA_ZK_CHAIN_GROUP.includes(chainAsset.originChain) && chainAsset.symbol.startsWith(_ZK_ASSET_PREFIX);
365
+ }
366
+ export function randomizeProvider(providers, excludedKeys) {
367
+ if (Object.keys(providers).length === 0) {
368
+ return {
369
+ providerKey: '',
370
+ providerValue: ''
371
+ };
372
+ }
373
+ let isValid = false;
374
+ let selectedProviderKey = '';
375
+ let selectedProviderValue = '';
376
+ while (!isValid) {
377
+ var _selectedProviderValu, _selectedProviderKey;
378
+ const randomProvider = Math.floor(Math.random() * Object.keys(providers).length);
379
+ selectedProviderKey = Object.keys(providers)[randomProvider];
380
+ selectedProviderValue = providers[selectedProviderKey];
381
+ if (!((_selectedProviderValu = selectedProviderValue) !== null && _selectedProviderValu !== void 0 && _selectedProviderValu.startsWith('light')) && !((_selectedProviderKey = selectedProviderKey) !== null && _selectedProviderKey !== void 0 && _selectedProviderKey.startsWith(_CUSTOM_PREFIX)) && !(excludedKeys !== null && excludedKeys !== void 0 && excludedKeys.includes(selectedProviderKey))) {
382
+ // if it's light client, then re-randomize
383
+ isValid = true;
384
+ }
385
+ }
386
+ return {
387
+ providerKey: selectedProviderKey,
388
+ providerValue: selectedProviderValue
389
+ };
390
+ }
391
+ export function updateLatestChainInfo(currentDataMap, latestChainInfoList) {
392
+ const currentChainInfoMap = currentDataMap.chainInfoMap;
393
+ const currentChainStateMap = currentDataMap.chainStateMap;
394
+ const storedChainInfoList = [];
395
+ const needUpdateChainApiList = [];
396
+ latestChainInfoList.forEach(latestChainInfo => {
397
+ const currentChainInfo = currentChainInfoMap[latestChainInfo.slug];
398
+ const currentChainState = currentChainStateMap[latestChainInfo.slug];
399
+ const currentChainProviderValue = currentChainInfo === null || currentChainInfo === void 0 ? void 0 : currentChainInfo.providers[currentChainState === null || currentChainState === void 0 ? void 0 : currentChainState.currentProvider];
400
+ if (currentChainInfo && currentChainState) {
401
+ const preservedProvider = {};
402
+ Object.entries(currentChainInfo.providers).forEach(([providerKey, providerValue]) => {
403
+ if (providerValue !== null && providerValue !== void 0 && providerValue.startsWith('light') || providerKey !== null && providerKey !== void 0 && providerKey.startsWith(_CUSTOM_PREFIX)) {
404
+ preservedProvider[providerKey] = providerValue;
405
+ }
406
+ });
407
+ currentChainInfo.providers = {
408
+ ...latestChainInfo.providers,
409
+ ...preservedProvider
410
+ };
411
+ const currentProviderNotFound = !Object.keys(currentChainInfo.providers).includes(currentChainState.currentProvider);
412
+ const currentProviderUpdated = Object.keys(currentChainInfo.providers).includes(currentChainState.currentProvider) && !Object.values(currentChainInfo.providers).includes(currentChainProviderValue);
413
+ if (currentChainInfo.chainStatus === _ChainStatus.ACTIVE && (currentProviderNotFound || currentProviderUpdated)) {
414
+ const {
415
+ providerKey
416
+ } = randomizeProvider(currentChainInfo.providers);
417
+ currentChainState.currentProvider = providerKey;
418
+ needUpdateChainApiList.push(currentChainInfo);
419
+ }
420
+ storedChainInfoList.push({
421
+ ...currentChainInfo,
422
+ ...currentChainState
423
+ });
424
+ }
425
+ });
426
+ return {
427
+ storedChainInfoList,
428
+ needUpdateChainApiList
429
+ };
365
430
  }
@@ -6,6 +6,6 @@ export default abstract class BaseLiquidStakingPoolHandler extends BaseSpecialSt
6
6
  /** Rate convert token when redeem */
7
7
  readonly minAmountPercent: number;
8
8
  static get defaultMinAmountPercent(): number;
9
- createParamToRedeem(amount: string, address: string): Promise<number>;
9
+ createParamToRedeem(amount: string, address: string): Promise<string>;
10
10
  validateYieldLeave(amount: string, address: string, fastLeave: boolean, selectedTarget?: string): Promise<TransactionError[]>;
11
11
  }
@@ -6,6 +6,7 @@ import { BasicTxErrorType, StakingTxErrorType } from '@subwallet/extension-base/
6
6
  import { convertDerivativeToOriginToken } from '@subwallet/extension-base/koni/api/yield/helper/utils';
7
7
  import { YieldPoolType } from '@subwallet/extension-base/types';
8
8
  import { formatNumber } from '@subwallet/extension-base/utils';
9
+ import BigN from 'bignumber.js';
9
10
  import { t } from 'i18next';
10
11
  import { BN, BN_ZERO } from '@polkadot/util';
11
12
  import BaseSpecialStakingPoolHandler from "../special.js";
@@ -31,7 +32,7 @@ export default class BaseLiquidStakingPoolHandler extends BaseSpecialStakingPool
31
32
  return Promise.reject(new TransactionError(BasicTxErrorType.INVALID_PARAMS));
32
33
  }
33
34
  const formattedMinAmount = convertDerivativeToOriginToken(amount, poolInfo, derivativeTokenInfo, originTokenInfo);
34
- return Math.floor(this.minAmountPercent * formattedMinAmount);
35
+ return new BigN(formattedMinAmount).multipliedBy(this.minAmountPercent).toFixed(0);
35
36
  }
36
37
  async validateYieldLeave(amount, address, fastLeave, selectedTarget) {
37
38
  const poolInfo = await this.getPoolInfo();
@@ -0,0 +1,37 @@
1
+ import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
2
+ import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
3
+ import { BaseYieldStepDetail, HandleYieldStepData, LiquidYieldPoolInfo, OptimalYieldPath, SubmitYieldJoinData, TransactionData } from '@subwallet/extension-base/types';
4
+ import BifrostLiquidStakingPoolHandler from './bifrost';
5
+ export interface BifrostLiquidStakingMeta {
6
+ apy: string;
7
+ apyBase: string;
8
+ apyReward: string;
9
+ tvl: number;
10
+ tvm: number;
11
+ holders: number;
12
+ }
13
+ export interface BifrostVtokenExchangeRateResp {
14
+ ratio: BifrostVtokenExchangeRate[];
15
+ }
16
+ export interface BifrostVtokenExchangeRate {
17
+ ratio: string;
18
+ key: string;
19
+ timestamp: string;
20
+ total_issuance: number;
21
+ token_pool: number;
22
+ }
23
+ export default class BifrostMantaLiquidStakingPoolHandler extends BifrostLiquidStakingPoolHandler {
24
+ protected readonly altInputAsset: string;
25
+ protected readonly derivativeAssets: string[];
26
+ protected readonly inputAsset: string;
27
+ protected readonly rewardAssets: string[];
28
+ protected readonly feeAssets: string[];
29
+ readonly minAmountPercent: number;
30
+ constructor(state: KoniState, chain: string);
31
+ protected getDescription(): string;
32
+ getPoolStat(): Promise<LiquidYieldPoolInfo>;
33
+ get submitJoinStepInfo(): BaseYieldStepDetail;
34
+ handleSubmitStep(data: SubmitYieldJoinData, path: OptimalYieldPath): Promise<HandleYieldStepData>;
35
+ handleYieldRedeem(amount: string, address: string, selectedTarget?: string): Promise<[ExtrinsicType, TransactionData]>;
36
+ handleYieldUnstake(amount: string, address: string, selectedTarget?: string): Promise<[ExtrinsicType, TransactionData]>;
37
+ }
@@ -0,0 +1,132 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
5
+ import { _getAssetDecimals, _getTokenOnChainInfo } from '@subwallet/extension-base/services/chain-service/utils';
6
+ import { YieldStepType } from '@subwallet/extension-base/types';
7
+ import fetch from 'cross-fetch';
8
+ import BifrostLiquidStakingPoolHandler from "./bifrost.js";
9
+ const STATS_URL = 'https://api.bifrost.app/api/site';
10
+ const RATIO_URL = 'https://api.bifrost.app/api/omni/MANTA';
11
+ export default class BifrostMantaLiquidStakingPoolHandler extends BifrostLiquidStakingPoolHandler {
12
+ altInputAsset = 'manta_network-NATIVE-MANTA';
13
+ derivativeAssets = ['bifrost_dot-LOCAL-vMANTA'];
14
+ inputAsset = 'bifrost_dot-LOCAL-MANTA';
15
+ rewardAssets = ['bifrost_dot-LOCAL-MANTA'];
16
+ feeAssets = ['bifrost_dot-NATIVE-BNC', 'bifrost_dot-LOCAL-MANTA'];
17
+ minAmountPercent = 0.985;
18
+ constructor(state, chain) {
19
+ super(state, chain);
20
+ const chainInfo = this.chainInfo;
21
+ this.slug = `MANTA___liquid_staking___${chain}`;
22
+ this.name = `${chainInfo.name} Liquid Staking Manta`;
23
+ this.shortName = chainInfo.name.replaceAll(' Relay Chain', '');
24
+ }
25
+ getDescription() {
26
+ return 'Stake MANTA to earn yield on vMANTA';
27
+ }
28
+
29
+ /* Subscribe pool info */
30
+
31
+ async getPoolStat() {
32
+ const substrateApi = await this.substrateApi.isReady;
33
+ const stakingMetaPromise = new Promise(function (resolve) {
34
+ fetch(STATS_URL, {
35
+ method: 'GET'
36
+ }).then(res => {
37
+ resolve(res.json());
38
+ }).catch(console.error);
39
+ });
40
+ const exchangeRatePromise = new Promise(function (resolve) {
41
+ fetch(RATIO_URL, {
42
+ method: 'GET'
43
+ }).then(resp => {
44
+ resolve(resp.json());
45
+ }).catch(console.error);
46
+ });
47
+ const derivativeTokenInfo = this.state.getAssetBySlug(this.derivativeAssets[0]);
48
+ const inputTokenInfo = this.state.getAssetBySlug(this.inputAsset);
49
+ const [_stakingMeta, _exchangeRate, _minimumRedeem, _minimumMint] = await Promise.all([stakingMetaPromise, exchangeRatePromise, substrateApi.api.query.vtokenMinting.minimumRedeem(_getTokenOnChainInfo(derivativeTokenInfo)), substrateApi.api.query.vtokenMinting.minimumMint(_getTokenOnChainInfo(inputTokenInfo))]);
50
+ const minimumRedeem = _minimumRedeem.toString();
51
+ const minimumMint = _minimumMint.toString();
52
+ const stakingMeta = _stakingMeta;
53
+ const exchangeRate = _exchangeRate;
54
+ const vMANTAStats = stakingMeta.vMANTA;
55
+ const assetInfo = this.state.getAssetBySlug(this.inputAsset);
56
+ const assetDecimals = 10 ** _getAssetDecimals(assetInfo);
57
+ const rate = parseFloat(exchangeRate.ratio[exchangeRate.ratio.length - 1].ratio); // TODO
58
+
59
+ this.updateExchangeRate(rate);
60
+ return {
61
+ ...this.baseInfo,
62
+ type: this.type,
63
+ metadata: {
64
+ ...this.metadataInfo,
65
+ description: this.getDescription()
66
+ },
67
+ statistic: {
68
+ assetEarning: [{
69
+ slug: this.rewardAssets[0],
70
+ apy: parseFloat(vMANTAStats.apyBase),
71
+ exchangeRate: rate
72
+ }],
73
+ farmerCount: vMANTAStats.holders,
74
+ unstakingPeriod: 24 * 7,
75
+ maxCandidatePerFarmer: 1,
76
+ maxWithdrawalRequestPerFarmer: 1,
77
+ earningThreshold: {
78
+ join: minimumMint,
79
+ defaultUnstake: minimumRedeem,
80
+ fastUnstake: '0'
81
+ },
82
+ totalApy: parseFloat(vMANTAStats.apyBase),
83
+ tvl: (vMANTAStats.tvm * assetDecimals).toString()
84
+ }
85
+ };
86
+ }
87
+
88
+ /* Subscribe pool info */
89
+
90
+ /* Join pool action */
91
+
92
+ get submitJoinStepInfo() {
93
+ return {
94
+ name: 'Mint vMANTA',
95
+ type: YieldStepType.MINT_VMANTA
96
+ };
97
+ }
98
+ async handleSubmitStep(data, path) {
99
+ const substrateApi = await this.substrateApi.isReady;
100
+ const inputTokenSlug = this.inputAsset;
101
+ const inputTokenInfo = this.state.getAssetBySlug(inputTokenSlug);
102
+ const extrinsic = substrateApi.api.tx.vtokenMinting.mint(_getTokenOnChainInfo(inputTokenInfo), data.amount, undefined, undefined);
103
+ return {
104
+ txChain: this.chain,
105
+ extrinsicType: ExtrinsicType.MINT_VMANTA,
106
+ extrinsic,
107
+ txData: data,
108
+ transferNativeAmount: '0',
109
+ chainType: ChainType.SUBSTRATE
110
+ };
111
+ }
112
+
113
+ /* Join pool action */
114
+
115
+ /* Leave pool action */
116
+
117
+ async handleYieldRedeem(amount, address, selectedTarget) {
118
+ const substrateApi = await this.substrateApi.isReady;
119
+ const weightedMinAmount = await this.createParamToRedeem(amount, address);
120
+ const extrinsic = substrateApi.api.tx.stablePool.swap(5, 1, 0, amount, weightedMinAmount);
121
+ return [ExtrinsicType.REDEEM_VMANTA, extrinsic];
122
+ }
123
+ async handleYieldUnstake(amount, address, selectedTarget) {
124
+ const chainApi = await this.substrateApi.isReady;
125
+ const derivativeTokenSlug = this.derivativeAssets[0];
126
+ const derivativeTokenInfo = this.state.getAssetBySlug(derivativeTokenSlug);
127
+ const extrinsic = chainApi.api.tx.vtokenMinting.redeem(_getTokenOnChainInfo(derivativeTokenInfo), amount);
128
+ return [ExtrinsicType.UNSTAKE_VMANTA, extrinsic];
129
+ }
130
+
131
+ /* Leave pool action */
132
+ }
@@ -24,14 +24,14 @@ export interface BifrostVtokenExchangeRate {
24
24
  }
25
25
  export default class BifrostLiquidStakingPoolHandler extends BaseLiquidStakingPoolHandler {
26
26
  slug: string;
27
- protected readonly name: string;
28
- protected readonly shortName: string;
27
+ protected name: string;
28
+ protected shortName: string;
29
29
  protected readonly altInputAsset: string;
30
30
  protected readonly derivativeAssets: string[];
31
31
  protected readonly inputAsset: string;
32
32
  protected readonly rewardAssets: string[];
33
33
  protected readonly feeAssets: string[];
34
- readonly minAmountPercent = 0.99;
34
+ readonly minAmountPercent: number;
35
35
  protected readonly availableMethod: YieldPoolMethodInfo;
36
36
  protected readonly rateDecimals = 0;
37
37
  constructor(state: KoniState, chain: string);
@@ -34,7 +34,7 @@ export default class BifrostLiquidStakingPoolHandler extends BaseLiquidStakingPo
34
34
  super(state, chain);
35
35
  const chainInfo = this.chainInfo;
36
36
  this.slug = `DOT___liquid_staking___${chain}`;
37
- this.name = `${chainInfo.name} Liquid Staking`;
37
+ this.name = `${chainInfo.name} Liquid Staking DOT`;
38
38
  this.shortName = chainInfo.name.replaceAll(' Relay Chain', '');
39
39
  }
40
40
  getDescription() {
@@ -93,6 +93,7 @@ export default class BifrostLiquidStakingPoolHandler extends BaseLiquidStakingPo
93
93
  unstakingPeriod: 24 * 28,
94
94
  maxCandidatePerFarmer: 1,
95
95
  maxWithdrawalRequestPerFarmer: 1,
96
+ farmerCount: vDOTStats.holders,
96
97
  earningThreshold: {
97
98
  join: minimumMint,
98
99
  defaultUnstake: minimumRedeem,
@@ -1,4 +1,5 @@
1
1
  export { default as AcalaLiquidStakingPoolHandler } from './acala';
2
2
  export { default as BifrostLiquidStakingPoolHandler } from './bifrost';
3
+ export { default as BifrostMantaLiquidStakingPoolHandler } from './bifrost-manta';
3
4
  export { default as ParallelLiquidStakingPoolHandler } from './parallel';
4
5
  export { default as StellaSwapLiquidStakingPoolHandler } from './stella-swap';
@@ -3,5 +3,6 @@
3
3
 
4
4
  export { default as AcalaLiquidStakingPoolHandler } from "./acala.js";
5
5
  export { default as BifrostLiquidStakingPoolHandler } from "./bifrost.js";
6
+ export { default as BifrostMantaLiquidStakingPoolHandler } from "./bifrost-manta.js";
6
7
  export { default as ParallelLiquidStakingPoolHandler } from "./parallel.js";
7
8
  export { default as StellaSwapLiquidStakingPoolHandler } from "./stella-swap.js";