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

124 lines
3.9 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 "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "./interfaces/ICouponBatch.sol";
import "./interfaces/ICompliance.sol";
/// @title Redemption1155 — ERC-1155 券兑付合约
/// @notice 支持多单位一次性兑付burn平台不介入资金
/// @dev Transparent Proxy 部署。消费者持券到店兑付,合约负责销毁。
contract Redemption1155 is Initializable, AccessControlUpgradeable, ReentrancyGuardUpgradeable {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
ICouponBatch public couponBatchContract;
ICompliance public compliance;
struct RedemptionRecord {
uint256 typeId;
uint256 amount; // 本次兑付数量
address consumer;
address issuer;
bytes32 storeId;
uint256 redeemedAt;
}
uint256 public totalRedemptions;
mapping(uint256 => RedemptionRecord) public redemptions;
// --- Events ---
event BatchCouponRedeemed(
uint256 indexed typeId,
uint256 amount,
address indexed consumer,
address indexed issuer,
bytes32 storeId,
uint256 redemptionId
);
function initialize(
address _couponBatchContract,
address _compliance,
address admin
) external initializer {
__AccessControl_init();
__ReentrancyGuard_init();
couponBatchContract = ICouponBatch(_couponBatchContract);
compliance = ICompliance(_compliance);
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(ADMIN_ROLE, admin);
}
/// @notice 兑付 `amount` 单位的 typeId 券
/// @param typeId ERC-1155 token type ID
/// @param amount 兑付数量
/// @param storeId 门店 ID 哈希
function redeem(
uint256 typeId,
uint256 amount,
bytes32 storeId
) external nonReentrant {
require(amount > 0, "Redemption1155: zero amount");
// 余额检查(隐含所有权验证)
require(
couponBatchContract.balanceOf(msg.sender, typeId) >= amount,
"Redemption1155: insufficient balance"
);
// 合规检查
require(!compliance.isBlacklisted(msg.sender), "Redemption1155: blacklisted");
ICouponBatch.BatchCouponConfig memory config = couponBatchContract.getConfig(typeId);
// 到期检查
require(block.timestamp <= config.expiryDate, "Redemption1155: coupon expired");
// 门店限制检查
if (config.allowedStores.length > 0) {
require(
_isAllowedStore(storeId, config.allowedStores),
"Redemption1155: store not allowed"
);
}
// 销毁券
couponBatchContract.burn(msg.sender, typeId, amount);
// 记录兑付
uint256 redemptionId = totalRedemptions++;
redemptions[redemptionId] = RedemptionRecord({
typeId: typeId,
amount: amount,
consumer: msg.sender,
issuer: config.issuer,
storeId: storeId,
redeemedAt: block.timestamp
});
emit BatchCouponRedeemed(
typeId, amount, msg.sender, config.issuer, storeId, redemptionId
);
}
/// @notice 查询兑付记录
function getRedemption(uint256 redemptionId) external view returns (RedemptionRecord memory) {
return redemptions[redemptionId];
}
/// @dev 门店白名单检查
function _isAllowedStore(
bytes32 storeId,
bytes32[] memory allowedStores
) internal pure returns (bool) {
for (uint256 i = 0; i < allowedStores.length; i++) {
if (allowedStores[i] == storeId) return true;
}
return false;
}
}