hashgraph / hedera-mirror-node

Hedera Mirror Node archives data from consensus nodes and serves it via an API
Apache License 2.0
143 stars 111 forks source link

HIP-331 Account NFTs REST API #3081

Closed rocketmay closed 2 years ago

rocketmay commented 2 years ago

Problem

Issue:

For NFT apps being able to obtain a list of all the NFTs in an account is one of the basic calls. Currently the /balance?account.id= endpoint only returns the token ids of an account, but not the serial numbers.

The serial number is a critical identifying piece of information since each serial number under a token id can be owned by a different account and hold different metadata.

At the moment, after calling the balance of an account, a separate call has to be made to this endpoint to retrieve the serial numbers for each account: https://mainnet-public.mirrornode.hedera.com/api/v1/tokens//nfts?account.id=

This means retrieving the token list for an account can take up to N+1 calls to the API.

Solution

At a minimum the /balances?account.id= endpoint should return the serial number for tokens held by the account, in addition to the token id and balance.

Alternatives

A more detailed endpoint could be provided that could provide all unique information for the NFTs in an account. That endpoint would return a list of all tokens owned by an account id with the following fields:

{ Tokens: [ Token id Symbol Type Memo Balance Serials: [ Serial Memo // typically used for metadata uri Balance ] ] }

Tokens that are type Non Fungible would contain the list of serials owned by the account and their corresponding balance and memos.

Memo and Balance are duplicated because they are a property of the base token, while also being unique to each serial.

topshef commented 2 years ago

Would the format be something like this? https://gomint.me/api/nft/?accountId=0.0.496242&pre

This just combines data from the 2 existing endpoints so isn't very performant but could serve as a mockup:

https://mainnet-public.mirrornode.hedera.com/api/v1/balances?account.id=$accountId
https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/$token_id/nfts?account.id=$accountId

btw i assume balance doesn't exist within serial number as it's 1 by definition (NFT)

topshef commented 2 years ago

What are the performance and latency requirements? GoMint could provide this as above initially throttled at low call rates

rocketmay commented 2 years ago

Balance is either 0 (associated) or 1 (associated and owned)

steven-sheehy commented 2 years ago

Performance wise, this isn't possible to do. We have potential use cases where particular accounts can hold millions of NFTs. It's also the reason why we don't return the serial numbers in the account balance file snapshot nor did we ever implement HAPI TokenGetNftInfosQuery.

So if you take a hypothetical scenario where we just return a single account who has 1M NFTs, the payload response will be 61MiB just for the serial numbers alone. Even right now we have accounts in production (like token treasury accounts) that each have thousands of NFTs and to return those in a list of accounts would not be performant. The balance API is already one of our slowest endpoints due to the amount of information it returns.

Due to the number of NFTs that can be returned per account, it isn't practical to return an unbounded list of items in a nested JSON structure. We have to have paging when we return NFT information. So the https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/:tokenId/nfts?account.id=:accountId is the only way we can return that information efficiently.

This means retrieving the token list for an account can take up to N+1 calls to the API.

To clarify, we support returning up to 100 NFTs in a single response by setting the limit parameter. So if N represents how many total NFTs your account owns you would only need ceiling(N/100) + 1 API calls.

rocketmay commented 2 years ago

Hi @steven-sheehy,

Right, sorry I should have clarified that I assumed that the results would be paginated, just like the other calls to the API. The request is to have a call that returns the tokens+serials that are in an account. Let's say that an account has 50 NFT's (each with a unique token id) and pagination returns 25 results, getting the list of NFT's in an account with serial #'s would take 2 calls, whereas right now to get the same information it takes 51 calls.

There's already the balances call which returns all the tokens an account holds, the request is to have somewhere to include the serials as well. Sort of a combination of the balances?account.id=$accountId endpoint and the tokens/:tokenId/nfts?account.id=:accountId endpoints.

Is this possible, or still has issues? Thank you for your response.

xin-hedera commented 2 years ago

@rocketmay We did some analysis and believe we can support such query in a performant thus scalable way. We prefer the endpoint /api/v1/accounts/:accountId/nfts

it will support the limit query param as in other endpoints, used to limit the number of nfts returned for a single query.

it will also support nft.id query param the response pages on. nft.id is in the format of tokenId-serial, for example, 0.0.12345-100.

rocketmay commented 2 years ago

This is great news! What's the best way for me to keep track of this so I know when it's implemented?

Thank you very much.

steven-sheehy commented 2 years ago

@rocketmay Hedera requires a Hedera Improvement Proposal (HIP) for any user facing change. Do you mind writing one up for this? I'm not sure when we would be able to implement it though as we're heads down on Smart Contracts 2.0 at the moment.

As Xin mentioned the URL would be /api/v1/accounts/:accountId/nfts and the response would be exactly the same as /api/v1/tokens/:id/nfts. The paging mechanism would need some consideration as it's our first multi-column pagination. I'm not a fan of the nft.id that Xin mentions and prefer a separate token.id and serial.number parameters.

rocketmay commented 2 years ago

Hi steven, this sounds fine to me. I will write up a HIP per your recommendations and it can go through the regular review process to refine it.

hashaxis commented 2 years ago

We're here to drop our full support behind the solution proposed by May Chan. It should also be noted that the need for this was originally recognized by Hedera core team with "TokenNftInfoQuery()" (https://docs.hedera.com/guides/docs/sdks/tokens/get-nft-token-info) but this was deprecated for reasons unknown. We very urgently need a solution for this.

aesthytik commented 2 years ago

Thanks @rocketmay for opening the HIP. We very much exactly need this api for our upcoming marketplace and mobile wallet for Mintmaster. It will save us from hitting multiple queries or syncing problems. Really eager to see the progress of this HIP.

steven-sheehy commented 2 years ago

This feature is now available in v0.52.0. You can try it out on previewnet. It will roll out to the other environments in the coming weeks. Please let us know if you have any feedback on the API and we can adjust it before it hits mainnet.

https://previewnet.mirrornode.hedera.com/api/v1/accounts/0.0.3047/nfts