becrypto/contracts/market/GameItemMarket.sol
2023-06-30 18:48:18 +08:00

127 lines
3.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../core/HasSignature.sol";
import "../utils/TimeChecker.sol";
import "./MallBase.sol";
/**
* @title GameItemMarket
* @dev GameItemMarket is a contract for users sell item in game.
*/
contract GameItemMarket is
MallBase,
ReentrancyGuard,
HasSignature,
TimeChecker
{
using SafeERC20 for IERC20;
mapping(uint256 => address) public orderIdUsed;
uint256 constant ROUND = 1000000;
uint256 public transactionFee = (3 * ROUND) / 100; // 3%
// min transaction fee is: 0
uint256 public constant MIN_TRANSACTION_FEE = 0;
// max transaction fee is: 10%
uint256 public constant MAX_TRANSACTION_FEE = (10 * ROUND) / 100;
event ItemSoldOut(
address indexed buyer,
address indexed seller,
uint256 indexed orderId,
address currency,
uint256 price
);
function buy(
uint256 orderId,
address seller,
address currency,
uint256 price,
uint256 startTime,
uint256 saltNonce,
bytes calldata signature
) external nonReentrant signatureValid(signature) timeValid(startTime) {
// check if orderId is used
require(
orderIdUsed[orderId] == address(0),
"GameItemMarket: orderId is used"
);
// check if currency is supported
require(
erc20Supported[currency],
"GameItemMarket: currency is not supported"
);
// check if price is valid
require(price > 0, "GameItemMarket: price is zero");
bytes32 criteriaMessageHash = getMessageHash(
_msgSender(),
seller,
orderId,
currency,
price,
startTime,
saltNonce
);
checkSigner(executor, criteriaMessageHash, signature);
require(
IERC20(currency).balanceOf(_msgSender()) >= price,
"GameItemMarket: buyer doesn't have enough token to buy this item"
);
require(
IERC20(currency).allowance(_msgSender(), address(this)) >= price,
"GameItemMarket: buyer doesn't approve marketplace to spend payment amount"
);
uint256 _transactionFee = (price * transactionFee) / ROUND;
if (_transactionFee > 0) {
IERC20(currency).safeTransferFrom(
_msgSender(),
feeToAddress,
_transactionFee
);
}
IERC20(currency).safeTransferFrom(
_msgSender(),
seller,
price - _transactionFee
);
orderIdUsed[orderId] = _msgSender();
_useSignature(signature);
emit ItemSoldOut(_msgSender(), seller, orderId, currency, price);
}
function setTransactionFee(uint256 _transactionFee) external onlyOwner {
require(
_transactionFee >= MIN_TRANSACTION_FEE &&
_transactionFee <= MAX_TRANSACTION_FEE,
"GameItemMarket: _transactionFee must >= 0 and <= 10%"
);
transactionFee = _transactionFee;
}
function getMessageHash(
address _buyer,
address _seller,
uint256 _orderId,
address _currency,
uint256 _price,
uint256 _startTime,
uint256 _saltNonce
) public pure returns (bytes32) {
bytes memory encoded = abi.encodePacked(
_buyer,
_seller,
_orderId,
_currency,
_price,
_startTime,
_saltNonce
);
return keccak256(encoded);
}
}