Skip to content
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

feat: add getNFTTokenInfo #4506

Closed
wants to merge 43 commits into from
Closed

feat: add getNFTTokenInfo #4506

wants to merge 43 commits into from

Conversation

OGPoyraz
Copy link
Member

@OGPoyraz OGPoyraz commented Jul 5, 2024

Explanation

This PR exposes NFT collections api through NFT controller.

References

Related to: https://github.com/MetaMask/MetaMask-planning/issues/2507
Extension PR using this PR's preview build: MetaMask/metamask-extension#25692

Changelog

@metamask/assets-controllers

  • ADDED: Add fetchNftCollectionMetadata to NFTController api

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've highlighted breaking changes using the "BREAKING" category above as appropriate

@OGPoyraz OGPoyraz requested a review from a team as a code owner July 5, 2024 08:40
@OGPoyraz
Copy link
Member Author

OGPoyraz commented Jul 5, 2024

@metamaskbot publish-preview

Copy link
Contributor

github-actions bot commented Jul 5, 2024

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/accounts-controller": "17.2.0-preview-80f6e80",
  "@metamask-previews/address-book-controller": "5.0.0-preview-80f6e80",
  "@metamask-previews/announcement-controller": "7.0.0-preview-80f6e80",
  "@metamask-previews/approval-controller": "7.0.0-preview-80f6e80",
  "@metamask-previews/assets-controllers": "34.0.0-preview-80f6e80",
  "@metamask-previews/base-controller": "6.0.0-preview-80f6e80",
  "@metamask-previews/build-utils": "3.0.0-preview-80f6e80",
  "@metamask-previews/chain-controller": "0.1.0-preview-80f6e80",
  "@metamask-previews/composable-controller": "7.0.0-preview-80f6e80",
  "@metamask-previews/controller-utils": "11.0.0-preview-80f6e80",
  "@metamask-previews/ens-controller": "12.0.0-preview-80f6e80",
  "@metamask-previews/eth-json-rpc-provider": "4.0.0-preview-80f6e80",
  "@metamask-previews/gas-fee-controller": "18.0.0-preview-80f6e80",
  "@metamask-previews/json-rpc-engine": "9.0.0-preview-80f6e80",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.0-preview-80f6e80",
  "@metamask-previews/keyring-controller": "17.1.0-preview-80f6e80",
  "@metamask-previews/logging-controller": "5.0.0-preview-80f6e80",
  "@metamask-previews/message-manager": "10.0.0-preview-80f6e80",
  "@metamask-previews/name-controller": "8.0.0-preview-80f6e80",
  "@metamask-previews/network-controller": "19.0.0-preview-80f6e80",
  "@metamask-previews/notification-controller": "6.0.0-preview-80f6e80",
  "@metamask-previews/notification-services-controller": "0.1.1-preview-80f6e80",
  "@metamask-previews/permission-controller": "10.0.0-preview-80f6e80",
  "@metamask-previews/permission-log-controller": "3.0.0-preview-80f6e80",
  "@metamask-previews/phishing-controller": "10.0.0-preview-80f6e80",
  "@metamask-previews/polling-controller": "8.0.0-preview-80f6e80",
  "@metamask-previews/preferences-controller": "13.0.0-preview-80f6e80",
  "@metamask-previews/profile-sync-controller": "0.1.2-preview-80f6e80",
  "@metamask-previews/queued-request-controller": "2.0.0-preview-80f6e80",
  "@metamask-previews/rate-limit-controller": "6.0.0-preview-80f6e80",
  "@metamask-previews/selected-network-controller": "15.0.2-preview-80f6e80",
  "@metamask-previews/signature-controller": "18.0.0-preview-80f6e80",
  "@metamask-previews/transaction-controller": "34.0.0-preview-80f6e80",
  "@metamask-previews/user-operation-controller": "13.0.0-preview-80f6e80"
}

`${this.#getNftCollectionApi()}?${urlParams.toString()}`,
{
headers: {
Version: '1',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit; can use NFT_API_VERSION for the version

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 1a8b435

sahar-fehri
sahar-fehri previously approved these changes Jul 5, 2024

for (const address of contractAddress) {
urlParams.append('contract', address);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to mention that this API will have a limit of 20 for the contract address param in case this is relevant to your usecase

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for calling this out, this will be very rare case to have more than 20 nfts in one transaction but good to know.
I will check later the metrics of simulations if we ever encountered this much.

@sahar-fehri
Copy link
Contributor

Note: When you are passing a simple contract address to this param contract, you will expect to have a response with a single collection (which is the collection that corresponds to you address param)
However, if the contract address you pass is an Artblocks contract address 0x99a9b7c1116f9ceeb1652de04d5969cce509b069 for example or any other shared contract, the response can potentially have a cursor in the response and you will want to paginate on the response to look for the exact collection you are looking for;
Artblocks collection ids would look like this in the response : "id": "0x99a9b7c1116f9ceeb1652de04d5969cce509b069:407000000:407999999",

salimtb
salimtb previously approved these changes Jul 5, 2024
* @param chainId - The chain ID of the network where the NFT is located.
* @returns NFT collections metadata.
*/
async fetchNftCollectionsMetadata(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should short circuit if a chain id is provided that the NFT API doesn't support, to save a request + time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call @bergeron
I couldn't see any endpoint / constants that I can use it. Can you elaborate how do I derive that information here?

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like you've gotten feedback already, but I had some suggestions as well.

packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
Comment on lines 538 to 547
const urlParams = new URLSearchParams({
chainId,
});

for (const address of contractAddress) {
urlParams.append('contract', address);
}

return await handleFetch(
`${this.#getNftCollectionApi()}?${urlParams.toString()}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleFetch was changed some time ago to take a URL object, so instead of constructing the URL like this, what do you think about:

Suggested change
const urlParams = new URLSearchParams({
chainId,
});
for (const address of contractAddress) {
urlParams.append('contract', address);
}
return await handleFetch(
`${this.#getNftCollectionApi()}?${urlParams.toString()}`,
const url = new URL(this.#getNftCollectionApi());
url.searchParams.append('chainId', chainId);
for (const address of contractAddress) {
url.searchParams.append('contract', address);
}
return await handleFetch(
url,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 09bfcf5

@@ -4378,4 +4394,25 @@ describe('NftController', () => {

expect(updateNftMetadataSpy).not.toHaveBeenCalled();
});

it('fetchNftCollectionsMetadata fetches NFT collections metadata successfully', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts on wrapping this test in a describe block named after the method you want to test? For instance:

  describe('fetchNftCollectionsMetadata', () => {
    it('fetches NFT collections metadata successfully', async () => {
      // ...
    });
  });

I realize that this is not the style for other tests in this file, but it's a principle that we've been trying to move to as a whole. More about this here: https://github.com/MetaMask/contributor-docs/blob/main/docs/unit-testing.md#use-describe-to-group-tests-for-the-same-functionmethod

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the way of putting multiple describe blocks as well, the only reason that I didn't here was that this was the only test we run for the method.
But if we are happy to have one test inside describe block, here it is 24ac62f

packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
packages/assets-controllers/src/NftController.ts Outdated Show resolved Hide resolved
@OGPoyraz OGPoyraz changed the title feat: add fetchNftCollectionMetadata feat: add getNFTTokenInfo Jul 10, 2024
@OGPoyraz
Copy link
Member Author

@metamaskbot publish-preview

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/accounts-controller": "17.2.0-preview-a6b31354",
  "@metamask-previews/address-book-controller": "5.0.0-preview-a6b31354",
  "@metamask-previews/announcement-controller": "7.0.0-preview-a6b31354",
  "@metamask-previews/approval-controller": "7.0.0-preview-a6b31354",
  "@metamask-previews/assets-controllers": "34.0.0-preview-a6b31354",
  "@metamask-previews/base-controller": "6.0.0-preview-a6b31354",
  "@metamask-previews/build-utils": "3.0.0-preview-a6b31354",
  "@metamask-previews/chain-controller": "0.1.0-preview-a6b31354",
  "@metamask-previews/composable-controller": "7.0.0-preview-a6b31354",
  "@metamask-previews/controller-utils": "11.0.0-preview-a6b31354",
  "@metamask-previews/ens-controller": "12.0.0-preview-a6b31354",
  "@metamask-previews/eth-json-rpc-provider": "4.0.0-preview-a6b31354",
  "@metamask-previews/gas-fee-controller": "18.0.0-preview-a6b31354",
  "@metamask-previews/json-rpc-engine": "9.0.0-preview-a6b31354",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.0-preview-a6b31354",
  "@metamask-previews/keyring-controller": "17.1.0-preview-a6b31354",
  "@metamask-previews/logging-controller": "5.0.0-preview-a6b31354",
  "@metamask-previews/message-manager": "10.0.0-preview-a6b31354",
  "@metamask-previews/name-controller": "8.0.0-preview-a6b31354",
  "@metamask-previews/network-controller": "19.0.0-preview-a6b31354",
  "@metamask-previews/notification-controller": "6.0.0-preview-a6b31354",
  "@metamask-previews/notification-services-controller": "0.1.1-preview-a6b31354",
  "@metamask-previews/permission-controller": "10.0.0-preview-a6b31354",
  "@metamask-previews/permission-log-controller": "3.0.0-preview-a6b31354",
  "@metamask-previews/phishing-controller": "10.0.0-preview-a6b31354",
  "@metamask-previews/polling-controller": "8.0.0-preview-a6b31354",
  "@metamask-previews/preferences-controller": "13.0.0-preview-a6b31354",
  "@metamask-previews/profile-sync-controller": "0.1.2-preview-a6b31354",
  "@metamask-previews/queued-request-controller": "2.0.0-preview-a6b31354",
  "@metamask-previews/rate-limit-controller": "6.0.0-preview-a6b31354",
  "@metamask-previews/selected-network-controller": "15.0.2-preview-a6b31354",
  "@metamask-previews/signature-controller": "18.0.0-preview-a6b31354",
  "@metamask-previews/transaction-controller": "34.0.0-preview-a6b31354",
  "@metamask-previews/user-operation-controller": "13.0.0-preview-a6b31354"
}

@OGPoyraz
Copy link
Member Author

@metamaskbot publish-preview

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/accounts-controller": "17.2.0-preview-3ee9d02a",
  "@metamask-previews/address-book-controller": "5.0.0-preview-3ee9d02a",
  "@metamask-previews/announcement-controller": "7.0.0-preview-3ee9d02a",
  "@metamask-previews/approval-controller": "7.0.0-preview-3ee9d02a",
  "@metamask-previews/assets-controllers": "34.0.0-preview-3ee9d02a",
  "@metamask-previews/base-controller": "6.0.0-preview-3ee9d02a",
  "@metamask-previews/build-utils": "3.0.0-preview-3ee9d02a",
  "@metamask-previews/chain-controller": "0.1.0-preview-3ee9d02a",
  "@metamask-previews/composable-controller": "7.0.0-preview-3ee9d02a",
  "@metamask-previews/controller-utils": "11.0.0-preview-3ee9d02a",
  "@metamask-previews/ens-controller": "12.0.0-preview-3ee9d02a",
  "@metamask-previews/eth-json-rpc-provider": "4.0.0-preview-3ee9d02a",
  "@metamask-previews/gas-fee-controller": "18.0.0-preview-3ee9d02a",
  "@metamask-previews/json-rpc-engine": "9.0.0-preview-3ee9d02a",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.0-preview-3ee9d02a",
  "@metamask-previews/keyring-controller": "17.1.0-preview-3ee9d02a",
  "@metamask-previews/logging-controller": "5.0.0-preview-3ee9d02a",
  "@metamask-previews/message-manager": "10.0.0-preview-3ee9d02a",
  "@metamask-previews/name-controller": "8.0.0-preview-3ee9d02a",
  "@metamask-previews/network-controller": "19.0.0-preview-3ee9d02a",
  "@metamask-previews/notification-controller": "6.0.0-preview-3ee9d02a",
  "@metamask-previews/notification-services-controller": "0.1.1-preview-3ee9d02a",
  "@metamask-previews/permission-controller": "10.0.0-preview-3ee9d02a",
  "@metamask-previews/permission-log-controller": "3.0.0-preview-3ee9d02a",
  "@metamask-previews/phishing-controller": "10.0.0-preview-3ee9d02a",
  "@metamask-previews/polling-controller": "8.0.0-preview-3ee9d02a",
  "@metamask-previews/preferences-controller": "13.0.0-preview-3ee9d02a",
  "@metamask-previews/profile-sync-controller": "0.1.2-preview-3ee9d02a",
  "@metamask-previews/queued-request-controller": "2.0.0-preview-3ee9d02a",
  "@metamask-previews/rate-limit-controller": "6.0.0-preview-3ee9d02a",
  "@metamask-previews/selected-network-controller": "15.0.2-preview-3ee9d02a",
  "@metamask-previews/signature-controller": "18.0.0-preview-3ee9d02a",
  "@metamask-previews/transaction-controller": "34.0.0-preview-3ee9d02a",
  "@metamask-previews/user-operation-controller": "13.0.0-preview-3ee9d02a"
}

@OGPoyraz
Copy link
Member Author

@metamaskbot publish-preview

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/accounts-controller": "17.2.0-preview-f776a395",
  "@metamask-previews/address-book-controller": "5.0.0-preview-f776a395",
  "@metamask-previews/announcement-controller": "7.0.0-preview-f776a395",
  "@metamask-previews/approval-controller": "7.0.0-preview-f776a395",
  "@metamask-previews/assets-controllers": "34.0.0-preview-f776a395",
  "@metamask-previews/base-controller": "6.0.0-preview-f776a395",
  "@metamask-previews/build-utils": "3.0.0-preview-f776a395",
  "@metamask-previews/chain-controller": "0.1.0-preview-f776a395",
  "@metamask-previews/composable-controller": "7.0.0-preview-f776a395",
  "@metamask-previews/controller-utils": "11.0.0-preview-f776a395",
  "@metamask-previews/ens-controller": "12.0.0-preview-f776a395",
  "@metamask-previews/eth-json-rpc-provider": "4.0.0-preview-f776a395",
  "@metamask-previews/gas-fee-controller": "18.0.0-preview-f776a395",
  "@metamask-previews/json-rpc-engine": "9.0.0-preview-f776a395",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.0-preview-f776a395",
  "@metamask-previews/keyring-controller": "17.1.0-preview-f776a395",
  "@metamask-previews/logging-controller": "5.0.0-preview-f776a395",
  "@metamask-previews/message-manager": "10.0.0-preview-f776a395",
  "@metamask-previews/name-controller": "8.0.0-preview-f776a395",
  "@metamask-previews/network-controller": "19.0.0-preview-f776a395",
  "@metamask-previews/notification-controller": "6.0.0-preview-f776a395",
  "@metamask-previews/notification-services-controller": "0.1.1-preview-f776a395",
  "@metamask-previews/permission-controller": "10.0.0-preview-f776a395",
  "@metamask-previews/permission-log-controller": "3.0.0-preview-f776a395",
  "@metamask-previews/phishing-controller": "10.0.0-preview-f776a395",
  "@metamask-previews/polling-controller": "8.0.0-preview-f776a395",
  "@metamask-previews/preferences-controller": "13.0.0-preview-f776a395",
  "@metamask-previews/profile-sync-controller": "0.1.2-preview-f776a395",
  "@metamask-previews/queued-request-controller": "2.0.0-preview-f776a395",
  "@metamask-previews/rate-limit-controller": "6.0.0-preview-f776a395",
  "@metamask-previews/selected-network-controller": "15.0.2-preview-f776a395",
  "@metamask-previews/signature-controller": "18.0.0-preview-f776a395",
  "@metamask-previews/transaction-controller": "34.0.0-preview-f776a395",
  "@metamask-previews/user-operation-controller": "13.0.0-preview-f776a395"
}

@@ -468,7 +470,7 @@ export class NftController extends BaseController<
}

getNftApi() {
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
// False negative.
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${NFT_API_BASE_URL}/tokens`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would ${NFT_API_BASE_URL as string}/tokens fix the warning?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking this now, funny part is that it doesn't fail locally, only in CI 😅

Copy link
Contributor

@MajorLift MajorLift Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly think we should opt for the eslint-disable directive instead of a type assertion here, since the cause of this lint error is abnormal behavior from the lint rule, not the code itself.

The type assertion would silence other potential errors with NFT_API_BASE_URL (e.g. if it were to be typed as implicit any due to an upstream import issue, the as string would suppress useful alarms).

Copy link
Member Author

@OGPoyraz OGPoyraz Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that assertion here will silence the potential errors but before that we most probably would have some failed unit tests at first place if such change happens. Since this is not a complex cast I don't think this makes much sense.

I am reverting this change and leave this as is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MajorLift do you have any idea why this is not failing locally but on CI?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I experienced that as well. yarn lint:eslint results were different on local as well so it wasn't just an issue with my IDE's eslint server.

I do remember that it turned out to be an issue with my local configuration, but I can't recall what solved it just now. It definitely wasn't outside of the range of usual-suspect fixes (removing eslint cache, reinstalling packages etc.). I'll leave a comment here if I can remember what it was!

chainIds: Hex[],
tokens: {
contractAddress: string;
tokenId: string;
Copy link
Member

@matthewwalsh0 matthewwalsh0 Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the confirmations use case, why do we need the token ID, since our primary goal is to extract whether a specific contract address has isSpam set?

In the case of petnames, we don't have a token ID, only an address, so I assumed this API would be retrieving contract info, rather than individual token info?

@OGPoyraz
Copy link
Member Author

To clear confusion, I've addressed all suggestions here and created a clean PR here: #4524

@OGPoyraz OGPoyraz closed this Jul 15, 2024
@OGPoyraz OGPoyraz mentioned this pull request Jul 19, 2024
3 tasks
OGPoyraz added a commit to MetaMask/metamask-extension that referenced this pull request Sep 23, 2024
…for `ERC721`s (#25692)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

This PR integrates NFT api (`/collections`) to display name and image of
collection for nfts within simulations only for `ERC721` nfts.

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25692?quickstart=1)

## **Related issues**

Fixes: MetaMask/MetaMask-planning#2507
Related to: MetaMask/core#4506

## **Manual testing steps**

1. Go to Opensea, try buying
https://opensea.io/assets/ethereum/0xd4307e0acd12cf46fd6cf93bc264f5d5d1598792/41757
(you don't need to buy) - this is `ERC721`
2. See collection name and image in simulations 

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

![Screenshot 2024-07-15 at 15 40
22](https://github.com/user-attachments/assets/b8c6debd-1eee-44af-b9d1-07c0e03cc378)

### **After**

![Screenshot 2024-07-15 at 15 39
42](https://github.com/user-attachments/assets/f393945a-3118-4747-a27b-e7a077b9b32f)

## **Pre-merge author checklist**

- [X] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [X] I've completed the PR template to the best of my ability
- [X] I’ve included tests if applicable
- [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [X] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants