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 perpetual. 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:
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.
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.
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
interfaceIHoldable/* is ERC-20 */{enumHoldStatusCode{Nonexistent,Ordered,Executed,ReleasedByNotary,ReleasedByPayee,ReleasedOnExpiration}functionhold(stringcalldataoperationId,addressto,addressnotary,uint256value,uint256timeToExpiration)externalreturns(bool);functionholdFrom(stringcalldataoperationId,addressfrom,addressto,addressnotary,uint256value,uint256timeToExpiration)externalreturns(bool);functionreleaseHold(stringcalldataoperationId)externalreturns(bool);functionexecuteHold(stringcalldataoperationId,uint256value)externalreturns(bool);functionrenewHold(stringcalldataoperationId,uint256timeToExpiration)externalreturns(bool);functionretrieveHoldData(stringcalldataoperationId)externalviewreturns(addressfrom,addressto,addressnotary,uint256value,uint256expiration,HoldStatusCodestatus);functionbalanceOnHold(addressaccount)externalviewreturns(uint256);functionnetBalanceOf(addressaccount)externalviewreturns(uint256);functiontotalSupplyOnHold()externalviewreturns(uint256);functionauthorizeHoldOperator(addressoperator)externalreturns(bool);functionrevokeHoldOperator(addressoperator)externalreturns(bool);functionisHoldOperatorFor(addressoperator,addressfrom)externalviewreturns(bool);eventHoldCreated(addressindexedholdIssuer,stringoperationId,addressfrom,addressto,addressindexednotary,uint256value,uint256expiration);eventHoldExecuted(addressindexedholdIssuer,stringoperationId,addressindexednotary,uint256heldValue,uint256transferredValue);eventHoldReleased(addressindexedholdIssuer,stringoperationId,HoldStatusCodestatus);eventHoldRenewed(addressindexedholdIssuer,stringoperationId,uint256oldExpiration,uint256newExpiration);eventAuthorizedHoldOperator(addressindexedoperator,addressindexedaccount);eventRevokedHoldOperator(addressindexedoperator,addressindexedaccount);}
Functions
hold
Creates a hold on behalf of the msg.sender in favor of the payee. It specifies a notary who is responsible to either execute or release the hold. The function must revert if the operation ID has been used before.
Parameter
Description
operationId
The unique ID 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.
timeToExpiration
The duration until the hold is expired. If it is ‘0’ the hold must be perpetual.
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. The function must revert if the operation ID has been used before.
Parameter
Description
operationId
The unique ID 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.
timeToExpiration
The duration until the hold is expired. If it is ‘0’ the hold must be perpetual.
releaseHold
Releases a hold. Release means that the transfer is not executed and the held amount is available again for the payer. Until a hold has expired it can only be released by the notary or the payee. After it has expired it can be released by anyone.
Parameter
Description
operationId
The unique ID 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
operationId
The unique ID 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. The new expiration time must be the block timestamp plus the given timeToExpiration, independently if the hold was perpetual or not before that. Furthermore a hold must be made perpetual if timeToExpiration is ‘0’. The implementation must verify that only the payer or operator are able to successfully call the function. Furthermore the only a hold, which has not yet expired can be successfully renewed.
Parameter
Description
operationId
The unique ID 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
operationId
The unique ID 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 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.
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 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.
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 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 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 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.
The operationId is a string and not something more gas efficient to allow easy traceability of the hold and allow human readable ids. It is up to the implementer if the string should be stored on-chain or only its hash, as it is enough to identify a hold.
The operationId is a competitive resource. It is recommended, but nor required, that the hold issuers used a unique prefix to avoid collisions.
Backwards Compatibility
This EIP is fully backwards compatible as its implementation extends the functionality of ERC-20.
Julio Faura <julio@adhara.io>, Fernando Paris <fer@io.builders>, Daniel Lehrner <daniel@io.builders>, "ERC-1996: Holdable Token [DRAFT]," Ethereum Improvement Proposals, no. 1996, April 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1996.