-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support HIP-336 Allowance APIs (#3252)
* Add an allowance design document * Add crypto_allowance, nft_allowance and token_allowance & corresponding history tables * Refactor history tables to use new History interface Signed-off-by: Steven Sheehy <steven.sheehy@hedera.com> Signed-off-by: Matheus DallRosa <matheus.dallrosa@swirlds.com>
- Loading branch information
1 parent
b3b616b
commit 867392d
Showing
63 changed files
with
2,337 additions
and
252 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
# HIP-336 Approval and Allowance | ||
|
||
## Purpose | ||
|
||
[HIP-336](https://hips.hedera.com/hip/hip-336) describes new APIs to approve and exercise allowances to a delegate | ||
account. An allowance grants a spender the right to transfer a predetermined maximum limit of the payer's hbars or | ||
tokens to another account of the spender's choice. | ||
|
||
## Goals | ||
|
||
* Enhance the database schema to store an account's allowances | ||
* Store the historical state of allowances | ||
* Enhance the REST API to show an account's crypto and token allowances | ||
|
||
## Non-Goals | ||
|
||
* Store the live state of allowances adjusted for each crypto transfer | ||
* Enhance gRPC APIs with allowance information | ||
* Enhance Web3 APIs with allowance information | ||
|
||
## Architecture | ||
|
||
### Database | ||
|
||
#### Crypto Allowance | ||
|
||
```sql | ||
create table if not exists crypto_allowance | ||
( | ||
amount bigint not null, | ||
owner bigint not null, | ||
payer_account_id bigint not null, | ||
spender bigint not null, | ||
timestamp_range int8range not null, | ||
primary key (owner, spender) | ||
); | ||
``` | ||
|
||
```sql | ||
create table if not exists crypto_allowance_history | ||
( | ||
like crypto_allowance including defaults, | ||
primary key (owner, spender, timestamp_range) | ||
); | ||
``` | ||
|
||
#### NFT Allowance | ||
|
||
```sql | ||
create table if not exists nft_allowance | ||
( | ||
approved_for_all boolean not null, | ||
owner bigint not null, | ||
payer_account_id bigint not null, | ||
serial_numbers bigint[] not null, | ||
spender bigint not null, | ||
timestamp_range int8range not null, | ||
token_id bigint not null, | ||
primary key (owner, spender, token_id) | ||
); | ||
``` | ||
|
||
```sql | ||
create table if not exists nft_allowance_history | ||
( | ||
like nft_allowance including defaults, | ||
primary key (owner, spender, token_id, timestamp_range) | ||
); | ||
``` | ||
|
||
#### Token Allowance | ||
|
||
```sql | ||
create table if not exists token_allowance | ||
( | ||
amount bigint not null, | ||
owner bigint not null, | ||
payer_account_id bigint not null, | ||
spender bigint not null, | ||
timestamp_range int8range not null, | ||
token_id bigint not null, | ||
primary key (owner, spender, token_id) | ||
); | ||
``` | ||
|
||
```sql | ||
create table if not exists token_allowance_history | ||
( | ||
like token_allowance including defaults, | ||
primary key (owner, spender, token_id, timestamp_range) | ||
); | ||
``` | ||
|
||
### REST API | ||
|
||
#### Crypto Allowances | ||
|
||
`/api/v1/accounts/{accountId}/allowances/crypto` | ||
|
||
```json | ||
{ | ||
"allowances": [ | ||
{ | ||
"amount": 10, | ||
"owner": "0.0.1000", | ||
"payer_account_id": "0.0.1000", | ||
"spender": "0.0.8488", | ||
"timestamp": { | ||
"from": "1633466229.96874612", | ||
"to": "1633466568.31556926" | ||
} | ||
}, | ||
{ | ||
"amount": 5, | ||
"owner": "0.0.1000", | ||
"payer_account_id": "0.0.1001", | ||
"spender": "0.0.9857", | ||
"timestamp": { | ||
"from": "1633466229.96874612", | ||
"to": null | ||
} | ||
} | ||
], | ||
"links": {} | ||
} | ||
``` | ||
|
||
Optional Filters | ||
|
||
* `limit`: The maximum amount of items to return. | ||
* `order`: Order by `spender`. Accepts `asc` or `desc` with a default of `asc`. | ||
* `spender`: Filter by the spender account ID. Only need to support `eq` operator and allow multiple. | ||
|
||
#### NFT Allowances | ||
|
||
`/api/v1/accounts/{accountId}/allowances/nfts` | ||
|
||
```json | ||
{ | ||
"allowances": [ | ||
{ | ||
"approved_for_all": false, | ||
"owner": "0.0.1000", | ||
"payer_account_id": "0.0.1000", | ||
"serial_numbers": [ | ||
1, | ||
2, | ||
3 | ||
], | ||
"spender": "0.0.8488", | ||
"token_id": "0.0.1032", | ||
"timestamp": { | ||
"from": "1633466229.96874612", | ||
"to": "1633466568.31556926" | ||
} | ||
}, | ||
{ | ||
"approved_for_all": true, | ||
"owner": "0.0.1000", | ||
"payer_account_id": "0.0.1000", | ||
"serial_numbers": [], | ||
"spender": "0.0.9857", | ||
"token_id": "0.0.1032", | ||
"timestamp": { | ||
"from": "1633466229.96874612", | ||
"to": null | ||
} | ||
} | ||
], | ||
"links": {} | ||
} | ||
``` | ||
|
||
Optional Filters | ||
|
||
* `limit`: The maximum amount of items to return. | ||
* `order`: Order by `spender` and `token_id`. Accepts `asc` or `desc` with a default of `asc`. | ||
* `spender`: Filter by the spender account ID. Only need to support `eq` operator and allow multiple. | ||
* `token.id`: Filter by the token ID. Only need to support `eq` operator and allow multiple. | ||
|
||
#### Token Allowances | ||
|
||
`/api/v1/accounts/{accountId}/allowances/tokens` | ||
|
||
```json | ||
{ | ||
"allowances": [ | ||
{ | ||
"amount": 10, | ||
"owner": "0.0.1000", | ||
"payer_account_id": "0.0.1000", | ||
"spender": "0.0.8488", | ||
"token_id": "0.0.1032", | ||
"timestamp": { | ||
"from": "1633466229.96874612", | ||
"to": "1633466568.31556926" | ||
} | ||
}, | ||
{ | ||
"amount": 5, | ||
"owner": "0.0.1000", | ||
"payer_account_id": "0.0.1000", | ||
"spender": "0.0.9857", | ||
"token_id": "0.0.1032", | ||
"timestamp": { | ||
"from": "1633466229.96874612", | ||
"to": null | ||
} | ||
} | ||
], | ||
"links": {} | ||
} | ||
``` | ||
|
||
Optional Filters | ||
|
||
* `limit`: The maximum amount of items to return. | ||
* `order`: Order by `spender` and `token_id`. Accepts `asc` or `desc` with a default of `asc`. | ||
* `spender`: Filter by the spender account ID. Only need to support `eq` operator and allow multiple. | ||
* `token.id`: Filter by the token ID. Only need to support `eq` operator and allow multiple. | ||
|
||
#### Transactions APIs | ||
|
||
Update all APIs that show transfers to return `is_approval` in its response. Including `/api/v1/accounts/:id` and all | ||
the transactions REST APIs. | ||
|
||
## Non-Functional Requirements | ||
|
||
* Ingest new transaction types at the same rate as consensus nodes | ||
|
||
## Open Questions | ||
|
||
1) How will we do REST API pagination using multiple columns? | ||
|
||
## Answered Questions | ||
|
||
1) How will we handle adjust allowance for serial numbers? | ||
|
||
The full list of allowed serials that result from the transaction will be provided in the record. | ||
|
||
2) What happens if client populates both `approvedForAll` and `serialNumbers`? | ||
|
||
It is an error if they populate `approvedForAll=true` and a non-empty `serialNumbers`. It is allowed, but not | ||
required, to populate `approvedForAll=false` when providing a non-empty `serialNumbers`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
hedera-mirror-common/src/main/java/com/hedera/mirror/common/domain/History.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.hedera.mirror.common.domain; | ||
|
||
/*- | ||
* | ||
* Hedera Mirror Node | ||
* | ||
* Copyright (C) 2019 - 2022 Hedera Hashgraph, LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnore; | ||
import com.google.common.collect.Range; | ||
|
||
public interface History { | ||
|
||
Range<Long> getTimestampRange(); | ||
|
||
void setTimestampRange(Range<Long> timestampRange); | ||
|
||
@JsonIgnore | ||
default Long getTimestampLower() { | ||
var timestampRange = getTimestampRange(); | ||
return timestampRange != null && timestampRange.hasLowerBound() ? timestampRange.lowerEndpoint() : null; | ||
} | ||
|
||
default void setTimestampLower(long timestampLower) { | ||
setTimestampRange(Range.atLeast(timestampLower)); | ||
} | ||
|
||
@JsonIgnore | ||
default Long getTimestampUpper() { | ||
var timestampRange = getTimestampRange(); | ||
return timestampRange != null && timestampRange.hasUpperBound() ? timestampRange.upperEndpoint() : null; | ||
} | ||
|
||
default void setTimestampUpper(long timestampUpper) { | ||
setTimestampRange(Range.closedOpen(getTimestampLower(), timestampUpper)); | ||
} | ||
} |
Oops, something went wrong.