transmissions11 / solmate

Modern, opinionated, and gas optimized building blocks for smart contract development.
GNU Affero General Public License v3.0
3.93k stars 647 forks source link

safeTransferFrom fails when casting the 'to' address down from a value greater than max(uint160) #299

Open gasperbr opened 2 years ago

gasperbr commented 2 years ago

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "solmate/utils/SafeTransferLib.sol";
import "solmate/tokens/ERC20.sol";

contract ContractTest is Test {

    Token token = new Token();
    Router router = new Router();

    function setUp() public {
        token.approve(address(router), 1);
    }
    function testSafeTransferOk() public {
        router.safeTransferSucceeds(address(token)); // Ok.
    }
    function testSafeTransferNok() public {
        router.safeTransferFails(address(token)); // This fails.
    }
}

contract Router {
    function safeTransferSucceeds(address token) public {
        address to = address(uint160(uint256(0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff))); // type(uint160).max
        SafeTransferLib.safeTransferFrom(ERC20(token), msg.sender, to, 1);
    }
    function safeTransferFails(address token) public {
        address to = address(uint160(uint256(0x0000000000000000000000010000000000000000000000000000000000000000))); // type(uint160).max + 1
        SafeTransferLib.safeTransferFrom(ERC20(token), msg.sender, to, 1);
    }
}

contract Token is ERC20("", "", 18) {
    constructor() {
        _mint(msg.sender, 1);
    }
}
regohiro commented 2 years ago

Encountered the same issue. This is also true for "from". A temporary fix is to mask the address:

address(uint160(uint256(0x0000000000000000000000010000000000000000000000000000000000000000)) & type(uint160).max)