Contract Tax Question

Hello,
I have recently been experimenting with contracts while I learn Solidity.
This contract is set to collect a 2% Buy and 2% Sell Tax.
After performing several example trades, it appears to me that the collected taxes are being sent to the contract itself. On the scanner, the contract address is showing as owning a small percentage of the tokens. My question is, how do I get these tokens out of the contract and back into my msg.sender/dev wallet? I looked through all the Functions and it doesnt appear that any of them pertain directly to this issue.

I also have a question about some fundamental understanding of how these ERC20 tokens interact with taxes. When a buy or sell tax is set by the token, is the tax collected in form of token itself usually or in the underlying base currency (Eth, BNB, etc)? Is there situations where it could be either?

Thanks

J

well the fee is just removing from the amount sent and adds the new amounts to the receipent and the fee to the contracts balance. Now you just need to enter another wallet than the contracts.
CONTRACT RECEIVES FEE

// 2% fee
uint fee = amount * 2 / 100;
amount -= fee;
balances[address(this)] += fee;
balances[receipient] += amount;

YOU RECEIVE FEE

// 2% fee
uint fee = amount * 2 / 100;
amount -= fee;
balances[YOUR_WALLET] += fee;
balances[receipient] += amount;

please look in your _transfer() function

First off, welcome to the community @jneptune!

how do I get these tokens out of the contract and back into my msg.sender/dev wallet?

Would you share the contract code? It would be helpful to see what you are working with.

is the tax collected in form of token itself usually or in the underlying base currency (Eth, BNB, etc)? Is there situations where it could be either?

It's most likely taking a percentage of whatever ERC20 token it is. Seeing the contract itself will be helpful to know for sure.

What happens with the tax is depending on what your tax function does with it. Normally you configure an marketing/developer wallet and transfer the tax to that wallet. We would have to see you contract code to tell you why it is doing what it currently does :slight_smile:

Normally a tax function is taking a % of the received token and sends it to another wallet. So the tax is in the original sending/receiving token.

Technically its possible to receive another currency. In those situations you will have to extend the contract to call the swap function on one of the amm dex's like uniswap or pancakeswap swap. Then you exchange the taxes token and swap it for usdt or something and send that to the marketing/developer wallet

1 Like

Hello all,
Thanks for the response! Here is the contract we are working with:
I currently have the Uniswap router address set for the BNB testnet router for testing.

contract TINKERTOYS4 is Context, IERC20, Ownable {
    using Address for address;
    using Address for address payable;

    IUniswapV2Router02 public uniswapV2Router;
    address public uniswapV2Pair;
    
    mapping (address => uint256) private _tOwned;
    mapping (address => mapping (address => uint256)) private _allowances;

    mapping (address => bool) private _isExcludedFromFee;
   
    uint256 private _tTotal = 10000 * 10**9;
    uint256 public _maxTxAmount = 10000 * 10**9; // 
    uint256 private constant SWAP_TOKENS_AT_AMOUNT = 5 * 10**9; //
    string private constant _name = "Tinker Toys4"; // 
    string private constant _symbol = "TINK4"; //    
    uint8 private constant _decimals = 9; // 
    
    uint256 public _marketingFee = 2;
    uint256 public _liquidityFee = 2;
    address public  _marketingWallet = 0x000MyWallet.....;
    
    uint256 public _buyCooldown = 0 minutes;
    mapping (address => uint256) private _lastBuy;
    
    bool private swapping;
    
    event SwapAndLiquify(uint256 tokensSwapped, uint256 ethReceived, uint256 tokensIntoLiquidity);
        
    constructor () {
        _tOwned[_msgSender()] = _tTotal;
        
        IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0xD99D1c33F9fC3444f8101754aBC46c52416550D1);
        // Create a uniswap pair for this new token
        address _uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory()).createPair(address(this), _uniswapV2Router.WETH());

        uniswapV2Router = _uniswapV2Router;
        uniswapV2Pair = _uniswapV2Pair;
        
        //exclude owner and this contract from fee
        _isExcludedFromFee[owner()] = true;
        _isExcludedFromFee[address(this)] = true;
        _isExcludedFromFee[_marketingWallet] = true;
        
        emit Transfer(address(0), _msgSender(), _tTotal);
    }

    function name() public pure returns (string memory) {
        return _name;
    }

    function symbol() public pure returns (string memory) {
        return _symbol;
    }

    function decimals() public pure returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view override returns (uint256) {
        return _tTotal;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _tOwned[account];
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - amount);
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
        return true;
    }
    
    function excludeFromFee(address account) public onlyOwner {
        _isExcludedFromFee[account] = true;
    }
    
    function includeInFee(address account) public onlyOwner {
        _isExcludedFromFee[account] = false;
    }
    
    
     //to recieve ETH from uniswapV2Router when swaping
    receive() external payable {}


    function _getValues(uint256 amount, address from) private returns (uint256) {
        uint256 marketingFee = amount * _marketingFee / 100; 
        uint256 liquidityFee = amount * _liquidityFee / 100; 
        _tOwned[address(this)] += marketingFee + liquidityFee;
        emit Transfer (from, address(this), marketingFee + liquidityFee);
        return (amount - marketingFee - liquidityFee);
    }
    
    
    function isExcludedFromFee(address account) public view returns(bool) {
        return _isExcludedFromFee[account];
    }
    
    function _approve(address owner, address spender, uint256 amount) private {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) private {
        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");
        
         if(from != owner() && to != owner() && to != uniswapV2Pair)
            require(balanceOf(to) + amount <= _maxTxAmount, "Transfer amount exceeds the maxTxAmount.");
            

        if (from == uniswapV2Pair) {
            require (_lastBuy[to] + _buyCooldown < block.timestamp, "Must wait til after coooldown to buy");
            _lastBuy[to] = block.timestamp;
        }
        
        
        if (balanceOf(address(this)) >= SWAP_TOKENS_AT_AMOUNT && !swapping && from != uniswapV2Pair && from != owner() && to != owner()) {
            swapping = true;
            uint256 sellTokens = balanceOf(address(this));
            swapAndSendToFee(sellTokens);
            swapping = false;
        }
        
        _tOwned[from] -= amount;
        uint256 transferAmount = amount;
        
        //if any account belongs to _isExcludedFromFee account then remove the fee
        if(!_isExcludedFromFee[from] && !_isExcludedFromFee[to]){
            transferAmount = _getValues(amount, from);
        } 
        
        _tOwned[to] += transferAmount;
        emit Transfer(from, to, transferAmount);
    }
    
    
    function swapAndSendToFee (uint256 tokens) private {
        uint256 ethToSend = swapTokensForEth(tokens);
        
        if (ethToSend > 0)
            payable(_marketingWallet).transfer(ethToSend);
    }

    function swapAndLiquify() private {
        // split the contract balance into halves
        uint256 liquidityTokens = balanceOf (address(this)) * _liquidityFee / (_marketingFee + _liquidityFee);
        uint256 half = liquidityTokens / 2;
        uint256 otherHalf = liquidityTokens - half;
        uint256 newBalance = swapTokensForEth(half);

        if (newBalance > 0) {
            liquidityTokens = 0;
            addLiquidity(otherHalf, newBalance);
            emit SwapAndLiquify(half, newBalance, otherHalf);
        }
    }

    function swapTokensForEth(uint256 tokenAmount) private returns (uint256) {
        uint256 initialBalance = address(this).balance;
        // 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
        );
        return (address(this).balance - initialBalance);
    }

    function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
        // approve token transfer to cover all possible scenarios
        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // add the liquidity
        (,uint256 ethFromLiquidity,) = uniswapV2Router.addLiquidityETH {value: ethAmount} (
            address(this),
            tokenAmount,
            0, // slippage is unavoidable
            0, // slippage is unavoidable
            owner(),
            block.timestamp
        );
        
        if (ethAmount - ethFromLiquidity > 0)
            payable(_marketingWallet).sendValue (ethAmount - ethFromLiquidity);
    }
}

Thank you responding! I think Ive been on the right track then, focusing on how the contract would send the deducted tax tokens to the marketing wallet. It does bring up a question about how contracts interact with liquidity pools.
In a normal situation where an trader trades currency (like ETH or BNB) for tokens, what you're explaining is the tokens he is buying will have the taxed amount removed and sent to the marketing wallet, with the remainder sent to the buyers wallet. Is there any situation where the tax is removed from the ETH or BNB first, then the remaining is what is exchanged for the token and delivered? With this idea, it seems to me the marketing wallet would receive ETH or BNB directly with each token purchase. Is this not something that is feasible with Uniswap or Pancake?

Thanks for the Info!

J

Thank you, I will scrutinize the transfer function on this one!

To do this with a native token like ETH on ethereum blockchain or BNB on the binance smart chain you will have to either implement
receive() external payable

(fallback if someone calls your contract with eth/bnb and there is not a function found that matches what is called) or make your own function that is external payable.
In this function you will have to use the msg.value this property will contain the amount of ETH/BNB that is received and from there you can send it to something/someone else.