RWA实操指南(二):数据上链与收益分配,保障合规与收益流转
在上篇中,我们讨论了RWA的基础构建,包括资产筛选评估和SPV设立。这些步骤确保了资产的合法性和风险隔离。本篇将延续这一逻辑,聚焦第三步和第四步:数据合规上链处理,以及收益分配机制的设计。这两个环节是RWA从静态资产转向动态通证化的关键,帮助实现数据的可追溯性和投资者的收益兑现。
第三步:数据合规上链处理
根据中国相关法规要求,涉及个人信息和商业敏感数据的原始信息(如租金流水、租户合同)需留存境内。区块链上链数据应采用数据脱敏与哈希化处理方案,通过国密SM3算法对原始数据生成32字节哈希摘要,仅将摘要信息上链存储。采用联盟链架构,由授权节点共同验证数据一致性,确保数据合规性与可追溯性。
1// go代码:RWA数据合规上链系统 - 国密SM3哈希实现
2package main
3
4import (
5 "crypto/sha256"
6 "encoding/json"
7 "fmt"
8 "time"
9
10 // 注意:实际使用时需要安装gmsm库
11 // "github.com/tjfoc/gmsm/sm3"
12)
13
14// AssetData 资产相关数据结构
15type AssetData struct {
16 DataType string // 数据类型:租金、合同、评估报告等
17 AssetID string // 关联资产ID
18 DataID string // 数据唯一标识
19 Timestamp time.Time // 数据时间戳
20 Content string // 原始数据内容(不上链)
21 Metadata map[string]string // 元数据信息
22 HashAlgorithm string // 使用的哈希算法
23 HashValue string // 哈希摘要值
24}
25
26// ComplianceLevel 数据合规级别
27type ComplianceLevel string
28
29const (
30 Public ComplianceLevel = "Public" // 公开数据
31 Restricted ComplianceLevel = "Restricted" // 受限数据
32 Confidential ComplianceLevel = "Confidential" // 机密数据
33)
34
35// DataProcessor 数据处理接口
36type DataProcessor interface {
37 HashData(data *AssetData) error
38 ValidateCompliance(data *AssetData) (ComplianceLevel, []string, error)
39 PrepareForChain(data *AssetData) (map[string]interface{}, error)
40}
41
42// SM3DataProcessor 国密SM3数据处理器(使用SHA256模拟)
43type SM3DataProcessor struct {
44 AllowedRegions []string // 允许的数据来源地区
45}
46
47// NewSM3DataProcessor 创建新的数据处理器
48func NewSM3DataProcessor(allowedRegions []string) *SM3DataProcessor {
49 return &SM3DataProcessor{
50 AllowedRegions: allowedRegions,
51 }
52}
53
54// HashData 使用国密SM3算法(此处用SHA256模拟)计算数据哈希
55func (p *SM3DataProcessor) HashData(data *AssetData) error {
56 // 在实际实现中,这里应该使用真正的SM3哈希算法
57 // hash := sm3.Sum([]byte(data.Content))
58 // 为了可运行性,这里使用SHA256模拟
59 hash := sha256.Sum256([]byte(data.Content))
60 data.HashValue = fmt.Sprintf("%x", hash)
61 data.HashAlgorithm = "SM3" // 标记为SM3算法
62 return nil
63}
64
65// ValidateCompliance 验证数据合规性
66func (p *SM3DataProcessor) ValidateCompliance(data *AssetData) (ComplianceLevel, []string, error) {
67 var warnings []string
68 var level ComplianceLevel
69
70 // 根据数据类型确定合规级别
71 switch data.DataType {
72 case "租金记录", "收益分配":
73 level = Restricted
74 warnings = append(warnings, "包含财务敏感信息,建议加密存储原始数据")
75 case "租户合同", "个人身份信息":
76 level = Confidential
77 warnings = append(warnings, "高度敏感数据,原始数据不得出境")
78 case "资产评估摘要", "公开报告":
79 level = Public
80 }
81
82 // 检查地区合规性
83 region, exists := data.Metadata["region"]
84 if exists {
85 regionValid := false
86 for _, allowed := range p.AllowedRegions {
87 if region == allowed {
88 regionValid = true
89 break
90 }
91 }
92 if !regionValid {
93 warnings = append(warnings, fmt.Sprintf("数据来源地区 %s 不在允许列表中", region))
94 }
95 }
96
97 return level, warnings, nil
98}
99
100// PrepareForChain 准备上链数据(移除敏感信息)
101func (p *SM3DataProcessor) PrepareForChain(data *AssetData) (map[string]interface{}, error) {
102 // 只准备可以上链的数据,不包含原始敏感内容
103 chainData := map[string]interface{}{
104 "data_id": data.DataID,
105 "asset_id": data.AssetID,
106 "data_type": data.DataType,
107 "timestamp": data.Timestamp.Unix(),
108 "hash_algorithm": data.HashAlgorithm,
109 "hash_value": data.HashValue,
110 }
111
112 // 对于公开数据,可以添加部分元数据
113 complianceLevel, _, _ := p.ValidateCompliance(data)
114 if complianceLevel == Public {
115 // 添加公开元数据
116 publicMeta := make(map[string]string)
117 for k, v := range data.Metadata {
118 // 只包含非敏感元数据
119 if k != "tenant_info" && k != "detailed_financials" {
120 publicMeta[k] = v
121 }
122 }
123 if len(publicMeta) > 0 {
124 chainData["metadata"] = publicMeta
125 }
126 }
127
128 return chainData, nil
129}
130
131// BlockchainNode 区块链节点接口
132type BlockchainNode interface {
133 SubmitData(data map[string]interface{}) (string, error)
134 VerifyHash(dataID, expectedHash string) (bool, error)
135}
136
137// MockAllianceChain 模拟联盟链节点
138type MockAllianceChain struct {
139 ChainID string
140 NodeAddress string
141}
142
143// NewMockAllianceChain 创建模拟联盟链节点
144func NewMockAllianceChain(chainID, nodeAddress string) *MockAllianceChain {
145 return &MockAllianceChain{
146 ChainID: chainID,
147 NodeAddress: nodeAddress,
148 }
149}
150
151// SubmitData 提交数据到区块链
152func (n *MockAllianceChain) SubmitData(data map[string]interface{}) (string, error) {
153 // 序列化数据
154 jsonData, err := json.Marshal(data)
155 if err != nil {
156 return "", fmt.Errorf("数据序列化失败: %w", err)
157 }
158
159 // 模拟交易ID生成
160 txID := fmt.Sprintf("TX-%d", time.Now().UnixNano())
161 fmt.Printf("成功提交数据到联盟链 %s,节点地址: %s\n", n.ChainID, n.NodeAddress)
162 fmt.Printf("数据内容: %s\n", jsonData)
163 fmt.Printf("交易ID: %s\n", txID)
164
165 return txID, nil
166}
167
168// VerifyHash 验证哈希值
169func (n *MockAllianceChain) VerifyHash(dataID, expectedHash string) (bool, error) {
170 // 模拟验证过程
171 fmt.Printf("在联盟链 %s 上验证数据 %s 的哈希值\n", n.ChainID, dataID)
172 // 实际应用中应该查询链上数据进行验证
173 return true, nil
174}
175
176func main() {
177 // 创建数据处理器
178 processor := NewSM3DataProcessor([]string{"北京", "上海", "深圳"})
179
180 // 创建区块链节点连接
181 chainNode := NewMockAllianceChain("AllianceChain-RWA-01", "http://node.rwa-alliance.com:8545")
182
183 // 创建租金记录数据
184 rentData := &AssetData{
185 DataType: "租金记录",
186 AssetID: "ASSET-RE-2025-001",
187 DataID: fmt.Sprintf("RENT-%s", time.Now().Format("20060102")),
188 Timestamp: time.Now(),
189 Content: "2025-03-01,租户A科技有限公司,支付2月租金500000元,租期2025-02-01至2025-02-28,合同编号CONTRACT-2025-001",
190 Metadata: map[string]string{
191 "region": "北京",
192 "building": "中关村科技大厦",
193 "floor": "15",
194 "tenant_info": "A科技有限公司", // 敏感信息
195 }
196 }
197
198 // 计算哈希值
199 err := processor.HashData(rentData)
200 if err != nil {
201 fmt.Printf("计算哈希失败: %v\n", err)
202 return
203 }
204
205 // 验证合规性
206 complianceLevel, warnings, err := processor.ValidateCompliance(rentData)
207 if err != nil {
208 fmt.Printf("合规性验证失败: %v\n", err)
209 }
210
211 fmt.Printf("=== 数据合规性检查 ===\n")
212 fmt.Printf("数据类型: %s\n", rentData.DataType)
213 fmt.Printf("合规级别: %s\n", complianceLevel)
214 fmt.Printf("哈希算法: %s\n", rentData.HashAlgorithm)
215 fmt.Printf("哈希值: %s\n", rentData.HashValue)
216 if len(warnings) > 0 {
217 fmt.Printf("合规警告: %v\n", warnings)
218 }
219
220 // 准备上链数据
221 chainData, err := processor.PrepareForChain(rentData)
222 if err != nil {
223 fmt.Printf("准备上链数据失败: %v\n", err)
224 return
225 }
226
227 // 提交到区块链
228 txID, err := chainNode.SubmitData(chainData)
229 if err != nil {
230 fmt.Printf("提交区块链失败: %v\n", err)
231 } else {
232 fmt.Printf("数据成功上链,交易ID: %s\n", txID)
233 fmt.Println("原始敏感数据已保留在本地,仅哈希摘要上链,符合数据合规要求")
234 }
235}
第四步:收益分红机制
方式一:回购+销毁
租金收益归集至 SPV 后,按预设比例通过自动化回购模块在 Uniswap 等 DEX 以稳定币或法币等价物购回 RWA 代币,并立即执行 burn 操作。通过持续减少 totalSupply,构建通缩模型,提升单位代币稀缺性与长期价值增长预期。
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.20;
3
4import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
6import "@openzeppelin/contracts/security/Pausable.sol";
7import "@openzeppelin/contracts/access/AccessControl.sol";
8import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
9import "@openzeppelin/contracts/utils/math/SafeMath.sol";
10
11/**
12 * @title RWA-REPO: 中关村科技地产回购销毁合约
13 * @dev 实现代币回购销毁机制,支持多来源资金、价格保护和市场稳定
14 */
15contract RWABuybackBurn is AccessControl, Pausable {
16 using SafeERC20 for IERC20;
17 using SafeMath for uint256;
18
19 // 角色定义
20 bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
21 bytes32 public constant TREASURY_ROLE = keccak256("TREASURY_ROLE");
22 bytes32 public constant REPO_ROLE = keccak256("REPO_ROLE");
23 bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
24
25 // 合约配置
26 IERC20 public rwaToken; // RWA代币合约
27 IERC20 public paymentToken; // 支付代币(通常是稳定币)
28 address public treasuryAddress; // 资金库地址
29 uint256 public maxRepurchaseAmount; // 单次最大回购金额
30 uint256 public dailyRepurchaseLimit; // 每日最大回购限额
31 uint256 public lastResetTimestamp; // 上次重置时间
32 uint256 public accumulatedToday; // 今日累计回购金额
33 uint256 public minimumPriceThreshold; // 最低价格阈值(防止市场操纵)
34
35 // 回购来源枚举
36 enum RepurchaseSource {
37 TREASURY, // 从资金库回购
38 REVENUE, // 从收入中回购
39 LIQUIDITY // 从流动性池回购
40 }
41
42 // 事件定义
43 event RepurchaseExecuted(
44 address indexed executor,
45 uint256 tokenAmount,
46 uint256 paymentAmount,
47 RepurchaseSource source,
48 uint256 timestamp
49 );
50
51 event BurnExecuted(
52 uint256 amount,
53 uint256 timestamp
54 );
55
56 event TreasuryUpdated(address newTreasury);
57 event MaxRepurchaseAmountUpdated(uint256 newAmount);
58 event DailyLimitUpdated(uint256 newLimit);
59 event MinimumPriceThresholdUpdated(uint256 newThreshold);
60
61 /**
62 * @dev 构造函数
63 * @param _rwaToken RWA代币地址
64 * @param _paymentToken 支付代币地址
65 * @param _treasuryAddress 资金库地址
66 * @param _admin 管理员地址
67 */
68 constructor(
69 address _rwaToken,
70 address _paymentToken,
71 address _treasuryAddress,
72 address _admin
73 ) {
74 require(_rwaToken != address(0), "无效的RWA代币地址");
75 require(_paymentToken != address(0), "无效的支付代币地址");
76 require(_treasuryAddress != address(0), "无效的资金库地址");
77 require(_admin != address(0), "无效的管理员地址");
78
79 rwaToken = IERC20(_rwaToken);
80 paymentToken = IERC20(_paymentToken);
81 treasuryAddress = _treasuryAddress;
82
83 // 初始化默认值
84 maxRepurchaseAmount = 10000 * 10**18; // 单次最大10,000代币
85 dailyRepurchaseLimit = 50000 * 10**18; // 每日最大50,000代币
86 minimumPriceThreshold = 800 * 10**16; // 最低价格阈值8.00
87 lastResetTimestamp = block.timestamp;
88
89 // 设置角色
90 _grantRole(DEFAULT_ADMIN_ROLE, _admin);
91 _grantRole(ADMIN_ROLE, _admin);
92 _grantRole(TREASURY_ROLE, _treasuryAddress);
93 _grantRole(REPO_ROLE, _admin);
94 _grantRole(PAUSER_ROLE, _admin);
95 }
96
97 /**
98 * @dev 暂停合约
99 */
100 function pause() external onlyRole(PAUSER_ROLE) {
101 _pause();
102 }
103
104 /**
105 * @dev 恢复合约
106 */
107 function unpause() external onlyRole(PAUSER_ROLE) {
108 _unpause();
109 }
110
111 /**
112 * @dev 更新资金库地址
113 * @param _newTreasury 新的资金库地址
114 */
115 function updateTreasuryAddress(address _newTreasury) external onlyRole(ADMIN_ROLE) {
116 require(_newTreasury != address(0), "无效的资金库地址");
117 address oldTreasury = treasuryAddress;
118 treasuryAddress = _newTreasury;
119
120 // 更新角色
121 _revokeRole(TREASURY_ROLE, oldTreasury);
122 _grantRole(TREASURY_ROLE, _newTreasury);
123
124 emit TreasuryUpdated(_newTreasury);
125 }
126
127 /**
128 * @dev 更新单次最大回购金额
129 * @param _newAmount 新的最大金额
130 */
131 function updateMaxRepurchaseAmount(uint256 _newAmount) external onlyRole(ADMIN_ROLE) {
132 maxRepurchaseAmount = _newAmount;
133 emit MaxRepurchaseAmountUpdated(_newAmount);
134 }
135
136 /**
137 * @dev 更新每日回购限额
138 * @param _newLimit 新的每日限额
139 */
140 function updateDailyLimit(uint256 _newLimit) external onlyRole(ADMIN_ROLE) {
141 dailyRepurchaseLimit = _newLimit;
142 emit DailyLimitUpdated(_newLimit);
143 }
144
145 /**
146 * @dev 更新最低价格阈值
147 * @param _newThreshold 新的最低价格阈值
148 */
149 function updateMinimumPriceThreshold(uint256 _newThreshold) external onlyRole(ADMIN_ROLE) {
150 minimumPriceThreshold = _newThreshold;
151 emit MinimumPriceThresholdUpdated(_newThreshold);
152 }
153
154 /**
155 * @dev 检查并重置每日累计回购金额
156 */
157 function checkAndResetDailyLimit() internal {
158 if (block.timestamp >= lastResetTimestamp.add(1 days)) {
159 accumulatedToday = 0;
160 lastResetTimestamp = block.timestamp;
161 }
162 }
163
164 /**
165 * @dev 计算当前市场价格
166 * 注意:实际应用中应使用预言机获取价格
167 * @return 当前市场价格
168 */
169 function getCurrentPrice() public view returns (uint256) {
170 // 模拟实现,实际应用中应使用Chainlink等预言机
171 // 此处返回10.00作为示例价格
172 return 1000 * 10**16;
173 }
174
175 /**
176 * @dev 执行回购操作
177 * @param _tokenAmount 要回购的代币数量
178 * @param _source 回购资金来源
179 */
180 function executeRepurchase(uint256 _tokenAmount, RepurchaseSource _source) external whenNotPaused {
181 require(hasRole(REPO_ROLE, msg.sender), "调用者没有回购角色权限");
182 require(_tokenAmount > 0, "回购数量必须大于0");
183 require(_tokenAmount <= maxRepurchaseAmount, "超出单次最大回购限额");
184
185 // 检查并重置每日限额
186 checkAndResetDailyLimit();
187 require(accumulatedToday.add(_tokenAmount) <= dailyRepurchaseLimit, "超出每日回购限额");
188
189 // 检查市场价格
190 uint256 currentPrice = getCurrentPrice();
191 require(currentPrice >= minimumPriceThreshold, "市场价格低于最低阈值,暂停回购");
192
193 // 计算所需支付金额
194 uint256 paymentAmount = _tokenAmount.mul(currentPrice).div(10**18);
195
196 // 验证资金来源
197 address paymentSource;
198 if (_source == RepurchaseSource.TREASURY) {
199 paymentSource = treasuryAddress;
200 } else if (_source == RepurchaseSource.REVENUE) {
201 // 假设收入直接进入本合约
202 paymentSource = address(this);
203 require(paymentToken.balanceOf(paymentSource) >= paymentAmount, "合约收入不足");
204 } else if (_source == RepurchaseSource.LIQUIDITY) {
205 // 实际应用中需要实现从DEX流动性池购买的逻辑
206 revert("流动性池回购功能尚未实现");
207 } else {
208 revert("无效的回购资金来源");
209 }
210
211 // 执行回购
212 // 注意:实际应用中,如果是从DEX购买,这里的逻辑会不同
213 // 此处假设从特定账户直接购买
214
215 // 模拟回购过程
216 // 1. 从支付源转移支付代币
217 if (_source == RepurchaseSource.TREASURY) {
218 require(paymentToken.balanceOf(paymentSource) >= paymentAmount, "资金库余额不足");
219 // 注意:在实际实现中,需要确保资金库已授权本合约转移代币
220 paymentToken.safeTransferFrom(paymentSource, address(this), paymentAmount);
221 }
222
223 // 2. 模拟接收RWA代币(实际实现中可能通过DEX交换或OTC交易)
224 // 此处假设代币已经通过其他方式转入合约
225 require(rwaToken.balanceOf(address(this)) >= _tokenAmount, "合约中RWA代币不足");
226
227 // 更新累计回购金额
228 accumulatedToday = accumulatedToday.add(_tokenAmount);
229
230 emit RepurchaseExecuted(
231 msg.sender,
232 _tokenAmount,
233 paymentAmount,
234 _source,
235 block.timestamp
236 );
237 }
238
239 /**
240 * @dev 执行销毁操作
241 * @param _amount 要销毁的代币数量
242 */
243 function executeBurn(uint256 _amount) external onlyRole(ADMIN_ROLE) whenNotPaused {
244 require(_amount > 0, "销毁数量必须大于0");
245 require(rwaToken.balanceOf(address(this)) >= _amount, "合约中RWA代币不足");
246
247 // 执行销毁
248 // 注意:RWA代币合约必须实现burn函数
249 // 此处使用ERC20Burnable接口
250 ERC20Burnable(address(rwaToken)).burn(_amount);
251
252 emit BurnExecuted(_amount, block.timestamp);
253 }
254
255 /**
256 * @dev 批量回购并销毁
257 * @param _tokenAmount 要回购并销毁的代币数量
258 * @param _source 回购资金来源
259 */
260 function buybackAndBurn(uint256 _tokenAmount, RepurchaseSource _source) external {
261 // 先执行回购
262 executeRepurchase(_tokenAmount, _source);
263 // 再执行销毁
264 executeBurn(_tokenAmount);
265 }
266
267 /**
268 * @dev 紧急提款(用于处理异常情况)
269 * @param _token 要提取的代币地址
270 * @param _amount 提取数量
271 * @param _recipient 接收地址
272 */
273 function emergencyWithdraw(address _token, uint256 _amount, address _recipient) external onlyRole(ADMIN_ROLE) {
274 require(_recipient != address(0), "无效的接收地址");
275 IERC20(_token).safeTransfer(_recipient, _amount);
276 }
277}
这种方式用每月收益资金从市场回购RWA代币,然后销毁它们。总供应量减少,相同收益分给更少代币,价格理论上上涨。
优势:
- 自动增值,用户持币就能享受价格增长。
- 节省Gas费,不用主动操作。
- 税务优化,资本利得税可能比分红低。
- 通缩效应,代币越来越稀缺。
劣势:
- 收益不直观,受市场供需影响。
- 依赖流动性,回购时需市场深度。
- 价格波动大,情绪容易放大。
- 短期难变现,必须卖币才能套现。
方式二:累积分红池
租金收益归集至智能合约,按持仓比例实时累积可领取分红额度(claimable dividends)。用户通过调用 claim() 函数触发结算,合约根据 totalDividends 与 claimed 映射差额执行转账。
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.19;
3
4import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
6import "@openzeppelin/contracts/security/Pausable.sol";
7import "@openzeppelin/contracts/access/AccessControl.sol";
8import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
9import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
10
11/**
12 * @title RWA-RENT: 中关村租金收益代币
13 * @dev 代表北京市海淀区中关村科技大厦租金收益权的合规代币化实现
14 * - 实现了完整的ERC20标准并增加了合规控制功能
15 * - 支持基于角色的访问控制,实现合规管理
16 * - 包含暂停功能以应对紧急情况
17 * - 支持代币销毁和投票功能
18 */
19contract RWARentToken is ERC20, ERC20Burnable, Pausable, AccessControl, ERC20Permit, ERC20Votes {
20 bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
21 bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
22 bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
23
24 // 资产信息
25 string public assetName; // 底层资产名称
26 string public assetLocation; // 资产位置
27 uint256 public assetValue; // 资产价值(元)
28 string public assetDescription; // 资产描述
29 string public spvInfo; // SPV信息
30
31 // 合规相关
32 bool public isComplianceEnabled = true; // 是否启用合规检查
33 mapping(address => bool) private _frozenAccounts; // 冻结账户映射
34 mapping(address => bool) public authorizedAddresses; // 授权地址(已通过KYC)
35
36 // 分红相关
37 uint256 public lastDistributionTimestamp; // 上次分红时间
38 mapping(address => uint256) public lastClaimTimestamp; // 用户上次领取分红时间
39 uint256 public distributionRate = 700; // 年化分红率(7.00%),以基点表示
40 uint256 public distributionInterval = 30 days; // 分红周期
41
42 // 事件
43 event ComplianceStatusChanged(bool newStatus);
44 event AccountFrozen(address indexed account);
45 event AccountUnfrozen(address indexed account);
46 event AccountAuthorized(address indexed account);
47 event AccountUnauthorized(address indexed account);
48 event DistributionRateChanged(uint256 newRate);
49 event DistributionExecuted(uint256 amountDistributed);
50 event YieldClaimed(address indexed holder, uint256 amount);
51
52 /**
53 * @dev 构造函数
54 * @param initialSupply 初始供应量
55 * @param admin 管理员地址
56 */
57 constructor(
58 uint256 initialSupply,
59 address admin
60 ) ERC20("中关村租金收益代币", "RWA-RENT") ERC20Permit("中关村租金收益代币") {
61 // 设置资产信息
62 assetName = "北京市海淀区中关村科技大厦";
63 assetLocation = "北京市海淀区中关村南大街";
64 assetValue = 1000000000; // 10亿元
65 assetDescription = "商业地产项目,总建筑面积20,000平方米,主要租户为科技企业";
66 spvInfo = "中关村租金收益专项计划 (深圳前海)";
67
68 // 初始化角色
69 _grantRole(DEFAULT_ADMIN_ROLE, admin);
70 _grantRole(PAUSER_ROLE, admin);
71 _grantRole(MINTER_ROLE, admin);
72 _grantRole(COMPLIANCE_ROLE, admin);
73
74 // 铸造初始代币
75 _mint(admin, initialSupply * 10 ** decimals());
76
77 // 初始化时间戳
78 lastDistributionTimestamp = block.timestamp;
79 }
80
81 /**
82 * @dev 暂停合约
83 * 只有PAUSER_ROLE角色可以调用
84 */
85 function pause() public onlyRole(PAUSER_ROLE) {
86 _pause();
87 }
88
89 /**
90 * @dev 恢复合约
91 * 只有PAUSER_ROLE角色可以调用
92 */
93 function unpause() public onlyRole(PAUSER_ROLE) {
94 _unpause();
95 }
96
97 /**
98 * @dev 铸造新代币
99 * 只有MINTER_ROLE角色可以调用
100 */
101 function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
102 _mint(to, amount);
103 }
104
105 /**
106 * @dev 启用/禁用合规检查
107 * 只有COMPLIANCE_ROLE角色可以调用
108 */
109 function setComplianceStatus(bool status) public onlyRole(COMPLIANCE_ROLE) {
110 isComplianceEnabled = status;
111 emit ComplianceStatusChanged(status);
112 }
113
114 /**
115 * @dev 冻结账户
116 * 只有COMPLIANCE_ROLE角色可以调用
117 */
118 function freezeAccount(address account) public onlyRole(COMPLIANCE_ROLE) {
119 _frozenAccounts[account] = true;
120 emit AccountFrozen(account);
121 }
122
123 /**
124 * @dev 解冻账户
125 * 只有COMPLIANCE_ROLE角色可以调用
126 */
127 function unfreezeAccount(address account) public onlyRole(COMPLIANCE_ROLE) {
128 _frozenAccounts[account] = false;
129 emit AccountUnfrozen(account);
130 }
131
132 /**
133 * @dev 授权账户(通过KYC)
134 * 只有COMPLIANCE_ROLE角色可以调用
135 */
136 function authorizeAddress(address account) public onlyRole(COMPLIANCE_ROLE) {
137 authorizedAddresses[account] = true;
138 emit AccountAuthorized(account);
139 }
140
141 /**
142 * @dev 取消账户授权
143 * 只有COMPLIANCE_ROLE角色可以调用
144 */
145 function unauthorizedAddress(address account) public onlyRole(COMPLIANCE_ROLE) {
146 authorizedAddresses[account] = false;
147 emit AccountUnauthorized(account);
148 }
149
150 /**
151 * @dev 修改分红率
152 * 只有DEFAULT_ADMIN_ROLE角色可以调用
153 */
154 function setDistributionRate(uint256 newRate) public onlyRole(DEFAULT_ADMIN_ROLE) {
155 require(newRate <= 2000, "分红率不能超过20%"); // 防止过高分红率
156 distributionRate = newRate;
157 emit DistributionRateChanged(newRate);
158 }
159
160 /**
161 * @dev 执行分红(由SPV调用)
162 * 只有DEFAULT_ADMIN_ROLE角色可以调用
163 */
164 function executeDistribution() public onlyRole(DEFAULT_ADMIN_ROLE) {
165 require(block.timestamp >= lastDistributionTimestamp + distributionInterval, "尚未到分红时间");
166
167 uint256 totalDistributed = 0;
168 // 实际应用中,这里应该从SPV公司的资金中转入相应金额
169 // 并记录每位持有者的应得分红
170
171 lastDistributionTimestamp = block.timestamp;
172 emit DistributionExecuted(totalDistributed);
173 }
174
175 /**
176 * @dev 领取分红
177 * 持有代币的用户可以领取分红
178 */
179 function claimYield() public {
180 require(balanceOf(msg.sender) > 0, "没有持有代币");
181 require(block.timestamp >= lastClaimTimestamp[msg.sender] + distributionInterval, "分红尚未到期");
182
183 // 计算应得分红:持仓量 * (年化率 / 分红次数)
184 uint256 yieldAmount = balanceOf(msg.sender) * distributionRate / (10000 * (365 days / distributionInterval));
185
186 // 实际应用中,这里应该将分红发放到用户地址
187 // 例如通过稳定币转账或通过其他代币合约
188
189 lastClaimTimestamp[msg.sender] = block.timestamp;
190 emit YieldClaimed(msg.sender, yieldAmount);
191 }
192
193 /**
194 * @dev 查询账户是否被冻结
195 */
196 function isAccountFrozen(address account) public view returns (bool) {
197 return _frozenAccounts[account];
198 }
199
200 /**
201 * @dev 查询下一次分红时间
202 */
203 function nextDistributionTimestamp() public view returns (uint256) {
204 return lastDistributionTimestamp + distributionInterval;
205 }
206
207 /**
208 * @dev 查询用户可领取分红
209 */
210 function getClaimableYield(address account) public view returns (uint256) {
211 if (balanceOf(account) == 0 || block.timestamp < lastClaimTimestamp[account] + distributionInterval) {
212 return 0;
213 }
214
215 return balanceOf(account) * distributionRate / (10000 * (365 days / distributionInterval));
216 }
217
218 /**
219 * @dev 重写transfer函数,添加合规检查
220 */
221 function transfer(address to, uint256 amount) public override returns (bool) {
222 require(!isComplianceEnabled || (authorizedAddresses[msg.sender] && authorizedAddresses[to]), "转账方或接收方未通过合规检查");
223 require(!_frozenAccounts[msg.sender] && !_frozenAccounts[to], "账户已被冻结");
224 return super.transfer(to, amount);
225 }
226
227 /**
228 * @dev 重写transferFrom函数,添加合规检查
229 */
230 function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
231 require(!isComplianceEnabled || (authorizedAddresses[from] && authorizedAddresses[to]), "转账方或接收方未通过合规检查");
232 require(!_frozenAccounts[from] && !_frozenAccounts[to], "账户已被冻结");
233 return super.transferFrom(from, to, amount);
234 }
235
236 /**
237 * @dev 在每次转账前调用,添加暂停检查
238 */
239 function _beforeTokenTransfer(address from, address to, uint256 amount) internal override whenNotPaused {
240 super._beforeTokenTransfer(from, to, amount);
241 }
242
243 /**
244 * @dev 实现ERC20Votes接口所需的函数
245 */
246 function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
247 super._afterTokenTransfer(from, to, amount);
248 }
249
250 /**
251 * @dev 实现ERC20Votes接口所需的函数
252 */
253 function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
254 super._mint(to, amount);
255 }
256
257 /**
258 * @dev 实现ERC20Votes接口所需的函数
259 */
260 function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
261 super._burn(account, amount);
262 }
263}
优势:
- 稳定现金流,定期到账可靠。
- 收益直观,计算简单易懂。
- 税务友好,便于申报。
- 流动性友好,价格相对稳。
劣势:
- Gas成本高,每次领取都费钱。
- 价格上涨空间小,围绕净值波动。
- 实现复杂,转账需额外结算。
- 合约中代币(如LP池)无法领分红。
方式三:弹性供应(stETH模式)
用户按 1:1 比例以稳定币铸造 RWA 代币。资产净值(NAV)变动时,系统通过 rebase 机制自动调整 totalSupply,确保代币价格锚定单位资产价值(pricePerShare = 1)。增值时正向 rebase 增发,减值时负向 rebase 销毁,无需用户干预,实现被动式收益归属。
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.20;
3
4import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
5import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
6import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
7import "@openzeppelin/contracts/access/AccessControl.sol";
8import "@openzeppelin/contracts/security/Pausable.sol";
9import "@openzeppelin/contracts/utils/math/SafeMath.sol";
10
11/**
12 * @title RWAElasticToken
13 * @dev 基于ERC4626标准的RWA弹性供应代币合约
14 * 实现类似stETH的弹性供应机制,资产价值变化通过rebase反映在代币供应量上
15 */
16contract RWAElasticToken is ERC20, ERC20Permit, ERC4626, AccessControl, Pausable, ERC20Votes {
17 using SafeMath for uint256;
18
19 // 角色定义
20 bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
21 bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
22 bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
23 bytes32 public constant ORACLE_ROLE = keccak256("ORACLE_ROLE");
24 bytes32 public constant REBASER_ROLE = keccak256("REBASER_ROLE");
25
26 // 资产相关信息
27 string public assetName;
28 string public assetLocation;
29 uint256 public assetValue; // 当前资产估值(单位:wei)
30 string public assetDescription;
31 string public spvInfo; // SPV相关信息
32
33 // 合规控制
34 bool public isComplianceEnabled = true; // 是否启用合规检查
35 mapping(address => bool) public authorizedAddresses; // 已授权地址(通过KYC)
36 mapping(address => bool) private _frozenAccounts; // 冻结账户
37
38 // 资产估值和rebase相关
39 uint256 public lastRebaseTimestamp; // 上次rebase时间戳
40 uint256 public rebaseInterval = 7 days; // rebase间隔(默认7天)
41 uint256 public targetExchangeRate = 1e18; // 目标兑换率(初始1:1)
42 uint256 public lastExchangeRate; // 上次兑换率
43
44 // 事件定义
45 event AssetValued(address indexed oracle, uint256 newValue, uint256 oldValue);
46 event RebaseExecuted(uint256 newSupply, uint256 oldSupply, uint256 newRate, uint256 oldRate);
47 event ComplianceStatusChanged(bool status);
48 event AccountFrozen(address indexed account);
49 event AccountUnfrozen(address indexed account);
50 event AccountAuthorized(address indexed account);
51 event AccountUnauthorized(address indexed account);
52 event RebaseIntervalChanged(uint256 newInterval);
53
54 /**
55 * @dev 构造函数
56 * @param _underlying 底层资产合约地址(通常是稳定币)
57 * @param admin 管理员地址
58 */
59 constructor(
60 ERC20 _underlying,
61 address admin
62 ) ERC20("中关村科技地产弹性代币", "RWA-ELASTIC")
63 ERC20Permit("中关村科技地产弹性代币")
64 ERC4626(_underlying) {
65 // 设置资产信息
66 assetName = "北京市海淀区中关村科技大厦组合";
67 assetLocation = "北京市海淀区中关村南大街5号";
68 assetValue = 2000000000 * 1e18; // 20亿元估值
69 assetDescription = "商业地产投资组合,包含3栋科技大厦,总建筑面积50,000平方米,年均租金回报率5.2%";
70 spvInfo = "中关村科技地产弹性收益专项计划 (上海自贸区)";
71
72 // 初始化角色
73 _grantRole(DEFAULT_ADMIN_ROLE, admin);
74 _grantRole(PAUSER_ROLE, admin);
75 _grantRole(MINTER_ROLE, admin);
76 _grantRole(COMPLIANCE_ROLE, admin);
77 _grantRole(ORACLE_ROLE, admin);
78 _grantRole(REBASER_ROLE, admin);
79
80 // 初始化时间戳
81 lastRebaseTimestamp = block.timestamp;
82 lastExchangeRate = targetExchangeRate;
83 }
84
85 /**
86 * @dev 暂停合约
87 * 只有PAUSER_ROLE角色可以调用
88 */
89 function pause() public onlyRole(PAUSER_ROLE) {
90 _pause();
91 }
92
93 /**
94 * @dev 恢复合约
95 * 只有PAUSER_ROLE角色可以调用
96 */
97 function unpause() public onlyRole(PAUSER_ROLE) {
98 _unpause();
99 }
100
101 /**
102 * @dev 启用/禁用合规检查
103 * 只有COMPLIANCE_ROLE角色可以调用
104 */
105 function setComplianceStatus(bool status) public onlyRole(COMPLIANCE_ROLE) {
106 isComplianceEnabled = status;
107 emit ComplianceStatusChanged(status);
108 }
109
110 /**
111 * @dev 冻结账户
112 * 只有COMPLIANCE_ROLE角色可以调用
113 */
114 function freezeAccount(address account) public onlyRole(COMPLIANCE_ROLE) {
115 _frozenAccounts[account] = true;
116 emit AccountFrozen(account);
117 }
118
119 /**
120 * @dev 解冻账户
121 * 只有COMPLIANCE_ROLE角色可以调用
122 */
123 function unfreezeAccount(address account) public onlyRole(COMPLIANCE_ROLE) {
124 _frozenAccounts[account] = false;
125 emit AccountUnfrozen(account);
126 }
127
128 /**
129 * @dev 授权账户(通过KYC)
130 * 只有COMPLIANCE_ROLE角色可以调用
131 */
132 function authorizeAddress(address account) public onlyRole(COMPLIANCE_ROLE) {
133 authorizedAddresses[account] = true;
134 emit AccountAuthorized(account);
135 }
136
137 /**
138 * @dev 取消账户授权
139 * 只有COMPLIANCE_ROLE角色可以调用
140 */
141 function unauthorizedAddress(address account) public onlyRole(COMPLIANCE_ROLE) {
142 authorizedAddresses[account] = false;
143 emit AccountUnauthorized(account);
144 }
145
146 /**
147 * @dev 更新资产估值
148 * 只有ORACLE_ROLE角色可以调用
149 */
150 function updateAssetValue(uint256 newValue) public onlyRole(ORACLE_ROLE) {
151 uint256 oldValue = assetValue;
152 assetValue = newValue;
153 emit AssetValued(msg.sender, newValue, oldValue);
154 }
155
156 /**
157 * @dev 修改rebase间隔
158 * 只有DEFAULT_ADMIN_ROLE角色可以调用
159 */
160 function setRebaseInterval(uint256 newInterval) public onlyRole(DEFAULT_ADMIN_ROLE) {
161 require(newInterval >= 1 days && newInterval <= 30 days, "间隔必须在1-30天之间");
162 rebaseInterval = newInterval;
163 emit RebaseIntervalChanged(newInterval);
164 }
165
166 /**
167 * @dev 执行rebase操作
168 * 只有REBASER_ROLE角色可以调用
169 * 通过调整代币供应量,使1 RWA-ELASTIC = 当前资产净值
170 */
171 function executeRebase() public onlyRole(REBASER_ROLE) whenNotPaused {
172 require(block.timestamp >= lastRebaseTimestamp + rebaseInterval, "尚未到rebase时间");
173
174 uint256 oldSupply = totalSupply();
175 uint256 oldRate = lastExchangeRate;
176
177 if (oldSupply == 0) {
178 // 无代币时直接更新时间戳
179 lastRebaseTimestamp = block.timestamp;
180 return;
181 }
182
183 // 计算新的代币供应量
184 // 总资产价值 / 目标兑换率 = 新代币供应量
185 uint256 newSupply = assetValue.div(targetExchangeRate);
186
187 // 计算新的兑换率
188 uint256 newExchangeRate = assetValue.div(oldSupply);
189 lastExchangeRate = newExchangeRate;
190
191 // 执行rebase操作
192 if (newSupply > oldSupply) {
193 // 资产增值,增发代币
194 _mint(address(this), newSupply.sub(oldSupply));
195 } else if (newSupply < oldSupply) {
196 // 资产减值,减少代币
197 _burn(address(this), oldSupply.sub(newSupply));
198 }
199
200 // 更新时间戳
201 lastRebaseTimestamp = block.timestamp;
202
203 emit RebaseExecuted(newSupply, oldSupply, newExchangeRate, oldRate);
204 }
205
206 /**
207 * @dev 查询账户是否被冻结
208 */
209 function isAccountFrozen(address account) public view returns (bool) {
210 return _frozenAccounts[account];
211 }
212
213 /**
214 * @dev 查询下一次rebase时间
215 */
216 function nextRebaseTimestamp() public view returns (uint256) {
217 return lastRebaseTimestamp + rebaseInterval;
218 }
219
220 /**
221 * @dev 查询当前资产净值(每单位代币对应的资产价值)
222 */
223 function assetNetValue() public view returns (uint256) {
224 if (totalSupply() == 0) {
225 return targetExchangeRate;
226 }
227 return assetValue.div(totalSupply());
228 }
229
230 /**
231 * @dev 重写transfer函数,添加合规检查
232 */
233 function transfer(address to, uint256 amount) public override returns (bool) {
234 require(!isComplianceEnabled || (authorizedAddresses[msg.sender] && authorizedAddresses[to]), "转账方或接收方未通过合规检查");
235 require(!_frozenAccounts[msg.sender] && !_frozenAccounts[to], "账户已被冻结");
236 return super.transfer(to, amount);
237 }
238
239 /**
240 * @dev 重写transferFrom函数,添加合规检查
241 */
242 function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
243 require(!isComplianceEnabled || (authorizedAddresses[from] && authorizedAddresses[to]), "转账方或接收方未通过合规检查");
244 require(!_frozenAccounts[from] && !_frozenAccounts[to], "账户已被冻结");
245 return super.transferFrom(from, to, amount);
246 }
247
248 /**
249 * @dev 重写withdraw函数,添加暂停检查
250 */
251 function withdraw(uint256 assets, address receiver, address owner)
252 public override whenNotPaused returns (uint256 shares) {
253 return super.withdraw(assets, receiver, owner);
254 }
255
256 /**
257 * @dev 重写redeem函数,添加暂停检查
258 */
259 function redeem(uint256 shares, address receiver, address owner)
260 public override whenNotPaused returns (uint256 assets) {
261 return super.redeem(shares, receiver, owner);
262 }
263
264 /**
265 * @dev 重写deposit函数,添加暂停检查
266 */
267 function deposit(uint256 assets, address receiver)
268 public override whenNotPaused returns (uint256 shares) {
269 return super.deposit(assets, receiver);
270 }
271
272 /**
273 * @dev 重写mint函数,添加暂停检查
274 */
275 function mint(uint256 shares, address receiver)
276 public override whenNotPaused returns (uint256 assets) {
277 return super.mint(shares, receiver);
278 }
279
280 /**
281 * @dev 在每次转账前调用,添加暂停检查
282 */
283 function _beforeTokenTransfer(address from, address to, uint256 amount)
284 internal override whenNotPaused {
285 super._beforeTokenTransfer(from, to, amount);
286 }
287
288 /**
289 * @dev 实现ERC20Votes接口所需的函数
290 */
291 function _afterTokenTransfer(address from, address to, uint256 amount)
292 internal override(ERC20, ERC20Votes) {
293 super._afterTokenTransfer(from, to, amount);
294 }
295
296 /**
297 * @dev 实现ERC20Votes接口所需的函数
298 */
299 function _mint(address to, uint256 amount)
300 internal override(ERC20, ERC20Votes) {
301 super._mint(to, amount);
302 }
303
304 /**
305 * @dev 实现ERC20Votes接口所需的函数
306 */
307 function _burn(address account, uint256 amount)
308 internal override(ERC20, ERC20Votes) {
309 super._burn(account, amount);
310 }
311
312 /**
313 * @dev 重写maxAssets函数,限制赎回最大资产数量为合约中持有的资产
314 */
315 function maxAssets() public view override returns (uint256) {
316 return asset.balanceOf(address(this));
317 }
318
319 /**
320 * @dev 重写previewRedeem函数,考虑最新的资产估值
321 */
322 function previewRedeem(uint256 shares) public view override returns (uint256) {
323 if (totalSupply() == 0) {
324 return super.previewRedeem(shares);
325 }
326
327 // 基于当前资产估值计算可赎回资产数量
328 return shares.mul(assetNetValue()).div(1e18);
329 }
330}
优势:
- 用户体验好,无需任何操作。
- 零Gas成本,自动增长。
- 流动性池友好,LP也受益。
- 税务优化,未实现收益可能不税。
- 公平分配,所有持币者同等。
劣势:
- 集成复杂,有些DeFi协议不支持。
- 会计处理麻烦,数量变化难记账。
- 税务风险,某些地区视作收入。
- 需要包装代币,在DeFi中用。
通过数据上链和收益分配,我们确保了RWA的透明性和可持续性。在系列第三篇中,我们将探讨流动性提供和用户交互界面开发,完成从资产到用户端的全链路闭环。
