Skip to content

feat: Implement display nft media #1893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
698c481
implement nft media
tommasini Aug 21, 2023
05acddb
solve conflicts and merge main
tommasini Aug 21, 2023
7facede
Merge branch 'main' into feat/1166-nft-media
tommasini Aug 21, 2023
d40f9b2
default value of displayNftValue is true
tommasini Aug 23, 2023
8e3cbed
fix preferences controller test
tommasini Aug 23, 2023
96485cf
merge main and update use case on NfTController with preferences, mor…
tommasini Aug 31, 2023
c697745
update tests for when ipfs is disabled and enabled and display nft me…
tommasini Aug 31, 2023
710df7e
add try catch for handle when fetch web2 or opensea via proxy fails a…
tommasini Aug 31, 2023
138a095
disable fetch URI if displayNftMedia is disabled and URI is not on IPFS
tommasini Sep 1, 2023
240b81d
update watch nft tests with the display nft media logic
tommasini Sep 7, 2023
c19d228
Merge branch 'main' into feat/1166-nft-media
tommasini Sep 7, 2023
f574fb1
merge main and solve the conflicts
tommasini Oct 24, 2023
234b303
remove duplicated test
tommasini Oct 24, 2023
0cc2d25
remove duplicated code
tommasini Oct 24, 2023
dd956ad
merge main and address conflicts on nft controller
tommasini Oct 26, 2023
6c6e74d
Merge branch 'main' into feat/1166-display-nft-media
tommasini Nov 13, 2023
b06d633
reduce code duplication and create an enum for the error entries
tommasini Nov 13, 2023
9b9f5ed
Merge branch 'main' into feat/1166-display-nft-media
tommasini Nov 13, 2023
68fe925
merge main and solve conflicts
tommasini Nov 15, 2023
8b66244
Update packages/assets-controllers/src/NftController.ts
tommasini Nov 15, 2023
592a893
update test cases
tommasini Nov 20, 2023
c6eebc1
Update packages/assets-controllers/src/NftController.test.ts
tommasini Jan 22, 2024
e2662fc
Update packages/assets-controllers/src/NftController.test.ts
tommasini Jan 22, 2024
7332e93
Update packages/assets-controllers/src/NftController.test.ts
tommasini Jan 22, 2024
f6d05a9
solve conflicts and unit tests failling, it is missing the global cov…
tommasini Feb 19, 2024
f4be49e
solve conflicts and unit tests failling, it is missing the global cov…
tommasini Feb 19, 2024
849fa92
Revert "solve conflicts and unit tests failling, it is missing the gl…
tommasini Feb 19, 2024
665f48c
Revert "solve conflicts and unit tests failling, it is missing the gl…
tommasini Feb 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 216 additions & 19 deletions packages/assets-controllers/src/NftController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { v4 } from 'uuid';
import { AssetsContractController } from './AssetsContractController';
import { getFormattedIpfsUrl } from './assetsUtil';
import { Source } from './constants';
import { NftController } from './NftController';
import { NftController, NFTFetchMetadataError } from './NftController';

const CRYPTOPUNK_ADDRESS = '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB';
const ERC721_KUDOSADDRESS = '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163';
Expand Down Expand Up @@ -249,7 +249,7 @@ function setupController({

preferences.update({
selectedAddress: OWNER_ADDRESS,
openSeaEnabled: true,
displayNftMedia: true,
});

return {
Expand Down Expand Up @@ -491,7 +491,7 @@ describe('NftController', () => {
expect(callActionSpy).toHaveBeenCalledTimes(0);
});

it('should handle ERC721 type and add pending request to ApprovalController with the OpenSea API disabled and IPFS gateway enabled', async function () {
it('should handle ERC721 type and add pending request to ApprovalController with display nft media disabled and IPFS gateway enabled', async function () {
nock('https://testtokenuri.com')
.get('/')
.reply(
Expand All @@ -509,7 +509,7 @@ describe('NftController', () => {
getERC721OwnerOfStub: jest.fn().mockImplementation(() => OWNER_ADDRESS),
});
preferences.setIsIpfsGatewayEnabled(true);
preferences.setOpenSeaEnabled(false);
preferences.setDisplayNftMedia(false);

const requestId = 'approval-request-id-1';

Expand Down Expand Up @@ -545,7 +545,7 @@ describe('NftController', () => {
clock.restore();
});

it('should handle ERC721 type and add pending request to ApprovalController with the OpenSea API enabled and IPFS gateway enabled', async function () {
it('should handle ERC721 type and add pending request to ApprovalController with the display nft media enabled and IPFS gateway enabled', async function () {
nock('https://testtokenuri.com')
.get('/')
.reply(
Expand All @@ -563,7 +563,7 @@ describe('NftController', () => {
getERC721OwnerOfStub: jest.fn().mockImplementation(() => OWNER_ADDRESS),
});
preferences.setIsIpfsGatewayEnabled(true);
preferences.setOpenSeaEnabled(true);
preferences.setDisplayNftMedia(true);

const requestId = 'approval-request-id-1';

Expand Down Expand Up @@ -599,7 +599,7 @@ describe('NftController', () => {
clock.restore();
});

it('should handle ERC721 type and add pending request to ApprovalController with the OpenSea API disabled and IPFS gateway disabled', async function () {
it('should handle ERC721 type and add pending request to ApprovalController with the display nft media disabled disabled and IPFS gateway disabled', async function () {
nock('https://testtokenuri.com')
.get('/')
.reply(
Expand All @@ -617,7 +617,7 @@ describe('NftController', () => {
getERC721OwnerOfStub: jest.fn().mockImplementation(() => OWNER_ADDRESS),
});
preferences.setIsIpfsGatewayEnabled(false);
preferences.setOpenSeaEnabled(false);
preferences.setDisplayNftMedia(false);

const requestId = 'approval-request-id-1';

Expand Down Expand Up @@ -653,7 +653,7 @@ describe('NftController', () => {
clock.restore();
});

it('should handle ERC721 type and add pending request to ApprovalController with the OpenSea API enabled and IPFS gateway disabled', async function () {
it('should handle ERC721 type and add pending request to ApprovalController with the display nft media enabled and IPFS gateway disabled', async function () {
nock('https://testtokenuri.com')
.get('/')
.reply(
Expand All @@ -671,7 +671,7 @@ describe('NftController', () => {
getERC721OwnerOfStub: jest.fn().mockImplementation(() => OWNER_ADDRESS),
});
preferences.setIsIpfsGatewayEnabled(false);
preferences.setOpenSeaEnabled(true);
preferences.setDisplayNftMedia(true);

const requestId = 'approval-request-id-1';

Expand Down Expand Up @@ -707,7 +707,7 @@ describe('NftController', () => {
clock.restore();
});

it('should handle ERC1155 type and add to suggestedNfts with the OpenSea API disabled', async function () {
it('should handle ERC1155 type and add to suggestedNfts with the display nft media disabled', async function () {
nock('https://testtokenuri.com')
.get('/')
.reply(
Expand All @@ -725,7 +725,7 @@ describe('NftController', () => {
.mockImplementation(() => 'https://testtokenuri.com'),
getERC1155BalanceOfStub: jest.fn().mockImplementation(() => new BN(1)),
});
preferences.setOpenSeaEnabled(false);
preferences.setDisplayNftMedia(false);
preferences.setIsIpfsGatewayEnabled(true);
const requestId = 'approval-request-id-1';

Expand Down Expand Up @@ -764,8 +764,60 @@ describe('NftController', () => {

clock.restore();
});
it('should handle ERC721 type and add pending request to ApprovalController with the display nft media enabled', async function () {
nock('https://testtokenuri.com')
.get('/')
.reply(
200,
JSON.stringify({
image: 'testERC721Image',
name: 'testERC721Name',
description: 'testERC721Description',
}),
);
const { nftController, messenger, preferences } = setupController({
getERC721TokenURIStub: jest
.fn()
.mockImplementation(() => 'https://testtokenuri.com'),
getERC721OwnerOfStub: jest.fn().mockImplementation(() => OWNER_ADDRESS),
});
preferences.setDisplayNftMedia(true);

const requestId = 'approval-request-id-1';

const clock = sinon.useFakeTimers(1);

(v4 as jest.Mock).mockImplementationOnce(() => requestId);

const callActionSpy = jest.spyOn(messenger, 'call').mockResolvedValue({});

await nftController.watchNft(ERC721_NFT, ERC721, 'https://test-dapp.com');
expect(callActionSpy).toHaveBeenCalledTimes(1);
expect(callActionSpy).toHaveBeenCalledWith(
'ApprovalController:addRequest',
{
id: requestId,
origin: 'https://test-dapp.com',
type: ApprovalType.WatchAsset,
requestData: {
id: requestId,
interactingAddress: OWNER_ADDRESS,
asset: {
...ERC721_NFT,
image: 'testERC721Image',
name: 'testERC721Name',
description: 'testERC721Description',
standard: ERC721,
},
},
},
true,
);

it('should handle ERC1155 type and add to suggestedNfts with the OpenSea API enabled', async function () {
clock.restore();
});

it('should handle ERC1155 type and add to suggestedNfts with display nft media enabled', async function () {
nock('https://testtokenuri.com')
.get('/')
.reply(
Expand All @@ -783,7 +835,7 @@ describe('NftController', () => {
.mockImplementation(() => 'https://testtokenuri.com'),
getERC1155BalanceOfStub: jest.fn().mockImplementation(() => new BN(1)),
});
preferences.setOpenSeaEnabled(true);
preferences.setDisplayNftMedia(true);
preferences.setIsIpfsGatewayEnabled(true);
const requestId = 'approval-request-id-1';

Expand All @@ -810,9 +862,9 @@ describe('NftController', () => {
interactingAddress: OWNER_ADDRESS,
asset: {
...ERC1155_NFT,
description: 'testERC1155Description',
image: 'testERC1155Image',
name: 'testERC1155Name',
description: 'testERC1155Description',
standard: ERC1155,
},
},
Expand Down Expand Up @@ -1010,6 +1062,74 @@ describe('NftController', () => {
clock.restore();
});

it('should handle ERC1155 type and add to suggestedNfts with the display nft media enabled', async function () {
const ALT_ERC1155_NFT = {
address: '0x0000000000000000000000000000000000000000',
tokenId: '99999',
};

nock(OPENSEA_PROXY_URL)
.get(`/asset_contract/${ALT_ERC1155_NFT.address}`)
.reply(200, {
description: 'description (from opensea)',
symbol: 'KDO',
total_supply: 10,
collection: {
name: 'name (from opensea)',
image_url: 'logo (from opensea)',
},
})
.get(`/asset/${ALT_ERC1155_NFT.address}/${ALT_ERC1155_NFT.tokenId}`)
.reply(200, {
image_url: 'image (directly from opensea)',
name: 'name (directly from opensea)',
description: 'description (directly from opensea)',
asset_contract: {
schema_name: 'ERC1155',
},
});
const { nftController, messenger, preferences } = setupController({
getERC1155BalanceOfStub: jest.fn().mockImplementation(() => new BN(1)),
});
preferences.setDisplayNftMedia(true);
const requestId = 'approval-request-id-1';

const clock = sinon.useFakeTimers(1);

(v4 as jest.Mock).mockImplementationOnce(() => requestId);

const callActionSpy = jest.spyOn(messenger, 'call').mockResolvedValue({});

await nftController.watchNft(
ALT_ERC1155_NFT,
ERC1155,
'https://test-dapp.com',
);
expect(callActionSpy).toHaveBeenCalledTimes(1);
expect(callActionSpy).toHaveBeenCalledWith(
'ApprovalController:addRequest',
{
id: requestId,
origin: 'https://test-dapp.com',
type: ApprovalType.WatchAsset,
requestData: {
id: requestId,
interactingAddress: OWNER_ADDRESS,
asset: {
...ALT_ERC1155_NFT,
description: 'description (directly from opensea)',
image: 'image (directly from opensea)',
name: 'name (directly from opensea)',
standard: ERC1155,
},
},
},
true,
);

clock.restore();
});

it('should throw an error when calls to `ownerOf` and `balanceOf` revert', async function () {
const { nftController, changeNetwork } = setupController();
// getERC721OwnerOf not mocked
Expand Down Expand Up @@ -1960,7 +2080,7 @@ describe('NftController', () => {
expect(nftController.state.ignoredNfts).toHaveLength(1);
});

it('should add NFT with metadata hosted in IPFS', async () => {
it('should add NFT with metadata hosted in IPFS and not on opensea', async () => {
const { assetsContract, nftController } = setupController();
nock('https://mainnet.infura.io:443', { encodedQueryParams: true })
.post('/v3/ad3a368836ff4596becc3be8e2f137ac', {
Expand Down Expand Up @@ -2069,6 +2189,7 @@ describe('NftController', () => {
isCurrentlyOwned: true,
tokenURI:
'https://bafybeidf7aw7bmnmewwj4ayq3she2jfk5jrdpp24aaucf6fddzb3cfhrvm.ipfs.cloudflare-ipfs.com',
error: NFTFetchMetadataError.OPEN_SEA,
});
});

Expand Down Expand Up @@ -2322,7 +2443,7 @@ describe('NftController', () => {
]);
});

it('should add an NFT with the correct chainId/userAddress and metadata when passed a userAddress', async () => {
it('should add an NFT with the correct chainId/userAddress and metadata when passed a userAddress from on chain but not opensea', async () => {
const userAddress = '0x123ABC';
nock('https://testtokenuri-1.com')
.get('/')
Expand Down Expand Up @@ -2408,6 +2529,7 @@ describe('NftController', () => {
standard: ERC721,
tokenURI: 'https://testtokenuri-1.com',
isCurrentlyOwned: true,
error: NFTFetchMetadataError.OPEN_SEA,
},
]);

Expand Down Expand Up @@ -2985,12 +3107,12 @@ 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 () => {
it('should add NFT with null metadata if the ipfs gateway is disabled and display NFT is disabled', async () => {
const { assetsContract, nftController, preferences } = setupController();

preferences.update({
isIpfsGatewayEnabled: false,
openSeaEnabled: false,
displayNftMedia: false,
});

sinon
Expand All @@ -3016,6 +3138,81 @@ describe('NftController', () => {
tokenURI: 'ipfs://*',
});
});
it('should add NFT with metadata if the ipfs gateway is enabled and display NFT is disabled', async () => {
const { assetsContract, nftController, preferences } = setupController();

preferences.update({
isIpfsGatewayEnabled: true,
displayNftMedia: false,
});

sinon
.stub(nftController, 'getNftURIAndStandard' as any)
.returns(['ipfs://*', ERC1155]);
sinon
.stub(nftController, 'getNftInformationFromTokenURI' as any)
.returns({
name: 'name',
description: 'description',
image: 'image.uri',
standard: ERC1155,
favorite: false,
isCurrentlyOwned: true,
tokenURI: 'ipfs://*',
});

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: 'name',
description: 'description',
image: 'image.uri',
tokenId: ERC1155_NFT_ID,
standard: ERC1155,
favorite: false,
isCurrentlyOwned: true,
tokenURI: 'ipfs://*',
});
});
it('should add NFT with metadata if the ipfs gateway is disabled and display NFT is enabled', async () => {
const { assetsContract, nftController, preferences } = setupController();

preferences.update({
isIpfsGatewayEnabled: false,
displayNftMedia: true,
});

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: 'name',
description: 'description',
image: null,
tokenId: ERC1155_NFT_ID,
standard: ERC1155,
favorite: false,
isCurrentlyOwned: true,
tokenURI: 'ipfs://*',
numberOfSales: 1,
imageOriginal: 'image.uri',
});
});
});

describe('updateNftFavoriteStatus', () => {
Expand Down
Loading