阅读时间 10 分钟
概述
ERC-3643是一项针对许可型代币的以太坊代币标准,即仅允许符合预定义合规规则(如 KYC/AML、投资者资格及管辖权要求)的身份主体持有和转让的代币。该标准专为受监管资产设计,此类资产要求必须明确持有者身份,并确认 其 是否被允许持有或交易该资产。
该项目最初名为T-REX(受监管交易所代币),由 Tokeny 发起,现已正式定名为 ERC-3643 ,并由ERC-3643协会管理,该协会致力于规范现实世界资产(RWAs)的许可型通证化。
实际上,ERC-3643是一套智能合约,共同实现了以下功能:
- 链上身份与声明(通过 ONCHAINID实现)
- 符合资格持有人的身份登记册
- 模块化合规规则
- 一种在确保ERC-20兼容性的同时,于转账时强制执行合规性的代币合约
由于 ERC-3643 将身份识别和合规规则直接整合到了代币标准中,因此它是将证券、房地产、基金及其他受监管金融工具等实物资产(RWA)代币化的绝佳选择。
您将负责的工作内容
- 了解与普通 ERC-20 相比,ERC-3643 旨在解决什么问题
- 了解高层次架构(核心合约、注册表和角色)
- 了解身份验证和合规性如何在链上得到落实
- 详细说明 ERC-3643 系统中的转账机制
- 了解部署、角色以及 RWA 的常见用例概览
您需要准备的物品
本指南中的代码示例未经审核,仅用于帮助理解概念。在未进行彻底审查、测试和独立安全审计之前,请勿将其用于生产环境。
ERC-3643 解决的问题
在深入探讨之前,让我们先明确一下背景。标准的 ERC-20 代币是无许可的。任何人都可以发送它,任何人都可以接收它,任何地址都可以持有它。这是像 DAI 或 UNI.
然而,在处理代币化证券(公司股份)或分权化房地产等现实世界资产时,这便成为了一个问题。现实世界的监管规定要求必须明确资产的持有者,并根据以下情况限制资产转让:
- 身份:持有人是否已通过KYC认证?
- 管辖权:收件人是否位于受制裁的国家?
- 资格认定:持有人是否属于有权购买该证券的“合格投资者”?
- 锁定期:该资产是否处于“锁定期”,在此期间无法出售?
ERC-3643(最初称为T-REX,即“受监管交易所代币”)的开发旨在将这些规则落实到链上。它是一套智能合约,可使符合 ERC-20 标准的代币具备“合规意识”。
标准要求
从宏观层面来看,ERC-3643 的实现应提供:
-
兼容性与表面
- 保持与ERC-20 兼容
- 提供一个用于转账的标准预检查(
canTransfer) - 支持受监管流程所需的批处理操作和管理功能
-
身份与合规
- 集成一个链上身份识别系统(例如ONCHAINID)
- 在转账、铸造和销毁过程中使用合规层
-
控制与操作
- 支持暂停和冻结(全局、地址级和部分冻结)
- 对于敏感操作(强制转账、恢复、铸造/销毁),请使用EIP-173 所有权机制结合代理角色
ERC-3643 架构
ERC-3643 采用的是模块化设计。它没有将所有内容都放在一个单一的代币合约中,而是将职责分配给多个合约。
简而言之,核心组件包括:
| 组件 | 摘要 |
|---|---|
| ONCHAINID | 一个独立的身份合约(基于 ERC-734/735),用于存储由可信发行方签发的、关于用户的可验证声明。 |
| 可信发行人注册库 | 一份可信地址列表(例如 KYC 服务提供商),这些地址被授权添加有关用户身份的声明 |
| 索赔主题登记册 | 必填声明类型目录(例如:已通过KYC验证、合格投资者)。 |
| 身份注册表 | 将钱包与 ONCHAINID 和国家/地区进行映射,并提供如下验证功能: isVerified. |
| 合规合同 | 编码了报价和转让规则,在转让前后进行查询。 |
| 许可型代币(T-REX) | 一种符合 ERC-20 标准的代币。它包含对身份注册表和合规合约的引用,并在进行任何转账操作前会查询这些合约。 |
ONCHAINID
ONCHAINID是一个通用身份合约,用于记录密钥和可验证的声明。
索赔的示例包括:
- 地址 X 已通过服务商 Y 的 KYC 认证
- 该投资者在Z司法管辖区已获得认证
这些声明由可信发行方(如KYC服务提供商或受监管机构)发布。该合约不会直接存储个人身份信息(PII),而是出于隐私保护的考虑,仅存储引用和哈希值。
多个钱包可以关联到同一个底层身份,在代码中,该身份通常被称为 IIdentity. ONCHAINID 充当 合规相关事实的依据.
可信发行人注册库
“可信发行方注册表”用于记录针对特定代币哪些声明发行方被视为可信,以及每个发行方可签署哪些声明主题。
例如,你可能会信任:
- 某特定KYC服务提供商
KYC_已验证主题。 - 受监管实体,适用于
合格投资者.
只有这些发行方提出的声明才会被视为该代币合规性核查的有效依据。
索赔主题登记册
“声明主题注册表”定义了哪些声明类型对持有该代币至关重要,例如:
KYC_已验证合格投资者NOT_IN_RESTRICTED_COUNTRY未列入制裁名单
与……一起 可信发行人注册库, 它定义了 哪些主张是必需的 以及 哪些机构可以签发这些文件.
身份注册表
身份注册处是代币持有者的“守门人”:
- 存储每个白名单钱包的 ONCHAINID
- 揭露
isVerified(address) - 确保只有经过身份验证的用户才能保留注册状态
它通常依赖于 身份注册表存储 因此,多个代币可以共享同一份白名单,同时保留各自专属的注册表。完成KYC认证后,投资者将ONCHAINID进行绑定或部署,从可信发行方处获取所需的声明,并以其钱包注册国家代码(ISO-3166数字格式)。
合规合同
合规合同中规定了发行规则和转让限制。例如:
- 每个国家/地区的投资者上限。
- 每位投资者的最大仓位规模。
- 管辖权允许/拒绝列表。
- 基于时间的规则,例如锁定期和归属期。
标准接口表面:
函数 canTransfer(地址 from, 地址 至, uint256 金额) 外部 视图 返回 (bool);
函数 已转移(地址 from, 地址 至, uint256 金额) external;
函数 创建(地址 to, uint256 金额) external;
函数 被销毁(地址 from, uint256 金额) external;
代币合约使用合规合约来:
- 预先检查转账是否被允许(
canTransfer). - 在转账、铸造和销毁操作完成后通知合规逻辑(
已转账,创建,被摧毁),以便保持内部状态(大写标志、计数器等)的同步。
许可型代币(T-REX)
该代币实现了标准的 ERC-20 功能,并持有对 IdentityRegistry 以及 合规 合约。它不会盲目发送代币,而是让每次转账都经过几项额外检查,以确保该操作被允许。它通过以下方式封装转账:
- 暂停/冻结检查
- 合规检查
- 数据传输后回调(
已转账)
常见的扩展包括:
- 强制转账和代币找回
- 暂停/冻结(全局和按地址)
- 部分冻结(仅部分余额可动用)
- 批处理操作
RWA 代币转账的运作原理
现在,让我们一步一步地看看 ERC-3643 代币转账是如何进行的。
步骤 1:用户发起转账
一位用户(爱丽丝)调用了该令牌的 转账 或 transferFrom 像往常一样操作,将代币转账至新地址(Bob):
token.transfer(bob.地址, 金额);
代币.transferFrom(from, bob.地址, 金额); // 如果使用了津贴
第 2 步:资格审核
该 代币 合约不会转移代币。它首先通过 身份注册表. 其逻辑通常如下:
- 检查该代币是否处于暂停状态,或者这两个地址中是否有任何一个被冻结
- 确保接收器处于 已注册 并且在
IdentityRegistry通过拨打isVerified()函数
在许多实现中,该令牌会调用
identityRegistry.isVerified(...)直接调用,然后调用compliance.canTransfer(...). 在其他情况下,资格检查被封装在canTransfer本身。关键在于两者都 资格 以及 合规规则 在任何余额发生变动之前,这些规则都会被执行。
第3步:合规性评估
资格审查结束后, 代币 合同进入第二项核查: compliance.canTransfer(alice.address, bob.address, amount).
该 合规 合约会检查其模块化逻辑,以确定该转账是否被允许且未违反任何规则。
合规合同可能会评估以下规则:
- 全球上限(例如,每个司法管辖区的持有人数量)
- 每位投资者和每个国家的持仓限额
- 时间窗口、锁定期和归属期
- 为该令牌配置的任何自定义模块
如果违反了任何规则, canTransfer 返回值 false 此时令牌应恢复原状。
第 4 步:转移
如果所有检查均通过,则该转账将像普通的 ERC-20 转账一样执行。然后,该 代币 呼叫 compliance.transferred(from, to, amount) 以更新合规状态,以便日后进行核查。
在Mint上,该 代币 呼叫 compliance.created(to, amount),并在刻录时调用 compliance.destroyed(from, amount) 因此,合规引擎也会跟踪供应方面的变化。
由于索赔由可信的签发方签署并通过 ONCHAINID 存储,因此您可以保留一份可验证的资格审核记录。
您拥有可审计的记录:
- 谁在什么时候持有什么
- 哪些索赔和规则允许进行转让
- 注册表和合规模块随时间推移的治理变更
角色与权限
ERC-3643 将所有者(根据 EIP-173)与能够执行敏感功能的代理地址区分开来。代理角色已标准化:
interface IAgentRole {
event AgentAdded(address indexed agent);
event AgentRemoved(address indexed agent);
function addAgent(address agent) external;
function removeAgent(address agent) external;
function isAgent(address agent) external view returns (bool);
}
所有者负责管理代理和高层次配置。代理根据具体实现情况执行冻结、强制转账、铸造/销毁以及批量操作等操作。
面向开发者的核心接口
ERC-3643 实现中体现的关键标准化接口:
- ERC3643 代币
- 身份注册表
- 身份注册表存储
- 合规
- 可信发行方
- 理赔主题
interface IERC3643 is IERC20 {
// getters
function onchainID() external view returns (address);
function identityRegistry() external view returns (IIdentityRegistry);
function compliance() external view returns (ICompliance);
function paused() external view returns (bool);
function isFrozen(address user) external view returns (bool);
function getFrozenTokens(address user) external view returns (uint256);
// admin
function pause() external;
function unpause() external;
function setAddressFrozen(address user, bool freeze) external;
function freezePartialTokens(address user, uint256 amount) external;
function unfreezePartialTokens(address user, uint256 amount) external;
function setIdentityRegistry(address identityRegistry) external;
function setCompliance(address compliance) external;
// lifecycle
function forcedTransfer(address from, address to, uint256 amount) external returns (bool);
function mint(address to, uint256 amount) external;
function burn(address user, uint256 amount) external;
function recoveryAddress(address lostWallet, address newWallet, address investorOnchainID) external returns (bool);
// batch helpers
function batchTransfer(address[] calldata toList, uint256[] calldata amounts) external;
}
interface IIdentityRegistry {
// wiring
function setIdentityRegistryStorage(address storageAddr) external;
function setClaimTopicsRegistry(address topics) external;
function setTrustedIssuersRegistry(address issuers) external;
// actions
function registerIdentity(address user, IIdentity id, uint16 country) external;
function deleteIdentity(address user) external;
function updateCountry(address user, uint16 country) external;
function updateIdentity(address user, IIdentity id) external;
// reads
function isVerified(address user) external view returns (bool);
function identity(address user) external view returns (IIdentity);
function investorCountry(address user) external view returns (uint16);
}
interface IIdentityRegistryStorage {
function storedIdentity(address user) external view returns (IIdentity);
function storedInvestorCountry(address user) external view returns (uint16);
function addIdentityToStorage(address user, IIdentity id, uint16 country) external;
function removeIdentityFromStorage(address user) external;
function modifyStoredInvestorCountry(address user, uint16 country) external;
function modifyStoredIdentity(address user, IIdentity id) external;
function bindIdentityRegistry(address identityRegistry) external;
function unbindIdentityRegistry(address identityRegistry) external;
function linkedIdentityRegistries() external view returns (address[] memory);
}
interface ICompliance {
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;
}
interface ITrustedIssuersRegistry {
function addTrustedIssuer(IClaimIssuer issuer, uint[] calldata claimTopics) external;
function removeTrustedIssuer(IClaimIssuer issuer) external;
function updateIssuerClaimTopics(IClaimIssuer issuer, uint[] calldata claimTopics) external;
function getTrustedIssuers() external view returns (IClaimIssuer[] memory);
function isTrustedIssuer(address issuer) external view returns (bool);
function getTrustedIssuerClaimTopics(IClaimIssuer issuer) external view returns (uint[] memory);
function getTrustedIssuersForClaimTopic(uint256 claimTopic) external view returns (IClaimIssuer[] memory);
function hasClaimTopic(address issuer, uint claimTopic) external view returns (bool);
}
interface IClaimTopicsRegistry {
function addClaimTopic(uint256 claimTopic) external;
function removeClaimTopic(uint256 claimTopic) external;
function getClaimTopics() external view returns (uint256[] memory);
}
设计回顾
ERC-3643 代币的部署至少包括以下内容:
- 部署注册表:
索赔主题注册表,可信发行人注册表,身份注册表存储,IdentityRegistry. - 配置主题和发行方:添加声明主题以及受信任的发行方及其支持的主题。
- 将存储绑定到身份注册表,并在身份注册表上设置注册表。
- 部署
合规并在部署后将其与令牌绑定。 - 将令牌部署到以下位置:
IdentityRegistry以及合规. - 设置“所有者”和“代理”角色,并将敏感功能限制为仅代理可使用。
- 登记投资者身份(
地址,ONCHAINID,国家) 在身份注册表中。 - 使用
薄荷分配供应量并确保执行铸造/销毁操作创建/销毁.
常见用例
- 代币化的私募基金/风险投资/私募股权:仅限合格投资者或机构投资者参与
- 证券型代币发行(STO):执行管辖范围限制、持有人上限、锁定期等规定。
- 房地产通证化:确保只有符合条件的投资者才能持有房产的份额
- 受监管的交易场所:构建合规的二级市场,确保每笔买单和卖单都在链上经过验证
由于 ERC-3643 代币仍与 ERC-20 兼容,因此只要相关平台能够处理该资产的许可制特性(例如,处理因合规要求导致的转账撤销),它们即可与钱包、托管方、交易所(在允许的情况下,包括中心化或去中心化交易所)以及投资组合和分析工具进行集成。
接下来会发生什么
在此,您可以:
-
阅读完整的ERC-3643 白皮书和规范
深入探讨超出本概述范围的边缘案例,例如“交割对交割”(DvD)、交易所集成以及可升级性模式。
-
探索参考实现
克隆ERC-3643 / T-REX 代码库,将注册表和代币部署到本地或测试网环境中,并尝试进行符合规范与不符合规范的转账操作。
-
制作一个简单的 RWA 用例原型
例如:
- 一种代币化的私募基金,仅允许来自特定司法管辖区的合格投资者持有该代币
- 一种设有锁定期和转让窗口的代币化房地产资产
使用 ERC-3643 将这些规则直接编码到代币中。
-
构建监控和合规工具
使用 Quicknode Streams 订阅:
转账ERC-3643 代币的事件- 来自合规模块的自定义事件(例如:违规行为、强制转移)
然后将这些数据导入仪表盘、警报系统或内部合规工具。
在探索这些后续步骤的过程中,您将更清楚地了解 ERC-3643 如何融入您自己的实物资产(RWA)或证券型代币发展路线图。
结论
在本指南中,您了解了 ERC-3643 如何在熟悉的 ERC-20 模型基础上,叠加链上身份识别、资格核查以及模块化合规规则,从而支持受监管的许可型资产。
如果您正在以太坊或其他 EVM 链上构建 RWA 或安全代币基础设施,ERC-3643 提供了一个开源基础架构(T-REX),让您无需从头开始重新构建合规方案。结合 Quicknode 提供的可靠基础设施,您可以设计、部署和监控符合现实世界监管框架的许可型代币。
订阅我们的新闻通讯,获取更多文章和指南。您可以通过Twitter提供反馈,或加入我们的Discord社区服务器。
我们 ❤️ 您的反馈!
如果您有任何反馈或对新主题的建议,请告诉我们。我们非常期待您的来信。