@subwallet/extension-base 1.3.78-0 → 1.3.80-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 (80) hide show
  1. package/background/KoniTypes.d.ts +16 -6
  2. package/cjs/core/logic-validation/index.js +0 -12
  3. package/cjs/core/substrate/system-pallet.js +3 -0
  4. package/cjs/koni/api/nft/index.js +0 -14
  5. package/cjs/koni/background/cron.js +0 -17
  6. package/cjs/koni/background/handlers/Extension.js +13 -7
  7. package/cjs/koni/background/handlers/State.js +24 -6
  8. package/cjs/packageInfo.js +1 -1
  9. package/cjs/services/balance-service/helpers/process.js +49 -21
  10. package/cjs/services/balance-service/index.js +2 -2
  11. package/cjs/services/balance-service/transfer/xcm/utils.js +9 -9
  12. package/cjs/services/chain-service/constants.js +6 -2
  13. package/cjs/services/earning-service/handlers/native-staking/dtao.js +13 -13
  14. package/cjs/services/earning-service/handlers/native-staking/tao.js +16 -10
  15. package/cjs/services/earning-service/handlers/special.js +12 -4
  16. package/cjs/services/nft-service/index.js +219 -150
  17. package/cjs/services/nft-service/multi-chain-nft-fetcher.js +145 -0
  18. package/cjs/services/nft-service/nft-handlers/base-nft-handler.js +28 -0
  19. package/cjs/services/nft-service/nft-handlers/evm/evm-nft-handler.js +179 -0
  20. package/cjs/services/nft-service/nft-handlers/registry.js +37 -0
  21. package/cjs/services/nft-service/nft-handlers/unique/unique-nft-handler.js +187 -0
  22. package/cjs/services/storage-service/DatabaseService.js +1 -1
  23. package/cjs/services/swap-service/handler/asset-hub/handler.js +7 -4
  24. package/cjs/services/swap-service/handler/asset-hub/router.js +2 -66
  25. package/cjs/services/swap-service/handler/base-handler.js +4 -3
  26. package/cjs/services/swap-service/handler/hydradx-handler.js +9 -5
  27. package/cjs/services/swap-service/index.js +5 -4
  28. package/cjs/types/swap/index.js +4 -9
  29. package/cjs/utils/account/common.js +44 -8
  30. package/core/logic-validation/index.d.ts +0 -1
  31. package/core/logic-validation/index.js +0 -1
  32. package/core/substrate/system-pallet.js +3 -0
  33. package/koni/api/nft/index.js +1 -15
  34. package/koni/background/cron.d.ts +0 -1
  35. package/koni/background/cron.js +1 -18
  36. package/koni/background/handlers/Extension.d.ts +4 -0
  37. package/koni/background/handlers/Extension.js +13 -7
  38. package/koni/background/handlers/State.d.ts +8 -2
  39. package/koni/background/handlers/State.js +24 -6
  40. package/package.json +31 -11
  41. package/packageInfo.js +1 -1
  42. package/services/balance-service/helpers/process.d.ts +1 -1
  43. package/services/balance-service/helpers/process.js +48 -21
  44. package/services/balance-service/index.js +2 -2
  45. package/services/balance-service/transfer/xcm/utils.js +9 -9
  46. package/services/chain-service/constants.d.ts +3 -0
  47. package/services/chain-service/constants.js +3 -0
  48. package/services/earning-service/handlers/native-staking/dtao.js +12 -13
  49. package/services/earning-service/handlers/native-staking/tao.d.ts +2 -1
  50. package/services/earning-service/handlers/native-staking/tao.js +15 -10
  51. package/services/earning-service/handlers/special.js +13 -5
  52. package/services/nft-service/index.d.ts +42 -6
  53. package/services/nft-service/index.js +219 -151
  54. package/services/nft-service/multi-chain-nft-fetcher.d.ts +13 -0
  55. package/services/nft-service/multi-chain-nft-fetcher.js +138 -0
  56. package/services/nft-service/nft-handlers/base-nft-handler.d.ts +13 -0
  57. package/services/nft-service/nft-handlers/base-nft-handler.js +21 -0
  58. package/services/nft-service/nft-handlers/evm/evm-nft-handler.d.ts +9 -0
  59. package/services/nft-service/nft-handlers/evm/evm-nft-handler.js +171 -0
  60. package/services/nft-service/nft-handlers/registry.d.ts +11 -0
  61. package/services/nft-service/nft-handlers/registry.js +29 -0
  62. package/services/nft-service/nft-handlers/unique/unique-nft-handler.d.ts +12 -0
  63. package/services/nft-service/nft-handlers/unique/unique-nft-handler.js +177 -0
  64. package/services/storage-service/DatabaseService.d.ts +1 -1
  65. package/services/storage-service/DatabaseService.js +1 -1
  66. package/services/swap-service/handler/asset-hub/handler.js +7 -4
  67. package/services/swap-service/handler/asset-hub/router.d.ts +0 -4
  68. package/services/swap-service/handler/asset-hub/router.js +1 -64
  69. package/services/swap-service/handler/base-handler.js +4 -3
  70. package/services/swap-service/handler/hydradx-handler.js +9 -5
  71. package/services/swap-service/index.js +5 -4
  72. package/types/swap/index.d.ts +7 -35
  73. package/types/swap/index.js +3 -8
  74. package/types/yield/actions/join/step.d.ts +1 -0
  75. package/types/yield/actions/join/submit.d.ts +2 -1
  76. package/utils/account/common.d.ts +22 -1
  77. package/utils/account/common.js +44 -8
  78. package/cjs/core/logic-validation/swap.js +0 -235
  79. package/core/logic-validation/swap.d.ts +0 -26
  80. package/core/logic-validation/swap.js +0 -219
@@ -122,14 +122,17 @@ class BittensorCache {
122
122
  }
123
123
  }
124
124
  exports.BittensorCache = BittensorCache;
125
- const getAlphaToTaoRate = async (substrateApi, netuid) => {
126
- const subnetInfo = (await substrateApi.api.call.subnetInfoRuntimeApi.getDynamicInfo(netuid)).toJSON();
127
- if (!subnetInfo) {
125
+ const getAlphaToTaoRate = async function (substrateApi, netuid) {
126
+ let priceScaleDecimals = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 9;
127
+ if (netuid === 0) {
128
128
  return '1';
129
129
  }
130
- const taoIn = subnetInfo.taoIn ? new _bignumber.default(subnetInfo.taoIn) : new _bignumber.default(0);
131
- const alphaIn = subnetInfo.alphaIn ? new _bignumber.default(subnetInfo.alphaIn) : new _bignumber.default(0);
132
- return netuid === 0 || alphaIn.lte(0) ? '1' : taoIn.dividedBy(alphaIn).toString();
130
+ const [rawSubnetPrice, rawRootPrice] = await Promise.all([substrateApi.api.call.swapRuntimeApi.currentAlphaPrice(netuid), substrateApi.api.call.swapRuntimeApi.currentAlphaPrice(0)]);
131
+ const subnetPrice = new _bignumber.default(rawSubnetPrice.toString());
132
+ const defaultScale = new _bignumber.default(10).pow(priceScaleDecimals);
133
+ const rootPrice = new _bignumber.default(rawRootPrice.toString());
134
+ const priceScale = rootPrice.lte(0) ? defaultScale : rootPrice;
135
+ return subnetPrice.lte(0) ? '0' : subnetPrice.dividedBy(priceScale).toFixed();
133
136
  };
134
137
  exports.getAlphaToTaoRate = getAlphaToTaoRate;
135
138
  class TaoNativeStakingPoolHandler extends _basePara.default {
@@ -142,6 +145,9 @@ class TaoNativeStakingPoolHandler extends _basePara.default {
142
145
  claimReward: false,
143
146
  changeValidator: true
144
147
  };
148
+ getAlphaPriceScaleDecimals() {
149
+ return (0, _utils2._getAssetDecimals)(this.nativeToken);
150
+ }
145
151
  async getMinBond(netuid) {
146
152
  var _cachedPool$metadata2, _cachedPool$statistic, _cachedPool$statistic2, _onlinePool$metadata2, _onlinePool$statistic, _onlinePool$statistic2;
147
153
  // @ts-ignore
@@ -483,7 +489,7 @@ class TaoNativeStakingPoolHandler extends _basePara.default {
483
489
  const hotkey = selectedValidatorInfo.address;
484
490
  const netuid = (_subnetData$netuid = subnetData === null || subnetData === void 0 ? void 0 : subnetData.netuid) !== null && _subnetData$netuid !== void 0 ? _subnetData$netuid : 0;
485
491
  const slippage = (_subnetData$slippage = subnetData === null || subnetData === void 0 ? void 0 : subnetData.slippage) !== null && _subnetData$slippage !== void 0 ? _subnetData$slippage : DEFAULT_BITTENSOR_SLIPPAGE;
486
- const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid));
492
+ const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid, this.getAlphaPriceScaleDecimals()));
487
493
  const limitPrice = alphaToTaoPrice.multipliedBy(10 ** (0, _utils2._getAssetDecimals)(this.nativeToken)).multipliedBy(1 + slippage);
488
494
  const BNlimitPrice = new _bignumber.default(limitPrice.integerValue(_bignumber.default.ROUND_CEIL).toFixed());
489
495
  const extrinsic = chainApi.api.tx.subtensorModule.addStakeLimit(hotkey, netuid, binaryAmount.toFixed(), BNlimitPrice.toFixed(), false);
@@ -527,7 +533,7 @@ class TaoNativeStakingPoolHandler extends _basePara.default {
527
533
  return Promise.reject(new _TransactionError.TransactionError(_types.BasicTxErrorType.INVALID_PARAMS));
528
534
  }
529
535
  const binaryAmount = new _bignumber.default(amount);
530
- const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid));
536
+ const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid, this.getAlphaPriceScaleDecimals()));
531
537
  const limitPrice = alphaToTaoPrice.multipliedBy(10 ** (0, _utils2._getAssetDecimals)(this.nativeToken)).multipliedBy(1 - slippage);
532
538
  const BNlimitPrice = new _bignumber.default(limitPrice.integerValue(_bignumber.default.ROUND_CEIL).toFixed());
533
539
  const extrinsic = apiPromise.api.tx.subtensorModule.removeStakeLimit(selectedTarget, netuid, binaryAmount.toFixed(), BNlimitPrice.toFixed(), false);
@@ -543,7 +549,7 @@ class TaoNativeStakingPoolHandler extends _basePara.default {
543
549
  return [new _TransactionError.TransactionError(_types.BasicTxErrorType.INVALID_PARAMS)];
544
550
  }
545
551
  const netuid = (_poolInfo$metadata$su = poolInfo.metadata.subnetData) === null || _poolInfo$metadata$su === void 0 ? void 0 : _poolInfo$metadata$su.netuid;
546
- const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid || 0));
552
+ const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid || 0, this.getAlphaPriceScaleDecimals()));
547
553
  const minDelegatorStake = await this.getMinBond(netuid);
548
554
  const minUnstake = minDelegatorStake.dividedBy(alphaToTaoPrice);
549
555
  if (new _bignumber.default(amount).lt(minUnstake)) {
@@ -583,7 +589,7 @@ class TaoNativeStakingPoolHandler extends _basePara.default {
583
589
  if (originValidator === destValidator) {
584
590
  return Promise.reject(new _TransactionError.TransactionError(_types.BasicTxErrorType.INVALID_PARAMS, (0, _i18next.t)('bg.EARNING.services.service.earning.nativeStaking.tao.fromValidatorSameAsTo')));
585
591
  }
586
- const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid));
592
+ const alphaToTaoPrice = new _bignumber.default(await getAlphaToTaoRate(this.substrateApi, netuid, this.getAlphaPriceScaleDecimals()));
587
593
  const bnMinStake = await this.getMinBond(netuid);
588
594
  const minUnstake = bnMinStake.dividedBy(alphaToTaoPrice);
589
595
  const formattedMinUnstake = minUnstake.dividedBy(1000000).integerValue(_bignumber.default.ROUND_CEIL).dividedBy(1000);
@@ -255,6 +255,7 @@ class BaseSpecialStakingPoolHandler extends _base.default {
255
255
  }
256
256
  const metadata = {
257
257
  sendingValue,
258
+ xcmDestinationFee,
258
259
  originTokenInfo: altInputTokenInfo,
259
260
  destinationTokenInfo: inputTokenInfo
260
261
  };
@@ -466,11 +467,13 @@ class BaseSpecialStakingPoolHandler extends _base.default {
466
467
  const {
467
468
  destinationTokenInfo,
468
469
  originTokenInfo,
469
- sendingValue
470
+ sendingValue,
471
+ xcmDestinationFee
470
472
  } = metadata;
471
473
  const originChainInfo = this.state.getChainInfo(originTokenInfo.originChain);
472
474
  const originTokenSlug = (0, _utils3._getChainNativeTokenSlug)(originChainInfo);
473
475
  const substrateApi = this.state.getSubstrateApi(originChainInfo.slug);
476
+ const fromAddress = (0, _utils4._reformatAddressWithChain)(address, originChainInfo);
474
477
  const id = (0, _getId.getId)();
475
478
  const feeInfo = await this.state.feeService.subscribeChainFee(id, originChainInfo.slug, 'substrate');
476
479
  const xcmRequest = {
@@ -479,7 +482,7 @@ class BaseSpecialStakingPoolHandler extends _base.default {
479
482
  recipient: address,
480
483
  sendingValue,
481
484
  substrateApi,
482
- sender: address,
485
+ sender: fromAddress,
483
486
  originChain: originChainInfo,
484
487
  destinationChain: this.chainInfo,
485
488
  feeInfo
@@ -491,7 +494,7 @@ class BaseSpecialStakingPoolHandler extends _base.default {
491
494
  const xcmData = {
492
495
  originNetworkKey: originChainInfo.slug,
493
496
  destinationNetworkKey: destinationTokenInfo.originChain,
494
- from: address,
497
+ from: fromAddress,
495
498
  to: address,
496
499
  value: sendingValue,
497
500
  tokenSlug: originTokenSlug,
@@ -504,7 +507,12 @@ class BaseSpecialStakingPoolHandler extends _base.default {
504
507
  txData: xcmData,
505
508
  transferNativeAmount: sendingValue,
506
509
  chainType: _KoniTypes.ChainType.SUBSTRATE,
507
- xcmStepFee
510
+ xcmStepFee,
511
+ xcmDestinationFee: xcmDestinationFee ? {
512
+ decimals: (0, _utils3._getAssetDecimals)(destinationTokenInfo),
513
+ symbol: (0, _utils3._getAssetSymbol)(destinationTokenInfo),
514
+ value: xcmDestinationFee
515
+ } : undefined
508
516
  };
509
517
  }
510
518
  handleYieldJoin(data, path, currentStep) {
@@ -4,175 +4,244 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.default = void 0;
8
- var _types = require("@subwallet/chain-list/types");
7
+ exports.NftService = void 0;
8
+ var _types = require("@subwallet/extension-base/services/base/types");
9
9
  var _utils = require("@subwallet/extension-base/services/chain-service/utils");
10
10
  var _utils2 = require("@subwallet/extension-base/utils");
11
- var _keyring = require("@subwallet/keyring");
12
- var _types2 = require("@subwallet/keyring/types");
13
- var _subwalletServicesSdk = _interopRequireDefault(require("@subwallet-monorepos/subwallet-services-sdk"));
11
+ var _uiKeyring = _interopRequireDefault(require("@subwallet/ui-keyring"));
12
+ var _rxjs = require("rxjs");
13
+ var _multiChainNftFetcher = require("./multi-chain-nft-fetcher");
14
14
  // Copyright 2019-2022 @subwallet/extension-base authors & contributors
15
15
  // SPDX-License-Identifier: Apache-2.0
16
16
 
17
- /**
18
- * NFT detection service
19
- * Responsible for managing NFT detection jobs per address
20
- */
17
+ const INITIAL_NFT_STATE = {
18
+ nftData: {
19
+ total: 0,
20
+ nftList: []
21
+ },
22
+ nftCollections: []
23
+ };
21
24
 
22
- function mapSdkToNftItem(rawInstance, chain, collectionId, owner) {
23
- var _rawInstance$token_ty, _rawInstance$token_ty2, _rawInstance$id;
24
- const metadata = rawInstance.metadata || {};
25
- const image = metadata.image || rawInstance.image_url || rawInstance.media_url || '';
26
- const attributes = Array.isArray(metadata.attributes) ? metadata.attributes : [];
27
- let rarity;
28
- const properties = {};
29
- for (const attr of attributes) {
30
- try {
31
- var _attr$trait_type;
32
- const key = (_attr$trait_type = attr.trait_type) === null || _attr$trait_type === void 0 ? void 0 : _attr$trait_type.trim();
33
- if (!key) {
34
- continue;
35
- }
36
- let value = attr.value;
37
- if (typeof value === 'string') {
38
- const lower = value.toLowerCase();
39
- if (lower === 'true') {
40
- value = true;
41
- } else if (lower === 'false') {
42
- value = false;
43
- }
44
- }
45
- properties[key] = value;
46
- if (key.toLowerCase() === 'rarity') {
47
- rarity = String(value);
48
- }
49
- } catch {}
50
- }
51
- const hasProperties = Object.keys(properties).length > 0;
52
- const normalizedType = (_rawInstance$token_ty = rawInstance.token_type) === null || _rawInstance$token_ty === void 0 ? void 0 : (_rawInstance$token_ty2 = _rawInstance$token_ty.replace('-', '')) === null || _rawInstance$token_ty2 === void 0 ? void 0 : _rawInstance$token_ty2.toUpperCase();
25
+ // HIGH PRIORITY no lazy
26
+ const IMMEDIATE_EVENTS = ['account.updateCurrent', 'account.add', 'account.remove'];
53
27
 
54
- // Only support ERC721
55
- if (normalizedType !== 'ERC721') {
56
- return null;
57
- }
58
- return {
59
- id: (_rawInstance$id = rawInstance.id) === null || _rawInstance$id === void 0 ? void 0 : _rawInstance$id.toString(),
60
- chain,
61
- collectionId,
62
- owner: rawInstance.owner || owner,
63
- originAsset: undefined,
64
- name: metadata.name || `#${rawInstance.id}`,
65
- image: (0, _utils2.baseParseIPFSUrl)(image),
66
- externalUrl: rawInstance.external_app_url || undefined,
67
- rarity,
68
- description: metadata.description || undefined,
69
- properties: hasProperties ? properties : null,
70
- type: normalizedType === 'ERC721' ? _types._AssetType.ERC721 : _types._AssetType.ERC721,
71
- // currently only support ERC721
72
- rmrk_ver: undefined,
73
- onChainOption: undefined,
74
- assetHubType: undefined
75
- };
76
- }
77
- function mapSdkToCollection(raw, chain) {
78
- var _raw$token_instances;
79
- const token = raw.token || {};
80
- return {
81
- // must-have
82
- collectionId: token.address_hash,
83
- chain,
84
- originAsset: undefined,
85
- // optional
86
- collectionName: token.name || token.symbol || 'Unknown Collection',
87
- image: token.icon_url || undefined,
88
- itemCount: Number(raw.amount) || ((_raw$token_instances = raw.token_instances) === null || _raw$token_instances === void 0 ? void 0 : _raw$token_instances.length) || 0,
89
- externalUrl: undefined
90
- };
91
- }
28
+ // LOW PRIORITY – lazy
29
+ const LAZY_EVENTS = ['asset.updateState', 'chain.add'];
92
30
  class NftService {
93
- inProgress = new Set();
31
+ NFT_INTERVAL_TIME = 2 * 60 * 60 * 1000; // 2 hours
32
+
33
+ nftStateSubject = new _rxjs.BehaviorSubject(INITIAL_NFT_STATE);
34
+ nftState$ = this.nftStateSubject.asObservable();
35
+ isReloading = false;
36
+ startPromiseHandler = (0, _utils2.createPromiseHandler)();
37
+ stopPromiseHandler = (0, _utils2.createPromiseHandler)();
38
+ status = _types.ServiceStatus.NOT_INITIALIZED;
39
+ get isStarted() {
40
+ return this.status === _types.ServiceStatus.STARTED;
41
+ }
94
42
  constructor(state) {
95
43
  this.state = state;
44
+ this.multiChainFetcher = new _multiChainNftFetcher.MultiChainNftFetcher(state);
45
+ }
46
+ async init() {
47
+ this.status = _types.ServiceStatus.INITIALIZING;
48
+ await this.state.eventService.waitKeyringReady;
49
+ await this.state.eventService.waitChainReady;
50
+ await this.loadCachedData();
51
+ this.status = _types.ServiceStatus.INITIALIZED;
52
+ this.state.eventService.onLazy(this.handleEvents.bind(this));
53
+ }
54
+ async loadCachedData() {
55
+ const [nftData, collections] = await Promise.all([this.state.getNft(), this.state.getNftCollection()]);
56
+ this.nftStateSubject.next({
57
+ nftData: nftData || {
58
+ total: 0,
59
+ nftList: []
60
+ },
61
+ nftCollections: collections || []
62
+ });
63
+ }
64
+ async start() {
65
+ if (this.status === _types.ServiceStatus.STOPPING) {
66
+ await this.waitForStopped();
67
+ }
68
+ if (this.isStarted || this.status === _types.ServiceStatus.STARTING) {
69
+ return this.waitForStarted();
70
+ }
71
+ this.status = _types.ServiceStatus.STARTING;
72
+ await this.refreshNftData();
73
+ this.status = _types.ServiceStatus.STARTED;
74
+ this.startPromiseHandler.resolve();
75
+ this.startScanNft();
76
+ }
77
+ async stop() {
78
+ if (this.status === _types.ServiceStatus.STARTING) {
79
+ await this.waitForStarted();
80
+ }
81
+ if (this.status === _types.ServiceStatus.STOPPED || this.status === _types.ServiceStatus.STOPPING) {
82
+ return this.waitForStopped();
83
+ }
84
+ this.status = _types.ServiceStatus.STOPPING;
85
+ this.stopScanNft();
86
+ this.stopPromiseHandler.resolve();
87
+ }
88
+ waitForStarted() {
89
+ return this.startPromiseHandler.promise;
90
+ }
91
+ waitForStopped() {
92
+ return this.stopPromiseHandler.promise;
93
+ }
94
+ checkIfNftUpdateNeeded(events, eventTypes) {
95
+ if (!eventTypes.includes('chain.updateState')) {
96
+ return false;
97
+ }
98
+ const updatedChains = this.extractUpdatedChains(events);
99
+ return this.hasNftSupportedChainUpdate(updatedChains);
100
+ }
101
+ extractUpdatedChains(events) {
102
+ return events.filter(event => event.type === 'chain.updateState').map(event => event.data[0]);
103
+ }
104
+ hasNftSupportedChainUpdate(updatedChains) {
105
+ if (updatedChains.length === 0) {
106
+ return false;
107
+ }
108
+ const chainInfoMap = this.state.getServiceInfo().chainInfoMap;
109
+ return updatedChains.some(chainSlug => {
110
+ const chainInfo = chainInfoMap[chainSlug];
111
+ return this.isChainNftSupported(chainInfo);
112
+ });
113
+ }
114
+ isChainNftSupported(chainInfo) {
115
+ return (0, _utils._isChainSupportNativeNft)(chainInfo) || (0, _utils._isChainSupportEvmNft)(chainInfo) || (0, _utils._isChainSupportWasmNft)(chainInfo);
116
+ }
117
+ handleImmediateRefresh(address) {
118
+ this.state.resetNft(address);
119
+ this.refreshNftData().catch(console.error);
96
120
  }
97
- async fetchEvmCollectionsWithPreview(addresses) {
98
- for (const address of addresses) {
99
- const type = (0, _keyring.getKeypairTypeByAddress)(address);
100
- const typeValid = [..._types2.EthereumKeypairTypes].includes(type);
101
- if (typeValid) {
102
- if (this.inProgress.has(address)) {
103
- console.log(`[NftService] ${address} already running`);
104
- continue;
105
- }
106
- this.inProgress.add(address);
107
- try {
108
- const nftDetectionApi = _subwalletServicesSdk.default.nftDetectionApi;
109
- if (!(nftDetectionApi !== null && nftDetectionApi !== void 0 && nftDetectionApi.getEvmNftCollectionsByAddress)) {
110
- console.warn('[NftService] NftDetectionApi not available');
111
- continue;
112
- }
113
- const rawData = await nftDetectionApi.getEvmNftCollectionsByAddress(address);
114
- const allItems = [];
115
- const allCollections = [];
116
- for (const [chain, collections] of Object.entries(rawData)) {
117
- if (!Array.isArray(collections)) {
118
- continue;
119
- }
120
- for (const col of collections) {
121
- const mappedCollection = mapSdkToCollection(col, chain);
122
- allCollections.push(mappedCollection);
123
- if (Array.isArray(col.token_instances)) {
124
- const items = col.token_instances.map(inst => mapSdkToNftItem(inst, chain, mappedCollection.collectionId, address)).filter(i => Boolean(i));
125
- allItems.push(...items);
126
- }
127
- }
128
- }
129
- await this.state.handleDetectedNftCollections(allCollections);
130
- await this.state.handleDetectedNfts(address, allItems);
131
- } catch (err) {
132
- console.warn(`[NftService] detect error for ${address}`, err);
133
- } finally {
134
- this.inProgress.delete(address);
135
- }
121
+ scheduleLazyRefresh(delay) {
122
+ (0, _utils2.addLazy)('nft.refresh', () => {
123
+ if (!this.isReloading && this.isStarted) {
124
+ this.refreshNftData().catch(console.error);
136
125
  }
126
+ }, delay, undefined, true);
127
+ }
128
+ handleEvents(events, eventTypes) {
129
+ const LAZY_REFRESH_DELAY = 1800;
130
+ const address = this.state.keyringService.context.currentAccount.proxyId;
131
+ const hasImmediateEvent = IMMEDIATE_EVENTS.some(event => eventTypes.includes(event));
132
+ const hasLazyEvent = LAZY_EVENTS.some(event => eventTypes.includes(event));
133
+ const needsNftUpdate = this.checkIfNftUpdateNeeded(events, eventTypes);
134
+ if (hasImmediateEvent || needsNftUpdate) {
135
+ this.handleImmediateRefresh(address);
136
+ return;
137
+ }
138
+ if (hasLazyEvent) {
139
+ this.scheduleLazyRefresh(LAZY_REFRESH_DELAY);
137
140
  }
138
141
  }
139
- async getFullNftInstancesByCollection(request) {
140
- const {
141
- chainInfo,
142
- contractAddress,
143
- owners
144
- } = request;
145
- const chainId = (0, _utils._getEvmChainId)(chainInfo);
146
- if (!contractAddress || !owners || !chainId) {
147
- console.warn('[NftService] missing params for getFullNftInstancesByCollection');
142
+ async fetchFullListNftOfACollection(request) {
143
+ if (this.isReloading) {
148
144
  return false;
149
145
  }
150
146
  try {
151
- const nftDetectionApi = _subwalletServicesSdk.default.nftDetectionApi;
152
- if (!(nftDetectionApi !== null && nftDetectionApi !== void 0 && nftDetectionApi.getAllNftInstances)) {
153
- console.warn('[NftService] getAllNftInstances not available');
154
- return false;
155
- }
156
- const ownerList = Array.isArray(owners) ? owners : [owners];
157
- for (const eachOwner of ownerList) {
158
- try {
159
- const instances = await nftDetectionApi.getAllNftInstances(contractAddress, eachOwner, chainId.toString());
160
- if (!Array.isArray(instances)) {
161
- continue;
162
- }
163
- console.log('FOR TESTER (before)', instances);
164
- const nftList = instances.map(inst => mapSdkToNftItem(inst, chainInfo.slug, contractAddress, eachOwner)).filter(i => Boolean(i));
165
- console.log('FOR TESTER (after)', nftList);
166
- await this.state.handleDetectedNfts(eachOwner, nftList);
167
- } catch (innerErr) {
168
- console.warn(`[NftService] getAllNftInstances failed for ${eachOwner}`, innerErr);
169
- }
170
- }
147
+ const result = await this.multiChainFetcher.fetchFullListNftOfACollection(request);
148
+
149
+ // Persist DB
150
+ this.persistNftData({
151
+ items: result.items,
152
+ collections: result.collections
153
+ });
171
154
  return true;
172
- } catch (err) {
173
- console.error(`[NftDetectionService] getFullNftInstancesByCollection error for ${contractAddress}`, err);
155
+ } catch (e) {
156
+ console.error('[NftServiceV2] fetchFullListNftOfaCollection failed', e);
174
157
  return false;
175
158
  }
176
159
  }
160
+ async fetchNftDetail(request) {
161
+ if (this.isReloading) {
162
+ return null;
163
+ }
164
+ try {
165
+ const result = await this.multiChainFetcher.fetchNftDetail(request);
166
+ return result.items[0];
167
+ } catch (e) {
168
+ console.error('[NftServiceV2] fetchNftDetail failed', e);
169
+ return null;
170
+ }
171
+ }
172
+ startScanNft() {
173
+ this.stopScanNft();
174
+ const scanNft = () => {
175
+ if (!this.isStarted || this.isReloading) {
176
+ return;
177
+ }
178
+ this.refreshNftData().catch(console.error);
179
+ };
180
+ this._intervalFetchNft = setInterval(scanNft, this.NFT_INTERVAL_TIME);
181
+ }
182
+ stopScanNft() {
183
+ this._intervalFetchNft && clearInterval(this._intervalFetchNft);
184
+ this._intervalFetchNft = undefined;
185
+ }
186
+ persistNftData(result) {
187
+ try {
188
+ for (const item of result.items) {
189
+ const sender = _uiKeyring.default.getPair(item.owner);
190
+ this.state.updateNftData(item.chain, item, sender.address || item.owner);
191
+ }
192
+ for (const col of result.collections) {
193
+ this.state.setNftCollection(col.chain, col);
194
+ }
195
+ } catch (error) {
196
+ console.error('[NftServiceV2] Persist failed:', error);
197
+ }
198
+ }
199
+ async refreshNftData() {
200
+ if (this.isReloading) {
201
+ return;
202
+ }
203
+ this.isReloading = true;
204
+ try {
205
+ const addresses = this.state.keyringService.context.getDecodedAddresses();
206
+ const activeChains = Object.keys(this.state.getActiveChainInfoMap());
207
+ if (addresses.length === 0 || activeChains.length === 0) {
208
+ this.nftStateSubject.next(INITIAL_NFT_STATE);
209
+ return;
210
+ }
211
+ const result = await this.multiChainFetcher.fetch(addresses, activeChains);
212
+ this.persistNftData(result);
213
+ this.nftStateSubject.next({
214
+ nftData: {
215
+ total: result.items.length,
216
+ nftList: result.items
217
+ },
218
+ nftCollections: result.collections
219
+ });
220
+ } catch (error) {
221
+ console.error('[NftService] Refresh failed:', error);
222
+ this.nftStateSubject.next({
223
+ ...this.nftStateSubject.getValue()
224
+ });
225
+ } finally {
226
+ this.isReloading = false;
227
+ }
228
+ }
229
+
230
+ /** Subscribe NFT state */
231
+ subscribeNftItem() {
232
+ return this.nftState$;
233
+ }
234
+ subscribeNftCollection() {
235
+ const getChains = () => this.state.activeChainSlugs;
236
+ return this.state.dbService.stores.nftCollection.subscribeNftCollection(getChains);
237
+ }
238
+
239
+ // TODO: Move NFT reset logic to this function after migration is complete
240
+ async forceReload() {
241
+ this.isReloading = true;
242
+ await (0, _utils2.waitTimeout)(1800);
243
+ this.isReloading = false;
244
+ await this.refreshNftData().catch(console.error);
245
+ }
177
246
  }
178
- exports.default = NftService;
247
+ exports.NftService = NftService;
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MultiChainNftFetcher = void 0;
7
+ var _registry = require("@subwallet/extension-base/services/nft-service/nft-handlers/registry");
8
+ // Copyright 2019-2022 @subwallet/extension-koni authors & contributors
9
+ // SPDX-License-Identifier: Apache-2.0
10
+
11
+ class MultiChainNftFetcher {
12
+ constructor(state) {
13
+ this.state = state;
14
+ }
15
+ handlerCache = new Map();
16
+ getOrCreate(chain, desc) {
17
+ const key = `${chain}:${desc.id}`;
18
+ let handler = this.handlerCache.get(key);
19
+ if (!handler) {
20
+ handler = desc.create(chain, this.state);
21
+ this.handlerCache.set(key, handler);
22
+ }
23
+ return handler;
24
+ }
25
+ getHandlersForChain(chainSlug) {
26
+ const chainInfo = this.state.chainService.getChainInfoByKey(chainSlug);
27
+ if (!chainInfo) {
28
+ return [];
29
+ }
30
+ return _registry.NFT_HANDLER_REGISTRY.filter(d => d.supports(chainInfo)).map(d => this.getOrCreate(chainSlug, d));
31
+ }
32
+ async fetch(addresses, chainSlugs) {
33
+ const allItems = [];
34
+ const allCollections = [];
35
+ const tasks = [];
36
+ for (const chain of chainSlugs) {
37
+ const handlers = this.getHandlersForChain(chain);
38
+ if (handlers.length === 0) {
39
+ console.warn(`[NftFetcher] No handler for chain: ${chain}`);
40
+ continue;
41
+ }
42
+ for (const handler of handlers) {
43
+ const handlerAddresses = handler.filterAddresses(addresses);
44
+ const task = handler.fetchPreview(handlerAddresses).then(result => {
45
+ allItems.push(...result.items);
46
+ allCollections.push(...result.collections);
47
+ }).catch(err => {
48
+ console.error(`[NftFetcher] Handler failed on ${chain}: handler.id`, err);
49
+ });
50
+ tasks.push(task);
51
+ }
52
+ }
53
+ await Promise.all(tasks);
54
+
55
+ // DEDUPLICATE
56
+ // Todo: Move logic DEDUPLICATE to each handler
57
+ const seenItemIds = new Set();
58
+ const seenCollectionKeys = new Set();
59
+ const uniqueItems = [];
60
+ const uniqueCollections = [];
61
+ for (const item of allItems) {
62
+ if (!seenItemIds.has(item.id)) {
63
+ seenItemIds.add(item.id);
64
+ uniqueItems.push(item);
65
+ }
66
+ }
67
+ for (const col of allCollections) {
68
+ const key = `${col.chain}:${col.collectionId}`;
69
+ if (!seenCollectionKeys.has(key)) {
70
+ seenCollectionKeys.add(key);
71
+ uniqueCollections.push(col);
72
+ }
73
+ }
74
+ return {
75
+ items: uniqueItems,
76
+ collections: uniqueCollections
77
+ };
78
+ }
79
+ async fetchFullListNftOfACollection(request) {
80
+ const {
81
+ chainInfo,
82
+ collectionId,
83
+ owners,
84
+ tokenIds
85
+ } = request;
86
+ const items = [];
87
+ const collections = [];
88
+ const handlers = this.getHandlersForChain(chainInfo.slug);
89
+ for (const handler of handlers) {
90
+ // Todo: Improve the full-list fetch feature
91
+ // if (!handler.supportsFetchFullNftList) {
92
+ // continue;
93
+ // }
94
+
95
+ const handlerOwners = handler.filterAddresses(owners);
96
+ try {
97
+ const result = await handler.fetchFullListNftOfACollection({
98
+ collectionId: collectionId,
99
+ owners: handlerOwners,
100
+ tokenIds: tokenIds,
101
+ chainInfo
102
+ });
103
+ items.push(...result.items);
104
+ collections.push(...result.collections);
105
+ } catch (e) {
106
+ console.error('[NftFetcher] fetchCollection failed', e);
107
+ }
108
+ }
109
+ return {
110
+ items,
111
+ collections
112
+ };
113
+ }
114
+ async fetchNftDetail(request) {
115
+ const {
116
+ chainSlug,
117
+ collectionId,
118
+ tokenId
119
+ } = request;
120
+ const items = [];
121
+ const collections = [];
122
+ const handlers = this.getHandlersForChain(chainSlug);
123
+ for (const handler of handlers) {
124
+ // Todo: Improve the detail nft fetch feature
125
+ // if (!handler.supportsFetchFullNftList) {
126
+ // continue;
127
+ // }
128
+
129
+ try {
130
+ return await handler.fetchNftDetail({
131
+ collectionId: collectionId,
132
+ tokenId: tokenId,
133
+ chainSlug
134
+ });
135
+ } catch (e) {
136
+ console.error('[NftFetcher] fetchCollection failed', e);
137
+ }
138
+ }
139
+ return {
140
+ items,
141
+ collections
142
+ };
143
+ }
144
+ }
145
+ exports.MultiChainNftFetcher = MultiChainNftFetcher;