Alert Source Discuss
Standards Track: ERC

ERC-3643: T-REX - 受监管交易所的代币

一种机构级证券型代币合约,它提供了用于管理和合规转移证券型代币的接口。

Authors Joachim Lebrun (@Joachim-Lebrun), Tony Malghem (@TonyMalghem), Kevin Thizy (@Nakasar), Luc Falempin (@lfalempin), Adam Boudjemaa (@Aboudjem)
Created 2021-07-09
Requires EIP-20, EIP-173

摘要

T-REX 代币是一种机构级证券型代币标准。该标准提供了一系列接口,用于管理和合规转移证券型代币,使用自动化的链上验证器系统,利用链上身份进行资格检查。

该标准定义了以下几个接口:

  • Token
  • Identity Registry (身份注册表)
  • Identity Registry Storage (身份注册表存储)
  • Compliance (合规性)
  • Trusted Issuers Registry (可信发行者注册表)
  • Claim Topics Registry (声明主题注册表)

动机

区块链技术的出现为资产转移带来了效率、可访问性和流动性的新时代。这在加密货币领域尤为明显,用户可以点对点地转移代币所有权,而无需中介。然而,当涉及到代币化证券或证券型代币时,由于需要遵守证券法,情况就更加复杂。这些代币不能像实用型代币那样无需许可;它们必须经过许可才能追踪所有权并确保只有符合条件的投资者才能持有代币。

现有的以太坊协议虽然强大而通用,但并未完全解决证券型代币带来的独特挑战。需要一种标准来支持许可型代币的合规发行和管理,适用于代表各种资产类别,包括小型企业和房地产。

拟议的 ERC-3643 标准正是基于这种需求。它旨在为管理证券型代币的生命周期提供一个全面的框架,从发行到符合条件的投资者之间的转移,同时在每个阶段执行合规规则。该标准还支持额外的功能,如代币暂停和冻结,这些功能可用于管理代币,以应对监管要求或代币或其持有者状态的变化。

此外,该标准旨在与链上身份系统结合使用,允许通过受信任的声明发行者发布的签名证明来验证投资者的身份和凭证。这确保了符合证券型代币交易的法律和监管要求。

总而言之,拟议标准背后的动机是将区块链技术的好处带到证券领域,同时确保遵守现有的证券法。它旨在为证券型代币的发行和管理提供一个稳健、灵活和高效的框架,从而加速资本市场的演进。

规范

拟议的标准具有以下要求:

  • 必须ERC-20 兼容。
  • 必须与链上身份系统结合使用
  • 必须能够应用监管机构或代币发行者要求的任何合规规则(关于身份的资格因素或关于代币本身的规则)
  • 必须具有一个标准接口,用于在发送到区块链之前预先检查转移是否会通过或失败
  • 必须具有一个恢复系统,以防止投资者失去对其私钥的访问权限
  • 必须能够根据需要部分或全部冻结投资者钱包中的代币
  • 必须能够暂停代币
  • 必须能够铸造和销毁代币
  • 必须定义代理角色和所有者(代币发行者)角色
  • 必须能够强制从代理钱包进行转移
  • 必须能够批量发布交易(以节省 gas 并使所有交易在同一区块中执行)

虽然此标准向后兼容 ERC-20,并且可以在 ERC-3643 代币上调用所有 ERC-20 函数,但由于 ERC-3643 的许可性质,这些函数的实现方式有所不同。此标准下的每个代币转移都涉及合规性检查,以验证转移以及利益相关者身份的资格。

代理角色接口

该标准定义了一个代理角色,这对于管理对智能合约各种功能的访问至关重要。代理角色的接口如下:

interface IAgentRole {

  // events
  event AgentAdded(address indexed _agent);
  event AgentRemoved(address indexed _agent);
  
  // functions
  // setters
  function addAgent(address _agent) external;
  function removeAgent(address _agent) external;

  // getters
  function isAgent(address _agent) external view returns (bool);
}

IAgentRole 接口允许添加和删除代理,以及检查地址是否为代理。在此标准中,由 ERC-173 定义的所有者角色负责任命和删除代理。在本标准上下文中,任何履行 Token 合约或 Identity Registry 角色的合约都必须与 IAgentRole 接口兼容。

主要功能

####转移

要在 T-REX 上执行转移,您需要满足以下几个条件:

  • 发送者必须持有足够的可用余额(总余额 - 冻结的代币,如果有)
  • 接收者必须在 Identity Registry 上列入白名单并经过验证(持有其链上身份的必要声明)
  • 发送者的钱包不得被冻结
  • 接收者的钱包不得被冻结
  • 代币不得被暂停
  • 转移必须遵守 Compliance 智能合约中定义的所有合规规则(canTransfer 需要返回 TRUE)

以下是 transfer 函数实现的一个示例:

function transfer(address _to, uint256 _amount) public override whenNotPaused returns (bool) {
        require(!_frozen[_to] && !_frozen[msg.sender], "ERC-3643: Frozen wallet");
        require(_amount <= balanceOf(msg.sender) - (_frozenTokens[msg.sender]), "ERC-3643: Insufficient Balance");
        require( _tokenIdentityRegistry.isVerified(to), "ERC-3643: Invalid identity" ); 
        require( _tokenCompliance.canTransfer(from, to, amount), "ERC-3643: Compliance failure" );
        _transfer(msg.sender, _to, _amount);
        _tokenCompliance.transferred(msg.sender, _to, _amount);
        return true;
    }

transferFrom 函数的工作方式相同,而 mint 函数和 forcedTransfer 函数仅要求接收者在 Identity Registry 上列入白名单并经过验证(它们绕过合规规则)。 burn 函数绕过所有资格检查。

isVerified

isVerified 函数是从 transfer 函数 transfertransferFrommintforcedTransfer 中调用的,用于指示 Identity Registry 检查接收者是否是有效的投资者,即他的钱包地址是否在代币的 Identity Registry 中,以及链接到他的钱包的 Identity 合约是否包含 Claim Topics Registry 中需要的声明(请参阅 Claim Holder),以及这些声明是否由 Trusted Issuers Registry 中要求的授权 Claim Issuer 签名。 如果满足所有要求,isVerified 函数将返回 TRUE,否则将返回 FALSE。 可以在 Tokeny 的 T-REX 存储库中找到此函数的实现。

canTransfer

canTransfer 函数也是从 transfer 函数中调用的。 此函数检查 transfer 是否符合应用于代币的全局合规规则,与仅检查投资者持有和接收代币资格的 isVerified 相对,canTransfer 函数着眼于全局合规规则,例如,检查 transfer 是否符合有固定的最大代币持有者数量需要遵守的情况(每个国家/地区的持有者数量也可能有限),检查转移是否遵守设置每个投资者最大代币数量的规则,… 如果满足所有要求,canTransfer 函数将返回 TRUE,否则将返回 FALSE,并且不允许进行转移。 可以在 Tokeny 的 T-REX 存储库中找到此函数的实现。

其他功能

有关 ERC-3643 的其他功能的描述可以在 interfaces 文件夹中找到。 可以在 Tokeny 的 T-REX 存储库中找到 ERC-3643 智能合约套件的实现。

Token 接口

ERC-3643 许可型代币建立在标准 ERC-20 结构之上,但具有额外的功能以确保证券型代币交易的合规性。函数 transfertransferFrom 以条件方式实现,仅当交易有效时才允许它们继续转移。许可型代币仅允许转移到经过验证的交易对手方,以避免代币被持有在不合格/未经授权的投资者的钱包/Identity 合约中。 ERC-3643 标准还支持在投资者失去对其钱包私钥的访问权限时恢复证券型代币。出于透明原因,区块链上维护了已恢复代币的历史记录。

ERC-3643 代币实现了一系列附加功能,使所有者或其指定的代理能够管理供应、转移规则、锁定以及证券管理中的任何其他要求。该标准依赖于 ERC-173 来定义合约所有权,所有者负责指定代理。在本标准上下文中,任何履行 Token 合约角色的合约都必须与 IAgentRole 接口兼容。

有关该函数的详细描述,请参见 interfaces folder

interface IERC3643 is IERC20 {

   // events
    event UpdatedTokenInformation(string _newName, string _newSymbol, uint8 _newDecimals, string _newVersion, address _newOnchainID);
    event IdentityRegistryAdded(address indexed _identityRegistry);
    event ComplianceAdded(address indexed _compliance);
    event RecoverySuccess(address _lostWallet, address _newWallet, address _investorOnchainID);
    event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner);
    event TokensFrozen(address indexed _userAddress, uint256 _amount);
    event TokensUnfrozen(address indexed _userAddress, uint256 _amount);
    event Paused(address _userAddress);
    event Unpaused(address _userAddress);


    // functions
    // getters
    function onchainID() external view returns (address);
    function version() external view returns (string memory);
    function identityRegistry() external view returns (IIdentityRegistry);
    function compliance() external view returns (ICompliance);
    function paused() external view returns (bool);
    function isFrozen(address _userAddress) external view returns (bool);
    function getFrozenTokens(address _userAddress) external view returns (uint256);

    // setters
    function setName(string calldata _name) external;
    function setSymbol(string calldata _symbol) external;
    function setOnchainID(address _onchainID) external;
    function pause() external;
    function unpause() external;
    function setAddressFrozen(address _userAddress, bool _freeze) external;
    function freezePartialTokens(address _userAddress, uint256 _amount) external;
    function unfreezePartialTokens(address _userAddress, uint256 _amount) external;
    function setIdentityRegistry(address _identityRegistry) external;
    function setCompliance(address _compliance) external;

    // transfer actions
    function forcedTransfer(address _from, address _to, uint256 _amount) external returns (bool);
    function mint(address _to, uint256 _amount) external;
    function burn(address _userAddress, uint256 _amount) external;
    function recoveryAddress(address _lostWallet, address _newWallet, address _investorOnchainID) external returns (bool);

    // batch functions
    function batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) external;
    function batchForcedTransfer(address[] calldata _fromList, address[] calldata _toList, uint256[] calldata _amounts) external;
    function batchMint(address[] calldata _toList, uint256[] calldata _amounts) external;
    function batchBurn(address[] calldata _userAddresses, uint256[] calldata _amounts) external;
    function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external;
    function batchFreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external;
    function batchUnfreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external;
}

Identity Registry 接口

Identity Registry 链接到包含身份动态白名单的存储。 它建立了钱包地址、Identity 智能合约和与投资者居住国家/地区相对应的国家/地区代码之间的链接。 此国家/地区代码根据 ISO-3166 标准设置。Identity Registry 还包括一个名为 isVerified() 的函数,该函数根据用户 Identity 合约中声明的有效性(根据证券型代币要求)返回一个状态。

该标准依赖于 ERC-173 来定义合约所有权,所有者负责指定代理。 在本标准上下文中,任何履行 Identity Registry 角色的合约都必须与 IAgentRole 接口兼容。 Identity Registry 由代理钱包管理,这意味着只有代理才能在注册表中添加或删除身份。 请注意,Identity Registry 上的代理角色由所有者设置,因此,如果所有者希望保持完全控制,则可以将自己设置为代理。 每个证券型代币都有一个特定的 Identity Registry。

有关该函数的详细描述,请参见 interfaces folder

请注意,此接口中需要 IClaimIssuerIIdentity,因为它们是身份资格检查所必需的。

interface IIdentityRegistry {


    // events
    event ClaimTopicsRegistrySet(address indexed claimTopicsRegistry);
    event IdentityStorageSet(address indexed identityStorage);
    event TrustedIssuersRegistrySet(address indexed trustedIssuersRegistry);
    event IdentityRegistered(address indexed investorAddress, IIdentity indexed identity);
    event IdentityRemoved(address indexed investorAddress, IIdentity indexed identity);
    event IdentityUpdated(IIdentity indexed oldIdentity, IIdentity indexed newIdentity);
    event CountryUpdated(address indexed investorAddress, uint16 indexed country);


    // functions
    // identity registry getters
    function identityStorage() external view returns (IIdentityRegistryStorage);
    function issuersRegistry() external view returns (ITrustedIssuersRegistry);
    function topicsRegistry() external view returns (IClaimTopicsRegistry);

    //identity registry setters
    function setIdentityRegistryStorage(address _identityRegistryStorage) external;
    function setClaimTopicsRegistry(address _claimTopicsRegistry) external;
    function setTrustedIssuersRegistry(address _trustedIssuersRegistry) external;

    // registry actions
    function registerIdentity(address _userAddress, IIdentity _identity, uint16 _country) external;
    function deleteIdentity(address _userAddress) external;
    function updateCountry(address _userAddress, uint16 _country) external;
    function updateIdentity(address _userAddress, IIdentity _identity) external;
    function batchRegisterIdentity(address[] calldata _userAddresses, IIdentity[] calldata _identities, uint16[] calldata _countries) external;

    // registry consultation
    function contains(address _userAddress) external view returns (bool);
    function isVerified(address _userAddress) external view returns (bool);
    function identity(address _userAddress) external view returns (IIdentity);
    function investorCountry(address _userAddress) external view returns (uint16);
}

Identity Registry Storage 接口

Identity Registry Storage 存储链接到存储合约的证券型代币中所有授权投资者的身份地址。 这些都是在经过适当的 KYC 和资格检查后被授权持有代币的投资者的所有身份。 Identity Registry Storage 可以绑定到一个或多个 Identity Registry 合约。 Identity Registry storage 的目标是将 Identity Registry 功能和规范与其存储分离。 这样,每个代币都可以保留一个 Identity Registry 合约,其中包含自己的 Trusted Issuers Registry 和 Claim Topics Registry,但具有 isVerifed() 函数使用的共享投资者白名单,该函数在 Identity Registries 中实现,用于检查转移交易中接收者的资格。

该标准依赖于 ERC-173 来定义合约所有权,所有者负责指定代理(在本例中通过 bindIdentityRegistry 函数)。 在本标准上下文中,任何履行 Identity Registry Storage 角色的合约都必须与 IAgentRole 接口兼容。 Identity Registry Storage 由代理地址管理(即绑定的 Identity Registries),这意味着只有代理才能在注册表中添加或删除身份。 请注意,Identity Registry Storage 上的代理角色由所有者设置,因此,如果所有者想要手动修改存储,则可以将自己设置为代理。 否则,是绑定的 Identity Registries 使用代理角色来写入 Identity Registry Storage。

有关该函数的详细描述,请参见 interfaces folder

interface IIdentityRegistryStorage {

    //events
    event IdentityStored(address indexed investorAddress, IIdentity indexed identity);
    event IdentityUnstored(address indexed investorAddress, IIdentity indexed identity);
    event IdentityModified(IIdentity indexed oldIdentity, IIdentity indexed newIdentity);
    event CountryModified(address indexed investorAddress, uint16 indexed country);
    event IdentityRegistryBound(address indexed identityRegistry);
    event IdentityRegistryUnbound(address indexed identityRegistry);

    //functions
    // storage related functions
    function storedIdentity(address _userAddress) external view returns (IIdentity);
    function storedInvestorCountry(address _userAddress) external view returns (uint16);
    function addIdentityToStorage(address _userAddress, IIdentity _identity, uint16 _country) external;
    function removeIdentityFromStorage(address _userAddress) external;
    function modifyStoredInvestorCountry(address _userAddress, uint16 _country) external;
    function modifyStoredIdentity(address _userAddress, IIdentity _identity) external;

    // role setter
    function bindIdentityRegistry(address _identityRegistry) external;
    function unbindIdentityRegistry(address _identityRegistry) external;

    // getter for bound IdentityRegistry role
    function linkedIdentityRegistries() external view returns (address[] memory);
}

Compliance 接口

Compliance 合约用于设置发行本身的规则,并确保在代币的整个生命周期内遵守这些规则。 例如,Compliance 合约将定义每个国家/地区的投资者最大数量、每个投资者的代币最大数量以及代币流通的接受国家/地区(使用与 Identity Registry 中每个投资者对应的国家/地区代码)。 Compliance 智能合约可以是“量身定制的”,遵循代币发行者的法律要求,也可以以通用模块化形式部署,然后可以添加和删除外部 compliance Modules 以适应代币的法律要求,其方式与自定义的“量身定制的”合约相同。

此合约由 Token 在每个交易中触发,如果交易符合发行的规则,则返回 TRUE,否则返回 FALSE

该标准依赖于 ERC-173 来定义合约所有权,所有者负责设置 Compliance 参数并将 Compliance 绑定到 Token 合约。

有关该函数的详细描述,请参见 interfaces folder

interface ICompliance {

    // events
    event TokenBound(address _token);
    event TokenUnbound(address _token);

    // functions
    // initialization of the compliance contract
    function bindToken(address _token) external;
    function unbindToken(address _token) external;

    // check the parameters of the compliance contract
    function isTokenBound(address _token) external view returns (bool);
    function getTokenBound() external view returns (address);

    // compliance check and state update
    function canTransfer(address _from, address _to, uint256 _amount) external view returns (bool);
    function transferred(address _from, address _to, uint256 _amount) external;
    function created(address _to, uint256 _amount) external;
    function destroyed(address _from, uint256 _amount) external;
}

Trusted Issuer’s Registry 接口

Trusted Issuer’s Registry 存储特定证券型代币的所有可信声明发行者的合约地址 (IClaimIssuer)。 代币所有者(投资者)的 Identity 合约 (IIdentity) 必须具有由存储在此智能合约中的声明发行者签名的声明,才能持有代币。

该标准依赖于 ERC-173 来定义合约所有权,所有者负责根据他们的要求管理此注册表。 这包括添加、删除和更新 Trusted Issuers 列表的能力。

有关该函数的详细描述,请参见 interfaces folder

interface ITrustedIssuersRegistry {

    // events
    event TrustedIssuerAdded(IClaimIssuer indexed trustedIssuer, uint[] claimTopics);
    event TrustedIssuerRemoved(IClaimIssuer indexed trustedIssuer);
    event ClaimTopicsUpdated(IClaimIssuer indexed trustedIssuer, uint[] claimTopics);

    // functions
    // setters
    function addTrustedIssuer(IClaimIssuer _trustedIssuer, uint[] calldata _claimTopics) external;
    function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external;
    function updateIssuerClaimTopics(IClaimIssuer _trustedIssuer, uint[] calldata _claimTopics) external;

    // getters
    function getTrustedIssuers() external view returns (IClaimIssuer[] memory);
    function isTrustedIssuer(address _issuer) external view returns(bool);
    function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer) external view returns(uint[] memory);
    function getTrustedIssuersForClaimTopic(uint256 claimTopic) external view returns (IClaimIssuer[] memory);
    function hasClaimTopic(address _issuer, uint _claimTopic) external view returns(bool);
}

Claim Topics Registry 接口

Claim Topics Registry 存储证券型代币的所有可信声明主题。 代币所有者的 Identity 合约 (IIdentity) 必须包含存储在此智能合约中的声明主题的声明。

该标准依赖于 ERC-173 来定义合约所有权,所有者负责根据他们的要求管理此注册表。 这包括添加和删除所需的 Claim Topics 的能力。

有关该函数的详细描述,请参见 interfaces folder

interface IClaimTopicsRegistry {

    // events
    event ClaimTopicAdded(uint256 indexed claimTopic);
    event ClaimTopicRemoved(uint256 indexed claimTopic);

    // functions
    // setters
    function addClaimTopic(uint256 _claimTopic) external;
    function removeClaimTopic(uint256 _claimTopic) external;

    // getter
    function getClaimTopics() external view returns (uint256[] memory);
}

理由

Transfer 限制

证券转移可能会因各种原因而失败。 这与实用型代币形成直接对比,实用型代币通常仅要求发送者具有足够的余额。 这些条件可能与投资者的钱包状态、证券发送者和接收者的身份(即,他们是否已通过 KYC 流程,他们是否是发行者的认可投资者或附属机构)有关,或者与特定转移无关但却在代币级别设置的原因(即,代币合约强制执行最大投资者数量或对任何单个投资者持有的百分比的上限)。 对于 ERC-20 代币,balanceOfallowance 函数提供了一种在执行转移之前检查转移是否可能成功的方法,这些转移可以在链上和链下执行。 对于代表证券的代币,T-REX 标准引入了一个函数 canTransfer,它提供了一种更通用的方法来实现此目的。 即,当失败的原因与代币的合规规则有关时,以及一个 isVerified 函数,该函数允许检查投资者身份的资格状态。 如果发送者和/或接收者的地址被冻结,或者如果发送者的可用余额(总余额 - 冻结的代币)低于要转移的数量,则转移也可能失败。 最终,如果代币被 paused,则转移可能会被阻止。

身份管理

转移的安全性和合规性通过链上身份的管理来执行。 这些包括:

  • Identity 合约:每个投资者的唯一标识符,用于管理他们的身份和声明。
  • 声明:由可信声明发行者发布的签名证明,确认代币持有者的某些属性或资格,例如他们的身份、位置、投资者身份或 KYC/AML 许可。
  • Identity Storage/Registry:所有 Identity 合约及其关联钱包的存储系统,用于在转移期间验证投资者的资格。

代币生命周期管理

T-REX 标准提供了一个全面的框架来管理证券型代币的生命周期。 这包括代币的发行、符合条件的投资者之间的转移以及在代币生命周期的每个阶段执行合规规则。 该标准还支持额外的功能,例如代币暂停和冻结,这些功能可用于管理代币,以应对监管要求或代币或其持有者状态的变化。

附加合规规则

T-REX 标准支持通过模块化合规性实施附加的合规规则。 这些模块可用于强制执行各种规则和限制,例如对投资者数量的上限或单个投资者持有的代币百分比的上限、对特定类型的投资者之间转移的限制等等。 这种灵活性使发行者可以根据其特定需求和监管环境调整其代币的合规规则。

包含与代理相关的功能

在标准接口中包含与代理相关的功能是经过深思熟虑的。 目的是适应安全和可适应的代币管理实践,这些实践超越了 EOA 管理的功能。 我们设想了这样的一种场景,即代理角色由自动化系统或智能合约来履行,这些系统或智能合约能够根据指定的标准或监管触发因素以编程方式执行诸如铸造、销毁和冻结之类的操作功能。 例如,智能合约可能会自动销毁代币以与开放式基金中的赎回请求保持一致,或者冻结与从事欺诈活动的钱包相关的代币。

因此,这些功能被标准化,以为与不同 ERC-3643 代币交互的各种自动化系统提供统一的接口,从而允许在整个生态系统中使用的标准化工具和接口。 这种方法确保了 ERC-3643 的灵活性、面向未来性以及支持各种操作模型的能力。

向后兼容性

T-REX 代币应与 ERC-20 和 ERC-173 向后兼容,并且应该能够与 Claim Holder 合约 交互以验证链接到 Identity 合约 的声明。

安全考虑

本规范已通过 Kapersky 和 Hacken 的审核,未发现值得注意的安全考虑因素。 虽然审核主要集中在 Tokeny 的特定实施上,但他们也对 T-REX 标准的核心原则提出了挑战并进行了验证。 审核团队对这些原则的批准可以确保该标准本身是可靠的,并且不会带来任何重大的安全问题。

版权

版权及相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Joachim Lebrun (@Joachim-Lebrun), Tony Malghem (@TonyMalghem), Kevin Thizy (@Nakasar), Luc Falempin (@lfalempin), Adam Boudjemaa (@Aboudjem), "ERC-3643: T-REX - 受监管交易所的代币," Ethereum Improvement Proposals, no. 3643, July 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3643.