前言本文梳理了ERC-3643标准的相关理论知识,并通过代码案例演示其实际应用。案例采用极简实现方案,未依赖任何第三方工具,同时也补充说明:生态中已有成熟库可直接复用(如T-REXProtocol)。此外,本文还将围绕该极简实现的合约,完整介绍基于HardhatV3的开发、测试与部
本文梳理了 ERC-3643 标准的相关理论知识,并通过代码案例演示其实际应用。案例采用极简实现方案,未依赖任何第三方工具,同时也补充说明:生态中已有成熟库可直接复用(如 T-REX Protocol)。此外,本文还将围绕该极简实现的合约,完整介绍基于 Hardhat V3 的开发、测试与部署全流程,为开发者提供可落地的实操指南。
ERC-3643标准简介
ERC-3643 是以太坊生态中面向合规证券代币和现实世界资产(RWA)代币化的核心标准,由 ERC-3643 协会治理,是在 ERC-20 代币标准基础上的合规增强版,专为满足传统金融监管要求设计,同时兼容 EVM(以太坊虚拟机)和 OnchainID 去中心化身份系统;
核心功能与特性
- 投资者身份校验:集成 KYC/AML(反洗钱 / 客户身份识别)验证接口,通过
isValidSender、isValidRecipient等方法校验交易双方是否为合规投资者,支持地址黑白名单管理。- 资产管控能力:新增代币冻结(
freeze)、强制转移(forceTransfer)、销毁(destroy)、批量操作等接口,满足监管机构对异常资产的处置需求。- 交易限制与审计:支持对代币转移额度、交易频率进行限制,同时提供可追溯的审计日志,方便监管机构核查。
- 兼容性:完全兼容 ERC-20 的核心接口,现有 ERC-20 生态工具可无缝适配,降低开发和迁移成本。
适用场景
- 证券、债券等金融衍生品的代币化发行;
- 房地产、艺术品等大额现实世界资产(RWA)的链上确权与交易;
- 需合规监管的私募股权、基金份额代币化。
核心价值
将传统金融的合规监管逻辑映射到区块链上,既保留了代币化的高效性,又解决了监管机构对资产溯源、投资者资质审核的核心需求,是连接传统金融与 Web3 的重要标准之一。
智能合约开发、测试、部署案例
智能合约
- 注册系统合约
// contracts/IdentityRegistry.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/AccessControl.sol";
contract IdentityRegistry is AccessControl { bytes32 public constant AGENT_ROLE = keccak256("AGENT_ROLE");
// 存储地址到身份的映射
mapping(address => address) public identity;
mapping(address => bool) public isVerified;
mapping(address => string) public investorCountry;
event IdentityRegistered(address indexed wallet, address indexed identityContract);
event IdentityRemoved(address indexed wallet);
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(AGENT_ROLE, msg.sender);
}
// 注册身份
function registerIdentity(
address _wallet,
address _identity,
string memory _country
) external onlyRole(AGENT_ROLE) {
require(_wallet != address(0), "Invalid wallet");
require(_identity != address(0), "Invalid identity");
identity[_wallet] = _identity;
isVerified[_wallet] = true;
investorCountry[_wallet] = _country;
emit IdentityRegistered(_wallet, _identity);
}
// 移除身份
function removeIdentity(address _wallet) external onlyRole(AGENT_ROLE) {
delete identity[_wallet];
delete isVerified[_wallet];
delete investorCountry[_wallet];
emit IdentityRemoved(_wallet);
}
// 验证身份
function verifyIdentity(address _wallet) external view returns (bool) {
return isVerified[_wallet];
}
}
* **合规合约**
// contracts/ModularCompliance.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/AccessControl.sol";
contract ModularCompliance is AccessControl { bytes32 public constant AGENT_ROLE = keccak256("AGENT_ROLE");
// 合规规则
mapping(string => bool) public allowedCountries;
mapping(address => bool) public frozenAddresses;
bool public complianceEnabled = true;
event CountryAllowed(string country);
event CountryRestricted(string country);
event AddressFrozen(address indexed addr);
event AddressUnfrozen(address indexed addr);
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(AGENT_ROLE, msg.sender);
}
// 设置允许的国家
function allowCountry(string memory _country) external onlyRole(AGENT_ROLE) {
allowedCountries[_country] = true;
emit CountryAllowed(_country);
}
function restrictCountry(string memory _country) external onlyRole(AGENT_ROLE) {
allowedCountries[_country] = false;
emit CountryRestricted(_country);
}
// 冻结地址
function freezeAddress(address _addr) external onlyRole(AGENT_ROLE) {
frozenAddresses[_addr] = true;
emit AddressFrozen(_addr);
}
function unfreezeAddress(address _addr) external onlyRole(AGENT_ROLE) {
frozenAddresses[_addr] = false;
emit AddressUnfrozen(_addr);
}
// 合规检查
function canTransfer(
address _from,
address _to,
string memory _toCountry
) external view returns (bool) {
if (!complianceEnabled) return true;
// 检查地址是否被冻结
if (frozenAddresses[_from] || frozenAddresses[_to]) return false;
// 检查国家是否允许
if (!allowedCountries[_toCountry]) return false;
return true;
}
}
* **代币合约**
// contracts/SecurityToken.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/utils/Pausable.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "./IdentityRegistry.sol"; import "./ModularCompliance.sol";
contract SecurityToken is ERC20, AccessControl, Pausable, ReentrancyGuard { bytes32 public constant AGENT_ROLE = keccak256("AGENT_ROLE");
IdentityRegistry public identityRegistry;
ModularCompliance public compliance;
// 代币元数据
address public onchainID;
event IdentityRegistryUpdated(address indexed registry);
event ComplianceUpdated(address indexed compliance);
constructor(
string memory name,
string memory symbol,
uint256 initialSupply,
address _identityRegistry,
address _compliance,
address _onchainID,
address _admin
) ERC20(name, symbol) {
require(_identityRegistry != address(0), "Invalid identity registry");
require(_compliance != address(0), "Invalid compliance");
identityRegistry = IdentityRegistry(_identityRegistry);
compliance = ModularCompliance(_compliance);
onchainID = _onchainID;
// 设置角色
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
_grantRole(AGENT_ROLE, _admin);
// 铸造初始供应
_mint(_admin, initialSupply);
}
// 修改 transfer 函数以添加合规检查
function transfer(address to, uint256 amount)
public
override
whenNotPaused
nonReentrant
returns (bool)
{
// 验证发送方和接收方身份
require(identityRegistry.isVerified(msg.sender), "Sender not verified");
require(identityRegistry.isVerified(to), "Recipient not verified");
// 获取接收方国家并检查合规性
string memory toCountry = identityRegistry.investorCountry(to);
require(
compliance.canTransfer(msg.sender, to, toCountry),
"Transfer not compliant"
);
return super.transfer(to, amount);
}
// 修改 transferFrom 函数
function transferFrom(
address from,
address to,
uint256 amount
) public override whenNotPaused nonReentrant returns (bool) {
require(identityRegistry.isVerified(from), "From address not verified");
require(identityRegistry.isVerified(to), "To address not verified");
string memory toCountry = identityRegistry.investorCountry(to);
require(
compliance.canTransfer(from, to, toCountry),
"Transfer not compliant"
);
return super.transferFrom(from, to, amount);
}
// 铸币功能(仅 AGENT)
function mint(address to, uint256 amount) external onlyRole(AGENT_ROLE) {
require(identityRegistry.isVerified(to), "Recipient not verified");
_mint(to, amount);
}
// 销毁代币
function burn(uint256 amount) external {
_burn(msg.sender, amount);
}
// 暂停功能
function pause() external onlyRole(AGENT_ROLE) {
_pause();
}
function unpause() external onlyRole(AGENT_ROLE) {
_unpause();
}
// 更新身份注册表
function setIdentityRegistry(address _registry) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_registry != address(0), "Invalid registry");
identityRegistry = IdentityRegistry(_registry);
emit IdentityRegistryUpdated(_registry);
}
// 更新合规模块
function setCompliance(address _compliance) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_compliance != address(0), "Invalid compliance");
compliance = ModularCompliance(_compliance);
emit ComplianceUpdated(_compliance);
}
}
**编译指令**
npx hardhat compile
### 智能合约部署脚本
// scripts/deploy.ts import { network, artifacts } from "hardhat"; import {parseEther} from "viem" async function main() { // 连接网络 const { viem } = await network.connect({ network: network.name });//指定网络进行链接 // 获取客户端 const [deployer] = await viem.getWalletClients(); const publicClient = await viem.getPublicClient(); // 部署身份注册表 const IdentityRegistry = await artifacts.readArtifact("IdentityRegistry"); // 部署(构造函数参数:recipient, initialOwner) const IdentityRegistryHash = await deployer.deployContract({ abi: IdentityRegistry.abi,//获取abi bytecode: IdentityRegistry.bytecode,//硬编码 args: [],//process.env.RECIPIENT, process.env.OWNER }); // console.log("IdentityRegistry合约部署哈希:", IdentityRegistryHash); // 等待确认并打印地址 const IdentityRegistryReceipt = await publicClient.getTransactionReceipt({ hash: IdentityRegistryHash }); console.log("IdentityRegistry合约地址:", IdentityRegistryReceipt.contractAddress);
// 部署合规模块 const ModularCompliance = await artifacts.readArtifact("ModularCompliance");
const ModularComplianceHash = await deployer.deployContract({ abi: ModularCompliance.abi,//获取abi bytecode: ModularCompliance.bytecode,//硬编码 args: [],//process.env.RECIPIENT, process.env.OWNER }); // 等待确认并打印地址 const ModularComplianceReceipt = await publicClient.getTransactionReceipt({ hash: ModularComplianceHash }); console.log("ModularCompliance合约地址:", await ModularComplianceReceipt.contractAddress);
// 部署权限代币
const IdentityRegistryAddress = IdentityRegistryReceipt.contractAddress;
console.log("IdentityRegistryReceipt.contractAddress:", IdentityRegistryAddress);
const ModularComplianceAddress = ModularComplianceReceipt.contractAddress;
console.log("ModularComplianceReceipt.contractAddress:", ModularComplianceAddress);
const SecurityToken = await artifacts.readArtifact("SecurityToken");
const SecurityTokenHash = await deployer.deployContract({ // ← 使用 deployContract
abi: SecurityToken.abi,
bytecode: SecurityToken.bytecode,
args: [
"My Security Token",
"MST",
parseEther("1000000"),
IdentityRegistryAddress as 0x${string},
ModularComplianceAddress as 0x${string},
deployer.account.address,//"0x0000000000000000000000000000000000000000"
deployer.account.address
],
});
const SecurityTokenReceipt = await publicClient.waitForTransactionReceipt({ hash: SecurityTokenHash }); console.log("SecurityToken合约地址:", SecurityTokenReceipt.contractAddress); }
main().catch((error) => { console.error(error); process.exit(1); });
**部署指令**
npx hardhat run ./scripts/xxx.ts
### 智能合约测试脚本
import assert from "node:assert/strict";
import { describe, it,beforeEach } from "node:test";
import { formatEther,parseEther } from 'viem'
import { network } from "hardhat";
describe("Token", async function () {
let viem: any;
let publicClient: any;
let owner: any, user1: any, user2: any, user3: any;
let deployerAddress: string;
let IdentityRegistry: any;
let ModularCompliance: any;
let SecurityToken: any;
beforeEach (async function () {
const { viem } = await network.connect();
publicClient = await viem.getPublicClient();//创建一个公共客户端实例用于读取链上数据(无需私钥签名)。
[owner,user1,user2,user3] = await viem.getWalletClients();//获取第一个钱包客户端 写入联合交易
deployerAddress = owner.account.address;//钱包地址
IdentityRegistry = await viem.deployContract("IdentityRegistry");//部署合约
ModularCompliance = await viem.deployContract("ModularCompliance");//部署合约
SecurityToken = await viem.deployContract("SecurityToken", [
"My Security Token",
"MST",
parseEther("1000000"),
IdentityRegistry.address,
ModularCompliance.address,
deployerAddress,//"0x0000000000000000000000000000000000000000"
deployerAddress
]);//部署合约
// 设置合规规则:允许美国和中国投资者
console.log("\n=== 设置合规规则 ===");
await owner.writeContract({
address: ModularCompliance.address,
abi: ModularCompliance.abi,
functionName: "allowCountry",
args: ["US"],
});
console.log("✓ 允许美国(US)投资者");
await owner.writeContract({
address: ModularCompliance.address,
abi: ModularCompliance.abi,
functionName: "allowCountry",
args: ["CN"],
});
console.log("✓ 允许中国(CN)投资者");
});
it("测试IdentityRegistry", async function () {
await owner.writeContract({
address: IdentityRegistry.address,
abi: IdentityRegistry.abi,
functionName: "registerIdentity",
args: [owner.account.address,user1.account.address,"EN"],
});
await owner.writeContract({
address: IdentityRegistry.address,
abi: IdentityRegistry.abi,
functionName: "registerIdentity",
args: [user1.account.address,user1.account.address,"US"],
});
await owner.writeContract({
address: IdentityRegistry.address,
abi: IdentityRegistry.abi,
functionName: "registerIdentity",
args: [user2.account.address,user2.account.address,"CN"],
});
await owner.writeContract({
address: IdentityRegistry.address,
abi: IdentityRegistry.abi,
functionName: "registerIdentity",
args: [user3.account.address,user3.account.address,"VN"],
});
const isUser1Verified = await publicClient.readContract({
address: IdentityRegistry.address,
abi: IdentityRegistry.abi,
functionName: "verifyIdentity",
args: [user1.account.address],
});
console.log("isUser1Verified:",isUser1Verified);
const IsVerified=await publicClient.readContract({
address: IdentityRegistry.address,
abi: IdentityRegistry.abi,
functionName: "isVerified",
args: [user2.account.address],
});
console.log("isUser2Verified:",IsVerified);
const INVESTORCOUNTRY=await publicClient.readContract({
address: IdentityRegistry.address,
abi: IdentityRegistry.abi,
functionName: "investorCountry",
args: [user2.account.address],
});
console.log("INVESTORCOUNTRY:",INVESTORCOUNTRY);
//删除用户2的身份注册
// await owner.writeContract({
// address: IdentityRegistry.address,
// abi: IdentityRegistry.abi,
// functionName: "removeIdentity",
// args: [user2.account.address],
// });
// const IsVerifiedUser2=await publicClient.readContract({
// address: IdentityRegistry.address,
// abi: IdentityRegistry.abi,
// functionName: "isVerified",
// args: [user2.account.address],
// });
// console.log("删除后的:",IsVerifiedUser2);
// ModularCompliance合约
// 允许用户2的地址
const allowCountry=await owner.writeContract({
address: ModularCompliance.address,
abi: ModularCompliance.abi,
functionName: "allowCountry",
args: ["EN"],
});
console.log("allowCountry:",allowCountry);
const restrictCountry=await owner.writeContract({
address: ModularCompliance.address,
abi: ModularCompliance.abi,
functionName: "restrictCountry",
args: ["EN"],
});
console.log("restrictCountry:",restrictCountry);
const canTransfer=await owner.writeContract({
address: ModularCompliance.address,
abi: ModularCompliance.abi,
functionName: "canTransfer",
args: [owner.account.address,user1.account.address,parseEther("10")],
});
console.log("canTransfer:",canTransfer);
// 冻结用户2的地址
const freezeAddress=await owner.writeContract({
address: ModularCompliance.address,
abi: ModularCompliance.abi,
functionName: "freezeAddress",
args: [user2.account.address],
});
console.log("freezeAddress user2:",freezeAddress);
// SecurityToken合约
const name=await publicClient.readContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "name",
args: [],
});
console.log("name:",name);
const symbol=await publicClient.readContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "symbol",
args: [],
});
console.log("symbol:",symbol);
const totalSupply=await publicClient.readContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "totalSupply",
args: [],
});
console.log("totalSupply:",formatEther(totalSupply) + " MST");
await owner.writeContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "mint",
args: [user1.account.address,parseEther("1000")],
});
const balance=await publicClient.readContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "balanceOf",
args: [user1.account.address],
});
console.log("user1余额:",formatEther(balance) + " MST");
// 测试用户1转账给用户2 应该失败 因为用户2被冻结了
await assert.rejects(
user1.writeContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "transfer",
args: [user2.account.address, parseEther("50")],
}),
/Transfer not compliant/
);
//尝试测试用户1转账给用户2 应该失败 因为用户2被冻结了
const unfreezeAddress=await owner.writeContract({
address: ModularCompliance.address,
abi: ModularCompliance.abi,
functionName: "unfreezeAddress",
args: [user2.account.address],
});
console.log("unfreezeAddress user2:",unfreezeAddress);
// 测试用户1转账给用户2
await user1.writeContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "transfer",
args: [user2.account.address,parseEther("500")],
});
const balance2=await publicClient.readContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "balanceOf",
args: [user2.account.address],
});
console.log("user2余额:",formatEther(balance2) + " MST");
//应该允许用户3从用户1那里转账500个MST
await owner.writeContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "approve",
args: [user3.account.address,parseEther("500")],
});
// 测试用户3从用户1那里转账500个MST
await user3.writeContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "transferFrom",
args: [owner.account.address,user2.account.address,parseEther("200")],
});
// 测试用户2的余额是否增加200个MST
const balance3=await publicClient.readContract({
address: SecurityToken.address,
abi: SecurityToken.abi,
functionName: "balanceOf",
args: [user2.account.address],
});
console.log("user2余额:",formatEther(balance3) + " MST");
});
});
**测试指令**
npx hardhat test ./test/xxx.ts
## 总结
至此,本文围绕 ERC-3643 合规证券与 RWA 代币化标准,构建了从理论到实践的完整指南。理论上,明晰其 ERC-20 合规增强版定位、核心特性(身份校验、资产管控等)及适用场景,凸显其连接传统金融与 Web3 的价值;实践上,以极简模块化思路拆分身份注册、合规校验、代币合约三大核心模块实现标准逻辑,并基于 Hardhat V3 完整呈现合约开发、测试与部署全流程。本文既助力开发者快速掌握标准核心,也为合规代币化项目落地提供可复用的技术方案,开发者可按需替换为 T-REX 等成熟库用于生产级开发。 如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!