Some tokens do not return a bool (e.g. USDT
, BNB
, OMG
) on ERC20 methods. see here
for a comprehensive (if somewhat outdated) list.
Some tokens (e.g. BNB
) may return a bool
for some methods, but fail to do so for others. This resulted in stuck BNB
tokens in Uniswap v1 (details).
Some particulary pathological tokens (e.g. Tether Gold) declare a bool return, but then return false
even when the transfer was successful (code).
function transfer(address dst, uint wad) external returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
// bad_1
function transferFrom(address src, address dst, uint wad) public {
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], wad);
emit Transfer(src, dst, wad);
// missing return value
}
// bad_2
function transferFrom(address src, address dst, uint wad) 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], wad);
emit Transfer(src, dst, wad);
return false; // return false
}
// good
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');
}
Check the return value
require( token.transfer(),"");
https://github.com/d-xo/weird-erc20#missing-return-values
https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface