diff --git a/src/ComposableController.test.ts b/src/ComposableController.test.ts index 57d45b6dc74..0f1570e7f3d 100644 --- a/src/ComposableController.test.ts +++ b/src/ComposableController.test.ts @@ -1,7 +1,7 @@ import sinon from 'sinon'; import type { Patch } from 'immer'; import { TokensController } from './assets/TokensController'; -import { CollectiblesController } from './assets/CollectiblesController'; +import { NftController } from './assets/NftController'; import { AddressBookController } from './user/AddressBookController'; import { EnsController } from './third-party/EnsController'; import { @@ -123,7 +123,7 @@ const setupControllers = () => { messenger.subscribe('NetworkController:stateChange', listener), }); - const collectiblesController = new CollectiblesController({ + const nftController = new NftController({ onPreferencesStateChange: (listener) => preferencesController.subscribe(listener), onNetworkStateChange: (listener) => @@ -146,7 +146,7 @@ const setupControllers = () => { getERC1155TokenURI: assetContractController.getERC1155TokenURI.bind( assetContractController, ), - onCollectibleAdded: jest.fn(), + onNftAdded: jest.fn(), }); const tokensController = new TokensController({ @@ -162,7 +162,7 @@ const setupControllers = () => { networkController, preferencesController, assetContractController, - collectiblesController, + nftController, tokensController, }; }; @@ -179,7 +179,7 @@ describe('ComposableController', () => { composableMessenger, networkController, assetContractController, - collectiblesController, + nftController, tokensController, preferencesController, } = setupControllers(); @@ -187,7 +187,7 @@ describe('ComposableController', () => { const controller = new ComposableController( [ new AddressBookController(), - collectiblesController, + nftController, assetContractController, new EnsController(), networkController, @@ -200,10 +200,10 @@ describe('ComposableController', () => { expect(controller.state).toStrictEqual({ AddressBookController: { addressBook: {} }, AssetsContractController: {}, - CollectiblesController: { - allCollectibleContracts: {}, - allCollectibles: {}, - ignoredCollectibles: [], + NftController: { + allNftContracts: {}, + allNfts: {}, + ignoredNfts: [], }, TokensController: { allTokens: {}, @@ -231,7 +231,7 @@ describe('ComposableController', () => { lostIdentities: {}, selectedAddress: '', useTokenDetection: true, - useCollectibleDetection: false, + useNftDetection: false, openSeaEnabled: false, }, }); @@ -245,7 +245,7 @@ describe('ComposableController', () => { composableMessenger, networkController, assetContractController, - collectiblesController, + nftController, tokensController, preferencesController, } = setupControllers(); @@ -253,7 +253,7 @@ describe('ComposableController', () => { const controller = new ComposableController( [ new AddressBookController(), - collectiblesController, + nftController, assetContractController, new EnsController(), networkController, @@ -264,14 +264,14 @@ describe('ComposableController', () => { ); expect(controller.flatState).toStrictEqual({ addressBook: {}, - allCollectibleContracts: {}, - allCollectibles: {}, + allNftContracts: {}, + allNfts: {}, allTokens: {}, ensEntries: {}, featureFlags: {}, frequentRpcList: [], identities: {}, - ignoredCollectibles: [], + ignoredNfts: [], ignoredTokens: [], allIgnoredTokens: {}, detectedTokens: [], @@ -284,7 +284,7 @@ describe('ComposableController', () => { provider: { type: 'mainnet', chainId: NetworksChainId.mainnet }, selectedAddress: '', useTokenDetection: true, - useCollectibleDetection: false, + useNftDetection: false, openSeaEnabled: false, suggestedAssets: [], tokens: [], diff --git a/src/assets/AssetsContractController.test.ts b/src/assets/AssetsContractController.test.ts index 566c1e00dbf..3f45988e26f 100644 --- a/src/assets/AssetsContractController.test.ts +++ b/src/assets/AssetsContractController.test.ts @@ -120,10 +120,10 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should get ERC-721 collectible tokenId correctly', async () => { + it('should get ERC-721 NFT tokenId correctly', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const tokenId = await assetsContract.getERC721CollectibleTokenId( + const tokenId = await assetsContract.getERC721NftTokenId( ERC721_GODS_ADDRESS, '0x9a90bd8d1149a88b42a99cf62215ad955d6f498a', 0, @@ -190,7 +190,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should get ERC-721 collectible tokenURI correctly', async () => { + it('should get ERC-721 NFT tokenURI correctly', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const tokenId = await assetsContract.getERC721TokenURI( @@ -201,7 +201,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should throw an error when address given is not an ERC-721 collectible', async () => { + it('should throw an error when address given is not an ERC-721 NFT', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const result = async () => { @@ -216,7 +216,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should get ERC-721 collectible name', async () => { + it('should get ERC-721 NFT name', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const name = await assetsContract.getERC721AssetName(ERC721_GODS_ADDRESS); @@ -224,7 +224,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should get ERC-721 collectible symbol', async () => { + it('should get ERC-721 NFT symbol', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const symbol = await assetsContract.getERC721AssetSymbol( @@ -234,7 +234,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should throw missing provider error when getting ERC-721 collectible symbol when missing provider', async () => { + it('should throw missing provider error when getting ERC-721 NFT symbol when missing provider', async () => { const { assetsContract, messenger } = setupControllers(); await expect( assetsContract.getERC721AssetSymbol(ERC721_GODS_ADDRESS), @@ -252,7 +252,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should get ERC-721 collectible ownership', async () => { + it('should get ERC-721 NFT ownership', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const tokenId = await assetsContract.getERC721OwnerOf( @@ -263,7 +263,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should throw missing provider error when getting ERC-721 collectible ownership', async () => { + it('should throw missing provider error when getting ERC-721 NFT ownership', async () => { const { assetsContract, messenger } = setupControllers(); await expect( assetsContract.getERC721OwnerOf(ERC721_GODS_ADDRESS, '148332'), @@ -319,7 +319,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should get the balance of a ERC-1155 collectible for a given address', async () => { + it('should get the balance of a ERC-1155 NFT for a given address', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const balance = await assetsContract.getERC1155BalanceOf( @@ -331,7 +331,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should throw missing provider error when getting the balance of a ERC-1155 collectible when missing provider', async () => { + it('should throw missing provider error when getting the balance of a ERC-1155 NFT when missing provider', async () => { const { assetsContract, messenger } = setupControllers(); await expect( assetsContract.getERC1155BalanceOf( @@ -343,7 +343,7 @@ describe('AssetsContractController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should get the URI of a ERC-1155 collectible', async () => { + it('should get the URI of a ERC-1155 NFT', async () => { const { assetsContract, messenger } = setupControllers(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const expectedUri = `https://api.opensea.io/api/v1/metadata/${ERC1155_ADDRESS}/0x{id}`; diff --git a/src/assets/AssetsContractController.ts b/src/assets/AssetsContractController.ts index 12af9dd9533..ef79c38756d 100644 --- a/src/assets/AssetsContractController.ts +++ b/src/assets/AssetsContractController.ts @@ -6,8 +6,8 @@ import type { PreferencesState } from '../user/PreferencesController'; import { IPFS_DEFAULT_GATEWAY_URL } from '../constants'; import { SupportedTokenDetectionNetworks } from '../util'; import { NetworkState } from '../network/NetworkController'; -import { ERC721Standard } from './Standards/CollectibleStandards/ERC721/ERC721Standard'; -import { ERC1155Standard } from './Standards/CollectibleStandards/ERC1155/ERC1155Standard'; +import { ERC721Standard } from './Standards/NftStandards/ERC721/ERC721Standard'; +import { ERC1155Standard } from './Standards/NftStandards/ERC1155/ERC1155Standard'; import { ERC20Standard } from './Standards/ERC20Standard'; /** @@ -170,10 +170,10 @@ export class AssetsContractController extends BaseController< * * @param address - ERC721 asset contract address. * @param selectedAddress - Current account public address. - * @param index - A collectible counter less than `balanceOf(selectedAddress)`. + * @param index - An NFT counter less than `balanceOf(selectedAddress)`. * @returns Promise resolving to token identifier for the 'index'th asset assigned to 'selectedAddress'. */ - getERC721CollectibleTokenId( + getERC721NftTokenId( address: string, selectedAddress: string, index: number, @@ -181,11 +181,7 @@ export class AssetsContractController extends BaseController< if (this.erc721Standard === undefined) { throw new Error(MISSING_PROVIDER_ERROR); } - return this.erc721Standard.getCollectibleTokenId( - address, - selectedAddress, - index, - ); + return this.erc721Standard.getNftTokenId(address, selectedAddress, index); } /** @@ -328,50 +324,50 @@ export class AssetsContractController extends BaseController< * Query for balance of a given ERC 1155 token. * * @param userAddress - Wallet public address. - * @param collectibleAddress - ERC1155 asset contract address. - * @param collectibleId - ERC1155 asset identifier. + * @param nftAddress - ERC1155 asset contract address. + * @param nftId - ERC1155 asset identifier. * @returns Promise resolving to the 'balanceOf'. */ async getERC1155BalanceOf( userAddress: string, - collectibleAddress: string, - collectibleId: string, + nftAddress: string, + nftId: string, ): Promise { if (this.erc1155Standard === undefined) { throw new Error(MISSING_PROVIDER_ERROR); } return await this.erc1155Standard.getBalanceOf( - collectibleAddress, + nftAddress, userAddress, - collectibleId, + nftId, ); } /** * Transfer single ERC1155 token. * - * @param collectibleAddress - ERC1155 token address. + * @param nftAddress - ERC1155 token address. * @param senderAddress - ERC1155 token sender. * @param recipientAddress - ERC1155 token recipient. - * @param collectibleId - ERC1155 token id. + * @param nftId - ERC1155 token id. * @param qty - Quantity of tokens to be sent. * @returns Promise resolving to the 'transferSingle' ERC1155 token. */ async transferSingleERC1155( - collectibleAddress: string, + nftAddress: string, senderAddress: string, recipientAddress: string, - collectibleId: string, + nftId: string, qty: string, ): Promise { if (this.erc1155Standard === undefined) { throw new Error(MISSING_PROVIDER_ERROR); } return await this.erc1155Standard.transferSingle( - collectibleAddress, + nftAddress, senderAddress, recipientAddress, - collectibleId, + nftId, qty, ); } diff --git a/src/assets/CollectiblesController.test.ts b/src/assets/NftController.test.ts similarity index 63% rename from src/assets/CollectiblesController.test.ts rename to src/assets/NftController.test.ts index e3d9e675d0c..bacbd839d67 100644 --- a/src/assets/CollectiblesController.test.ts +++ b/src/assets/NftController.test.ts @@ -17,16 +17,15 @@ import { } from '../constants'; import { ControllerMessenger } from '../ControllerMessenger'; import { AssetsContractController } from './AssetsContractController'; -import { CollectiblesController } from './CollectiblesController'; +import { NftController } from './NftController'; const CRYPTOPUNK_ADDRESS = '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB'; const ERC721_KUDOSADDRESS = '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163'; const ERC721_KUDOS_TOKEN_ID = '1203'; -const ERC721_COLLECTIBLE_ADDRESS = '0x60F80121C31A0d46B5279700f9DF786054aa5eE5'; -const ERC721_COLLECTIBLE_ID = '1144858'; -const ERC1155_COLLECTIBLE_ADDRESS = - '0x495f947276749Ce646f68AC8c248420045cb7b5e'; -const ERC1155_COLLECTIBLE_ID = +const ERC721_NFT_ADDRESS = '0x60F80121C31A0d46B5279700f9DF786054aa5eE5'; +const ERC721_NFT_ID = '1144858'; +const ERC1155_NFT_ADDRESS = '0x495f947276749Ce646f68AC8c248420045cb7b5e'; +const ERC1155_NFT_ID = '40815311521795738946686668571398122012172359753720345430028676522525371400193'; const ERC721_DEPRESSIONIST_ADDRESS = '0x18E8E76aeB9E2d9FA2A2b88DD9CF3C8ED45c3660'; @@ -50,12 +49,12 @@ const DEPRESSIONIST_CLOUDFLARE_IPFS_SUBDOMAIN_PATH = getFormattedIpfsUrl( * Setup a test controller instance. * * @param options - Controller options. - * @param options.includeOnCollectibleAdded - Whether to include the "onCollectibleAdded" parameter. + * @param options.includeOnNftAdded - Whether to include the "onNftAdded" parameter. * @returns A collection of test controllers and stubs. */ function setupController({ - includeOnCollectibleAdded = false, -}: { includeOnCollectibleAdded?: boolean } = {}) { + includeOnNftAdded = false, +}: { includeOnNftAdded?: boolean } = {}) { const messenger: NetworkControllerMessenger = new ControllerMessenger().getRestricted({ name: 'NetworkController', @@ -72,11 +71,9 @@ function setupController({ onNetworkStateChange: (listener) => messenger.subscribe('NetworkController:stateChange', listener), }); - const onCollectibleAddedSpy = includeOnCollectibleAdded - ? jest.fn() - : undefined; + const onNftAddedSpy = includeOnNftAdded ? jest.fn() : undefined; - const collectiblesController = new CollectiblesController({ + const nftController = new NftController({ onPreferencesStateChange: (listener) => preferences.subscribe(listener), onNetworkStateChange: (listener) => messenger.subscribe('NetworkController:stateChange', listener), @@ -88,7 +85,7 @@ function setupController({ getERC1155BalanceOf: assetsContract.getERC1155BalanceOf.bind(assetsContract), getERC1155TokenURI: assetsContract.getERC1155TokenURI.bind(assetsContract), - onCollectibleAdded: onCollectibleAddedSpy, + onNftAdded: onNftAddedSpy, }); preferences.update({ @@ -98,15 +95,15 @@ function setupController({ return { assetsContract, - collectiblesController, + nftController, network, - onCollectibleAddedSpy, + onNftAddedSpy, preferences, messenger, }; } -describe('CollectiblesController', () => { +describe('NftController', () => { beforeAll(() => { nock.disableNetConnect(); }); @@ -155,7 +152,7 @@ describe('CollectiblesController', () => { .replyWithError(new TypeError('Failed to fetch')); nock(OPENSEA_PROXY_URL) - .get(`/asset/${ERC1155_COLLECTIBLE_ADDRESS}/${ERC1155_COLLECTIBLE_ID}`) + .get(`/asset/${ERC1155_NFT_ADDRESS}/${ERC1155_NFT_ID}`) .reply(200, { num_sales: 1, image_original_url: 'image.uri', @@ -178,22 +175,22 @@ describe('CollectiblesController', () => { }); it('should set default state', () => { - const { collectiblesController, messenger } = setupController(); + const { nftController, messenger } = setupController(); - expect(collectiblesController.state).toStrictEqual({ - allCollectibleContracts: {}, - allCollectibles: {}, - ignoredCollectibles: [], + expect(nftController.state).toStrictEqual({ + allNftContracts: {}, + allNfts: {}, + ignoredNfts: [], }); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - describe('addCollectible', () => { - it('should add collectible and collectible contract', async () => { - const { collectiblesController, messenger } = setupController(); + describe('addNft', () => { + it('should add NFT and NFT contract', async () => { + const { nftController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible('0x01', '1', { + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', @@ -202,9 +199,7 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ address: '0x01', description: 'description', @@ -217,9 +212,7 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ][0], + nftController.state.allNftContracts[selectedAddress][chainId][0], ).toStrictEqual({ address: '0x01', description: 'Description', @@ -232,11 +225,12 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should call onCollectibleAdded callback correctly when collectible is manually added', async () => { - const { collectiblesController, onCollectibleAddedSpy, messenger } = - setupController({ includeOnCollectibleAdded: true }); + it('should call onNftAdded callback correctly when NFT is manually added', async () => { + const { nftController, onNftAddedSpy, messenger } = setupController({ + includeOnNftAdded: true, + }); - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', @@ -244,7 +238,7 @@ describe('CollectiblesController', () => { favorite: false, }); - expect(onCollectibleAddedSpy).toHaveBeenCalledWith({ + expect(onNftAddedSpy).toHaveBeenCalledWith({ source: 'custom', tokenId: '1', address: '0x01', @@ -255,12 +249,13 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should call onCollectibleAdded callback correctly when collectible is added via detection', async () => { - const { collectiblesController, onCollectibleAddedSpy, messenger } = - setupController({ includeOnCollectibleAdded: true }); + it('should call onNftAdded callback correctly when NFT is added via detection', async () => { + const { nftController, onNftAddedSpy, messenger } = setupController({ + includeOnNftAdded: true, + }); const detectedUserAddress = '0x123'; - await collectiblesController.addCollectible( + await nftController.addNft( '0x01', '2', { @@ -270,11 +265,11 @@ describe('CollectiblesController', () => { standard: 'ERC721', favorite: false, }, - // this object in the third argument slot is only defined when the collectible is added via detection + // this object in the third argument slot is only defined when the NFT is added via detection { userAddress: detectedUserAddress, chainId: '0x2' }, ); - expect(onCollectibleAddedSpy).toHaveBeenCalledWith({ + expect(onNftAddedSpy).toHaveBeenCalledWith({ source: 'detected', tokenId: '2', address: '0x01', @@ -285,23 +280,22 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible by selected address', async () => { - const { collectiblesController, preferences, messenger } = - setupController(); - const { chainId } = collectiblesController.config; + it('should add NFT by selected address', async () => { + const { nftController, preferences, messenger } = setupController(); + const { chainId } = nftController.config; const firstAddress = '0x123'; const secondAddress = '0x321'; sinon - .stub(collectiblesController, 'getCollectibleInformation' as any) + .stub(nftController, 'getNftInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); preferences.update({ selectedAddress: firstAddress }); - await collectiblesController.addCollectible('0x01', '1234'); + await nftController.addNft('0x01', '1234'); preferences.update({ selectedAddress: secondAddress }); - await collectiblesController.addCollectible('0x02', '4321'); + await nftController.addNft('0x02', '4321'); preferences.update({ selectedAddress: firstAddress }); expect( - collectiblesController.state.allCollectibles[firstAddress][chainId][0], + nftController.state.allNfts[firstAddress][chainId][0], ).toStrictEqual({ address: '0x01', description: 'description', @@ -315,11 +309,11 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should update collectible if image is different', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; + it('should update NFT if image is different', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', @@ -328,9 +322,7 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ address: '0x01', description: 'description', @@ -342,7 +334,7 @@ describe('CollectiblesController', () => { isCurrentlyOwned: true, }); - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image-updated', description: 'description', @@ -351,9 +343,7 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ address: '0x01', description: 'description', @@ -368,10 +358,10 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should not duplicate collectible nor collectible contract if already added', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible('0x01', '1', { + it('should not duplicate NFT nor NFT contract if already added', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', @@ -379,7 +369,7 @@ describe('CollectiblesController', () => { favorite: false, }); - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', @@ -388,27 +378,23 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(1); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ], + nftController.state.allNftContracts[selectedAddress][chainId], ).toHaveLength(1); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible and get information from OpenSea', async () => { - const { collectiblesController, messenger } = setupController(); + it('should add NFT and get information from OpenSea', async () => { + const { nftController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible('0x01', '1'); + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft('0x01', '1'); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ address: '0x01', description: 'Description', @@ -424,9 +410,8 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible erc721 and aggregate collectible data from both contract and OpenSea', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should add NFT erc721 and aggregate NFT data from both contract and OpenSea', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock(OPENSEA_PROXY_URL) .get(`/asset/${ERC721_KUDOSADDRESS}/${ERC721_KUDOS_TOKEN_ID}`) .reply(200, { @@ -531,23 +516,15 @@ describe('CollectiblesController', () => { }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; + const { selectedAddress, chainId } = nftController.config; sinon - .stub( - collectiblesController, - 'getCollectibleContractInformationFromApi' as any, - ) + .stub(nftController, 'getNftContractInformationFromApi' as any) .returns(undefined); - await collectiblesController.addCollectible( - ERC721_KUDOSADDRESS, - ERC721_KUDOS_TOKEN_ID, - ); + await nftController.addNft(ERC721_KUDOSADDRESS, ERC721_KUDOS_TOKEN_ID); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ address: ERC721_KUDOSADDRESS, image: 'Kudos Image (directly from tokenURI)', @@ -561,9 +538,7 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ][0], + nftController.state.allNftContracts[selectedAddress][chainId][0], ).toStrictEqual({ address: ERC721_KUDOSADDRESS, name: 'KudosToken', @@ -573,9 +548,8 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible erc1155 and get collectible information from contract when OpenSea Proxy API fails to fetch and no OpenSeaAPI key is set', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should add NFT erc1155 and get NFT information from contract when OpenSea Proxy API fails to fetch and no OpenSeaAPI key is set', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock('https://mainnet.infura.io:443', { encodedQueryParams: true }) .post('/v3/ad3a368836ff4596becc3be8e2f137ac', { jsonrpc: '2.0', @@ -651,13 +625,13 @@ describe('CollectiblesController', () => { }); nock(OPENSEA_PROXY_URL) - .get(`/asset_contract/${ERC1155_COLLECTIBLE_ADDRESS}`) + .get(`/asset_contract/${ERC1155_NFT_ADDRESS}`) .replyWithError(new TypeError('Failed to fetch')); - // the tokenURI for ERC1155_COLLECTIBLE_ADDRESS + ERC1155_COLLECTIBLE_ID + // the tokenURI for ERC1155_NFT_ADDRESS + ERC1155_NFT_ID nock('https://api.opensea.io') .get( - `/api/v1/metadata/${ERC1155_COLLECTIBLE_ADDRESS}/0x5a3ca5cd63807ce5e4d7841ab32ce6b6d9bbba2d000000000000010000000001`, + `/api/v1/metadata/${ERC1155_NFT_ADDRESS}/0x5a3ca5cd63807ce5e4d7841ab32ce6b6d9bbba2d000000000000010000000001`, ) .reply(200, { name: 'name (directly from tokenURI)', @@ -668,25 +642,20 @@ describe('CollectiblesController', () => { }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; + const { selectedAddress, chainId } = nftController.config; - expect(collectiblesController.openSeaApiKey).toBeUndefined(); + expect(nftController.openSeaApiKey).toBeUndefined(); - await collectiblesController.addCollectible( - ERC1155_COLLECTIBLE_ADDRESS, - ERC1155_COLLECTIBLE_ID, - ); + await nftController.addNft(ERC1155_NFT_ADDRESS, ERC1155_NFT_ID); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ - address: ERC1155_COLLECTIBLE_ADDRESS, + address: ERC1155_NFT_ADDRESS, image: 'image (directly from tokenURI)', name: 'name (directly from tokenURI)', description: 'description (direclty from tokenURI)', - tokenId: ERC1155_COLLECTIBLE_ID, + tokenId: ERC1155_NFT_ID, standard: ERC1155, favorite: false, isCurrentlyOwned: true, @@ -697,9 +666,8 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible erc721 and get collectible information only from contract', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should add NFT erc721 and get NFT information only from contract', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock('https://ipfs.gitcoin.co:443') .get('/api/v0/cat/QmPmt6EAaioN78ECnW5oCL8v2YvVSpoBjLCjrXhhsAvoov') .reply(200, { @@ -783,27 +751,19 @@ describe('CollectiblesController', () => { }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; + const { selectedAddress, chainId } = nftController.config; sinon - .stub( - collectiblesController, - 'getCollectibleContractInformationFromApi' as any, - ) + .stub(nftController, 'getNftContractInformationFromApi' as any) .returns(undefined); sinon - .stub(collectiblesController, 'getCollectibleInformationFromApi' as any) + .stub(nftController, 'getNftInformationFromApi' as any) .returns(undefined); - await collectiblesController.addCollectible( - ERC721_KUDOSADDRESS, - ERC721_KUDOS_TOKEN_ID, - ); + await nftController.addNft(ERC721_KUDOSADDRESS, ERC721_KUDOS_TOKEN_ID); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ address: ERC721_KUDOSADDRESS, image: 'Kudos Image (directly from tokenURI)', @@ -816,9 +776,7 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ][0], + nftController.state.allNftContracts[selectedAddress][chainId][0], ).toStrictEqual({ address: ERC721_KUDOSADDRESS, name: 'KudosToken', @@ -828,28 +786,28 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible by provider type', async () => { - const { collectiblesController, network, messenger } = setupController(); + it('should add NFT by provider type', async () => { + const { nftController, network, messenger } = setupController(); const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; - const { selectedAddress } = collectiblesController.config; + const { selectedAddress } = nftController.config; sinon - .stub(collectiblesController, 'getCollectibleInformation' as any) + .stub(nftController, 'getNftInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); network.setProviderType(firstNetworkType); - await collectiblesController.addCollectible('0x01', '1234'); + await nftController.addNft('0x01', '1234'); network.setProviderType(secondNetworkType); network.setProviderType(firstNetworkType); expect( - collectiblesController.state.allCollectibles[selectedAddress]?.[ + nftController.state.allNfts[selectedAddress]?.[ NetworksChainId[secondNetworkType] ], ).toBeUndefined(); expect( - collectiblesController.state.allCollectibles[selectedAddress][ + nftController.state.allNfts[selectedAddress][ NetworksChainId[firstNetworkType] ][0], ).toStrictEqual({ @@ -865,8 +823,8 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should not add collectibles with no contract information when auto detecting', async () => { - const { collectiblesController, messenger } = setupController(); + it('should not add NFTs with no contract information when auto detecting', async () => { + const { nftController, messenger } = setupController(); nock(OPENSEA_PROXY_URL) .get(`/asset/${ERC721_KUDOSADDRESS}/${ERC721_KUDOS_TOKEN_ID}`) .reply(200, { @@ -888,8 +846,8 @@ describe('CollectiblesController', () => { }, }); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible( + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft( '0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab', '123', undefined, @@ -900,18 +858,14 @@ describe('CollectiblesController', () => { ); expect( - collectiblesController.state.allCollectibles[selectedAddress]?.[ - chainId - ], + nftController.state.allNfts[selectedAddress]?.[chainId], ).toBeUndefined(); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress]?.[ - chainId - ], + nftController.state.allNftContracts[selectedAddress]?.[chainId], ).toBeUndefined(); - await collectiblesController.addCollectible( + await nftController.addNft( ERC721_KUDOSADDRESS, ERC721_KUDOS_TOKEN_ID, undefined, @@ -922,7 +876,7 @@ describe('CollectiblesController', () => { ); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toStrictEqual([ { address: ERC721_KUDOSADDRESS, @@ -938,9 +892,7 @@ describe('CollectiblesController', () => { ]); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ], + nftController.state.allNftContracts[selectedAddress][chainId], ).toStrictEqual([ { address: ERC721_KUDOSADDRESS, @@ -955,18 +907,18 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should not add duplicate collectibles to the ignoredCollectibles list', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; + it('should not add duplicate NFTs to the ignoredNfts list', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', standard: 'standard', }); - await collectiblesController.addCollectible('0x01', '2', { + await nftController.addNft('0x01', '2', { name: 'name', image: 'image', description: 'description', @@ -974,17 +926,17 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(2); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(0); + expect(nftController.state.ignoredNfts).toHaveLength(0); - collectiblesController.removeAndIgnoreCollectible('0x01', '1'); + nftController.removeAndIgnoreNft('0x01', '1'); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(1); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(1); + expect(nftController.state.ignoredNfts).toHaveLength(1); - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', @@ -992,22 +944,21 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(2); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(1); + expect(nftController.state.ignoredNfts).toHaveLength(1); - collectiblesController.removeAndIgnoreCollectible('0x01', '1'); + nftController.removeAndIgnoreNft('0x01', '1'); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(1); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(1); + expect(nftController.state.ignoredNfts).toHaveLength(1); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible with metadata hosted in IPFS', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should add NFT with metadata hosted in IPFS', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock('https://mainnet.infura.io:443', { encodedQueryParams: true }) .post('/v3/ad3a368836ff4596becc3be8e2f137ac', { jsonrpc: '2.0', @@ -1085,19 +1036,17 @@ describe('CollectiblesController', () => { }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - collectiblesController.configure({ + nftController.configure({ ipfsGateway: IPFS_DEFAULT_GATEWAY_URL, }); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible( + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, ); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ][0], + nftController.state.allNftContracts[selectedAddress][chainId][0], ).toStrictEqual({ address: '0x18E8E76aeB9E2d9FA2A2b88DD9CF3C8ED45c3660', name: "Maltjik.jpg's Depressionists", @@ -1105,9 +1054,7 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ address: '0x18E8E76aeB9E2d9FA2A2b88DD9CF3C8ED45c3660', tokenId: '36', @@ -1122,19 +1069,18 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should add collectible erc721 and get collectible information directly from OpenSea API when OpenSeaAPIkey is set and queries to OpenSea proxy fail', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should add NFT erc721 and get NFT information directly from OpenSea API when OpenSeaAPIkey is set and queries to OpenSea proxy fail', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock(OPENSEA_PROXY_URL) - .get(`/asset_contract/${ERC721_COLLECTIBLE_ADDRESS}`) + .get(`/asset_contract/${ERC721_NFT_ADDRESS}`) .replyWithError(new Error('Failed to fetch')) - .get(`/asset/${ERC721_COLLECTIBLE_ADDRESS}/${ERC721_COLLECTIBLE_ID}`) + .get(`/asset/${ERC721_NFT_ADDRESS}/${ERC721_NFT_ID}`) .replyWithError(new Error('Failed to fetch')); nock(OPENSEA_API_URL, { encodedQueryParams: true, }) - .get(`/asset_contract/${ERC721_COLLECTIBLE_ADDRESS}`) + .get(`/asset_contract/${ERC721_NFT_ADDRESS}`) .reply(200, { description: 'description (from opensea)', symbol: 'KDO', @@ -1144,7 +1090,7 @@ describe('CollectiblesController', () => { image_url: 'logo (from opensea)', }, }) - .get(`/asset/${ERC721_COLLECTIBLE_ADDRESS}/${ERC721_COLLECTIBLE_ID}`) + .get(`/asset/${ERC721_NFT_ADDRESS}/${ERC721_NFT_ID}`) .reply(200, { image_original_url: 'image (directly from opensea)', name: 'name (directly from opensea)', @@ -1161,7 +1107,7 @@ describe('CollectiblesController', () => { method: 'eth_call', params: [ { - to: ERC721_COLLECTIBLE_ADDRESS, + to: ERC721_NFT_ADDRESS, data: '0x06fdde03', }, 'latest', @@ -1181,7 +1127,7 @@ describe('CollectiblesController', () => { method: 'eth_call', params: [ { - to: ERC721_COLLECTIBLE_ADDRESS, + to: ERC721_NFT_ADDRESS, data: '0x95d89b41', }, 'latest', @@ -1199,7 +1145,7 @@ describe('CollectiblesController', () => { method: 'eth_call', params: [ { - to: ERC721_COLLECTIBLE_ADDRESS, + to: ERC721_NFT_ADDRESS, data: '0x0e89341c5a3ca5cd63807ce5e4d7841ab32ce6b6d9bbba2d000000000000010000000001', }, 'latest', @@ -1219,7 +1165,7 @@ describe('CollectiblesController', () => { method: 'eth_call', params: [ { - to: ERC721_COLLECTIBLE_ADDRESS, + to: ERC721_NFT_ADDRESS, data: '0xc87b56dd000000000000000000000000000000000000000000000000000000000011781a', }, 'latest', @@ -1233,35 +1179,28 @@ describe('CollectiblesController', () => { }); nock('https://api.opensea.io:443', { encodedQueryParams: true }) - .get( - `/api/v1/metadata/${ERC721_COLLECTIBLE_ADDRESS}/${ERC721_COLLECTIBLE_ID}`, - ) + .get(`/api/v1/metadata/${ERC721_NFT_ADDRESS}/${ERC721_NFT_ID}`) .reply(200, [ '1f8b080000000000000334ce5d6f82301480e1ffd26b1015a3913bcdd8d4c1b20f9dc31bd274b51c3d3d85b664a0f1bf2f66d9ed9bbcc97365c4b564095be440e3e168ce02f62d9db0507b30c4126a1103263b2f2d712c11e8fc1f4173755f2bef6b97441156f14019a350b64e5a61c84bf203617494ef8aed27e5611cea7836f5fdfe510dc561cf9fcb23d8d364ed8a99cd2e4db30a1fb2d57184d9d9c6c547caab27dc35cbf779dd6bdfbfa88d5abca1b079d77ea5cbf4f24a6b389c5c2f4074d39fb16201e3049adfe1656bf1cf79fb050000ffff03002c5b5b9be3000000', ]); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; + const { selectedAddress, chainId } = nftController.config; - collectiblesController.setApiKey('fake-api-key'); - expect(collectiblesController.openSeaApiKey).toBe('fake-api-key'); + nftController.setApiKey('fake-api-key'); + expect(nftController.openSeaApiKey).toBe('fake-api-key'); - await collectiblesController.addCollectible( - ERC721_COLLECTIBLE_ADDRESS, - ERC721_COLLECTIBLE_ID, - ); + await nftController.addNft(ERC721_NFT_ADDRESS, ERC721_NFT_ID); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual({ - address: ERC721_COLLECTIBLE_ADDRESS, + address: ERC721_NFT_ADDRESS, image: null, imageOriginal: 'image (directly from opensea)', name: 'name (directly from opensea)', description: 'description (directly from opensea)', - tokenId: ERC721_COLLECTIBLE_ID, + tokenId: ERC721_NFT_ID, standard: ERC721, favorite: false, isCurrentlyOwned: true, @@ -1271,34 +1210,25 @@ describe('CollectiblesController', () => { }); }); - describe('addCollectibleVerifyOwnership', () => { - it('should verify ownership by selected address and add collectible', async () => { - const { collectiblesController, preferences, messenger } = - setupController(); + describe('addNftVerifyOwnership', () => { + it('should verify ownership by selected address and add NFT', async () => { + const { nftController, preferences, messenger } = setupController(); const firstAddress = '0x123'; const secondAddress = '0x321'; - const { chainId } = collectiblesController.config; + const { chainId } = nftController.config; - sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) - .returns(true); + sinon.stub(nftController, 'isNftOwner' as any).returns(true); sinon - .stub(collectiblesController, 'getCollectibleInformation' as any) + .stub(nftController, 'getNftInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); preferences.update({ selectedAddress: firstAddress }); - await collectiblesController.addCollectibleVerifyOwnership( - '0x01', - '1234', - ); + await nftController.addNftVerifyOwnership('0x01', '1234'); preferences.update({ selectedAddress: secondAddress }); - await collectiblesController.addCollectibleVerifyOwnership( - '0x02', - '4321', - ); + await nftController.addNftVerifyOwnership('0x02', '4321'); preferences.update({ selectedAddress: firstAddress }); expect( - collectiblesController.state.allCollectibles[firstAddress][chainId][0], + nftController.state.allNfts[firstAddress][chainId][0], ).toStrictEqual({ address: '0x01', description: 'description', @@ -1312,102 +1242,91 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should throw an error if selected address is not owner of input collectible', async () => { - const { collectiblesController, preferences, messenger } = - setupController(); - sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) - .returns(false); + it('should throw an error if selected address is not owner of input NFT', async () => { + const { nftController, preferences, messenger } = setupController(); + sinon.stub(nftController, 'isNftOwner' as any).returns(false); const firstAddress = '0x123'; preferences.update({ selectedAddress: firstAddress }); const result = async () => - await collectiblesController.addCollectibleVerifyOwnership( - '0x01', - '1234', - ); - const error = 'This collectible is not owned by the user'; + await nftController.addNftVerifyOwnership('0x01', '1234'); + const error = 'This NFT is not owned by the user'; await expect(result).rejects.toThrow(error); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); }); - describe('removeCollectible', () => { - it('should remove collectible and collectible contract', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; + describe('removeNft', () => { + it('should remove NFT and NFT contract', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', standard: 'standard', }); - collectiblesController.removeCollectible('0x01', '1'); + nftController.removeNft('0x01', '1'); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(0); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ], + nftController.state.allNftContracts[selectedAddress][chainId], ).toHaveLength(0); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should not remove collectible contract if collectible still exists', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; + it('should not remove NFT contract if NFT still exists', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; - await collectiblesController.addCollectible('0x01', '1', { + await nftController.addNft('0x01', '1', { name: 'name', image: 'image', description: 'description', standard: 'standard', }); - await collectiblesController.addCollectible('0x01', '2', { + await nftController.addNft('0x01', '2', { name: 'name', image: 'image', description: 'description', standard: 'standard', }); - collectiblesController.removeCollectible('0x01', '1'); + nftController.removeNft('0x01', '1'); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(1); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ], + nftController.state.allNftContracts[selectedAddress][chainId], ).toHaveLength(1); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should remove collectible by selected address', async () => { - const { collectiblesController, preferences, messenger } = - setupController(); - const { chainId } = collectiblesController.config; + it('should remove NFT by selected address', async () => { + const { nftController, preferences, messenger } = setupController(); + const { chainId } = nftController.config; sinon - .stub(collectiblesController, 'getCollectibleInformation' as any) + .stub(nftController, 'getNftInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); const firstAddress = '0x123'; const secondAddress = '0x321'; preferences.update({ selectedAddress: firstAddress }); - await collectiblesController.addCollectible('0x02', '4321'); + await nftController.addNft('0x02', '4321'); preferences.update({ selectedAddress: secondAddress }); - await collectiblesController.addCollectible('0x01', '1234'); - collectiblesController.removeCollectible('0x01', '1234'); - expect( - collectiblesController.state.allCollectibles[secondAddress][chainId], - ).toHaveLength(0); + await nftController.addNft('0x01', '1234'); + nftController.removeNft('0x01', '1234'); + expect(nftController.state.allNfts[secondAddress][chainId]).toHaveLength( + 0, + ); preferences.update({ selectedAddress: firstAddress }); expect( - collectiblesController.state.allCollectibles[firstAddress][chainId][0], + nftController.state.allNfts[firstAddress][chainId][0], ).toStrictEqual({ address: '0x02', description: 'description', @@ -1421,23 +1340,23 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should remove collectible by provider type', async () => { - const { collectiblesController, network, messenger } = setupController(); - const { selectedAddress } = collectiblesController.config; + it('should remove NFT by provider type', async () => { + const { nftController, network, messenger } = setupController(); + const { selectedAddress } = nftController.config; sinon - .stub(collectiblesController, 'getCollectibleInformation' as any) + .stub(nftController, 'getNftInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; network.setProviderType(firstNetworkType); - await collectiblesController.addCollectible('0x02', '4321'); + await nftController.addNft('0x02', '4321'); network.setProviderType(secondNetworkType); - await collectiblesController.addCollectible('0x01', '1234'); - // collectiblesController.removeToken('0x01'); - collectiblesController.removeCollectible('0x01', '1234'); + await nftController.addNft('0x01', '1234'); + // nftController.removeToken('0x01'); + nftController.removeNft('0x01', '1234'); expect( - collectiblesController.state.allCollectibles[selectedAddress][ + nftController.state.allNfts[selectedAddress][ NetworksChainId[secondNetworkType] ], ).toHaveLength(0); @@ -1445,7 +1364,7 @@ describe('CollectiblesController', () => { network.setProviderType(firstNetworkType); expect( - collectiblesController.state.allCollectibles[selectedAddress][ + nftController.state.allNfts[selectedAddress][ NetworksChainId[firstNetworkType] ][0], ).toStrictEqual({ @@ -1462,11 +1381,11 @@ describe('CollectiblesController', () => { }); }); - it('should be able to clear the ignoredCollectibles list', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; + it('should be able to clear the ignoredNfts list', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; - await collectiblesController.addCollectible('0x02', '1', { + await nftController.addNft('0x02', '1', { name: 'name', image: 'image', description: 'description', @@ -1474,34 +1393,33 @@ describe('CollectiblesController', () => { favorite: false, }); - expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], - ).toHaveLength(1); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(0); + expect(nftController.state.allNfts[selectedAddress][chainId]).toHaveLength( + 1, + ); + expect(nftController.state.ignoredNfts).toHaveLength(0); - collectiblesController.removeAndIgnoreCollectible('0x02', '1'); - expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], - ).toHaveLength(0); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(1); + nftController.removeAndIgnoreNft('0x02', '1'); + expect(nftController.state.allNfts[selectedAddress][chainId]).toHaveLength( + 0, + ); + expect(nftController.state.ignoredNfts).toHaveLength(1); - collectiblesController.clearIgnoredCollectibles(); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(0); + nftController.clearIgnoredNfts(); + expect(nftController.state.ignoredNfts).toHaveLength(0); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); it('should set api key correctly', () => { - const { collectiblesController, messenger } = setupController(); - collectiblesController.setApiKey('new-api-key'); - expect(collectiblesController.openSeaApiKey).toBe('new-api-key'); + const { nftController, messenger } = setupController(); + nftController.setApiKey('new-api-key'); + expect(nftController.openSeaApiKey).toBe('new-api-key'); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - describe('isCollectibleOwner', () => { - it('should verify the ownership of an ERC-721 collectible with the correct owner address', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + describe('isNftOwner', () => { + it('should verify the ownership of an ERC-721 NFT with the correct owner address', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock('https://mainnet.infura.io:443', { encodedQueryParams: true }) .post('/v3/ad3a368836ff4596becc3be8e2f137ac', { jsonrpc: '2.0', @@ -1509,7 +1427,7 @@ describe('CollectiblesController', () => { method: 'eth_call', params: [ { - to: ERC721_COLLECTIBLE_ADDRESS, + to: ERC721_NFT_ADDRESS, data: '0x6352211e000000000000000000000000000000000000000000000000000000000011781a', }, 'latest', @@ -1523,19 +1441,18 @@ describe('CollectiblesController', () => { }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const isOwner = await collectiblesController.isCollectibleOwner( + const isOwner = await nftController.isNftOwner( OWNER_ADDRESS, - ERC721_COLLECTIBLE_ADDRESS, - String(ERC721_COLLECTIBLE_ID), + ERC721_NFT_ADDRESS, + String(ERC721_NFT_ID), ); expect(isOwner).toBe(true); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should not verify the ownership of an ERC-721 collectible with the wrong owner address', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should not verify the ownership of an ERC-721 NFT with the wrong owner address', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock('https://mainnet.infura.io:443', { encodedQueryParams: true }) .post('/v3/ad3a368836ff4596becc3be8e2f137ac', { jsonrpc: '2.0', @@ -1543,7 +1460,7 @@ describe('CollectiblesController', () => { method: 'eth_call', params: [ { - to: ERC721_COLLECTIBLE_ADDRESS, + to: ERC721_NFT_ADDRESS, data: '0x6352211e000000000000000000000000000000000000000000000000000000000011781a', }, 'latest', @@ -1557,19 +1474,18 @@ describe('CollectiblesController', () => { }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const isOwner = await collectiblesController.isCollectibleOwner( + const isOwner = await nftController.isNftOwner( '0x0000000000000000000000000000000000000000', - ERC721_COLLECTIBLE_ADDRESS, - String(ERC721_COLLECTIBLE_ID), + ERC721_NFT_ADDRESS, + String(ERC721_NFT_ID), ); expect(isOwner).toBe(false); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should verify the ownership of an ERC-1155 collectible with the correct owner address', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should verify the ownership of an ERC-1155 NFT with the correct owner address', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock('https://mainnet.infura.io:443', { encodedQueryParams: true }) .post('/v3/ad3a368836ff4596becc3be8e2f137ac', { jsonrpc: '2.0', @@ -1607,19 +1523,18 @@ describe('CollectiblesController', () => { '0x0000000000000000000000000000000000000000000000000000000000000001', }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const isOwner = await collectiblesController.isCollectibleOwner( + const isOwner = await nftController.isNftOwner( OWNER_ADDRESS, - ERC1155_COLLECTIBLE_ADDRESS, - ERC1155_COLLECTIBLE_ID, + ERC1155_NFT_ADDRESS, + ERC1155_NFT_ID, ); expect(isOwner).toBe(true); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should not verify the ownership of an ERC-1155 collectible with the wrong owner address', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should not verify the ownership of an ERC-1155 NFT with the wrong owner address', async () => { + const { assetsContract, nftController, messenger } = setupController(); nock('https://mainnet.infura.io:443', { encodedQueryParams: true }) .post('/v3/ad3a368836ff4596becc3be8e2f137ac', { jsonrpc: '2.0', @@ -1658,10 +1573,10 @@ describe('CollectiblesController', () => { }); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const isOwner = await collectiblesController.isCollectibleOwner( + const isOwner = await nftController.isNftOwner( '0x0000000000000000000000000000000000000000', - ERC1155_COLLECTIBLE_ADDRESS, - ERC1155_COLLECTIBLE_ID, + ERC1155_NFT_ADDRESS, + ERC1155_NFT_ID, ); expect(isOwner).toBe(false); @@ -1669,13 +1584,12 @@ describe('CollectiblesController', () => { }); it('should throw an error for an unsupported standard', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + const { assetsContract, nftController, messenger } = setupController(); assetsContract.configure({ provider: MAINNET_PROVIDER }); const error = 'Unable to verify ownership. Probably because the standard is not supported or the chain is incorrect'; const result = async () => { - await collectiblesController.isCollectibleOwner( + await nftController.isNftOwner( '0x0000000000000000000000000000000000000000', CRYPTOPUNK_ADDRESS, '0', @@ -1687,27 +1601,24 @@ describe('CollectiblesController', () => { }); }); - describe('updateCollectibleFavoriteStatus', () => { - it('should set collectible as favorite', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + describe('updateNftFavoriteStatus', () => { + it('should set NFT as favorite', async () => { + const { assetsContract, nftController, messenger } = setupController(); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible( + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, ); - collectiblesController.updateCollectibleFavoriteStatus( + nftController.updateNftFavoriteStatus( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, true, ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual( expect.objectContaining({ address: ERC721_DEPRESSIONIST_ADDRESS, @@ -1719,26 +1630,23 @@ describe('CollectiblesController', () => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should set collectible as favorite and then unset it', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + it('should set NFT as favorite and then unset it', async () => { + const { assetsContract, nftController, messenger } = setupController(); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible( + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, ); - collectiblesController.updateCollectibleFavoriteStatus( + nftController.updateNftFavoriteStatus( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, true, ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual( expect.objectContaining({ address: ERC721_DEPRESSIONIST_ADDRESS, @@ -1747,16 +1655,14 @@ describe('CollectiblesController', () => { }), ); - collectiblesController.updateCollectibleFavoriteStatus( + nftController.updateNftFavoriteStatus( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, false, ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual( expect.objectContaining({ address: ERC721_DEPRESSIONIST_ADDRESS, @@ -1769,25 +1675,22 @@ describe('CollectiblesController', () => { }); it('should keep the favorite status as true after updating metadata', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + const { assetsContract, nftController, messenger } = setupController(); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible( + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, ); - collectiblesController.updateCollectibleFavoriteStatus( + nftController.updateNftFavoriteStatus( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, true, ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual( expect.objectContaining({ address: ERC721_DEPRESSIONIST_ADDRESS, @@ -1796,7 +1699,7 @@ describe('CollectiblesController', () => { }), ); - await collectiblesController.addCollectible( + await nftController.addNft( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, { @@ -1808,9 +1711,7 @@ describe('CollectiblesController', () => { ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual( expect.objectContaining({ image: 'new_image', @@ -1824,26 +1725,23 @@ describe('CollectiblesController', () => { ); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(1); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); it('should keep the favorite status as false after updating metadata', async () => { - const { assetsContract, collectiblesController, messenger } = - setupController(); + const { assetsContract, nftController, messenger } = setupController(); assetsContract.configure({ provider: MAINNET_PROVIDER }); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible( + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual( expect.objectContaining({ address: ERC721_DEPRESSIONIST_ADDRESS, @@ -1852,7 +1750,7 @@ describe('CollectiblesController', () => { }), ); - await collectiblesController.addCollectible( + await nftController.addNft( ERC721_DEPRESSIONIST_ADDRESS, ERC721_DEPRESSIONIST_ID, { @@ -1864,9 +1762,7 @@ describe('CollectiblesController', () => { ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], + nftController.state.allNfts[selectedAddress][chainId][0], ).toStrictEqual( expect.objectContaining({ image: 'new_image', @@ -1880,22 +1776,20 @@ describe('CollectiblesController', () => { ); expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], + nftController.state.allNfts[selectedAddress][chainId], ).toHaveLength(1); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - describe('checkAndUpdateCollectiblesOwnershipStatus', () => { - describe('checkAndUpdateAllCollectiblesOwnershipStatus', () => { - it('should check whether collectibles for the current selectedAddress/chainId combination are still owned by the selectedAddress and update the isCurrentlyOwned value to false when collectible is not still owned', async () => { - const { collectiblesController, messenger } = setupController(); - sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) - .returns(false); + describe('checkAndUpdateNftsOwnershipStatus', () => { + describe('checkAndUpdateAllNftsOwnershipStatus', () => { + it('should check whether NFTs for the current selectedAddress/chainId combination are still owned by the selectedAddress and update the isCurrentlyOwned value to false when NFT is not still owned', async () => { + const { nftController, messenger } = setupController(); + sinon.stub(nftController, 'isNftOwner' as any).returns(false); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible('0x02', '1', { + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft('0x02', '1', { name: 'name', image: 'image', description: 'description', @@ -1904,30 +1798,26 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); - await collectiblesController.checkAndUpdateAllCollectiblesOwnershipStatus(); + await nftController.checkAndUpdateAllNftsOwnershipStatus(); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(false); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); }); - it('should check whether collectibles for the current selectedAddress/chainId combination are still owned by the selectedAddress and leave/set the isCurrentlyOwned value to true when collectible is still owned', async () => { - const { collectiblesController, messenger } = setupController(); - sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) - .returns(true); + it('should check whether NFTs for the current selectedAddress/chainId combination are still owned by the selectedAddress and leave/set the isCurrentlyOwned value to true when NFT is still owned', async () => { + const { nftController, messenger } = setupController(); + sinon.stub(nftController, 'isNftOwner' as any).returns(true); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible('0x02', '1', { + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft('0x02', '1', { name: 'name', image: 'image', description: 'description', @@ -1936,29 +1826,27 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); - await collectiblesController.checkAndUpdateAllCollectiblesOwnershipStatus(); + await nftController.checkAndUpdateAllNftsOwnershipStatus(); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should check whether collectibles for the current selectedAddress/chainId combination are still owned by the selectedAddress and leave the isCurrentlyOwned value as is when collectible ownership check fails', async () => { - const { collectiblesController, messenger } = setupController(); + it('should check whether NFTs for the current selectedAddress/chainId combination are still owned by the selectedAddress and leave the isCurrentlyOwned value as is when NFT ownership check fails', async () => { + const { nftController, messenger } = setupController(); sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) + .stub(nftController, 'isNftOwner' as any) .throws(new Error('Unable to verify ownership')); - const { selectedAddress, chainId } = collectiblesController.config; - await collectiblesController.addCollectible('0x02', '1', { + const { selectedAddress, chainId } = nftController.config; + await nftController.addNft('0x02', '1', { name: 'name', image: 'image', description: 'description', @@ -1967,26 +1855,24 @@ describe('CollectiblesController', () => { }); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); - await collectiblesController.checkAndUpdateAllCollectiblesOwnershipStatus(); + await nftController.checkAndUpdateAllNftsOwnershipStatus(); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - describe('checkAndUpdateSingleCollectibleOwnershipStatus', () => { - it('should check whether the passed collectible is still owned by the the current selectedAddress/chainId combination and update its isCurrentlyOwned property in state if batch is false and isCollectibleOwner returns false', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; - const collectible = { + describe('checkAndUpdateSingleNftOwnershipStatus', () => { + it('should check whether the passed NFT is still owned by the the current selectedAddress/chainId combination and update its isCurrentlyOwned property in state if batch is false and isNftOwner returns false', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; + const nft = { address: '0x02', tokenId: '1', name: 'name', @@ -1996,41 +1882,33 @@ describe('CollectiblesController', () => { favorite: false, }; - await collectiblesController.addCollectible( - collectible.address, - collectible.tokenId, - collectible, - ); + await nftController.addNft(nft.address, nft.tokenId, nft); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); - sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) - .returns(false); + sinon.stub(nftController, 'isNftOwner' as any).returns(false); - await collectiblesController.checkAndUpdateSingleCollectibleOwnershipStatus( - collectible, + await nftController.checkAndUpdateSingleNftOwnershipStatus( + nft, false, ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(false); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); }); - it('should check whether the passed collectible is still owned by the the current selectedAddress/chainId combination and return the updated collectible object without updating state if batch is true', async () => { - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; - const collectible = { + it('should check whether the passed NFT is still owned by the the current selectedAddress/chainId combination and return the updated NFT object without updating state if batch is true', async () => { + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; + const nft = { address: '0x02', tokenId: '1', name: 'name', @@ -2040,41 +1918,30 @@ describe('CollectiblesController', () => { favorite: false, }; - await collectiblesController.addCollectible( - collectible.address, - collectible.tokenId, - collectible, - ); + await nftController.addNft(nft.address, nft.tokenId, nft); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); - sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) - .returns(false); + sinon.stub(nftController, 'isNftOwner' as any).returns(false); - const updatedCollectible = - await collectiblesController.checkAndUpdateSingleCollectibleOwnershipStatus( - collectible, - true, - ); + const updatedNft = + await nftController.checkAndUpdateSingleNftOwnershipStatus(nft, true); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); - expect(updatedCollectible.isCurrentlyOwned).toBe(false); + expect(updatedNft.isCurrentlyOwned).toBe(false); messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should check whether the passed collectible is still owned by the the selectedAddress/chainId combination passed in the accountParams argument and update its isCurrentlyOwned property in state, when the currently configured selectedAddress/chainId are different from those passed', async () => { - const { collectiblesController, network, preferences, messenger } = + it('should check whether the passed NFT is still owned by the the selectedAddress/chainId combination passed in the accountParams argument and update its isCurrentlyOwned property in state, when the currently configured selectedAddress/chainId are different from those passed', async () => { + const { nftController, network, preferences, messenger } = setupController(); const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; @@ -2082,8 +1949,8 @@ describe('CollectiblesController', () => { preferences.update({ selectedAddress: OWNER_ADDRESS }); network.setProviderType(firstNetworkType); - const { selectedAddress, chainId } = collectiblesController.config; - const collectible = { + const { selectedAddress, chainId } = nftController.config; + const nft = { address: '0x02', tokenId: '1', name: 'name', @@ -2093,36 +1960,25 @@ describe('CollectiblesController', () => { favorite: false, }; - await collectiblesController.addCollectible( - collectible.address, - collectible.tokenId, - collectible, - ); + await nftController.addNft(nft.address, nft.tokenId, nft); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].isCurrentlyOwned, + nftController.state.allNfts[selectedAddress][chainId][0] + .isCurrentlyOwned, ).toBe(true); - sinon - .stub(collectiblesController, 'isCollectibleOwner' as any) - .returns(false); + sinon.stub(nftController, 'isNftOwner' as any).returns(false); preferences.update({ selectedAddress: SECOND_OWNER_ADDRESS }); network.setProviderType(secondNetworkType); - await collectiblesController.checkAndUpdateSingleCollectibleOwnershipStatus( - collectible, - false, - { - userAddress: OWNER_ADDRESS, - chainId: NetworksChainId[firstNetworkType], - }, - ); + await nftController.checkAndUpdateSingleNftOwnershipStatus(nft, false, { + userAddress: OWNER_ADDRESS, + chainId: NetworksChainId[firstNetworkType], + }); expect( - collectiblesController.state.allCollectibles[OWNER_ADDRESS][ + nftController.state.allNfts[OWNER_ADDRESS][ NetworksChainId[firstNetworkType] ][0].isCurrentlyOwned, ).toBe(false); @@ -2132,8 +1988,8 @@ describe('CollectiblesController', () => { }); }); - describe('findCollectibleByAddressAndTokenId', () => { - const mockCollectible = { + describe('findNftByAddressAndTokenId', () => { + const mockNft = { address: '0x02', tokenId: '1', name: 'name', @@ -2142,45 +1998,45 @@ describe('CollectiblesController', () => { standard: 'standard', favorite: false, }; - const { collectiblesController, messenger } = setupController(); - const { selectedAddress, chainId } = collectiblesController.config; + const { nftController, messenger } = setupController(); + const { selectedAddress, chainId } = nftController.config; afterAll(() => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should return null if the collectible does not exist in the state', async () => { + it('should return null if the NFT does not exist in the state', async () => { expect( - collectiblesController.findCollectibleByAddressAndTokenId( - mockCollectible.address, - mockCollectible.tokenId, + nftController.findNftByAddressAndTokenId( + mockNft.address, + mockNft.tokenId, selectedAddress, chainId, ), ).toBeNull(); }); - it('should return the collectible by the address and tokenId', () => { - collectiblesController.state.allCollectibles = { - [selectedAddress]: { [chainId]: [mockCollectible] }, + it('should return the NFT by the address and tokenId', () => { + nftController.state.allNfts = { + [selectedAddress]: { [chainId]: [mockNft] }, }; expect( - collectiblesController.findCollectibleByAddressAndTokenId( - mockCollectible.address, - mockCollectible.tokenId, + nftController.findNftByAddressAndTokenId( + mockNft.address, + mockNft.tokenId, selectedAddress, chainId, ), - ).toStrictEqual({ collectible: mockCollectible, index: 0 }); + ).toStrictEqual({ nft: mockNft, index: 0 }); }); }); - describe('updateCollectibleByAddressAndTokenId', () => { - const { collectiblesController, messenger } = setupController(); + describe('updateNftByAddressAndTokenId', () => { + const { nftController, messenger } = setupController(); const mockTransactionId = '60d36710-b150-11ec-8a49-c377fbd05e27'; - const mockCollectible = { + const mockNft = { address: '0x02', tokenId: '1', name: 'name', @@ -2190,7 +2046,7 @@ describe('CollectiblesController', () => { favorite: false, }; - const expectedMockCollectible = { + const expectedMockNft = { address: '0x02', description: 'description', favorite: false, @@ -2201,19 +2057,19 @@ describe('CollectiblesController', () => { transactionId: mockTransactionId, }; - const { selectedAddress, chainId } = collectiblesController.config; + const { selectedAddress, chainId } = nftController.config; afterAll(() => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should update the collectible if the collectible exist', async () => { - collectiblesController.state.allCollectibles = { - [selectedAddress]: { [chainId]: [mockCollectible] }, + it('should update the NFT if the NFT exist', async () => { + nftController.state.allNfts = { + [selectedAddress]: { [chainId]: [mockNft] }, }; - collectiblesController.updateCollectible( - mockCollectible, + nftController.updateNft( + mockNft, { transactionId: mockTransactionId, }, @@ -2222,16 +2078,14 @@ describe('CollectiblesController', () => { ); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0], - ).toStrictEqual(expectedMockCollectible); + nftController.state.allNfts[selectedAddress][chainId][0], + ).toStrictEqual(expectedMockNft); }); - it('should return undefined if the collectible does not exist', () => { + it('should return undefined if the NFT does not exist', () => { expect( - collectiblesController.updateCollectible( - mockCollectible, + nftController.updateNft( + mockNft, { transactionId: mockTransactionId, }, @@ -2242,13 +2096,13 @@ describe('CollectiblesController', () => { }); }); - describe('resetCollectibleTransactionStatusByTransactionId', () => { - const { collectiblesController, messenger } = setupController(); + describe('resetNftTransactionStatusByTransactionId', () => { + const { nftController, messenger } = setupController(); const mockTransactionId = '60d36710-b150-11ec-8a49-c377fbd05e27'; const nonExistTransactionId = '0123'; - const mockCollectible = { + const mockNft = { address: '0x02', tokenId: '1', name: 'name', @@ -2259,15 +2113,15 @@ describe('CollectiblesController', () => { transactionId: mockTransactionId, }; - const { selectedAddress, chainId } = collectiblesController.config; + const { selectedAddress, chainId } = nftController.config; afterAll(() => { messenger.clearEventSubscriptions('NetworkController:stateChange'); }); - it('should not update any collectible state and should return false when passed a transaction id that does not match that of any collectible', async () => { + it('should not update any NFT state and should return false when passed a transaction id that does not match that of any NFT', async () => { expect( - collectiblesController.resetCollectibleTransactionStatusByTransactionId( + nftController.resetNftTransactionStatusByTransactionId( nonExistTransactionId, selectedAddress, chainId, @@ -2275,19 +2129,17 @@ describe('CollectiblesController', () => { ).toBe(false); }); - it('should set the transaction id of a collectible in state to undefined, and return true when it has successfully updated this state', async () => { - collectiblesController.state.allCollectibles = { - [selectedAddress]: { [chainId]: [mockCollectible] }, + it('should set the transaction id of an NFT in state to undefined, and return true when it has successfully updated this state', async () => { + nftController.state.allNfts = { + [selectedAddress]: { [chainId]: [mockNft] }, }; expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].transactionId, + nftController.state.allNfts[selectedAddress][chainId][0].transactionId, ).toBe(mockTransactionId); expect( - collectiblesController.resetCollectibleTransactionStatusByTransactionId( + nftController.resetNftTransactionStatusByTransactionId( mockTransactionId, selectedAddress, chainId, @@ -2295,9 +2147,7 @@ describe('CollectiblesController', () => { ).toBe(true); expect( - collectiblesController.state.allCollectibles[selectedAddress][ - chainId - ][0].transactionId, + nftController.state.allNfts[selectedAddress][chainId][0].transactionId, ).toBeUndefined(); }); }); diff --git a/src/assets/CollectiblesController.ts b/src/assets/NftController.ts similarity index 51% rename from src/assets/CollectiblesController.ts rename to src/assets/NftController.ts index 9849e8cb6fc..6bc1fc42cb1 100644 --- a/src/assets/CollectiblesController.ts +++ b/src/assets/NftController.ts @@ -25,57 +25,57 @@ import { } from '../constants'; import type { - ApiCollectible, - ApiCollectibleCreator, - ApiCollectibleContract, - ApiCollectibleLastSale, -} from './CollectibleDetectionController'; + ApiNft, + ApiNftCreator, + ApiNftContract, + ApiNftLastSale, +} from './NftDetectionController'; import type { AssetsContractController } from './AssetsContractController'; -import { compareCollectiblesMetadata } from './assetsUtil'; +import { compareNftMetadata } from './assetsUtil'; /** - * @type Collectible + * @type Nft * - * Collectible representation + * NFT representation * @property address - Hex address of a ERC721 contract - * @property description - The collectible description - * @property image - URI of custom collectible image associated with this tokenId + * @property description - The NFT description + * @property image - URI of custom NFT image associated with this tokenId * @property name - Name associated with this tokenId and contract address - * @property tokenId - The collectible identifier + * @property tokenId - The NFT identifier * @property numberOfSales - Number of sales * @property backgroundColor - The background color to be displayed with the item - * @property imagePreview - URI of a smaller image associated with this collectible - * @property imageThumbnail - URI of a thumbnail image associated with this collectible - * @property imageOriginal - URI of the original image associated with this collectible - * @property animation - URI of a animation associated with this collectible - * @property animationOriginal - URI of the original animation associated with this collectible + * @property imagePreview - URI of a smaller image associated with this NFT + * @property imageThumbnail - URI of a thumbnail image associated with this NFT + * @property imageOriginal - URI of the original image associated with this NFT + * @property animation - URI of a animation associated with this NFT + * @property animationOriginal - URI of the original animation associated with this NFT * @property externalLink - External link containing additional information - * @property creator - The collectible owner information object - * @property isCurrentlyOwned - Boolean indicating whether the address/chainId combination where it's currently stored currently owns this collectible - * @property transactionId - Transaction Id associated with the collectible + * @property creator - The NFT owner information object + * @property isCurrentlyOwned - Boolean indicating whether the address/chainId combination where it's currently stored currently owns this NFT + * @property transactionId - Transaction Id associated with the NFT */ -export interface Collectible extends CollectibleMetadata { +export interface Nft extends NftMetadata { tokenId: string; address: string; isCurrentlyOwned?: boolean; } /** - * @type CollectibleContract + * @type NftContract * - * Collectible contract information representation + * NFT contract information representation * @property name - Contract name * @property logo - Contract logo * @property address - Contract address * @property symbol - Contract symbol * @property description - Contract description - * @property totalSupply - Total supply of collectibles - * @property assetContractType - The collectible type, it could be `semi-fungible` or `non-fungible` + * @property totalSupply - Total supply of NFTs + * @property assetContractType - The NFT type, it could be `semi-fungible` or `non-fungible` * @property createdDate - Creation date * @property schemaName - The schema followed by the contract, it could be `ERC721` or `ERC1155` * @property externalLink - External link containing additional information */ -export interface CollectibleContract { +export interface NftContract { name?: string; logo?: string; address: string; @@ -89,24 +89,24 @@ export interface CollectibleContract { } /** - * @type CollectibleMetadata + * @type NftMetadata * - * Collectible custom information - * @property name - Collectible custom name - * @property description - The collectible description + * NFT custom information + * @property name - NFT custom name + * @property description - The NFT description * @property numberOfSales - Number of sales * @property backgroundColor - The background color to be displayed with the item * @property image - Image custom image URI - * @property imagePreview - URI of a smaller image associated with this collectible - * @property imageThumbnail - URI of a thumbnail image associated with this collectible - * @property imageOriginal - URI of the original image associated with this collectible - * @property animation - URI of a animation associated with this collectible - * @property animationOriginal - URI of the original animation associated with this collectible + * @property imagePreview - URI of a smaller image associated with this NFT + * @property imageThumbnail - URI of a thumbnail image associated with this NFT + * @property imageOriginal - URI of the original image associated with this NFT + * @property animation - URI of a animation associated with this NFT + * @property animationOriginal - URI of the original animation associated with this NFT * @property externalLink - External link containing additional information - * @property creator - The collectible owner information object - * @property standard - NFT standard name for the collectible, e.g., ERC-721 or ERC-1155 + * @property creator - The NFT owner information object + * @property standard - NFT standard name for the NFT, e.g., ERC-721 or ERC-1155 */ -export interface CollectibleMetadata { +export interface NftMetadata { name: string | null; description: string | null; image: string | null; @@ -120,8 +120,8 @@ export interface CollectibleMetadata { animation?: string; animationOriginal?: string; externalLink?: string; - creator?: ApiCollectibleCreator; - lastSale?: ApiCollectibleLastSale; + creator?: ApiNftCreator; + lastSale?: ApiNftLastSale; transactionId?: string; } @@ -131,13 +131,13 @@ interface AccountParams { } /** - * @type CollectiblesConfig + * @type NftConfig * - * Collectibles controller configuration + * NFT controller configuration * @property networkType - Network ID as per net_version * @property selectedAddress - Vault selected address */ -export interface CollectiblesConfig extends BaseConfig { +export interface NftConfig extends BaseConfig { networkType: NetworkType; selectedAddress: string; chainId: string; @@ -147,36 +147,31 @@ export interface CollectiblesConfig extends BaseConfig { } /** - * @type CollectiblesState + * @type NftState * - * Assets controller state - * @property allCollectibleContracts - Object containing collectibles contract information - * @property allCollectibles - Object containing collectibles per account and network - * @property collectibleContracts - List of collectibles contracts associated with the active vault - * @property collectibles - List of collectibles associated with the active vault - * @property ignoredCollectibles - List of collectibles that should be ignored + * NFT controller state + * @property allNftContracts - Object containing NFT contract information + * @property allNfts - Object containing NFTs per account and network + * @property ignoredNfts - List of NFTs that should be ignored */ -export interface CollectiblesState extends BaseState { - allCollectibleContracts: { - [key: string]: { [key: string]: CollectibleContract[] }; +export interface NftState extends BaseState { + allNftContracts: { + [key: string]: { [key: string]: NftContract[] }; }; - allCollectibles: { [key: string]: { [key: string]: Collectible[] } }; - ignoredCollectibles: Collectible[]; + allNfts: { [key: string]: { [key: string]: Nft[] } }; + ignoredNfts: Nft[]; } -const ALL_COLLECTIBLES_STATE_KEY = 'allCollectibles'; -const ALL_COLLECTIBLES_CONTRACTS_STATE_KEY = 'allCollectibleContracts'; +const ALL_NFTS_STATE_KEY = 'allNfts'; +const ALL_NFTS_CONTRACTS_STATE_KEY = 'allNftContracts'; /** * Controller that stores assets and exposes convenience methods */ -export class CollectiblesController extends BaseController< - CollectiblesConfig, - CollectiblesState -> { +export class NftController extends BaseController { private mutex = new Mutex(); - private getCollectibleApi({ + private getNftApi({ contractAddress, tokenId, useProxy, @@ -195,7 +190,7 @@ export class CollectiblesController extends BaseController< : `${OPENSEA_API_URL}/asset/${contractAddress}/${tokenId}`; } - private getCollectibleContractInformationApi({ + private getNftContractInformationApi({ contractAddress, useProxy, }: { @@ -214,17 +209,17 @@ export class CollectiblesController extends BaseController< } /** - * Helper method to update nested state for allCollectibles and allCollectibleContracts. + * Helper method to update nested state for allNfts and allNftContracts. * * @param newCollection - the modified piece of state to update in the controller's store * @param baseStateKey - The root key in the store to update. * @param passedConfig - An object containing the selectedAddress and chainId that are passed through the auto-detection flow. - * @param passedConfig.userAddress - the address passed through the collectible detection flow to ensure detected assets are stored to the correct account - * @param passedConfig.chainId - the chainId passed through the collectible detection flow to ensure detected assets are stored to the correct account + * @param passedConfig.userAddress - the address passed through the NFT detection flow to ensure detected assets are stored to the correct account + * @param passedConfig.chainId - the chainId passed through the NFT detection flow to ensure detected assets are stored to the correct account */ - private updateNestedCollectibleState( - newCollection: Collectible[] | CollectibleContract[], - baseStateKey: 'allCollectibles' | 'allCollectibleContracts', + private updateNestedNftState( + newCollection: Nft[] | NftContract[], + baseStateKey: 'allNfts' | 'allNftContracts', { userAddress, chainId }: AccountParams | undefined = { userAddress: this.config.selectedAddress, chainId: this.config.chainId, @@ -248,30 +243,29 @@ export class CollectiblesController extends BaseController< } /** - * Request individual collectible information from OpenSea API. + * Request individual NFT information from OpenSea API. * - * @param contractAddress - Hex address of the collectible contract. - * @param tokenId - The collectible identifier. - * @returns Promise resolving to the current collectible name and image. + * @param contractAddress - Hex address of the NFT contract. + * @param tokenId - The NFT identifier. + * @returns Promise resolving to the current NFT name and image. */ - private async getCollectibleInformationFromApi( + private async getNftInformationFromApi( contractAddress: string, tokenId: string, - ): Promise { + ): Promise { // Attempt to fetch the data with the proxy - let collectibleInformation: ApiCollectible | undefined = - await fetchWithErrorHandling({ - url: this.getCollectibleApi({ - contractAddress, - tokenId, - useProxy: true, - }), - }); + let nftInformation: ApiNft | undefined = await fetchWithErrorHandling({ + url: this.getNftApi({ + contractAddress, + tokenId, + useProxy: true, + }), + }); // if an openSeaApiKey is set we should attempt to refetch calling directly to OpenSea - if (!collectibleInformation && this.openSeaApiKey) { - collectibleInformation = await fetchWithErrorHandling({ - url: this.getCollectibleApi({ + if (!nftInformation && this.openSeaApiKey) { + nftInformation = await fetchWithErrorHandling({ + url: this.getNftApi({ contractAddress, tokenId, useProxy: false, @@ -284,8 +278,8 @@ export class CollectiblesController extends BaseController< }); } - // if we were still unable to fetch the data we return out the default/null of `CollectibleMetadata` - if (!collectibleInformation) { + // if we were still unable to fetch the data we return out the default/null of `NftMetadata` + if (!nftInformation) { return { name: null, description: null, @@ -294,8 +288,8 @@ export class CollectiblesController extends BaseController< }; } - // if we've reached this point, we have successfully fetched some data for collectibleInformation - // now we reconfigure the data to conform to the `CollectibleMetadata` type for storage. + // if we've reached this point, we have successfully fetched some data for nftInformation + // now we reconfigure the data to conform to the `NftMetadata` type for storage. const { num_sales, background_color, @@ -311,10 +305,10 @@ export class CollectiblesController extends BaseController< creator, last_sale, asset_contract: { schema_name }, - } = collectibleInformation; + } = nftInformation; /* istanbul ignore next */ - const collectibleMetadata: CollectibleMetadata = Object.assign( + const nftMetadata: NftMetadata = Object.assign( {}, { name: name || null }, { description: description || null }, @@ -334,25 +328,22 @@ export class CollectiblesController extends BaseController< schema_name && { standard: schema_name }, ); - return collectibleMetadata; + return nftMetadata; } /** - * Request individual collectible information from contracts that follows Metadata Interface. + * Request individual NFT information from contracts that follows Metadata Interface. * - * @param contractAddress - Hex address of the collectible contract. - * @param tokenId - The collectible identifier. - * @returns Promise resolving to the current collectible name and image. + * @param contractAddress - Hex address of the NFT contract. + * @param tokenId - The NFT identifier. + * @returns Promise resolving to the current NFT name and image. */ - private async getCollectibleInformationFromTokenURI( + private async getNftInformationFromTokenURI( contractAddress: string, tokenId: string, - ): Promise { + ): Promise { const { ipfsGateway, useIPFSSubdomains } = this.config; - const result = await this.getCollectibleURIAndStandard( - contractAddress, - tokenId, - ); + const result = await this.getNftURIAndStandard(contractAddress, tokenId); let tokenURI = result[0]; const standard = result[1]; @@ -386,13 +377,13 @@ export class CollectiblesController extends BaseController< } /** - * Retrieve collectible uri with metadata. TODO Update method to use IPFS. + * Retrieve NFT uri with metadata. TODO Update method to use IPFS. * - * @param contractAddress - Collectible contract address. - * @param tokenId - Collectible token id. - * @returns Promise resolving collectible uri and token standard. + * @param contractAddress - NFT contract address. + * @param tokenId - NFT token id. + * @returns Promise resolving NFT uri and token standard. */ - private async getCollectibleURIAndStandard( + private async getNftURIAndStandard( contractAddress: string, tokenId: string, ): Promise<[string, string]> { @@ -430,30 +421,24 @@ export class CollectiblesController extends BaseController< } /** - * Request individual collectible information (name, image url and description). + * Request individual NFT information (name, image url and description). * - * @param contractAddress - Hex address of the collectible contract. - * @param tokenId - The collectible identifier. - * @returns Promise resolving to the current collectible name and image. + * @param contractAddress - Hex address of the NFT contract. + * @param tokenId - The NFT identifier. + * @returns Promise resolving to the current NFT name and image. */ - private async getCollectibleInformation( + private async getNftInformation( contractAddress: string, tokenId: string, - ): Promise { + ): Promise { const blockchainMetadata = await safelyExecute(async () => { - return await this.getCollectibleInformationFromTokenURI( - contractAddress, - tokenId, - ); + return await this.getNftInformationFromTokenURI(contractAddress, tokenId); }); let openSeaMetadata; if (this.config.openSeaEnabled) { openSeaMetadata = await safelyExecute(async () => { - return await this.getCollectibleInformationFromApi( - contractAddress, - tokenId, - ); + return await this.getNftInformationFromApi(contractAddress, tokenId); }); } return { @@ -468,33 +453,33 @@ export class CollectiblesController extends BaseController< } /** - * Request collectible contract information from OpenSea API. + * Request NFT contract information from OpenSea API. * - * @param contractAddress - Hex address of the collectible contract. - * @returns Promise resolving to the current collectible name and image. + * @param contractAddress - Hex address of the NFT contract. + * @returns Promise resolving to the current NFT name and image. */ - private async getCollectibleContractInformationFromApi( + private async getNftContractInformationFromApi( contractAddress: string, - ): Promise { + ): Promise { /* istanbul ignore if */ - let apiCollectibleContractObject: ApiCollectibleContract | undefined = + let apiNftContractObject: ApiNftContract | undefined = await fetchWithErrorHandling({ - url: this.getCollectibleContractInformationApi({ + url: this.getNftContractInformationApi({ contractAddress, useProxy: true, }), }); // if we successfully fetched return the fetched data immediately - if (apiCollectibleContractObject) { - return apiCollectibleContractObject; + if (apiNftContractObject) { + return apiNftContractObject; } // if we were unsuccessful in fetching from the API and an OpenSea API key is present // attempt to refetch directly against the OpenSea API and if successful return the data immediately if (this.openSeaApiKey) { - apiCollectibleContractObject = await fetchWithErrorHandling({ - url: this.getCollectibleContractInformationApi({ + apiNftContractObject = await fetchWithErrorHandling({ + url: this.getNftContractInformationApi({ contractAddress, useProxy: false, }), @@ -505,13 +490,13 @@ export class CollectiblesController extends BaseController< errorCodesToCatch: [403], }); - if (apiCollectibleContractObject) { - return apiCollectibleContractObject; + if (apiNftContractObject) { + return apiNftContractObject; } } // If we've reached this point we were unable to fetch data from either the proxy or opensea so we return - // the default/null of ApiCollectibleContract + // the default/null of ApiNftContract return { address: contractAddress, asset_contract_type: null, @@ -529,17 +514,17 @@ export class CollectiblesController extends BaseController< } /** - * Request collectible contract information from the contract itself. + * Request NFT contract information from the contract itself. * - * @param contractAddress - Hex address of the collectible contract. - * @returns Promise resolving to the current collectible name and image. + * @param contractAddress - Hex address of the NFT contract. + * @returns Promise resolving to the current NFT name and image. */ - private async getCollectibleContractInformationFromContract( + private async getNftContractInformationFromContract( contractAddress: string, ): Promise< - Partial & - Pick & - Pick + Partial & + Pick & + Pick > { const name = await this.getERC721AssetName(contractAddress); const symbol = await this.getERC721AssetSymbol(contractAddress); @@ -551,34 +536,28 @@ export class CollectiblesController extends BaseController< } /** - * Request collectible contract information from OpenSea API. + * Request NFT contract information from OpenSea API. * - * @param contractAddress - Hex address of the collectible contract. - * @returns Promise resolving to the collectible contract name, image and description. + * @param contractAddress - Hex address of the NFT contract. + * @returns Promise resolving to the NFT contract name, image and description. */ - private async getCollectibleContractInformation( + private async getNftContractInformation( contractAddress: string, ): Promise< - Partial & - Pick & - Pick + Partial & + Pick & + Pick > { - const blockchainContractData: Partial & - Pick & - Pick = await safelyExecute( - async () => { - return await this.getCollectibleContractInformationFromContract( - contractAddress, - ); - }, - ); + const blockchainContractData: Partial & + Pick & + Pick = await safelyExecute(async () => { + return await this.getNftContractInformationFromContract(contractAddress); + }); - let openSeaContractData: Partial | undefined; + let openSeaContractData: Partial | undefined; if (this.config.openSeaEnabled) { openSeaContractData = await safelyExecute(async () => { - return await this.getCollectibleContractInformationFromApi( - contractAddress, - ); + return await this.getNftContractInformationFromApi(contractAddress); }); } @@ -609,27 +588,27 @@ export class CollectiblesController extends BaseController< } /** - * Adds an individual collectible to the stored collectible list. + * Adds an individual NFT to the stored NFT list. * - * @param address - Hex address of the collectible contract. - * @param tokenId - The collectible identifier. - * @param collectibleMetadata - Collectible optional information (name, image and description). - * @param collectibleContract - An object containing contract data of the collectible being added. - * @param detection - The chain ID and address of the currently selected network and account at the moment the collectible was detected. - * @returns Promise resolving to the current collectible list. + * @param address - Hex address of the NFT contract. + * @param tokenId - The NFT identifier. + * @param nftMetadata - NFT optional information (name, image and description). + * @param nftContract - An object containing contract data of the NFT being added. + * @param detection - The chain ID and address of the currently selected network and account at the moment the NFT was detected. + * @returns Promise resolving to the current NFT list. */ - private async addIndividualCollectible( + private async addIndividualNft( address: string, tokenId: string, - collectibleMetadata: CollectibleMetadata, - collectibleContract: CollectibleContract, + nftMetadata: NftMetadata, + nftContract: NftContract, detection?: AccountParams, - ): Promise { + ): Promise { // TODO: Remove unused return const releaseLock = await this.mutex.acquire(); try { address = toChecksumHexAddress(address); - const { allCollectibles } = this.state; + const { allNfts } = this.state; let chainId, selectedAddress; if (detection) { @@ -640,81 +619,80 @@ export class CollectiblesController extends BaseController< selectedAddress = this.config.selectedAddress; } - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; + const nfts = allNfts[selectedAddress]?.[chainId] || []; - const existingEntry: Collectible | undefined = collectibles.find( - (collectible) => - collectible.address.toLowerCase() === address.toLowerCase() && - collectible.tokenId === tokenId, + const existingEntry: Nft | undefined = nfts.find( + (nft) => + nft.address.toLowerCase() === address.toLowerCase() && + nft.tokenId === tokenId, ); if (existingEntry) { - const differentMetadata = compareCollectiblesMetadata( - collectibleMetadata, + const differentMetadata = compareNftMetadata( + nftMetadata, existingEntry, ); if (differentMetadata) { // TODO: Switch to indexToUpdate - const indexToRemove = collectibles.findIndex( - (collectible) => - collectible.address.toLowerCase() === address.toLowerCase() && - collectible.tokenId === tokenId, + const indexToRemove = nfts.findIndex( + (nft) => + nft.address.toLowerCase() === address.toLowerCase() && + nft.tokenId === tokenId, ); /* istanbul ignore next */ if (indexToRemove !== -1) { - collectibles.splice(indexToRemove, 1); + nfts.splice(indexToRemove, 1); } } else { - return collectibles; + return nfts; } } - const newEntry: Collectible = { + const newEntry: Nft = { address, tokenId, favorite: existingEntry?.favorite || false, isCurrentlyOwned: true, - ...collectibleMetadata, + ...nftMetadata, }; - const newCollectibles = [...collectibles, newEntry]; - this.updateNestedCollectibleState( - newCollectibles, - ALL_COLLECTIBLES_STATE_KEY, - { chainId, userAddress: selectedAddress }, - ); + const newNfts = [...nfts, newEntry]; + this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY, { + chainId, + userAddress: selectedAddress, + }); - if (this.onCollectibleAdded) { - this.onCollectibleAdded({ + if (this.onNftAdded) { + this.onNftAdded({ address, - symbol: collectibleContract.symbol, + symbol: nftContract.symbol, tokenId: tokenId.toString(), - standard: collectibleMetadata.standard, + standard: nftMetadata.standard, source: detection ? 'detected' : 'custom', }); } - return newCollectibles; + return newNfts; } finally { releaseLock(); } } /** - * Adds a collectible contract to the stored collectible contracts list. + * Adds an NFT contract to the stored NFT contracts list. * - * @param address - Hex address of the collectible contract. - * @param detection - The chain ID and address of the currently selected network and account at the moment the collectible was detected. - * @returns Promise resolving to the current collectible contracts list. + * @param address - Hex address of the NFT contract. + * @param detection - The chain ID and address of the currently selected network and account at the moment the NFT was detected. + * @returns Promise resolving to the current NFT contracts list. */ - private async addCollectibleContract( + private async addNftContract( address: string, detection?: AccountParams, - ): Promise { + ): Promise { const releaseLock = await this.mutex.acquire(); try { address = toChecksumHexAddress(address); - const { allCollectibleContracts } = this.state; + const { allNftContracts } = this.state; let chainId, selectedAddress; if (detection) { @@ -725,19 +703,16 @@ export class CollectiblesController extends BaseController< selectedAddress = this.config.selectedAddress; } - const collectibleContracts = - allCollectibleContracts[selectedAddress]?.[chainId] || []; + const nftContracts = allNftContracts[selectedAddress]?.[chainId] || []; - const existingEntry = collectibleContracts.find( - (collectibleContract) => - collectibleContract.address.toLowerCase() === address.toLowerCase(), + const existingEntry = nftContracts.find( + (nftContract) => + nftContract.address.toLowerCase() === address.toLowerCase(), ); if (existingEntry) { - return collectibleContracts; + return nftContracts; } - const contractInformation = await this.getCollectibleContractInformation( - address, - ); + const contractInformation = await this.getNftContractInformation(address); const { asset_contract_type, @@ -755,11 +730,11 @@ export class CollectiblesController extends BaseController< (detection && !name) || Object.keys(contractInformation).length === 0 ) { - return collectibleContracts; + return nftContracts; } /* istanbul ignore next */ - const newEntry: CollectibleContract = Object.assign( + const newEntry: NftContract = Object.assign( {}, { address }, description && { description }, @@ -773,105 +748,91 @@ export class CollectiblesController extends BaseController< schema_name && { schemaName: schema_name }, external_link && { externalLink: external_link }, ); - const newCollectibleContracts = [...collectibleContracts, newEntry]; - this.updateNestedCollectibleState( - newCollectibleContracts, - ALL_COLLECTIBLES_CONTRACTS_STATE_KEY, - { chainId, userAddress: selectedAddress }, - ); + const newNftContracts = [...nftContracts, newEntry]; + this.updateNestedNftState(newNftContracts, ALL_NFTS_CONTRACTS_STATE_KEY, { + chainId, + userAddress: selectedAddress, + }); - return newCollectibleContracts; + return newNftContracts; } finally { releaseLock(); } } /** - * Removes an individual collectible from the stored token list and saves it in ignored collectibles list. + * Removes an individual NFT from the stored token list and saves it in ignored NFTs list. * - * @param address - Hex address of the collectible contract. - * @param tokenId - Token identifier of the collectible. + * @param address - Hex address of the NFT contract. + * @param tokenId - Token identifier of the NFT. */ - private removeAndIgnoreIndividualCollectible( - address: string, - tokenId: string, - ) { + private removeAndIgnoreIndividualNft(address: string, tokenId: string) { address = toChecksumHexAddress(address); - const { allCollectibles, ignoredCollectibles } = this.state; + const { allNfts, ignoredNfts } = this.state; const { chainId, selectedAddress } = this.config; - const newIgnoredCollectibles = [...ignoredCollectibles]; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const newCollectibles = collectibles.filter((collectible) => { + const newIgnoredNfts = [...ignoredNfts]; + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const newNfts = nfts.filter((nft) => { if ( - collectible.address.toLowerCase() === address.toLowerCase() && - collectible.tokenId === tokenId + nft.address.toLowerCase() === address.toLowerCase() && + nft.tokenId === tokenId ) { - const alreadyIgnored = newIgnoredCollectibles.find( + const alreadyIgnored = newIgnoredNfts.find( (c) => c.address === address && c.tokenId === tokenId, ); - !alreadyIgnored && newIgnoredCollectibles.push(collectible); + !alreadyIgnored && newIgnoredNfts.push(nft); return false; } return true; }); - this.updateNestedCollectibleState( - newCollectibles, - ALL_COLLECTIBLES_STATE_KEY, - ); + this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY); this.update({ - ignoredCollectibles: newIgnoredCollectibles, + ignoredNfts: newIgnoredNfts, }); } /** - * Removes an individual collectible from the stored token list. + * Removes an individual NFT from the stored token list. * - * @param address - Hex address of the collectible contract. - * @param tokenId - Token identifier of the collectible. + * @param address - Hex address of the NFT contract. + * @param tokenId - Token identifier of the NFT. */ - private removeIndividualCollectible(address: string, tokenId: string) { + private removeIndividualNft(address: string, tokenId: string) { address = toChecksumHexAddress(address); - const { allCollectibles } = this.state; + const { allNfts } = this.state; const { chainId, selectedAddress } = this.config; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const newCollectibles = collectibles.filter( - (collectible) => + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const newNfts = nfts.filter( + (nft) => !( - collectible.address.toLowerCase() === address.toLowerCase() && - collectible.tokenId === tokenId + nft.address.toLowerCase() === address.toLowerCase() && + nft.tokenId === tokenId ), ); - this.updateNestedCollectibleState( - newCollectibles, - ALL_COLLECTIBLES_STATE_KEY, - ); + this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY); } /** - * Removes a collectible contract to the stored collectible contracts list. + * Removes an NFT contract to the stored NFT contracts list. * - * @param address - Hex address of the collectible contract. - * @returns Promise resolving to the current collectible contracts list. + * @param address - Hex address of the NFT contract. + * @returns Promise resolving to the current NFT contracts list. */ - private removeCollectibleContract(address: string): CollectibleContract[] { + private removeNftContract(address: string): NftContract[] { address = toChecksumHexAddress(address); - const { allCollectibleContracts } = this.state; + const { allNftContracts } = this.state; const { chainId, selectedAddress } = this.config; - const collectibleContracts = - allCollectibleContracts[selectedAddress]?.[chainId] || []; + const nftContracts = allNftContracts[selectedAddress]?.[chainId] || []; - const newCollectibleContracts = collectibleContracts.filter( - (collectibleContract) => - !(collectibleContract.address.toLowerCase() === address.toLowerCase()), - ); - this.updateNestedCollectibleState( - newCollectibleContracts, - ALL_COLLECTIBLES_CONTRACTS_STATE_KEY, + const newNftContracts = nftContracts.filter( + (nftContract) => + !(nftContract.address.toLowerCase() === address.toLowerCase()), ); + this.updateNestedNftState(newNftContracts, ALL_NFTS_CONTRACTS_STATE_KEY); - return newCollectibleContracts; + return newNftContracts; } /** @@ -887,7 +848,7 @@ export class CollectiblesController extends BaseController< /** * Name of this controller used during composition */ - override name = 'CollectiblesController'; + override name = 'NftController'; private getERC721AssetName: AssetsContractController['getERC721AssetName']; @@ -901,7 +862,7 @@ export class CollectiblesController extends BaseController< private getERC1155TokenURI: AssetsContractController['getERC1155TokenURI']; - private onCollectibleAdded?: (data: { + private onNftAdded?: (data: { address: string; symbol: string | undefined; tokenId: string; @@ -910,7 +871,7 @@ export class CollectiblesController extends BaseController< }) => void; /** - * Creates a CollectiblesController instance. + * Creates an NftController instance. * * @param options - The controller options. * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes. @@ -918,11 +879,11 @@ export class CollectiblesController extends BaseController< * @param options.getERC721AssetName - Gets the name of the asset at the given address. * @param options.getERC721AssetSymbol - Gets the symbol of the asset at the given address. * @param options.getERC721TokenURI - Gets the URI of the ERC721 token at the given address, with the given ID. - * @param options.getERC721OwnerOf - Get the owner of a ERC-721 collectible. - * @param options.getERC1155BalanceOf - Gets balance of a ERC-1155 collectible. + * @param options.getERC721OwnerOf - Get the owner of a ERC-721 NFT. + * @param options.getERC1155BalanceOf - Gets balance of a ERC-1155 NFT. * @param options.getERC1155TokenURI - Gets the URI of the ERC1155 token at the given address, with the given ID. - * @param options.onCollectibleAdded - Callback that is called when a collectible is added. Currently used pass data - * for tracking the collectible added event. + * @param options.onNftAdded - Callback that is called when an NFT is added. Currently used pass data + * for tracking the NFT added event. * @param config - Initial options used to configure this controller. * @param state - Initial state to set on this controller. */ @@ -936,7 +897,7 @@ export class CollectiblesController extends BaseController< getERC721OwnerOf, getERC1155BalanceOf, getERC1155TokenURI, - onCollectibleAdded, + onNftAdded, }: { onPreferencesStateChange: ( listener: (preferencesState: PreferencesState) => void, @@ -950,7 +911,7 @@ export class CollectiblesController extends BaseController< getERC721OwnerOf: AssetsContractController['getERC721OwnerOf']; getERC1155BalanceOf: AssetsContractController['getERC1155BalanceOf']; getERC1155TokenURI: AssetsContractController['getERC1155TokenURI']; - onCollectibleAdded?: (data: { + onNftAdded?: (data: { address: string; symbol: string | undefined; tokenId: string; @@ -959,7 +920,7 @@ export class CollectiblesController extends BaseController< }) => void; }, config?: Partial, - state?: Partial, + state?: Partial, ) { super(config, state); this.defaultConfig = { @@ -972,9 +933,9 @@ export class CollectiblesController extends BaseController< }; this.defaultState = { - allCollectibleContracts: {}, - allCollectibles: {}, - ignoredCollectibles: [], + allNftContracts: {}, + allNfts: {}, + ignoredNfts: [], }; this.initialize(); this.getERC721AssetName = getERC721AssetName; @@ -983,7 +944,7 @@ export class CollectiblesController extends BaseController< this.getERC721OwnerOf = getERC721OwnerOf; this.getERC1155BalanceOf = getERC1155BalanceOf; this.getERC1155TokenURI = getERC1155TokenURI; - this.onCollectibleAdded = onCollectibleAdded; + this.onNftAdded = onNftAdded; onPreferencesStateChange( ({ selectedAddress, ipfsGateway, openSeaEnabled }) => { @@ -998,7 +959,7 @@ export class CollectiblesController extends BaseController< } /** - * Sets an OpenSea API key to retrieve collectible information. + * Sets an OpenSea API key to retrieve NFT information. * * @param openSeaApiKey - OpenSea API key. */ @@ -1007,24 +968,21 @@ export class CollectiblesController extends BaseController< } /** - * Checks the ownership of a ERC-721 or ERC-1155 collectible for a given address. + * Checks the ownership of a ERC-721 or ERC-1155 NFT for a given address. * * @param ownerAddress - User public address. - * @param collectibleAddress - Collectible contract address. - * @param collectibleId - Collectible token ID. - * @returns Promise resolving the collectible ownership. + * @param nftAddress - NFT contract address. + * @param nftId - NFT token ID. + * @returns Promise resolving the NFT ownership. */ - async isCollectibleOwner( + async isNftOwner( ownerAddress: string, - collectibleAddress: string, - collectibleId: string, + nftAddress: string, + nftId: string, ): Promise { // Checks the ownership for ERC-721. try { - const owner = await this.getERC721OwnerOf( - collectibleAddress, - collectibleId, - ); + const owner = await this.getERC721OwnerOf(nftAddress, nftId); return ownerAddress.toLowerCase() === owner.toLowerCase(); // eslint-disable-next-line no-empty } catch { @@ -1035,8 +993,8 @@ export class CollectiblesController extends BaseController< try { const balance = await this.getERC1155BalanceOf( ownerAddress, - collectibleAddress, - collectibleId, + nftAddress, + nftId, ); return balance > 0; // eslint-disable-next-line no-empty @@ -1050,133 +1008,127 @@ export class CollectiblesController extends BaseController< } /** - * Verifies currently selected address owns entered collectible address/tokenId combo and - * adds the collectible and respective collectible contract to the stored collectible and collectible contracts lists. + * Verifies currently selected address owns entered NFT address/tokenId combo and + * adds the NFT and respective NFT contract to the stored NFT and NFT contracts lists. * - * @param address - Hex address of the collectible contract. - * @param tokenId - The collectible identifier. + * @param address - Hex address of the NFT contract. + * @param tokenId - The NFT identifier. */ - async addCollectibleVerifyOwnership(address: string, tokenId: string) { + async addNftVerifyOwnership(address: string, tokenId: string) { const { selectedAddress } = this.config; - if (!(await this.isCollectibleOwner(selectedAddress, address, tokenId))) { - throw new Error('This collectible is not owned by the user'); + if (!(await this.isNftOwner(selectedAddress, address, tokenId))) { + throw new Error('This NFT is not owned by the user'); } - await this.addCollectible(address, tokenId); + await this.addNft(address, tokenId); } /** - * Adds a collectible and respective collectible contract to the stored collectible and collectible contracts lists. + * Adds an NFT and respective NFT contract to the stored NFT and NFT contracts lists. * - * @param address - Hex address of the collectible contract. - * @param tokenId - The collectible identifier. - * @param collectibleMetadata - Collectible optional metadata. - * @param detection - The chain ID and address of the currently selected network and account at the moment the collectible was detected. - * @returns Promise resolving to the current collectible list. + * @param address - Hex address of the NFT contract. + * @param tokenId - The NFT identifier. + * @param nftMetadata - NFT optional metadata. + * @param detection - The chain ID and address of the currently selected network and account at the moment the NFT was detected. + * @returns Promise resolving to the current NFT list. */ - async addCollectible( + async addNft( address: string, tokenId: string, - collectibleMetadata?: CollectibleMetadata, + nftMetadata?: NftMetadata, detection?: AccountParams, ) { address = toChecksumHexAddress(address); - const newCollectibleContracts = await this.addCollectibleContract( - address, - detection, - ); - collectibleMetadata = - collectibleMetadata || - (await this.getCollectibleInformation(address, tokenId)); + const newNftContracts = await this.addNftContract(address, detection); + nftMetadata = + nftMetadata || (await this.getNftInformation(address, tokenId)); - // If collectible contract was not added, do not add individual collectible - const collectibleContract = newCollectibleContracts.find( + // If NFT contract was not added, do not add individual NFT + const nftContract = newNftContracts.find( (contract) => contract.address.toLowerCase() === address.toLowerCase(), ); - // If collectible contract information, add individual collectible - if (collectibleContract) { - await this.addIndividualCollectible( + // If NFT contract information, add individual NFT + if (nftContract) { + await this.addIndividualNft( address, tokenId, - collectibleMetadata, - collectibleContract, + nftMetadata, + nftContract, detection, ); } } /** - * Removes a collectible from the stored token list. + * Removes an NFT from the stored token list. * - * @param address - Hex address of the collectible contract. - * @param tokenId - Token identifier of the collectible. + * @param address - Hex address of the NFT contract. + * @param tokenId - Token identifier of the NFT. */ - removeCollectible(address: string, tokenId: string) { + removeNft(address: string, tokenId: string) { address = toChecksumHexAddress(address); - this.removeIndividualCollectible(address, tokenId); - const { allCollectibles } = this.state; + this.removeIndividualNft(address, tokenId); + const { allNfts } = this.state; const { chainId, selectedAddress } = this.config; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const remainingCollectible = collectibles.find( - (collectible) => - collectible.address.toLowerCase() === address.toLowerCase(), + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const remainingNft = nfts.find( + (nft) => nft.address.toLowerCase() === address.toLowerCase(), ); - if (!remainingCollectible) { - this.removeCollectibleContract(address); + if (!remainingNft) { + this.removeNftContract(address); } } /** - * Removes a collectible from the stored token list and saves it in ignored collectibles list. + * Removes an NFT from the stored token list and saves it in ignored NFTs list. * - * @param address - Hex address of the collectible contract. - * @param tokenId - Token identifier of the collectible. + * @param address - Hex address of the NFT contract. + * @param tokenId - Token identifier of the NFT. */ - removeAndIgnoreCollectible(address: string, tokenId: string) { + removeAndIgnoreNft(address: string, tokenId: string) { address = toChecksumHexAddress(address); - this.removeAndIgnoreIndividualCollectible(address, tokenId); - const { allCollectibles } = this.state; + this.removeAndIgnoreIndividualNft(address, tokenId); + const { allNfts } = this.state; const { chainId, selectedAddress } = this.config; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const remainingCollectible = collectibles.find( - (collectible) => - collectible.address.toLowerCase() === address.toLowerCase(), + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const remainingNft = nfts.find( + (nft) => nft.address.toLowerCase() === address.toLowerCase(), ); - if (!remainingCollectible) { - this.removeCollectibleContract(address); + if (!remainingNft) { + this.removeNftContract(address); } } /** - * Removes all collectibles from the ignored list. + * Removes all NFTs from the ignored list. */ - clearIgnoredCollectibles() { - this.update({ ignoredCollectibles: [] }); + clearIgnoredNfts() { + this.update({ ignoredNfts: [] }); } /** - * Checks whether input collectible is still owned by the user - * And updates the isCurrentlyOwned value on the collectible object accordingly. + * Checks whether input NFT is still owned by the user + * And updates the isCurrentlyOwned value on the NFT object accordingly. * - * @param collectible - The collectible object to check and update. + * @param nft - The NFT object to check and update. * @param batch - A boolean indicating whether this method is being called as part of a batch or single update. * @param accountParams - The userAddress and chainId to check ownership against * @param accountParams.userAddress - the address passed through the confirmed transaction flow to ensure detected assets are stored to the correct account * @param accountParams.chainId - the chainId passed through the confirmed transaction flow to ensure detected assets are stored to the correct account - * @returns the collectible with the updated isCurrentlyOwned value + * @returns the NFT with the updated isCurrentlyOwned value */ - async checkAndUpdateSingleCollectibleOwnershipStatus( - collectible: Collectible, + async checkAndUpdateSingleNftOwnershipStatus( + nft: Nft, batch: boolean, { userAddress, chainId }: AccountParams | undefined = { userAddress: this.config.selectedAddress, chainId: this.config.chainId, }, ) { - const { address, tokenId } = collectible; - let isOwned = collectible.isCurrentlyOwned; + const { address, tokenId } = nft; + let isOwned = nft.isCurrentlyOwned; try { - isOwned = await this.isCollectibleOwner(userAddress, address, tokenId); + isOwned = await this.isNftOwner(userAddress, address, tokenId); } catch (error) { if ( !( @@ -1188,208 +1140,187 @@ export class CollectiblesController extends BaseController< } } - collectible.isCurrentlyOwned = isOwned; + nft.isCurrentlyOwned = isOwned; if (batch === true) { - return collectible; + return nft; } - // if this is not part of a batched update we update this one collectible in state - const { allCollectibles } = this.state; - const collectibles = allCollectibles[userAddress]?.[chainId] || []; - const collectibleToUpdate = collectibles.find( + // if this is not part of a batched update we update this one NFT in state + const { allNfts } = this.state; + const nfts = allNfts[userAddress]?.[chainId] || []; + const nftToUpdate = nfts.find( (item) => item.tokenId === tokenId && item.address.toLowerCase() === address.toLowerCase(), ); - if (collectibleToUpdate) { - collectibleToUpdate.isCurrentlyOwned = isOwned; - this.updateNestedCollectibleState( - collectibles, - ALL_COLLECTIBLES_STATE_KEY, - { userAddress, chainId }, - ); + if (nftToUpdate) { + nftToUpdate.isCurrentlyOwned = isOwned; + this.updateNestedNftState(nfts, ALL_NFTS_STATE_KEY, { + userAddress, + chainId, + }); } - return collectible; + return nft; } /** - * Checks whether Collectibles associated with current selectedAddress/chainId combination are still owned by the user + * Checks whether NFTs associated with current selectedAddress/chainId combination are still owned by the user * And updates the isCurrentlyOwned value on each accordingly. */ - async checkAndUpdateAllCollectiblesOwnershipStatus() { - const { allCollectibles } = this.state; + async checkAndUpdateAllNftsOwnershipStatus() { + const { allNfts } = this.state; const { chainId, selectedAddress } = this.config; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const updatedCollectibles = await Promise.all( - collectibles.map(async (collectible) => { + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const updatedNfts = await Promise.all( + nfts.map(async (nft) => { return ( - (await this.checkAndUpdateSingleCollectibleOwnershipStatus( - collectible, - true, - )) ?? collectible + (await this.checkAndUpdateSingleNftOwnershipStatus(nft, true)) ?? nft ); }), ); - this.updateNestedCollectibleState( - updatedCollectibles, - ALL_COLLECTIBLES_STATE_KEY, - ); + this.updateNestedNftState(updatedNfts, ALL_NFTS_STATE_KEY); } /** - * Update collectible favorite status. + * Update NFT favorite status. * - * @param address - Hex address of the collectible contract. - * @param tokenId - Hex address of the collectible contract. - * @param favorite - Collectible new favorite status. + * @param address - Hex address of the NFT contract. + * @param tokenId - Hex address of the NFT contract. + * @param favorite - NFT new favorite status. */ - updateCollectibleFavoriteStatus( - address: string, - tokenId: string, - favorite: boolean, - ) { - const { allCollectibles } = this.state; + updateNftFavoriteStatus(address: string, tokenId: string, favorite: boolean) { + const { allNfts } = this.state; const { chainId, selectedAddress } = this.config; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const index: number = collectibles.findIndex( - (collectible) => - collectible.address === address && collectible.tokenId === tokenId, + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const index: number = nfts.findIndex( + (nft) => nft.address === address && nft.tokenId === tokenId, ); if (index === -1) { return; } - const updatedCollectible: Collectible = { - ...collectibles[index], + const updatedNft: Nft = { + ...nfts[index], favorite, }; - // Update Collectibles array - collectibles[index] = updatedCollectible; + // Update Nfts array + nfts[index] = updatedNft; - this.updateNestedCollectibleState(collectibles, ALL_COLLECTIBLES_STATE_KEY); + this.updateNestedNftState(nfts, ALL_NFTS_STATE_KEY); } /** - * Returns the collectible by the address and token id. + * Returns an NFT by the address and token id. * - * @param address - Hex address of the collectible contract. + * @param address - Hex address of the NFT contract. * @param tokenId - Number that represents the id of the token. * @param selectedAddress - Hex address of the user account. * @param chainId - Id of the current network. - * @returns Object containing the Collectible and their position in the array + * @returns Object containing the NFT and its position in the array */ - findCollectibleByAddressAndTokenId( + findNftByAddressAndTokenId( address: string, tokenId: string, selectedAddress: string, chainId: string, - ): { collectible: Collectible; index: number } | null { - const { allCollectibles } = this.state; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - - const index: number = collectibles.findIndex( - (collectible) => - collectible.address.toLowerCase() === address.toLowerCase() && - collectible.tokenId === tokenId, + ): { nft: Nft; index: number } | null { + const { allNfts } = this.state; + const nfts = allNfts[selectedAddress]?.[chainId] || []; + + const index: number = nfts.findIndex( + (nft) => + nft.address.toLowerCase() === address.toLowerCase() && + nft.tokenId === tokenId, ); if (index === -1) { return null; } - return { collectible: collectibles[index], index }; + return { nft: nfts[index], index }; } /** - * Update collectible data. + * Update NFT data. * - * @param collectible - Collectible object to find the right collectible to updates. - * @param updates - Collectible partial object to update properties of the collectible. + * @param nft - NFT object to find the right NFT to updates. + * @param updates - NFT partial object to update properties of the NFT. * @param selectedAddress - Hex address of the user account. * @param chainId - Id of the current network. */ - updateCollectible( - collectible: Collectible, - updates: Partial, + updateNft( + nft: Nft, + updates: Partial, selectedAddress: string, chainId: string, ) { - const { allCollectibles } = this.state; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const collectibleInfo = this.findCollectibleByAddressAndTokenId( - collectible.address, - collectible.tokenId, + const { allNfts } = this.state; + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const nftInfo = this.findNftByAddressAndTokenId( + nft.address, + nft.tokenId, selectedAddress, chainId, ); - if (!collectibleInfo) { + if (!nftInfo) { return; } - const updatedCollectible: Collectible = { - ...collectible, + const updatedNft: Nft = { + ...nft, ...updates, }; - // Update Collectibles array - - const newCollectibles = [ - ...collectibles.slice(0, collectibleInfo.index), - updatedCollectible, - ...collectibles.slice(collectibleInfo.index + 1), + const newNfts = [ + ...nfts.slice(0, nftInfo.index), + updatedNft, + ...nfts.slice(nftInfo.index + 1), ]; - this.updateNestedCollectibleState( - newCollectibles, - ALL_COLLECTIBLES_STATE_KEY, - ); + this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY); } /** - * Resets the transaction status of a collectible. + * Resets the transaction status of an NFT. * - * @param transactionId - Collectible transaction id. + * @param transactionId - NFT transaction id. * @param selectedAddress - Hex address of the user account. * @param chainId - Id of the current network. * @returns a boolean indicating if the reset was well succeded or not */ - resetCollectibleTransactionStatusByTransactionId( + resetNftTransactionStatusByTransactionId( transactionId: string, selectedAddress: string, chainId: string, ): boolean { - const { allCollectibles } = this.state; - const collectibles = allCollectibles[selectedAddress]?.[chainId] || []; - const index: number = collectibles.findIndex( - (collectible) => collectible.transactionId === transactionId, + const { allNfts } = this.state; + const nfts = allNfts[selectedAddress]?.[chainId] || []; + const index: number = nfts.findIndex( + (nft) => nft.transactionId === transactionId, ); if (index === -1) { return false; } - const updatedCollectible: Collectible = { - ...collectibles[index], + const updatedNft: Nft = { + ...nfts[index], transactionId: undefined, }; - // Update Collectibles array - const newCollectibles = [ - ...collectibles.slice(0, index), - updatedCollectible, - ...collectibles.slice(index + 1), + const newNfts = [ + ...nfts.slice(0, index), + updatedNft, + ...nfts.slice(index + 1), ]; - this.updateNestedCollectibleState( - newCollectibles, - ALL_COLLECTIBLES_STATE_KEY, - ); + this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY); return true; } } -export default CollectiblesController; +export default NftController; diff --git a/src/assets/CollectibleDetectionController.test.ts b/src/assets/NftDetectionController.test.ts similarity index 66% rename from src/assets/CollectibleDetectionController.test.ts rename to src/assets/NftDetectionController.test.ts index b6d73e7055d..480838f7af2 100644 --- a/src/assets/CollectibleDetectionController.test.ts +++ b/src/assets/NftDetectionController.test.ts @@ -7,18 +7,18 @@ import { import { PreferencesController } from '../user/PreferencesController'; import { OPENSEA_PROXY_URL } from '../constants'; import { ControllerMessenger } from '../ControllerMessenger'; -import { CollectiblesController } from './CollectiblesController'; +import { NftController } from './NftController'; import { AssetsContractController } from './AssetsContractController'; -import { CollectibleDetectionController } from './CollectibleDetectionController'; +import { NftDetectionController } from './NftDetectionController'; const DEFAULT_INTERVAL = 180000; const MAINNET = 'mainnet'; const ROPSTEN = 'ropsten'; -describe('CollectibleDetectionController', () => { - let collectibleDetection: CollectibleDetectionController; +describe('NftDetectionController', () => { + let nftDetection: NftDetectionController; let preferences: PreferencesController; - let collectiblesController: CollectiblesController; + let nftController: NftController; let assetsContract: AssetsContractController; let messenger: NetworkControllerMessenger; @@ -49,7 +49,7 @@ describe('CollectibleDetectionController', () => { messenger.subscribe('NetworkController:stateChange', listener), }); - collectiblesController = new CollectiblesController({ + nftController = new NftController({ onPreferencesStateChange: (listener) => preferences.subscribe(listener), onNetworkStateChange: (listener) => messenger.subscribe('NetworkController:stateChange', listener), @@ -63,25 +63,22 @@ describe('CollectibleDetectionController', () => { assetsContract.getERC1155BalanceOf.bind(assetsContract), getERC1155TokenURI: assetsContract.getERC1155TokenURI.bind(assetsContract), - onCollectibleAdded: jest.fn(), + onNftAdded: jest.fn(), }); - collectibleDetection = new CollectibleDetectionController({ - onCollectiblesStateChange: (listener) => - collectiblesController.subscribe(listener), + nftDetection = new NftDetectionController({ + onNftsStateChange: (listener) => nftController.subscribe(listener), onPreferencesStateChange: (listener) => preferences.subscribe(listener), onNetworkStateChange: (listener) => messenger.subscribe('NetworkController:stateChange', listener), getOpenSeaApiKey: getOpenSeaApiKeyStub, - addCollectible: collectiblesController.addCollectible.bind( - collectiblesController, - ), - getCollectiblesState: () => collectiblesController.state, + addNft: nftController.addNft.bind(nftController), + getNftState: () => nftController.state, }); - collectiblesController.configure({ chainId: '1', selectedAddress: '0x1' }); + nftController.configure({ chainId: '1', selectedAddress: '0x1' }); preferences.setOpenSeaEnabled(true); - preferences.setUseCollectibleDetection(true); + preferences.setUseNftDetection(true); nock(OPENSEA_PROXY_URL) .get(`/assets?owner=0x2&offset=0&limit=50`) @@ -219,8 +216,8 @@ describe('CollectibleDetectionController', () => { }); it('should set default config', () => { - preferences.setUseCollectibleDetection(false); - expect(collectibleDetection.config).toStrictEqual({ + preferences.setUseNftDetection(false); + expect(nftDetection.config).toStrictEqual({ interval: DEFAULT_INTERVAL, networkType: 'mainnet', chainId: '1', @@ -229,92 +226,84 @@ describe('CollectibleDetectionController', () => { }); }); - it('should poll and detect collectibles on interval while on mainnet', async () => { + it('should poll and detect NFTs on interval while on mainnet', async () => { await new Promise((resolve) => { - const mockCollectibles = sinon.stub( - CollectibleDetectionController.prototype, - 'detectCollectibles', + const mockNfts = sinon.stub( + NftDetectionController.prototype, + 'detectNfts', ); - const collectiblesDetectionController = - new CollectibleDetectionController( - { - onCollectiblesStateChange: (listener) => - collectiblesController.subscribe(listener), - onPreferencesStateChange: (listener) => - preferences.subscribe(listener), - onNetworkStateChange: (listener) => - messenger.subscribe('NetworkController:stateChange', listener), - getOpenSeaApiKey: () => collectiblesController.openSeaApiKey, - addCollectible: collectiblesController.addCollectible.bind( - collectiblesController, - ), - getCollectiblesState: () => collectiblesController.state, - }, - { interval: 10 }, - ); - collectiblesDetectionController.configure({ disabled: false }); - collectiblesDetectionController.start(); - expect(mockCollectibles.calledOnce).toBe(true); + const nftsDetectionController = new NftDetectionController( + { + onNftsStateChange: (listener) => nftController.subscribe(listener), + onPreferencesStateChange: (listener) => + preferences.subscribe(listener), + onNetworkStateChange: (listener) => + messenger.subscribe('NetworkController:stateChange', listener), + getOpenSeaApiKey: () => nftController.openSeaApiKey, + addNft: nftController.addNft.bind(nftController), + getNftState: () => nftController.state, + }, + { interval: 10 }, + ); + nftsDetectionController.configure({ disabled: false }); + nftsDetectionController.start(); + expect(mockNfts.calledOnce).toBe(true); setTimeout(() => { - expect(mockCollectibles.calledTwice).toBe(true); + expect(mockNfts.calledTwice).toBe(true); resolve(''); }, 15); }); }); it('should detect mainnet correctly', () => { - collectibleDetection.configure({ networkType: MAINNET }); - expect(collectibleDetection.isMainnet()).toStrictEqual(true); - collectibleDetection.configure({ networkType: ROPSTEN }); - expect(collectibleDetection.isMainnet()).toStrictEqual(false); + nftDetection.configure({ networkType: MAINNET }); + expect(nftDetection.isMainnet()).toStrictEqual(true); + nftDetection.configure({ networkType: ROPSTEN }); + expect(nftDetection.isMainnet()).toStrictEqual(false); }); it('should not autodetect while not on mainnet', async () => { await new Promise((resolve) => { - const mockCollectibles = sinon.stub( - CollectibleDetectionController.prototype, - 'detectCollectibles', + const mockNfts = sinon.stub( + NftDetectionController.prototype, + 'detectNfts', ); - new CollectibleDetectionController( + new NftDetectionController( { - onCollectiblesStateChange: (listener) => - collectiblesController.subscribe(listener), + onNftsStateChange: (listener) => nftController.subscribe(listener), onPreferencesStateChange: (listener) => preferences.subscribe(listener), onNetworkStateChange: (listener) => messenger.subscribe('NetworkController:stateChange', listener), - getOpenSeaApiKey: () => collectiblesController.openSeaApiKey, - addCollectible: collectiblesController.addCollectible.bind( - collectiblesController, - ), - getCollectiblesState: () => collectiblesController.state, + getOpenSeaApiKey: () => nftController.openSeaApiKey, + addNft: nftController.addNft.bind(nftController), + getNftState: () => nftController.state, }, { interval: 10, networkType: ROPSTEN }, ); - expect(mockCollectibles.called).toBe(false); + expect(mockNfts.called).toBe(false); resolve(''); }); }); - it('should detect and add collectibles correctly', async () => { + it('should detect and add NFTs correctly', async () => { const selectedAddress = '0x1'; - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress, }); - collectiblesController.configure({ + nftController.configure({ networkType: MAINNET, selectedAddress, }); - const { chainId } = collectibleDetection.config; + const { chainId } = nftDetection.config; - await collectibleDetection.detectCollectibles(); + await nftDetection.detectNfts(); - const collectibles = - collectiblesController.state.allCollectibles[selectedAddress][chainId]; - expect(collectibles).toStrictEqual([ + const nfts = nftController.state.allNfts[selectedAddress][chainId]; + expect(nfts).toStrictEqual([ { address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description 2574', @@ -328,17 +317,17 @@ describe('CollectibleDetectionController', () => { ]); }); - it('should detect, add collectibles and do nor remove not detected collectibles correctly', async () => { + it('should detect, add NFTs and do nor remove not detected NFTs correctly', async () => { const selectedAddress = '0x1'; - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress, }); - collectiblesController.configure({ selectedAddress }); + nftController.configure({ selectedAddress }); - const { chainId } = collectibleDetection.config; + const { chainId } = nftDetection.config; - await collectiblesController.addCollectible( + await nftController.addNft( '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', '2573', { @@ -349,12 +338,11 @@ describe('CollectibleDetectionController', () => { }, ); - await collectibleDetection.detectCollectibles(); + await nftDetection.detectNfts(); - const collectibles = - collectiblesController.state.allCollectibles[selectedAddress][chainId]; + const nfts = nftController.state.allNfts[selectedAddress][chainId]; - expect(collectibles).toStrictEqual([ + expect(nfts).toStrictEqual([ { address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description 2573', @@ -378,96 +366,96 @@ describe('CollectibleDetectionController', () => { ]); }); - it('should not autodetect collectibles that exist in the ignoreList', async () => { + it('should not autodetect NFTs that exist in the ignoreList', async () => { const selectedAddress = '0x2'; - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress: '0x2', }); - collectiblesController.configure({ selectedAddress }); + nftController.configure({ selectedAddress }); - const { chainId } = collectibleDetection.config; + const { chainId } = nftDetection.config; - await collectibleDetection.detectCollectibles(); - expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], - ).toHaveLength(1); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(0); - collectiblesController.removeAndIgnoreCollectible( + await nftDetection.detectNfts(); + expect(nftController.state.allNfts[selectedAddress][chainId]).toHaveLength( + 1, + ); + expect(nftController.state.ignoredNfts).toHaveLength(0); + nftController.removeAndIgnoreNft( '0x1d963688FE2209A98dB35C67A041524822Cf04ff', '2577', ); - expect(collectiblesController.state.ignoredCollectibles).toHaveLength(1); - await collectibleDetection.detectCollectibles(); - expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], - ).toHaveLength(0); + expect(nftController.state.ignoredNfts).toHaveLength(1); + await nftDetection.detectNfts(); + expect(nftController.state.allNfts[selectedAddress][chainId]).toHaveLength( + 0, + ); }); - it('should not detect and add collectibles if there is no selectedAddress', async () => { + it('should not detect and add NFTs if there is no selectedAddress', async () => { const selectedAddress = ''; - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress, }); - const { chainId } = collectibleDetection.config; - await collectibleDetection.detectCollectibles(); - const { allCollectibles } = collectiblesController.state; - expect(allCollectibles[selectedAddress]?.[chainId]).toBeUndefined(); + const { chainId } = nftDetection.config; + await nftDetection.detectNfts(); + const { allNfts } = nftController.state; + expect(allNfts[selectedAddress]?.[chainId]).toBeUndefined(); }); - it('should not detect and add collectibles to the wrong selectedAddress', async () => { - collectibleDetection.configure({ + it('should not detect and add NFTs to the wrong selectedAddress', async () => { + nftDetection.configure({ networkType: MAINNET, selectedAddress: '0x9', }); - const { chainId } = collectibleDetection.config; + const { chainId } = nftDetection.config; - collectiblesController.configure({ selectedAddress: '0x9' }); - collectibleDetection.detectCollectibles(); - collectibleDetection.configure({ selectedAddress: '0x12' }); - collectiblesController.configure({ selectedAddress: '0x12' }); + nftController.configure({ selectedAddress: '0x9' }); + nftDetection.detectNfts(); + nftDetection.configure({ selectedAddress: '0x12' }); + nftController.configure({ selectedAddress: '0x12' }); await new Promise((res) => setTimeout(() => res(true), 1000)); - expect(collectibleDetection.config.selectedAddress).toStrictEqual('0x12'); + expect(nftDetection.config.selectedAddress).toStrictEqual('0x12'); expect( - collectiblesController.state.allCollectibles[ - collectibleDetection.config.selectedAddress - ]?.[chainId], + nftController.state.allNfts[nftDetection.config.selectedAddress]?.[ + chainId + ], ).toBeUndefined(); }); - it('should not detect and add collectibles if preferences controller useCollectibleDetection is set to false', async () => { - preferences.setUseCollectibleDetection(false); + it('should not detect and add NFTs if preferences controller useNftDetection is set to false', async () => { + preferences.setUseNftDetection(false); const selectedAddress = '0x9'; - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress, }); - const { chainId } = collectiblesController.config; - collectibleDetection.detectCollectibles(); + const { chainId } = nftController.config; + nftDetection.detectNfts(); expect( - collectiblesController.state.allCollectibles[selectedAddress]?.[chainId], + nftController.state.allNfts[selectedAddress]?.[chainId], ).toBeUndefined(); }); - it('should not detect and add collectibles if preferences controller openSeaEnabled is set to false', async () => { + it('should not detect and add NFTs if preferences controller openSeaEnabled is set to false', async () => { preferences.setOpenSeaEnabled(false); const selectedAddress = '0x9'; - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress, }); - const { chainId } = collectiblesController.config; - collectibleDetection.detectCollectibles(); + const { chainId } = nftController.config; + nftDetection.detectNfts(); expect( - collectiblesController.state.allCollectibles[selectedAddress]?.[chainId], + nftController.state.allNfts[selectedAddress]?.[chainId], ).toBeUndefined(); }); - it('should not add collectible if collectible or collectible contract has no information to display', async () => { - const collectibleHH2574 = { + it('should not add NFT if NFT or NFT contract has no information to display', async () => { + const nftHH2574 = { address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description 2574', image: 'image/2574.png', @@ -477,7 +465,7 @@ describe('CollectibleDetectionController', () => { favorite: false, isCurrentlyOwned: true, }; - const collectibleGG2574 = { + const nftGG2574 = { address: '0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc', description: 'Description 2574', image: 'image/2574.png', @@ -487,7 +475,7 @@ describe('CollectibleDetectionController', () => { favorite: false, isCurrentlyOwned: true, }; - const collectibleII2577 = { + const nftII2577 = { address: '0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d', description: 'Description 2577', image: 'image/2577.png', @@ -497,7 +485,7 @@ describe('CollectibleDetectionController', () => { favorite: false, isCurrentlyOwned: true, }; - const collectibleContractHH = { + const nftContractHH = { address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description HH', logo: 'url HH', @@ -505,7 +493,7 @@ describe('CollectibleDetectionController', () => { symbol: 'HH', totalSupply: 10, }; - const collectibleContractGG = { + const nftContractGG = { address: '0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc', description: 'Description GG', logo: 'url GG', @@ -513,7 +501,7 @@ describe('CollectibleDetectionController', () => { symbol: 'GG', totalSupply: 10, }; - const collectibleContractII = { + const nftContractII = { address: '0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d', description: 'Description II', logo: 'url II', @@ -523,28 +511,26 @@ describe('CollectibleDetectionController', () => { }; const selectedAddress = '0x1'; - collectibleDetection.configure({ + nftDetection.configure({ selectedAddress, networkType: MAINNET, }); - collectiblesController.configure({ + nftController.configure({ selectedAddress, networkType: MAINNET, }); - const { chainId } = collectibleDetection.config; - await collectibleDetection.detectCollectibles(); + const { chainId } = nftDetection.config; + await nftDetection.detectNfts(); // First fetch to API, only gets information from contract ending in HH - expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], - ).toStrictEqual([collectibleHH2574]); + expect(nftController.state.allNfts[selectedAddress][chainId]).toStrictEqual( + [nftHH2574], + ); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ], - ).toStrictEqual([collectibleContractHH]); + nftController.state.allNftContracts[selectedAddress][chainId], + ).toStrictEqual([nftContractHH]); // During next call of assets detection, API succeds returning contract ending in gg information nock(OPENSEA_PROXY_URL) @@ -620,28 +606,22 @@ describe('CollectibleDetectionController', () => { assets: [], }); - // Now user should have respective collectibles - await collectibleDetection.detectCollectibles(); + // Now user should have respective NFTs + await nftDetection.detectNfts(); expect( - collectiblesController.state.allCollectibleContracts[selectedAddress][ - chainId - ], - ).toStrictEqual([ - collectibleContractHH, - collectibleContractII, - collectibleContractGG, - ]); + nftController.state.allNftContracts[selectedAddress][chainId], + ).toStrictEqual([nftContractHH, nftContractII, nftContractGG]); - expect( - collectiblesController.state.allCollectibles[selectedAddress][chainId], - ).toStrictEqual([collectibleHH2574, collectibleII2577, collectibleGG2574]); + expect(nftController.state.allNfts[selectedAddress][chainId]).toStrictEqual( + [nftHH2574, nftII2577, nftGG2574], + ); }); it('should fallback to use OpenSea API directly when the OpenSea proxy server is down or responds with a failure', async () => { const selectedAddress = '0x3'; getOpenSeaApiKeyStub.mockImplementation(() => 'FAKE API KEY'); - collectiblesController.setApiKey('FAKE API KEY'); + nftController.setApiKey('FAKE API KEY'); nock('https://proxy.metaswap.codefi.network:443', { encodedQueryParams: true, @@ -700,23 +680,22 @@ describe('CollectibleDetectionController', () => { }, }); - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress, }); - collectiblesController.configure({ + nftController.configure({ networkType: MAINNET, selectedAddress, }); - const { chainId } = collectibleDetection.config; + const { chainId } = nftDetection.config; - await collectibleDetection.detectCollectibles(); + await nftDetection.detectNfts(); - const collectibles = - collectiblesController.state.allCollectibles[selectedAddress][chainId]; - expect(collectibles).toStrictEqual([ + const nfts = nftController.state.allNfts[selectedAddress][chainId]; + expect(nfts).toStrictEqual([ { address: '0x1d963688FE2209A98dB35C67A041524822Cf04ff', description: 'DESCRIPTION: DIRECT FROM OPENSEA', @@ -739,18 +718,18 @@ describe('CollectibleDetectionController', () => { .query({ owner: selectedAddress, offset: '0', limit: '50' }) .replyWithError(new Error('UNEXPECTED ERROR')); - collectibleDetection.configure({ + nftDetection.configure({ networkType: MAINNET, selectedAddress, }); - collectiblesController.configure({ + nftController.configure({ networkType: MAINNET, selectedAddress, }); - await expect(() => - collectibleDetection.detectCollectibles(), - ).rejects.toThrow('UNEXPECTED ERROR'); + await expect(() => nftDetection.detectNfts()).rejects.toThrow( + 'UNEXPECTED ERROR', + ); }); }); diff --git a/src/assets/CollectibleDetectionController.ts b/src/assets/NftDetectionController.ts similarity index 50% rename from src/assets/CollectibleDetectionController.ts rename to src/assets/NftDetectionController.ts index 1914a21c370..019222bbcd3 100644 --- a/src/assets/CollectibleDetectionController.ts +++ b/src/assets/NftDetectionController.ts @@ -4,35 +4,31 @@ import type { PreferencesState } from '../user/PreferencesController'; import { fetchWithErrorHandling, toChecksumHexAddress } from '../util'; import { MAINNET, OPENSEA_PROXY_URL, OPENSEA_API_URL } from '../constants'; -import type { - CollectiblesController, - CollectiblesState, - CollectibleMetadata, -} from './CollectiblesController'; +import type { NftController, NftState, NftMetadata } from './NftController'; const DEFAULT_INTERVAL = 180000; /** - * @type ApiCollectible + * @type ApiNft * - * Collectible object coming from OpenSea api - * @property token_id - The collectible identifier + * NFT object coming from OpenSea api + * @property token_id - The NFT identifier * @property num_sales - Number of sales * @property background_color - The background color to be displayed with the item - * @property image_url - URI of an image associated with this collectible - * @property image_preview_url - URI of a smaller image associated with this collectible - * @property image_thumbnail_url - URI of a thumbnail image associated with this collectible - * @property image_original_url - URI of the original image associated with this collectible - * @property animation_url - URI of a animation associated with this collectible - * @property animation_original_url - URI of the original animation associated with this collectible - * @property name - The collectible name - * @property description - The collectible description + * @property image_url - URI of an image associated with this NFT + * @property image_preview_url - URI of a smaller image associated with this NFT + * @property image_thumbnail_url - URI of a thumbnail image associated with this NFT + * @property image_original_url - URI of the original image associated with this NFT + * @property animation_url - URI of a animation associated with this NFT + * @property animation_original_url - URI of the original animation associated with this NFT + * @property name - The NFT name + * @property description - The NFT description * @property external_link - External link containing additional information - * @property assetContract - The collectible contract information object - * @property creator - The collectible owner information object + * @property assetContract - The NFT contract information object + * @property creator - The NFT owner information object * @property lastSale - When this item was last sold */ -export interface ApiCollectible { +export interface ApiNft { token_id: string; num_sales: number | null; background_color: string | null; @@ -45,26 +41,26 @@ export interface ApiCollectible { name: string | null; description: string | null; external_link: string | null; - asset_contract: ApiCollectibleContract; - creator: ApiCollectibleCreator; - last_sale: ApiCollectibleLastSale | null; + asset_contract: ApiNftContract; + creator: ApiNftCreator; + last_sale: ApiNftLastSale | null; } /** - * @type ApiCollectibleContract + * @type ApiNftContract * - * Collectible contract object coming from OpenSea api - * @property address - Address of the collectible contract - * @property asset_contract_type - The collectible type, it could be `semi-fungible` or `non-fungible` + * NFT contract object coming from OpenSea api + * @property address - Address of the NFT contract + * @property asset_contract_type - The NFT type, it could be `semi-fungible` or `non-fungible` * @property created_date - Creation date * @property collection - Object containing the contract name and URI of an image associated * @property schema_name - The schema followed by the contract, it could be `ERC721` or `ERC1155` - * @property symbol - The collectible contract symbol - * @property total_supply - Total supply of collectibles - * @property description - The collectible contract description + * @property symbol - The NFT contract symbol + * @property total_supply - Total supply of NFTs + * @property description - The NFT contract description * @property external_link - External link containing additional information */ -export interface ApiCollectibleContract { +export interface ApiNftContract { address: string; asset_contract_type: string | null; created_date: string | null; @@ -80,43 +76,43 @@ export interface ApiCollectibleContract { } /** - * @type ApiCollectibleLastSale + * @type ApiNftLastSale * - * Collectible sale object coming from OpenSea api + * NFT sale object coming from OpenSea api * @property event_timestamp - Object containing a `username` - * @property total_price - URI of collectible image associated with this owner + * @property total_price - URI of NFT image associated with this owner * @property transaction - Object containing transaction_hash and block_hash */ -export interface ApiCollectibleLastSale { +export interface ApiNftLastSale { event_timestamp: string; total_price: string; transaction: { transaction_hash: string; block_hash: string }; } /** - * @type ApiCollectibleCreator + * @type ApiNftCreator * - * Collectible creator object coming from OpenSea api + * NFT creator object coming from OpenSea api * @property user - Object containing a `username` - * @property profile_img_url - URI of collectible image associated with this owner + * @property profile_img_url - URI of NFT image associated with this owner * @property address - The owner address */ -export interface ApiCollectibleCreator { +export interface ApiNftCreator { user: { username: string }; profile_img_url: string; address: string; } /** - * @type CollectibleDetectionConfig + * @type NftDetectionConfig * - * CollectibleDetection configuration + * NftDetection configuration * @property interval - Polling interval used to fetch new token rates * @property networkType - Network type ID as per net_version * @property selectedAddress - Vault selected address * @property tokens - List of tokens associated with the active vault */ -export interface CollectibleDetectionConfig extends BaseConfig { +export interface NftDetectionConfig extends BaseConfig { interval: number; networkType: NetworkType; chainId: `0x${string}` | `${number}` | number; @@ -124,15 +120,15 @@ export interface CollectibleDetectionConfig extends BaseConfig { } /** - * Controller that passively polls on a set interval for Collectibles auto detection + * Controller that passively polls on a set interval for NFT auto detection */ -export class CollectibleDetectionController extends BaseController< - CollectibleDetectionConfig, +export class NftDetectionController extends BaseController< + NftDetectionConfig, BaseState > { private intervalId?: NodeJS.Timeout; - private getOwnerCollectiblesApi({ + private getOwnerNftApi({ address, offset, useProxy, @@ -146,22 +142,22 @@ export class CollectibleDetectionController extends BaseController< : `${OPENSEA_API_URL}/assets?owner=${address}&offset=${offset}&limit=50`; } - private async getOwnerCollectibles(address: string) { - let collectiblesApiResponse: { assets: ApiCollectible[] }; - let collectibles: ApiCollectible[] = []; + private async getOwnerNfts(address: string) { + let nftApiResponse: { assets: ApiNft[] }; + let nfts: ApiNft[] = []; const openSeaApiKey = this.getOpenSeaApiKey(); let offset = 0; let pagingFinish = false; /* istanbul ignore if */ do { - collectiblesApiResponse = await fetchWithErrorHandling({ - url: this.getOwnerCollectiblesApi({ address, offset, useProxy: true }), + nftApiResponse = await fetchWithErrorHandling({ + url: this.getOwnerNftApi({ address, offset, useProxy: true }), timeout: 15000, }); - if (openSeaApiKey && !collectiblesApiResponse) { - collectiblesApiResponse = await fetchWithErrorHandling({ - url: this.getOwnerCollectiblesApi({ + if (openSeaApiKey && !nftApiResponse) { + nftApiResponse = await fetchWithErrorHandling({ + url: this.getOwnerNftApi({ address, offset, useProxy: false, @@ -173,40 +169,40 @@ export class CollectibleDetectionController extends BaseController< }); } - if (!collectiblesApiResponse) { - return collectibles; + if (!nftApiResponse) { + return nfts; } - collectiblesApiResponse?.assets?.length !== 0 - ? (collectibles = [...collectibles, ...collectiblesApiResponse.assets]) + nftApiResponse?.assets?.length !== 0 + ? (nfts = [...nfts, ...nftApiResponse.assets]) : (pagingFinish = true); offset += 50; } while (!pagingFinish); - return collectibles; + return nfts; } /** * Name of this controller used during composition */ - override name = 'CollectibleDetectionController'; + override name = 'NftDetectionController'; private getOpenSeaApiKey: () => string | undefined; - private addCollectible: CollectiblesController['addCollectible']; + private addNft: NftController['addNft']; - private getCollectiblesState: () => CollectiblesState; + private getNftState: () => NftState; /** - * Creates a CollectibleDetectionController instance. + * Creates an NftDetectionController instance. * * @param options - The controller options. - * @param options.onCollectiblesStateChange - Allows subscribing to assets controller state changes. + * @param options.onNftsStateChange - Allows subscribing to assets controller state changes. * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes. * @param options.onNetworkStateChange - Allows subscribing to network controller state changes. * @param options.getOpenSeaApiKey - Gets the OpenSea API key, if one is set. - * @param options.addCollectible - Add a collectible. - * @param options.getCollectiblesState - Gets the current state of the Assets controller. + * @param options.addNft - Add an NFT. + * @param options.getNftState - Gets the current state of the Assets controller. * @param config - Initial options used to configure this controller. * @param state - Initial state to set on this controller. */ @@ -215,12 +211,10 @@ export class CollectibleDetectionController extends BaseController< onPreferencesStateChange, onNetworkStateChange, getOpenSeaApiKey, - addCollectible, - getCollectiblesState, + addNft, + getNftState, }: { - onCollectiblesStateChange: ( - listener: (collectiblesState: CollectiblesState) => void, - ) => void; + onNftsStateChange: (listener: (nftsState: NftState) => void) => void; onPreferencesStateChange: ( listener: (preferencesState: PreferencesState) => void, ) => void; @@ -228,10 +222,10 @@ export class CollectibleDetectionController extends BaseController< listener: (networkState: NetworkState) => void, ) => void; getOpenSeaApiKey: () => string | undefined; - addCollectible: CollectiblesController['addCollectible']; - getCollectiblesState: () => CollectiblesState; + addNft: NftController['addNft']; + getNftState: () => NftState; }, - config?: Partial, + config?: Partial, state?: Partial, ) { super(config, state); @@ -243,20 +237,20 @@ export class CollectibleDetectionController extends BaseController< disabled: true, }; this.initialize(); - this.getCollectiblesState = getCollectiblesState; - onPreferencesStateChange(({ selectedAddress, useCollectibleDetection }) => { + this.getNftState = getNftState; + onPreferencesStateChange(({ selectedAddress, useNftDetection }) => { const { selectedAddress: previouslySelectedAddress, disabled } = this.config; if ( selectedAddress !== previouslySelectedAddress || - !useCollectibleDetection !== disabled + !useNftDetection !== disabled ) { - this.configure({ selectedAddress, disabled: !useCollectibleDetection }); + this.configure({ selectedAddress, disabled: !useNftDetection }); } - if (useCollectibleDetection !== undefined) { - if (useCollectibleDetection) { + if (useNftDetection !== undefined) { + if (useNftDetection) { this.start(); } else { this.stop(); @@ -267,11 +261,11 @@ export class CollectibleDetectionController extends BaseController< onNetworkStateChange(({ provider }) => { this.configure({ networkType: provider.type, - chainId: provider.chainId as CollectibleDetectionConfig['chainId'], + chainId: provider.chainId as NftDetectionConfig['chainId'], }); }); this.getOpenSeaApiKey = getOpenSeaApiKey; - this.addCollectible = addCollectible; + this.addNft = addNft; } /** @@ -306,9 +300,9 @@ export class CollectibleDetectionController extends BaseController< private async startPolling(interval?: number): Promise { interval && this.configure({ interval }, false, false); this.stopPolling(); - await this.detectCollectibles(); + await this.detectNfts(); this.intervalId = setInterval(async () => { - await this.detectCollectibles(); + await this.detectNfts(); }, this.config.interval); } @@ -320,10 +314,10 @@ export class CollectibleDetectionController extends BaseController< isMainnet = (): boolean => this.config.networkType === MAINNET; /** - * Triggers asset ERC721 token auto detection on mainnet. Any newly detected collectibles are + * Triggers asset ERC721 token auto detection on mainnet. Any newly detected NFTs are * added. */ - async detectCollectibles() { + async detectNfts() { /* istanbul ignore if */ if (!this.isMainnet() || this.disabled) { return; @@ -335,72 +329,70 @@ export class CollectibleDetectionController extends BaseController< return; } - const apiCollectibles = await this.getOwnerCollectibles(selectedAddress); - const addCollectiblesPromises = apiCollectibles.map( - async (collectible: ApiCollectible) => { - const { - token_id, - num_sales, - background_color, - image_url, - image_preview_url, - image_thumbnail_url, - image_original_url, - animation_url, - animation_original_url, - name, - description, - external_link, - creator, - asset_contract: { address, schema_name }, - last_sale, - } = collectible; - - let ignored; - /* istanbul ignore else */ - const { ignoredCollectibles } = this.getCollectiblesState(); - if (ignoredCollectibles.length) { - ignored = ignoredCollectibles.find((c) => { - /* istanbul ignore next */ - return ( - c.address === toChecksumHexAddress(address) && - c.tokenId === token_id - ); - }); - } - - /* istanbul ignore else */ - if (!ignored) { + const apiNfts = await this.getOwnerNfts(selectedAddress); + const addNftPromises = apiNfts.map(async (nft: ApiNft) => { + const { + token_id, + num_sales, + background_color, + image_url, + image_preview_url, + image_thumbnail_url, + image_original_url, + animation_url, + animation_original_url, + name, + description, + external_link, + creator, + asset_contract: { address, schema_name }, + last_sale, + } = nft; + + let ignored; + /* istanbul ignore else */ + const { ignoredNfts } = this.getNftState(); + if (ignoredNfts.length) { + ignored = ignoredNfts.find((c) => { /* istanbul ignore next */ - const collectibleMetadata: CollectibleMetadata = Object.assign( - {}, - { name }, - creator && { creator }, - description && { description }, - image_url && { image: image_url }, - num_sales && { numberOfSales: num_sales }, - background_color && { backgroundColor: background_color }, - image_preview_url && { imagePreview: image_preview_url }, - image_thumbnail_url && { imageThumbnail: image_thumbnail_url }, - image_original_url && { imageOriginal: image_original_url }, - animation_url && { animation: animation_url }, - animation_original_url && { - animationOriginal: animation_original_url, - }, - schema_name && { standard: schema_name }, - external_link && { externalLink: external_link }, - last_sale && { lastSale: last_sale }, + return ( + c.address === toChecksumHexAddress(address) && + c.tokenId === token_id ); + }); + } - await this.addCollectible(address, token_id, collectibleMetadata, { - userAddress: selectedAddress, - chainId: chainId as string, - }); - } - }, - ); - await Promise.all(addCollectiblesPromises); + /* istanbul ignore else */ + if (!ignored) { + /* istanbul ignore next */ + const nftMetadata: NftMetadata = Object.assign( + {}, + { name }, + creator && { creator }, + description && { description }, + image_url && { image: image_url }, + num_sales && { numberOfSales: num_sales }, + background_color && { backgroundColor: background_color }, + image_preview_url && { imagePreview: image_preview_url }, + image_thumbnail_url && { imageThumbnail: image_thumbnail_url }, + image_original_url && { imageOriginal: image_original_url }, + animation_url && { animation: animation_url }, + animation_original_url && { + animationOriginal: animation_original_url, + }, + schema_name && { standard: schema_name }, + external_link && { externalLink: external_link }, + last_sale && { lastSale: last_sale }, + ); + + await this.addNft(address, token_id, nftMetadata, { + userAddress: selectedAddress, + chainId: chainId as string, + }); + } + }); + await Promise.all(addNftPromises); } } -export default CollectibleDetectionController; +export default NftDetectionController; diff --git a/src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.test.ts b/src/assets/Standards/NftStandards/ERC1155/ERC1155Standard.test.ts similarity index 100% rename from src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.test.ts rename to src/assets/Standards/NftStandards/ERC1155/ERC1155Standard.test.ts diff --git a/src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.ts b/src/assets/Standards/NftStandards/ERC1155/ERC1155Standard.ts similarity index 100% rename from src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.ts rename to src/assets/Standards/NftStandards/ERC1155/ERC1155Standard.ts diff --git a/src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.test.ts b/src/assets/Standards/NftStandards/ERC721/ERC721Standard.test.ts similarity index 100% rename from src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.test.ts rename to src/assets/Standards/NftStandards/ERC721/ERC721Standard.test.ts diff --git a/src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.ts b/src/assets/Standards/NftStandards/ERC721/ERC721Standard.ts similarity index 98% rename from src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.ts rename to src/assets/Standards/NftStandards/ERC721/ERC721Standard.ts index ca4f9eebb84..048ea19905d 100644 --- a/src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.ts +++ b/src/assets/Standards/NftStandards/ERC721/ERC721Standard.ts @@ -62,10 +62,10 @@ export class ERC721Standard { * * @param address - ERC721 asset contract address. * @param selectedAddress - Current account public address. - * @param index - A collectible counter less than `balanceOf(selectedAddress)`. + * @param index - An NFT counter less than `balanceOf(selectedAddress)`. * @returns Promise resolving to token identifier for the 'index'th asset assigned to 'selectedAddress'. */ - getCollectibleTokenId = async ( + getNftTokenId = async ( address: string, selectedAddress: string, index: number, diff --git a/src/assets/TokensController.test.ts b/src/assets/TokensController.test.ts index dd5a24366cd..41c11a1a066 100644 --- a/src/assets/TokensController.test.ts +++ b/src/assets/TokensController.test.ts @@ -488,7 +488,7 @@ describe('TokensController', () => { describe('isERC721 flag', function () { describe('updateTokenType method', function () { - it('should add isERC721 = true to token object already in state when token is collectible and in our contract-metadata repo', async function () { + it('should add isERC721 = true to token object already in state when token is NFT and in our contract-metadata repo', async function () { const contractAddresses = Object.keys(contractMaps); const erc721ContractAddresses = contractAddresses.filter( (contractAddress) => contractMaps[contractAddress].erc721 === true, @@ -502,7 +502,7 @@ describe('TokensController', () => { expect(result.isERC721).toBe(true); }); - it('should add isERC721 = false to token object already in state when token is not a collectible and is in our contract-metadata repo', async function () { + it('should add isERC721 = false to token object already in state when token is not an NFT and is in our contract-metadata repo', async function () { const contractAddresses = Object.keys(contractMaps); const erc20ContractAddresses = contractAddresses.filter( (contractAddress) => contractMaps[contractAddress].erc20 === true, @@ -516,7 +516,7 @@ describe('TokensController', () => { expect(result.isERC721).toBe(false); }); - it('should add isERC721 = true to token object already in state when token is collectible and is not in our contract-metadata repo', async function () { + it('should add isERC721 = true to token object already in state when token is NFT and is not in our contract-metadata repo', async function () { const stub = stubCreateEthers(tokensController, true); const tokenAddress = '0xda5584cc586d07c7141aa427224a4bd58e64af7d'; tokensController.update({ @@ -535,7 +535,7 @@ describe('TokensController', () => { stub.restore(); }); - it('should add isERC721 = false to token object already in state when token is not a collectible and not in our contract-metadata repo', async function () { + it('should add isERC721 = false to token object already in state when token is not an NFT and not in our contract-metadata repo', async function () { const stub = stubCreateEthers(tokensController, false); const tokenAddress = '0xda5584cc586d07c7141aa427224a4bd58e64af7d'; tokensController.update({ @@ -556,7 +556,7 @@ describe('TokensController', () => { }); describe('addToken method', function () { - it('should add isERC721 = true when token is a collectible and is in our contract-metadata repo', async function () { + it('should add isERC721 = true when token is an NFT and is in our contract-metadata repo', async function () { const contractAddresses = Object.keys(contractMaps); const erc721ContractAddresses = contractAddresses.filter( (contractAddress) => contractMaps[contractAddress].erc721 === true, @@ -575,7 +575,7 @@ describe('TokensController', () => { ]); }); - it('should add isERC721 = true when the token is a collectible but not in our contract-metadata repo', async function () { + it('should add isERC721 = true when the token is an NFT but not in our contract-metadata repo', async function () { const stub = stubCreateEthers(tokensController, true); const tokenAddress = '0xDA5584Cc586d07c7141aA427224A4Bd58E64aF7D'; @@ -596,7 +596,7 @@ describe('TokensController', () => { stub.restore(); }); - it('should add isERC721 = false to token object already in state when token is not a collectible and in our contract-metadata repo', async function () { + it('should add isERC721 = false to token object already in state when token is not an NFT and in our contract-metadata repo', async function () { const contractAddresses = Object.keys(contractMaps); const erc20ContractAddresses = contractAddresses.filter( (contractAddress) => contractMaps[contractAddress].erc20 === true, @@ -616,7 +616,7 @@ describe('TokensController', () => { ]); }); - it('should add isERC721 = false when the token is not a collectible and not in our contract-metadata repo', async function () { + it('should add isERC721 = false when the token is not an NFT and not in our contract-metadata repo', async function () { const stub = stubCreateEthers(tokensController, false); const tokenAddress = '0xDA5584Cc586d07c7141aA427224A4Bd58E64aF7D'; diff --git a/src/assets/assetsUtil.test.ts b/src/assets/assetsUtil.test.ts index cabb6efacc0..a67925867b6 100644 --- a/src/assets/assetsUtil.test.ts +++ b/src/assets/assetsUtil.test.ts @@ -1,11 +1,11 @@ import { NetworksChainId } from '../network/NetworkController'; import * as assetsUtil from './assetsUtil'; -import { Collectible, CollectibleMetadata } from './CollectiblesController'; +import { Nft, NftMetadata } from './NftController'; describe('assetsUtil', () => { - describe('compareCollectiblesMetadata', () => { + describe('compareNftMetadata', () => { it('should resolve true if any key is different', () => { - const collectibleMetadata: CollectibleMetadata = { + const nftMetadata: NftMetadata = { name: 'name', image: 'image', description: 'description', @@ -18,7 +18,7 @@ describe('assetsUtil', () => { animationOriginal: 'animationOriginal', externalLink: 'externalLink-123', }; - const collectible: Collectible = { + const nft: Nft = { address: 'address', tokenId: '123', name: 'name', @@ -33,22 +33,19 @@ describe('assetsUtil', () => { animationOriginal: 'animationOriginal', externalLink: 'externalLink', }; - const different = assetsUtil.compareCollectiblesMetadata( - collectibleMetadata, - collectible, - ); + const different = assetsUtil.compareNftMetadata(nftMetadata, nft); expect(different).toStrictEqual(true); }); it('should resolve true if any key is different as always as metadata is not undefined', () => { - const collectibleMetadata: CollectibleMetadata = { + const nftMetadata: NftMetadata = { name: 'name', image: 'image', description: 'description', standard: 'standard', externalLink: 'externalLink', }; - const collectible: Collectible = { + const nft: Nft = { address: 'address', tokenId: '123', name: 'name', @@ -58,15 +55,12 @@ describe('assetsUtil', () => { backgroundColor: 'backgroundColor', externalLink: 'externalLink', }; - const different = assetsUtil.compareCollectiblesMetadata( - collectibleMetadata, - collectible, - ); + const different = assetsUtil.compareNftMetadata(nftMetadata, nft); expect(different).toStrictEqual(false); }); it('should resolve false if no key is different', () => { - const collectibleMetadata: CollectibleMetadata = { + const nftMetadata: NftMetadata = { name: 'name', image: 'image', description: 'description', @@ -79,7 +73,7 @@ describe('assetsUtil', () => { animationOriginal: 'animationOriginal', externalLink: 'externalLink', }; - const collectible: Collectible = { + const nft: Nft = { address: 'address', tokenId: '123', name: 'name', @@ -94,10 +88,7 @@ describe('assetsUtil', () => { animationOriginal: 'animationOriginal', externalLink: 'externalLink', }; - const different = assetsUtil.compareCollectiblesMetadata( - collectibleMetadata, - collectible, - ); + const different = assetsUtil.compareNftMetadata(nftMetadata, nft); expect(different).toStrictEqual(false); }); diff --git a/src/assets/assetsUtil.ts b/src/assets/assetsUtil.ts index f12b2c298b2..ee02087f627 100644 --- a/src/assets/assetsUtil.ts +++ b/src/assets/assetsUtil.ts @@ -1,20 +1,17 @@ import { convertHexToDecimal } from '../util'; -import { Collectible, CollectibleMetadata } from './CollectiblesController'; +import { Nft, NftMetadata } from './NftController'; /** - * Compares collectible metadata entries to any collectible entry. - * We need this method when comparing a new fetched collectible metadata, in case a entry changed to a defined value, - * there's a need to update the collectible in state. + * Compares NFT metadata entries to any NFT entry. + * We need this method when comparing a new fetched NFT metadata, in case a entry changed to a defined value, + * there's a need to update the NFT in state. * - * @param newCollectibleMetadata - Collectible metadata object. - * @param collectible - Collectible object to compare with. + * @param newNftMetadata - NFT metadata object. + * @param nft - NFT object to compare with. * @returns Whether there are differences. */ -export function compareCollectiblesMetadata( - newCollectibleMetadata: CollectibleMetadata, - collectible: Collectible, -) { - const keys: (keyof CollectibleMetadata)[] = [ +export function compareNftMetadata(newNftMetadata: NftMetadata, nft: Nft) { + const keys: (keyof NftMetadata)[] = [ 'image', 'backgroundColor', 'imagePreview', @@ -25,10 +22,7 @@ export function compareCollectiblesMetadata( 'externalLink', ]; const differentValues = keys.reduce((value, key) => { - if ( - newCollectibleMetadata[key] && - newCollectibleMetadata[key] !== collectible[key] - ) { + if (newNftMetadata[key] && newNftMetadata[key] !== nft[key]) { return value + 1; } return value; diff --git a/src/constants.ts b/src/constants.ts index 5ce57a214f3..e1a16dd9195 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -32,7 +32,7 @@ export const ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error'; export const ASSET_TYPES = { NATIVE: 'NATIVE', TOKEN: 'TOKEN', - COLLECTIBLE: 'COLLECTIBLE', + NFT: 'NFT', UNKNOWN: 'UNKNOWN', }; diff --git a/src/index.ts b/src/index.ts index 240872deaf1..816ed3b0f67 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,9 +33,9 @@ export * from './announcement/AnnouncementController'; export * from './assets/TokenListController'; export * from './gas/GasFeeController'; export * from './assets/TokensController'; -export * from './assets/CollectiblesController'; +export * from './assets/NftController'; export * from './assets/TokenDetectionController'; -export * from './assets/CollectibleDetectionController'; +export * from './assets/NftDetectionController'; export * from './permissions'; export * from './subject-metadata'; export * from './ratelimit/RateLimitController'; diff --git a/src/user/PreferencesController.test.ts b/src/user/PreferencesController.test.ts index ce48dc5bba5..73dc46efab1 100644 --- a/src/user/PreferencesController.test.ts +++ b/src/user/PreferencesController.test.ts @@ -11,7 +11,7 @@ describe('PreferencesController', () => { lostIdentities: {}, selectedAddress: '', useTokenDetection: true, - useCollectibleDetection: false, + useNftDetection: false, openSeaEnabled: false, }); }); @@ -219,10 +219,10 @@ describe('PreferencesController', () => { expect(controller.state.useTokenDetection).toStrictEqual(true); }); - it('should set useCollectibleDetection', () => { + it('should set useNftDetection', () => { const controller = new PreferencesController(); controller.setOpenSeaEnabled(true); - controller.setUseCollectibleDetection(true); - expect(controller.state.useCollectibleDetection).toStrictEqual(true); + controller.setUseNftDetection(true); + expect(controller.state.useNftDetection).toStrictEqual(true); }); }); diff --git a/src/user/PreferencesController.ts b/src/user/PreferencesController.ts index 374ef128d96..6d13bc6cd10 100644 --- a/src/user/PreferencesController.ts +++ b/src/user/PreferencesController.ts @@ -46,7 +46,7 @@ export interface PreferencesState extends BaseState { lostIdentities: { [address: string]: ContactEntry }; selectedAddress: string; useTokenDetection: boolean; - useCollectibleDetection: boolean; + useNftDetection: boolean; openSeaEnabled: boolean; } @@ -78,7 +78,7 @@ export class PreferencesController extends BaseController< lostIdentities: {}, selectedAddress: '', useTokenDetection: true, - useCollectibleDetection: false, + useNftDetection: false, openSeaEnabled: false, }; this.initialize(); @@ -298,17 +298,17 @@ export class PreferencesController extends BaseController< } /** - * Toggle the collectible detection setting. + * Toggle the NFT detection setting. * - * @param useCollectibleDetection - Boolean indicating user preference on collectible detection. + * @param useNftDetection - Boolean indicating user preference on NFT detection. */ - setUseCollectibleDetection(useCollectibleDetection: boolean) { - if (useCollectibleDetection && !this.state.openSeaEnabled) { + setUseNftDetection(useNftDetection: boolean) { + if (useNftDetection && !this.state.openSeaEnabled) { throw new Error( - 'useCollectibleDetection cannot be enabled if openSeaEnabled is false', + 'useNftDetection cannot be enabled if openSeaEnabled is false', ); } - this.update({ useCollectibleDetection }); + this.update({ useNftDetection }); } /** @@ -319,7 +319,7 @@ export class PreferencesController extends BaseController< setOpenSeaEnabled(openSeaEnabled: boolean) { this.update({ openSeaEnabled }); if (!openSeaEnabled) { - this.update({ useCollectibleDetection: false }); + this.update({ useNftDetection: false }); } } }