@subwallet/extension-base 1.3.79-1 → 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 +13 -3
- 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 +6 -5
- 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/chain-service/constants.js +6 -2
- package/cjs/services/earning-service/handlers/special.js +3 -2
- 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/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 +1 -0
- package/koni/background/handlers/Extension.js +6 -5
- package/koni/background/handlers/State.d.ts +8 -2
- package/koni/background/handlers/State.js +24 -6
- package/package.json +31 -6
- 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/chain-service/constants.d.ts +3 -0
- package/services/chain-service/constants.js +3 -0
- package/services/earning-service/handlers/special.js +4 -3
- 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
|
@@ -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;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BaseNftHandler = void 0;
|
|
7
|
+
// Copyright 2019-2022 @subwallet/extension-base authors & contributors
|
|
8
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
9
|
+
|
|
10
|
+
const EMPTY_NFT_RESULT = {
|
|
11
|
+
items: [],
|
|
12
|
+
collections: []
|
|
13
|
+
};
|
|
14
|
+
class BaseNftHandler {
|
|
15
|
+
constructor(chain) {
|
|
16
|
+
this.chain = chain;
|
|
17
|
+
}
|
|
18
|
+
// Optional method - subclasses can choose to implement or not
|
|
19
|
+
fetchFullListNftOfACollection(_request) {
|
|
20
|
+
return Promise.resolve(EMPTY_NFT_RESULT);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Optional method - subclasses can choose to implement or not
|
|
24
|
+
fetchNftDetail(_request) {
|
|
25
|
+
return Promise.resolve(EMPTY_NFT_RESULT);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.BaseNftHandler = BaseNftHandler;
|