Merge pull request #64 from propeller-heads/router/dc/ENG-4223-uniswap-v4-callback
feat: Uniswap v4 callback
This commit is contained in:
@@ -5,7 +5,17 @@ libs = ['lib']
|
|||||||
auto_detect_sol = true
|
auto_detect_sol = true
|
||||||
evm_version = 'cancun'
|
evm_version = 'cancun'
|
||||||
optimizer = true
|
optimizer = true
|
||||||
optimizer_runs = 1000
|
optimizer_runs = 200
|
||||||
|
via_ir = true
|
||||||
|
|
||||||
|
[profile.production]
|
||||||
|
src = 'src'
|
||||||
|
out = 'out'
|
||||||
|
libs = ['lib']
|
||||||
|
auto_detect_sol = true
|
||||||
|
evm_version = 'cancun'
|
||||||
|
optimizer = true
|
||||||
|
optimizer_runs = 44444444
|
||||||
via_ir = true
|
via_ir = true
|
||||||
|
|
||||||
[rpc_endpoints]
|
[rpc_endpoints]
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import "@uniswap/v3-updated/CallbackValidationV2.sol";
|
|||||||
import "./ExecutionDispatcher.sol";
|
import "./ExecutionDispatcher.sol";
|
||||||
import "./CallbackVerificationDispatcher.sol";
|
import "./CallbackVerificationDispatcher.sol";
|
||||||
import {LibSwap} from "../lib/LibSwap.sol";
|
import {LibSwap} from "../lib/LibSwap.sol";
|
||||||
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
||||||
|
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||||
|
|
||||||
error TychoRouter__WithdrawalFailed();
|
error TychoRouter__WithdrawalFailed();
|
||||||
error TychoRouter__AddressZero();
|
error TychoRouter__AddressZero();
|
||||||
@@ -28,7 +30,8 @@ contract TychoRouter is
|
|||||||
ExecutionDispatcher,
|
ExecutionDispatcher,
|
||||||
CallbackVerificationDispatcher,
|
CallbackVerificationDispatcher,
|
||||||
Pausable,
|
Pausable,
|
||||||
ReentrancyGuard
|
ReentrancyGuard,
|
||||||
|
SafeCallback
|
||||||
{
|
{
|
||||||
IAllowanceTransfer public immutable permit2;
|
IAllowanceTransfer public immutable permit2;
|
||||||
IWETH private immutable _weth;
|
IWETH private immutable _weth;
|
||||||
@@ -65,7 +68,12 @@ contract TychoRouter is
|
|||||||
|
|
||||||
address private immutable _usv3Factory;
|
address private immutable _usv3Factory;
|
||||||
|
|
||||||
constructor(address _permit2, address weth, address usv3Factory) {
|
constructor(
|
||||||
|
IPoolManager _poolManager,
|
||||||
|
address _permit2,
|
||||||
|
address weth,
|
||||||
|
address usv3Factory
|
||||||
|
) SafeCallback(_poolManager) {
|
||||||
if (
|
if (
|
||||||
_permit2 == address(0) || weth == address(0)
|
_permit2 == address(0) || weth == address(0)
|
||||||
|| usv3Factory == address(0)
|
|| usv3Factory == address(0)
|
||||||
@@ -434,4 +442,26 @@ contract TychoRouter is
|
|||||||
|
|
||||||
return (amountIn, tokenIn);
|
return (amountIn, tokenIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _unlockCallback(bytes calldata data)
|
||||||
|
internal
|
||||||
|
override
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
require(data.length >= 20, "Invalid data length");
|
||||||
|
bytes4 selector = bytes4(data[data.length - 24:data.length - 20]);
|
||||||
|
address executor = address(uint160(bytes20(data[data.length - 20:])));
|
||||||
|
bytes memory protocolData = data[:data.length - 24];
|
||||||
|
|
||||||
|
if (!executors[executor]) {
|
||||||
|
revert ExecutionDispatcher__UnapprovedExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// slither-disable-next-line controlled-delegatecall,low-level-calls
|
||||||
|
(bool success,) = executor.delegatecall(
|
||||||
|
abi.encodeWithSelector(selector, protocolData)
|
||||||
|
);
|
||||||
|
require(success, "delegatecall to uniswap v4 callback failed");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity ^0.8.26;
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
import "@src/executors/UniswapV4Executor.sol";
|
||||||
import {TychoRouter} from "@src/TychoRouter.sol";
|
import {TychoRouter} from "@src/TychoRouter.sol";
|
||||||
import "./TychoRouterTestSetup.sol";
|
import "./TychoRouterTestSetup.sol";
|
||||||
|
import "./executors/UniswapV4Utils.sol";
|
||||||
|
|
||||||
contract TychoRouterTest is TychoRouterTestSetup {
|
contract TychoRouterTest is TychoRouterTestSetup {
|
||||||
bytes32 public constant EXECUTOR_SETTER_ROLE =
|
bytes32 public constant EXECUTOR_SETTER_ROLE =
|
||||||
@@ -254,7 +256,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps));
|
tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps));
|
||||||
|
|
||||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(tychoRouterAddr);
|
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(tychoRouterAddr);
|
||||||
assertEq(daiBalance, 2630432278145144658455);
|
assertEq(daiBalance, 2659881924818443699787);
|
||||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +293,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
tychoRouter.exposedSwap(amountIn, 3, pleEncode(swaps));
|
tychoRouter.exposedSwap(amountIn, 3, pleEncode(swaps));
|
||||||
|
|
||||||
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr);
|
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr);
|
||||||
assertEq(usdcBalance, 2610580090);
|
assertEq(usdcBalance, 2644659787);
|
||||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +354,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
tychoRouter.exposedSwap(amountIn, 4, pleEncode(swaps));
|
tychoRouter.exposedSwap(amountIn, 4, pleEncode(swaps));
|
||||||
|
|
||||||
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr);
|
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr);
|
||||||
assertEq(usdcBalance, 2581503157);
|
assertEq(usdcBalance, 2615491639);
|
||||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +402,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
pleEncode(swaps)
|
pleEncode(swaps)
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 expectedAmount = 2630432278145144658455;
|
uint256 expectedAmount = 2659881924818443699787;
|
||||||
assertEq(amountOut, expectedAmount);
|
assertEq(amountOut, expectedAmount);
|
||||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||||
assertEq(daiBalance, expectedAmount);
|
assertEq(daiBalance, expectedAmount);
|
||||||
@@ -442,7 +444,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
vm.expectRevert(
|
vm.expectRevert(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
TychoRouter__NegativeSlippage.selector,
|
TychoRouter__NegativeSlippage.selector,
|
||||||
2630432278145144658455, // actual amountOut
|
2659881924818443699787, // actual amountOut
|
||||||
minAmountOut
|
minAmountOut
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -511,11 +513,11 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
pleEncode(swaps)
|
pleEncode(swaps)
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 expectedAmount = 2604127955363693211871;
|
uint256 expectedAmount = 2633283105570259262790;
|
||||||
assertEq(amountOut, expectedAmount);
|
assertEq(amountOut, expectedAmount);
|
||||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||||
assertEq(daiBalance, expectedAmount);
|
assertEq(daiBalance, expectedAmount);
|
||||||
assertEq(IERC20(DAI_ADDR).balanceOf(FEE_RECEIVER), 26304322781451446584);
|
assertEq(IERC20(DAI_ADDR).balanceOf(FEE_RECEIVER), 26598819248184436997);
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
@@ -567,7 +569,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
"",
|
"",
|
||||||
pleEncode(swaps)
|
pleEncode(swaps)
|
||||||
);
|
);
|
||||||
uint256 expectedAmount = 2630432278145144658455;
|
uint256 expectedAmount = 2659881924818443699787;
|
||||||
assertEq(amountOut, expectedAmount);
|
assertEq(amountOut, expectedAmount);
|
||||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||||
assertEq(daiBalance, expectedAmount);
|
assertEq(daiBalance, expectedAmount);
|
||||||
@@ -617,7 +619,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
pleEncode(swaps)
|
pleEncode(swaps)
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 expectedAmount = 1132829934891544187; // 1.13 ETH
|
uint256 expectedAmount = 1120007305574805922; // 1.12 ETH
|
||||||
assertEq(amountOut, expectedAmount);
|
assertEq(amountOut, expectedAmount);
|
||||||
assertEq(ALICE.balance, expectedAmount);
|
assertEq(ALICE.balance, expectedAmount);
|
||||||
|
|
||||||
@@ -695,7 +697,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
||||||
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
||||||
(bool success,) = tychoRouterAddr.call(
|
(bool success,) = tychoRouterAddr.call(
|
||||||
hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067c43ba900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000679cb5b10000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415bfd02ffd61c11192d1b54d76e0af125afbb32568aad37ec35f918bd5fb304cd314954213ed77c0d071301ddc45243ad57e86fe18f2905b682acc4f1a43ad8dc1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000"
|
hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067d481bb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfbc3000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f2740fde9662d8bc1f8fe8e8fc29447c1832d625f06f4a56ee5103ad555c12323af5d50eb840f73d17873383ae3b7573956d5df7b2bf76bddba768c2837894a51b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000"
|
||||||
);
|
);
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
@@ -717,14 +719,14 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
|
|
||||||
// Approve permit2
|
// Approve permit2
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max);
|
// IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max);
|
||||||
// Encoded solution generated using
|
// Encoded solution generated using
|
||||||
// `test_split_swap_strategy_encoder_simple_route_wrap`
|
// `test_split_swap_strategy_encoder_simple_route_wrap`
|
||||||
// but manually replacing the executor address
|
// but manually replacing the executor address
|
||||||
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
||||||
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
||||||
(bool success,) = tychoRouterAddr.call{value: 1 ether}(
|
(bool success,) = tychoRouterAddr.call{value: 1 ether}(
|
||||||
hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067c9179300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067a1919b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041cea77a63613f6a02aaee522c91f9569b8377a7f0200d141fafa3e1c42011e1c668555b49a1e7dd960091d0e33764ad24db6550bc761e228864495b478f1a23721b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000"
|
hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067d4806b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfa73000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c36406a750c499ac7f79f7666650f0d4f20fc27bb49ab68121c0be6554cb5cab6caf90dc3aab2e21083a8fa46976521a1e9df41ce74be59abf03e0d3691541e91c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000"
|
||||||
);
|
);
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
@@ -753,7 +755,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
||||||
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
||||||
(bool success,) = tychoRouterAddr.call(
|
(bool success,) = tychoRouterAddr.call(
|
||||||
hex"4860f9ed0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067c9185300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067a1925b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041fd1c3dfce5afcb47988cc68165d5de64186cedbeb7eee6fc9cd087bceeaacdfe1ab799d60e0c628f24edfd9819b94ed60846dd23240c481f1d6e5470a7815a891c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950100000000"
|
hex"4860f9ed0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067d4809800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfaa000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004146411c70ec7fee0d5d260803cb220f5365792426c5d94f7a0a4d37abb05205752c5418b1fadd059570a71f0911814e546728e1f21876f2a1c6d38d34bd235fd61c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950100000000"
|
||||||
);
|
);
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
@@ -784,7 +786,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
||||||
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
||||||
(bool success,) = tychoRouterAddr.call(
|
(bool success,) = tychoRouterAddr.call(
|
||||||
hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067c48ea700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000679d08af00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004197c2ff7801fa573e4e8e4af1df41499045485c2b48d090833dc85be38e002c1a1e7ef354285d79c2dcb40c4837e5156069de9aaf42365aef54fdc4cca2c76ccb1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000170005a00028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005a02030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005a01030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000"
|
hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067d4810d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfb15000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ecaab75f0791c9683b001ea2f0e01a0a6aaf03e6e49c83e9c8a8e588a38e3be9230d962926628ffbf6a5370cda559ff0e7876a63ed38eebe33dbef5b5e2e46ef1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000170005a00028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005a02030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005a01030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000"
|
||||||
);
|
);
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
@@ -850,4 +852,36 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testSwapSingleUSV4Callback() public {
|
||||||
|
uint256 amountIn = 100 ether;
|
||||||
|
deal(USDE_ADDR, tychoRouterAddr, amountIn);
|
||||||
|
|
||||||
|
bytes memory protocolData = UniswapV4Utils.encodeExactInputSingle(
|
||||||
|
USDE_ADDR, USDT_ADDR, 100, true, 1, uint128(amountIn)
|
||||||
|
);
|
||||||
|
|
||||||
|
// add executor and selector for callback
|
||||||
|
bytes memory protocolDataWithCallBack = abi.encodePacked(
|
||||||
|
protocolData,
|
||||||
|
SafeCallback.unlockCallback.selector,
|
||||||
|
address(usv4Executor)
|
||||||
|
);
|
||||||
|
|
||||||
|
bytes memory swap = encodeSwap(
|
||||||
|
uint8(0),
|
||||||
|
uint8(1),
|
||||||
|
uint24(0),
|
||||||
|
address(usv4Executor),
|
||||||
|
bytes4(0),
|
||||||
|
protocolDataWithCallBack
|
||||||
|
);
|
||||||
|
|
||||||
|
bytes[] memory swaps = new bytes[](1);
|
||||||
|
swaps[0] = swap;
|
||||||
|
|
||||||
|
tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps));
|
||||||
|
|
||||||
|
assertTrue(IERC20(USDT_ADDR).balanceOf(tychoRouterAddr) == 99943852);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,22 @@
|
|||||||
pragma solidity ^0.8.26;
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
import "../src/executors/UniswapV2Executor.sol";
|
import "../src/executors/UniswapV2Executor.sol";
|
||||||
|
import "../src/executors/UniswapV3Executor.sol";
|
||||||
|
import "../src/executors/UniswapV4Executor.sol";
|
||||||
import "./Constants.sol";
|
import "./Constants.sol";
|
||||||
import "./mock/MockERC20.sol";
|
import "./mock/MockERC20.sol";
|
||||||
import "@src/TychoRouter.sol";
|
import "@src/TychoRouter.sol";
|
||||||
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
||||||
import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol";
|
import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol";
|
||||||
import "../src/executors/UniswapV3Executor.sol";
|
import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol";
|
||||||
|
|
||||||
contract TychoRouterExposed is TychoRouter {
|
contract TychoRouterExposed is TychoRouter {
|
||||||
constructor(address _permit2, address weth, address usv3Factory)
|
constructor(
|
||||||
TychoRouter(_permit2, weth, usv3Factory)
|
IPoolManager _poolManager,
|
||||||
{}
|
address _permit2,
|
||||||
|
address weth,
|
||||||
|
address usv3Factory
|
||||||
|
) TychoRouter(_poolManager, _permit2, weth, usv3Factory) {}
|
||||||
|
|
||||||
function wrapETH(uint256 amount) external payable {
|
function wrapETH(uint256 amount) external payable {
|
||||||
return _wrapETH(amount);
|
return _wrapETH(amount);
|
||||||
@@ -36,16 +42,20 @@ contract TychoRouterTestSetup is Test, Constants {
|
|||||||
address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3);
|
address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3);
|
||||||
UniswapV2Executor public usv2Executor;
|
UniswapV2Executor public usv2Executor;
|
||||||
UniswapV3Executor public usv3Executor;
|
UniswapV3Executor public usv3Executor;
|
||||||
|
UniswapV4Executor public usv4Executor;
|
||||||
MockERC20[] tokens;
|
MockERC20[] tokens;
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
uint256 forkBlock = 21000000;
|
uint256 forkBlock = 21817316;
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
|
|
||||||
vm.startPrank(ADMIN);
|
vm.startPrank(ADMIN);
|
||||||
address factoryV3 = address(0x1F98431c8aD98523631AE4a59f267346ea31F984);
|
address factoryV3 = address(0x1F98431c8aD98523631AE4a59f267346ea31F984);
|
||||||
tychoRouter =
|
address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
||||||
new TychoRouterExposed(permit2Address, WETH_ADDR, factoryV3);
|
IPoolManager poolManager = IPoolManager(poolManagerAddress);
|
||||||
|
tychoRouter = new TychoRouterExposed(
|
||||||
|
poolManager, permit2Address, WETH_ADDR, factoryV3
|
||||||
|
);
|
||||||
tychoRouterAddr = address(tychoRouter);
|
tychoRouterAddr = address(tychoRouter);
|
||||||
tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER);
|
tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER);
|
||||||
tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER);
|
tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER);
|
||||||
@@ -59,10 +69,12 @@ contract TychoRouterTestSetup is Test, Constants {
|
|||||||
|
|
||||||
usv2Executor = new UniswapV2Executor();
|
usv2Executor = new UniswapV2Executor();
|
||||||
usv3Executor = new UniswapV3Executor();
|
usv3Executor = new UniswapV3Executor();
|
||||||
|
usv4Executor = new UniswapV4Executor(poolManager);
|
||||||
vm.startPrank(EXECUTOR_SETTER);
|
vm.startPrank(EXECUTOR_SETTER);
|
||||||
address[] memory executors = new address[](2);
|
address[] memory executors = new address[](3);
|
||||||
executors[0] = address(usv2Executor);
|
executors[0] = address(usv2Executor);
|
||||||
executors[1] = address(usv3Executor);
|
executors[1] = address(usv3Executor);
|
||||||
|
executors[2] = address(usv4Executor);
|
||||||
tychoRouter.setExecutors(executors);
|
tychoRouter.setExecutors(executors);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ contract BalancerV2ExecutorTest is
|
|||||||
assertEq(balanceAfter - balanceBefore, amountOut);
|
assertEq(balanceAfter - balanceBefore, amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDecodeIntegration() public {
|
function testDecodeIntegration() public view {
|
||||||
// Generated by the SwapEncoder - test_encode_balancer_v2
|
// Generated by the SwapEncoder - test_encode_balancer_v2
|
||||||
bytes memory protocolData =
|
bytes memory protocolData =
|
||||||
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01";
|
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01";
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ contract UniswapV2ExecutorTest is UniswapV2ExecutorExposed, Test, Constants {
|
|||||||
assertGe(finalBalance, amountOut);
|
assertGe(finalBalance, amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDecodeIntegration() public {
|
function testDecodeIntegration() public view {
|
||||||
// Generated by the ExecutorStrategyEncoder - test_executor_strategy_encode
|
// Generated by the ExecutorStrategyEncoder - test_executor_strategy_encode
|
||||||
bytes memory protocolData =
|
bytes memory protocolData =
|
||||||
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f5640000000000000000000000000000000000000000100";
|
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f5640000000000000000000000000000000000000000100";
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity ^0.8.26;
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
import "./UniswapV4Utils.sol";
|
||||||
import "@src/executors/UniswapV4Executor.sol";
|
import "@src/executors/UniswapV4Executor.sol";
|
||||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
|
||||||
import {Constants} from "../Constants.sol";
|
import {Constants} from "../Constants.sol";
|
||||||
|
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||||
import {console} from "forge-std/console.sol";
|
import {console} from "forge-std/console.sol";
|
||||||
|
|
||||||
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
||||||
@@ -42,7 +43,7 @@ contract UniswapV4ExecutorTest is Test, Constants {
|
|||||||
uint24 expectedPoolFee = 500;
|
uint24 expectedPoolFee = 500;
|
||||||
uint128 expectedAmount = 100;
|
uint128 expectedAmount = 100;
|
||||||
|
|
||||||
bytes memory data = _encodeExactInputSingle(
|
bytes memory data = UniswapV4Utils.encodeExactInputSingle(
|
||||||
USDE_ADDR, USDT_ADDR, expectedPoolFee, false, 1, expectedAmount
|
USDE_ADDR, USDT_ADDR, expectedPoolFee, false, 1, expectedAmount
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ contract UniswapV4ExecutorTest is Test, Constants {
|
|||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
bytes memory data = _encodeExactInputSingle(
|
bytes memory data = UniswapV4Utils.encodeExactInputSingle(
|
||||||
USDE_ADDR, USDT_ADDR, 100, true, 1, uint128(amountIn)
|
USDE_ADDR, USDT_ADDR, 100, true, 1, uint128(amountIn)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -74,44 +75,4 @@ contract UniswapV4ExecutorTest is Test, Constants {
|
|||||||
);
|
);
|
||||||
assertTrue(USDT.balanceOf(address(uniswapV4Exposed)) == amountOut);
|
assertTrue(USDT.balanceOf(address(uniswapV4Exposed)) == amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _encodeExactInputSingle(
|
|
||||||
address tokenIn,
|
|
||||||
address tokenOut,
|
|
||||||
uint24 fee,
|
|
||||||
bool zeroForOne,
|
|
||||||
uint24 tickSpacing,
|
|
||||||
uint128 amountIn
|
|
||||||
) internal pure returns (bytes memory) {
|
|
||||||
PoolKey memory key = PoolKey({
|
|
||||||
currency0: Currency.wrap(zeroForOne ? tokenIn : tokenOut),
|
|
||||||
currency1: Currency.wrap(zeroForOne ? tokenOut : tokenIn),
|
|
||||||
fee: fee,
|
|
||||||
tickSpacing: int24(tickSpacing),
|
|
||||||
hooks: IHooks(address(0))
|
|
||||||
});
|
|
||||||
|
|
||||||
bytes memory actions = abi.encodePacked(
|
|
||||||
uint8(Actions.SWAP_EXACT_IN_SINGLE),
|
|
||||||
uint8(Actions.SETTLE_ALL),
|
|
||||||
uint8(Actions.TAKE_ALL)
|
|
||||||
);
|
|
||||||
|
|
||||||
bytes[] memory params = new bytes[](3);
|
|
||||||
|
|
||||||
params[0] = abi.encode(
|
|
||||||
IV4Router.ExactInputSingleParams({
|
|
||||||
poolKey: key,
|
|
||||||
zeroForOne: zeroForOne,
|
|
||||||
amountIn: amountIn,
|
|
||||||
amountOutMinimum: 0,
|
|
||||||
hookData: bytes("")
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
params[1] = abi.encode(key.currency0, amountIn);
|
|
||||||
params[2] = abi.encode(key.currency1, 0);
|
|
||||||
|
|
||||||
return abi.encode(actions, params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
46
foundry/test/executors/UniswapV4Utils.sol
Normal file
46
foundry/test/executors/UniswapV4Utils.sol
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
import "@src/executors/UniswapV4Executor.sol";
|
||||||
|
|
||||||
|
library UniswapV4Utils {
|
||||||
|
function encodeExactInputSingle(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
uint24 fee,
|
||||||
|
bool zeroForOne,
|
||||||
|
uint24 tickSpacing,
|
||||||
|
uint128 amountIn
|
||||||
|
) public pure returns (bytes memory) {
|
||||||
|
PoolKey memory key = PoolKey({
|
||||||
|
currency0: Currency.wrap(zeroForOne ? tokenIn : tokenOut),
|
||||||
|
currency1: Currency.wrap(zeroForOne ? tokenOut : tokenIn),
|
||||||
|
fee: fee,
|
||||||
|
tickSpacing: int24(tickSpacing),
|
||||||
|
hooks: IHooks(address(0))
|
||||||
|
});
|
||||||
|
|
||||||
|
bytes memory actions = abi.encodePacked(
|
||||||
|
uint8(Actions.SWAP_EXACT_IN_SINGLE),
|
||||||
|
uint8(Actions.SETTLE_ALL),
|
||||||
|
uint8(Actions.TAKE_ALL)
|
||||||
|
);
|
||||||
|
|
||||||
|
bytes[] memory params = new bytes[](3);
|
||||||
|
|
||||||
|
params[0] = abi.encode(
|
||||||
|
IV4Router.ExactInputSingleParams({
|
||||||
|
poolKey: key,
|
||||||
|
zeroForOne: zeroForOne,
|
||||||
|
amountIn: amountIn,
|
||||||
|
amountOutMinimum: 0,
|
||||||
|
hookData: bytes("")
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
params[1] = abi.encode(key.currency0, amountIn);
|
||||||
|
params[2] = abi.encode(key.currency1, 0);
|
||||||
|
|
||||||
|
return abi.encode(actions, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user