Solidity ERC-20 EVM Uniswap PancakeSwap – How to Differentiate AddLiquidity from SwapExactTokensForETH in UniSwap Fork?

erc-20evmpancakeswapsolidityuniswap

we are trying to process a fee when tokens are "Sold" on PancakeSwap. For now "Sold" is when the user swaps their tokens for BNB. Initially our current logic would also charge a fee when adding to the LP. (Which is not considered a sell in our usecase)

We noticed Pancake uses AddLiquidityETH to transfer tokens to the LP, and SwapExactTokensForETH also performs the same action during execution.

How can we differentiate during execution time between AddLiquidtyETH or SwapExactTokensForETH?

Our current _transfer function looks as follows:

    /// @dev overrides transfer function to meet tokenomics of UFF
    function _transfer(address sender, address recipient, uint256 amount) internal virtual override antiWhale(sender, recipient, amount) {
        // If not modified, full ammount is transfer
        uint256 sendAmount = amount;
        
        // Buy action?
        if(lpToken != address(0) && msg.sender == lpToken && recipient != PCSRouter){
            // default buy fee is 5% / Token OPs Team (dev and marketing)
            uint256 buyFeeAmount = amount.mul(maxBuyFee).div(10000);
            sendAmount = amount.sub(buyFeeAmount);
            require(amount == sendAmount.add(buyFeeAmount), "UFF::transfer: Buy value invalid");
            super._transfer(sender, team, buyFeeAmount);
        }


        // Sell Action?
        if(lpToken != address(0) && recipient == lpToken && msg.sender != PCSRouter){
            uint256 sellFeeAmount = amount.mul(maxSellFee).div(10000);
            sendAmount = amount.sub(sellFeeAmount);
            require(amount == sendAmount.add(sellFeeAmount), "UFF::transfer: Sell value invalid");
            super._transfer(sender, team, sellFeeAmount);
        }
        
        // default 100% of transfer sent to recipient
        super._transfer(sender, recipient, sendAmount);
    }

Thank you in advance.

Best Answer

Unfortunately, this is not possible if you don't know when your tokens are added to liquidity. Why?

Here is the "sell" function of pancakeSwap's router:

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    )
        external
        virtual
        override
        ensure(deadline)
    {
        require(path[path.length - 1] == WETH, 'PancakeRouter: INVALID_PATH');
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, PancakeLibrary.pairFor(factory, path[0], path[1]), amountIn
        ); // <- transfer happens here
        _swapSupportingFeeOnTransferTokens(path, address(this));
        uint amountOut = IERC20(WETH).balanceOf(address(this));
        require(amountOut >= amountOutMin, 'PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT');
        IWETH(WETH).withdraw(amountOut);
        TransferHelper.safeTransferETH(to, amountOut);
    }

As you can see, the tokens are transferred using TransferHelper.safeTransferFrom(path[0], msg.sender, PancakeLibrary.pairFor(factory, path[0], path[1]), amountIn);

Which is a wrapper for the transfer() function:

    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeTransfer: transfer failed'
        );
    }

Same goes with addLiquidityEth():

    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
        (amountToken, amountETH) = _addLiquidity(
            token,
            WETH,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        address pair = PancakeLibrary.pairFor(factory, token, WETH);
        TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken); // <- transfer happens here
        IWETH(WETH).deposit{value: amountETH}();
        assert(IWETH(WETH).transfer(pair, amountETH));
        liquidity = IPancakePair(pair).mint(to);
        // refund dust eth, if any
        if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
    }

You can only modify the behavior of the transfer function by adding a hook which would trigger depending of one of the 3 inputs: from, to and value.

However,from and to and value are the same for both functions and are thus useless to detect which function is used, preventing you in effect from creating a hook to differentiate functions.

Related Topic