@@ -35,23 +33,25 @@
{localize('views.collectibles.collectionsGallery.title')}
- {String(queriedCollections.length ?? '')}
+ {String(Object.keys($selectedAccountCollections).length ?? '')}
- {#if collections.length}
-
-
+ {#if hasCollections}
+
{/if}
{#if features.collectibles.erc721.enabled}
{/if}
- {#if collections.length}
- {#if queriedCollections.length}
+ {#if hasCollections}
+ {#if Object.keys(queriedCollections).length > 0}
+ {#each Object.keys(queriedCollections) as collection}
+ {queriedCollections[collection].name}
+ {/each}
{:else}
{
- let hasEnoughFunds = false
+ let hasEnoughFunds = true
if (selectedToken && isEvmNetwork(selectedToken.networkId)) {
hasEnoughFunds = await canAccountMakeEvmTransaction(
$selectedAccountIndex,
diff --git a/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts b/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts
index a0102ef16b..758584959b 100644
--- a/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts
+++ b/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts
@@ -23,7 +23,7 @@ export function fetchL2BalanceForAccount(profileId: string, account: IAccountSta
}
if (features.collectibles.erc721.enabled) {
- void updateErc721NftsOwnership(account)
+ void updateErc721NftsOwnership(account, evmNetwork.id)
}
const l2TokenBalance = isIscChain(evmNetwork)
diff --git a/packages/shared/src/lib/core/network/classes/isc-chain.class.ts b/packages/shared/src/lib/core/network/classes/isc-chain.class.ts
index b2a2c18fee..7774f8fa0e 100644
--- a/packages/shared/src/lib/core/network/classes/isc-chain.class.ts
+++ b/packages/shared/src/lib/core/network/classes/isc-chain.class.ts
@@ -14,8 +14,7 @@ export class IscChain extends BaseEvmNetwork {
try {
const { rpcEndpoint, aliasAddress, apiEndpoint } = chainConfiguration
const _rpcEndpoint = `${rpcEndpoint}/v1/chains/${aliasAddress}/evm`
- chainConfiguration.rpcEndpoint = _rpcEndpoint
- super(chainConfiguration)
+ super({ ...chainConfiguration, rpcEndpoint: _rpcEndpoint })
this.aliasAddress = aliasAddress
this.apiEndpoint = apiEndpoint
diff --git a/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts b/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts
index cee6632566..b3c65cd83c 100644
--- a/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts
+++ b/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts
@@ -5,12 +5,13 @@ import { IErc721Nft } from '../interfaces'
import { getAllAccountNfts, persistedNftForActiveProfile, updatePersistedNft } from '../stores'
import { getOwnerOfErc721Nft } from '../utils'
import { get } from 'svelte/store'
+import { NetworkId } from '@core/network'
-export async function updateErc721NftsOwnership(account: IAccountState): Promise {
+export async function updateErc721NftsOwnership(account: IAccountState, networkId: NetworkId): Promise {
try {
const trackedErc721Nfts =
(getAllAccountNfts()[account.index]?.filter((nft) => {
- return nft.standard === NftStandard.Erc721
+ return nft.standard === NftStandard.Erc721 && nft.networkId === networkId
}) as IErc721Nft[]) ?? []
const promises = trackedErc721Nfts.map(async (nft) => {
const updatedOwner = await getOwnerOfErc721Nft(nft)
diff --git a/packages/shared/src/lib/core/nfts/interfaces/collection.interface.ts b/packages/shared/src/lib/core/nfts/interfaces/collection.interface.ts
new file mode 100644
index 0000000000..0d392293d2
--- /dev/null
+++ b/packages/shared/src/lib/core/nfts/interfaces/collection.interface.ts
@@ -0,0 +1,10 @@
+import { NftStandard } from '../enums'
+import { Nft } from './nft.interface'
+
+export interface Collection {
+ standard: NftStandard
+ name: string
+ type: string
+ uri: string
+ nfts: Nft[]
+}
diff --git a/packages/shared/src/lib/core/nfts/interfaces/index.ts b/packages/shared/src/lib/core/nfts/interfaces/index.ts
index 6d0e272141..0daf27508b 100644
--- a/packages/shared/src/lib/core/nfts/interfaces/index.ts
+++ b/packages/shared/src/lib/core/nfts/interfaces/index.ts
@@ -1,3 +1,4 @@
+export * from './collection.interface'
export * from './download-metadata.interface'
export * from './erc721-contract-metadata.interface'
export * from './nft-filter.interface'
diff --git a/packages/shared/src/lib/core/nfts/interfaces/nft.interface.ts b/packages/shared/src/lib/core/nfts/interfaces/nft.interface.ts
index 47183bd6b0..af716bf044 100644
--- a/packages/shared/src/lib/core/nfts/interfaces/nft.interface.ts
+++ b/packages/shared/src/lib/core/nfts/interfaces/nft.interface.ts
@@ -1,5 +1,4 @@
import { EvmNetworkId, NetworkId } from '@core/network/types'
-import { Address } from '@iota/sdk/out/types'
import { MimeType, NftStandard } from '../enums'
import { IDownloadMetadata, IErc721ContractMetadata, IErc721TokenMetadata, IIrc27Metadata } from '../interfaces'
@@ -10,7 +9,7 @@ export interface IIrc27Nft extends IBaseNft {
nftAddress: string
rawMetadata: string
metadata?: IIrc27Metadata
- issuer?: Address
+ issuer?: { type: number; aliasId?: string; nftId?: string }
timelockTime?: number
expirationTime?: number
latestOutputId: string
diff --git a/packages/shared/src/lib/core/nfts/stores/index.ts b/packages/shared/src/lib/core/nfts/stores/index.ts
index 34585e3cc5..45c2c513d3 100644
--- a/packages/shared/src/lib/core/nfts/stores/index.ts
+++ b/packages/shared/src/lib/core/nfts/stores/index.ts
@@ -2,6 +2,7 @@ export * from './all-account-nfts.store'
export * from './downloading-nft.store'
export * from './nft-download-queue.store'
export * from './nft-filter.store'
+export * from './selected-account-collections.store'
export * from './persisted-nfts.store'
export * from './selected-account-nfts.store'
export * from './selected-collectibles-tabs.store'
diff --git a/packages/shared/src/lib/core/nfts/stores/selected-account-collections.store.ts b/packages/shared/src/lib/core/nfts/stores/selected-account-collections.store.ts
new file mode 100644
index 0000000000..d537ca49d7
--- /dev/null
+++ b/packages/shared/src/lib/core/nfts/stores/selected-account-collections.store.ts
@@ -0,0 +1,58 @@
+import { derived, get, Readable, Writable, writable } from 'svelte/store'
+import { selectedAccountNfts } from './selected-account-nfts.store'
+import { NftStandard } from '../enums'
+import { Nft } from '../interfaces'
+import { getCollectionFromNft } from '../utils'
+import { Collections } from '../types'
+
+export const collectionsStore: Writable = writable({})
+
+async function updateCollections(nfts: Nft[]): Promise {
+ const existingCollections = get(collectionsStore)
+
+ if (nfts.length === 0) {
+ if (Object.keys(existingCollections).length > 0) {
+ collectionsStore.set({})
+ }
+ return
+ }
+
+ const collectionsUpdate = { ...existingCollections }
+
+ await Promise.all(
+ nfts.map(async (nft) => {
+ if (nft.standard !== NftStandard.Irc27 || !nft.issuer) {
+ return
+ }
+
+ const issuerId = nft.issuer.aliasId ?? nft.issuer.nftId
+ if (!issuerId) {
+ return
+ }
+
+ if (!collectionsUpdate[issuerId]) {
+ const collection = await getCollectionFromNft(nft)
+ if (collection) {
+ collectionsUpdate[issuerId] = { ...collection, nfts: [nft] }
+ }
+ } else {
+ const existingNfts = collectionsUpdate[issuerId].nfts
+ if (!existingNfts.find((existingNft) => existingNft.id === nft.id)) {
+ collectionsUpdate[issuerId].nfts.push(nft)
+ }
+ }
+ })
+ )
+ collectionsStore.set(collectionsUpdate)
+}
+
+selectedAccountNfts.subscribe((nfts) => {
+ void updateCollections(nfts)
+})
+
+export const selectedAccountCollections: Readable = derived(
+ collectionsStore,
+ ($collectionsStore) => $collectionsStore
+)
+
+export const collectionsSearchTerm: Writable = writable('')
diff --git a/packages/shared/src/lib/core/nfts/types/collections.type.ts b/packages/shared/src/lib/core/nfts/types/collections.type.ts
new file mode 100644
index 0000000000..32dae6f7df
--- /dev/null
+++ b/packages/shared/src/lib/core/nfts/types/collections.type.ts
@@ -0,0 +1,3 @@
+import { Collection } from '../interfaces'
+
+export type Collections = { [key: string]: Collection }
diff --git a/packages/shared/src/lib/core/nfts/types/index.ts b/packages/shared/src/lib/core/nfts/types/index.ts
index 3d34a70382..77e3cbecb6 100644
--- a/packages/shared/src/lib/core/nfts/types/index.ts
+++ b/packages/shared/src/lib/core/nfts/types/index.ts
@@ -1,2 +1,3 @@
+export * from './collections.type'
export * from './nft-download-options.type'
export * from './persisted-nft.type'
diff --git a/packages/shared/src/lib/core/nfts/utils/getCollectionFromNft.ts b/packages/shared/src/lib/core/nfts/utils/getCollectionFromNft.ts
new file mode 100644
index 0000000000..232f4d632e
--- /dev/null
+++ b/packages/shared/src/lib/core/nfts/utils/getCollectionFromNft.ts
@@ -0,0 +1,42 @@
+import { NftStandard } from '../enums'
+import { Collection, Nft } from '../interfaces'
+import { Converter } from '@core/utils'
+import { getClient } from '@core/profile-manager'
+import type { AliasOutput, MetadataFeature, NftOutput } from '@iota/sdk'
+import { FeatureType } from '@iota/sdk/out/types'
+
+export async function getCollectionFromNft(nft: Nft): Promise {
+ if (nft.standard !== NftStandard.Irc27) {
+ return
+ }
+
+ const { aliasId = '', nftId = '' } = nft.issuer ?? {}
+ if (!aliasId && !nftId) {
+ return
+ }
+
+ try {
+ const client = await getClient()
+ const outputId = aliasId ? await client.aliasOutputId(aliasId) : await client.nftOutputId(nftId)
+ if (!outputId) {
+ return
+ }
+
+ const outputResponse = await client.getOutput(outputId)
+ const output = outputResponse.output as AliasOutput | NftOutput
+
+ const metadataFeature = output.immutableFeatures?.find(
+ (feature) => feature.type === FeatureType.Metadata
+ ) as MetadataFeature
+
+ if (!metadataFeature?.data) {
+ return
+ }
+
+ const { standard, name, type, uri } = JSON.parse(Converter.hexToUtf8(metadataFeature.data))
+
+ return { standard, name, type, uri, nfts: [] }
+ } catch (error) {
+ console.error('Error retrieving collection from NFT:', error)
+ }
+}
diff --git a/packages/shared/src/lib/core/nfts/utils/index.ts b/packages/shared/src/lib/core/nfts/utils/index.ts
index 93e479f6d5..f8305f6b34 100644
--- a/packages/shared/src/lib/core/nfts/utils/index.ts
+++ b/packages/shared/src/lib/core/nfts/utils/index.ts
@@ -1,17 +1,19 @@
export * from './buildNftFromPersistedErc721Nft'
+export * from './buildPersistedErc721Nft'
export * from './checkIfNftShouldBeDownloaded'
+export * from './fetchWithTimeout'
+export * from './getCollectionFromNft'
+export * from './getFetchableNftUrls'
export * from './getFilePathForNft'
export * from './getNftsFromNftIds'
export * from './getOwnerOfErc721Nft'
-export * from './buildPersistedErc721Nft'
-export * from './getFetchableNftUrls'
export * from './getPrimaryNftUrl'
export * from './getSpendableStatusFromUnspentNftOutput'
-export * from './fetchWithTimeout'
export * from './isIrc27Nft'
-export * from './isNftOwnedByAnyAccount'
export * from './isNftLocked'
+export * from './isNftOwnedByAnyAccount'
export * from './isScamIrc27Nft'
export * from './isValidNftUri'
+export * from './isVisibleCollection'
export * from './isVisibleNft'
export * from './parseNftMetadata'
diff --git a/packages/shared/src/lib/core/nfts/utils/isVisibleCollection.ts b/packages/shared/src/lib/core/nfts/utils/isVisibleCollection.ts
new file mode 100644
index 0000000000..6c367e4cbd
--- /dev/null
+++ b/packages/shared/src/lib/core/nfts/utils/isVisibleCollection.ts
@@ -0,0 +1,20 @@
+import { get } from 'svelte/store'
+import { collectionsSearchTerm } from '../stores'
+import { Collection } from '../interfaces'
+
+export function isVisibleCollection(collection: Collection): boolean {
+ const searchTerm = get(collectionsSearchTerm)
+
+ if (!isVisibleWithSearchTerm(collection, searchTerm)) {
+ return false
+ }
+
+ return true
+}
+
+function isVisibleWithSearchTerm(collection: Collection, searchTerm: string): boolean {
+ if (searchTerm) {
+ return collection.name.toLowerCase().includes(searchTerm.toLowerCase())
+ }
+ return true
+}