RWA 代币化 —— 发行管理模块

发行管理模块负责 RWA 代币发行请求的全生命周期管理,通过审批工作流确保代币发行的合规性和安全性。

1. 概述

RWATokenIssuanceManager 实现了代币发行的审批流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────────────────┐
│ 发行请求生命周期 │
├─────────────────────────────────────────────────────────┤
│ │
│ 提交请求 审批/拒绝 执行调用 │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │Pending│ ────► │Approved│ ────► │Deployed│ │
│ └───────┘ └───────┘ └───────┘ │
│ │ │
│ │ ┌───────┐ │
│ └─────────► │Rejected│ │
│ └───────┘ │
└─────────────────────────────────────────────────────────┘
状态 说明
Pending 请求已提交,等待审批
Approved 请求已批准,等待执行
Rejected 请求已拒绝
Deployed 调用已执行完成

2. 角色与权限

1
2
bytes32 public constant APPROVER_ROLE = keccak256("APPROVER_ROLE");
bytes32 public constant EMERGENCY_ROLE = keccak256("EMERGENCY_ROLE");
角色 权限
DEFAULT_ADMIN_ROLE 管理角色、设置费用、白名单管理、升级合约
APPROVER_ROLE 审批/拒绝发行请求
EMERGENCY_ROLE 紧急暂停、紧急提款

3. 数据结构

3.1 发行请求

1
2
3
4
5
6
7
8
9
10
struct TokenIssuanceRequest {
address requester; // 请求者地址
address to; // 目标合约地址
bytes data; // 调用数据(calldata)
RequestStatus status; // 请求状态
uint256 timestamp; // 提交时间戳
bool isDelegateCall; // 是否使用 delegatecall
string rejectionReason; // 拒绝原因(如被拒绝)
bytes result; // 执行结果(如已执行)
}

3.2 状态枚举

1
2
3
4
5
6
enum RequestStatus {
Pending, // 0 - 待审批
Approved, // 1 - 已批准
Rejected, // 2 - 已拒绝
Deployed // 3 - 已执行
}

3.3 存储映射

1
2
3
4
5
6
7
8
9
10
11
// 请求 ID → 请求详情
mapping(uint256 => TokenIssuanceRequest) private _requests;

// 请求者地址 → 该用户的所有请求 ID
mapping(address => uint256[]) private _requesterToRequests;

// 状态 → 该状态下的所有请求 ID
mapping(RequestStatus => uint256[]) private _statusToRequests;

// delegatecall 目标白名单
mapping(address => bool) private _delegateCallWhitelist;

4. 核心功能

4.1 提交请求

submitTokenRequest

提交代币发行请求。

1
2
3
4
5
6
7
8
9
10
11
function submitTokenRequest(
address to,
bytes memory data
) external payable whenNotPaused notEmergencyPaused nonReentrant returns (uint256);

// 带 delegatecall 选项
function submitTokenRequest(
address to,
bytes memory data,
bool isDelegateCall
) external payable whenNotPaused notEmergencyPaused nonReentrant returns (uint256);

内部实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function _submitTokenRequest(
address to,
bytes memory data,
bool isDelegateCall
) internal returns (uint256) {
// 验证目标地址
require(to != address(0), "RWATokenIssuanceManager: invalid target contract");
require(to.code.length > 0, "RWATokenIssuanceManager: target is not contract");
require(data.length > 0, "RWATokenIssuanceManager: empty call data");

// delegatecall 需要目标在白名单中
if (isDelegateCall) {
require(_delegateCallWhitelist[to], "RWATokenIssuanceManager: target not whitelisted for delegatecall");
}

// 处理费用
if (feeRequired && requestFee > 0) {
require(msg.value >= requestFee, "RWATokenIssuanceManager: insufficient fee");
}

// 创建请求
uint256 requestId = _nextRequestId++;

TokenIssuanceRequest storage request = _requests[requestId];
request.requester = msg.sender;
request.to = to;
request.data = data;
request.status = RequestStatus.Pending;
request.timestamp = block.timestamp;
request.isDelegateCall = isDelegateCall;

// 更新索引
_requesterToRequests[msg.sender].push(requestId);
_statusToRequests[RequestStatus.Pending].push(requestId);
totalRequests++;

emit RequestSubmitted(requestId, msg.sender, to, isDelegateCall);

return requestId;
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
// 构造铸造调用数据
bytes memory mintCalldata = abi.encodeWithSignature(
"mint(address,uint256)",
recipientAddress,
1000 * 10**18
);

// 提交发行请求
uint256 requestId = issuanceManager.submitTokenRequest(
tokenAddress,
mintCalldata
);

4.2 审批请求

approveRequest

批准发行请求。

1
2
3
4
5
6
7
8
9
10
function approveRequest(uint256 requestId) external onlyApprover validRequest(requestId) {
TokenIssuanceRequest storage request = _requests[requestId];
require(request.status == RequestStatus.Pending, "RWATokenIssuanceManager: request not pending");

// 更新状态
request.status = RequestStatus.Approved;
_updateRequestStatus(requestId, RequestStatus.Pending, RequestStatus.Approved);

emit RequestApproved(requestId, msg.sender);
}

rejectRequest

拒绝发行请求,需提供拒绝原因。

1
2
3
4
5
6
7
8
9
10
11
12
function rejectRequest(uint256 requestId, string memory reason) external onlyApprover validRequest(requestId) {
TokenIssuanceRequest storage request = _requests[requestId];
require(request.status == RequestStatus.Pending, "RWATokenIssuanceManager: request not pending");
require(bytes(reason).length > 0, "RWATokenIssuanceManager: empty rejection reason");

// 更新状态
request.status = RequestStatus.Rejected;
request.rejectionReason = reason;
_updateRequestStatus(requestId, RequestStatus.Pending, RequestStatus.Rejected);

emit RequestRejected(requestId, msg.sender, reason);
}

4.3 执行调用

executeApprovedCall

执行已批准的发行请求。

1
2
3
4
5
6
function executeApprovedCall(uint256 requestId) external validRequest(requestId) nonReentrant returns (bytes memory) {
TokenIssuanceRequest storage request = _requests[requestId];
require(request.status == RequestStatus.Approved, "RWATokenIssuanceManager: request not approved");

return _executeCall(requestId);
}

内部执行逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function _executeCall(uint256 requestId) internal returns (bytes memory) {
TokenIssuanceRequest storage request = _requests[requestId];

// 缓存变量
address to = request.to;
bytes memory data = request.data;
bool isDelegateCall = request.isDelegateCall;

// 使用 assembly 执行调用
bytes memory result;
bool success;

assembly {
let dataPtr := add(data, 0x20)
let dataSize := mload(data)

result := mload(0x40)
mstore(0x40, add(result, 0x20))

switch isDelegateCall
case true {
// delegatecall
success := delegatecall(gas(), to, dataPtr, dataSize, 0, 0)
}
default {
// 普通 call
success := call(gas(), to, 0, dataPtr, dataSize, 0, 0)
}

// 处理返回数据
let returnSize := returndatasize()
mstore(0x40, add(result, add(0x20, returnSize)))
mstore(result, returnSize)
returndatacopy(add(result, 0x20), 0, returnSize)
}

require(success, "RWATokenIssuanceManager: call to target contract failed");

// 更新请求状态
request.status = RequestStatus.Deployed;
request.result = result;
_updateRequestStatus(requestId, RequestStatus.Approved, RequestStatus.Deployed);

emit CallExecuted(requestId, request.requester, result, isDelegateCall);

return result;
}

4.4 批量操作

batchApproveRequests

批量批准多个请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
function batchApproveRequests(uint256[] calldata requestIds) external onlyApprover nonReentrant {
for (uint256 i = 0; i < requestIds.length; i++) {
uint256 requestId = requestIds[i];
if (requestId < _nextRequestId && _requests[requestId].status == RequestStatus.Pending) {
TokenIssuanceRequest storage request = _requests[requestId];

request.status = RequestStatus.Approved;
_updateRequestStatus(requestId, RequestStatus.Pending, RequestStatus.Approved);

emit RequestApproved(requestId, msg.sender);
}
}
}

batchRejectRequests

批量拒绝多个请求。

1
2
3
4
5
6
7
8
9
10
function batchRejectRequests(
uint256[] calldata requestIds,
string[] calldata reasons
) external onlyApprover {
require(requestIds.length == reasons.length, "RWATokenIssuanceManager: arrays length mismatch");

for (uint256 i = 0; i < requestIds.length; i++) {
// ... 验证并拒绝每个请求
}
}

batchExecuteApprovedCalls

批量执行已批准的请求。

1
2
3
4
5
6
7
8
9
function batchExecuteApprovedCalls(uint256[] calldata requestIds) external nonReentrant {
for (uint256 i = 0; i < requestIds.length;) {
uint256 requestId = requestIds[i];
if (requestId < _nextRequestId && _requests[requestId].status == RequestStatus.Approved) {
_executeCall(requestId);
}
unchecked { ++i; }
}
}

5. DelegateCall 白名单

delegatecall 会在调用者的上下文中执行目标代码,存在安全风险,因此需要白名单控制。

5.1 设置白名单

1
2
3
4
5
6
7
8
9
10
function setDelegateCallTargetWhitelist(address target, bool whitelisted)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
require(target != address(0), "RWATokenIssuanceManager: invalid target address");
require(target.code.length > 0, "RWATokenIssuanceManager: target is not contract");

_delegateCallWhitelist[target] = whitelisted;
emit DelegateCallTargetWhitelisted(target, whitelisted);
}

5.2 批量设置

1
2
3
4
5
6
7
8
9
10
11
12
function batchSetDelegateCallTargetWhitelist(
address[] calldata targets,
bool[] calldata whitelistStatuses
) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(targets.length == whitelistStatuses.length, "RWATokenIssuanceManager: arrays length mismatch");

for (uint256 i = 0; i < targets.length; i++) {
// ... 验证并设置
_delegateCallWhitelist[targets[i]] = whitelistStatuses[i];
emit DelegateCallTargetWhitelisted(targets[i], whitelistStatuses[i]);
}
}

5.3 查询白名单

1
2
3
function isDelegateCallTargetWhitelisted(address target) external view returns (bool) {
return _delegateCallWhitelist[target];
}

6. 费用管理

支持收取请求费用。

1
2
3
4
5
6
7
uint256 public requestFee;
bool public feeRequired;

function setRequestFee(uint256 newFee, bool required) external onlyRole(DEFAULT_ADMIN_ROLE) {
requestFee = newFee;
feeRequired = required;
}

7. 查询函数

1
2
3
4
5
6
7
8
9
10
11
// 获取请求详情
function getRequest(uint256 requestId) external view returns (TokenIssuanceRequest memory);

// 获取某用户的所有请求 ID
function getRequestsByRequester(address requester) external view returns (uint256[] memory);

// 获取某状态下的所有请求 ID
function getRequestsByStatus(RequestStatus status) external view returns (uint256[] memory);

// 获取总请求数
function getTotalRequests() external view returns (uint256);

8. 紧急控制

8.1 紧急暂停

1
2
3
4
5
6
7
8
9
bool public emergencyPaused;

function emergencyPause() external onlyEmergency {
emergencyPaused = true;
}

function emergencyUnpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
emergencyPaused = false;
}

区别

  • pause():常规暂停,由 Admin 控制
  • emergencyPause():紧急暂停,由 Emergency 角色控制,恢复需要 Admin

8.2 紧急提款

1
2
3
4
5
6
7
8
9
10
function emergencyWithdraw(address token, uint256 amount) external onlyEmergency {
if (token == address(0)) {
// 提取 ETH
require(amount <= address(this).balance, "RWATokenIssuanceManager: insufficient ETH balance");
payable(msg.sender).transfer(amount);
} else {
// 提取 ERC20
IERC20(token).transfer(msg.sender, amount);
}
}

9. 工作流程示例

9.1 标准发行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. 项目方准备发行
├── 部署 RWAToken 合约(或使用已有代币)
└── 授予 IssuanceManager 的 MINTER_ROLE

2. 提交发行请求
└── issuanceManager.submitTokenRequest(tokenAddress, mintCalldata)

3. 审批者审核
├── 检查请求参数是否合理
├── 验证目标合约和调用数据
└── issuanceManager.approveRequest(requestId)

4. 执行发行
└── issuanceManager.executeApprovedCall(requestId)

5. 验证结果
└── 检查代币余额是否正确

9.2 批量发行流程

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 准备多个铸造请求
uint256[] memory requestIds = new uint256[](3);

requestIds[0] = issuanceManager.submitTokenRequest(token, mintData1);
requestIds[1] = issuanceManager.submitTokenRequest(token, mintData2);
requestIds[2] = issuanceManager.submitTokenRequest(token, mintData3);

// 2. 批量审批
issuanceManager.batchApproveRequests(requestIds);

// 3. 批量执行
issuanceManager.batchExecuteApprovedCalls(requestIds);

10. 事件

1
2
3
4
5
event RequestSubmitted(uint256 indexed requestId, address indexed requester, address indexed to, bool isDelegateCall);
event RequestApproved(uint256 indexed requestId, address indexed approver);
event RequestRejected(uint256 indexed requestId, address indexed approver, string reason);
event CallExecuted(uint256 indexed requestId, address indexed requester, bytes result, bool isDelegateCall);
event DelegateCallTargetWhitelisted(address indexed target, bool whitelisted);

11. 安全考虑

11.1 调用验证

  • 目标地址必须是合约
  • 调用数据不能为空
  • delegatecall 目标必须在白名单中

11.2 状态检查

  • 只能审批 Pending 状态的请求
  • 只能执行 Approved 状态的请求
  • 状态转换是单向的,不可逆

11.3 权限分离

  • 提交:任何人
  • 审批:APPROVER_ROLE
  • 执行:任何人(但必须先被批准)
  • 紧急操作:EMERGENCY_ROLE

11.4 Gas 优化

  • 使用 assembly 执行调用
  • 批量操作使用 unchecked 算术
  • 缓存存储变量到内存