chore: OneTransferFromOnly docs + proper slots

Also revert instead of returning if multiple `transferFrom`s are attempted - as this signals that encoding is incorrect or has been messed with.
This commit is contained in:
TAMARA LIPOWSKI
2025-05-15 10:08:33 -04:00
parent 6cff470999
commit 69c8325a1d
2 changed files with 30 additions and 13 deletions

View File

@@ -5,24 +5,42 @@ import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@permit2/src/interfaces/IAllowanceTransfer.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 { contract OneTransferFromOnly {
using SafeERC20 for IERC20; 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 permit2;
IAllowanceTransfer public immutable permit2lal; // keccak256("Dispatcher#TOKEN_IN_SLOT")
uint256 private constant _TOKEN_IN_SLOT = 123; uint256 private constant _TOKEN_IN_SLOT =
uint256 private constant _AMOUNT_IN_SLOT = 124; 0x66f353cfe8e3cbe0d03292348fbf0fca32e6e07fa0c2a52b4aac22193ac3b894;
uint256 private constant _IS_PERMIT2_SLOT = 125; // keccak256("Dispatcher#AMOUNT_IN_SLOT")
uint256 private constant _SENDER_SLOT = 126; uint256 private constant _AMOUNT_IN_SLOT =
uint256 private constant _IS_TRANSFER_EXECUTED_SLOT = 127; 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) { constructor(address _permit2) {
if (_permit2 == address(0)) { if (_permit2 == address(0)) {
revert TokenTransfer__AddressZero(); revert OneTransferFromOnly__AddressZero();
} }
permit2lal = IAllowanceTransfer(_permit2); permit2 = IAllowanceTransfer(_permit2);
} }
// slither-disable-next-line assembly // slither-disable-next-line assembly
@@ -56,12 +74,12 @@ contract OneTransferFromOnly {
isTransferExecuted := tload(_IS_TRANSFER_EXECUTED_SLOT) isTransferExecuted := tload(_IS_TRANSFER_EXECUTED_SLOT)
} }
if (isTransferExecuted) { if (isTransferExecuted) {
return; // or revert? revert OneTransferFromOnly__MultipleTransferFrom();
} }
if (isPermit2) { if (isPermit2) {
// Permit2.permit is already called from the TychoRouter // Permit2.permit is already called from the TychoRouter
permit2lal.transferFrom(sender, receiver, uint160(amount), tokenIn); permit2.transferFrom(sender, receiver, uint160(amount), tokenIn);
assembly { assembly {
tstore(_IS_TRANSFER_EXECUTED_SLOT, true) tstore(_IS_TRANSFER_EXECUTED_SLOT, true)
} }

View File

@@ -73,7 +73,6 @@ contract TychoRouter is
ReentrancyGuard, ReentrancyGuard,
OneTransferFromOnly OneTransferFromOnly
{ {
IAllowanceTransfer public immutable permit2;
IWETH private immutable _weth; IWETH private immutable _weth;
using SafeERC20 for IERC20; using SafeERC20 for IERC20;