feat: Add TokenTransfer class to Curve

- This needed to be in Curve so that the executor can also take care of transfers from the user into the tycho router, to avoid having transfer actions mixed between the router and executors
This commit is contained in:
TAMARA LIPOWSKI
2025-04-14 16:52:52 -04:00
committed by Diana Carvalho
parent dbc9042a2f
commit 462be5463b
7 changed files with 61 additions and 37 deletions

View File

@@ -3,6 +3,7 @@ pragma solidity ^0.8.26;
import "@interfaces/IExecutor.sol"; import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokenTransfer.sol";
error CurveExecutor__AddressZero(); error CurveExecutor__AddressZero();
@@ -32,12 +33,14 @@ interface CryptoPoolETH {
// slither-disable-end naming-convention // slither-disable-end naming-convention
} }
contract CurveExecutor is IExecutor { contract CurveExecutor is IExecutor, TokenTransfer {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
address public immutable nativeToken; address public immutable nativeToken;
constructor(address _nativeToken) { constructor(address _nativeToken, address _permit2)
TokenTransfer(_permit2)
{
if (_nativeToken == address(0)) { if (_nativeToken == address(0)) {
revert CurveExecutor__AddressZero(); revert CurveExecutor__AddressZero();
} }
@@ -57,9 +60,12 @@ contract CurveExecutor is IExecutor {
uint8 poolType, uint8 poolType,
int128 i, int128 i,
int128 j, int128 j,
bool tokenApprovalNeeded bool tokenApprovalNeeded,
TransferType transferType
) = _decodeData(data); ) = _decodeData(data);
_transfer(tokenIn, msg.sender, pool, amountIn, transferType);
if (tokenApprovalNeeded && tokenIn != nativeToken) { if (tokenApprovalNeeded && tokenIn != nativeToken) {
// slither-disable-next-line unused-return // slither-disable-next-line unused-return
IERC20(tokenIn).approve(address(pool), type(uint256).max); IERC20(tokenIn).approve(address(pool), type(uint256).max);
@@ -105,7 +111,8 @@ contract CurveExecutor is IExecutor {
uint8 poolType, uint8 poolType,
int128 i, int128 i,
int128 j, int128 j,
bool tokenApprovalNeeded bool tokenApprovalNeeded,
TransferType transferType
) )
{ {
tokenIn = address(bytes20(data[0:20])); tokenIn = address(bytes20(data[0:20]));
@@ -115,6 +122,7 @@ contract CurveExecutor is IExecutor {
i = int128(uint128(uint8(data[61]))); i = int128(uint128(uint8(data[61])));
j = int128(uint128(uint8(data[62]))); j = int128(uint128(uint8(data[62])));
tokenApprovalNeeded = data[63] != 0; tokenApprovalNeeded = data[63] != 0;
transferType = TransferType(uint8(data[64]));
} }
receive() external payable { receive() external payable {

View File

@@ -257,6 +257,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de780000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000413c8b048dc7b7614106a5aa1fa13e48c02a6a9714dfa07d2c424f68b81a5f828c39ace62f2dd57d7bfad10910ae44f77d68aec5c079fce456028b1bd7f72053151c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000" hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de780000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000413c8b048dc7b7614106a5aa1fa13e48c02a6a9714dfa07d2c424f68b81a5f828c39ace62f2dd57d7bfad10910ae44f77d68aec5c079fce456028b1bd7f72053151c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000"
); );
assertTrue(success, "Call Failed");
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294);
vm.stopPrank(); vm.stopPrank();
@@ -273,6 +274,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821659d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfa5000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041dd84c5cdc51719e377598eccd8eac0aae036e7e0745a7c65b5d44cc817071a7460ccc73934363f33cc7af71dc07545aeff1d92f8c2f0b2973e1fc37e7b2de3551c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80102005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821659d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfa5000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041dd84c5cdc51719e377598eccd8eac0aae036e7e0745a7c65b5d44cc817071a7460ccc73934363f33cc7af71dc07545aeff1d92f8c2f0b2973e1fc37e7b2de3551c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80102005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000"
); );
assertTrue(success, "Call Failed");
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171);
vm.stopPrank(); vm.stopPrank();
@@ -289,26 +291,27 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000682165ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfb400000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004107f2b0f9c2e4e308ab43b288d69de30d84b10c8075e4dd9a2cf66594f97a52fb34de2534b89bf1887da74c92fd03464f45baff700dd32e213e3add1a3f351e891b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950102006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000" hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000682165ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfb400000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004107f2b0f9c2e4e308ab43b288d69de30d84b10c8075e4dd9a2cf66594f97a52fb34de2534b89bf1887da74c92fd03464f45baff700dd32e213e3add1a3f351e891b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950102006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000"
); );
assertTrue(success, "Call Failed");
assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908);
vm.stopPrank(); vm.stopPrank();
} }
// TODO uncomment when Curve TokenTransfer is implemented (next commits) function testSplitCurveIntegration() public {
// function testSplitCurveIntegration() public { deal(UWU_ADDR, ALICE, 1 ether);
// deal(UWU_ADDR, ALICE, 1 ether);
// vm.startPrank(ALICE);
// vm.startPrank(ALICE); IERC20(UWU_ADDR).approve(tychoRouterAddr, type(uint256).max);
// IERC20(UWU_ADDR).approve(tychoRouterAddr, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_curve`
// // Encoded solution generated using `test_split_encoding_strategy_curve` (bool success,) = tychoRouterAddr.call(
// (bool success,) = tychoRouterAddr.call( hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005c005a00010000001d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e71020100010300000000"
// hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005b005900010000001d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e71020100010000000000" );
// );
// assertTrue(success, "Call Failed");
// assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 4691958787921); assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 4691958787921);
//
// vm.stopPrank(); vm.stopPrank();
// } }
function testSplitCurveIntegrationStETH() public { function testSplitCurveIntegrationStETH() public {
deal(ALICE, 1 ether); deal(ALICE, 1 ether);
@@ -316,9 +319,10 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup {
vm.startPrank(ALICE); vm.startPrank(ALICE);
// Encoded solution generated using `test_split_encoding_strategy_curve_st_eth` // Encoded solution generated using `test_split_encoding_strategy_curve_st_eth`
(bool success,) = tychoRouterAddr.call{value: 1 ether}( (bool success,) = tychoRouterAddr.call{value: 1 ether}(
hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe840000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005b005900010000001d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f67022010001000000000000" hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe840000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005c005a00010000001d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f67022010001000500000000"
); );
assertTrue(success, "Call Failed");
assertEq(IERC20(STETH_ADDR).balanceOf(ALICE), 1000754689941529590); assertEq(IERC20(STETH_ADDR).balanceOf(ALICE), 1000754689941529590);
vm.stopPrank(); vm.stopPrank();

View File

@@ -108,7 +108,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper {
); );
balancerv2Executor = new BalancerV2Executor(); balancerv2Executor = new BalancerV2Executor();
ekuboExecutor = new EkuboExecutor(ekuboCore); ekuboExecutor = new EkuboExecutor(ekuboCore);
curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE); curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS);
address[] memory executors = new address[](7); address[] memory executors = new address[](7);
executors[0] = address(usv2Executor); executors[0] = address(usv2Executor);

View File

@@ -22,7 +22,9 @@ interface MetaRegistry {
} }
contract CurveExecutorExposed is CurveExecutor { contract CurveExecutorExposed is CurveExecutor {
constructor(address _nativeToken) CurveExecutor(_nativeToken) {} constructor(address _nativeToken, address _permit2)
CurveExecutor(_nativeToken, _permit2)
{}
function decodeData(bytes calldata data) function decodeData(bytes calldata data)
external external
@@ -34,7 +36,8 @@ contract CurveExecutorExposed is CurveExecutor {
uint8 poolType, uint8 poolType,
int128 i, int128 i,
int128 j, int128 j,
bool tokenApprovalNeeded bool tokenApprovalNeeded,
TokenTransfer.TransferType transferType
) )
{ {
return _decodeData(data); return _decodeData(data);
@@ -50,7 +53,8 @@ contract CurveExecutorTest is Test, Constants {
function setUp() public { function setUp() public {
uint256 forkBlock = 22031795; uint256 forkBlock = 22031795;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
curveExecutorExposed = new CurveExecutorExposed(ETH_ADDR_FOR_CURVE); curveExecutorExposed =
new CurveExecutorExposed(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS);
metaRegistry = MetaRegistry(CURVE_META_REGISTRY); metaRegistry = MetaRegistry(CURVE_META_REGISTRY);
} }
@@ -62,7 +66,8 @@ contract CurveExecutorTest is Test, Constants {
uint8(3), uint8(3),
uint8(2), uint8(2),
uint8(0), uint8(0),
true true,
TokenTransfer.TransferType.NONE
); );
( (
@@ -72,7 +77,8 @@ contract CurveExecutorTest is Test, Constants {
uint8 poolType, uint8 poolType,
int128 i, int128 i,
int128 j, int128 j,
bool tokenApprovalNeeded bool tokenApprovalNeeded,
TokenTransfer.TransferType transferType
) = curveExecutorExposed.decodeData(data); ) = curveExecutorExposed.decodeData(data);
assertEq(tokenIn, WETH_ADDR); assertEq(tokenIn, WETH_ADDR);
@@ -82,6 +88,7 @@ contract CurveExecutorTest is Test, Constants {
assertEq(i, 2); assertEq(i, 2);
assertEq(j, 0); assertEq(j, 0);
assertEq(tokenApprovalNeeded, true); assertEq(tokenApprovalNeeded, true);
assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE));
} }
function testTriPool() public { function testTriPool() public {
@@ -311,7 +318,8 @@ contract CurveExecutorTest is Test, Constants {
poolType, poolType,
uint8(uint256(uint128(i))), uint8(uint256(uint128(i))),
uint8(uint256(uint128(j))), uint8(uint256(uint128(j))),
true true,
TokenTransfer.TransferType.NONE
); );
} }

View File

@@ -31,7 +31,7 @@ pub static IN_TRANSFER_OPTIMIZABLE_PROTOCOLS: LazyLock<HashSet<&'static str>> =
pub static PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER: LazyLock<HashSet<&'static str>> = pub static PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER: LazyLock<HashSet<&'static str>> =
LazyLock::new(|| { LazyLock::new(|| {
let mut set = HashSet::new(); let mut set = HashSet::new();
set.insert("curve"); set.insert("vm:curve");
set.insert("balancer_v2"); set.insert("balancer_v2");
// TODO remove uniswap_v4 when we add callback support for transfer optimizations // TODO remove uniswap_v4 when we add callback support for transfer optimizations
set.insert("uniswap_v4"); set.insert("uniswap_v4");

View File

@@ -27,11 +27,11 @@ pub trait TransferOptimization {
let is_first_swap = let is_first_swap =
(swap.token_in == given_token) || ((swap.token_in == wrapped_token) && wrap); (swap.token_in == given_token) || ((swap.token_in == wrapped_token) && wrap);
if is_first_swap && send_funds_to_pool {
if swap.token_in == native_token { if swap.token_in == native_token {
// Funds are already in router. Protocol takes care of native transfer. // Funds are already in router. All protocols currently take care of native transfers.
TransferType::None TransferType::None
} else if permit2 { } else if is_first_swap && send_funds_to_pool {
if permit2 {
// Transfer from swapper to pool using permit2. // Transfer from swapper to pool using permit2.
TransferType::Permit2Transfer TransferType::Permit2Transfer
} else { } else {
@@ -39,10 +39,7 @@ pub trait TransferOptimization {
TransferType::TransferFrom TransferType::TransferFrom
} }
} else if is_first_swap && funds_expected_in_router { } else if is_first_swap && funds_expected_in_router {
if swap.token_in == native_token { if permit2 {
// Funds already in router. Do nothing.
TransferType::None
} else if permit2 {
// Transfer from swapper to router using permit2. // Transfer from swapper to router using permit2.
TransferType::Permit2TransferToRouter TransferType::Permit2TransferToRouter
} else { } else {

View File

@@ -545,6 +545,7 @@ impl SwapEncoder for CurveSwapEncoder {
i.to_be_bytes::<1>(), i.to_be_bytes::<1>(),
j.to_be_bytes::<1>(), j.to_be_bytes::<1>(),
approval_needed, approval_needed,
(encoding_context.transfer_type as u8).to_be_bytes(),
); );
Ok(args.abi_encode_packed()) Ok(args.abi_encode_packed())
@@ -1263,6 +1264,8 @@ mod tests {
"01", "01",
// approval needed // approval needed
"01", "01",
// transfer type
"05",
)) ))
); );
} }
@@ -1329,6 +1332,8 @@ mod tests {
"00", "00",
// approval needed // approval needed
"01", "01",
// transfer type
"05",
)) ))
); );
} }
@@ -1405,6 +1410,8 @@ mod tests {
"01", "01",
// approval needed // approval needed
"01", "01",
// transfer type
"05",
)) ))
); );
} }