diff --git a/foundry/src/OneTransferFromOnly.sol b/foundry/src/OneTransferFromOnly.sol index 072ac48..1a92344 100644 --- a/foundry/src/OneTransferFromOnly.sol +++ b/foundry/src/OneTransferFromOnly.sol @@ -5,24 +5,42 @@ import "@interfaces/IExecutor.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@permit2/src/interfaces/IAllowanceTransfer.sol"; -error TokenTransfer__AddressZero(); +error OneTransferFromOnly__AddressZero(); +error OneTransferFromOnly__MultipleTransferFrom(); +/** + * @title OneTransferFromOnly - Restrict to one transferFrom on approved params per swap + * @author PropellerHeads Devs + * @dev Restricts to one `transferFrom` (using `permit2` or regular `transferFrom`) + * per swap, while ensuring that the `transferFrom` is only performed on the input + * token and the input amount, from the msg.sender's wallet that calls the main swap + * method. Reverts if multiple `transferFrom`s are attempted. + */ contract OneTransferFromOnly { using SafeERC20 for IERC20; - // this is a stupid name but the compiler was complaining that we already had a permit2 variable in TychoRouter - IAllowanceTransfer public immutable permit2lal; - uint256 private constant _TOKEN_IN_SLOT = 123; - uint256 private constant _AMOUNT_IN_SLOT = 124; - uint256 private constant _IS_PERMIT2_SLOT = 125; - uint256 private constant _SENDER_SLOT = 126; - uint256 private constant _IS_TRANSFER_EXECUTED_SLOT = 127; + IAllowanceTransfer public immutable permit2; + // keccak256("Dispatcher#TOKEN_IN_SLOT") + uint256 private constant _TOKEN_IN_SLOT = + 0x66f353cfe8e3cbe0d03292348fbf0fca32e6e07fa0c2a52b4aac22193ac3b894; + // keccak256("Dispatcher#AMOUNT_IN_SLOT") + uint256 private constant _AMOUNT_IN_SLOT = + 0x1f40aa2d23d66d03722685ce02e5d3a95545dfc8e7c56d1026790aa30be48937; + // keccak256("Dispatcher#IS_PERMIT2_SLOT") + uint256 private constant _IS_PERMIT2_SLOT = + 0x3162c9d1175ca0ca7441f87984fdac41bbfdb13246f42c8bb4414d345da39e2a; + // keccak256("Dispatcher#SENDER_SLOT") + uint256 private constant _SENDER_SLOT = + 0x5dcc7974be5cb30f183f878073999aaa6620995b9e052ab5a713071ff60ae9b5; + // keccak256("Dispatcher#IS_TRANSFER_EXECUTED_SLOT") + uint256 private constant _IS_TRANSFER_EXECUTED_SLOT = + 0x1c64085c839fc2ff0f0aad20613eb6d056a1024e5990211e9eb30824dcd128c2; constructor(address _permit2) { if (_permit2 == address(0)) { - revert TokenTransfer__AddressZero(); + revert OneTransferFromOnly__AddressZero(); } - permit2lal = IAllowanceTransfer(_permit2); + permit2 = IAllowanceTransfer(_permit2); } // slither-disable-next-line assembly @@ -56,12 +74,12 @@ contract OneTransferFromOnly { isTransferExecuted := tload(_IS_TRANSFER_EXECUTED_SLOT) } if (isTransferExecuted) { - return; // or revert? + revert OneTransferFromOnly__MultipleTransferFrom(); } if (isPermit2) { // Permit2.permit is already called from the TychoRouter - permit2lal.transferFrom(sender, receiver, uint160(amount), tokenIn); + permit2.transferFrom(sender, receiver, uint160(amount), tokenIn); assembly { tstore(_IS_TRANSFER_EXECUTED_SLOT, true) } diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 538cc43..773e235 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -73,7 +73,6 @@ contract TychoRouter is ReentrancyGuard, OneTransferFromOnly { - IAllowanceTransfer public immutable permit2; IWETH private immutable _weth; using SafeERC20 for IERC20;