195 lines
5.7 KiB
Solidity
195 lines
5.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "forge-std/Test.sol";
|
|
import "../src/Compliance.sol";
|
|
import "../src/interfaces/ICoupon.sol";
|
|
|
|
contract ComplianceTest is Test {
|
|
Compliance compliance;
|
|
|
|
address admin = address(1);
|
|
address user1 = address(2);
|
|
address user2 = address(3);
|
|
address officer = address(4);
|
|
|
|
function setUp() public {
|
|
vm.startPrank(admin);
|
|
compliance = new Compliance();
|
|
compliance.initialize(admin);
|
|
compliance.grantRole(keccak256("COMPLIANCE_OFFICER_ROLE"), officer);
|
|
vm.stopPrank();
|
|
}
|
|
|
|
// === OFAC ===
|
|
|
|
function test_AddToBlacklist() public {
|
|
vm.prank(officer);
|
|
compliance.addToBlacklist(user1, "OFAC SDN");
|
|
assertTrue(compliance.isBlacklisted(user1));
|
|
}
|
|
|
|
function test_RemoveFromBlacklist() public {
|
|
vm.prank(officer);
|
|
compliance.addToBlacklist(user1, "OFAC SDN");
|
|
|
|
vm.prank(admin);
|
|
compliance.removeFromBlacklist(user1);
|
|
assertFalse(compliance.isBlacklisted(user1));
|
|
}
|
|
|
|
function test_RemoveFromBlacklistOnlyGovernance() public {
|
|
vm.prank(officer);
|
|
compliance.addToBlacklist(user1, "OFAC SDN");
|
|
|
|
vm.prank(officer);
|
|
vm.expectRevert();
|
|
compliance.removeFromBlacklist(user1);
|
|
}
|
|
|
|
// === KYC ===
|
|
|
|
function test_SetKycLevel() public {
|
|
vm.prank(admin);
|
|
compliance.setKycLevel(user1, 2);
|
|
assertEq(compliance.getKycLevel(user1), 2);
|
|
}
|
|
|
|
function test_InvalidKycLevelReverts() public {
|
|
vm.prank(admin);
|
|
vm.expectRevert("Compliance: invalid KYC level");
|
|
compliance.setKycLevel(user1, 4);
|
|
}
|
|
|
|
function test_RequireKycLevelSuccess() public view {
|
|
// L0 默认
|
|
compliance.requireKycLevel(user1, 0);
|
|
}
|
|
|
|
function test_RequireKycLevelFails() public {
|
|
vm.expectRevert("Compliance: insufficient KYC level");
|
|
compliance.requireKycLevel(user1, 1);
|
|
}
|
|
|
|
function test_BatchSetKycLevel() public {
|
|
address[] memory accounts = new address[](2);
|
|
accounts[0] = user1;
|
|
accounts[1] = user2;
|
|
uint8[] memory levels = new uint8[](2);
|
|
levels[0] = 1;
|
|
levels[1] = 2;
|
|
|
|
vm.prank(admin);
|
|
compliance.batchSetKycLevel(accounts, levels);
|
|
|
|
assertEq(compliance.getKycLevel(user1), 1);
|
|
assertEq(compliance.getKycLevel(user2), 2);
|
|
}
|
|
|
|
// === Freeze ===
|
|
|
|
function test_FreezeAccount() public {
|
|
vm.prank(admin);
|
|
compliance.freezeAccount(user1);
|
|
assertTrue(compliance.isFrozen(user1));
|
|
}
|
|
|
|
function test_UnfreezeAccount() public {
|
|
vm.prank(admin);
|
|
compliance.freezeAccount(user1);
|
|
vm.prank(admin);
|
|
compliance.unfreezeAccount(user1);
|
|
assertFalse(compliance.isFrozen(user1));
|
|
}
|
|
|
|
// === Travel Rule ===
|
|
|
|
function test_RecordTravelRule() public {
|
|
vm.prank(officer);
|
|
compliance.recordTravelRule(user1, user2, keccak256("sender"), keccak256("receiver"));
|
|
assertTrue(compliance.hasTravelRuleRecord(user1, user2));
|
|
}
|
|
|
|
// === Pre-Trade Check ===
|
|
|
|
function test_PreTradeCheckUtilityRequiresKycL1() public {
|
|
// 无 KYC
|
|
vm.expectRevert("Compliance: buyer needs KYC L1 for Utility");
|
|
compliance.preTradeCheck(user1, user2, 100e6, ICoupon.CouponType.Utility);
|
|
}
|
|
|
|
function test_PreTradeCheckSecurityRequiresKycL2() public {
|
|
vm.startPrank(admin);
|
|
compliance.setKycLevel(user1, 1);
|
|
compliance.setKycLevel(user2, 1);
|
|
vm.stopPrank();
|
|
|
|
vm.expectRevert("Compliance: buyer needs KYC L2 for Security");
|
|
compliance.preTradeCheck(user1, user2, 100e6, ICoupon.CouponType.Security);
|
|
}
|
|
|
|
function test_PreTradeCheckLargeTradeRequiresKycL2() public {
|
|
vm.startPrank(admin);
|
|
compliance.setKycLevel(user1, 1);
|
|
compliance.setKycLevel(user2, 1);
|
|
vm.stopPrank();
|
|
|
|
vm.expectRevert("Compliance: buyer needs KYC L2 for large trade");
|
|
compliance.preTradeCheck(user1, user2, 10000e6, ICoupon.CouponType.Utility);
|
|
}
|
|
|
|
function test_PreTradeCheckBlacklistedReverts() public {
|
|
vm.startPrank(admin);
|
|
compliance.setKycLevel(user1, 2);
|
|
compliance.setKycLevel(user2, 2);
|
|
vm.stopPrank();
|
|
|
|
vm.prank(officer);
|
|
compliance.addToBlacklist(user1, "OFAC");
|
|
|
|
vm.expectRevert("Compliance: buyer blacklisted");
|
|
compliance.preTradeCheck(user1, user2, 100e6, ICoupon.CouponType.Utility);
|
|
}
|
|
|
|
function test_PreTradeCheckFrozenReverts() public {
|
|
vm.startPrank(admin);
|
|
compliance.setKycLevel(user1, 2);
|
|
compliance.setKycLevel(user2, 2);
|
|
compliance.freezeAccount(user2);
|
|
vm.stopPrank();
|
|
|
|
vm.expectRevert("Compliance: seller frozen");
|
|
compliance.preTradeCheck(user1, user2, 100e6, ICoupon.CouponType.Utility);
|
|
}
|
|
|
|
// === P2P Check ===
|
|
|
|
function test_P2pCheckRequiresTravelRule() public {
|
|
vm.startPrank(admin);
|
|
compliance.setKycLevel(user1, 2);
|
|
compliance.setKycLevel(user2, 2);
|
|
vm.stopPrank();
|
|
|
|
vm.expectRevert("Compliance: Travel Rule data required");
|
|
compliance.p2pComplianceCheck(user1, user2, 5000e6);
|
|
}
|
|
|
|
function test_P2pCheckWithTravelRuleSuccess() public {
|
|
vm.startPrank(admin);
|
|
compliance.setKycLevel(user1, 2);
|
|
compliance.setKycLevel(user2, 2);
|
|
vm.stopPrank();
|
|
|
|
vm.prank(officer);
|
|
compliance.recordTravelRule(user1, user2, keccak256("s"), keccak256("r"));
|
|
|
|
// 不应 revert
|
|
compliance.p2pComplianceCheck(user1, user2, 5000e6);
|
|
}
|
|
|
|
function test_P2pCheckBelowThresholdNoTravelRule() public {
|
|
// $2,999 无需 Travel Rule
|
|
compliance.p2pComplianceCheck(user1, user2, 2999e6);
|
|
}
|
|
}
|