mail@pastecode.io avatarunknown
a month ago
8.5 kB
//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";

/// @title PUK: An erc20 token with sell fees
contract PUK is ERC20, Ownable {
            ///ERRORS //
      error MaxFeeLimitExceeded();
      error ZeroAddressNotAllowed();
      error UpdateBoolValue();
      error AmountNotInRange();
      error CanNotModifyMainPair();
      error NotAuthorized();
      /// @notice Max fees Limit     
      uint16 constant private MAX_FEE = 6;
      /// @notice Minimum tax tokens limit required to swap for BNB
      uint256 constant private MIN_SWAP_AT_AMOUNT = 1e3 * 1e18;
      /// @notice max limit to swap collected tax for bnb per tx
      uint256 constant private MAX_SWAP_AT_AMOUNT = 5e9 * 1e18;
      /// @notice global burn address
      address constant private DEAD = address(0xdead);
      /// @notice fees on every sell    
      uint16 public sellFees = 6;

      /// @notice fees wallet to receive bnb  
      address public feeWallet = address(0x123);
      /// @notice pancakswap main pair address (TOKEN/BNB)
      address public uniswapV2Pair;
      /// @notice  router address for pancakeswap
      IUniswapV2Router02 public uniswapV2Router;
      /// @notice max Supply for token  
      uint256 maxSupply = 5e11 * 1e18; // 500 Billion
      /// @notice collected tax token amount after that it will be swapped for bnb
      uint256 swapTokensAtAmount = (maxSupply * 10) / 100000; // 0.01% of the supply
      /// @notice mapping for users excluded from fees   
      mapping(address => bool) public isExcludedFromFees;
      /// @notice mapping for liquidity pairs addresses
      mapping(address=> bool) public isLiquidityPair;

      /// @notice allows collected tokens fees to swapped for bnb   
      bool public swapEnabled = true;
      bool swapping;

      event SwapTokensAmountUpdated (uint256 indexed newAmount);
      event FeeWalletUpdated(address indexed newFeeWallet);
      event ExcludedFromFees (address indexed account, bool value);
      event NewLPUpdated(address indexed lp, bool value); 
      event FeesUpdated( uint16 indexed sellFee);  
      /// @dev create an erc20 token using openzeppelin ownable and erc20.
      /// sets the pancakeswap router address, create main pair, exclude the
      /// owner, feewallet, burn address and token itself from fees. mint the
      /// maxSupply to the owner during deployment.
      constructor() ERC20("Punk Rabbit", "PUK"){
            IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(
            0x10ED43C718714eb63d5aA57B78B54704E256024E//Pancakeswap V2 Router

        uniswapV2Router = _uniswapV2Router;
        uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
            .createPair(address(this), _uniswapV2Router.WETH());
        isLiquidityPair[uniswapV2Pair] = true;    
        isExcludedFromFees[msg.sender] = true;
        isExcludedFromFees[address(this)] = true;
        isExcludedFromFees[feeWallet] = true;
        isExcludedFromFees[DEAD] = true;
        _mint(owner(), maxSupply);
    ///@dev update fees wallet to receive BNB
    ///@param _newFeeWallet: new wallet address for fees
    ///Requirements -
    /// _newFeeWallet address should not be zero address.
    function updateFeesWallet (address _newFeeWallet) external  onlyOwner {
        if(_newFeeWallet == address(0)){
            revert ZeroAddressNotAllowed();
        feeWallet = _newFeeWallet;
        emit FeeWalletUpdated(_newFeeWallet);
    ///@dev update fees for sell
    ///@param sell: new sell fees
    /// sell should be less than equal to MAX_FEE 
    function updateFees (uint16 sell) external onlyOwner {
        if(sell > MAX_FEE){
            revert MaxFeeLimitExceeded();
        sellFees = sell;
        emit FeesUpdated( sell);
    ///@dev exclude or include in fee mapping
    ///@param user: user to exclude or include in fee
    function excludeFromFees (address user, bool isExcluded) external onlyOwner {
        if(isExcludedFromFees[user] == isExcluded){
            revert UpdateBoolValue();
        isExcludedFromFees[user] = isExcluded;
        emit ExcludedFromFees(user, isExcluded);
    ///@dev add or remove new pairs
    ///@param newPair; new pair address
    ///@param value: boolean value true true for adding, false for removing
    ///Requirements -
    ///Can't modify uniswapV2Pair (main pair)
    function setLiquidityPairs (address newPair, bool value) external onlyOwner{
        if(newPair == uniswapV2Pair){
            revert CanNotModifyMainPair();
        isLiquidityPair[newPair] = value;
        emit NewLPUpdated(newPair, value);
    ///@dev update the swap token amount
    ///@param _newSwapAmount: new token amount to swap threshold
    /// amount must greator than equal to MIN_SWAP_AT_AMOUNT
    function setSwapTokensAtAmount (uint256 _newSwapAmount) external onlyOwner {
        if(_newSwapAmount < MIN_SWAP_AT_AMOUNT && _newSwapAmount > MAX_SWAP_AT_AMOUNT){
            revert AmountNotInRange();
        swapTokensAtAmount = _newSwapAmount;
        emit SwapTokensAmountUpdated(_newSwapAmount);
    ///@notice dev can claim stucked tokens, if sent by someone accidently
    ///@param token: address to token to be resuced
    function claimStuckedTokens (address token) external {
       if(msg.sender != feeWallet){
        revert NotAuthorized();
        IERC20 tkn = IERC20(token);
        uint256 balance = tkn.balanceOf(address(this));
        tkn.transfer(feeWallet, balance);

    ///@notice transfer function to manage token transfer/fees/limits
    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(amount > 0, "Transfer amount must be greater than zero");
        uint256 contractBalance = balanceOf(address(this));

        if (
            swapEnabled && 
            !swapping &&
            !isLiquidityPair[from] && 
            !isExcludedFromFees[from] && 
            !isExcludedFromFees[to] && 
            contractBalance >=swapTokensAtAmount
        ) {
            if(contractBalance > MAX_SWAP_AT_AMOUNT){
                contractBalance = MAX_SWAP_AT_AMOUNT;
            swapping = true;
            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 ( isLiquidityPair[to] && sellFees > 0) {
                fees = (amount * sellFees) / 100;
            if (fees > 0) {
                super._transfer(from, address(this), fees);
            amount -= fees;
        super._transfer(from, to, amount);
    ///@notice private function to swap tax to BNB
    function swapTokensForEth(uint256 tokenAmount) private {
        // generate the uniswap pair path of token -> wbnb
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = uniswapV2Router.WETH();
        if(allowance(address(this), address(uniswapV2Router)) < tokenAmount){
          _approve(address(this), address(uniswapV2Router), type(uint256).max);
        // make the swap
            0, // accept any amount of BNB