diff --git a/packages/extension-base/src/koni/background/cron.ts b/packages/extension-base/src/koni/background/cron.ts index 2ef0c6c0786..f30054b7062 100644 --- a/packages/extension-base/src/koni/background/cron.ts +++ b/packages/extension-base/src/koni/background/cron.ts @@ -247,7 +247,7 @@ export class KoniCron { return; } - this.state.nftDetectionService.fetchEvmCollectionsWithPreview(addresses) + this.state.nftService.syncPreview(address) .catch((err) => console.warn(`[Cron] NFT detection failed for ${address}:`, err)); }; }; diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 3c117b2afb0..e8db4291479 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -1323,7 +1323,7 @@ export default class KoniExtension { } private async handleGetNftFullList (request: NftFullListRequest): Promise { - return this.#koniState.nftDetectionService.getFullNftInstancesByCollection(request); + return this.#koniState.nftService.syncFull(request); } private getStakingReward (): Promise { diff --git a/packages/extension-base/src/koni/background/handlers/State.ts b/packages/extension-base/src/koni/background/handlers/State.ts index bac073fedca..f98dadd3b69 100644 --- a/packages/extension-base/src/koni/background/handlers/State.ts +++ b/packages/extension-base/src/koni/background/handlers/State.ts @@ -32,7 +32,7 @@ import { KeyringService } from '@subwallet/extension-base/services/keyring-servi import MigrationService from '@subwallet/extension-base/services/migration-service'; import MintCampaignService from '@subwallet/extension-base/services/mint-campaign-service'; import MktCampaignService from '@subwallet/extension-base/services/mkt-campaign-service'; -import NftService from '@subwallet/extension-base/services/nft-service'; +import { NftService } from '@subwallet/extension-base/services/nft-service'; import NotificationService from '@subwallet/extension-base/services/notification-service/NotificationService'; import { PriceService } from '@subwallet/extension-base/services/price-service'; import RequestService from '@subwallet/extension-base/services/request-service'; @@ -136,7 +136,7 @@ export default class KoniState { readonly mintCampaignService: MintCampaignService; readonly campaignService: CampaignService; readonly mktCampaignService: MktCampaignService; - readonly nftDetectionService: NftService; + readonly nftService: NftService; readonly buyService: BuyService; readonly earningService: EarningService; readonly feeService: FeeService; @@ -176,7 +176,7 @@ export default class KoniState { this.campaignService = new CampaignService(this); this.mktCampaignService = new MktCampaignService(this); - this.nftDetectionService = new NftService(this); + this.nftService = new NftService(this); this.buyService = new BuyService(this); this.earningService = new EarningService(this); this.swapService = new SwapService(this); diff --git a/packages/extension-base/src/services/nft-service/base/abstract-nft-service.ts b/packages/extension-base/src/services/nft-service/base/abstract-nft-service.ts new file mode 100644 index 00000000000..894283f3884 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/base/abstract-nft-service.ts @@ -0,0 +1,22 @@ +// Copyright 2019-2022 @subwallet/extension-koni authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { NftCollection, NftFullListRequest, NftItem } from '@subwallet/extension-base/background/KoniTypes'; +import KoniState from '@subwallet/extension-base/koni/background/handlers/State'; + +export interface FetchCollectionsResult { + items: NftItem[]; + collections: NftCollection[]; +} + +export abstract class AbstractNftService { + protected state: KoniState; + + constructor (state: KoniState) { + this.state = state; + } + + abstract detectPreview(addresses: string[]): Promise; + // abstract fetchFull(address: string): Promise; + abstract getFullNftInstances(request: NftFullListRequest): Promise; +} diff --git a/packages/extension-base/src/services/nft-service/base/types.ts b/packages/extension-base/src/services/nft-service/base/types.ts new file mode 100644 index 00000000000..5ca62fc78b3 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/base/types.ts @@ -0,0 +1,4 @@ +// Copyright 2019-2022 @subwallet/extension-koni authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +// Utils function diff --git a/packages/extension-base/src/services/nft-service/base/utils.ts b/packages/extension-base/src/services/nft-service/base/utils.ts new file mode 100644 index 00000000000..5ca62fc78b3 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/base/utils.ts @@ -0,0 +1,4 @@ +// Copyright 2019-2022 @subwallet/extension-koni authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +// Utils function diff --git a/packages/extension-base/src/services/nft-service/index.ts b/packages/extension-base/src/services/nft-service/index.ts index e7699fe513b..f9471717e0d 100644 --- a/packages/extension-base/src/services/nft-service/index.ts +++ b/packages/extension-base/src/services/nft-service/index.ts @@ -1,249 +1,49 @@ -// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// Copyright 2019-2022 @subwallet/extension-koni authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { _AssetType } from '@subwallet/chain-list/types'; -import { NftCollection, NftFullListRequest, NftItem } from '@subwallet/extension-base/background/KoniTypes'; import KoniState from '@subwallet/extension-base/koni/background/handlers/State'; -import { _getEvmChainId } from '@subwallet/extension-base/services/chain-service/utils'; -import { baseParseIPFSUrl } from '@subwallet/extension-base/utils'; -import { getKeypairTypeByAddress } from '@subwallet/keyring'; -import { EthereumKeypairTypes } from '@subwallet/keyring/types'; -import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk'; -import { BlockscoutNftInstanceRaw } from '@subwallet-monorepos/subwallet-services-sdk/services/blockscout/types'; - -/** - * NFT detection service - * Responsible for managing NFT detection jobs per address - */ - -interface SdkToken { - address_hash: string; - name?: string | null; - symbol?: string | null; - type?: _AssetType.ERC721; - icon_url?: string | null; -} - -interface SdkCollection { - amount: string; - token: SdkToken; - token_instances: BlockscoutNftInstanceRaw[]; -} - -type SdkCollectionsByChain = Record; - -function mapSdkToNftItem ( - rawInstance: BlockscoutNftInstanceRaw, - chain: string, - collectionId: string, - owner: string -): NftItem | null { - const metadata = rawInstance.metadata || {}; - - const image = metadata.image || rawInstance.image_url || rawInstance.media_url || ''; - - const attributes = Array.isArray(metadata.attributes) ? metadata.attributes : []; - - let rarity: string | undefined; - const properties: Record = {}; - - for (const attr of attributes) { - try { - const key = attr.trait_type?.trim(); - - if (!key) { - continue; - } - - let value = attr.value as unknown as string | number | boolean | null; - - if (typeof value === 'string') { - const lower = value.toLowerCase(); - - if (lower === 'true') { - value = true; - } else if (lower === 'false') { - value = false; - } - } - - properties[key] = value; - - if (key.toLowerCase() === 'rarity') { - rarity = String(value); - } - } catch { - } +import EvmNftService from '@subwallet/extension-base/services/nft-service/multichain/evm/evm-nft-service'; +import { UniqueNftService } from '@subwallet/extension-base/services/nft-service/multichain/unique'; +import { NftServiceRegistry } from '@subwallet/extension-base/services/nft-service/registry/nft-service-registry'; + +export class NftService { + private registry: NftServiceRegistry; + constructor (private state: KoniState) { + this.registry = new NftServiceRegistry(); + this.registerDefaultServices(); } - const hasProperties = Object.keys(properties).length > 0; - const normalizedType = rawInstance.token_type?.replace('-', '')?.toUpperCase(); - - // Only support ERC721 - if (normalizedType !== 'ERC721') { - return null; + private registerDefaultServices (): void { + this.registry.register('evm', new EvmNftService(this.state)); + this.registry.register('unique', new UniqueNftService(this.state)); } - return { - id: rawInstance.id?.toString(), - chain, - collectionId, - owner: rawInstance.owner || owner, - - originAsset: undefined, - name: metadata.name || `#${rawInstance.id}`, - image: baseParseIPFSUrl(image), - externalUrl: rawInstance.external_app_url || undefined, - rarity, - description: metadata.description || undefined, - properties: hasProperties ? properties : null, + // Cron trigger (detect collection and preview nft) + async syncPreview (address: string) { + const chains = this.registry.getAllSupportedChains(); - type: normalizedType === 'ERC721' ? _AssetType.ERC721 : _AssetType.ERC721, // currently only support ERC721 - rmrk_ver: undefined, - onChainOption: undefined, - assetHubType: undefined - }; -} + for (const chain of chains) { + const service = this.registry.getService(chain); -function mapSdkToCollection (raw: SdkCollection, chain: string): NftCollection { - const token = raw.token || {}; - - return { - // must-have - collectionId: token.address_hash, - chain, - originAsset: undefined, - - // optional - collectionName: token.name || token.symbol || 'Unknown Collection', - image: token.icon_url || undefined, - itemCount: Number(raw.amount) || raw.token_instances?.length || 0, - externalUrl: undefined - }; -} - -export default class NftService { - private inProgress = new Set(); - private state: KoniState; - - constructor (state: KoniState) { - this.state = state; - } + const { collections, items } = await service.detectPreview(address); - async fetchEvmCollectionsWithPreview (addresses: string[]) { - for (const address of addresses) { - const type = getKeypairTypeByAddress(address); - const typeValid = [...EthereumKeypairTypes].includes(type); - - if (typeValid) { - if (this.inProgress.has(address)) { - console.log(`[NftService] ${address} already running`); - - continue; - } - - this.inProgress.add(address); - - try { - const nftDetectionApi = subwalletApiSdk.nftDetectionApi; - - if (!nftDetectionApi?.getEvmNftCollectionsByAddress) { - console.warn('[NftService] NftDetectionApi not available'); - - continue; - } - - const rawData: SdkCollectionsByChain = await nftDetectionApi.getEvmNftCollectionsByAddress(address); - - const allItems: NftItem[] = []; - const allCollections: NftCollection[] = []; - - for (const [chain, collections] of Object.entries(rawData)) { - if (!Array.isArray(collections)) { - continue; - } - - for (const col of collections) { - const mappedCollection = mapSdkToCollection(col, chain); - - allCollections.push(mappedCollection); - - if (Array.isArray(col.token_instances)) { - const items = col.token_instances.map((inst) => - mapSdkToNftItem(inst, chain, mappedCollection.collectionId, address) - ).filter((i): i is NftItem => Boolean(i)); - - allItems.push(...items); - } - } - } - - await this.state.handleDetectedNftCollections(allCollections); - await this.state.handleDetectedNfts(address, allItems); - } catch (err) { - console.warn(`[NftService] detect error for ${address}`, err); - } finally { - this.inProgress.delete(address); - } - } + await this.state.handleDetectedNftCollections(collections); + await this.state.handleDetectedNfts(address, items); } } - async getFullNftInstancesByCollection (request: NftFullListRequest): Promise { - const { chainInfo, contractAddress, owners } = request; - const chainId = _getEvmChainId(chainInfo); - - if (!contractAddress || !owners || !chainId) { - console.warn('[NftService] missing params for getFullNftInstancesByCollection'); - - return false; - } - - try { - const nftDetectionApi = subwalletApiSdk.nftDetectionApi; - - if (!nftDetectionApi?.getAllNftInstances) { - console.warn('[NftService] getAllNftInstances not available'); - - return false; - } - - const ownerList = Array.isArray(owners) ? owners : [owners]; - - for (const eachOwner of ownerList) { - try { - const instances = await nftDetectionApi.getAllNftInstances( - contractAddress, - eachOwner, - chainId.toString() - ); - - if (!Array.isArray(instances)) { - continue; - } - - console.log('FOR TESTER (before)', instances); - - const nftList = instances.map((inst) => - mapSdkToNftItem(inst, chainInfo.slug, contractAddress, eachOwner) - ).filter((i): i is NftItem => Boolean(i)); - - console.log('FOR TESTER (after)', nftList); + // UI trigger gọi hàm này (fetch full list nft) + async syncFull (addresses: string[]) { + const address = addresses[0]; + const chains = this.registry.getAllSupportedChains(); - await this.state.handleDetectedNfts(eachOwner, nftList); - } catch (innerErr) { - console.warn(`[NftService] getAllNftInstances failed for ${eachOwner}`, innerErr); - } - } + for (const chain of chains) { + const service = this.registry.getService(chain); - return true; - } catch (err) { - console.error( - `[NftDetectionService] getFullNftInstancesByCollection error for ${contractAddress}`, - err - ); + const { collections, items } = await service.getFullNftInstances(address); - return false; + await this.state.handleDetectedNftCollections(collections); + await this.state.handleDetectedNfts(address, items); } } } diff --git a/packages/extension-base/src/services/nft-service/multichain/evm/evm-nft-adapter.ts b/packages/extension-base/src/services/nft-service/multichain/evm/evm-nft-adapter.ts new file mode 100644 index 00000000000..09a663baeb4 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/multichain/evm/evm-nft-adapter.ts @@ -0,0 +1,113 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { _AssetType } from '@subwallet/chain-list/types'; +import { NftCollection, NftItem } from '@subwallet/extension-base/background/KoniTypes'; +import { baseParseIPFSUrl } from '@subwallet/extension-base/utils'; +import { BlockscoutNftInstanceRaw } from '@subwallet-monorepos/subwallet-services-sdk/services/blockscout/types'; + +/** + * Adapter: map SDK raw (blockscout) data -> internal NftItem / NftCollection + * Keep all mapping logic in adapter so EvmNftService remains clean. + */ + +export interface SdkToken { + address_hash: string; + name?: string | null; + symbol?: string | null; + type?: _AssetType.ERC721; + icon_url?: string | null; +} + +export interface SdkCollection { + amount: string; + token: SdkToken; + token_instances: BlockscoutNftInstanceRaw[]; +} + +export type SdkCollectionsByChain = Record; + +export function mapSdkToNftItem ( + rawInstance: BlockscoutNftInstanceRaw, + chain: string, + collectionId: string, + owner: string +): NftItem { + const metadata = rawInstance.metadata || {}; + + const image = metadata.image || rawInstance.image_url || rawInstance.media_url || ''; + + const attributes = Array.isArray(metadata.attributes) ? metadata.attributes : []; + + let rarity: string | undefined; + const properties: Record = {}; + + for (const attr of attributes) { + try { + const key = attr.trait_type?.trim(); + + if (!key) { + continue; + } + + let value = attr.value as unknown as string | number | boolean | null; + + if (typeof value === 'string') { + const lower = value.toLowerCase(); + + if (lower === 'true') { + value = true; + } else if (lower === 'false') { + value = false; + } + } + + properties[key] = value; + + if (key.toLowerCase() === 'rarity') { + rarity = String(value); + } + } catch { + } + } + + const hasProperties = Object.keys(properties).length > 0; + const normalizedType = rawInstance.token_type?.replace('-', '')?.toUpperCase(); + + return { + id: rawInstance.id?.toString(), + chain, + collectionId, + owner: rawInstance.owner || owner, + + originAsset: undefined, + name: metadata.name || `#${rawInstance.id}`, + image: baseParseIPFSUrl(image), + externalUrl: rawInstance.external_app_url || undefined, + rarity, + description: metadata.description || undefined, + properties: hasProperties ? properties : null, + + type: normalizedType === 'ERC721' ? _AssetType.ERC721 : _AssetType.ERC721, // currently only support ERC721 + rmrk_ver: undefined, + onChainOption: undefined, + assetHubType: undefined + }; +} + +export function mapSdkToCollection (raw: SdkCollection, chain: string): NftCollection { + const token = raw.token || {}; + + return { + // must-have + collectionId: token.address_hash, + chain, + originAsset: undefined, + + // optional + collectionName: token.name || token.symbol || 'Unknown Collection', + image: token.icon_url || undefined, + itemCount: Number(raw.amount) || raw.token_instances?.length || 0, + externalUrl: undefined + }; +} diff --git a/packages/extension-base/src/services/nft-service/multichain/evm/evm-nft-service.ts b/packages/extension-base/src/services/nft-service/multichain/evm/evm-nft-service.ts new file mode 100644 index 00000000000..b35041a4939 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/multichain/evm/evm-nft-service.ts @@ -0,0 +1,139 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { NftCollection, NftFullListRequest, NftItem } from '@subwallet/extension-base/background/KoniTypes'; +import KoniState from '@subwallet/extension-base/koni/background/handlers/State'; +import { _getEvmChainId } from '@subwallet/extension-base/services/chain-service/utils'; +import { AbstractNftService } from '@subwallet/extension-base/services/nft-service/base/abstract-nft-service'; +import { mapSdkToCollection, mapSdkToNftItem, SdkCollectionsByChain } from '@subwallet/extension-base/services/nft-service/multichain/evm/evm-nft-adapter'; +import { getKeypairTypeByAddress } from '@subwallet/keyring'; +import { EthereumKeypairTypes } from '@subwallet/keyring/types'; +import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk'; + +/** + * EvmNftService + * - Uses SDK's nftDetectionApi for detection and detailed instance fetching + * - Keeps only orchestration and state update logic + * - Delegates mapping to adapter + */ + +export default class EvmNftService extends AbstractNftService { + private inProgress = new Set(); + + constructor (state: KoniState) { + super(state); + } + + async detectPreview (addresses: string[]) { + for (const address of addresses) { + const type = getKeypairTypeByAddress(address); + const typeValid = [...EthereumKeypairTypes].includes(type); + + if (typeValid) { + if (this.inProgress.has(address)) { + console.log(`[NftService] ${address} already running`); + + continue; + } + + this.inProgress.add(address); + + try { + const nftDetectionApi = subwalletApiSdk.nftDetectionApi; + + if (!nftDetectionApi?.getEvmNftCollectionsByAddress) { + console.warn('[NftService] NftDetectionApi not available'); + + continue; + } + + const rawData: SdkCollectionsByChain = await nftDetectionApi.getEvmNftCollectionsByAddress(address); + + const allItems: NftItem[] = []; + const allCollections: NftCollection[] = []; + + for (const [chain, collections] of Object.entries(rawData)) { + if (!Array.isArray(collections)) { + continue; + } + + for (const col of collections) { + const mappedCollection = mapSdkToCollection(col, chain); + + allCollections.push(mappedCollection); + + if (Array.isArray(col.token_instances)) { + const items = col.token_instances.map((inst) => + mapSdkToNftItem(inst, chain, mappedCollection.collectionId, address) + ); + + allItems.push(...items); + } + } + } + + await this.state.handleDetectedNftCollections(allCollections); + await this.state.handleDetectedNfts(address, allItems); + } catch (err) { + console.warn(`[NftService] detect error for ${address}`, err); + } finally { + this.inProgress.delete(address); + } + } + } + } + + async getFullNftInstances (request: NftFullListRequest): Promise { + const { chainInfo, contractAddress, owners } = request; + const chainId = _getEvmChainId(chainInfo); + + if (!contractAddress || !owners || !chainId) { + console.warn('[NftService] missing params for getFullNftInstancesByCollection'); + + return false; + } + + try { + const nftDetectionApi = subwalletApiSdk.nftDetectionApi; + + if (!nftDetectionApi?.getAllNftInstances) { + console.warn('[NftService] getAllNftInstances not available'); + + return false; + } + + const ownerList = Array.isArray(owners) ? owners : [owners]; + + for (const eachOwner of ownerList) { + try { + const instances = await nftDetectionApi.getAllNftInstances( + contractAddress, + eachOwner, + chainId.toString() + ); + + if (!Array.isArray(instances)) { + continue; + } + + const nftList = instances.map((inst) => + mapSdkToNftItem(inst, chainInfo.slug, contractAddress, eachOwner) + ); + + await this.state.handleDetectedNfts(eachOwner, nftList); + } catch (innerErr) { + console.warn(`[NftService] getAllNftInstances failed for ${eachOwner}`, innerErr); + } + } + + return true; + } catch (err) { + console.error( + `[NftDetectionService] getFullNftInstancesByCollection error for ${contractAddress}`, + err + ); + + return false; + } + } +} diff --git a/packages/extension-base/src/services/nft-service/multichain/evm/index.ts b/packages/extension-base/src/services/nft-service/multichain/evm/index.ts new file mode 100644 index 00000000000..bc3dd979d9e --- /dev/null +++ b/packages/extension-base/src/services/nft-service/multichain/evm/index.ts @@ -0,0 +1,5 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export * from './evm-nft-service'; +export * from './evm-nft-adapter'; diff --git a/packages/extension-base/src/services/nft-service/multichain/unique/index.ts b/packages/extension-base/src/services/nft-service/multichain/unique/index.ts new file mode 100644 index 00000000000..cba4deadf08 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/multichain/unique/index.ts @@ -0,0 +1,5 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export * from './unique-nft-adapter'; +export * from './unique-nft-service'; diff --git a/packages/extension-base/src/services/nft-service/multichain/unique/unique-nft-adapter.ts b/packages/extension-base/src/services/nft-service/multichain/unique/unique-nft-adapter.ts new file mode 100644 index 00000000000..ea256608987 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/multichain/unique/unique-nft-adapter.ts @@ -0,0 +1,5 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export const tmp = 1; +// Todo: function mapping data diff --git a/packages/extension-base/src/services/nft-service/multichain/unique/unique-nft-service.ts b/packages/extension-base/src/services/nft-service/multichain/unique/unique-nft-service.ts new file mode 100644 index 00000000000..6314abc03d3 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/multichain/unique/unique-nft-service.ts @@ -0,0 +1,26 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { NftFullListRequest } from '@subwallet/extension-base/background/KoniTypes'; +import KoniState from '@subwallet/extension-base/koni/background/handlers/State'; +import { AbstractNftService } from '@subwallet/extension-base/services/nft-service/base/abstract-nft-service'; + +export class UniqueNftService extends AbstractNftService { + constructor (state: KoniState) { + super(state); + } + + async detectPreview (addresses: string[]) { + // const nftDetectionApi = subwalletApiSdk.uniqueNftDetectionApi; + + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + async getFullNftInstances (request: NftFullListRequest): Promise { + // const nftDetectionApi = subwalletApiSdk.uniqueNftDetectionApi; + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + return true; + } +} diff --git a/packages/extension-base/src/services/nft-service/registry/nft-detector-service.ts b/packages/extension-base/src/services/nft-service/registry/nft-detector-service.ts new file mode 100644 index 00000000000..8841eb8581e --- /dev/null +++ b/packages/extension-base/src/services/nft-service/registry/nft-detector-service.ts @@ -0,0 +1,24 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { _ChainInfo } from '@subwallet/chain-list/types'; + +export function getPreferredNftServiceKey (chainInfo: _ChainInfo): string { + switch (chainInfo.name) { + case 'EVM': + return 'evm'; + case 'SUBSTRATE': + return 'substrate'; + case 'BITCOIN': + return 'bitcoin'; + default: + return 'evm'; // Fallback + } +} + +// export function isNftSupported (chainInfo: _ChainInfo): boolean { +// // Logic kiểm tra chain có support NFT không +// return chainInfo.hasNftSupport && +// !chainInfo.isTestnet && // Tuỳ config +// chainInfo.chainStatus === 'ACTIVE'; +// } diff --git a/packages/extension-base/src/services/nft-service/registry/nft-service-registry.ts b/packages/extension-base/src/services/nft-service/registry/nft-service-registry.ts new file mode 100644 index 00000000000..c49489f8604 --- /dev/null +++ b/packages/extension-base/src/services/nft-service/registry/nft-service-registry.ts @@ -0,0 +1,34 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { _ChainInfo } from '@subwallet/chain-list/types'; +import { AbstractNftService } from '@subwallet/extension-base/services/nft-service/base/abstract-nft-service'; +import { getPreferredNftServiceKey } from '@subwallet/extension-base/services/nft-service/registry/nft-detector-service'; + +export class NftServiceRegistry { + private services = new Map(); + + register (key: string, service: AbstractNftService) { + this.services.set(key, service); + } + + getService (chainInfo: _ChainInfo): AbstractNftService { + const key = getPreferredNftServiceKey(chainInfo); + const service = this.services.get(key); + + if (!service) { + throw new Error(`No NFT service registered for chain type: ${key}`); + } + + return service; + } + + getAllSupportedChains (): string[] { + // Cần lấy từ chain service hoặc state + // Ví dụ: + // return this.state.getNftSupportedChains(); + + // Hoặc hardcode tạm: + return ['ethereum', 'polygon', 'binance', 'polkadot', 'kusama']; + } +}