// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract SafeMoneyUp is ERC20, Ownable {
uint256 private constant PRECISION = 1e18;
uint256 private constant FIXED_SUPPLY = 400 * 1e12 * PRECISION;
mapping(address => bool) public excludedFromFees;
mapping(address => bool) public isBlacklisted;
uint256 public adminFee = 50; // 0.5% (50/10000)
constructor() ERC20("SafeMoney Up", "SMU") {
_mint(address(this), FIXED_SUPPLY);
excludedFromFees[owner()] = true;
}
///@notice user can buy tokens by calling this function.
///@notice user shouldn't be blacklisted and smartcontract should have enough tokens to process his buy
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));
uint256 ethAmountInPool = address(this).balance;
uint256 perTokenPrice = supplyWithoutContractAndBurn / ethAmountInPool;
uint256 tokensToBuy = msg.value/perTokenPrice;
require (balanceOf(address(this)) >= tokensToBuy, "not enough tokens in the pool");
uint256 fee = (msg.value * adminFee) / 10000;
uint256 tokensWithFee = (tokensToBuy * 98) / 100; // 2% fee
if (!excludedFromFees[msg.sender]) {
tokensToBuy = tokensWithFee;
require (balanceOf(address(this)) >= tokensToBuy, "not enough tokens in the pool");
(bool sent,) = owner().call{value: fee}("");
require(sent, "eth fees to admin failed");
}
_transfer(address(this), msg.sender, tokensToBuy);
}
/// @param tokensToSell - number of tokens users want to sell.
/// @notice user can call this function to sell his/her tokens.
function sellTokens(uint256 tokensToSell) public {
require(!isBlacklisted[msg.sender], "Address is blacklisted");
require(tokensToSell > 0, "Amount must be greater than 0");
uint256 supplyWithoutContractAndBurn = totalSupply() - balanceOf(address(this)) - balanceOf(address(0));
uint256 ethAmountInPool = address(this).balance;
uint256 perTokenPrice = supplyWithoutContractAndBurn / ethAmountInPool;
uint256 ethAmount = tokensToSell * perTokenPrice;
require (address(this).balance >= ethAmount, "not enough eth in the pool");
uint256 fee = (tokensToSell * adminFee) / 10000;
uint256 ethWithFee = (ethAmount * 98) / 100; // 2% fee
_transfer(msg.sender, address(this), tokensToSell);
if (!excludedFromFees[msg.sender]) {
ethAmount = ethWithFee;
require (address(this).balance >= ethAmount, "not enough eth in the pool");
_transfer (address(this), owner(), fee); //deduct fee in tokens
}
payable(msg.sender).transfer(ethAmount);
}
///@param newAdminFee amount of fees in basis point
///@dev set admin fees, limited to 10 percent.
function setAdminFee(uint256 newAdminFee) public onlyOwner {
require(newAdminFee >= 0 && newAdminFee <= 1000, "Invalid fee value"); //max admin fee is 10 percent
adminFee = newAdminFee;
}
///@param account - user address
///@param excluded - boolean value
///@dev excluded or include someone from/in fees
function excludeFromFees(address account, bool excluded) public onlyOwner {
excludedFromFees[account] = excluded;
}
///@param account - user address
///@param blacklisted - boolean value
///@dev manage blacklist of users using this function.
function setBlacklistStatus(address account, bool blacklisted) public onlyOwner {
isBlacklisted[account] = blacklisted;
}
/// @dev owner can add initial liquidity using this function (eth)
function injectInitialETHLiquidity () public onlyOwner payable {
require (msg.value > 0, "eth amount should be greator than 0");
}
/// @param - tokenAddress - address of ERC20 token
/// @param - amount - amount to withdraw from smart contract
/// @dev - owner can withdraw any ERC20 token using this function
function withdrawToken(address tokenAddress, uint256 amount) public onlyOwner {
require(tokenAddress != address(0), "not a valid token");
IERC20 token = IERC20(tokenAddress);
uint256 tokenBalance = token.balanceOf(address(this));
require(amount <= tokenBalance, "Not enough tokens in contract");
token.transfer(owner(), amount);
}
/// @dev withdraw eth from contract
withdrawETH (uint256 amount) external onlyOwner {
(bool sent,) = owner().call{value: amount}("");
require (sent, "eth transfer failed");
}
/// @notice receive any external ether sent by user (except owner of this smartcontract)
receive() external payable {
require(msg.sender != owner(), "Owner cannot send Ether directly");
}
}