Merge pull request #195 from propeller-heads/router/dc/ENG-4529-arbitrage-balance-check
feat: Arbitrage balance check and explicitly handle the TransferType.NONE case
This commit is contained in:
@@ -436,16 +436,15 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
Address.sendValue(payable(receiver), amountOut);
|
Address.sendValue(payable(receiver), amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenIn != tokenOut) {
|
_verifyAmountOutWasReceived(
|
||||||
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
tokenIn,
|
||||||
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
tokenOut,
|
||||||
if (userAmount != amountOut) {
|
initialBalanceTokenOut,
|
||||||
revert TychoRouter__AmountOutNotFullyReceived(
|
amountOut,
|
||||||
userAmount, amountOut
|
receiver,
|
||||||
|
amountIn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Internal implementation of the core swap logic shared between singleSwap() and singleSwapPermit2().
|
* @notice Internal implementation of the core swap logic shared between singleSwap() and singleSwapPermit2().
|
||||||
@@ -493,16 +492,15 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
Address.sendValue(payable(receiver), amountOut);
|
Address.sendValue(payable(receiver), amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenIn != tokenOut) {
|
_verifyAmountOutWasReceived(
|
||||||
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
tokenIn,
|
||||||
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
tokenOut,
|
||||||
if (userAmount != amountOut) {
|
initialBalanceTokenOut,
|
||||||
revert TychoRouter__AmountOutNotFullyReceived(
|
amountOut,
|
||||||
userAmount, amountOut
|
receiver,
|
||||||
|
amountIn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Internal implementation of the core swap logic shared between sequentialSwap() and sequentialSwapPermit2().
|
* @notice Internal implementation of the core swap logic shared between sequentialSwap() and sequentialSwapPermit2().
|
||||||
@@ -546,17 +544,15 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
_unwrapETH(amountOut);
|
_unwrapETH(amountOut);
|
||||||
Address.sendValue(payable(receiver), amountOut);
|
Address.sendValue(payable(receiver), amountOut);
|
||||||
}
|
}
|
||||||
|
_verifyAmountOutWasReceived(
|
||||||
if (tokenIn != tokenOut) {
|
tokenIn,
|
||||||
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
tokenOut,
|
||||||
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
initialBalanceTokenOut,
|
||||||
if (userAmount != amountOut) {
|
amountOut,
|
||||||
revert TychoRouter__AmountOutNotFullyReceived(
|
receiver,
|
||||||
userAmount, amountOut
|
amountIn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Executes sequential swaps as defined by the provided swap graph.
|
* @dev Executes sequential swaps as defined by the provided swap graph.
|
||||||
@@ -784,4 +780,27 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
return
|
return
|
||||||
token == address(0) ? owner.balance : IERC20(token).balanceOf(owner);
|
token == address(0) ? owner.balance : IERC20(token).balanceOf(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Verifies that the expected amount of output tokens was received by the receiver.
|
||||||
|
* It also handles the case of arbitrage swaps where the input and output tokens are the same.
|
||||||
|
*/
|
||||||
|
function _verifyAmountOutWasReceived(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
uint256 initialBalanceTokenOut,
|
||||||
|
uint256 amountOut,
|
||||||
|
address receiver,
|
||||||
|
uint256 amountIn
|
||||||
|
) internal view {
|
||||||
|
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
||||||
|
if (tokenIn == tokenOut) {
|
||||||
|
// If it is an arbitrage, we need to remove the amountIn from the initial balance to get a correct userAmount
|
||||||
|
initialBalanceTokenOut -= amountIn;
|
||||||
|
}
|
||||||
|
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
||||||
|
if (userAmount != amountOut) {
|
||||||
|
revert TychoRouter__AmountOutNotFullyReceived(userAmount, amountOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|||||||
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
|
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
|
||||||
|
|
||||||
error TokenTransfer__AddressZero();
|
error TokenTransfer__AddressZero();
|
||||||
|
error TokenTransfer__InvalidTransferType();
|
||||||
|
|
||||||
contract TokenTransfer {
|
contract TokenTransfer {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
@@ -45,7 +46,9 @@ contract TokenTransfer {
|
|||||||
uint256 amount,
|
uint256 amount,
|
||||||
TransferType transferType
|
TransferType transferType
|
||||||
) internal {
|
) internal {
|
||||||
if (transferType == TransferType.TRANSFER_TO_PROTOCOL) {
|
if (transferType == TransferType.NONE) {
|
||||||
|
return;
|
||||||
|
} else if (transferType == TransferType.TRANSFER_TO_PROTOCOL) {
|
||||||
if (tokenIn == address(0)) {
|
if (tokenIn == address(0)) {
|
||||||
payable(receiver).transfer(amount);
|
payable(receiver).transfer(amount);
|
||||||
} else {
|
} else {
|
||||||
@@ -65,6 +68,8 @@ contract TokenTransfer {
|
|||||||
permit2.transferFrom(
|
permit2.transferFrom(
|
||||||
sender, address(this), uint160(amount), tokenIn
|
sender, address(this), uint160(amount), tokenIn
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
revert TokenTransfer__InvalidTransferType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user