181 lines
5.6 KiB
Solidity
181 lines
5.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "forge-std/Test.sol";
|
|
import "../src/CouponFactory.sol";
|
|
import "../src/Coupon.sol";
|
|
import "../src/Compliance.sol";
|
|
import "../src/interfaces/ICoupon.sol";
|
|
|
|
contract CouponFactoryTest is Test {
|
|
CouponFactory factory;
|
|
Coupon coupon;
|
|
Compliance compliance;
|
|
|
|
address admin = address(1);
|
|
address issuer = address(2);
|
|
address minter = address(3);
|
|
|
|
function setUp() public {
|
|
vm.startPrank(admin);
|
|
|
|
// 部署 Compliance
|
|
compliance = new Compliance();
|
|
compliance.initialize(admin);
|
|
|
|
// 部署 Coupon
|
|
coupon = new Coupon();
|
|
coupon.initialize("Genex Coupon", "GXC", admin);
|
|
|
|
// 部署 CouponFactory
|
|
factory = new CouponFactory();
|
|
factory.initialize(address(coupon), address(compliance), admin);
|
|
|
|
// 授权
|
|
coupon.grantRole(keccak256("FACTORY_ROLE"), address(factory));
|
|
factory.grantRole(keccak256("MINTER_ROLE"), minter);
|
|
|
|
// 设置发行方 KYC
|
|
compliance.setKycLevel(issuer, 3);
|
|
|
|
vm.stopPrank();
|
|
}
|
|
|
|
function test_MintBatchUtility() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
uint256[] memory tokenIds = factory.mintBatch(issuer, 100e6, 5, config);
|
|
|
|
assertEq(tokenIds.length, 5);
|
|
for (uint256 i = 0; i < 5; i++) {
|
|
assertEq(coupon.ownerOf(tokenIds[i]), issuer);
|
|
assertEq(coupon.getFaceValue(tokenIds[i]), 100e6);
|
|
}
|
|
}
|
|
|
|
function test_MintBatchSecurity() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _securityConfig();
|
|
uint256[] memory tokenIds = factory.mintBatch(issuer, 1000e6, 3, config);
|
|
|
|
assertEq(tokenIds.length, 3);
|
|
}
|
|
|
|
function test_UtilityMaxPriceExceedsFaceValue() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
config.maxPrice = 200e6; // 超过面值 100e6
|
|
|
|
vm.expectRevert("Utility: maxPrice <= faceValue");
|
|
factory.mintBatch(issuer, 100e6, 1, config);
|
|
}
|
|
|
|
function test_UtilityMaxExpiry12Months() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
config.expiryDate = block.timestamp + 400 days;
|
|
|
|
vm.expectRevert("Utility: max 12 months");
|
|
factory.mintBatch(issuer, 100e6, 1, config);
|
|
}
|
|
|
|
function test_SecurityRequiresExpiry() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _securityConfig();
|
|
config.expiryDate = 0;
|
|
|
|
vm.expectRevert("Security: expiry required");
|
|
factory.mintBatch(issuer, 1000e6, 1, config);
|
|
}
|
|
|
|
function test_SecurityRequiresKycL3() public {
|
|
address lowKycIssuer = address(10);
|
|
vm.prank(admin);
|
|
compliance.setKycLevel(lowKycIssuer, 1);
|
|
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _securityConfig();
|
|
|
|
vm.expectRevert("Compliance: insufficient KYC level");
|
|
factory.mintBatch(lowKycIssuer, 1000e6, 1, config);
|
|
}
|
|
|
|
function test_OnlyMinterCanMint() public {
|
|
vm.prank(issuer);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
|
|
vm.expectRevert();
|
|
factory.mintBatch(issuer, 100e6, 1, config);
|
|
}
|
|
|
|
function test_ZeroQuantityReverts() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
|
|
vm.expectRevert("CouponFactory: invalid quantity");
|
|
factory.mintBatch(issuer, 100e6, 0, config);
|
|
}
|
|
|
|
function test_ExcessiveQuantityReverts() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
|
|
vm.expectRevert("CouponFactory: invalid quantity");
|
|
factory.mintBatch(issuer, 100e6, 10001, config);
|
|
}
|
|
|
|
function test_BatchInfoRecorded() public {
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
factory.mintBatch(issuer, 100e6, 3, config);
|
|
|
|
CouponFactory.BatchInfo memory batch = factory.getBatchInfo(0);
|
|
assertEq(batch.issuer, issuer);
|
|
assertEq(batch.faceValue, 100e6);
|
|
assertEq(batch.quantity, 3);
|
|
assertEq(batch.tokenIds.length, 3);
|
|
}
|
|
|
|
function testFuzz_MintBatchQuantity(uint256 quantity) public {
|
|
quantity = bound(quantity, 1, 100);
|
|
vm.prank(minter);
|
|
ICoupon.CouponConfig memory config = _utilityConfig();
|
|
uint256[] memory tokenIds = factory.mintBatch(issuer, 100e6, quantity, config);
|
|
assertEq(tokenIds.length, quantity);
|
|
}
|
|
|
|
// --- Helpers ---
|
|
|
|
function _utilityConfig() internal view returns (ICoupon.CouponConfig memory) {
|
|
bytes32[] memory stores = new bytes32[](0);
|
|
return ICoupon.CouponConfig({
|
|
couponType: ICoupon.CouponType.Utility,
|
|
transferable: true,
|
|
maxResaleCount: 3,
|
|
maxPrice: 100e6,
|
|
expiryDate: block.timestamp + 180 days,
|
|
minPurchase: 0,
|
|
stackable: false,
|
|
allowedStores: stores,
|
|
issuer: issuer,
|
|
faceValue: 100e6
|
|
});
|
|
}
|
|
|
|
function _securityConfig() internal view returns (ICoupon.CouponConfig memory) {
|
|
bytes32[] memory stores = new bytes32[](0);
|
|
return ICoupon.CouponConfig({
|
|
couponType: ICoupon.CouponType.Security,
|
|
transferable: true,
|
|
maxResaleCount: 10,
|
|
maxPrice: 0,
|
|
expiryDate: block.timestamp + 365 days,
|
|
minPurchase: 0,
|
|
stackable: false,
|
|
allowedStores: stores,
|
|
issuer: issuer,
|
|
faceValue: 1000e6
|
|
});
|
|
}
|
|
}
|