STO相关ERC协议
1.1 ERC-20 Token标准协议
是Token的标准接口。ERC-20标准定义了一套智能合约中的Token实现标准API。这个标准提供了转账Token的基本功能,并且允许Token被批准,以便链上第三方可以使用它们。EIP地址
该标准定义的接口如下:
contract ERC20 {
/*** 可选实现接口begin ***/
// 返回Token的名字,例如 "MyToken"
function name() public view returns (string);
// 返回Token的符号,例如 "HIX"
function symbol() public view returns (string);
// 返回Token使用的小数位数,例如 8
function decimals() public view returns (uint8);
/*** 可选实现接口end ***/
/*** 必须实现接口begin ***/
// 返回Token供应总量
function totalSupply() public view returns (uint256);
// 返回地址为_owner的帐户的帐户余额
function balanceOf(address _owner) public view returns (uint256 balance);
// 将Token的_value数量转到地址_to,并且必须发送Transfer事件。如果_from帐户余额没有足够的Token可以使用,方法应该抛出异常。0值的转账必须视为正常转账并发送Transfer事件。
function transfer(address _to, uint256 _value) public returns (bool success);
// 将Token的值从地址_value转到地址_to,并且必须发送Transfer事件。transferFrom方法用于提现工作流,允许合约代表您转账Token。例如,这可以用于允许合约以您的名义转让Token和/或收取子货币的费用。除非_from帐户通过某种机制有意授权消息的发送方,否则函数应该抛出异常,0值的转账必须视为正常转账并发送Transfer事件。
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
// 允许_spender多次从您的帐户中取款,直到_value金额。如果再次调用这个函数,它将用_value覆盖当前的允许量
function approve(address _spender, uint256 _value) public returns (bool success)
// 返回_spender仍然允许从_owner中提取的金额。
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
/*** 必须实现接口end ***/
/*** 事件begin ***/
// 必须在转账Token时发送,包括零值转账。当创建Token时,创建新Token的Token合约应该发送一个将_from地址设置为0x0的Transfer事件。
event Transfer(address indexed _from, address indexed _to, uint256 _value)
// 必须在成功调用approve(address _spender, uint256 _value)方法后发送
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
/*** 事件end ***/
}
实现示例:
1.2 ERC-1410 部分可替换Token标准
- 标题:部分可替换Token标准(是ERC-1400安全Token标准的一部分)
- 需要的其他标准: ERC-1066(状态码标准)
- 状态: Draft
- 官方地址
1.2.1 概述
用于将所有者Token组织到一组分区中的标准接口。
此标准位于与安全Token相关的ERC-1400标准集合之下。描述一个接口,该接口支持将所有者Token分组到分区中,每个分区由标识键和余额表示。
Token在分区粒度上操作,但是还跟踪关于Token的总体供应和所有者的总体余额的数据。
这个标准可以与ERC-20(#20)或ERC-777结合使用,从而为Token持有者余额的不同分区上的Token合约的行为提供额外的粒度透明层。
1.2.2 基本原理
部分可替换的Token允许将元数据附加到Token持有者的部分余额。这些部分余额称为分区,并由一个bytes32 _partition
键进行索引,该键可以与链上或链外的元数据相关联。这个元数据的规范,除了_partition
键的存在之外,并不构成这个标准的一部分。如果数据在具有相同分区的Token持有者之间发生变化,则可以将Token持有者地址与用作元数据键的分区配对(例如,“受限”分区可能与每个Token持有者的不同锁定日期相关联)。
对于单个所有者,分区中的每个Token因此共享公共元数据。
Token可替代性包括元数据,因此有:
- 对于特定的用户,给定分区中的Token是可替换的
- 对于特定的用户,来自不同分区的Token可能不可替换
注:根据实现的不同,不同用户之间具有相同bytes32
键的分区可能与不同的元数据相关联。
1.2.3 向后兼容性
这个标准可以很容易地与ERC-20和ERC-777任何一种标准结合起来,我们希望通常都是这样。因此,我们不定义标准的Token view函数(name
,symbol
,decimals
)。为了保持向后兼容ERC-20 / ERC-777(以及其他可替换Token标准),有必要定义在执行transfer
/send
操作时(即没有显式指定分区时)使用的分区。然而,这被视为实现细节(可以通过一个固定的列表,也可以通过编程确定)。一个选项是简单地遍历Token持有者的所有分区
,尽管这种方法需要知道块的gas限制。
1.2.4 说明
1.2.4.1 Token信息
balanceOf
聚合Token持有者跨所有分区的余额。相当于ERC-20/777规范中的balanceOf
。必须计算分配给Token持有者的所有分区余额的总和。
function balanceOf(address _tokenHolder) external view returns (uint256);
balanceOfByPartition
查询某个特定分区的余额。对于给定的Token持有者,跨partitionof
的balanceOfByPartition
的和必须等于balanceOf
。
function balanceOfByPartition(bytes32 _partition, address _tokenHolder) external view returns (uint256);
partitionsOf
Token持有者可以将其余额划分为多个分区——该函数将返回与特定Token持有者地址关联的所有分区。
function partitionsOf(address _tokenHolder) external view returns (bytes32[]);
totalSupply
返回跨所有Token持有者和分区发行的Token总数。必须计算此合约跟踪的所有Token。
1.2.4.2 Token转移
Token转移总是有一个相关的源和目标分区,以及转移数量和发送/接收地址。
例如,一个被许可的Token可以使用分区元数据来执行基于以下的转移限制:
-
_partition
值 - 与
_partition
值关联的任何其他数据(例如可能与_partition
关联的锁定时间戳) - 与Token的发送人或接收人有关的任何详情(例如,他们的身份是否已确定)
- 转让Token的数量(例如,是否遵守任何每日或其他基于时段的数量限制)
-
_data
参数允许调用方提供与转移相关的任何额外授权或详细信息(例如,来自授权实体的签名数据,该授权实体被允许授权转移)
其他用例包括通过将以前的持有者与目标分区关联来跟踪Token的来源。
transferByPartition
如果Token的转移因任何原因不成功,则此函数必须抛出异常。当从特定的分区转移Token时,了解这些Token的目标分区在链上(即不仅仅通过触发事件)是很有用的。目标分区将由这个函数的实现决定,并且根据用例的不同而有所不同。函数必须返回接收器的bytes32 _partition
。bytes _data
允许在转移的同时提交任意数据,以便Token合约进行解释或记录。这可以是授权转移的签名数据(例如一个动态白名单),或者为Token合约提供一些输入来确定接收者分区。这个函数必须发出一个TransferByPartition
事件,才能成功转移。
function transferByPartition(bytes32 _partition, address _to, uint256 _value, bytes _data) external returns (bytes32);
operatorTransferByPartition
允许一个操作者代表Token持有者在指定分区内转移安全的Token。如果由一个地址调用该函数,而该地址没有isOperatorForPartition
或isOperatorFor
定义的适当批准,则该函数必须回退。这个函数必须发出一个TransferByPartition
事件来成功地进行Token转移,并包含操作符地址。
function operatorTransferByPartition(bytes32 _partition, address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external returns (bytes32);
canTransferByPartition
由于多种原因,部分可替换Token的转移可能会失败,这可能与Token持有者的部分余额有关,也可能与正在转移的分区相关的规则有关。这些规则可以使用智能合约和链上数据定义,也可以依赖于作为transferByPartition
函数的一部分传递的_data
,该函数可以表示对转移的授权(例如,由转移代理签名的消息,以证明该特定转移的有效性)。
该函数将返回一个符合EIP-1066标准的ESC (Ethereum状态代码)和一个额外的bytes32
参数,该参数可用于定义应用程序特定的原因代码和附加细节(例如负责使转移操作无效的转移限制规则)。
它还返回正在以类似于transferByPartition
的方式传输的Token的目标分区。
function canTransferByPartition(address _from, address _to, bytes32 _partition, uint256 _value, bytes _data) external view returns (byte, bytes32, bytes32);
1.2.4.3 操作者
每个Token持有者可以授权所有分区或特定分区的操作者。
- 一个特定的Token持有者和所有分区(
authorizeOperator
,revokeOperator
,isOperatorFor
) - 特定分区的特定Token持有者(
authorizeOperatorByPartition
,revokeOperatorByPartition
,isOperatorForPartition
)
authorizeOperator
允许Token持有者跨所有分区设置Token的操作者。必须为msg.sender
的所有分区授权操作者。每次调用该函数时,它都必须发出事件AuthorizedOperator
。
function authorizeOperator(address _operator) external;
revokeOperator
允许Token持有者跨所有分区撤销Token的操作者。
注意:有可能操作者将通过authorizeOperatorByPartition
对这个Token持有者和一些分区保持授权。
必须撤销先前给予msg.sender
所有分区的一个操作者的授权。这个函数必须在每次调用时发出事件RevokedOperator
。
function revokeOperator(address _operator) external;
isOperatorFor
返回指定的地址是否是给定Token持有者和所有分区的操作者。必须查询_operator
是否是_tokenHolder
的所有分区的一个操作者。
function isOperatorFor(address _operator, address _tokenHolder) external view returns (bool);
authorizeOperatorByPartition
允许Token持有者在特定分区上为其Token设置操作符。每次调用该函数时,它都必须发出事件AuthorizedOperatorByPartition
。
function authorizeOperatorByPartition(bytes32 _partition, address _operator) external;
revokeOperatorByPartition
允许Token持有者在特定分区上撤销Token的一个操作者。
注:有可能操作者将通过defaultOperatorsByPartition
或defaultoperator
保留对该Token持有者和分区的授权。
这个函数必须在每次调用时发出事件RevokedOperatorByPartition
。
function revokeOperatorByPartition(bytes32 _partition, address _operator) external;
isOperatorForPartition
返回指定的地址是否是给定Token持有者和分区的操作者。如果地址是上述任何类别下的操作者,则返回TRUE。
function isOperatorForPartition(bytes32 _partition, address _operator, address _tokenHolder) external view returns (bool);
1.2.5 接口
/// @title ERC-1410 Partially Fungible Token Standard
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec
interface IERC1410 {
// Token Information
function balanceOf(address _tokenHolder) external view returns (uint256);
function balanceOfByPartition(bytes32 _partition, address _tokenHolder) external view returns (uint256);
function partitionsOf(address _tokenHolder) external view returns (bytes32[]);
function totalSupply() external view returns (uint256);
// Token Transfers
function transferByPartition(bytes32 _partition, address _to, uint256 _value, bytes _data) external returns (bytes32);
function operatorTransferByPartition(bytes32 _partition, address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external returns (bytes32);
function canTransferByPartition(address _from, address _to, bytes32 _partition, uint256 _value, bytes _data) external view returns (byte, bytes32, bytes32);
// Operator Information
function isOperator(address _operator, address _tokenHolder) external view returns (bool);
function isOperatorForPartition(bytes32 _partition, address _operator, address _tokenHolder) external view returns (bool);
// Operator Management
function authorizeOperator(address _operator) external;
function revokeOperator(address _operator) external;
function authorizeOperatorByPartition(bytes32 _partition, address _operator) external;
function revokeOperatorByPartition(bytes32 _partition, address _operator) external;
// Issuance / Redemption
function issueByPartition(bytes32 _partition, address _tokenHolder, uint256 _value, bytes _data) external;
function redeemByPartition(bytes32 _partition, uint256 _value, bytes _data) external;
function operatorRedeemByPartition(bytes32 _partition, address _tokenHolder, uint256 _value, bytes _operatorData) external;
// Transfer Events
event TransferByPartition(
bytes32 indexed _fromPartition,
address _operator,
address indexed _from,
address indexed _to,
uint256 _value,
bytes _data,
bytes _operatorData
);
// Operator Events
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
event AuthorizedOperatorByPartition(bytes32 indexed partition, address indexed operator, address indexed tokenHolder);
event RevokedOperatorByPartition(bytes32 indexed partition, address indexed operator, address indexed tokenHolder);
// Issuance / Redemption Events
event IssuedByPartition(bytes32 indexed partition, address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
event RedeemedByPartition(bytes32 indexed partition, address indexed operator, address indexed from, uint256 amount, bytes operatorData);
}
1.3 ERC-1400(1411) ST标准
- 标题:安全Token标准
- 需要的其他标准: ERC-1410 (#1410), ERC-1594 (#1594), ERC-1644 (#1644), ERC-1643 (#1643), ERC-20 (#20), ERC-1066 (#1066)
- 状态: Draft
- 官方地址
1.3.1 概述
表示Ethereum上安全Token的标准库。
总的来说,它提供了一套标准接口,用于发布/赎回安全Token、管理其所有权和转移限制,并向Token持有者提供关于其Token余额的不同子集在传输限制、权利和义务方面的行为的透明性。
通过指定安全Token可由所有相关方操作和查询的标准接口,加速Ethereum区块链上证券的发行和管理。
1.3.2 摘要
标准应该向后兼容ERC-20(#20),并且容易扩展到兼容ERC-777(#777)。
- ERC-1410 (#1410): 区分所有权/透明限制
- ERC-1594 (#1594): 链上限制检查与错误信号,离线数据注入转移限制和发行/赎回语义
- ERC-1643 (#1643): 文件/图例管理
- ERC-1644 (#1644): 管理员操作(强制转移)
1.3.3 需求
将证券的发行、交易和生命周期事件转移到公共分类账上,需要有一种标准的方法来建模证券、其所有权和链上属性。
以下需求是在与安全Token生态系统中的各方进行讨论之后编制的。
- 必须有一个标准接口来查询转移是否成功,并返回失败的原因
- 必须能够为法律诉讼或追讨资金而强制转移
- 必须为发行和赎回发出标准事件
- 必须能够将元数据附加到Token持有者余额的子集,例如特殊的股东权利或用于转移限制的数据
- 必须能够在转移时基于链外数据、链上数据和转移参数修改元数据。
- 必须支持查询和订阅关于安全的任何相关文档的更新
- 可能需要将签名数据传递到转移交易中,以便在链上验证它
- 不应限制可代表的不同司法管辖区的资产类别的范围
- 必须兼容ERC-20
- 可以兼容ERC-777
1.3.4 原理
1.3.4.1 ERC-1594: 核心ST标准(Core Security Token Standard)
与通常只要求发送方拥有足够余额的一般Token相比,证券的转移可能由于各种原因而失败。
这些条件可能与正在转让的证券的元数据(即它们是否受到禁售期的限制)、证券的发送方和接收方的身份(即它们是否经过KYC流程、是否经过认证或发行人的附属机构)有关,或出于与特定转让无关但设置在Token级别的原因(即Token合约强制执行投资者的最大数量或任何单个投资者持有的百分比的上限)。
对于ERC-20 Token,balanceOf
和allowance
函数提供了一种方法,可以在执行转移之前检查转移是否可能成功,这可以在链上和链外执行。
对于代表证券的Token,该标准引入了一个函数canTransfer/canTransferByPartition
,当失败的原因更复杂时,该函数提供了一种更通用的方法来实现这一点;以及整个转让的功能(即包括随转让和证券接收方发送的任何数据)。
为了提供比true或false更丰富的结果,将返回字节返回代码。这允许我们给出转移失败的原因,或者至少是失败的原因属于哪一类。安全Token部分包括查询文档的能力和预期的转移成功。
为了支持转移函数的脱链数据输入,转移函数被扩展为transferWithData/transferFromWithData
,可以选择附加一个bytes _data
参数。
1.3.4.2 ERC-1410: 部分可替换Token标准
有许多类型的证券,尽管它们代表相同的基础资产,但它们需要与不同的数据相关联。
这些额外的元数据隐式地使这些证券不可替换,但是在实践中,这些数据通常应用于安全性的一个子集,而不是单独的安全性。将Token持有者的余额划分为多个分区的能力(每个分区都有单独的元数据)在部分可替换Token部分中得到解决。
例如,Token持有者的余额可以分为两部分:在主发行期间发行的Token,以及通过二级交易接收的Token。
安全Token合约可以引用此元数据,以便应用额外的逻辑来确定转移是否有效,并确定在转移到接收方的余额后应该与Token关联的元数据。
或者,安全Token可以使用这种机制,以便能够向投资者透明地显示其Token的不同子集在转移限制方面的行为。在这种情况下,可以通过编程确定余额。
1.3.4.3 ERC-1643: 文档管理标准
安全Token通常具有与之关联的文档。这可以是发行文件、图例细节等。
能够设置/删除和检索这些文档,并具有与这些操作相关的事件,使投资者能够随时了解有关其投资的文档。
这一标准没有为投资者提供任何方式来表明他们已经阅读或同意这些文件。
1.3.4.4 ERC-1644: Controller Token Operation Standard
安全Token受到监管和法律监管(这将取决于管辖权的细节,监管框架和基础资产),在许多情况下,发行人(或一方委托给发行人作为控制员,如监管机构或转移代理)需要保留地址之间力传递Token的能力。
这些控制员转移应该是透明的(发出将此标记为强制转移的事件),Token合约本身应该明确表示这是否可能。
可能需要这样做的例子是反欺诈交易、解决丢失的私钥以及响应法院命令。
1.3.5 说明
这个标准没有指定任何额外的函数,但是引用了ERC-1410(#1410)、ERC-1594(#1594)、ERC-1643(#1643)和ERC-1655(#1644)作为安全Token标准的底层库,每个标准都涵盖了安全Token功能的不同方面。
为了将这两个标准结合起来,需要指定额外的约束。
operatorTransferByPartition
如果Token是可控的(iscontrolled
返回“TRUE”),那么控制员可以使用operatorTransferByPartition
,而无需得到Token持有者的显式授权。
在本例中,operatorTransferByPartition
还必须发出一个ControllerTransfer事件。
相应地,如果isControllable
返回FALSE
,那么控制员不能调用operatorTransferByPartition
,除非Token持有者明确授权。
operatorRedeemByPartition
如果Token是可控的(iscontrolled
返回TRUE
),那么控制员可以使用operatorRedeemByPartition
,而无需得到Token持有者的显式授权。
在本例中,operatorRedeemByPartition
还必须发出一个ControllerRedemption事件。
相应地,如果iscontrolled
返回FALSE
,那么控制员不能调用operatorRedeemByPartition
,除非Token持有者明确授权。
默认分区
为了让transfer
和transferWithData
对部分可替换的Token进行操作,需要有一些这些函数适用的默认分区的概念。如何确定它们的细节(例如,一个固定的列表、动态的或者使用1partitionsOf`)作为实现细节,而不是定义为标准的一部分。
当作为transfer
或transferWithData
操作的一部分转移Token时,这些转移应该尊重部分可替换Token的不变性,即所有分区的余额之和应该等于Token持有者的总余额。
1.3.6 接口
/// @title IERC1400 Security Token Standard
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec
interface IERC1400 is IERC20 {
// Document Management
function getDocument(bytes32 _name) external view returns (string, bytes32);
function setDocument(bytes32 _name, string _uri, bytes32 _documentHash) external;
// Token Information
function balanceOfByPartition(bytes32 _partition, address _tokenHolder) external view returns (uint256);
function partitionsOf(address _tokenHolder) external view returns (bytes32[]);
// Transfers
function transferWithData(address _to, uint256 _value, bytes _data) external;
function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) external;
// Partition Token Transfers
function transferByPartition(bytes32 _partition, address _to, uint256 _value, bytes _data) external returns (bytes32);
function operatorTransferByPartition(bytes32 _partition, address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external returns (bytes32);
// Controller Operation
function isControllable() external view returns (bool);
function controllerTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external;
function controllerRedeem(address _tokenHolder, uint256 _value, bytes _data, bytes _operatorData) external;
// Operator Management
function authorizeOperator(address _operator) external;
function revokeOperator(address _operator) external;
function authorizeOperatorByPartition(bytes32 _partition, address _operator) external;
function revokeOperatorByPartition(bytes32 _partition, address _operator) external;
// Operator Information
function isOperator(address _operator, address _tokenHolder) external view returns (bool);
function isOperatorForPartition(bytes32 _partition, address _operator, address _tokenHolder) external view returns (bool);
// Token Issuance
function isIssuable() external view returns (bool);
function issue(address _tokenHolder, uint256 _value, bytes _data) external;
function issueByPartition(bytes32 _partition, address _tokenHolder, uint256 _value, bytes _data) external;
// Token Redemption
function redeem(uint256 _value, bytes _data) external;
function redeemFrom(address _tokenHolder, uint256 _value, bytes _data) external;
function redeemByPartition(bytes32 _partition, uint256 _value, bytes _data) external;
function operatorRedeemByPartition(bytes32 _partition, address _tokenHolder, uint256 _value, bytes _operatorData) external;
// Transfer Validity
function canTransfer(address _to, uint256 _value, bytes _data) external view returns (byte, bytes32);
function canTransferFrom(address _from, address _to, uint256 _value, bytes _data) external view returns (byte, bytes32);
function canTransferByPartition(address _from, address _to, bytes32 _partition, uint256 _value, bytes _data) external view returns (byte, bytes32, bytes32);
// Controller Events
event ControllerTransfer(
address _controller,
address indexed _from,
address indexed _to,
uint256 _value,
bytes _data,
bytes _operatorData
);
event ControllerRedemption(
address _controller,
address indexed _tokenHolder,
uint256 _value,
bytes _data,
bytes _operatorData
);
// Document Events
event Document(bytes32 indexed _name, string _uri, bytes32 _documentHash);
// Transfer Events
event TransferByPartition(
bytes32 indexed _fromPartition,
address _operator,
address indexed _from,
address indexed _to,
uint256 _value,
bytes _data,
bytes _operatorData
);
event ChangedPartition(
bytes32 indexed _fromPartition,
bytes32 indexed _toPartition,
uint256 _value
);
// Operator Events
event AuthorizedOperator(address indexed _operator, address indexed _tokenHolder);
event RevokedOperator(address indexed _operator, address indexed _tokenHolder);
event AuthorizedOperatorByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _tokenHolder);
event RevokedOperatorByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _tokenHolder);
// Issuance / Redemption Events
event Issued(address indexed _operator, address indexed _to, uint256 _value, bytes _data);
event Redeemed(address indexed _operator, address indexed _from, uint256 _value, bytes _data);
event IssuedByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _to, uint256 _value, bytes _data, bytes _operatorData);
event RedeemedByPartition(bytes32 indexed _partition, address indexed _operator, address indexed _from, uint256 _value, bytes _operatorData);
}
1.3.7 注意
链上与链外传输限制
决定是否可以发送安全Token的规则可能是自动执行的(例如,限制安全Token中投资者的最大数量的规则),或者需要链外输入(例如,明确的经纪人对交易的批准)。为了方便后者,transferByPartition
、transferWithData
、transferFromWithData
、canTransferByPartition
和canTransfer
函数接受一个额外的bytes _data
参数,该参数可以由被批准方签名并用于验证转移。
此数据的规范不在本标准的范围内,并且是特定于实现的。
身份
在许多管辖区下,一方是否能够接收和发送安全Token取决于该方的身份特征。例如,在一方有资格购买或出售特定证券之前,大多数司法管辖区都需要某种程度的KYC / AML流程。此外,一方可被归类为投资者资格类别(如合格投资者、合格买方),其公民身份也可告知与其证券相关的限制。
可以使用各种身份标准(例如ERC-725(#725)、Civic、uPort)来捕获一方的身份数据,以及其他集中管理的方法(例如维护一个从KYC角度获得批准的地址白名单)。这些标识标准的共同点来切断一个Ethereum地址(这可能是一个政党的钱包,或身份合同),因此canTransfer
功能可以使用的发送方和接收方的地址安全Token作为身份的代理在决定是否满足资格要求。
除此之外,该标准不要求任何特定的身份识别方法。
原因代码
为了改善Token持有者的体验,canTransfer
必须根据指定的EIP-1066应用程序特定的状态码,在成功或失败时返回一个原因字节码。实现还可以以bytes32
的形式返回任意数据,以提供原因代码没有捕获的其他信息。
1.4 ERC-1594 核心ST标准
- 标题:核心ST标准(是ERC-1400 ST标准的一部分)
- 需要的其他标准: ERC-20 (#20), ERC-1066 (#1066)
- 状态: Draft
- 官方地址
1.4.1 概述
此标准位于与安全Token相关的ERC-1400(#1411)标准集合之下。
提供一个标准来支持数据链外注入到转移/发行/赎回中,以及检查与执行不同的转移的有效性。
在转移、发布和赎回功能的同时提供数据(例如签名授权)的能力允许安全Token更灵活地实现转移限制,而不需要完全依赖于链上的白名单。
使用ERC-1066(#1066)来提供为什么转移会失败的原因代码,而不需要用户实际尝试和执行转移,这可以改进用户体验,并可能节省转移失败时的资源。
发行和赎回语义的正式化(类似于铸币/焚烧)提供了对Token的总供应以及它随时间如何变化的可见性。
1.4.2 摘要
包含错误信号、脱链数据注入和发布/赎回语义。
该标准继承自ERC-20(#20),如果需要,可以轻松扩展以满足ERC-777(#777)标准
1.4.3 需求
请参见ERC-1400需求
1.4.4 原理
请参见ERC-1400原理中讲解该标准的部分
1.4.5 说明
1.4.5.1 限制转移
canTransfer / canTransferFrom
证券转让可能因下列原因而失败:
- Token的发送方或接收方的身份
- 对转移的特定Token设置的限制(即对特定数量的Token进行锁定)
- 有关Token整体状态的限制(即投资者总数)
该标准提供了一个on-chain函数,用于确定转移是否成功,并返回详细信息,说明转移无效的原因。
这些规则既可以使用智能合约和链上数据定义,也可以依赖作为transferWithData
函数的一部分传递的_data
,该函数可以表示对转移的授权(例如,由转移代理签名的消息,以证明该特定转移的有效性)。
该函数将返回一个符合EIP-1066标准的ESC (Ethereum状态代码)和一个额外的bytes32
参数,该参数可用于定义应用程序特定的原因代码和附加细节(例如负责使转移操作无效的转移限制规则)。
如果bytes _data
为空,则对应检查transfer
(或transferFrom
)请求是否成功,如果bytes _data
被填充,则对应检查transferWithData
(或transferFromWithData
)是否成功。
canTransfer
假设Token的发送方是msg.sender
,将通过transfer
或transferWithData
执行,而canTransferFrom
允许Token发送方的规范,并且转移将通过transferFrom
或transferFromWithData
执行。
function canTransfer(address _to, uint256 _value, bytes _data) external view returns (byte, bytes32);
function canTransferFrom(address _from, address _to, uint256 _value, bytes _data) external view returns (byte, bytes32);
transferWithData
转移限制可以采用多种形式,通常涉及到链上规则或白名单。然而,对于许多类型的已批准的转移,维护已批准的转移的链上列表可能既麻烦又昂贵。另一种选择是联合签名方法,在这种方法中,除了Token持有者批准Token转移外,授权实体还提供签名数据,以进一步验证传输。
bytes _data
允许在转移的同时提交任意数据,以便Token合约进行解释或记录。这可以是授权转移的签名数据(例如动态白名单),但是足够灵活,可以容纳其他用例。
transferWithData
必须发出带有转移细节的Transfer
事件。
function transferWithData(address _to, uint256 _value, bytes _data) external;
transferFromWithData
这与transferWithData
函数类似。msg.sender
必须有足够的allowance
集合,这个集合必须计入相应的_value
。
function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) external;
1.4.5.2 Token发行
isIssuable
安全Token发行者可以指定Token的发行已经完成(即不能生成或发行新的Token)。
如果一个Token的isIssuable()
返回FALSE,那么它将来必须总是返回FALSE。
如果一个Token的isIssuable()
返回FALSE,则它必须永远不允许发出其他Token。
function isIssuable() external view returns (bool);
issue
此函数的调用必须用来增加总供应。
参数bytes _data
可用于注入链外数据(例如签名数据),以授权或验证已发行Token的发行和接收方。
调用时,此函数必须发出Issued
事件。
function issue(address _tokenHolder, uint256 _value, bytes _data) external;
1.4.5.3 Token赎回
redeem
允许Token持有者赎回Token。赎回的Token必须从总供应量和Token持有人的余额中减去。Token赎回应该类似于发送Token,并受相同的条件约束。
每次调用这个函数时,都必须发出Redeemed
事件。
与transferWithData
一样,这个函数有一个bytes _data
参数,可以在Token合约中使用该参数对赎回进行身份验证。
function redeem(uint256 _value, bytes _data) external;
redeemFrom
这与redeem
函数类似。msg.sender
必须有足够的allowance
集合,这个集合必须计入相应的_value
。
每次调用这个函数时,都必须发出Redeemed
事件。
function redeemFrom(address _tokenHolder, uint256 _value, bytes _data) external;
1.4.6 接口
/// @title IERC1594 Security Token Standard
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec
interface IERC1594 is IERC20 {
// Transfers
function transferWithData(address _to, uint256 _value, bytes _data) external;
function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) external;
// Token Issuance
function isIssuable() external view returns (bool);
function issue(address _tokenHolder, uint256 _value, bytes _data) external;
// Token Redemption
function redeem(uint256 _value, bytes _data) external;
function redeemFrom(address _tokenHolder, uint256 _value, bytes _data) external;
// Transfer Validity
function canTransfer(address _to, uint256 _value, bytes _data) external view returns (bool, byte, bytes32);
function canTransferFrom(address _from, address _to, uint256 _value, bytes _data) external view returns (bool, byte, bytes32);
// Issuance / Redemption Events
event Issued(address indexed _operator, address indexed _to, uint256 _value, bytes _data);
event Redeemed(address indexed _operator, address indexed _from, uint256 _value, bytes _data);
}
1.5 ERC-1643 文档管理标准
- 标题:文档管理标准(是ERC-1400 ST标准的一部分)
- 需要的其他标准: None
- 状态: Draft
- 官方地址
1.5.1 概述
此标准位于与安全Token相关的ERC-1400(#1411)标准集合之下。
提供一个标准来支持将文档附加到智能合约,特别是ERC-1400(#1411)上下文中的安全Token合约。
1.5.2 摘要
允许文档与智能合约和用于查询/修改这些合约的标准接口相关联,以及接收对这些文档上的更改的更新(通过事件)。
文档的示例可以包括与安全Token相关的提供文档和图例。
由于安全Token及其所有权通常需要来自投资者或发行方的权利和义务,因此将法律文件与相关合同相关联的能力非常重要。通过标准化的方式,钱包、交易所和其他生态系统成员可以提供这些文档的标准视图,并允许投资者以标准化的方式订阅更新。
1.5.3 需求
请参见ERC-1400需求
1.5.4 原理
能够将文档附加到安全Token,允许发行者或其他授权实体将与安全相关的文档传递给Token持有者。附加的文档可以选择性地包含其内容的hash,以便提供不变性保证。
1.5.5 说明
这些函数用于管理与Token关联的文档库。这些文件可以是法律文件,也可以是其他参考资料。
文档与一个短名称(表示为bytes32
)、一个修改过的时间戳相关联,并且可以选择链上与之关联的文档内容的hash。
它通过可以指向网站或其他文档门户的通用URI引用。
getDocument
用于返回具有已知名称的文档的详细信息(bytes32
)。返回与文档关联的URI (string
)、文档的hash(bytes32
)和文档最后通过setDocument(uint256)
修改的时间戳。
function getDocument(bytes32 _name) external view returns (string, bytes32, uint256);
setDocument
用于将新文档附加到合约,或更新已附加文档的URI或hash。
如果文档没有成功存储,setDocument
必须抛出。setDocument
必须发出一个DocumentUpdated
事件,其中包含附加或修改的文档的详细信息。
function setDocument(bytes32 _name, string _uri, bytes32 _documentHash) external;
removeDocument
用于从合约中删除现有的文档。removeDocument
必须抛出,如果文件没有成功删除。removeDocument
必须发出一个DocumentRemoved
事件,其中包含附加或修改的文档的详细信息。
function removeDocument(bytes32 _name) external;
getAllDocuments
用于检索附加到智能合约的完整文档列表。任何通过setDocument
添加而不是随后通过removeDocument
函数删除的文档都必须返回。
function getAllDocuments() view returns (bytes32[]);
1.5.6 接口
/// @title IERC1643 Document Management (part of the ERC1400 Security Token Standards)
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec
interface IERC1643 {
// Document Management
function getDocument(bytes32 _name) external view returns (string, bytes32, uint256);
function setDocument(bytes32 _name, string _uri, bytes32 _documentHash) external;
function removeDocument(bytes32 _name) external;
function getAllDocuments() external view returns (bytes32[]);
// Document Events
event DocumentRemoved(bytes32 indexed _name, string _uri, bytes32 _documentHash);
event DocumentUpdated(bytes32 indexed _name, string _uri, bytes32 _documentHash);
}
1.6 ERC-1644 控制员Token操作标准(Controller Token Operation Standard)
- 标题:控制员Token操作标准(是ERC-1400 ST标准的一部分)
- 需要的其他标准: None
- 状态: Draft
- 官方地址
1.6.1 概述
此标准位于与安全Token相关的ERC-1400(#1411)标准集合之下。
提供支持Token上的控制员操作(即强制转移)的标准。
安全Token受到监管(这将取决于管辖权的细节,监管框架和基础资产),在许多情况下,发行人(或一方委托给发行人作为控制员,如监管机构或转移代理)需要保留地址之间强制转移Token的能力。
这些控制员转移应该是透明的(发出将此标记为强制转移的事件),Token合约本身应该明确表示这是否可能。
可能需要这样做的例子是反欺诈交易、解决丢失私钥问题以及响应法院命令。
1.6.2 摘要
允许Token透明地声明控制员是否可以在地址之间单方面转移Token。
1.6.3 需求
请参见ERC-1400需求
1.6.4 原理
考虑到这个功能和Ethereum的分布式本质之间的冲突,使这些操作和执行这些事务的能力尽可能透明,允许不同的用例遵守标准。
表示安全性中的所有权的Token可能要求授权的操作人员对Token具有额外的控制。
这包括能够发行额外的供应量,以及强制转移Token。该标准允许对这些控件进行管理,并严格确保它们的透明性。如果发行者要求能够发行额外的Token,或者进行控制员转移(强制转移),那么可以透明地评估这些权限,而不是以定制或混淆的方式实现这些权限。
1.6.5 说明
controllerTransfer
此功能允许授权地址在任意两个Token持有者之间转移Token。转移仍然必须遵守Token持有者的余额(因此,转移数量最多只能为balanceOf(_from)
金额),并且可能还需要遵守其他转移限制。
controllerTransfer
必须发出ControllerTransfer
事件。
function controllerTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external;
controllerRedeem
此功能允许授权地址为任何Token持有者赎回Token。赎回仍然必须遵守Token持有者的余额(因此,赎回最多只能针对balanceOf(_tokenHolder)
的余额),并且可能还需要遵守其他转移限制。
controllerTransfer
必须发出ControllerRedemption
事件。
function controllerRedeem(address _tokenHolder, uint256 _value, bytes _data, bytes _operatorData) external;
isControllable
为了验证controllerTransfer
/controllerredemption
是否可用,可以使用isControllable
函数。
如果isControllable()
返回FALSE,那么它必须:
- 将来总是返回FALSE
-
controllerTransfer
必须恢复 -
controllerRedeem
必须恢复
换句话说,如果发行者将isControllable
设置为返回FALSE,则此Token合约将不再有控制员交易。
function isControllable() external view returns (bool);
1.6.6 接口
/// @title IERC1644 Controller Token Operation (part of the ERC1400 Security Token Standards)
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec
interface IERC1644 is IERC20 {
// Controller Operation
function isControllable() external view returns (bool);
function controllerTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _operatorData) external;
function controllerRedeem(address _tokenHolder, uint256 _value, bytes _data, bytes _operatorData) external;
// Controller Events
event ControllerTransfer(
address _controller,
address indexed _from,
address indexed _to,
uint256 _value,
bytes _data,
bytes _operatorData
);
event ControllerRedemption(
address _controller,
address indexed _tokenHolder,
uint256 _value,
bytes _data,
bytes _operatorData
);
}
网友评论