// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {MockERC20} from "./utils/mocks/MockERC20.sol"; import {MockERC20LikeUSDT} from "./utils/mocks/MockERC20LikeUSDT.sol"; import {MockETHRecipient} from "./utils/mocks/MockETHRecipient.sol"; import {RevertingToken} from "./utils/weird-tokens/RevertingToken.sol"; import {ReturnsTwoToken} from "./utils/weird-tokens/ReturnsTwoToken.sol"; import {ReturnsFalseToken} from "./utils/weird-tokens/ReturnsFalseToken.sol"; import {MissingReturnToken} from "./utils/weird-tokens/MissingReturnToken.sol"; import {ReturnsTooMuchToken} from "./utils/weird-tokens/ReturnsTooMuchToken.sol"; import {ReturnsRawBytesToken} from "./utils/weird-tokens/ReturnsRawBytesToken.sol"; import {ReturnsTooLittleToken} from "./utils/weird-tokens/ReturnsTooLittleToken.sol"; import "./utils/SoladyTest.sol"; import {ERC20} from "../src/tokens/ERC20.sol"; import {SafeTransferLib} from "../src/utils/SafeTransferLib.sol"; interface IPermit2 { struct PermitDetails { address token; // By right, this is uint160, but we use uint256 to test the overflow errors. uint256 amount; uint48 expiration; uint48 nonce; } struct PermitSingle { PermitDetails details; address spender; uint256 sigDeadline; } function allowance(address owner, address token, address spender) external view returns (uint160 amount, uint48 expiration, uint48 nonce); } contract SafeTransferLibTest is SoladyTest { uint256 internal constant _SUCCESS = 1; uint256 internal constant _REVERTS_WITH_SELECTOR = 2; uint256 internal constant _REVERTS_WITH_ANY = 3; RevertingToken reverting; ReturnsTwoToken returnsTwo; ReturnsFalseToken returnsFalse; MissingReturnToken missingReturn; ReturnsTooMuchToken returnsTooMuch; ReturnsRawBytesToken returnsRawBytes; ReturnsTooLittleToken returnsTooLittle; MockERC20 erc20; address internal constant _DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address internal constant _WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; bytes32 public constant _PERMIT_DETAILS_TYPEHASH = keccak256("PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"); bytes32 public constant _PERMIT_SINGLE_TYPEHASH = keccak256( "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)" ); bytes32 internal constant _DAI_PERMIT_TYPEHASH = keccak256( "Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)" ); bytes32 internal constant _PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); function setUp() public { vm.chainId(1); reverting = new RevertingToken(); returnsTwo = new ReturnsTwoToken(); returnsFalse = new ReturnsFalseToken(); missingReturn = new MissingReturnToken(); returnsTooMuch = new ReturnsTooMuchToken(); returnsRawBytes = new ReturnsRawBytesToken(); returnsTooLittle = new ReturnsTooLittleToken(); erc20 = new MockERC20("StandardToken", "ST", 18); erc20.mint(address(this), type(uint256).max); _deployWETH9(); _deployDAI(); _deployPermit2(); } function _deployWETH9() internal { bytes memory bytecode = hex"6060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029"; vm.etch(_WETH9, bytecode); } function _deployDAI() internal { bytes memory bytecode = hex"608060405234801561001057600080fd5b50600436106101425760003560e01c80637ecebe00116100b8578063a9059cbb1161007c578063a9059cbb146106b4578063b753a98c1461071a578063bb35783b14610768578063bf353dbb146107d6578063dd62ed3e1461082e578063f2d5d56b146108a657610142565b80637ecebe00146104a15780638fcbaf0c146104f957806395d89b411461059f5780639c52a7f1146106225780639dc29fac1461066657610142565b8063313ce5671161010a578063313ce567146102f25780633644e5151461031657806340c10f191461033457806354fd4d501461038257806365fae35e1461040557806370a082311461044957610142565b806306fdde0314610147578063095ea7b3146101ca57806318160ddd1461023057806323b872dd1461024e57806330adf81f146102d4575b600080fd5b61014f6108f4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561018f578082015181840152602081019050610174565b50505050905090810190601f1680156101bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610216600480360360408110156101e057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061092d565b604051808215151515815260200191505060405180910390f35b610238610a1f565b6040518082815260200191505060405180910390f35b6102ba6004803603606081101561026457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a25565b604051808215151515815260200191505060405180910390f35b6102dc610f3a565b6040518082815260200191505060405180910390f35b6102fa610f61565b604051808260ff1660ff16815260200191505060405180910390f35b61031e610f66565b6040518082815260200191505060405180910390f35b6103806004803603604081101561034a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610f6c565b005b61038a611128565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103ca5780820151818401526020810190506103af565b50505050905090810190601f1680156103f75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6104476004803603602081101561041b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611161565b005b61048b6004803603602081101561045f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061128f565b6040518082815260200191505060405180910390f35b6104e3600480360360208110156104b757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506112a7565b6040518082815260200191505060405180910390f35b61059d600480360361010081101561051057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803515159060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506112bf565b005b6105a76117fa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156105e75780820151818401526020810190506105cc565b50505050905090810190601f1680156106145780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6106646004803603602081101561063857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611833565b005b6106b26004803603604081101561067c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611961565b005b610700600480360360408110156106ca57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611df4565b604051808215151515815260200191505060405180910390f35b6107666004803603604081101561073057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611e09565b005b6107d46004803603606081101561077e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611e19565b005b610818600480360360208110156107ec57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611e2a565b6040518082815260200191505060405180910390f35b6108906004803603604081101561084457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611e42565b6040518082815260200191505060405180910390f35b6108f2600480360360408110156108bc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611e67565b005b6040518060400160405280600e81526020017f44616920537461626c65636f696e00000000000000000000000000000000000081525081565b600081600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60015481565b600081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610adc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f4461692f696e73756666696369656e742d62616c616e6365000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614158015610bb457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b15610db25781600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610cab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4461692f696e73756666696369656e742d616c6c6f77616e636500000000000081525060200191505060405180910390fd5b610d31600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611e77565b600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b610dfb600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611e77565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610e87600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611e91565b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b7fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb60001b81565b601281565b60055481565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414611020576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4461692f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b611069600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611e91565b600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506110b860015482611e91565b6001819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b6040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525081565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414611215576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4461692f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b60016000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505961012081016040526020815260e0602082015260e0600060408301376024356004353360003560e01c60e01b61012085a45050565b60026020528060005260406000206000915090505481565b60046020528060005260406000206000915090505481565b60006005547fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb60001b8a8a8a8a8a604051602001808781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018381526020018215151515815260200196505050505050506040516020818303038152906040528051906020012060405160200180807f190100000000000000000000000000000000000000000000000000000000000081525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050600073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff16141561148c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4461692f696e76616c69642d616464726573732d30000000000000000000000081525060200191505060405180910390fd5b60018185858560405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156114e9573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614611593576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4461692f696e76616c69642d7065726d6974000000000000000000000000000081525060200191505060405180910390fd5b60008614806115a25750854211155b611614576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4461692f7065726d69742d65787069726564000000000000000000000000000081525060200191505060405180910390fd5b600460008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008154809291906001019190505587146116d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f4461692f696e76616c69642d6e6f6e636500000000000000000000000000000081525060200191505060405180910390fd5b6000856116e4576000611706565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b905080600360008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a350505050505050505050565b6040518060400160405280600381526020017f444149000000000000000000000000000000000000000000000000000000000081525081565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146118e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4461692f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505961012081016040526020815260e0602082015260e0600060408301376024356004353360003560e01c60e01b61012085a45050565b80600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015611a16576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f4461692f696e73756666696369656e742d62616c616e6365000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158015611aee57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b15611cec5780600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015611be5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4461692f696e73756666696369656e742d616c6c6f77616e636500000000000081525060200191505060405180910390fd5b611c6b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611e77565b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b611d35600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611e77565b600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550611d8460015482611e77565b600181905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b6000611e01338484610a25565b905092915050565b611e14338383610a25565b505050565b611e24838383610a25565b50505050565b60006020528060005260406000206000915090505481565b6003602052816000526040600020602052806000526040600020600091509150505481565b611e72823383610a25565b505050565b6000828284039150811115611e8b57600080fd5b92915050565b6000828284019150811015611ea557600080fd5b9291505056fea265627a7a72315820c0ae2c29860c0a59d5586a579abbcddfe4bcef0524a87301425cbc58c3e94e3164736f6c634300050c0032"; vm.etch(_DAI, bytecode); vm.store(_DAI, bytes32(uint256(5)), SafeTransferLib.DAI_DOMAIN_SEPARATOR); } function _deployPermit2() internal { bytes memory bytecode = hex"6040608081526004908136101561001557600080fd5b600090813560e01c80630d58b1db1461126c578063137c29fe146110755780632a2d80d114610db75780632b67b57014610bde57806330f28b7a14610ade5780633644e51514610a9d57806336c7851614610a285780633ff9dcb1146109a85780634fe02b441461093f57806365d9723c146107ac57806387517c451461067a578063927da105146105c3578063cc53287f146104a3578063edd9444b1461033a5763fe8ec1a7146100c657600080fd5b346103365760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff833581811161033257610114903690860161164b565b60243582811161032e5761012b903690870161161a565b6101336114e6565b9160843585811161032a5761014b9036908a016115c1565b98909560a43590811161032657610164913691016115c1565b969095815190610173826113ff565b606b82527f5065726d697442617463685769746e6573735472616e7366657246726f6d285460208301527f6f6b656e5065726d697373696f6e735b5d207065726d69747465642c61646472838301527f657373207370656e6465722c75696e74323536206e6f6e63652c75696e74323560608301527f3620646561646c696e652c000000000000000000000000000000000000000000608083015282519a8b9181610222602085018096611f93565b918237018a8152039961025b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09b8c8101835282611437565b5190209085515161026b81611ebb565b908a5b8181106102f95750506102f6999a6102ed9183516102a081610294602082018095611f66565b03848101835282611437565b519020602089810151858b015195519182019687526040820192909252336060820152608081019190915260a081019390935260643560c08401528260e081015b03908101835282611437565b51902093611cf7565b80f35b8061031161030b610321938c5161175e565b51612054565b61031b828661175e565b52611f0a565b61026e565b8880fd5b8780fd5b8480fd5b8380fd5b5080fd5b5091346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff9080358281116103325761038b903690830161164b565b60243583811161032e576103a2903690840161161a565b9390926103ad6114e6565b9160643590811161049f576103c4913691016115c1565b949093835151976103d489611ebb565b98885b81811061047d5750506102f697988151610425816103f9602082018095611f66565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611437565b5190206020860151828701519083519260208401947ffcf35f5ac6a2c28868dc44c302166470266239195f02b0ee408334829333b7668652840152336060840152608083015260a082015260a081526102ed8161141b565b808b61031b8261049461030b61049a968d5161175e565b9261175e565b6103d7565b8680fd5b5082346105bf57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103325780359067ffffffffffffffff821161032e576104f49136910161161a565b929091845b848110610504578580f35b8061051a610515600193888861196c565b61197c565b61052f84610529848a8a61196c565b0161197c565b3389528385528589209173ffffffffffffffffffffffffffffffffffffffff80911692838b528652868a20911690818a5285528589207fffffffffffffffffffffffff000000000000000000000000000000000000000081541690558551918252848201527f89b1add15eff56b3dfe299ad94e01f2b52fbcb80ae1a3baea6ae8c04cb2b98a4853392a2016104f9565b8280fd5b50346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610676816105ff6114a0565b936106086114c3565b6106106114e6565b73ffffffffffffffffffffffffffffffffffffffff968716835260016020908152848420928816845291825283832090871683528152919020549251938316845260a083901c65ffffffffffff169084015260d09190911c604083015281906060820190565b0390f35b50346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336576106b26114a0565b906106bb6114c3565b916106c46114e6565b65ffffffffffff926064358481169081810361032a5779ffffffffffff0000000000000000000000000000000000000000947fda9fa7c1b00402c17d0161b249b1ab8bbec047c5a52207b9c112deffd817036b94338a5260016020527fffffffffffff0000000000000000000000000000000000000000000000000000858b209873ffffffffffffffffffffffffffffffffffffffff809416998a8d5260205283878d209b169a8b8d52602052868c209486156000146107a457504216925b8454921697889360a01b16911617179055815193845260208401523392a480f35b905092610783565b5082346105bf5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576107e56114a0565b906107ee6114c3565b9265ffffffffffff604435818116939084810361032a57338852602091600183528489209673ffffffffffffffffffffffffffffffffffffffff80911697888b528452858a20981697888a5283528489205460d01c93848711156109175761ffff9085840316116108f05750907f55eb90d810e1700b35a8e7e25395ff7f2b2259abd7415ca2284dfb1c246418f393929133895260018252838920878a528252838920888a5282528389209079ffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffff000000000000000000000000000000000000000000000000000083549260d01b16911617905582519485528401523392a480f35b84517f24d35a26000000000000000000000000000000000000000000000000000000008152fd5b5084517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b503461033657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336578060209273ffffffffffffffffffffffffffffffffffffffff61098f6114a0565b1681528084528181206024358252845220549051908152f35b5082346105bf57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf577f3704902f963766a4e561bbaab6e6cdc1b1dd12f6e9e99648da8843b3f46b918d90359160243533855284602052818520848652602052818520818154179055815193845260208401523392a280f35b8234610a9a5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a9a57610a606114a0565b610a686114c3565b610a706114e6565b6064359173ffffffffffffffffffffffffffffffffffffffff8316830361032e576102f6936117a1565b80fd5b503461033657817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657602090610ad7611b1e565b9051908152f35b508290346105bf576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf57610b1a3661152a565b90807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c36011261033257610b4c611478565b9160e43567ffffffffffffffff8111610bda576102f694610b6f913691016115c1565b939092610b7c8351612054565b6020840151828501519083519260208401947f939c21a48a8dbe3a9a2404a1d46691e4d39f6583d6ec6b35714604c986d801068652840152336060840152608083015260a082015260a08152610bd18161141b565b51902091611c25565b8580fd5b509134610336576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610c186114a0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360160c08112610332576080855191610c51836113e3565b1261033257845190610c6282611398565b73ffffffffffffffffffffffffffffffffffffffff91602435838116810361049f578152604435838116810361049f57602082015265ffffffffffff606435818116810361032a5788830152608435908116810361049f576060820152815260a435938285168503610bda576020820194855260c4359087830182815260e43567ffffffffffffffff811161032657610cfe90369084016115c1565b929093804211610d88575050918591610d786102f6999a610d7e95610d238851611fbe565b90898c511690519083519260208401947ff3841cd1ff0085026a6327b620b67997ce40f282c88a8e905a7a5626e310f3d086528401526060830152608082015260808152610d70816113ff565b519020611bd9565b916120c7565b519251169161199d565b602492508a51917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b5091346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc93818536011261033257610df36114a0565b9260249081359267ffffffffffffffff9788851161032a578590853603011261049f578051978589018981108282111761104a578252848301358181116103265785019036602383011215610326578382013591610e50836115ef565b90610e5d85519283611437565b838252602093878584019160071b83010191368311611046578801905b828210610fe9575050508a526044610e93868801611509565b96838c01978852013594838b0191868352604435908111610fe557610ebb90369087016115c1565b959096804211610fba575050508998995151610ed681611ebb565b908b5b818110610f9757505092889492610d7892610f6497958351610f02816103f98682018095611f66565b5190209073ffffffffffffffffffffffffffffffffffffffff9a8b8b51169151928551948501957faf1b0d30d2cab0380e68f0689007e3254993c596f2fdd0aaa7f4d04f794408638752850152830152608082015260808152610d70816113ff565b51169082515192845b848110610f78578580f35b80610f918585610f8b600195875161175e565b5161199d565b01610f6d565b80610311610fac8e9f9e93610fb2945161175e565b51611fbe565b9b9a9b610ed9565b8551917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b8a80fd5b6080823603126110465785608091885161100281611398565b61100b85611509565b8152611018838601611509565b838201526110278a8601611607565b8a8201528d611037818701611607565b90820152815201910190610e7a565b8c80fd5b84896041867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5082346105bf576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576110b03661152a565b91807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c360112610332576110e2611478565b67ffffffffffffffff93906101043585811161049f5761110590369086016115c1565b90936101243596871161032a57611125610bd1966102f6983691016115c1565b969095825190611134826113ff565b606482527f5065726d69745769746e6573735472616e7366657246726f6d28546f6b656e5060208301527f65726d697373696f6e73207065726d69747465642c6164647265737320737065848301527f6e6465722c75696e74323536206e6f6e63652c75696e7432353620646561646c60608301527f696e652c0000000000000000000000000000000000000000000000000000000060808301528351948591816111e3602085018096611f93565b918237018b8152039361121c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe095868101835282611437565b5190209261122a8651612054565b6020878101518589015195519182019687526040820192909252336060820152608081019190915260a081019390935260e43560c08401528260e081016102e1565b5082346105bf576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033257813567ffffffffffffffff92838211610bda5736602383011215610bda5781013592831161032e576024906007368386831b8401011161049f57865b8581106112e5578780f35b80821b83019060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83360301126103265761139288876001946060835161132c81611398565b611368608461133c8d8601611509565b9485845261134c60448201611509565b809785015261135d60648201611509565b809885015201611509565b918291015273ffffffffffffffffffffffffffffffffffffffff80808093169516931691166117a1565b016112da565b6080810190811067ffffffffffffffff8211176113b457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176113b457604052565b60a0810190811067ffffffffffffffff8211176113b457604052565b60c0810190811067ffffffffffffffff8211176113b457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176113b457604052565b60c4359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b600080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01906080821261149b576040805190611563826113e3565b8082941261149b57805181810181811067ffffffffffffffff8211176113b457825260043573ffffffffffffffffffffffffffffffffffffffff8116810361149b578152602435602082015282526044356020830152606435910152565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020838186019501011161149b57565b67ffffffffffffffff81116113b45760051b60200190565b359065ffffffffffff8216820361149b57565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020808501948460061b01011161149b57565b91909160608184031261149b576040805191611666836113e3565b8294813567ffffffffffffffff9081811161149b57830182601f8201121561149b578035611693816115ef565b926116a087519485611437565b818452602094858086019360061b8501019381851161149b579086899897969594939201925b8484106116e3575050505050855280820135908501520135910152565b90919293949596978483031261149b578851908982019082821085831117611730578a928992845261171487611509565b81528287013583820152815201930191908897969594936116c6565b602460007f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b80518210156117725760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b92919273ffffffffffffffffffffffffffffffffffffffff604060008284168152600160205282828220961695868252602052818120338252602052209485549565ffffffffffff8760a01c16804211611884575082871696838803611812575b5050611810955016926118b5565b565b878484161160001461184f57602488604051907ff96fb0710000000000000000000000000000000000000000000000000000000082526004820152fd5b7fffffffffffffffffffffffff000000000000000000000000000000000000000084846118109a031691161790553880611802565b602490604051907fd81b2f2e0000000000000000000000000000000000000000000000000000000082526004820152fd5b9060006064926020958295604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d116001600051141617161561190e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b91908110156117725760061b0190565b3573ffffffffffffffffffffffffffffffffffffffff8116810361149b5790565b9065ffffffffffff908160608401511673ffffffffffffffffffffffffffffffffffffffff908185511694826020820151169280866040809401511695169560009187835260016020528383208984526020528383209916988983526020528282209184835460d01c03611af5579185611ace94927fc6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708ec98979694508715600014611ad35779ffffffffffff00000000000000000000000000000000000000009042165b60a01b167fffffffffffff00000000000000000000000000000000000000000000000000006001860160d01b1617179055519384938491604091949373ffffffffffffffffffffffffffffffffffffffff606085019616845265ffffffffffff809216602085015216910152565b0390a4565b5079ffffffffffff000000000000000000000000000000000000000087611a60565b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b467f000000000000000000000000000000000000000000000000000000000000000103611b69577f866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f2890565b60405160208101907f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86682527f9ac997416e8ff9d2ff6bebeb7149f65cdae5e32e2b90440b566bb3044041d36a604082015246606082015230608082015260808152611bd3816113ff565b51902090565b611be1611b1e565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611bd381611398565b9192909360a435936040840151804211611cc65750602084510151808611611c955750918591610d78611c6594611c60602088015186611e47565b611bd9565b73ffffffffffffffffffffffffffffffffffffffff809151511692608435918216820361149b57611810936118b5565b602490604051907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b602490604051907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b959093958051519560409283830151804211611e175750848803611dee57611d2e918691610d7860209b611c608d88015186611e47565b60005b868110611d42575050505050505050565b611d4d81835161175e565b5188611d5a83878a61196c565b01359089810151808311611dbe575091818888886001968596611d84575b50505050505001611d31565b611db395611dad9273ffffffffffffffffffffffffffffffffffffffff6105159351169561196c565b916118b5565b803888888883611d78565b6024908651907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b600484517fff633a38000000000000000000000000000000000000000000000000000000008152fd5b6024908551907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b9073ffffffffffffffffffffffffffffffffffffffff600160ff83161b9216600052600060205260406000209060081c6000526020526040600020818154188091551615611e9157565b60046040517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b90611ec5826115ef565b611ed26040519182611437565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611f0082946115ef565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611f375760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805160208092019160005b828110611f7f575050505090565b835185529381019392810192600101611f71565b9081519160005b838110611fab575050016000815290565b8060208092840101518185015201611f9a565b60405160208101917f65626cad6cb96493bf6f5ebea28756c966f023ab9e8a83a7101849d5573b3678835273ffffffffffffffffffffffffffffffffffffffff8082511660408401526020820151166060830152606065ffffffffffff9182604082015116608085015201511660a082015260a0815260c0810181811067ffffffffffffffff8211176113b45760405251902090565b6040516020808201927f618358ac3db8dc274f0cd8829da7e234bd48cd73c4a740aede1adec9846d06a1845273ffffffffffffffffffffffffffffffffffffffff81511660408401520151606082015260608152611bd381611398565b919082604091031261149b576020823592013590565b6000843b61222e5750604182036121ac576120e4828201826120b1565b939092604010156117725760209360009360ff6040608095013560f81c5b60405194855216868401526040830152606082015282805260015afa156121a05773ffffffffffffffffffffffffffffffffffffffff806000511691821561217657160361214c57565b60046040517f815e1d64000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b6040513d6000823e3d90fd5b60408203612204576121c0918101906120b1565b91601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84169360ff1c019060ff8211611f375760209360009360ff608094612102565b60046040517f4be6321b000000000000000000000000000000000000000000000000000000008152fd5b929391601f928173ffffffffffffffffffffffffffffffffffffffff60646020957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0604051988997889687947f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8752600487015260406024870152816044870152868601378b85828601015201168101030192165afa9081156123a857829161232a575b507fffffffff000000000000000000000000000000000000000000000000000000009150160361230057565b60046040517fb0669cbc000000000000000000000000000000000000000000000000000000008152fd5b90506020813d82116123a0575b8161234460209383611437565b810103126103365751907fffffffff0000000000000000000000000000000000000000000000000000000082168203610a9a57507fffffffff0000000000000000000000000000000000000000000000000000000090386122d4565b3d9150612337565b6040513d84823e3d90fdfea164736f6c6343000811000a"; vm.etch(_PERMIT2, bytecode); } function testSuccessTrick(bool success, uint256 extCodeSize, uint256 returnDataSize) public { bool expected = success && extCodeSize != 0 && returnDataSize == 0; bool computed; /// @solidity memory-safe-assembly assembly { computed := lt(or(iszero(extCodeSize), returnDataSize), success) } assertEq(computed, expected); } function testTransferWithMissingReturn() public { verifySafeTransfer(address(missingReturn), address(0xBEEF), 1e18, _SUCCESS); } function testTransferWithStandardERC20() public { verifySafeTransfer(address(erc20), address(0xBEEF), 1e18, _SUCCESS); } function testTransferWithReturnsTooMuch() public { verifySafeTransfer(address(returnsTooMuch), address(0xBEEF), 1e18, _SUCCESS); } function testTransferWithNonContractReverts() public { vm.expectRevert(SafeTransferLib.TransferFailed.selector); this.safeTransfer(address(0xBADBEEF), address(0xBEEF), 1e18); } function testTransferFromWithMissingReturn() public { verifySafeTransferFrom( address(missingReturn), address(0xFEED), address(0xBEEF), 1e18, _SUCCESS ); } function testTransferFromWithStandardERC20() public { verifySafeTransferFrom(address(erc20), address(0xFEED), address(0xBEEF), 1e18, _SUCCESS); } function testTransferFromWithReturnsTooMuch() public { verifySafeTransferFrom( address(returnsTooMuch), address(0xFEED), address(0xBEEF), 1e18, _SUCCESS ); } function testTransferFromWithNonContractReverts() public { vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); this.safeTransferFrom(address(0xBADBEEF), address(0xFEED), address(0xBEEF), 1e18); } function safeTransferFrom(address token, address from, address to, uint256 amount) public { SafeTransferLib.safeTransferFrom( _brutalized(token), _brutalized(from), _brutalized(to), amount ); } function testApproveWithMissingReturn() public { verifySafeApprove(address(missingReturn), address(0xBEEF), 1e18, _SUCCESS); } function testApproveWithStandardERC20() public { verifySafeApprove(address(erc20), address(0xBEEF), 1e18, _SUCCESS); } function testApproveWithReturnsTooMuch() public { verifySafeApprove(address(returnsTooMuch), address(0xBEEF), 1e18, _SUCCESS); } function testApproveWithNonContractReverts() public { vm.expectRevert(SafeTransferLib.ApproveFailed.selector); this.safeApprove(address(0xBADBEEF), address(0xBEEF), 1e18); } function safeApprove(address token, address to, uint256 amount) public { SafeTransferLib.safeApprove(token, to, amount); } function testApproveWithRetryWithNonContractReverts() public { vm.expectRevert(SafeTransferLib.ApproveFailed.selector); this.safeApproveWithRetry(address(0xBADBEEF), address(0xBEEF), 1e18); } function safeApproveWithRetry(address token, address to, uint256 amount) public { SafeTransferLib.safeApproveWithRetry(token, to, amount); } function testTransferETH() public { SafeTransferLib.safeTransferETH(address(0xBEEF), 1e18); } function testTransferAllETH() public { SafeTransferLib.safeTransferAllETH(address(0xBEEF)); } function testTryTransferETH() public { MockETHRecipient recipient = new MockETHRecipient(false, false); bool success = SafeTransferLib.trySafeTransferETH(address(recipient), 1e18, gasleft()); assertTrue(success); } function testTryTransferAllETH() public { MockETHRecipient recipient = new MockETHRecipient(false, false); bool success = SafeTransferLib.trySafeTransferAllETH(address(recipient), gasleft()); assertTrue(success); } function testTryTransferETHWithNoStorageWrites() public { MockETHRecipient recipient = new MockETHRecipient(true, false); { bool success = SafeTransferLib.trySafeTransferETH( address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES ); assertFalse(success); } { uint256 counterBefore = recipient.counter(); bool success = SafeTransferLib.trySafeTransferETH( address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_GRIEF ); assertTrue(success); assertEq(recipient.counter(), counterBefore + 1); } { uint256 counterBefore = recipient.counter(); bool success = SafeTransferLib.trySafeTransferETH(address(recipient), 1e18, gasleft()); assertTrue(success); assertEq(recipient.counter(), counterBefore + 1); } } function testTryTransferETHWithNoGrief() public { MockETHRecipient recipient = new MockETHRecipient(false, true); { bool success = SafeTransferLib.trySafeTransferETH( address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES ); assertFalse(success); assertTrue(recipient.garbage() == 0); } { bool success = SafeTransferLib.trySafeTransferETH( address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_GRIEF ); assertFalse(success); assertTrue(recipient.garbage() == 0); } { bool success = SafeTransferLib.trySafeTransferETH(address(recipient), 1e18, gasleft()); assertTrue(success); assertTrue(recipient.garbage() != 0); } } function testForceTransferETHToGriever(uint256 amount, uint256 randomness) public { amount = amount % 1000 ether; uint256 originalBalance = address(this).balance; vm.deal(address(this), amount * 2); MockETHRecipient recipient = new MockETHRecipient(false, true); { uint256 receipientBalanceBefore = address(recipient).balance; uint256 senderBalanceBefore = address(this).balance; uint256 r = uint256(keccak256(abi.encode(randomness))) % 3; // Send to a griever with a gas stipend. Should not revert. if (r == 0) { this.forceSafeTransferETH( address(recipient), amount, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES ); } else if (r == 1) { this.forceSafeTransferETH( address(recipient), amount, SafeTransferLib.GAS_STIPEND_NO_GRIEF ); } else { this.forceSafeTransferETH(address(recipient), amount); } assertEq(address(recipient).balance - receipientBalanceBefore, amount); assertEq(senderBalanceBefore - address(this).balance, amount); // We use the `SELFDESTRUCT` to send, and thus the `garbage` should NOT be updated. assertTrue(recipient.garbage() == 0); } { uint256 receipientBalanceBefore = address(recipient).balance; uint256 senderBalanceBefore = address(this).balance; // Send more than remaining balance without gas stipend. Should revert. vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); this.forceSafeTransferETH(address(recipient), address(this).balance + 1, gasleft()); assertEq(address(recipient).balance - receipientBalanceBefore, 0); assertEq(senderBalanceBefore - address(this).balance, 0); // We did not send anything, and thus the `garbage` should NOT be updated. assertTrue(recipient.garbage() == 0); } { uint256 receipientBalanceBefore = address(recipient).balance; uint256 senderBalanceBefore = address(this).balance; // Send all the remaining balance without gas stipend. Should not revert. amount = address(this).balance; this.forceSafeTransferETH(address(recipient), amount, gasleft()); assertEq(address(recipient).balance - receipientBalanceBefore, amount); assertEq(senderBalanceBefore - address(this).balance, amount); // We use the normal `CALL` to send, and thus the `garbage` should be updated. assertTrue(recipient.garbage() != 0); } vm.deal(address(this), originalBalance); } function testForceTransferETHToGriever() public { testForceTransferETHToGriever(1 ether, 0); testForceTransferETHToGriever(1 ether, 1); testForceTransferETHToGriever(1 ether, 2); } function testTransferWithReturnsFalseReverts() public { verifySafeTransfer(address(returnsFalse), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR); } function testTransferWithRevertingReverts() public { verifySafeTransfer(address(reverting), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR); } function testTransferWithReturnsTooLittleReverts() public { verifySafeTransfer(address(returnsTooLittle), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR); } function testTransferFromWithReturnsFalseReverts() public { verifySafeTransferFrom( address(returnsFalse), address(0xFEED), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR ); } function testTransferFromWithRevertingReverts() public { verifySafeTransferFrom( address(reverting), address(0xFEED), address(0xBEEF), 1e18, _REVERTS_WITH_ANY ); } function testTransferFromWithReturnsTooLittleReverts() public { verifySafeTransferFrom( address(returnsTooLittle), address(0xFEED), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR ); } function testApproveWithReturnsFalseReverts() public { verifySafeApprove(address(returnsFalse), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR); } function testApproveWithRevertingReverts() public { verifySafeApprove(address(reverting), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR); } function testApproveWithReturnsTooLittleReverts() public { verifySafeApprove(address(returnsTooLittle), address(0xBEEF), 1e18, _REVERTS_WITH_SELECTOR); } function testBalanceOfStandardERC20() public view { erc20.balanceOf(address(this)); } function testBalanceOfStandardERC20(address to, uint256 amount) public { uint256 originalBalance = erc20.balanceOf(address(this)); while (originalBalance < amount) amount = _random(); while (to == address(this)) to = _randomHashedAddress(); SafeTransferLib.safeTransfer(address(erc20), _brutalized(to), originalBalance - amount); assertEq(SafeTransferLib.balanceOf(address(erc20), _brutalized(address(this))), amount); } function testTransferAllWithStandardERC20() public { SafeTransferLib.safeTransferAll(address(erc20), address(1)); } function testTransferAllWithStandardERC20(address to, uint256 amount) public { uint256 originalBalance = erc20.balanceOf(address(this)); while (originalBalance < amount) amount = _random(); while (to == address(this)) to = _randomHashedAddress(); SafeTransferLib.safeTransfer(address(erc20), _brutalized(to), originalBalance - amount); assertEq(erc20.balanceOf(address(this)), amount); assertEq(SafeTransferLib.safeTransferAll(address(erc20), _brutalized(to)), amount); assertEq(erc20.balanceOf(address(this)), 0); assertEq(erc20.balanceOf(to), originalBalance); } function testTrySafeTransferFrom(address from, address to, uint256 amount) public { uint256 balance = _random(); while (from == address(this) || to == address(this) || from == to) { from = _randomNonZeroAddress(); to = _randomNonZeroAddress(); } erc20.transfer(from, balance); vm.prank(from); erc20.approve(address(this), type(uint256).max); bool result = SafeTransferLib.trySafeTransferFrom(address(erc20), from, to, amount); assertEq(result, amount <= balance); } function testTransferAllFromWithStandardERC20() public { forceApprove(address(erc20), address(this), address(this), type(uint256).max); SafeTransferLib.safeTransferAllFrom(address(erc20), address(this), address(1)); } function testTransferAllFromWithStandardERC20(address from, address to, uint256 amount) public { while (!(to != from && to != address(this) && from != address(this))) { to = _randomNonZeroAddress(); from = _randomNonZeroAddress(); } SafeTransferLib.safeTransferAll(address(erc20), _brutalized(from)); uint256 originalBalance = erc20.balanceOf(from); while (originalBalance < amount) amount = _random(); forceApprove(address(erc20), from, address(this), type(uint256).max); SafeTransferLib.safeTransferFrom( address(erc20), _brutalized(from), _brutalized(to), originalBalance - amount ); assertEq(erc20.balanceOf(from), amount); assertEq( SafeTransferLib.safeTransferAllFrom(address(erc20), _brutalized(from), _brutalized(to)), amount ); assertEq(erc20.balanceOf(address(this)), 0); assertEq(erc20.balanceOf(to), originalBalance); } function testTransferWithMissingReturn(address to, uint256 amount) public { verifySafeTransfer(address(missingReturn), to, amount, _SUCCESS); } function testTransferWithStandardERC20(address to, uint256 amount) public { verifySafeTransfer(address(erc20), to, amount, _SUCCESS); } function testTransferWithReturnsTooMuch(address to, uint256 amount) public { verifySafeTransfer(address(returnsTooMuch), to, amount, _SUCCESS); } function testTransferWithNonGarbage(address to, uint256 amount) public { returnsRawBytes.setRawBytes(_generateNonGarbage()); verifySafeTransfer(address(returnsRawBytes), to, amount, _SUCCESS); } function testTransferWithNonContractReverts(bytes32, address to, uint256 amount) public { vm.expectRevert(SafeTransferLib.TransferFailed.selector); this.safeTransfer(_randomHashedAddress(), to, amount); } function safeTransfer(address token, address to, uint256 amount) public { SafeTransferLib.safeTransfer(token, to, amount); } function testTransferETHToContractWithoutFallbackReverts() public { vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); this.safeTransferETH(address(this), 1e18); } function testTransferAllETHToContractWithoutFallbackReverts() public { vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); this.safeTransferAllETH(address(this)); } function testTransferFromWithMissingReturn(address from, address to, uint256 amount) public { verifySafeTransferFrom(address(missingReturn), from, to, amount, _SUCCESS); } function testTransferFromWithStandardERC20(address from, address to, uint256 amount) public { verifySafeTransferFrom(address(erc20), from, to, amount, _SUCCESS); } function testTransferFromWithReturnsTooMuch(address from, address to, uint256 amount) public { verifySafeTransferFrom(address(returnsTooMuch), from, to, amount, _SUCCESS); } function testTransferFromWithNonGarbage(address from, address to, uint256 amount) public { returnsRawBytes.setRawBytes(_generateNonGarbage()); verifySafeTransferFrom(address(returnsRawBytes), from, to, amount, _SUCCESS); } function testTransferFromWithNonContractReverts( address nonContract, address from, address to, uint256 amount ) public { if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) { return; } vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); this.safeTransferFrom(nonContract, from, to, amount); } function testApproveWithMissingReturn(address to, uint256 amount) public { verifySafeApprove(address(missingReturn), to, amount, _SUCCESS); } function testApproveWithStandardERC20(address to, uint256 amount) public { verifySafeApprove(address(erc20), to, amount, _SUCCESS); } function testApproveWithReturnsTooMuch(address to, uint256 amount) public { verifySafeApprove(address(returnsTooMuch), to, amount, _SUCCESS); } function testApproveWithNonGarbage(address to, uint256 amount) public { returnsRawBytes.setRawBytes(_generateNonGarbage()); verifySafeApprove(address(returnsRawBytes), to, amount, _SUCCESS); } function testApproveWithNonContractReverts(address nonContract, address to, uint256 amount) public { if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) { return; } vm.expectRevert(SafeTransferLib.ApproveFailed.selector); this.safeApprove(nonContract, to, amount); } function testApproveWithRetryWithNonContractReverts( address nonContract, address to, uint256 amount ) public { if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) { return; } vm.expectRevert(SafeTransferLib.ApproveFailed.selector); this.safeApproveWithRetry(nonContract, to, amount); } function testApproveWithRetry(address to, uint256 amount0, uint256 amount1) public { MockERC20LikeUSDT usdt = new MockERC20LikeUSDT(); assertEq(usdt.allowance(address(this), to), 0); SafeTransferLib.safeApproveWithRetry(address(usdt), _brutalized(to), amount0); assertEq(usdt.allowance(address(this), to), amount0); if (amount0 != 0 && amount1 != 0) { verifySafeApprove(address(usdt), to, amount1, _REVERTS_WITH_SELECTOR); } SafeTransferLib.safeApproveWithRetry(address(usdt), _brutalized(to), amount1); assertEq(usdt.allowance(address(this), to), amount1); } function testApproveWithRetry() public { testApproveWithRetry(address(1), 123, 456); } function testTransferETH(bytes32, uint256 amount) public { amount = _bound(amount, 0, address(this).balance); SafeTransferLib.safeTransferETH(_randomHashedAddress(), amount); } function testTransferAllETH(bytes32) public { SafeTransferLib.safeTransferAllETH(_randomHashedAddress()); } function testTransferWithReturnsFalseReverts(address to, uint256 amount) public { verifySafeTransfer(address(returnsFalse), to, amount, _REVERTS_WITH_SELECTOR); } function testTransferWithRevertingReverts(address to, uint256 amount) public { verifySafeTransfer(address(reverting), to, amount, _REVERTS_WITH_SELECTOR); } function testTransferWithReturnsTooLittleReverts(address to, uint256 amount) public { verifySafeTransfer(address(returnsTooLittle), to, amount, _REVERTS_WITH_SELECTOR); } function testTransferWithReturnsTwoReverts(address to, uint256 amount) public { verifySafeTransfer(address(returnsTwo), to, amount, _REVERTS_WITH_SELECTOR); } function testTransferWithGarbageReverts(address to, uint256 amount) public { returnsRawBytes.setRawBytes(_generateGarbage()); verifySafeTransfer(address(returnsRawBytes), to, amount, _REVERTS_WITH_ANY); } function testTransferFromWithReturnsFalseReverts(address from, address to, uint256 amount) public { verifySafeTransferFrom(address(returnsFalse), from, to, amount, _REVERTS_WITH_SELECTOR); } function testTransferFromWithRevertingReverts(address from, address to, uint256 amount) public { verifySafeTransferFrom(address(reverting), from, to, amount, _REVERTS_WITH_ANY); } function testTransferFromWithReturnsTooLittleReverts(address from, address to, uint256 amount) public { verifySafeTransferFrom(address(returnsTooLittle), from, to, amount, _REVERTS_WITH_SELECTOR); } function testTransferFromWithReturnsTwoReverts(address from, address to, uint256 amount) public { verifySafeTransferFrom(address(returnsTwo), from, to, amount, _REVERTS_WITH_SELECTOR); } function testTransferFromWithGarbageReverts(address from, address to, uint256 amount) public { returnsRawBytes.setRawBytes(_generateGarbage()); verifySafeTransferFrom(address(returnsRawBytes), from, to, amount, _REVERTS_WITH_ANY); } function testApproveWithReturnsFalseReverts(address to, uint256 amount) public { verifySafeApprove(address(returnsFalse), to, amount, _REVERTS_WITH_SELECTOR); } function testApproveWithRevertingReverts(address to, uint256 amount) public { verifySafeApprove(address(reverting), to, amount, _REVERTS_WITH_SELECTOR); } function testApproveWithReturnsTooLittleReverts(address to, uint256 amount) public { verifySafeApprove(address(returnsTooLittle), to, amount, _REVERTS_WITH_SELECTOR); } function testApproveWithReturnsTwoReverts(address to, uint256 amount) public { verifySafeApprove(address(returnsTwo), to, amount, _REVERTS_WITH_SELECTOR); } function testApproveWithGarbageReverts(address to, uint256 amount) public { returnsRawBytes.setRawBytes(_generateGarbage()); verifySafeApprove(address(returnsRawBytes), to, amount, _REVERTS_WITH_ANY); } function testTransferETHToContractWithoutFallbackReverts(uint256 amount) public { vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); this.safeTransferETH(address(this), amount); } function testTransferAllETHToContractWithoutFallbackReverts(uint256) public { vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); this.safeTransferAllETH(address(this)); } function verifySafeTransfer(address token, address to, uint256 amount, uint256 mode) public { if (mode == _REVERTS_WITH_SELECTOR) { vm.expectRevert(SafeTransferLib.TransferFailed.selector); } else if (mode == _REVERTS_WITH_ANY) { (bool success,) = address(this).call( abi.encodeWithSignature( "verifySafeTransfer(address,address,uint256)", token, to, amount ) ); assertFalse(success); return; } this.verifySafeTransfer(token, to, amount); } function verifySafeTransfer(address token, address to, uint256 amount) public brutalizeMemory { uint256 preBal = ERC20(token).balanceOf(to); if (amount == ERC20(token).balanceOf(address(this)) && _randomChance(2)) { SafeTransferLib.safeTransferAll(address(token), _brutalized(to)); } else { SafeTransferLib.safeTransfer(address(token), _brutalized(to), amount); } uint256 postBal = ERC20(token).balanceOf(to); if (to == address(this)) { assertEq(preBal, postBal); } else { assertEq(postBal - preBal, amount); } } function verifySafeTransferFrom( address token, address from, address to, uint256 amount, uint256 mode ) public { if (mode == _REVERTS_WITH_SELECTOR) { vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); } else if (mode == _REVERTS_WITH_ANY) { (bool success,) = address(this).call( abi.encodeWithSignature( "verifySafeTransferFrom(address,address,address,uint256)", token, from, to, amount ) ); assertFalse(success); return; } this.verifySafeTransferFrom(token, from, to, amount); } function verifySafeTransferFrom(address token, address from, address to, uint256 amount) public brutalizeMemory { forceApprove(token, from, address(this), amount); // We cast to MissingReturnToken here because it won't check // that there was return data, which accommodates all tokens. MissingReturnToken(token).transfer(from, amount); uint256 preBal = ERC20(token).balanceOf(to); if (amount == ERC20(token).balanceOf(from) && _randomChance(2)) { SafeTransferLib.safeTransferAllFrom(address(token), _brutalized(from), _brutalized(to)); } else { SafeTransferLib.safeTransferFrom(token, _brutalized(from), _brutalized(to), amount); } uint256 postBal = ERC20(token).balanceOf(to); if (from == to) { assertEq(preBal, postBal); } else { assertEq(postBal - preBal, amount); } } function verifySafeApprove(address token, address to, uint256 amount, uint256 mode) public { if (mode == _REVERTS_WITH_SELECTOR) { vm.expectRevert(SafeTransferLib.ApproveFailed.selector); } else if (mode == _REVERTS_WITH_ANY) { (bool success,) = address(this).call( abi.encodeWithSignature( "verifySafeApprove(address,address,uint256)", token, to, amount ) ); assertFalse(success); return; } this.verifySafeApprove(token, to, amount); } function verifySafeApprove(address token, address to, uint256 amount) public { SafeTransferLib.safeApprove(_brutalized(address(token)), _brutalized(to), amount); assertEq(ERC20(token).allowance(address(this), to), amount); } function forceApprove(address token, address from, address to, uint256 amount) public { if (token == address(erc20)) { bytes32 allowanceSlot; /// @solidity memory-safe-assembly assembly { mstore(0x20, to) mstore(0x0c, 0x7f5e9f20) // `_ALLOWANCE_SLOT_SEED`. mstore(0x00, from) allowanceSlot := keccak256(0x0c, 0x34) } vm.store(token, allowanceSlot, bytes32(uint256(amount))); } else { vm.store( token, keccak256(abi.encode(to, keccak256(abi.encode(from, uint256(2))))), bytes32(uint256(amount)) ); } assertEq(ERC20(token).allowance(from, to), amount, "wrong allowance"); } function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) public { SafeTransferLib.forceSafeTransferETH(to, amount, gasStipend); } function forceSafeTransferETH(address to, uint256 amount) public { SafeTransferLib.forceSafeTransferETH(to, amount); } function safeTransferETH(address to, uint256 amount) public { SafeTransferLib.safeTransferETH(to, amount); } function safeTransferAllETH(address to) public { SafeTransferLib.safeTransferAllETH(to); } function _generateGarbage() internal returns (bytes memory result) { uint256 r = _random(); /// @solidity memory-safe-assembly assembly { for {} 1 {} { mstore(0x00, r) result := mload(0x40) let n := and(r, 0x7f) mstore(result, n) r := keccak256(0x00, 0x40) mstore(add(result, 0x20), r) mstore(0x40, add(result, 0x100)) if and(or(lt(n, 0x20), iszero(eq(r, 1))), gt(n, 0)) { break } } } } function _generateNonGarbage() internal returns (bytes memory result) { uint256 r = _random(); /// @solidity memory-safe-assembly assembly { if iszero(and(r, 1)) { result := mload(0x40) mstore(result, 0x20) mstore(add(result, 0x20), 1) mstore(0x40, add(result, 0x40)) } } } struct _TestTemps { address signer; uint256 privateKey; uint8 v; bytes32 r; bytes32 s; uint256 amount; address from; address spender; address to; uint256 nonce; bytes32 hash; address token; uint256 deadline; IPermit2.PermitSingle permit; } function testPermit2() public { _TestTemps memory t; t.token = address(erc20); t.deadline = block.timestamp; (t.signer, t.privateKey) = _randomSigner(); t.spender = _randomNonZeroAddress(); t.amount = _bound(_random(), 0, type(uint160).max); t.nonce = erc20.nonces(t.signer); t.hash = keccak256( abi.encode(_PERMIT_TYPEHASH, t.signer, t.spender, t.amount, t.nonce, t.deadline) ); t.hash = keccak256(abi.encodePacked("\x19\x01", erc20.DOMAIN_SEPARATOR(), t.hash)); (t.v, t.r, t.s) = vm.sign(t.privateKey, t.hash); this.permit2(t); } function testPermit2OnDAI() public { _TestTemps memory t; t.token = _DAI; t.deadline = block.timestamp; (t.signer, t.privateKey) = _randomSigner(); t.spender = _randomNonZeroAddress(); t.amount = _bound(_random(), 0, type(uint160).max); t.nonce = ERC20(_DAI).nonces(t.signer); t.hash = keccak256( abi.encode(_DAI_PERMIT_TYPEHASH, t.signer, t.spender, t.nonce, t.deadline, true) ); t.hash = keccak256(abi.encodePacked("\x19\x01", SafeTransferLib.DAI_DOMAIN_SEPARATOR, t.hash)); (t.v, t.r, t.s) = vm.sign(t.privateKey, t.hash); this.permit2(t); } function testSimplePermit2AndPermit2TransferFrom() public { for (uint256 t; t < 10; ++t) { _testSimplePermit2AndPermit2TransferFrom(); } } function _testSimplePermit2AndPermit2TransferFrom() internal { _TestTemps memory t; t.token = address(erc20); t.deadline = block.timestamp; (t.signer, t.privateKey) = _randomSigner(); t.spender = _randomNonZeroAddress(); t.amount = _bound(_random(), 0, type(uint160).max); erc20.transfer(t.signer, t.amount); vm.prank(t.signer); erc20.approve(_PERMIT2, type(uint256).max); t.permit.details.token = address(erc20); t.permit.details.amount = t.amount; t.permit.details.expiration = type(uint48).max; (,, t.permit.details.nonce) = IPermit2(_PERMIT2).allowance(t.signer, address(erc20), t.spender); t.permit.spender = t.spender; t.permit.sigDeadline = t.deadline; _generatePermitSignatureRaw(t); this.simplePermit2(t); t.to = _randomNonZeroAddress(); uint256 balanceBefore = erc20.balanceOf(t.to); vm.startPrank(t.spender); if (_randomChance(2)) { SafeTransferLib.permit2TransferFrom(address(erc20), t.signer, t.to, t.amount); } else { SafeTransferLib.safeTransferFrom2(address(erc20), t.signer, t.to, t.amount); } vm.stopPrank(); if (t.signer != t.to) { assertEq(erc20.balanceOf(t.to), balanceBefore + t.amount); } } function testSimplePermit2AndPermit2TransferFromGas() public { _TestTemps memory t; t.token = address(erc20); t.deadline = block.timestamp; (t.signer, t.privateKey) = _randomSigner(); t.spender = _randomNonZeroAddress(); t.amount = type(uint160).max; erc20.transfer(t.signer, t.amount); vm.prank(t.signer); erc20.approve(_PERMIT2, type(uint256).max); t.permit.details.token = address(erc20); t.permit.details.amount = uint160(t.amount); t.permit.details.expiration = type(uint48).max; (,, t.permit.details.nonce) = IPermit2(_PERMIT2).allowance(t.signer, address(erc20), t.spender); t.permit.spender = t.spender; t.permit.sigDeadline = t.deadline; _generatePermitSignatureRaw(t); this.simplePermit2(t); t.to = _randomNonZeroAddress(); vm.startPrank(t.spender); SafeTransferLib.permit2TransferFrom(address(erc20), t.signer, t.to, t.amount); vm.stopPrank(); } function testPermit2AnythingWithNonContractReverts() public { _TestTemps memory t; t.token = _randomHashedAddress(); t.deadline = block.timestamp; (t.signer, t.privateKey) = _randomSigner(); t.spender = _randomNonZeroAddress(); t.amount = type(uint160).max; t.permit.details.token = address(t.token); t.permit.details.amount = uint160(t.amount); t.permit.details.expiration = type(uint48).max; (,, t.permit.details.nonce) = IPermit2(_PERMIT2).allowance(t.signer, address(t.token), t.spender); t.permit.spender = t.spender; t.permit.sigDeadline = t.deadline; _generatePermitSignatureRaw(t); vm.expectRevert(SafeTransferLib.Permit2Failed.selector); this.simplePermit2(t); vm.expectRevert(SafeTransferLib.Permit2Failed.selector); this.permit2(t); t.to = _randomNonZeroAddress(); vm.startPrank(t.spender); vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); this.permit2TransferFrom(address(t.token), t.signer, t.to, t.amount); vm.stopPrank(); } function testPermit2TransferFromInvalidAmount(uint256) public { _TestTemps memory t; t.token = address(erc20); t.deadline = block.timestamp; (t.signer, t.privateKey) = _randomSigner(); t.spender = _randomNonZeroAddress(); t.amount = _bound(_random(), 0, type(uint160).max); erc20.transfer(t.signer, t.amount); vm.prank(t.signer); erc20.approve(_PERMIT2, type(uint256).max); t.permit.details.token = address(erc20); t.permit.details.amount = uint160(t.amount); t.permit.details.expiration = type(uint48).max; (,, t.permit.details.nonce) = IPermit2(_PERMIT2).allowance(t.signer, address(erc20), t.spender); t.permit.spender = t.spender; t.permit.sigDeadline = t.deadline; _generatePermitSignatureRaw(t); this.simplePermit2(t); t.to = _randomNonZeroAddress(); uint256 overflowedAmount = _bound(_random(), 2 ** 160, type(uint256).max); vm.expectRevert(SafeTransferLib.Permit2AmountOverflow.selector); this.permit2TransferFrom(address(erc20), t.signer, t.to, overflowedAmount); } function testPermit2InvalidAmount(uint256) public { _TestTemps memory t; t.token = address(erc20); t.deadline = block.timestamp; (t.signer, t.privateKey) = _randomSigner(); t.spender = _randomNonZeroAddress(); t.amount = _bound(_random(), 2 ** 160, type(uint256).max); erc20.transfer(t.signer, t.amount); vm.prank(t.signer); erc20.approve(_PERMIT2, type(uint256).max); t.permit.details.token = address(erc20); t.permit.details.amount = t.amount; t.permit.details.expiration = type(uint48).max; (,, t.permit.details.nonce) = IPermit2(_PERMIT2).allowance(t.signer, address(erc20), t.spender); t.permit.spender = t.spender; t.permit.sigDeadline = t.deadline; _generatePermitSignatureRaw(t); vm.expectRevert(SafeTransferLib.Permit2AmountOverflow.selector); this.simplePermit2(t); } function _generatePermitSignatureRaw(_TestTemps memory t) internal view { bytes32 domainSeparator = ERC20(_PERMIT2).DOMAIN_SEPARATOR(); t.hash = keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, t.permit.details)); t.hash = keccak256( abi.encode(_PERMIT_SINGLE_TYPEHASH, t.hash, t.permit.spender, t.permit.sigDeadline) ); t.hash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, t.hash)); (t.v, t.r, t.s) = vm.sign(t.privateKey, t.hash); } function permit2TransferFrom(address token, address from, address to, uint256 amount) public { SafeTransferLib.permit2TransferFrom( _brutalized(token), _brutalized(from), _brutalized(to), amount ); } function safeTransferFrom2(address token, address from, address to, uint256 amount) public { SafeTransferLib.safeTransferFrom2( _brutalized(token), _brutalized(from), _brutalized(to), amount ); } function permit2(_TestTemps calldata t) public { SafeTransferLib.permit2( _brutalized(t.token), _brutalized(t.signer), _brutalized(t.spender), t.amount, t.deadline, t.v, t.r, t.s ); } function simplePermit2(_TestTemps calldata t) public { SafeTransferLib.simplePermit2( _brutalized(t.token), _brutalized(t.signer), _brutalized(t.spender), t.amount, t.deadline, t.v, t.r, t.s ); } function testTotalSupplyQuery() public { uint256 totalSupplyBefore = this.totalSupplyQuery(address(erc20)); erc20.burn(address(this), 123); assertEq(this.totalSupplyQuery(address(erc20)), totalSupplyBefore - 123); vm.expectRevert(SafeTransferLib.TotalSupplyQueryFailed.selector); this.totalSupplyQuery(address(0)); } function totalSupplyQuery(address token) public view returns (uint256) { return SafeTransferLib.totalSupply(token); } }