// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; interface IUniswapV2Factory { event PairCreated( address indexed token0, address indexed token1, address pair, uint256 ); function feeTo() external view returns (address); function feeToSetter() external view returns (address); function getPair(address tokenA, address tokenB) external view returns (address pair); function allPairs(uint256) external view returns (address pair); function allPairsLength() external view returns (uint256); function createPair(address tokenA, address tokenB) external returns (address pair); function setFeeTo(address) external; function setFeeToSetter(address) external; } interface IUniswapV2Pair { event Approval( address indexed owner, address indexed spender, uint256 value ); event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; event Mint(address indexed sender, uint256 amount0, uint256 amount1); event Burn( address indexed sender, uint256 amount0, uint256 amount1, address indexed to ); event Swap( address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint256); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast ); function price0CumulativeLast() external view returns (uint256); function price1CumulativeLast() external view returns (uint256); function kLast() external view returns (uint256); function mint(address to) external returns (uint256 liquidity); function burn(address to) external returns (uint256 amount0, uint256 amount1); function swap( uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data ) external; function skim(address to) external; function sync() external; function initialize(address, address) external; } interface IUniswapV2Router02 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns ( uint256 amountToken, uint256 amountETH, uint256 liquidity ); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; } contract Memo is ERC20, Ownable { //Memo board variables struct MemoStruct { uint256 value; address sender; uint64 timestamp; uint8 index; string text; } uint256 public constant maxMemos = 10; uint256 public constant maxChars = 141; uint256 public memoLeaderValue; address public memoLeaderAddress; MemoStruct[maxMemos] public memos; // Token variables IUniswapV2Router02 public immutable uniswapV2Router; address public immutable uniswapV2Pair; address public constant deadAddress = address(0xdead); bool private swapping; address public teamWallet; uint256 public totalSupplyValue = 1_000_000 * 1e18; uint256 public maxTransactionAmount = 10_000 * 1e18; // 1% uint256 public maxWallet = 10_000 * 1e18; // 1% uint256 public swapTokensAtAmount = (totalSupplyValue * 5) / 10000; // 0.05% bool public limitsInEffect = true; bool public tradingActive = false; bool public swapEnabled = false; bool public blacklistRenounced = false; // Anti-bot and anti-whale mappings and variables mapping(address => bool) blacklisted; // both buy and sell fees are the same, no need for separate variables uint256 public constant totalFees = 4; uint256 public constant leaderFee = 3; uint256 public constant teamFee = 1; uint256 public tokensForLeader; uint256 public tokensForTeam; /******************/ // exclude from fees and max transaction amount mapping(address => bool) private _isExcludedFromFees; mapping(address => bool) public _isExcludedMaxTransactionAmount; // store addresses that a automatic market maker pairs. Any transfer *to* these addresses // could be subject to a maximum transfer amount mapping(address => bool) public automatedMarketMakerPairs; event UpdateUniswapV2Router( address indexed newAddress, address indexed oldAddress ); event SetAutomatedMarketMakerPair( address indexed pair, bool indexed value ); event newLeaderHasEmerges( address indexed newWallet, address indexed oldWallet ); constructor() ERC20("Memo", "MEMO") { IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02( 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D ); excludeFromMaxTransaction(address(_uniswapV2Router), true); uniswapV2Router = _uniswapV2Router; uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory()) .createPair(address(this), _uniswapV2Router.WETH()); excludeFromMaxTransaction(address(uniswapV2Pair), true); _setAutomatedMarketMakerPair(address(uniswapV2Pair), true); teamWallet = owner(); _isExcludedFromFees[owner()] = true; _isExcludedFromFees[address(this)] = true; _isExcludedFromFees[deadAddress] = true; _isExcludedMaxTransactionAmount[owner()] = true; _isExcludedMaxTransactionAmount[address(this)] = true; _isExcludedMaxTransactionAmount[deadAddress] = true; uint256 totalSupply = 1_000_000 * 1e18; _mint(msg.sender, totalSupply); } receive() external payable {} // once enabled, can never be turned off function enableTrading() external onlyOwner { tradingActive = true; swapEnabled = true; } // remove limits after token is stable function removeLimits() external onlyOwner returns (bool) { limitsInEffect = false; return true; } // change the minimum amount of tokens to sell from fees function updateSwapTokensAtAmount(uint256 newAmount) external onlyOwner returns (bool) { require( newAmount >= (totalSupply() * 1) / 100000, "Swap amount cannot be lower than 0.001% total supply." ); require( newAmount <= (totalSupply() * 5) / 1000, "Swap amount cannot be higher than 0.5% total supply." ); swapTokensAtAmount = newAmount; return true; } function excludeFromMaxTransaction(address updAds, bool isEx) public onlyOwner { _isExcludedMaxTransactionAmount[updAds] = isEx; } function excludeFromFees(address account, bool excluded) public onlyOwner { _isExcludedFromFees[account] = excluded; } function setAutomatedMarketMakerPair(address pair, bool value) public onlyOwner { require( pair != uniswapV2Pair, "The pair cannot be removed from automatedMarketMakerPairs" ); _setAutomatedMarketMakerPair(pair, value); } function _setAutomatedMarketMakerPair(address pair, bool value) private { automatedMarketMakerPairs[pair] = value; emit SetAutomatedMarketMakerPair(pair, value); } function updateTeamWallet(address newWallet) external onlyOwner { teamWallet = newWallet; } function isExcludedFromFees(address account) public view returns (bool) { return _isExcludedFromFees[account]; } function isBlacklisted(address account) public view returns (bool) { return blacklisted[account]; } function _transfer( address from, address to, uint256 amount ) internal override { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); require(!blacklisted[from],"Sender blacklisted"); require(!blacklisted[to],"Receiver blacklisted"); if (amount == 0) { super._transfer(from, to, 0); return; } if (limitsInEffect) { if ( from != owner() && to != owner() && to != address(0) && to != address(0xdead) && !swapping ) { if (!tradingActive) { require( _isExcludedFromFees[from] || _isExcludedFromFees[to], "Trading is not active." ); } //when buy if ( automatedMarketMakerPairs[from] && !_isExcludedMaxTransactionAmount[to] ) { require( amount <= maxTransactionAmount, "Buy transfer amount exceeds the maxTransactionAmount." ); require( amount + balanceOf(to) <= maxWallet, "Max wallet exceeded" ); } //when sell else if ( automatedMarketMakerPairs[to] && !_isExcludedMaxTransactionAmount[from] ) { require( amount <= maxTransactionAmount, "Sell transfer amount exceeds the maxTransactionAmount." ); } else if (!_isExcludedMaxTransactionAmount[to]) { require( amount + balanceOf(to) <= maxWallet, "Max wallet exceeded" ); } } } uint256 contractTokenBalance = balanceOf(address(this)); bool canSwap = contractTokenBalance >= swapTokensAtAmount; if ( canSwap && swapEnabled && !swapping && !automatedMarketMakerPairs[from] && !_isExcludedFromFees[from] && !_isExcludedFromFees[to] ) { swapping = true; swapBack(); swapping = false; } bool takeFee = !swapping; // if any account belongs to _isExcludedFromFee account then remove the fee if (_isExcludedFromFees[from] || _isExcludedFromFees[to]) { takeFee = false; } uint256 fees = 0; // only take fees on buys/sells, do not take on wallet transfers if (takeFee) { // on sell if (automatedMarketMakerPairs[to] ) { fees = (amount * totalFees) / 100; tokensForTeam += (fees * teamFee) / totalFees; tokensForLeader += (fees * leaderFee) / totalFees; } // on buy else if (automatedMarketMakerPairs[from]) { fees = (amount * totalFees) / 100; tokensForTeam += (fees * teamFee) / totalFees; tokensForLeader += (fees * leaderFee) / totalFees; } if (fees > 0) { super._transfer(from, address(this), fees); } amount -= fees; } super._transfer(from, to, amount); } function swapTokensForEth(uint256 tokenAmount) private { // generate the uniswap pair path of token -> weth address[] memory path = new address[](2); path[0] = address(this); path[1] = uniswapV2Router.WETH(); _approve(address(this), address(uniswapV2Router), tokenAmount); // make the swap uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens( tokenAmount, 0, // accept any amount of ETH path, address(this), block.timestamp ); } function swapBack() private { uint256 contractBalance = balanceOf(address(this)); uint256 totalTokensToSwap = tokensForLeader + tokensForTeam; bool success; if (contractBalance == 0 || totalTokensToSwap == 0) { return; } if (contractBalance > swapTokensAtAmount * 20) { contractBalance = swapTokensAtAmount * 20; } uint256 amountToSwapForETH = contractBalance; uint256 initialETHBalance = address(this).balance; swapTokensForEth(amountToSwapForETH); uint256 ethBalance = address(this).balance - initialETHBalance; uint256 ethForTeam = (ethBalance * tokensForTeam) / totalTokensToSwap ; tokensForLeader = 0; tokensForTeam = 0; (success, ) = address(teamWallet).call{value: ethForTeam}(""); (success, ) = address(memoLeaderAddress).call{value: address(this).balance}(""); } function withdrawStuckToken(address _token, address _to) external onlyOwner { require(_token != address(0), "_token address cannot be 0"); uint256 _contractBalance = IERC20(_token).balanceOf(address(this)); IERC20(_token).transfer(_to, _contractBalance); } function withdrawStuckEth(address toAddr) external onlyOwner { (bool success, ) = toAddr.call{ value: address(this).balance } (""); require(success); } // @dev team renounce blacklist commands function renounceBlacklist() public onlyOwner { blacklistRenounced = true; } function blacklist(address _addr) public onlyOwner { require(!blacklistRenounced, "Team has revoked blacklist rights"); require( _addr != address(uniswapV2Pair) && _addr != address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D), "Cannot blacklist token's v2 router or v2 pool." ); blacklisted[_addr] = true; } // @dev blacklist v3 pools; can unblacklist() down the road to suit project and community function blacklistLiquidityPool(address lpAddress) public onlyOwner { require(!blacklistRenounced, "Team has revoked blacklist rights"); require( lpAddress != address(uniswapV2Pair) && lpAddress != address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D), "Cannot blacklist token's v2 router or v2 pool." ); blacklisted[lpAddress] = true; } // @dev unblacklist address; not affected by blacklistRenounced incase team wants to unblacklist v3 pools down the road function unblacklist(address _addr) public onlyOwner { blacklisted[_addr] = false; } // // memoboard functions // function sendMemo( string calldata _message, uint256 tokenValue) public { require(bytes(_message).length < maxChars, "message too long"); require(tokenValue > 1, "message value incorrect"); (bool qualifies, uint256 indexToRemove) = _getIndexToReplace(tokenValue); if (qualifies) { _insertMemo( msg.sender, _message, tokenValue, indexToRemove ); if (tokenValue > memoLeaderValue) { memoLeaderValue = tokenValue; memoLeaderAddress = msg.sender; } } else { revert("message value too low"); } _burn(msg.sender, tokenValue); } function boostMemo( uint8 memoIndex, uint64 memoTimestamp, uint256 boostValue) public { MemoStruct memory memoToBoost = memos[memoIndex]; require(memoToBoost.timestamp == memoTimestamp, "timestamp incorrect"); // confirm the boosted memo is the intented one require(memoToBoost.sender == msg.sender, "sender incorrect"); // confirm the boosted memo is the intented one memoToBoost.value += boostValue; if (memoToBoost.value > memoLeaderValue) { memoLeaderValue = memoToBoost.value; memoLeaderAddress = msg.sender; } _burn(msg.sender, boostValue); memos[memoIndex] = memoToBoost; } function _getIndexToReplace( uint256 _value ) private view returns (bool qualifies, uint256 indexToRemove) { uint256 lowIndex; uint256 lowValue = memos[0].value; uint256 dripedValue; for (uint256 i = 0; i < maxMemos; i++) { dripedValue = memos[i].value; if (dripedValue < lowValue) { lowIndex = i; lowValue = dripedValue; } if (qualifies == false && _value > dripedValue) { qualifies = true; } } return (qualifies, lowIndex); } function _insertMemo( address _from, string calldata _message, uint256 _value, uint256 _index ) private { MemoStruct memory newMemo; newMemo.sender = _from; newMemo.timestamp = uint64(block.timestamp); newMemo.index = uint8(_index); newMemo.text = _message; newMemo.value = _value; memos[_index] = newMemo; } // should only be used offchain. function getOrderedMemos() public view returns (MemoStruct[maxMemos] memory) { MemoStruct[maxMemos] memory _memos; for (uint256 i = 1; i < maxMemos; i++) { for (uint256 j = 0; j < i; j++) { if (_memos[i].value > _memos[j].value) { MemoStruct memory x = _memos[i]; _memos[i] = _memos[j]; _memos[j] = x; } } } return memos; } }
0.7.0