78 lines
2.5 KiB
Solidity
78 lines
2.5 KiB
Solidity
// SPDX-License-Identifier: BUSL-1.1
|
|
pragma solidity ^0.8.26;
|
|
|
|
import "@interfaces/IExecutor.sol";
|
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
|
|
|
|
error TokenTransfer__AddressZero();
|
|
|
|
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;
|
|
|
|
constructor(address _permit2) {
|
|
if (_permit2 == address(0)) {
|
|
revert TokenTransfer__AddressZero();
|
|
}
|
|
permit2lal = IAllowanceTransfer(_permit2);
|
|
}
|
|
|
|
function tstoreTransferFromInfo(
|
|
address tokenIn,
|
|
address amountIn,
|
|
bool isPermit2,
|
|
address sender
|
|
) internal {
|
|
assembly {
|
|
tstore(_TOKEN_IN_SLOT, tokenIn)
|
|
tstore(_AMOUNT_IN_SLOT, amountIn)
|
|
tstore(_IS_PERMIT2_SLOT, isPermit2)
|
|
tstore(_SENDER_SLOT, sender)
|
|
tstore(_IS_TRANSFER_EXECUTED_SLOT, false)
|
|
}
|
|
}
|
|
|
|
function _transfer(address receiver)
|
|
// we could pass the amount and address too and compare to what is in the slots?
|
|
internal
|
|
{
|
|
address tokenIn;
|
|
uint256 amount;
|
|
bool isPermit2;
|
|
address sender;
|
|
bool isTransferExecuted;
|
|
assembly {
|
|
tokenIn := tload(_TOKEN_IN_SLOT)
|
|
amount := tload(_AMOUNT_IN_SLOT)
|
|
isPermit2 := tload(_IS_PERMIT2_SLOT)
|
|
sender := tload(_SENDER_SLOT)
|
|
isTransferExecuted := tload(_IS_TRANSFER_EXECUTED_SLOT)
|
|
}
|
|
if (isTransferExecuted) {
|
|
return; // or revert?
|
|
}
|
|
|
|
if (isPermit2) {
|
|
// Permit2.permit is already called from the TychoRouter
|
|
permit2lal.transferFrom(sender, receiver, uint160(amount), tokenIn);
|
|
assembly {
|
|
tstore(_IS_TRANSFER_EXECUTED_SLOT, true)
|
|
}
|
|
} else {
|
|
// slither-disable-next-line arbitrary-send-erc20
|
|
IERC20(tokenIn).safeTransferFrom(sender, receiver, amount);
|
|
assembly {
|
|
tstore(_IS_TRANSFER_EXECUTED_SLOT, true)
|
|
}
|
|
}
|
|
}
|
|
}
|