gcx/blockchain/genex-contracts/test/Settlement.t.sol

193 lines
5.7 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Settlement.sol";
import "../src/Coupon.sol";
import "../src/Compliance.sol";
import "../src/interfaces/ICoupon.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockUSDC is ERC20 {
constructor() ERC20("USD Coin", "USDC") {
_mint(msg.sender, 1_000_000e6);
}
function decimals() public pure override returns (uint8) {
return 6;
}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
contract SettlementTest is Test {
Settlement settlement;
Coupon coupon;
Compliance compliance;
MockUSDC usdc;
MockUSDC usdt;
address admin = address(1);
address buyer = address(4);
address seller = address(5);
function setUp() public {
vm.startPrank(admin);
usdc = new MockUSDC();
usdt = new MockUSDC();
compliance = new Compliance();
compliance.initialize(admin);
coupon = new Coupon();
coupon.initialize("Genex Coupon", "GXC", admin);
settlement = new Settlement();
settlement.initialize(address(coupon), address(compliance), address(usdc), admin);
// 授权
coupon.grantRole(keccak256("FACTORY_ROLE"), admin);
coupon.grantRole(keccak256("SETTLER_ROLE"), address(settlement));
// KYC
compliance.setKycLevel(buyer, 1);
compliance.setKycLevel(seller, 1);
// 给买方 USDC
usdc.mint(buyer, 1000e6);
vm.stopPrank();
// Buyer approve settlement
vm.prank(buyer);
usdc.approve(address(settlement), type(uint256).max);
// Seller approve coupon to settlement
vm.prank(seller);
coupon.setApprovalForAll(address(settlement), true);
}
function test_AtomicSwapSuccess() public {
uint256 tokenId = _mintCouponToSeller(100e6);
vm.prank(admin);
settlement.executeSwap(tokenId, buyer, seller, 85e6, address(usdc));
assertEq(coupon.ownerOf(tokenId), buyer);
assertEq(usdc.balanceOf(seller), 85e6);
}
function test_UtilityCannotExceedMaxPrice() public {
uint256 tokenId = _mintCouponToSeller(100e6);
vm.prank(admin);
vm.expectRevert("Utility: price exceeds max price");
settlement.executeSwap(tokenId, buyer, seller, 110e6, address(usdc));
}
function test_RefundReverseSwap() public {
uint256 tokenId = _mintCouponToSeller(100e6);
// 先正常交易
vm.prank(admin);
settlement.executeSwap(tokenId, buyer, seller, 85e6, address(usdc));
// Seller 退 USDC approve
vm.prank(seller);
usdc.approve(address(settlement), type(uint256).max);
// Buyer approve coupon
vm.prank(buyer);
coupon.setApprovalForAll(address(settlement), true);
// 退款
vm.prank(admin);
settlement.executeRefund(tokenId, buyer, seller, 85e6, address(usdc));
assertEq(coupon.ownerOf(tokenId), seller);
}
function test_BlacklistedBuyerReverts() public {
uint256 tokenId = _mintCouponToSeller(100e6);
vm.prank(admin);
compliance.addToBlacklist(buyer, "OFAC");
vm.prank(admin);
vm.expectRevert("Compliance: buyer blacklisted");
settlement.executeSwap(tokenId, buyer, seller, 85e6, address(usdc));
}
function test_MaxResaleCountBlocks() public {
uint256 tokenId = _mintCouponToSeller(100e6);
// 交易 3 次达到上限
for (uint256 i = 0; i < 3; i++) {
vm.prank(admin);
settlement.executeSwap(tokenId, buyer, seller, 50e6, address(usdc));
// Reset: transfer back + fund buyer (skip after last iteration
// because resaleCount == maxResaleCount and P2P transfer would fail)
if (i < 2) {
vm.prank(buyer);
coupon.safeTransferFrom(buyer, seller, tokenId);
vm.prank(admin);
usdc.mint(buyer, 50e6);
}
}
// 第 4 次应失败
vm.prank(admin);
vm.expectRevert("Settlement: max resale count exceeded");
settlement.executeSwap(tokenId, buyer, seller, 50e6, address(usdc));
}
function test_ExpiredCouponReverts() public {
uint256 tokenId = _mintCouponToSeller(100e6);
// 快进到过期
vm.warp(block.timestamp + 200 days);
vm.prank(admin);
vm.expectRevert("Settlement: coupon expired");
settlement.executeSwap(tokenId, buyer, seller, 85e6, address(usdc));
}
function test_UnsupportedStablecoinReverts() public {
uint256 tokenId = _mintCouponToSeller(100e6);
vm.prank(admin);
vm.expectRevert("Settlement: unsupported stablecoin");
settlement.executeSwap(tokenId, buyer, seller, 85e6, address(usdt));
}
function test_AddStablecoin() public {
vm.prank(admin);
settlement.addStablecoin(address(usdt));
assertTrue(settlement.supportedStablecoins(address(usdt)));
}
// --- Helpers ---
function _mintCouponToSeller(uint256 faceValue) internal returns (uint256) {
bytes32[] memory stores = new bytes32[](0);
ICoupon.CouponConfig memory config = ICoupon.CouponConfig({
couponType: ICoupon.CouponType.Utility,
transferable: true,
maxResaleCount: 3,
maxPrice: faceValue,
expiryDate: block.timestamp + 180 days,
minPurchase: 0,
stackable: false,
allowedStores: stores,
issuer: admin,
faceValue: faceValue
});
vm.prank(admin);
return coupon.mint(seller, faceValue, config);
}
}