Merge branch 'main' into router/hr/ENG-4171-Implement-Pause

This commit is contained in:
Harsh Vardhan Roy
2025-01-27 20:40:36 +05:30
committed by GitHub
19 changed files with 586 additions and 95 deletions

View File

@@ -0,0 +1,102 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import "@interfaces/IExecutor.sol";
error ExecutionDispatcher__UnapprovedExecutor();
error ExecutionDispatcher__NonContractExecutor();
/**
* @title ExecutionDispatcher - Dispatch execution to external contracts
* @author PropellerHeads Devs
* @dev Provides the ability to delegate execution of swaps to external
* contracts. This allows dynamically adding new supported protocols
* without needing to upgrade any contracts. External contracts will
* be called using delegatecall so they can share state with the main
* contract if needed.
*
* Note Executor contracts need to implement the IExecutor interface unless
* an alternate selector is specified.
*/
contract ExecutionDispatcher {
mapping(address => bool) public executors;
event ExecutorSet(address indexed executor);
event ExecutorRemoved(address indexed executor);
/**
* @dev Adds or replaces an approved executor contract address if it is a
* contract.
* @param target address of the executor contract
*/
function _setExecutor(address target) internal {
if (target.code.length == 0) {
revert ExecutionDispatcher__NonContractExecutor();
}
executors[target] = true;
emit ExecutorSet(target);
}
/**
* @dev Removes an approved executor contract address
* @param target address of the executor contract
*/
function _removeExecutor(address target) internal {
delete executors[target];
emit ExecutorRemoved(target);
}
/**
* @dev Calls an executor, assumes swap.protocolData contains
* protocol-specific data required by the executor.
*/
// slither-disable-next-line dead-code
function _callExecutor(uint256 amount, bytes calldata data)
internal
returns (uint256 calculatedAmount)
{
address executor;
bytes4 decodedSelector;
bytes memory protocolData;
(executor, decodedSelector, protocolData) =
_decodeExecutorAndSelector(data);
if (!executors[executor]) {
revert ExecutionDispatcher__UnapprovedExecutor();
}
bytes4 selector = decodedSelector == bytes4(0)
? IExecutor.swap.selector
: decodedSelector;
// slither-disable-next-line low-level-calls
(bool success, bytes memory result) = executor.delegatecall(
abi.encodeWithSelector(selector, amount, protocolData)
);
if (!success) {
revert(
string(
result.length > 0
? result
: abi.encodePacked("Execution failed")
)
);
}
calculatedAmount = abi.decode(result, (uint256));
}
// slither-disable-next-line dead-code
function _decodeExecutorAndSelector(bytes calldata data)
internal
pure
returns (address executor, bytes4 selector, bytes memory protocolData)
{
require(data.length >= 24, "Invalid data length");
executor = address(uint160(bytes20(data[:20])));
selector = bytes4(data[20:24]);
protocolData = data[24:];
}
}

View File

@@ -1,17 +0,0 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
/**
* @title SwapExecutionDispatcher - Dispatch swap execution to external contracts
* @author PropellerHeads Devs
* @dev Provides the ability to delegate execution of swaps to external
* contracts. This allows dynamically adding new supported protocols
* without needing to upgrade any contracts. External contracts will
* be called using delegatecall so they can share state with the main
* contract if needed.
*
* Note Executor contracts need to implement the ISwapExecutor interface
*/
contract SwapExecutionDispatcher {
mapping(address => bool) public swapExecutors;
}

View File

@@ -5,13 +5,12 @@ import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
import "./SwapExecutionDispatcher.sol";
import "./ExecutionDispatcher.sol";
import "./CallbackVerificationDispatcher.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
error TychoRouter__WithdrawalFailed();
error TychoRouter__AddressZero();
error TychoRouter__NonContractExecutor();
error TychoRouter__NonContractVerifier();
contract TychoRouter is
@@ -49,7 +48,6 @@ contract TychoRouter is
address indexed oldFeeReceiver, address indexed newFeeReceiver
);
event FeeSet(uint256 indexed oldFee, uint256 indexed newFee);
event ExecutorSet(address indexed executor);
event CallbackVerifierSet(address indexed callbackVerifier);
constructor(address _permit2) {
@@ -112,32 +110,30 @@ contract TychoRouter is
}
/**
* @dev Entrypoint to add or replace an approved swap executor contract address
* @param target address of the swap method contract
* @dev Entrypoint to add or replace an approved executor contract address
* @param target address of the executor contract
*/
function setSwapExecutor(address target)
function setExecutor(address target)
external
onlyRole(EXECUTOR_SETTER_ROLE)
{
if (target.code.length == 0) revert TychoRouter__NonContractExecutor();
swapExecutors[target] = true;
emit ExecutorSet(target);
_setExecutor(target);
}
/**
* @dev Entrypoint to remove an approved swap executor contract address
* @param target address of the swap method contract
* @dev Entrypoint to remove an approved executor contract address
* @param target address of the executor contract
*/
function removeSwapExecutor(address target)
function removeExecutor(address target)
external
onlyRole(EXECUTOR_SETTER_ROLE)
{
delete swapExecutors[target];
_removeExecutor(target);
}
/**
* @dev Entrypoint to add or replace an approved swap executor contract address
* @param target address of the swap method contract
* @dev Entrypoint to add or replace an approved callback verifier contract address
* @param target address of the callback verifier contract
*/
function setCallbackVerifier(address target)
external
@@ -149,8 +145,8 @@ contract TychoRouter is
}
/**
* @dev Entrypoint to remove an approved swap executor contract address
* @param target address of the swap method contract
* @dev Entrypoint to remove an approved callback verifier contract address
* @param target address of the callback verifier contract
*/
function removeCallbackVerifier(address target)
external

View File

@@ -0,0 +1,75 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol";
error UniswapV2Executor__InvalidDataLength();
contract UniswapV2Executor is IExecutor {
using SafeERC20 for IERC20;
function swap(uint256 givenAmount, bytes calldata data)
external
returns (uint256 calculatedAmount)
{
address target;
address receiver;
bool zeroForOne;
IERC20 tokenIn;
(tokenIn, target, receiver, zeroForOne) = _decodeData(data);
calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne);
tokenIn.safeTransfer(target, givenAmount);
IUniswapV2Pair pool = IUniswapV2Pair(target);
if (zeroForOne) {
pool.swap(0, calculatedAmount, receiver, "");
} else {
pool.swap(calculatedAmount, 0, receiver, "");
}
}
function _decodeData(bytes calldata data)
internal
pure
returns (
IERC20 inToken,
address target,
address receiver,
bool zeroForOne
)
{
if (data.length != 61) {
revert UniswapV2Executor__InvalidDataLength();
}
inToken = IERC20(address(bytes20(data[0:20])));
target = address(bytes20(data[20:40]));
receiver = address(bytes20(data[40:60]));
zeroForOne = uint8(data[60]) > 0;
}
function _getAmountOut(address target, uint256 amountIn, bool zeroForOne)
internal
view
returns (uint256 amount)
{
IUniswapV2Pair pair = IUniswapV2Pair(target);
uint112 reserveIn;
uint112 reserveOut;
if (zeroForOne) {
// slither-disable-next-line unused-return
(reserveIn, reserveOut,) = pair.getReserves();
} else {
// slither-disable-next-line unused-return
(reserveOut, reserveIn,) = pair.getReserves();
}
require(reserveIn > 0 && reserveOut > 0, "L");
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * uint256(reserveOut);
uint256 denominator = (uint256(reserveIn) * 1000) + amountInWithFee;
amount = numerator / denominator;
}
}