Skip to content
This repository was archived by the owner on Mar 2, 2026. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .cursor/rules/use-git-mv-for-moves.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
description: Moving files or folders
alwaysApply: false
---

# Use Git MV for File Moves

To preserve Git history and ensure features like Timeline in Cursor/VS Code work correctly across renames, always use `git mv old-path new-path` when moving or renaming files instead of manual file operations.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const flowContext = flow
},
mapMethod,
} = ctx;
consoleLog('flow - use #3 - check approval', mapMethod, origin, name, icon);
consoleLog('flow - use #3 - check approval', mapMethod, origin, name, icon, params);

const [approvalType, condition, { height = 599 } = {}] =
Reflect.getMetadata('APPROVAL', providerController, mapMethod) || [];
Expand Down
28 changes: 16 additions & 12 deletions apps/extension/src/background/controller/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ import {
type UserInfoResponse,
} from '@onflow/flow-wallet-shared/types/network-types';
import {
type NFTCollectionData,
type NFTCollections,
type CollectionNftItems,
type CadenceCollectionDetails,
} from '@onflow/flow-wallet-shared/types/nft-types';
import { type CategoryScripts } from '@onflow/flow-wallet-shared/types/script-types';
import { type TokenInfo } from '@onflow/flow-wallet-shared/types/token-info';
Expand Down Expand Up @@ -142,9 +142,9 @@ import {
coinListKey,
evmNftCollectionListKey,
type EvmNftCollectionListStore,
evmNftIdsKey,
type EvmNftIdsStore,
getCachedNftCollection,
evmNftCollectionsDetailsPageKey,
type EvmCollectionNftIdsStore,
getCachedCollectionNftItemsPage,
getCachedScripts,
mainAccountsKey,
nftCatalogCollectionsKey,
Expand Down Expand Up @@ -1181,13 +1181,13 @@ export class WalletController extends BaseController {
reqeustEvmNft = async () => {
const address = await this.getEvmAddress();
const network = await this.getNetwork();
const evmList = await openapiService.EvmNFTID(network, address);
const evmList = await openapiService.EvmNftCollectionDetails(network, address);
return evmList;
};

EvmNFTcollectionList = async (collection) => {
const address = await this.getEvmAddress();
const evmList = await openapiService.EvmNFTcollectionList(address, collection);
const evmList = await openapiService.fetchEvmCollectionNftItemList(address, collection);
return evmList;
};

Expand Down Expand Up @@ -3250,9 +3250,9 @@ export class WalletController extends BaseController {
address: string,
collectionId: string,
offset = 0
): Promise<NFTCollectionData | undefined> => {
): Promise<CollectionNftItems | undefined> => {
const network = await this.getNetwork();
const list = await getCachedNftCollection(network, address, collectionId, offset);
const list = await getCachedCollectionNftItemsPage(network, address, collectionId, offset);
if (!list) {
return this.refreshSingleCollection(address, collectionId, offset);
}
Expand All @@ -3263,15 +3263,17 @@ export class WalletController extends BaseController {
address: string,
collectionId: string,
offset: number
): Promise<NFTCollectionData | undefined> => {
): Promise<CollectionNftItems | undefined> => {
const network = await this.getNetwork();

return nftService.loadSingleNftCollection(network, address, collectionId, `${offset || 0}`);
};

getCollectionCache = async (address: string) => {
const network = await this.getNetwork();
const list = await getValidData<NFTCollections[]>(nftCatalogCollectionsKey(network, address));
const list = await getValidData<CadenceCollectionDetails[]>(
nftCatalogCollectionsKey(network, address)
);
if (!list || list.length === 0) {
return await this.refreshCollection(address);
}
Expand Down Expand Up @@ -3532,7 +3534,9 @@ export class WalletController extends BaseController {
throw new Error('Invalid Ethereum address');
}
const network = await this.getNetwork();
const cacheData = await getValidData<EvmNftIdsStore>(evmNftIdsKey(network, address));
const cacheData = await getValidData<EvmCollectionNftIdsStore>(
evmNftCollectionsDetailsPageKey(network, address)
);
if (cacheData) {
return cacheData;
}
Expand Down
100 changes: 84 additions & 16 deletions apps/extension/src/core/service/nft-evm.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import {
type EvmCollectionNftItemListPage,
type EvmCollectionDetails,
} from '@onflow/flow-wallet-shared/types';
import { isValidEthereumAddress } from '@onflow/flow-wallet-shared/utils/address';

import {
evmNftCollectionListKey,
evmNftCollectionListRefreshRegex,
evmNftIdsKey,
evmNftIdsRefreshRegex,
evmCollectionNftItemsPageRefreshRegex,
entireEvmCollectionNftItemsRefreshRegex,
evmCollectionDetailsRefreshRegex,
evmCollectionDetailsKey,
evmCollectionNftItemsPageKey,
NFT_LIST_PAGE_SIZE,
type EntireEvmCollectionNftItemsStore,
} from '@/data-model/cache-data-keys';

import { openapiService } from '.';
Expand All @@ -13,53 +20,114 @@ import { fclConfirmNetwork } from '../utils/fclConfig';

class EvmNfts {
init = async () => {
registerRefreshListener(evmNftCollectionListRefreshRegex, this.loadEvmCollectionList);
registerRefreshListener(evmNftIdsRefreshRegex, this.loadEvmNftIds);
registerRefreshListener(
evmCollectionNftItemsPageRefreshRegex,
this.loadEvmCollectionNftItemListPage
);
registerRefreshListener(evmCollectionDetailsRefreshRegex, this.loadEvmCollectionDetailsList);
registerRefreshListener(
entireEvmCollectionNftItemsRefreshRegex,
this.loadEntireEvmCollectionList
);
};

loadEvmNftIds = async (network: string, address: string) => {
loadEvmCollectionDetailsList = async (
network: string,
address: string
): Promise<EvmCollectionDetails[]> => {
if (!(await fclConfirmNetwork(network))) {
// Do nothing if the network is switched
// Don't update the cache
return [];
}
const result = await openapiService.EvmNFTID(network, address);
const result = await openapiService.fetchEvmNftCollectionDetailsList(network, address);

setCachedData(evmNftIdsKey(network, address), result);
setCachedData(evmCollectionDetailsKey(network, address), result);
return result;
};

loadEvmCollectionList = async (
loadEvmCollectionNftItemListPage = async (
network: string,
address: string,
collectionIdentifier: string,
offset: string
) => {
offset: string // For EVM, offset can be a JWT token string or a number
): Promise<EvmCollectionNftItemListPage | undefined> => {
if (!isValidEthereumAddress(address)) {
throw new Error('Invalid Ethereum address');
}

if (!(await fclConfirmNetwork(network))) {
// Do nothing if the network is switched
// Don't update the cache
return [];
return undefined;
}

// For EVM, offset can be a JWT token string
// Don't convert to integer if it's a JWT token
const offsetParam = offset && !isNaN(Number(offset)) ? parseInt(offset) : offset;

const result = await openapiService.EvmNFTcollectionList(
const result = await openapiService.fetchEvmCollectionNftItemListPage(
address,
collectionIdentifier,
50,
NFT_LIST_PAGE_SIZE,
offsetParam as string | number
);

setCachedData(evmNftCollectionListKey(network, address, collectionIdentifier, offset), result);
setCachedData(
evmCollectionNftItemsPageKey(network, address, collectionIdentifier, offset),
result
);
return result;
};

loadEntireEvmCollectionList = async (
network: string,
address: string,
collectionIdentifier: string
): Promise<EntireEvmCollectionNftItemsStore | undefined> => {
if (!isValidEthereumAddress(address)) {
throw new Error('Invalid Ethereum address');
}

if (!(await fclConfirmNetwork(network))) {
// Do nothing if the network is switched
// Don't update the cache
return undefined;
}

// We can't run this in parallel as we don't know the total number of pages
// So we need to run it sequentially
let entireEvmCollectionNftItemList: EntireEvmCollectionNftItemsStore | undefined = undefined;
let offset = '';
do {
const evmNftCollectionListPage = await this.loadEvmCollectionNftItemListPage(
network,
address,
collectionIdentifier,
offset
);
Comment on lines +98 to +108

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a timeout or maximum retry limit for the do-while loop. If the API keeps returning valid offsets but no actual NFTs, this could potentially run indefinitely.

if (!evmNftCollectionListPage) {
break;
}
if (!entireEvmCollectionNftItemList) {
entireEvmCollectionNftItemList = {
nftCount: evmNftCollectionListPage.nftCount,
nfts: [...evmNftCollectionListPage.nfts],
collection: evmNftCollectionListPage.collection,
};
} else {
entireEvmCollectionNftItemList.nfts = [
...entireEvmCollectionNftItemList.nfts,
...evmNftCollectionListPage.nfts,
];
entireEvmCollectionNftItemList.nftCount += evmNftCollectionListPage.nftCount;
}
offset = evmNftCollectionListPage.offset ?? '';
} while (offset);

return entireEvmCollectionNftItemList;
};

clearEvmNfts = async () => {};
}

Expand Down
Loading
Loading