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

188 lines
5.6 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Redemption1155.sol";
import "../src/CouponBatch.sol";
import "../src/Compliance.sol";
import "../src/interfaces/ICouponBatch.sol";
contract Redemption1155Test is Test {
Redemption1155 redemption;
CouponBatch batch;
Compliance compliance;
address admin = address(1);
address factory = address(2);
address consumer = address(4);
address issuer = address(5);
bytes32 storeA = keccak256("STORE_A");
bytes32 storeB = keccak256("STORE_B");
function setUp() public {
vm.startPrank(admin);
// 部署 Compliance
compliance = new Compliance();
compliance.initialize(admin);
// 部署 CouponBatch
batch = new CouponBatch();
batch.initialize("https://genex.io/api/coupon-batch/{id}.json", admin);
// 部署 Redemption1155
redemption = new Redemption1155();
redemption.initialize(address(batch), address(compliance), admin);
// 角色授权
batch.grantRole(keccak256("FACTORY_ROLE"), factory);
batch.grantRole(keccak256("BURNER_ROLE"), address(redemption));
compliance.setKycLevel(consumer, 1);
vm.stopPrank();
}
function test_RedeemSuccess() public {
uint256 typeId = _mintBatch(consumer, 100e6, 10, false);
vm.prank(consumer);
redemption.redeem(typeId, 3, storeA);
// 余额减少
assertEq(batch.balanceOf(consumer, typeId), 7);
// 记录已创建
Redemption1155.RedemptionRecord memory record = redemption.getRedemption(0);
assertEq(record.typeId, typeId);
assertEq(record.amount, 3);
assertEq(record.consumer, consumer);
assertEq(record.issuer, issuer);
assertEq(record.storeId, storeA);
}
function test_RedeemAllUnits() public {
uint256 typeId = _mintBatch(consumer, 50e6, 5, false);
vm.prank(consumer);
redemption.redeem(typeId, 5, storeA);
assertEq(batch.balanceOf(consumer, typeId), 0);
assertEq(redemption.totalRedemptions(), 1);
}
function test_RedeemMultipleTimes() public {
uint256 typeId = _mintBatch(consumer, 100e6, 100, false);
vm.startPrank(consumer);
redemption.redeem(typeId, 30, storeA);
redemption.redeem(typeId, 50, storeA);
vm.stopPrank();
assertEq(batch.balanceOf(consumer, typeId), 20);
assertEq(redemption.totalRedemptions(), 2);
Redemption1155.RedemptionRecord memory r0 = redemption.getRedemption(0);
assertEq(r0.amount, 30);
Redemption1155.RedemptionRecord memory r1 = redemption.getRedemption(1);
assertEq(r1.amount, 50);
}
function test_RedeemZeroAmountReverts() public {
uint256 typeId = _mintBatch(consumer, 100e6, 10, false);
vm.prank(consumer);
vm.expectRevert("Redemption1155: zero amount");
redemption.redeem(typeId, 0, storeA);
}
function test_RedeemInsufficientBalanceReverts() public {
uint256 typeId = _mintBatch(consumer, 100e6, 5, false);
vm.prank(consumer);
vm.expectRevert("Redemption1155: insufficient balance");
redemption.redeem(typeId, 10, storeA);
}
function test_RedeemBlacklistedReverts() public {
uint256 typeId = _mintBatch(consumer, 100e6, 10, false);
vm.prank(admin);
compliance.addToBlacklist(consumer, "OFAC");
vm.prank(consumer);
vm.expectRevert("Redemption1155: blacklisted");
redemption.redeem(typeId, 1, storeA);
}
function test_RedeemExpiredReverts() public {
uint256 typeId = _mintBatch(consumer, 100e6, 10, false);
// 快进到过期
vm.warp(block.timestamp + 200 days);
vm.prank(consumer);
vm.expectRevert("Redemption1155: coupon expired");
redemption.redeem(typeId, 1, storeA);
}
function test_RedeemStoreRestricted() public {
uint256 typeId = _mintBatch(consumer, 100e6, 10, true);
// 不在允许门店列表中
bytes32 storeC = keccak256("STORE_C");
vm.prank(consumer);
vm.expectRevert("Redemption1155: store not allowed");
redemption.redeem(typeId, 1, storeC);
// 使用允许的门店
vm.prank(consumer);
redemption.redeem(typeId, 1, storeA);
assertEq(batch.balanceOf(consumer, typeId), 9);
}
function test_RedeemNoBalanceReverts() public {
uint256 typeId = _mintBatch(issuer, 100e6, 10, false);
// consumer 没有该 typeId 的余额
vm.prank(consumer);
vm.expectRevert("Redemption1155: insufficient balance");
redemption.redeem(typeId, 1, storeA);
}
// ==========================================
// Helpers
// ==========================================
function _mintBatch(
address to,
uint256 faceValue,
uint256 quantity,
bool withStores
) internal returns (uint256) {
bytes32[] memory stores;
if (withStores) {
stores = new bytes32[](2);
stores[0] = storeA;
stores[1] = storeB;
} else {
stores = new bytes32[](0);
}
ICouponBatch.BatchCouponConfig memory config = ICouponBatch.BatchCouponConfig({
transferable: true,
maxPrice: faceValue,
expiryDate: block.timestamp + 180 days,
minPurchase: 0,
stackable: false,
allowedStores: stores,
issuer: issuer,
faceValue: faceValue
});
vm.prank(factory);
return batch.mint(to, faceValue, quantity, config);
}
}