alchemy-sdk-js icon indicating copy to clipboard operation
alchemy-sdk-js copied to clipboard

FR: getSingleOwnedNft

Open BZhelev opened this issue 2 years ago • 3 comments

Is your feature request related to a problem? Please describe. There are a few instances in which fetching a single OwnedNft would be immensely helpful Navigating directly to a "nft" page. Showcasing how many Nfts you have so you know how much you can put on sale. These are just a few instances in which this feature would be helpful.

Describe the solution you'd like Either a new endpoint like getNftForOwner(address, tokenId, contractAddress) that will return a single OwnedNft. Or reuse current getNftsForOwner to have a { tokenIds?: string[] } filter option.

Describe alternatives you've considered The alternative I have right now it to use getNftMetadata or go thru all pages of getNftsForOwner and find the item I need. Since I need to display the balance of a given Nft.

Additional context Ditto

BZhelev avatar Nov 09 '22 10:11 BZhelev

@BZhelev Would the getNtMetadataBatch endpoint help? This way, you can call getNftsForOwner(address, contractAddress) to get all the owned nfts for the single contract you're interested in, and then pass the ones you'd like. Is your concern mainly the amount of Network IO you go through within a single contract?

thebrianchen avatar Nov 09 '22 20:11 thebrianchen

Hey there thank you for the reply. Yes my concern is the amount of requests needed to find a single NFT for a given Owner. Keep in mind that some details are obscured since it's a project in BETA and I cannot share that many details.

I experimented a bit and found that getNtMetadataBatch:

  1. Does not include token balance as seen from below response, made with fetch with the following options

Options:

            const options = {
              method: 'POST',
              headers: {
                accept: 'application/json',
                'content-type': 'application/json',
              },
              body: JSON.stringify({
                tokens: [
                  {
                    contractAddress:
                      'ADDRESS',
                    tokenId: '7',
                  },
                ],
              }),
            };

            fetch(
              'https://polygon-mumbai.g.alchemy.com/nft/v2/{API_KEY}/getNFTMetadataBatch',
              options,
            )
              .then(response => response.json())
              .then(response => console.log('from API', response))
              .catch(err => console.error(err));

Response:

[
    {
        "contract": {
            "address": "ADDRESS"
        },
        "id": {
            "tokenId": "7",
            "tokenMetadata": {
                "tokenType": "ERC1155"
            }
        },
        "title": "TITLE",
        "description": "A placeholder NFT",
        "tokenUri": {
            "raw": "URI",
            "gateway": "URI"
        },
        "media": [
            {
                "raw": "",
                "gateway": ""
            }
        ],
        "metadata": {
            "externalUrl": "URL",
            "tokenId": "7",
            "contract": {
                "name": "NAME",
                "description": "DESCRIPTION",
                "externalUrl": "URL",
                "id": 2,
                "imageUrl": "IMG_URL"
            },
            "description": "DESC",
            "type": "placeholder",
            "imageUrl": "IMG_URL",
            "name": "NAME",
            "contractId": 2,
            "attributes": [],
            "id": 6,
            "rarity": "rare"
        },
        "timeLastUpdated": "2022-09-26T06:52:52.067Z",
        "contractMetadata": {
            "tokenType": "ERC1155",
            "openSea": {
                "lastIngestedAt": "2022-11-05T13:10:09.000Z"
            }
        }
    }
]

2 ) I'm not sure that getNtMetadataBatch is included in the JS-SDK since in the docs it refers you to alchemy.nft.getNftMetadata: image

3 ) On the other hand alchemy.nft.getNftMetadata does not return balance as well: Options:

            const res = await alchemyProvider.nft.getNftMetadata(
              address,
              tokenId,
            );

Response:

{
    "contract": {
        "address": "ADDRESS",
        "tokenType": "ERC1155"
    },
    "tokenId": "7",
    "tokenType": "ERC1155",
    "title": "TITLE",
    "description": "DESC",
    "timeLastUpdated": "2022-09-26T06:52:52.067Z",
    "rawMetadata": {
        "externalUrl": "URL",
        "tokenId": "7",
        "contract": {
            "name": "NAME",
            "description": "DESC",
            "externalUrl": "URL",
            "id": 2,
            "imageUrl": "URL"
        },
        "description": "A placeholder NFT",
        "type": "placeholder",
        "imageUrl": "IMG_URL",
        "name": "NAME",
        "contractId": 2,
        "attributes": [],
        "id": 6,
        "rarity": "rare"
    },
    "tokenUri": {
        "raw": "URL",
        "gateway": "URL"
    },
    "media": []
}

4 ) This is the response I'm looking for Options:

            const fromOwner = await alchemyProvider.nft.getNftsForOwner(
              userAddress,
              {
                pageSize: 1,
              },
            );

Owner Response:

{
    "ownedNfts": [
        {
            "contract": {
                "address": "ADDRESS",
                "tokenType": "ERC1155"
            },
            "tokenId": "7",
            "tokenType": "ERC1155",
            "title": "TITLE",
            "description": "DESC",
            "timeLastUpdated": "2022-09-26T06:52:52.067Z",
            "rawMetadata": {
                "externalUrl": "URL",
                "tokenId": "7",
                "contract": {
                    "name": "NAME",
                    "description": "DESC",
                    "externalUrl": "URL",
                    "id": 2,
                    "imageUrl": "URL"
                },
                "description": "A placeholder NFT",
                "type": "placeholder",
                "imageUrl": "IMG",
                "name": "NAME",
                "contractId": 2,
                "attributes": [],
                "id": 6,
                "rarity": "rare"
            },
            "tokenUri": {
                "raw": "URL",
                "gateway": "URL"
            },
            "media": [],
            "balance": 9 // This is what I need, how many tokens does the user hold for this TokenId
        }
    ],
    "pageKey": "MHg0NTVhODFjZWJjNWNlZTljZmZiOTlhYmVkMzY4MDhmNzFiMTZhODE3OjB4MDc6ZmFsc2U=",
    "totalCount": 9
}

To summarize, I need a convenient way in which I can fetch a single NFT for a given owner along with it's balance for that given owner. So basically an endpoint that will return me a single OwnedNft Response rather than the standard Nft one.

At the moment the only way in which I can do so is to loop thru the pages returned from alchemy.nft.getNftsForOwner.

My use case for this is that we're building a marketplace and in that marketplace you could navigate to a page for a given NFT and if you own that NFT then you can sell it. However in order to sell it you need to say how much of it you want to sell, and in order to know that you need a OwnedNft response.

The API I'm hoping for is one of two.

First option:

            const fromOwner = await alchemyProvider.nft.getNftsForOwner(
              userAddress,
              {
                pageSize: 1,
                contractAddresses: [
                  'ADDRESS',
                ],
                tokenIds: [
                  'TOKEN_ID'
                ]
              },
            );

Response:

{
    "ownedNfts": [
        {
            "contract": {
                "address": "ADDRESS",
                "tokenType": "ERC1155"
            },
            "tokenId": "7",
            "tokenType": "ERC1155",
            "title": "TITLE",
            "description": "DESC",
            "timeLastUpdated": "2022-09-26T06:52:52.067Z",
            "rawMetadata": {
                "externalUrl": "URL",
                "tokenId": "7",
                "contract": {
                    "name": "NAME",
                    "description": "DESC",
                    "externalUrl": "URL",
                    "id": 2,
                    "imageUrl": "URL"
                },
                "description": "A placeholder NFT",
                "type": "placeholder",
                "imageUrl": "IMG",
                "name": "NAME",
                "contractId": 2,
                "attributes": [],
                "id": 6,
                "rarity": "rare"
            },
            "tokenUri": {
                "raw": "URL",
                "gateway": "URL"
            },
            "media": [],
            "balance": 9 // This is what I need, how many tokens does the user hold for this TokenId
        }
    ],
    "pageKey": "MHg0NTVhODFjZWJjNWNlZTljZmZiOTlhYmVkMzY4MDhmNzFiMTZhODE3OjB4MDc6ZmFsc2U=",
    "totalCount": 9
}

So basically adding another field to filter by.

Second option is to provide a new endpoint as alchemy.nft.getSingleNftForOwner. Second Option:

            const fromOwner = await alchemyProvider.nft.getSingleNftForOwner(
              userAddress,
               'ADDRESS',
               'TOKEN_ID'
            );

Response:

    {
            "contract": {
                "address": "ADDRESS",
                "tokenType": "ERC1155"
            },
            "tokenId": "7",
            "tokenType": "ERC1155",
            "title": "TITLE",
            "description": "DESC",
            "timeLastUpdated": "2022-09-26T06:52:52.067Z",
            "rawMetadata": {
                "externalUrl": "URL",
                "tokenId": "7",
                "contract": {
                    "name": "NAME",
                    "description": "DESC",
                    "externalUrl": "URL",
                    "id": 2,
                    "imageUrl": "URL"
                },
                "description": "A placeholder NFT",
                "type": "placeholder",
                "imageUrl": "IMG",
                "name": "NAME",
                "contractId": 2,
                "attributes": [],
                "id": 6,
                "rarity": "rare"
            },
            "tokenUri": {
                "raw": "URL",
                "gateway": "URL"
            },
            "media": [],
            "balance": 9 // This is what I need, how many tokens does the user hold for this TokenId
        }

All of the above are made with latest alchemy-sdk version 2.2.1

BZhelev avatar Nov 10 '22 09:11 BZhelev

@BZhelev I'll keep this feature request open, and we'll prioritize this if there's sufficient developer demand. In the meantime, you can reduce the payload size by specifying omitMetadata: true in the method options. This will return only the contract address, token id, and balance and you can filter from there.

thebrianchen avatar Nov 16 '22 11:11 thebrianchen

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs

github-actions[bot] avatar Dec 17 '22 02:12 github-actions[bot]

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs

github-actions[bot] avatar Jan 19 '23 02:01 github-actions[bot]

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs

github-actions[bot] avatar Feb 19 '23 02:02 github-actions[bot]

Hi @thebrianchen. I'm looking for an issue to contribute. Is this available?

ArtemFrantsiian avatar Feb 20 '23 18:02 ArtemFrantsiian

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs

github-actions[bot] avatar Mar 24 '23 02:03 github-actions[bot]