Skip to content

Commit

Permalink
NFT Allowances REST API (#857)
Browse files Browse the repository at this point in the history
Signed-off-by: Mugdha Goel <[email protected]>
Signed-off-by: Michael Garber <[email protected]>
Co-authored-by: Steven Sheehy <[email protected]>
Co-authored-by: Michael Garber <[email protected]>
  • Loading branch information
3 people authored Jan 24, 2024
1 parent 904db85 commit c72dcf9
Showing 1 changed file with 236 additions and 0 deletions.
236 changes: 236 additions & 0 deletions HIP/hip-857.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
---
hip: 857
title: NFT Allowances REST API
author: Mugdha Goel <[email protected]>
working-group: Steven Sheehy (@steven-sheehy),Ashe Oro (@Ashe-Oro)
type: Standards Track
category: Mirror
needs-council-approval: Yes
status: Last Call
last-call-date-time: 2024-02-01T07:00:00Z
created: 2023-12-07
discussions-to: https://github.com/hashgraph/hedera-improvement-proposal/discussions/858
updated: 2024-01-19
---

## Abstract

Hedera customers have requested the ability to view their current NFT allowances. Hence, we propose adding a
new mirror node REST API `/api/v1/accounts/{id}/allowances/nfts` to return this information.

## Motivation

NFT marketplaces have requested the ability to view a NFT allowance's details via an API.

## Rationale
There have been requests to enable querying spending approvals for NFTs based on the owner ID. Additionally, there is a request to be able to query by the spender ID as well. It was also asked for the ability to know whether the approval was given via `approveForAll` or not. This information was requested by HashPack and Zuse marketplace.

The API described below has been chosen to make the implementation easier for NFT explorers and marketplaces that wish to search NFT allowances based on the owner and spender. The decision to design one API for both use cases is aimed at reducing the number of APIs that essentially perform similar tasks. This API would also return the `approveForAll` information.

## User stories

- As a mirror node operator, I want to provide my API clients with the ability to list NFT allowances for an account that has granted allowance for a specific NFT serial number, as well as for those who have used `approveForAll`.
- As a mirror node operator, I want to provide my API clients with the ability to list NFT allowances for a spender who has received an allowance for a single NFT serial number, as well as via `approveForAll`.

## Specification

This is a HIP for adding the ability to query the allowances on NFTs from the mirror node.

### Constraints

When designing the NFT allowances APIs, it is important to consider certain constraints on the number of allowances and tokens. These constraints may limit the ability to query unbounded information.

- An approval transaction can include a maximum of 20 allowance change operations.
- An account can have a maximum of 100 hbar, token, and approved for all NFT allowances.
- An account can have an unbounded number of individual NFT allowances.
- A non-fungible token can have an unbounded number of approved for all NFT allowances.
- A specific NFT can only have one explicit approval. The last one to be approved will take precedence.
- When an NFT is transferred by either the owner or a spender, it results in the individual spender's allowance being cleared.
- A specific NFT can have an unbounded number of implicit approved for all NFT allowances.

### Approved for all NFT Allowances API

This API accepts a path parameter that represents either the owner or spender, depending on a boolean flag provided as a query parameter called `owner`. When the `owner` value is true or omitted, the `accountId` path parameter should specify the ID of the owner, and the API will retrieve the allowances that the owner has granted to different spenders. Conversely, when the `owner` value is false, the `accountId` path parameter should indicate the ID of the spender who has an allowance, and the API will instead provide the allowances granted to the spender by different owners of those tokens.

Following are example responses of the NFT allowance API:

GET `/api/v1/accounts/0.0.1000/allowances/nfts?limit=3&owner=true`

```json
{
"allowances": [
{
"approved_for_all": true,
"owner": "0.0.1000",
"spender": "0.0.8488",
"token_id": "0.0.1033",
"timestamp": {
"from": "1633466229.96874612",
"to": null
}
},
{
"approved_for_all": false,
"owner": "0.0.1000",
"spender": "0.0.8489",
"token_id": "0.0.1034",
"timestamp": {
"from": "1633466229.96874618",
"to": null
}
},
{
"approved_for_all": true,
"owner": "0.0.1000",
"spender": "0.0.9857",
"token_id": "0.0.1032",
"timestamp": {
"from": "1633466229.96884615",
"to": null
}
}
],
"links": {
"next": "/api/v1/accounts/0.0.1000/allowances/nfts?limit=3&order=asc&account.id=gte:9857&token.id=gt:0.0.1032"
}
}
```

GET `/api/v1/accounts/0.0.8488/allowances/nfts?limit=3&owner=false`

```json
{
"allowances": [
{
"approved_for_all": true,
"owner": "0.0.1000",
"spender": "0.0.8488",
"token_id": "0.0.1033",
"timestamp": {
"from": "1633466229.96874612",
"to": null
}
},
{
"approved_for_all": true,
"owner": "0.0.1000",
"spender": "0.0.8488",
"token_id": "0.0.1034",
"timestamp": {
"from": "1633466229.96874618",
"to": null
}
},
{
"approved_for_all": false,
"owner": "0.0.1001",
"spender": "0.0.8488",
"token_id": "0.0.1099",
"timestamp": {
"from": "1633466229.96875612",
"to": null
}
}
],
"links": {
"next": "/api/v1/accounts/0.0.8488/allowances/nfts?limit=3&order=asc&account.id=gte:1001&token.id=gt:0.0.1099"
}
}
```

Query Parameters:

- `account.id`: Filter by the spender account ID or owner account ID, depending on the owner flag. `ne` operator is not supported. Only one occurrence is allowed.
- `limit`: The maximum number of items to return. Defaults to 25 with a maximum of 100 allowed.
- `order`: Order by `account.id` then `token.id`. Accepts `asc` or `desc` with a default of `asc`.
- `owner`: Indicates whether the path parameter `accountId` is the owner or the spender ID. Accepts a boolean value of `true` or `false` with a default value set to `true`.
- `token.id`: Filter by the token ID. `ne` operator is not supported. Only one occurrence is allowed.

Pagination is important to implement because there are accounts that could have a large number of NFT allowances, making a non-paginated response impractical. This requires multi-column pagination, including owner, spender, and token IDs.

**Ordering**

The order is governed by a combination of the account ID and the token ID values, with the account ID being the parent column. The token ID value governs its order within the given account ID.
The default order for this API is ascending.

**Filtering**

When filtering there are some restrictions enforced to ensure correctness and scalability.

The table below defines the restrictions and support for the endpoint.

| Query Param | Comparison Operator | Support | Description | Example |
| --- | --- | --- | --- | --- |
| account.id | eq | Y | Single occurrence only. | ?account.id=X |
| | ne | N | | |
| | lt(e) | Y | Single occurrence only. | ?account.id=lte:X |
| | gt(e) | Y | Single occurrence only. | ?account.id=gte:X |
| token.id | eq | Y | Single occurrence only. Requires the presence of an `account.id` query parameter| ?account.id=X&token.id=eq:Y |
| | ne | N | | |
| | lt(e) | Y | Single occurrence only. Requires the presence of a `lte` or `eq` `account.id` query parameter| ?account.id=lte:X&token.id=lt:Y |
| | gt(e) | Y | Single occurrence only. Requires the presence of a `gte` or `eq` `account.id` query parameter| ?account.id=gte:X&token.id=gt:Y |

Both filters must be a single occurrence of **gt(e)** or **lt(e)** which provide a lower and or upper boundary for search.

### Serial level allowance information

To retrieve NFT serial number level allowance information for a specific token ID you can use the existing API:

GET `/api/v1/tokens/{tokenId}/nfts`

```json
{
"nfts": [
{
"account_id": "0.0.123",
"created_timestamp": "1704827664.003435784",
"delegating_spender": null,
"deleted": false,
"metadata": "AA==",
"modified_timestamp": "1704827664.003435784",
"serial_number": 1,
"spender": null,
"token_id": "0.0.1000"
}
],
"links": {
"next": null
}
}
```

## Backwards Compatibility

This HIP primarily adds new information that mirror node operators and other record stream consumers can simply ignore if they do not need it. However, NFT marketplaces using the Hedera mirror node could start using the new API.

## Security Implications

There are no security implications for this HIP.

## How to Teach This

The Hedera documentation and the OpenAPI specification will be updated to add this new API.

## Reference Implementation

Please follow [this issue](https://github.com/hashgraph/hedera-mirror-node/issues/7480) for progress on the reference implementation.

## Rejected Ideas

- We briefly considered adding serial number level information to `/api/v1/accounts/{id}/allowances/nft` in order for an NFT marketplace to view which spender has `approvedForAll` for particular serial numbers. However, this idea was rejected due to the unbounded nature of the data. When a token has a very high number of serials and allowances, implementing this feature could become a performance concern.
- There was a consideration to add `approve_for_all` to `/api/v1/tokens/{id}/nfts/{id}`. This idea was rejected because it is possible to have an unbounded number of `approve_for_all` allowances and it is not feasible to return this data in a single response payload.
- There was a consideration to add `approve_for_all` to `/api/v1/accounts/{id}/tokens`. This idea was rejected because it is possible to have an unbounded number of `approve_for_all` allowances and it is not feasible to return this data in a single response payload.

## Open Issues

No known open issues exist.

## References

[Epic](https://github.com/hashgraph/hedera-mirror-node/issues/7480)
[HIP 336](https://hips.hedera.com/hip/hip-336)
[Design](https://github.com/hashgraph/hedera-mirror-node/blob/main/docs/design/allowances.md)

## Copyright/license

This document is licensed under the Apache License, Version 2.0 -- see [LICENSE](../LICENSE) or (https://www.apache.org/licenses/LICENSE-2.0)

0 comments on commit c72dcf9

Please sign in to comment.