diff --git a/packages/assets-controllers/src/NftController.test.ts b/packages/assets-controllers/src/NftController.test.ts index 940f10f86b..4342f80143 100644 --- a/packages/assets-controllers/src/NftController.test.ts +++ b/packages/assets-controllers/src/NftController.test.ts @@ -3408,6 +3408,7 @@ describe('NftController', () => { nftMetadata: { name: '', description: '', image: '', standard: '' }, networkClientId: testNetworkClientId, }); + sinon .stub(nftController, 'getNftInformation' as keyof typeof nftController) .returns({ @@ -3464,6 +3465,7 @@ describe('NftController', () => { }, networkClientId: testNetworkClientId, }); + sinon .stub(nftController, 'getNftInformation' as keyof typeof nftController) .rejects(new Error('Error')); @@ -3500,7 +3502,7 @@ describe('NftController', () => { }); }); - it('should update metadata when some calls to fetch metadata succeed', async () => { + it('should update metadata when some calls to fetch metadata succeed', async () => { const { nftController } = setupController(); const { selectedAddress } = nftController.config; const spy = jest.spyOn(nftController, 'updateNft'); @@ -3515,6 +3517,7 @@ describe('NftController', () => { }, networkClientId: testNetworkClientId, }); + await nftController.addNft('0xtest2', '2', { nftMetadata: { name: '', @@ -3524,6 +3527,7 @@ describe('NftController', () => { }, networkClientId: testNetworkClientId, }); + await nftController.addNft('0xtest3', '3', { nftMetadata: { name: '', @@ -3533,6 +3537,7 @@ describe('NftController', () => { }, networkClientId: testNetworkClientId, }); + sinon .stub(nftController, 'getNftInformation' as keyof typeof nftController) .onFirstCall() @@ -3624,5 +3629,120 @@ describe('NftController', () => { }, ]); }); + + it('should not update metadata when nfts has image/name/description already', async () => { + const { nftController, triggerPreferencesStateChange } = + setupController(); + const spy = jest.spyOn(nftController, 'updateNftMetadata'); + const testNetworkClientId = 'sepolia'; + + // Add nfts + await nftController.addNft('0xtest', '3', { + nftMetadata: { + name: 'test name', + description: 'test description', + image: 'test image', + standard: 'ERC721', + }, + userAddress: OWNER_ADDRESS, + networkClientId: testNetworkClientId, + }); + + // trigger preference change + triggerPreferencesStateChange({ + ...getDefaultPreferencesState(), + isIpfsGatewayEnabled: false, + openSeaEnabled: true, + selectedAddress: OWNER_ADDRESS, + }); + + expect(spy).toHaveBeenCalledTimes(0); + }); + + it('should trigger calling updateNftMetadata when preferences change - openseaEnabled', async () => { + const { nftController, triggerPreferencesStateChange, changeNetwork } = + setupController(); + changeNetwork(SEPOLIA); + const spy = jest.spyOn(nftController, 'updateNftMetadata'); + + const testNetworkClientId = 'sepolia'; + // Add nfts + await nftController.addNft('0xtest', '1', { + nftMetadata: { + name: '', + description: '', + image: '', + standard: 'ERC721', + }, + userAddress: OWNER_ADDRESS, + networkClientId: testNetworkClientId, + }); + + expect( + nftController.state.allNfts[OWNER_ADDRESS][SEPOLIA.chainId][0] + .isCurrentlyOwned, + ).toBe(true); + + sinon + .stub(nftController, 'getNftInformation' as keyof typeof nftController) + .returns({ + name: 'name pudgy', + image: 'url pudgy', + description: 'description pudgy', + }); + + // trigger preference change + triggerPreferencesStateChange({ + ...getDefaultPreferencesState(), + isIpfsGatewayEnabled: false, + openSeaEnabled: true, + selectedAddress: OWNER_ADDRESS, + }); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should trigger calling updateNftMetadata when preferences change - ipfs enabled', async () => { + const { nftController, triggerPreferencesStateChange, changeNetwork } = + setupController(); + changeNetwork(SEPOLIA); + const spy = jest.spyOn(nftController, 'updateNftMetadata'); + + const testNetworkClientId = 'sepolia'; + // Add nfts + await nftController.addNft('0xtest', '1', { + nftMetadata: { + name: '', + description: '', + image: '', + standard: 'ERC721', + }, + userAddress: OWNER_ADDRESS, + networkClientId: testNetworkClientId, + }); + + expect( + nftController.state.allNfts[OWNER_ADDRESS][SEPOLIA.chainId][0] + .isCurrentlyOwned, + ).toBe(true); + + sinon + .stub(nftController, 'getNftInformation' as keyof typeof nftController) + .returns({ + name: 'name pudgy', + image: 'url pudgy', + description: 'description pudgy', + }); + + // trigger preference change + triggerPreferencesStateChange({ + ...getDefaultPreferencesState(), + isIpfsGatewayEnabled: true, + openSeaEnabled: false, + selectedAddress: OWNER_ADDRESS, + }); + + expect(spy).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/packages/assets-controllers/src/NftController.ts b/packages/assets-controllers/src/NftController.ts index 2d27107273..9061b8f180 100644 --- a/packages/assets-controllers/src/NftController.ts +++ b/packages/assets-controllers/src/NftController.ts @@ -279,7 +279,6 @@ export class NftController extends BaseControllerV1 { ...oldState, ...{ [userAddress]: newAddressState }, }; - this.update({ [baseStateKey]: newState, }); @@ -1041,7 +1040,7 @@ export class NftController extends BaseControllerV1 { this.messagingSystem = messenger; onPreferencesStateChange( - ({ + async ({ selectedAddress, ipfsGateway, openSeaEnabled, @@ -1053,6 +1052,26 @@ export class NftController extends BaseControllerV1 { openSeaEnabled, isIpfsGatewayEnabled, }); + + const needsUpdateNftMetadata = + (isIpfsGatewayEnabled && ipfsGateway !== '') || openSeaEnabled; + + if (needsUpdateNftMetadata) { + const { chainId } = this.config; + const nfts: Nft[] = + this.state.allNfts[selectedAddress]?.[chainId] ?? []; + // filter only nfts + const nftsToUpdate = nfts.filter( + (singleNft) => + !singleNft.name && !singleNft.description && !singleNft.image, + ); + if (nftsToUpdate.length !== 0) { + await this.updateNftMetadata({ + nfts: nftsToUpdate, + userAddress: selectedAddress, + }); + } + } }, ); @@ -1363,20 +1382,21 @@ export class NftController extends BaseControllerV1 { * Refetches NFT metadata and updates the state * * @param options - Options for refetching NFT metadata - * @param options.nfts - Array of nfts - * @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request. + * @param options.nfts - nfts to update metadata for. * @param options.userAddress - The current user address + * @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request. */ async updateNftMetadata({ nfts, - networkClientId, userAddress = this.config.selectedAddress, + networkClientId, }: { nfts: Nft[]; - networkClientId?: NetworkClientId; userAddress?: string; + networkClientId?: NetworkClientId; }) { const chainId = this.getCorrectChainId({ networkClientId }); + const nftsWithChecksumAdr = nfts.map((nft) => { return { ...nft, @@ -1696,7 +1716,6 @@ export class NftController extends BaseControllerV1 { updatedNft, ...nfts.slice(nftInfo.index + 1), ]; - this.updateNestedNftState(newNfts, ALL_NFTS_STATE_KEY, { chainId, userAddress: selectedAddress,