Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-1996: 可持有代币

Authors Julio Faura <julio@adhara.io>, Fernando Paris <fer@io.builders>, Daniel Lehrner <daniel@io.builders>
Created 2019-04-10
Discussion Link https://github.com/ethereum/EIPs/issues/2103
Requires EIP-20

简单总结

对 ERC-20 标准代币的扩展,允许代币被持有。这保证了未来的转账,并使持有的代币在期间不可用于转账。持有类似于托管,是确定的并导致最终结算。

参与者

操作者

一个账户,已被某个账户批准代表其创建持有。

持有发行者

创建持有的账户。这可以是账户所有者本身,也可以是被批准为该账户的操作者的任何账户。

公证人

决定是否执行持有的账户。

摘要

持有指定了付款人、收款人、最大金额、公证人和到期时间。当持有被创建时,付款人指定的代币余额将被持有。被持有的余额在持有被执行或释放之前不能被转移。持有只能由公证人执行,这将触发代币从付款人到收款人的转移。如果持有被释放,可以由公证人随时释放,或在到期后由任何人释放,则不会进行转移,并且该金额可再次供付款人使用。

如果执行指定的金额小于最大金额,则可以部分执行持有。在这种情况下,指定的金额将被转移到收款人,剩余的金额可再次供付款人使用。

持有可以被指定为永久的。在这种情况下,持有在到期时不能被释放,因此只能由公证人执行或由公证人或收款人释放。

动机

在以下不同的情况下,必须使用持有,即账户之间的立即转账是不可能的,或者必须预先保证:

  1. 受监管的代币可能不允许在未先验证是否符合所有法规的情况下在账户之间进行代币转移。在这种情况下,必须使用可清算的转账。在清算过程中,创建一个持有以确保在所有检查通过后转账成功。如果转账违反任何法规,则将其清除且不进一步处理。

  2. 在某些商业情况下,必须在可以使用其服务之前保证付款。例如:在酒店办理入住时,酒店将在客人的账户上设置一个持有,以确保有足够的余额来支付房间费用,然后再交出钥匙。

  3. 在其他情况下,必须在事先不知道确切金额的情况下保证付款。继续以酒店为例:酒店可以在客人的账户上设置一个持有,作为任何可能的额外费用(如客房服务)的担保。当客人结账时,持有被部分执行,剩余金额可再次在客人的账户上使用。

ERC-20 的 approve 函数为上述用例提供了一些必要的功能。与持有的主要区别在于,approve 不保证付款,因为批准的金额未被阻止,并且可以随时转移。

规范

interface IHoldable /* is ERC-20 */ {
    enum HoldStatusCode {
        Nonexistent,
        Ordered,
        Executed,
        ReleasedByNotary,
        ReleasedByPayee,
        ReleasedOnExpiration
    }

    function hold(string calldata operationId, address to, address notary, uint256 value, uint256 timeToExpiration) external returns (bool); 
    function holdFrom(string calldata operationId, address from, address to, address notary, uint256 value, uint256 timeToExpiration) external returns (bool);
    function releaseHold(string calldata operationId) external returns (bool);
    function executeHold(string calldata operationId, uint256 value) external returns (bool);
    function renewHold(string calldata operationId, uint256 timeToExpiration) external returns (bool);
    function retrieveHoldData(string calldata operationId) external view returns (address from, address to, address notary, uint256 value, 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, 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);
}

函数

hold

代表 msg.sender 创建一个有利于收款人的持有。它指定一个公证人,负责执行或释放持有。如果操作 ID 之前已经使用过,该函数必须回滚。

参数 描述
operationId 用于标识持有的唯一 ID
to 收款人的地址,如果执行,代币将被转移到该地址
notary 公证人的地址,他将决定是否执行或释放持有
value 要转移的金额。必须小于或等于付款人的余额。
timeToExpiration 持有到期前的持续时间。如果为“0”,则持有必须是永久的。

holdFrom

代表付款人创建一个有利于收款人的持有。from 账户必须事先批准,通过调用 approveToHold,另一个账户可以代表其发行持有。如果操作 ID 之前已经使用过,该函数必须回滚。

参数 描述
operationId 用于标识持有的唯一 ID
from 付款人的地址,如果执行,代币将从该地址扣除
to 收款人的地址,如果执行,代币将被转移到该地址
notary 公证人的地址,他将决定是否执行或释放持有
value 要转移的金额。必须小于或等于付款人的余额。
timeToExpiration 持有到期前的持续时间。如果为“0”,则持有必须是永久的。

releaseHold

释放持有。释放意味着不执行转移,并且持有的金额可再次供付款人使用。在持有到期之前,只能由公证人或收款人释放。到期后,可以由任何人释放。

参数 描述
operationId 用于标识持有的唯一 ID

executeHold

执行持有。执行意味着指定的价值从付款人转移到收款人。如果指定的值小于持有值,则剩余金额可再次供付款人使用。实现必须验证只有公证人才能成功调用该函数。

参数 描述
operationId 用于标识持有的唯一 ID
value 要转移的金额。该金额必须小于或等于持有值

renewHold

续订持有。新的到期时间必须是区块时间戳加上给定的 timeToExpiration,无论之前持有是否是永久性的。此外,如果 timeToExpiration 为“0”,则必须使持有成为永久性的。实现必须验证只有付款人或操作员才能成功调用该函数。此外,只有尚未到期的持有才能成功续订。

参数 描述
operationId 用于标识持有的唯一 ID
timeToExpiration 持有到期的新持续时间。

retrieveHoldData

检索特定持有的所有可用信息。

参数 描述
operationId 用于标识持有的唯一 ID

balanceOnHold

检索当前持有多少余额,因此不可用于转账。

参数 描述
account 应返回哪个地址的持有余额

netBalanceOf

检索净余额,即 balanceOfbalanceOnHold 的总和。

参数 描述
account 应返回哪个地址的净余额

totalSupplyOnHold

检索有多少代币被持有的总和。

| 参数 | 描述 | | ———|————-| | - | - |

authorizeHoldOperator

批准一个操作者代表 msg.sender 发行持有。

参数 描述
operator 要批准作为持有操作者的地址

revokeHoldOperator

撤销代表 msg.sender 发行持有的批准。

参数 描述
operator 要撤销作为持有操作者的地址

isHoldOperatorFor

检索操作员是否被批准代表 from 创建持有。

参数 描述
operator 要成为持有操作者的地址
from 将在其上创建持有的地址

balanceOf

必须更改 ERC-20 的标准实现,以便从 ERC-20 余额中扣除持有的余额。

transfer

必须更改 ERC-20 的标准实现,以便从 ERC-20 余额中扣除持有的余额。任何被持有的金额都不得转移。

transferFrom

必须更改 ERC-20 的标准实现,以便从 ERC-20 余额中扣除持有的余额。任何被持有的金额都不得转移。

事件

HoldCreated

当持有被创建时发出。

参数 描述
holdIssuer 持有发行人的地址
operationId 用于标识持有的唯一 ID
from 付款人的地址,如果执行,代币将从该地址扣除
to 收款人的地址,如果执行,代币将被支付给该地址
notary 公证人的地址,他将决定是否执行或释放持有
value 要转移的金额。必须小于或等于付款人的余额。
expiration 持有到期的 unix 时间戳

HoldExecuted

当持有被执行时发出。

参数 描述
holdIssuer 持有发行人的地址
operationId 用于标识持有的唯一 ID
notary 执行持有的公证人的地址
heldValue 创建期间被持有的金额
transferredValue 用于转账的金额

HoldReleased

当持有被释放时发出。

参数 描述
holdIssuer 持有发行人的地址
operationId 用于标识持有的唯一 ID
status 可以是以下值之一:ReleasedByNotaryReleasedByPayeeReleasedOnExpiration

HoldRenewed

当持有被续订时发出。

参数 描述
holdIssuer 持有发行人的地址
operationId 用于标识持有的唯一 ID
oldExpiration 续订之前的到期时间
newExpiration 续订之后的到期时间

AuthorizedHoldOperator

当一个操作员被批准代表另一个帐户创建持有时发出。

参数 描述
operator 要成为持有操作者的地址
account 潜在地在其上创建持有的地址

RevokedHoldOperator

当一个操作员被撤销代表另一个帐户创建持有时发出。

参数 描述
operator 要成为持有操作者的地址
account 潜在地在其上创建持有的地址

理由

该标准提供了一种功能,以保证未来的付款,这对于许多需要保证转移的业务案例是必需的。

它比 ERC-20 的 approve 函数更进一步,确保在完成转移时持有的余额可用。这是 approve 无法做到的,因为批准的金额只是最大支出金额,但永远无法保证可用。

虽然不是必需的,但函数 authorizeHoldOperatorrevokeHoldOperatorisHoldOperatorFor 的命名遵循 ERC-777 的命名约定。

operationId 是一个字符串,而不是更节省 gas 的东西,以便于跟踪持有并允许人类可读的 ID。如果字符串应该存储在链上还是只存储其哈希值,则由实现者决定,因为它足以标识持有。

operationId 是一种竞争性资源。建议(但不是必需的)持有发行人使用唯一的前缀以避免冲突。

向后兼容性

此 EIP 完全向后兼容,因为它的实现扩展了 ERC-20 的功能。

实现

GitHub 仓库 IoBuilders/holdable-token 包含参考实现。

贡献者

该提案由 adhara.ioio.builders 协同实施。

版权

CC0 下放弃版权及相关权利。

Citation

Please cite this document as:

Julio Faura <julio@adhara.io>, Fernando Paris <fer@io.builders>, Daniel Lehrner <daniel@io.builders>, "ERC-1996: 可持有代币 [DRAFT]," Ethereum Improvement Proposals, no. 1996, April 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1996.