Skip to content

Commit

Permalink
OpenSea V2 -> V1 compatibility (#3654)
Browse files Browse the repository at this point in the history
## Explanation

Moves to using OpenSea V2 API, but modifies the responses to keep
compatibility with the existing controller state schema.

## References


## Changelog


### `@metamask/controller-utils`

- **BREAKING**: `OPENSEA_PROXY_URL` now points to OpenSea's v2 API.
`OPENSEA_API_URL` + `OPENSEA_TEST_API_URL` have been removed.

### `@metamask/assets-controllers`

- **BREAKING**:
- `NftDetectionController` constructor now requires the
`NftController.getNftApi` function.
- NFT controllers will no longer return `last_sale` information for NFTs
fetched after the OpenSea V2 update

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate
  • Loading branch information
bergeron authored Dec 15, 2023
1 parent 5080a4e commit 3ed517b
Show file tree
Hide file tree
Showing 6 changed files with 622 additions and 303 deletions.
225 changes: 121 additions & 104 deletions packages/assets-controllers/src/NftController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
OPENSEA_PROXY_URL,
IPFS_DEFAULT_GATEWAY_URL,
ERC1155,
OPENSEA_API_URL,
ERC721,
ChainId,
NetworkType,
Expand Down Expand Up @@ -266,52 +265,62 @@ function setupController({
describe('NftController', () => {
beforeEach(() => {
nock(OPENSEA_PROXY_URL)
.get(`/asset_contract/0x01`)
.get(`/chain/ethereum/contract/0x01`)
.reply(200, {
description: 'Description',
symbol: 'FOO',
total_supply: 0,
collection: {
name: 'Name',
image_url: 'url',
},
address: '0x01',
chain: 'ethereum',
collection: 'FOO',
contract_standard: 'erc721',
name: 'Name',
supply: 0,
})
.get(`/asset_contract/0x02`)
.get(`/collections/FOO`)
.reply(200, {
description: 'Description',
image_url: 'url',
name: 'Name',
symbol: 'FOU',
total_supply: 10,
collection: {
name: 'Name',
image_url: 'url',
},
})
.get(`/asset/0x01/1`)
.get(`/chain/ethereum/contract/0x02`)
.reply(200, {
address: '0x02',
chain: 'ethereum',
collection: 'FOU',
contract_standard: 'erc721',
name: 'FOU',
supply: 0,
})
.get(`/collections/FOO`)
.reply(200, {
description: 'Description',
image_original_url: 'url',
image_url: 'url',
name: 'Name',
asset_contract: {
schema_name: 'ERC1155',
})
.get(`/chain/ethereum/contract/0x01/nfts/1`)
.reply(200, {
nft: {
token_standard: 'erc1155',
name: 'Name',
description: 'Description',
image_url: 'url',
},
})
.get(`/asset/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab/798958393`)
.get(
`/chain/ethereum/contract/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab/nfts/798958393`,
)
.replyWithError(new TypeError('Failed to fetch'))
.get(`/asset_contract/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab`)
.get(
`/chain/ethereum/contract/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab`,
)
.replyWithError(new TypeError('Failed to fetch'));

nock(OPENSEA_PROXY_URL)
.get(`/asset/${ERC1155_NFT_ADDRESS}/${ERC1155_NFT_ID}`)
.get(
`/chain/ethereum/contract/${ERC1155_NFT_ADDRESS}/nfts/${ERC1155_NFT_ID}`,
)
.reply(200, {
num_sales: 1,
image_original_url: 'image.uri',
name: 'name',
image: 'image',
description: 'description',
asset_contract: { schema_name: 'ERC1155' },
nft: {
token_standard: 'erc1155',
name: 'name',
description: 'description',
},
});

nock(DEPRESSIONIST_CLOUDFLARE_IPFS_SUBDOMAIN_PATH).get('/').reply(200, {
Expand Down Expand Up @@ -1065,8 +1074,8 @@ describe('NftController', () => {
description: 'Description',
logo: 'url',
name: 'Name',
symbol: 'FOO',
totalSupply: 0,
totalSupply: '0',
schemaName: 'ERC721',
});
});

Expand All @@ -1090,7 +1099,6 @@ describe('NftController', () => {
tokenId: '1',
address: '0x01',
standard: 'ERC1155',
symbol: 'FOO',
});
});

Expand All @@ -1117,7 +1125,6 @@ describe('NftController', () => {
tokenId: '2',
address: '0x01',
standard: 'ERC721',
symbol: 'FOO',
});
});

Expand Down Expand Up @@ -1243,38 +1250,45 @@ describe('NftController', () => {
).toStrictEqual({
address: '0x01',
description: 'Description',
imageOriginal: 'url',
image: 'url',
name: 'Name',
standard: 'ERC1155',
tokenId: '1',
favorite: false,
isCurrentlyOwned: true,
tokenURI: '',
creator: {
address: undefined,
profile_img_url: '',
user: {
username: '',
},
},
});
});

it('should add NFT erc721 and aggregate NFT data from both contract and OpenSea', async () => {
const { assetsContract, nftController } = setupController();
nock(OPENSEA_PROXY_URL)
.get(`/asset/${ERC721_KUDOSADDRESS}/${ERC721_KUDOS_TOKEN_ID}`)
.get(
`/chain/ethereum/contract/${ERC721_KUDOSADDRESS}/nfts/${ERC721_KUDOS_TOKEN_ID}`,
)
.reply(200, {
image_original_url: 'Kudos image (from proxy API)',
name: 'Kudos Name',
description: 'Kudos Description',
asset_contract: {
schema_name: 'ERC721',
nft: {
token_standard: 'erc721',
name: 'Kudos Name',
description: 'Kudos Description',
image_url: 'url',
},
})
.get(`/asset_contract/${ERC721_KUDOSADDRESS}`)
.get(`/chain/ethereum/contract/${ERC721_KUDOSADDRESS}`)
.reply(200, {
description: 'Kudos Description',
symbol: 'KDO',
total_supply: 10,
collection: {
name: 'Kudos',
image_url: 'Kudos logo (from proxy API)',
},
address: ERC721_KUDOSADDRESS,
chain: 'ethereum',
collection: 'Kudos',
contract_standard: 'erc721',
name: 'Name',
supply: 10,
});

nock('https://ipfs.gitcoin.co:443')
Expand Down Expand Up @@ -1377,12 +1391,18 @@ describe('NftController', () => {
name: 'Kudos Name (directly from tokenURI)',
description: 'Kudos Description (directly from tokenURI)',
tokenId: ERC721_KUDOS_TOKEN_ID,
imageOriginal: 'Kudos image (from proxy API)',
standard: 'ERC721',
favorite: false,
isCurrentlyOwned: true,
tokenURI:
'https://ipfs.gitcoin.co:443/api/v0/cat/QmPmt6EAaioN78ECnW5oCL8v2YvVSpoBjLCjrXhhsAvoov',
creator: {
address: undefined,
profile_img_url: '',
user: {
username: '',
},
},
});

expect(
Expand Down Expand Up @@ -1471,7 +1491,7 @@ describe('NftController', () => {
});

nock(OPENSEA_PROXY_URL)
.get(`/asset_contract/${ERC1155_NFT_ADDRESS}`)
.get(`/chain/ethereum/contract/${ERC1155_NFT_ADDRESS}`)
.replyWithError(new TypeError('Failed to fetch'));

// the tokenURI for ERC1155_NFT_ADDRESS + ERC1155_NFT_ID
Expand Down Expand Up @@ -1505,10 +1525,15 @@ describe('NftController', () => {
standard: ERC1155,
favorite: false,
isCurrentlyOwned: true,
imageOriginal: 'image.uri',
numberOfSales: 1,
tokenURI:
'https://api.opensea.io/api/v1/metadata/0x495f947276749Ce646f68AC8c248420045cb7b5e/0x5a3ca5cd63807ce5e4d7841ab32ce6b6d9bbba2d000000000000010000000001',
creator: {
address: undefined,
profile_img_url: '',
user: {
username: '',
},
},
});
});

Expand Down Expand Up @@ -1802,24 +1827,30 @@ describe('NftController', () => {
includeOnNftAdded: true,
});
nock(OPENSEA_PROXY_URL)
.get(`/asset/${ERC721_KUDOSADDRESS}/${ERC721_KUDOS_TOKEN_ID}`)
.get(
`/chain/ethereum/contract/${ERC721_KUDOSADDRESS}/nfts/${ERC721_KUDOS_TOKEN_ID}`,
)
.reply(200, {
image_original_url: 'Kudos image (from proxy API)',
name: 'Kudos Name',
description: 'Kudos Description',
asset_contract: {
schema_name: 'ERC721',
nft: {
token_standard: 'erc721',
name: 'Kudos Name',
description: 'Kudos Description',
image_url: 'Kudos image (from proxy API)',
},
})
.get(`/asset_contract/${ERC721_KUDOSADDRESS}`)
.get(`/chain/ethereum/contract/${ERC721_KUDOSADDRESS}`)
.reply(200, {
address: ERC721_KUDOSADDRESS,
chain: 'ethereum',
collection: 'KDO',
contract_standard: 'erc721',
name: 'Kudos',
supply: 10,
})
.get(`/collections/KDO`)
.reply(200, {
description: 'Kudos Description',
symbol: 'KDO',
total_supply: 10,
collection: {
name: 'Kudos',
image_url: 'Kudos logo (from proxy API)',
},
image_url: 'Kudos logo (from proxy API)',
});

const { selectedAddress, chainId } = nftController.config;
Expand Down Expand Up @@ -1851,14 +1882,20 @@ describe('NftController', () => {
{
address: ERC721_KUDOSADDRESS,
description: 'Kudos Description',
imageOriginal: 'Kudos image (from proxy API)',
image: 'Kudos image (from proxy API)',
name: 'Kudos Name',
image: null,
standard: 'ERC721',
tokenId: ERC721_KUDOS_TOKEN_ID,
favorite: false,
isCurrentlyOwned: true,
tokenURI: '',
creator: {
address: undefined,
profile_img_url: '',
user: {
username: '',
},
},
},
]);

Expand All @@ -1870,16 +1907,15 @@ describe('NftController', () => {
description: 'Kudos Description',
logo: 'Kudos logo (from proxy API)',
name: 'Kudos',
symbol: 'KDO',
totalSupply: 10,
totalSupply: '10',
schemaName: 'ERC721',
},
]);

expect(onNftAddedSpy).toHaveBeenCalledWith({
address: ERC721_KUDOSADDRESS,
tokenId: ERC721_KUDOS_TOKEN_ID,
standard: 'ERC721',
symbol: 'KDO',
source: Source.Detected,
});
});
Expand All @@ -1889,16 +1925,18 @@ describe('NftController', () => {
includeOnNftAdded: true,
});
nock(OPENSEA_PROXY_URL)
.get(`/asset/${ERC721_KUDOSADDRESS}/${ERC721_KUDOS_TOKEN_ID}`)
.get(
`/chain/ethereum/contract/${ERC721_KUDOSADDRESS}/nfts/${ERC721_KUDOS_TOKEN_ID}`,
)
.reply(200, {
image_original_url: 'Kudos image (from proxy API)',
name: 'Kudos Name',
description: 'Kudos Description',
asset_contract: {
schema_name: 'ERC721',
nft: {
token_standard: 'erc721',
name: 'Kudos Name',
description: 'Kudos Description',
image_url: 'Kudos image (from proxy API)',
},
})
.get(`/asset_contract/${ERC721_KUDOSADDRESS}`)
.get(`/chain/ethereum/contract${ERC721_KUDOSADDRESS}`)
.replyWithError(new Error('Failed to fetch'));

const { selectedAddress } = nftController.config;
Expand Down Expand Up @@ -2092,34 +2130,13 @@ describe('NftController', () => {
it('should add NFT erc721 and not get NFT information directly from OpenSea API when OpenSeaAPIkey is set and queries to OpenSea proxy fail', async () => {
const { assetsContract, nftController } = setupController();
nock(OPENSEA_PROXY_URL)
.get(`/asset_contract/${ERC721_NFT_ADDRESS}`)
.get(`/chain/ethereum/contract/${ERC721_NFT_ADDRESS}`)
.replyWithError(new Error('Failed to fetch'))
.get(`/asset/${ERC721_NFT_ADDRESS}/${ERC721_NFT_ID}`)
.get(
`/chain/ethereum/contract/${ERC721_NFT_ADDRESS}/nfts/${ERC721_NFT_ID}`,
)
.replyWithError(new Error('Failed to fetch'));

nock(OPENSEA_API_URL, {
encodedQueryParams: true,
})
.get(`/asset_contract/${ERC721_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/${ERC721_NFT_ADDRESS}/${ERC721_NFT_ID}`)
.reply(200, {
image_original_url: 'image (directly from opensea)',
name: 'name (directly from opensea)',
description: 'description (directly from opensea)',
asset_contract: {
schema_name: 'ERC721',
},
});

nock('https://mainnet.infura.io:443', { encodedQueryParams: true })
.post('/v3/ad3a368836ff4596becc3be8e2f137ac', {
jsonrpc: '2.0',
Expand Down
Loading

0 comments on commit 3ed517b

Please sign in to comment.