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

EIP-1996: Holdable token #1996

Open
wants to merge 3 commits into
base: master
from
Open
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -0,0 +1,298 @@
---
eip: 1996
title: Holdable Token
author: Daniel Lehrner <daniel@io.builders>
discussions-to: https://github.com/IoBuilders/EIPs/pull/1
status: Draft
type: Standards Track
category: ERC
created: 2019-04-10
requires: 20
---

## Simple Summary
An extension to the ERC-20 standard token that allows tokens to be put on hold. This guarantees a future transfer and makes the held tokens unavailable for transfer in the mean time. Holds are similar to escrows in that are firm and lead to final settlement.

## Actors

#### Operator
An account which has been approved by an account to create holds on its behalf.

#### Hold issuer
The account, which creates a hold. This can be the account owner itself, or any account, which has been approved as an operator for the account.

#### Notary
The account which decides if a hold should be executed.

## Abstract
A hold specifies a payer, a payee, a maximum amount, a notary and an expiration time. When the hold is created, the specified token balance from the payer is put on hold. A held balance cannot be transferred until the hold is either executed or released. The hold can only be executed by the notary, which triggers the transfer of the tokens from the payer to the payee. If a hold is released, either by the notary at any time, or by anyone after the expiration, no transfer is carried out and the amount is available again for the payer.

A hold can be partially executed, if the execution specifies an amount less than the maximum amount. In this case the specified amount is transferred to the payee and the remaining amount is available again to the payer.

Holds can be specified to be "eternal". In this case, the hold cannot be released upon expiration, and thus can only be executed by the notary or released by the notary or payee.

## Motivation

A hold has to be used in different scenarios where a immediate transfer between accounts is not possible or has to be guaranteed beforehand:

1. A regulated token may not allow to do a token transfer between accounts without verifying first, that it follows all the regulations. In this case a clearable transfer has to be used. During the clearing process a hold is created to ensure, that the transfer is successful after all checks have passed. If the transfer violates any of the regulations, it is cleared and not further processed.

1. In certain business situations a payment has to be guaranteed before its services can be used. For example: When checking in a hotel, the hotel will put a hold on the guest's account to ensure that enough balance is available to pay for the room before handing over the keys.

1. In other occasions a payment has to be guaranteed without knowing the exact amount beforehand. To stay with the hotel example: The hotel can put a hold on the guest's account as a guarantee for any possible extras, like room service. When the guest checks out the hold is partially executed and the remaining amount is available again on the guest's account.

The ERC-20 `approve` function provides some of the necessary functionality for the use cases above. The main difference to holds, is that `approve` does not ensure a payment, as the approved money is not blocked and can be transferred at any moment.

## Specification

```solidity
interface IHoldable /* is ERC-20 */ {
enum HoldStatusCode {
Nonexistent,
Ordered,
Executed,
ReleasedByNotary,
ReleasedByPayee,
ReleasedOnExpiration
}
function hold(string calldata operationId, address to, address notary, uint256 value, bool expires, uint256 timeToExpiration) external returns (bool);
function holdFrom(string calldata operationId, address from, address to, address notary, uint256 value, bool expires, uint256 timeToExpiration) external returns (bool);
function releaseHold(address holdIssuer, string calldata operationId) external returns (bool);
function executeHold(address holdIssuer, string calldata operationId, uint256 value) external returns (bool);
function renewHold(string calldata operationId, uint256 timeToExpiration) external returns (bool);
function retrieveHoldData(address holdIssuer, string calldata operationId) external view returns (address from, address to, address notary, uint256 value, bool expires, uint256 expiration, HoldStatusCode status);
function balanceOnHold(address account) external view returns (uint256);
function netBalanceOf(address account) external view returns (uint256);
function totalSupplyOnHold() external view returns (uint256);
function authorizeHoldOperator(address operator) external returns (bool);
function revokeHoldOperator(address operator) external returns (bool);
function isHoldOperatorFor(address operator, address from) external view returns (bool);
event HoldCreated(address indexed holdIssuer, string operationId, address from, address to, address indexed notary, uint256 value, bool expires, uint256 expiration);
event HoldExecuted(address indexed holdIssuer, string operationId, address indexed notary, uint256 heldValue, uint256 transferredValue);
event HoldReleased(address indexed holdIssuer, string operationId, HoldStatusCode status);
event HoldRenewed(address indexed holdIssuer, string operationId, uint256 oldExpiration, uint256 newExpiration);
event AuthorizedHoldOperator(address indexed operator, address indexed account);
event RevokedHoldOperator(address indexed operator, address indexed account);
}
```

### Functions

#### hold

Creates a hold on behalf of the msg.sender in favor of the payee. It specifies a notary who is responsable to either execute or release the hold.

| Parameter | Description |
| ---------|-------------|
| operationId | The unique ID per hold issuer to identify the hold |
| to | The address of the payee, to whom the tokens are to be transferred if executed |
| notary | The address of the notary who is going to determine whether the hold is to be executed or released |
| value | The amount to be transferred. Must be less or equal than the balance of the payer. |
| expires | The flag specifies whether the hold should expire or not |
| timeToExpiration | The duration until the hold is expired |

#### holdFrom

Creates a hold on behalf of the payer in favor of the payee. The `from` account has to approve beforehand, that another account can issue holds on its behalf by calling `approveToHold`.

| Parameter | Description |
| ---------|-------------|
| operationId | The unique ID per hold issuer to identify the hold |
| from | The address of the payer, from whom the tokens are to be taken if executed |
| to | The address of the payee, to whom the tokens are to be transferred if executed |
| notary | The address of the notary who is going to determine whether the hold is to be executed or released |
| value | The amount to be transferred. Must be less or equal than the balance of the payer. |
| expires | The flag specifies whether the hold should expire or not |
| timeToExpiration | The duration until the hold is expired |

#### releaseHold

Releases a hold. Release means that the transfer is not executed and the held amount is available again for the payer. A hold can be released by the notary or the payee at any time. After it has expired it can be released by anyone.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |

#### executeHold

Executes a hold. Execute means that the specified value is transferred from the payer to the payee. If the specified value is less than the hold value the remaining amount is available again to the payer. The implementation must verify that only the notary is able to successfully call the function.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |
| value | The amount to be transferred. This amount has to be less or equal than the hold value |

#### renewHold

Renews a hold. If a hold can expire it can be extended. The new expiration time is the block timestamp plus the given `timeToExpiration`. The implementation must verify that only the payer is able to successfully call the function.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |
| timeToExpiration | The new duration until the hold is expired |

#### retrieveHoldData

Retrieves all the information available for a particular hold.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |

#### balanceOnHold

Retrieves how much of the balance is currently held and therefore not available for transfer.

| Parameter | Description |
| ---------|-------------|
| account | The address which held balance should be returned |

#### netBalanceOf

Retrieves how much of the net balance, which is the sum of `balanceOf` and `balanceOnHold`.

| Parameter | Description |
| ---------|-------------|
| account | The address which net balance should be returned |

#### totalSupplyOnHold

Retrieves the total sum of how many tokens are on hold.

| Parameter | Description |
| ---------|-------------|
| - | - |

#### authorizeHoldOperator

Approves an operator to issue holds on behalf of msg.sender.

| Parameter | Description |
| ---------|-------------|
| operator | The address to be approved as operator of holds |

#### revokeHoldOperator

Revokes the approval to issue holds on behalf of msg.sender.

| Parameter | Description |
| ---------|-------------|
| operator | The address to be revoked as operator of holds |

#### isHoldOperatorFor

Retrieves if an operator is approved to create holds on behalf of `from`.

| Parameter | Description |
| ---------|-------------|
| operator | The address to be a operator of holds |
| from | The address on which the holds would be created |

#### balanceOf

The standard implementation of ERC-20 has to be changed in order to deduct the held balance from the ERC-20 balance.

#### transfer

The standard implementation of ERC-20 has to be changed in order to deduct the held balance from the ERC-20 balance. Any amount that is held must not be transferred.

#### transferFrom

The standard implementation of ERC-20 has to be changed in order to deduct the held balance from the ERC-20 balance. Any amount that is held must not be transferred.

### Events

#### HoldCreated

Emitted when a hold has been created.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |
| from | The address of the payer, from whom the tokens are to be taken if executed |
| to | The address of the payee, to whom the tokens are to be paid if executed |
| notary | The address of the notary who is going to determine whether the hold is to be executed or released |
| value | The amount to be transferred. Must be less or equal than the balance of the payer. |
| expires | `true` if the hold can expire, `false` if not |
| expiration | The unix timestamp when the hold is expired |

#### HoldExecuted

Emitted when a hold has been executed.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |
| notary | The address of the notary who executed the hold |
| heldValue | The amount which was put on hold during creation |
| transferredValue | The amount which was used for the transfer |

#### HoldReleased

Emitted when a hold has been released.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |
| status | Can be one of the following values: `ReleasedByNotary`, `ReleasedByPayee`, `ReleasedOnExpiration` |

#### HoldRenewed

Emitted when a hold has been renewed.

| Parameter | Description |
| ---------|-------------|
| holdIssuer | The address of the hold issuer of the hold |
| operationId | The unique ID per hold issuer to identify the hold |
| oldExpiration | The expiration time before the renewal |
| newExpiration | The expiration time after the renewal |

#### AuthorizedHoldOperator

Emitted when an operator has been approved to create holds on behalf of another account.

| Parameter | Description |
| ---------|-------------|
| operator | The address to be a operator of holds |
| account | Address on which behalf holds will potentially be created |

#### RevokedHoldOperator

Emitted when an operator has been revoked from creating holds on behalf of another account.

| Parameter | Description |
| ---------|-------------|
| operator | The address to be a operator of holds |
| account | Address on which behalf holds could potentially be created |

## Rationale

This standards provides a functionality, to guarantee future payments, which is needed for many business cases where transfers have to be guaranteed.

It goes a step further than the ERC-20 `approve` function by ensuring that the held balance will be available when the transfer is done. Something that can not be done with `approve`, as the approved amount is only a maximum spending amount, but never guaranteed to be available.

While not requiring it, the naming of the functions `authorizeHoldOperator`, `revokeHoldOperator` and `isHoldOperatorFor` follows the naming convention of [ERC-777](https://eips.ethereum.org/EIPS/eip-777).

## Backwards Compatibility

This EIP is fully backwards compatible as its implementation extends the functionality of ERC-20.

## Implementation
The GitHub repository [IoBuilders/holdable-token](https://github.com/IoBuilders/holdable-token) contains the work in progress implementation.

## Contributors
This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/).

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.