■ Description

Some tokens take a transfer fee (e.g. STAPAXG), some do not currently charge a fee but may do so in the future (e.g. USDTUSDC).

■ Example

function transferFrom(address src, address dst, uint wad) override public returns (bool) {
    require(balanceOf[src] >= wad, "insufficient-balance");
    if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) {
        require(allowance[src][msg.sender] >= wad, "insufficient-allowance");
        allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
    }

    balanceOf[src] = sub(balanceOf[src], wad);
    balanceOf[dst] = add(balanceOf[dst], sub(wad, fee));
    balanceOf[address(0)] = add(balanceOf[address(0)], fee);

    emit Transfer(src, dst, sub(wad, fee));
    emit Transfer(src, address(0), fee);

    return true;
}

■ How to protect against this attack?

If you want to use the amount of token, you should use the afterTransfer amount because it is taxed.

■ Resources

https://github.com/d-xo/weird-erc20#missing-return-values

https://blog.1inch.io/balancer-hack-2020-a8f7131c980e