diff --git a/packages/assets-controllers/src/NftController.test.ts b/packages/assets-controllers/src/NftController.test.ts index a74ae8952bb..aed90fc274b 100644 --- a/packages/assets-controllers/src/NftController.test.ts +++ b/packages/assets-controllers/src/NftController.test.ts @@ -982,6 +982,7 @@ describe('NftController', () => { tokenId: '1', favorite: false, isCurrentlyOwned: true, + tokenURI: '', }); }); @@ -1110,6 +1111,8 @@ describe('NftController', () => { standard: 'ERC721', favorite: false, isCurrentlyOwned: true, + tokenURI: + 'https://ipfs.gitcoin.co:443/api/v0/cat/QmPmt6EAaioN78ECnW5oCL8v2YvVSpoBjLCjrXhhsAvoov', }); expect( @@ -1234,6 +1237,8 @@ describe('NftController', () => { isCurrentlyOwned: true, imageOriginal: 'image.uri', numberOfSales: 1, + tokenURI: + 'https://api.opensea.io/api/v1/metadata/0x495f947276749Ce646f68AC8c248420045cb7b5e/0x5a3ca5cd63807ce5e4d7841ab32ce6b6d9bbba2d000000000000010000000001', }); }); @@ -1344,6 +1349,8 @@ describe('NftController', () => { standard: 'ERC721', favorite: false, isCurrentlyOwned: true, + tokenURI: + 'https://ipfs.gitcoin.co:443/api/v0/cat/QmPmt6EAaioN78ECnW5oCL8v2YvVSpoBjLCjrXhhsAvoov', }); expect( @@ -1577,6 +1584,7 @@ describe('NftController', () => { tokenId: ERC721_KUDOS_TOKEN_ID, favorite: false, isCurrentlyOwned: true, + tokenURI: '', }, ]); @@ -1804,6 +1812,8 @@ describe('NftController', () => { standard: 'ERC721', favorite: false, isCurrentlyOwned: true, + tokenURI: + 'https://bafybeidf7aw7bmnmewwj4ayq3she2jfk5jrdpp24aaucf6fddzb3cfhrvm.ipfs.cloudflare-ipfs.com', }); }); @@ -1941,6 +1951,7 @@ describe('NftController', () => { standard: null, favorite: false, isCurrentlyOwned: true, + tokenURI: '', }); }); }); @@ -2303,6 +2314,38 @@ describe('NftController', () => { }; await expect(result).rejects.toThrow(error); }); + + it('should add NFT with null metadata if the ipfs gateway is disabled and opensea is disabled', async () => { + const { assetsContract, nftController, preferences } = setupController(); + + preferences.update({ + isIpfsGatewayEnabled: false, + openSeaEnabled: false, + }); + + sinon + .stub(nftController, 'getNftURIAndStandard' as any) + .returns(['ipfs://*', ERC1155]); + + assetsContract.configure({ provider: MAINNET_PROVIDER }); + const { selectedAddress, chainId } = nftController.config; + + await nftController.addNft(ERC1155_NFT_ADDRESS, ERC1155_NFT_ID); + + expect( + nftController.state.allNfts[selectedAddress][chainId][0], + ).toStrictEqual({ + address: ERC1155_NFT_ADDRESS, + name: null, + description: null, + image: null, + tokenId: ERC1155_NFT_ID, + standard: ERC1155, + favorite: false, + isCurrentlyOwned: true, + tokenURI: 'ipfs://*', + }); + }); }); describe('updateNftFavoriteStatus', () => { diff --git a/packages/assets-controllers/src/NftController.ts b/packages/assets-controllers/src/NftController.ts index b66b8adc490..ac4f1309259 100644 --- a/packages/assets-controllers/src/NftController.ts +++ b/packages/assets-controllers/src/NftController.ts @@ -139,6 +139,7 @@ export interface NftMetadata { creator?: ApiNftCreator; lastSale?: ApiNftLastSale; transactionId?: string; + tokenURI?: string | null; } interface AccountParams { @@ -158,6 +159,7 @@ export interface NftConfig extends BaseConfig { ipfsGateway: string; openSeaEnabled: boolean; useIPFSSubdomains: boolean; + isIpfsGatewayEnabled: boolean; } /** @@ -352,12 +354,25 @@ export class NftController extends BaseController { contractAddress: string, tokenId: string, ): Promise { - const { ipfsGateway, useIPFSSubdomains } = this.config; + const { ipfsGateway, useIPFSSubdomains, isIpfsGatewayEnabled } = + this.config; const result = await this.getNftURIAndStandard(contractAddress, tokenId); let tokenURI = result[0]; const standard = result[1]; - if (tokenURI.startsWith('ipfs://')) { + const hasIpfsTokenURI = tokenURI.startsWith('ipfs://'); + + if (hasIpfsTokenURI && !isIpfsGatewayEnabled) { + return { + image: null, + name: null, + description: null, + standard: standard || null, + favorite: false, + tokenURI: tokenURI ?? null, + }; + } + if (hasIpfsTokenURI) { tokenURI = getFormattedIpfsUrl(ipfsGateway, tokenURI, useIPFSSubdomains); } @@ -374,6 +389,7 @@ export class NftController extends BaseController { description: object.description, standard, favorite: false, + tokenURI: tokenURI ?? null, }; } catch { return { @@ -382,6 +398,7 @@ export class NftController extends BaseController { description: null, standard: standard || null, favorite: false, + tokenURI: tokenURI ?? null, }; } } @@ -459,6 +476,7 @@ export class NftController extends BaseController { image: blockchainMetadata.image ?? openSeaMetadata?.image ?? null, standard: blockchainMetadata.standard ?? openSeaMetadata?.standard ?? null, + tokenURI: blockchainMetadata.tokenURI ?? null, }; } @@ -955,6 +973,7 @@ export class NftController extends BaseController { ipfsGateway: IPFS_DEFAULT_GATEWAY_URL, openSeaEnabled: false, useIPFSSubdomains: true, + isIpfsGatewayEnabled: true, }; this.defaultState = { @@ -973,8 +992,18 @@ export class NftController extends BaseController { this.messagingSystem = messenger; onPreferencesStateChange( - ({ selectedAddress, ipfsGateway, openSeaEnabled }) => { - this.configure({ selectedAddress, ipfsGateway, openSeaEnabled }); + ({ + selectedAddress, + ipfsGateway, + openSeaEnabled, + isIpfsGatewayEnabled, + }) => { + this.configure({ + selectedAddress, + ipfsGateway, + openSeaEnabled, + isIpfsGatewayEnabled, + }); }, ); diff --git a/packages/preferences-controller/src/PreferencesController.test.ts b/packages/preferences-controller/src/PreferencesController.test.ts index be839996d85..20c8e2eafaf 100644 --- a/packages/preferences-controller/src/PreferencesController.test.ts +++ b/packages/preferences-controller/src/PreferencesController.test.ts @@ -18,6 +18,7 @@ describe('PreferencesController', () => { }, isMultiAccountBalancesEnabled: true, showTestNetworks: false, + isIpfsGatewayEnabled: true, }); }); @@ -200,4 +201,10 @@ describe('PreferencesController', () => { controller.setShowTestNetworks(true); expect(controller.state.showTestNetworks).toBe(true); }); + + it('should set isIpfsGatewayEnabled', () => { + const controller = new PreferencesController(); + controller.setIsIpfsGatewayEnabled(true); + expect(controller.state.isIpfsGatewayEnabled).toBe(true); + }); }); diff --git a/packages/preferences-controller/src/PreferencesController.ts b/packages/preferences-controller/src/PreferencesController.ts index 95d22f90b0c..7e3b25e9c9f 100644 --- a/packages/preferences-controller/src/PreferencesController.ts +++ b/packages/preferences-controller/src/PreferencesController.ts @@ -39,6 +39,7 @@ export interface PreferencesState extends BaseState { [methodName: string]: boolean; }; showTestNetworks: boolean; + isIpfsGatewayEnabled: boolean; } /** @@ -76,6 +77,7 @@ export class PreferencesController extends BaseController< eth_sign: false, }, showTestNetworks: false, + isIpfsGatewayEnabled: true, }; this.initialize(); } @@ -310,6 +312,15 @@ export class PreferencesController extends BaseController< setShowTestNetworks(showTestNetworks: boolean) { this.update({ showTestNetworks }); } + + /** + * A setter for the user allow to be fetched IPFS content + * + * @param isIpfsGatewayEnabled - true to enable ipfs source + */ + setIsIpfsGatewayEnabled(isIpfsGatewayEnabled: boolean) { + this.update({ isIpfsGatewayEnabled }); + } } export default PreferencesController;