diff --git a/foundry/src/interfaces/IExecutor.sol b/foundry/interfaces/IExecutor.sol similarity index 100% rename from foundry/src/interfaces/IExecutor.sol rename to foundry/interfaces/IExecutor.sol diff --git a/foundry/interfaces/ISwapExecutor.sol b/foundry/interfaces/ISwapExecutor.sol deleted file mode 100644 index fef5750..0000000 --- a/foundry/interfaces/ISwapExecutor.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -pragma abicoder v2; - -interface ISwapExecutor { - /** - * @notice Performs a swap on a liquidity pool. - * @dev This method takes the amount of the input token and returns the amount of - * the output token which has been swapped. - * - * Note Part of the informal interface is that the executor supports sending the received - * tokens to a receiver address. If the underlying smart contract does not provide this - * functionality consider adding an additional transfer in the implementation. - * - * @param givenAmount The amount of the input token to swap. - * @param data Data that holds information necessary to perform the swap. - * @return calculatedAmount The amount of the output token swapped, depending on - * the givenAmount inputted. - */ - function swap(uint256 givenAmount, bytes calldata data) - external - returns (uint256 calculatedAmount); -} - -interface ISwapExecutorErrors { - error InvalidParameterLength(uint256); - error UnknownPoolType(uint8); -} diff --git a/foundry/src/SwapExecutionDispatcher.sol b/foundry/src/ExecutionDispatcher.sol similarity index 64% rename from foundry/src/SwapExecutionDispatcher.sol rename to foundry/src/ExecutionDispatcher.sol index 99f7743..090368e 100644 --- a/foundry/src/SwapExecutionDispatcher.sol +++ b/foundry/src/ExecutionDispatcher.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; -import "@interfaces/ISwapExecutor.sol"; +import "@interfaces/IExecutor.sol"; -error SwapExecutionDispatcher__UnapprovedExecutor(); -error SwapExecutionDispatcher__NonContractExecutor(); +error ExecutionDispatcher__UnapprovedExecutor(); +error ExecutionDispatcher__NonContractExecutor(); /** - * @title SwapExecutionDispatcher - Dispatch swap execution to external contracts + * @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 @@ -15,34 +15,34 @@ error SwapExecutionDispatcher__NonContractExecutor(); * be called using delegatecall so they can share state with the main * contract if needed. * - * Note Executor contracts need to implement the ISwapExecutor interface unless + * Note Executor contracts need to implement the IExecutor interface unless * an alternate selector is specified. */ -contract SwapExecutionDispatcher { - mapping(address => bool) public swapExecutors; +contract ExecutionDispatcher { + mapping(address => bool) public executors; event ExecutorSet(address indexed executor); event ExecutorRemoved(address indexed executor); /** - * @dev Adds or replaces an approved swap executor contract address if it is a + * @dev Adds or replaces an approved executor contract address if it is a * contract. - * @param target address of the swap executor contract + * @param target address of the executor contract */ - function _setSwapExecutor(address target) internal { + function _setExecutor(address target) internal { if (target.code.length == 0) { - revert SwapExecutionDispatcher__NonContractExecutor(); + revert ExecutionDispatcher__NonContractExecutor(); } - swapExecutors[target] = true; + executors[target] = true; emit ExecutorSet(target); } /** - * @dev Removes an approved swap executor contract address - * @param target address of the swap executor contract + * @dev Removes an approved executor contract address + * @param target address of the executor contract */ - function _removeSwapExecutor(address target) internal { - delete swapExecutors[target]; + function _removeExecutor(address target) internal { + delete executors[target]; emit ExecutorRemoved(target); } @@ -51,7 +51,7 @@ contract SwapExecutionDispatcher { * protocol-specific data required by the executor. */ // slither-disable-next-line dead-code - function _callSwapExecutor(uint256 amount, bytes calldata data) + function _callExecutor(uint256 amount, bytes calldata data) internal returns (uint256 calculatedAmount) { @@ -62,12 +62,12 @@ contract SwapExecutionDispatcher { (executor, decodedSelector, protocolData) = _decodeExecutorAndSelector(data); - if (!swapExecutors[executor]) { - revert SwapExecutionDispatcher__UnapprovedExecutor(); + if (!executors[executor]) { + revert ExecutionDispatcher__UnapprovedExecutor(); } bytes4 selector = decodedSelector == bytes4(0) - ? ISwapExecutor.swap.selector + ? IExecutor.swap.selector : decodedSelector; // slither-disable-next-line low-level-calls @@ -80,7 +80,7 @@ contract SwapExecutionDispatcher { string( result.length > 0 ? result - : abi.encodePacked("Swap execution failed") + : abi.encodePacked("Execution failed") ) ); } diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 635c2ea..44d7752 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -5,7 +5,7 @@ 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"; error TychoRouter__WithdrawalFailed(); @@ -14,7 +14,7 @@ error TychoRouter__NonContractVerifier(); contract TychoRouter is AccessControl, - SwapExecutionDispatcher, + ExecutionDispatcher, CallbackVerificationDispatcher { IAllowanceTransfer public immutable permit2; @@ -92,30 +92,30 @@ contract TychoRouter is } /** - * @dev Entrypoint to add or replace an approved swap executor contract address - * @param target address of the swap executor 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) { - _setSwapExecutor(target); + _setExecutor(target); } /** - * @dev Entrypoint to remove an approved swap executor contract address - * @param target address of the swap executor 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) { - _removeSwapExecutor(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 @@ -127,8 +127,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 diff --git a/foundry/src/executors/UniswapV2Executor.sol b/foundry/src/executors/UniswapV2Executor.sol index ebcde96..01a507e 100644 --- a/foundry/src/executors/UniswapV2Executor.sol +++ b/foundry/src/executors/UniswapV2Executor.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; -import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol"; +import "@interfaces/IExecutor.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {IExecutor} from "../interfaces/IExecutor.sol"; +import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol"; error UniswapV2Executor__InvalidDataLength(); diff --git a/foundry/test/SwapExecutionDispatcher.t.sol b/foundry/test/ExecutionDispatcher.t.sol similarity index 77% rename from foundry/test/SwapExecutionDispatcher.t.sol rename to foundry/test/ExecutionDispatcher.t.sol index c313694..4895d03 100644 --- a/foundry/test/SwapExecutionDispatcher.t.sol +++ b/foundry/test/ExecutionDispatcher.t.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; -import "@src/SwapExecutionDispatcher.sol"; +import "@src/ExecutionDispatcher.sol"; import "./TychoRouterTestSetup.sol"; -contract SwapExecutionDispatcherExposed is SwapExecutionDispatcher { - function exposedCallSwapExecutor(uint256 amount, bytes calldata data) +contract ExecutionDispatcherExposed is ExecutionDispatcher { + function exposedCallExecutor(uint256 amount, bytes calldata data) external returns (uint256 calculatedAmount) { - return _callSwapExecutor(amount, data); + return _callExecutor(amount, data); } function exposedDecodeExecutorAndSelector(bytes calldata data) @@ -20,17 +20,17 @@ contract SwapExecutionDispatcherExposed is SwapExecutionDispatcher { return _decodeExecutorAndSelector(data); } - function exposedSetSwapExecutor(address target) external { - _setSwapExecutor(target); + function exposedSetExecutor(address target) external { + _setExecutor(target); } - function exposedRemoveSwapExecutor(address target) external { - _removeSwapExecutor(target); + function exposedRemoveExecutor(address target) external { + _removeExecutor(target); } } -contract SwapExecutionDispatcherTest is Constants { - SwapExecutionDispatcherExposed dispatcherExposed; +contract ExecutionDispatcherTest is Constants { + ExecutionDispatcherExposed dispatcherExposed; event ExecutorSet(address indexed executor); event ExecutorRemoved(address indexed executor); @@ -38,7 +38,7 @@ contract SwapExecutionDispatcherTest is Constants { function setUp() public { uint256 forkBlock = 20673900; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - dispatcherExposed = new SwapExecutionDispatcherExposed(); + dispatcherExposed = new ExecutionDispatcherExposed(); deal(WETH_ADDR, address(dispatcherExposed), 15 ether); deployDummyContract(); } @@ -47,34 +47,34 @@ contract SwapExecutionDispatcherTest is Constants { vm.expectEmit(); // Define the event we expect to be emitted at the next step emit ExecutorSet(DUMMY); - dispatcherExposed.exposedSetSwapExecutor(DUMMY); - assert(dispatcherExposed.swapExecutors(DUMMY) == true); + dispatcherExposed.exposedSetExecutor(DUMMY); + assert(dispatcherExposed.executors(DUMMY) == true); } function testRemoveExecutor() public { - dispatcherExposed.exposedSetSwapExecutor(DUMMY); + dispatcherExposed.exposedSetExecutor(DUMMY); vm.expectEmit(); // Define the event we expect to be emitted at the next step emit ExecutorRemoved(DUMMY); - dispatcherExposed.exposedRemoveSwapExecutor(DUMMY); - assert(dispatcherExposed.swapExecutors(DUMMY) == false); + dispatcherExposed.exposedRemoveExecutor(DUMMY); + assert(dispatcherExposed.executors(DUMMY) == false); } function testRemoveUnSetExecutor() public { - dispatcherExposed.exposedRemoveSwapExecutor(BOB); - assert(dispatcherExposed.swapExecutors(BOB) == false); + dispatcherExposed.exposedRemoveExecutor(BOB); + assert(dispatcherExposed.executors(BOB) == false); } function testSetExecutorNonContract() public { vm.expectRevert( abi.encodeWithSelector( - SwapExecutionDispatcher__NonContractExecutor.selector + ExecutionDispatcher__NonContractExecutor.selector ) ); - dispatcherExposed.exposedSetSwapExecutor(BOB); + dispatcherExposed.exposedSetExecutor(BOB); } - function testCallSwapExecutor() public { + function testCallExecutor() public { // Test case taken from existing transaction // 0x755d603962b30f416cf3eefae8d55204d6ffdf746465b2a94aca216faab63804 // For this test, we can use any executor and any calldata that we know works @@ -84,18 +84,18 @@ contract SwapExecutionDispatcherTest is Constants { // Thus, we chose a previously-deployed Hashflow executor for simplicity. To // change this test, we can find any of our transactions that succeeded, and // obtain the calldata passed to the executor via Tenderly. - dispatcherExposed.exposedSetSwapExecutor( + dispatcherExposed.exposedSetExecutor( address(0xe592557AB9F4A75D992283fD6066312FF013ba3d) ); bytes memory data = hex"e592557AB9F4A75D992283fD6066312FF013ba3dbd0625ab5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72fc8c39af7983bf329086de522229a7be5fc4e41cc51c72848c68a965f66fa7a88855f9f7784502a7f2606beffe61000613d6a25b5bfef4cd7652aa94777d4a46b39f2e206411280a12c9344b769ff1066c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000000000082ec8ad1b0000000000000000000000000000000000000000000000000000000066d7b65800000000000000000000000000000000000000000000000000000191ba9f843c125000064000640000d52de09955f0ffffffffffffff00225c389e595fe9000001fcc910754b349f821e4bb5d8444822a63920be943aba6f1b31ee14ef0fc6840b6d28d604e04a78834b668dba24a6c082ffb901e4fffa9600649e8d991af593c81c"; uint256 givenAmount = 15 ether; uint256 amount = - dispatcherExposed.exposedCallSwapExecutor(givenAmount, data); + dispatcherExposed.exposedCallExecutor(givenAmount, data); assert(amount == 35144641819); } - function testCallSwapExecutorNoSelector() public { + function testCallExecutorNoSelector() public { // Test case taken from existing transaction // 0x755d603962b30f416cf3eefae8d55204d6ffdf746465b2a94aca216faab63804 // No selector is passed, so the standard swap selector should be used @@ -107,33 +107,33 @@ contract SwapExecutionDispatcherTest is Constants { // Thus, we chose a previously-deployed Hashflow executor for simplicity. To // change this test, we can find any of our transactions that succeeded, and // obtain the calldata passed to the executor via Tenderly. - dispatcherExposed.exposedSetSwapExecutor( + dispatcherExposed.exposedSetExecutor( address(0xe592557AB9F4A75D992283fD6066312FF013ba3d) ); bytes memory data = hex"e592557AB9F4A75D992283fD6066312FF013ba3d000000005615dEB798BB3E4dFa0139dFa1b3D433Cc23b72fc8c39af7983bf329086de522229a7be5fc4e41cc51c72848c68a965f66fa7a88855f9f7784502a7f2606beffe61000613d6a25b5bfef4cd7652aa94777d4a46b39f2e206411280a12c9344b769ff1066c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000000000082ec8ad1b0000000000000000000000000000000000000000000000000000000066d7b65800000000000000000000000000000000000000000000000000000191ba9f843c125000064000640000d52de09955f0ffffffffffffff00225c389e595fe9000001fcc910754b349f821e4bb5d8444822a63920be943aba6f1b31ee14ef0fc6840b6d28d604e04a78834b668dba24a6c082ffb901e4fffa9600649e8d991af593c81c"; uint256 givenAmount = 15 ether; uint256 amount = - dispatcherExposed.exposedCallSwapExecutor(givenAmount, data); + dispatcherExposed.exposedCallExecutor(givenAmount, data); assert(amount == 35144641819); } - function testCallSwapExecutorCallFailed() public { - // Bad data is provided to an approved swap executor - causing the call to fail - dispatcherExposed.exposedSetSwapExecutor( + function testCallExecutorCallFailed() public { + // Bad data is provided to an approved executor - causing the call to fail + dispatcherExposed.exposedSetExecutor( address(0xe592557AB9F4A75D992283fD6066312FF013ba3d) ); bytes memory data = hex"e592557AB9F4A75D992283fD6066312FF013ba3dbd0625ab5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72fc8c39af7983bf329086de522229a7be5fc4e41cc51c72848c68a965f66fa7a88855f9f7784502a7f2606beffe61000613d6a25b5bfef4cd7652aa94777d4a46b39f2e206411280a12c9344b769ff1066c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000000000082ec8ad1b0000000000000000000000000000000000000000000000000000000066d7b65800000000000000000000000000000000000000000000000000000191ba9f843c125000064000640000d52de09955f0ffffffffffffff00225c389e595fe9000001fcc910754b349f821e4bb5d8444822a63920be943aba6f1b31ee14ef0fc6840b6d28d604e04a78834b668dba24a6c082ffb901e4fffa9600649e8d991af593"; vm.expectRevert(); - dispatcherExposed.exposedCallSwapExecutor(0, data); + dispatcherExposed.exposedCallExecutor(0, data); } - function testCallSwapExecutorUnapprovedExecutor() public { + function testCallExecutorUnapprovedExecutor() public { bytes memory data = hex"5d622C9053b8FFB1B3465495C8a42E603632bA70aabbccdd1111111111111111"; vm.expectRevert(); - dispatcherExposed.exposedCallSwapExecutor(0, data); + dispatcherExposed.exposedCallExecutor(0, data); } function testDecodeExecutorAndSelector() public { diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 809586f..d5019bc 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -21,19 +21,19 @@ contract TychoRouterTest is TychoRouterTestSetup { function testSetExecutorValidRole() public { vm.startPrank(executorSetter); - tychoRouter.setSwapExecutor(DUMMY); + tychoRouter.setExecutor(DUMMY); vm.stopPrank(); - assert(tychoRouter.swapExecutors(DUMMY) == true); + assert(tychoRouter.executors(DUMMY) == true); } function testRemoveExecutorMissingSetterRole() public { vm.expectRevert(); - tychoRouter.removeSwapExecutor(BOB); + tychoRouter.removeExecutor(BOB); } function testSetExecutorMissingSetterRole() public { vm.expectRevert(); - tychoRouter.setSwapExecutor(DUMMY); + tychoRouter.setExecutor(DUMMY); } function testSetValidVerifier() public {