// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {HasSignature} from "../core/HasSignature.sol"; import {TimeChecker} from "../utils/TimeChecker.sol"; import {MallBase} from "./MallBase.sol"; /** * @title GameItemMall * @dev GameItemMall is a contract for managing centralized game items sale, * allowing users to buy item in game. */ contract GameItemMall is MallBase, ReentrancyGuard, HasSignature, TimeChecker { using SafeERC20 for IERC20; mapping(uint256 itemId => address user) public orderIdUsed; event ItemSoldOut( address indexed buyer, address indexed passport, uint256 indexed orderId, address currency, uint256 amount ); constructor(address _currency, address _feeToAddress, address _verifier, uint256 _duration) TimeChecker(_duration) MallBase(_currency, _feeToAddress, _verifier){ } function buy( address passport, uint256 orderId, address currency, uint256 amount, uint256 signTime, uint256 saltNonce, bytes calldata signature ) external nonReentrant signatureValid(signature) timeValid(signTime) { require(passport != address(0), "passport address can not be zero"); // check if orderId is used require(orderIdUsed[orderId] == address(0), "orderId is used"); // check if currency is supported require(erc20Supported[currency], "currency is not supported"); // check if amount is valid require(amount > 0, "amount is zero"); address buyer = _msgSender(); bytes32 criteriaMessageHash = getMessageHash( buyer, passport, orderId, currency, amount, _CACHED_THIS, _CACHED_CHAIN_ID, signTime, saltNonce ); checkSigner(verifier, criteriaMessageHash, signature); IERC20 paymentContract = IERC20(currency); _useSignature(signature); orderIdUsed[orderId] = buyer; paymentContract.safeTransferFrom(buyer, feeToAddress, amount); emit ItemSoldOut(buyer, passport, orderId, currency, amount); } function getMessageHash( address _buyer, address _passport, uint256 _orderId, address _currency, uint256 _amount, address _contract, uint256 _chainId, uint256 _signTime, uint256 _saltNonce ) public pure returns (bytes32) { bytes memory encoded = abi.encodePacked( _buyer, _passport, _orderId, _currency, _amount, _contract, _chainId, _signTime, _saltNonce ); return keccak256(encoded); } }