Uniswap Transfer – Why Does Uniswap V2 Use _safeTransfer to Transfer Tokens?

erc-20transferuniswap

I'm looking into Uniswap V2 code and found that it uses _safeTransfer function to transfer ERC20 tokens in the Pair contract.

    function _safeTransfer(address token, address to, uint value) private {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
    }
...

    function burn(address to) external lock returns (uint amount0, uint amount1) {
        ...
        _safeTransfer(_token0, to, amount0);
        ...
    }

Why doesn't it simply call IERC20(_token).transfer(to, amount0) ?

Best Answer

Because the ERC20 standard kinda sucks, and is unclear about how you should handle non-succeeding token transfers, both reverting and just returning false are technically allowed, so this takes both cases into account

(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
        // Case where there is no return data      case where there is return data
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
  • if success is false, the call to the token contract reverted, and we revert the whole transaction,
  • if success is true and there is no return data, the token contract doesnt return anything on transfer and the transfer succeeded (this is technically non compliant with the ERC20 standard, but USDT infamously does that, which is a pain)
  • if success is true but there is return data, we check if it's a bool (if it's not the contract we called isnt compliant with the ERC20 standard and we revert)
    • if that bool is false the transfer didnt complete and we revert
    • if it's true, the transfer completed normally
Related Topic