// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract SafeMoneyUp is ERC20, Ownable {
using SafeMath for uint256;
uint256 private constant PRECISION = 1e18;
uint256 private constant FIXED_SUPPLY = 400 * 1e12 * PRECISION;
mapping(address => bool) public excludedFromFees;
mapping(address => bool) public isBlacklisted;
uint256 private adminFeeTokens;
uint256 public adminFee = 50; // 0.5% (50/10000)
event tokenbought (uint256 eth, uint256 tokens);
event tokenSold (uint eth, uint256 tokens);
constructor() ERC20("SafeMoney Up", "SMU") {
_mint(address(this), FIXED_SUPPLY);
excludedFromFees[owner()] = true;
}
function buyTokens() public payable {
require(!isBlacklisted[msg.sender], "Address is blacklisted");
require(msg.value > 0, "Amount must be greater than 0");
uint256 supplyWithoutContractAndBurn = totalSupply() - balanceOf(address(this)) - balanceOf(address(0)) - adminFeeTokens;
uint256 ethAmountInPool = address(this).balance;
uint256 perTokenPrice = supplyWithoutContractAndBurn.div(ethAmountInPool);
uint256 tokensToBuy = msg.value.mul(perTokenPrice);
if (!excludedFromFees[msg.sender]) {
uint256 ethForAdmin = (msg.value * adminFee) / 10000;
(bool sent,) = owner().call{value: ethForAdmin}("");
require(sent, "eth fees to admin failed");
tokensToBuy = (tokensToBuy * (1000 - 20))/1000;
}
_transfer(address(this), msg.sender, tokensToBuy);
emit tokenbought (msg.value, tokensToBuy);
}
function sellTokens(uint256 tokensToSell) public {
require(!isBlacklisted[msg.sender], "Address is blacklisted");
require(tokensToSell > 0, "Amount must be greater than 0");
uint256 supplyWithoutContractAndBurn;
if (totalSupply() > balanceOf(address(this)) + balanceOf(address(0))){
supplyWithoutContractAndBurn = totalSupply() - balanceOf(address(this)) - balanceOf(address(0)) - adminFeeTokens;
}else {
supplyWithoutContractAndBurn = totalSupply();
}
uint256 ethAmountInPool = address(this).balance;
uint256 perTokenPrice = supplyWithoutContractAndBurn.div(ethAmountInPool);
uint256 ethAmount = tokensToSell.div(perTokenPrice);
_transfer(msg.sender, address(this), tokensToSell);
if (!excludedFromFees[msg.sender]) {
uint256 ethWithFee = (ethAmount * (1000 - (20))) / 1000;
uint256 tokensForAdmin = (tokensToSell * adminFee) / 10000;
ethAmount = ethWithFee;
require (address(this).balance >= ethAmount, "not enough eth in the pool");
_transfer (address(this), owner(), tokensForAdmin); //deduct fee in tokens
adminFeeTokens += tokensForAdmin;
}
(bool sent,) = msg.sender.call{value: ethAmount}("");
require(sent, "eth fees to admin failed");
emit tokenSold (ethAmount, tokensToSell);
}
function setAdminFee(uint256 newAdminFee) public onlyOwner {
require(newAdminFee >= 0 && newAdminFee <= 1000, "Invalid fee value"); //max admin fee is 10 percent
adminFee = newAdminFee;
}
function excludeFromFees(address account, bool excluded) public onlyOwner {
excludedFromFees[account] = excluded;
}
function setBlacklistStatus(address account, bool blacklisted) public onlyOwner {
isBlacklisted[account] = blacklisted;
}
function injectInitialETHLiquidity () public onlyOwner payable {
require (msg.value > 0, "eth amount should be greator than 0");
}
function withdrawToken (address token, uint256 amount) external onlyOwner {
IERC20 TOKEN = IERC20(token);
TOKEN.transfer(owner(), amount);
}
function withdrawETH (uint256 amount) external onlyOwner {
(bool sent,) = owner().call{value: amount}("");
require (sent, "eth transfer failed");
}
receive() external payable {
require(msg.sender != owner(), "Owner cannot send Ether directly");
}
}