// 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 "../utils/UInt.sol"; import "./MallBase.sol"; interface ICurrency is IERC20 { function decimals() external view returns (uint256); } contract BETokenMall is MallBase, ReentrancyGuard { using SafeERC20 for IERC20; using UInt for uint256; uint256 constant ROUND = 1000000; address public tokenAddress; address public seller; // currency => price mapping(address => uint256) public prices; // Events event BuyTransaction( address indexed buyer, address tokenAddress, address currency, uint256 amount, uint256 tokenAmount ); event TokenAddressUpdated(address tokenAddress); event SellerUpdated(address seller); event UpdateTokenPrice(address currency, uint256 price, uint256 pricePre); /** * @dev Constructor */ constructor(address _tokenAddress, address _seller) { tokenAddress = _tokenAddress; seller = _seller; } /** * @dev Update token address */ function updateTokenAddress(address _tokenAddress) external onlyOwner { tokenAddress = _tokenAddress; emit TokenAddressUpdated(_tokenAddress); } /** * @dev Update seller address */ function updateSeller(address _seller) external onlyOwner { seller = _seller; emit SellerUpdated(_seller); } /** * @dev Update token price */ function updateTokenPrice( address currency, uint256 price ) external onlyOwner { uint256 pricePre = prices[currency]; prices[currency] = price; emit UpdateTokenPrice(currency, price, pricePre); } /** * @dev Buy token */ function buyToken(address currency, uint256 amount) external nonReentrant { require(amount > 0, "BETokenMall: invalid amount"); require(erc20Supported[currency], "BETokenMall: invalid payment method"); require(prices[currency] > 0, "BETokenMall: invalid token price"); // calc currency amount uint256 currencyDecimal = ICurrency(currency).decimals(); uint256 tokenAmount = (prices[currency] * amount * (10 ** (18 - currencyDecimal))) / ROUND; address buyer = _msgSender(); require( IERC20(tokenAddress).balanceOf(seller) >= tokenAmount, "BETokenMall: seller doesn't have enough token to sell this item" ); require( IERC20(tokenAddress).allowance(seller, address(this)) >= tokenAmount, "BETokenMall: seller doesn't approve enough token to sell this item" ); // Check payment approval and buyer balance require( IERC20(currency).balanceOf(buyer) >= amount, "BETokenMall: buyer doesn't have enough token to buy this item" ); require( IERC20(currency).allowance(buyer, address(this)) >= amount, "BETokenMall: buyer doesn't approve enough token to buy this item" ); // Transfer payment to seller IERC20(currency).safeTransferFrom(buyer, feeToAddress, amount); // Transfer token to buyer IERC20(tokenAddress).safeTransferFrom(seller, buyer, tokenAmount); // emit buy event emit BuyTransaction(buyer, tokenAddress, currency, amount, tokenAmount); } }