@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.
- package/background/KoniTypes.d.ts +16 -6
- package/cjs/core/logic-validation/index.js +0 -12
- package/cjs/core/substrate/system-pallet.js +3 -0
- package/cjs/koni/api/nft/index.js +0 -14
- package/cjs/koni/background/cron.js +0 -17
- package/cjs/koni/background/handlers/Extension.js +13 -7
- package/cjs/koni/background/handlers/State.js +24 -6
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/helpers/process.js +49 -21
- package/cjs/services/balance-service/index.js +2 -2
- package/cjs/services/balance-service/transfer/xcm/utils.js +9 -9
- package/cjs/services/chain-service/constants.js +6 -2
- package/cjs/services/earning-service/handlers/native-staking/dtao.js +13 -13
- package/cjs/services/earning-service/handlers/native-staking/tao.js +16 -10
- package/cjs/services/earning-service/handlers/special.js +12 -4
- package/cjs/services/nft-service/index.js +219 -150
- package/cjs/services/nft-service/multi-chain-nft-fetcher.js +145 -0
- package/cjs/services/nft-service/nft-handlers/base-nft-handler.js +28 -0
- package/cjs/services/nft-service/nft-handlers/evm/evm-nft-handler.js +179 -0
- package/cjs/services/nft-service/nft-handlers/registry.js +37 -0
- package/cjs/services/nft-service/nft-handlers/unique/unique-nft-handler.js +187 -0
- package/cjs/services/storage-service/DatabaseService.js +1 -1
- package/cjs/services/swap-service/handler/asset-hub/handler.js +7 -4
- package/cjs/services/swap-service/handler/asset-hub/router.js +2 -66
- package/cjs/services/swap-service/handler/base-handler.js +4 -3
- package/cjs/services/swap-service/handler/hydradx-handler.js +9 -5
- package/cjs/services/swap-service/index.js +5 -4
- package/cjs/types/swap/index.js +4 -9
- package/cjs/utils/account/common.js +44 -8
- package/core/logic-validation/index.d.ts +0 -1
- package/core/logic-validation/index.js +0 -1
- package/core/substrate/system-pallet.js +3 -0
- package/koni/api/nft/index.js +1 -15
- package/koni/background/cron.d.ts +0 -1
- package/koni/background/cron.js +1 -18
- package/koni/background/handlers/Extension.d.ts +4 -0
- package/koni/background/handlers/Extension.js +13 -7
- package/koni/background/handlers/State.d.ts +8 -2
- package/koni/background/handlers/State.js +24 -6
- package/package.json +31 -11
- package/packageInfo.js +1 -1
- package/services/balance-service/helpers/process.d.ts +1 -1
- package/services/balance-service/helpers/process.js +48 -21
- package/services/balance-service/index.js +2 -2
- package/services/balance-service/transfer/xcm/utils.js +9 -9
- package/services/chain-service/constants.d.ts +3 -0
- package/services/chain-service/constants.js +3 -0
- package/services/earning-service/handlers/native-staking/dtao.js +12 -13
- package/services/earning-service/handlers/native-staking/tao.d.ts +2 -1
- package/services/earning-service/handlers/native-staking/tao.js +15 -10
- package/services/earning-service/handlers/special.js +13 -5
- package/services/nft-service/index.d.ts +42 -6
- package/services/nft-service/index.js +219 -151
- package/services/nft-service/multi-chain-nft-fetcher.d.ts +13 -0
- package/services/nft-service/multi-chain-nft-fetcher.js +138 -0
- package/services/nft-service/nft-handlers/base-nft-handler.d.ts +13 -0
- package/services/nft-service/nft-handlers/base-nft-handler.js +21 -0
- package/services/nft-service/nft-handlers/evm/evm-nft-handler.d.ts +9 -0
- package/services/nft-service/nft-handlers/evm/evm-nft-handler.js +171 -0
- package/services/nft-service/nft-handlers/registry.d.ts +11 -0
- package/services/nft-service/nft-handlers/registry.js +29 -0
- package/services/nft-service/nft-handlers/unique/unique-nft-handler.d.ts +12 -0
- package/services/nft-service/nft-handlers/unique/unique-nft-handler.js +177 -0
- package/services/storage-service/DatabaseService.d.ts +1 -1
- package/services/storage-service/DatabaseService.js +1 -1
- package/services/swap-service/handler/asset-hub/handler.js +7 -4
- package/services/swap-service/handler/asset-hub/router.d.ts +0 -4
- package/services/swap-service/handler/asset-hub/router.js +1 -64
- package/services/swap-service/handler/base-handler.js +4 -3
- package/services/swap-service/handler/hydradx-handler.js +9 -5
- package/services/swap-service/index.js +5 -4
- package/types/swap/index.d.ts +7 -35
- package/types/swap/index.js +3 -8
- package/types/yield/actions/join/step.d.ts +1 -0
- package/types/yield/actions/join/submit.d.ts +2 -1
- package/utils/account/common.d.ts +22 -1
- package/utils/account/common.js +44 -8
- package/cjs/core/logic-validation/swap.js +0 -235
- package/core/logic-validation/swap.d.ts +0 -26
- 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
|
-
|
|
127
|
-
if (
|
|
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
|
|
131
|
-
const
|
|
132
|
-
|
|
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:
|
|
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:
|
|
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.
|
|
8
|
-
var _types = require("@subwallet/
|
|
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
|
|
12
|
-
var
|
|
13
|
-
var
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const INITIAL_NFT_STATE = {
|
|
18
|
+
nftData: {
|
|
19
|
+
total: 0,
|
|
20
|
+
nftList: []
|
|
21
|
+
},
|
|
22
|
+
nftCollections: []
|
|
23
|
+
};
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
140
|
-
|
|
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
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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 (
|
|
173
|
-
console.error(
|
|
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.
|
|
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;
|