gcx/blockchain/genex-contracts/src/Compliance.sol

232 lines
8.6 KiB
Solidity
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "./interfaces/ICoupon.sol";
/// @title Compliance — 合规合约
/// @notice OFAC 黑名单筛查、Travel Rule、KYC 差异化检查、账户冻结
/// @dev 使用 Transparent Proxy 部署
contract Compliance is Initializable, AccessControlUpgradeable {
bytes32 public constant COMPLIANCE_OFFICER_ROLE = keccak256("COMPLIANCE_OFFICER_ROLE");
bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
bytes32 public constant KYC_PROVIDER_ROLE = keccak256("KYC_PROVIDER_ROLE");
// OFAC 黑名单
mapping(address => bool) private _blacklist;
// 冻结账户(紧急冻结,需 Governance 多签)
mapping(address => bool) private _frozen;
// KYC 等级映射: 0=L0(未验证), 1=L1(基本), 2=L2(增强), 3=L3(机构)
mapping(address => uint8) private _kycLevels;
// Travel Rule 记录
mapping(address => mapping(address => bool)) private _travelRuleRecorded;
// Travel Rule 详情
mapping(bytes32 => TravelRuleRecord) private _travelRuleRecords;
struct TravelRuleRecord {
address sender;
address receiver;
bytes32 senderInfoHash;
bytes32 receiverInfoHash;
uint256 recordedAt;
}
// Travel Rule 阈值美元USDC 精度 6 位)
uint256 public constant TRAVEL_RULE_THRESHOLD = 3000e6; // $3,000
// 大额交易阈值
uint256 public constant LARGE_TRADE_THRESHOLD = 10000e6; // $10,000
// --- Events ---
event AddressBlacklisted(address indexed account, string reason);
event AddressRemovedFromBlacklist(address indexed account);
event AccountFrozen(address indexed account);
event AccountUnfrozen(address indexed account);
event KycLevelUpdated(address indexed account, uint8 oldLevel, uint8 newLevel);
event TravelRuleRecorded(
address indexed sender,
address indexed receiver,
bytes32 senderInfoHash,
bytes32 receiverInfoHash
);
function initialize(address admin) external initializer {
__AccessControl_init();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(COMPLIANCE_OFFICER_ROLE, admin);
_grantRole(GOVERNANCE_ROLE, admin);
_grantRole(KYC_PROVIDER_ROLE, admin);
}
// ========================
// OFAC 黑名单
// ========================
/// @notice OFAC 筛查
function isBlacklisted(address account) external view returns (bool) {
return _blacklist[account];
}
/// @notice 添加 OFAC 黑名单
function addToBlacklist(address account, string calldata reason)
external
onlyRole(COMPLIANCE_OFFICER_ROLE)
{
_blacklist[account] = true;
emit AddressBlacklisted(account, reason);
}
/// @notice 从黑名单移除
function removeFromBlacklist(address account) external onlyRole(GOVERNANCE_ROLE) {
_blacklist[account] = false;
emit AddressRemovedFromBlacklist(account);
}
// ========================
// 账户冻结
// ========================
/// @notice 查询是否冻结
function isFrozen(address account) external view returns (bool) {
return _frozen[account];
}
/// @notice 紧急冻结(需 Governance 多签)
function freezeAccount(address account) external onlyRole(GOVERNANCE_ROLE) {
_frozen[account] = true;
emit AccountFrozen(account);
}
/// @notice 解除冻结
function unfreezeAccount(address account) external onlyRole(GOVERNANCE_ROLE) {
_frozen[account] = false;
emit AccountUnfrozen(account);
}
// ========================
// KYC 等级管理
// ========================
/// @notice 获取 KYC 等级
function getKycLevel(address account) external view returns (uint8) {
return _kycLevels[account];
}
/// @notice 设置 KYC 等级
function setKycLevel(address account, uint8 level) external onlyRole(KYC_PROVIDER_ROLE) {
require(level <= 3, "Compliance: invalid KYC level");
uint8 oldLevel = _kycLevels[account];
_kycLevels[account] = level;
emit KycLevelUpdated(account, oldLevel, level);
}
/// @notice 批量设置 KYC 等级
function batchSetKycLevel(address[] calldata accounts, uint8[] calldata levels)
external
onlyRole(KYC_PROVIDER_ROLE)
{
require(accounts.length == levels.length, "Compliance: length mismatch");
for (uint256 i = 0; i < accounts.length; i++) {
require(levels[i] <= 3, "Compliance: invalid KYC level");
uint8 oldLevel = _kycLevels[accounts[i]];
_kycLevels[accounts[i]] = levels[i];
emit KycLevelUpdated(accounts[i], oldLevel, levels[i]);
}
}
/// @notice 要求最低 KYC 等级revert if不满足
function requireKycLevel(address account, uint8 requiredLevel) external view {
require(_kycLevels[account] >= requiredLevel, "Compliance: insufficient KYC level");
}
// ========================
// Travel Rule
// ========================
/// @notice 记录 Travel Rule 数据(≥$3,000 强制)
function recordTravelRule(
address sender,
address receiver,
bytes32 senderInfoHash,
bytes32 receiverInfoHash
) external onlyRole(COMPLIANCE_OFFICER_ROLE) {
require(sender != address(0) && receiver != address(0), "Compliance: zero address");
require(senderInfoHash != bytes32(0) && receiverInfoHash != bytes32(0), "Compliance: empty hash");
_travelRuleRecorded[sender][receiver] = true;
bytes32 recordId = keccak256(abi.encodePacked(sender, receiver, block.timestamp));
_travelRuleRecords[recordId] = TravelRuleRecord({
sender: sender,
receiver: receiver,
senderInfoHash: senderInfoHash,
receiverInfoHash: receiverInfoHash,
recordedAt: block.timestamp
});
emit TravelRuleRecorded(sender, receiver, senderInfoHash, receiverInfoHash);
}
/// @notice 查询 Travel Rule 是否已记录
function hasTravelRuleRecord(address sender, address receiver) external view returns (bool) {
return _travelRuleRecorded[sender][receiver];
}
// ========================
// 综合合规检查
// ========================
/// @notice 交易前合规检查Settlement 调用)
function preTradeCheck(
address buyer,
address seller,
uint256 amount,
ICoupon.CouponType couponType
) external view {
// OFAC 黑名单检查
require(!_blacklist[buyer], "Compliance: buyer blacklisted");
require(!_blacklist[seller], "Compliance: seller blacklisted");
// 冻结检查
require(!_frozen[buyer], "Compliance: buyer frozen");
require(!_frozen[seller], "Compliance: seller frozen");
// Utility Track: 双方至少 KYC L1
if (couponType == ICoupon.CouponType.Utility) {
require(_kycLevels[buyer] >= 1, "Compliance: buyer needs KYC L1 for Utility");
require(_kycLevels[seller] >= 1, "Compliance: seller needs KYC L1 for Utility");
}
// Securities Track: 双方至少 KYC L2
else {
require(_kycLevels[buyer] >= 2, "Compliance: buyer needs KYC L2 for Security");
require(_kycLevels[seller] >= 2, "Compliance: seller needs KYC L2 for Security");
}
// 大额交易: KYC L2+
if (amount >= LARGE_TRADE_THRESHOLD) {
require(_kycLevels[buyer] >= 2, "Compliance: buyer needs KYC L2 for large trade");
require(_kycLevels[seller] >= 2, "Compliance: seller needs KYC L2 for large trade");
}
}
/// @notice P2P 转移合规路由
function p2pComplianceCheck(
address sender,
address receiver,
uint256 amount
) external view {
require(!_blacklist[sender], "Compliance: sender blacklisted");
require(!_blacklist[receiver], "Compliance: receiver blacklisted");
require(!_frozen[sender], "Compliance: sender frozen");
require(!_frozen[receiver], "Compliance: receiver frozen");
// ≥$3,000 强制 Travel Rule
if (amount >= TRAVEL_RULE_THRESHOLD) {
require(_kycLevels[sender] >= 2, "Compliance: sender needs KYC L2 for Travel Rule");
require(_kycLevels[receiver] >= 2, "Compliance: receiver needs KYC L2 for Travel Rule");
require(_travelRuleRecorded[sender][receiver], "Compliance: Travel Rule data required");
}
}
}