feat(univ4): Refactor input and handle single swap case
Construct the uniswap v4 specific objects inside the executor The swap test in the executor alone doesn't work anymore because of the callback data prepended to the rest of he calldata --- don't change below this line --- ENG-4222 Took 4 hours 0 minutes Took 40 seconds
This commit is contained in:
@@ -449,9 +449,9 @@ contract TychoRouter is
|
|||||||
returns (bytes memory)
|
returns (bytes memory)
|
||||||
{
|
{
|
||||||
require(data.length >= 20, "Invalid data length");
|
require(data.length >= 20, "Invalid data length");
|
||||||
bytes4 selector = bytes4(data[data.length - 24:data.length - 20]);
|
address executor = address(uint160(bytes20(data[0:20])));
|
||||||
address executor = address(uint160(bytes20(data[data.length - 20:])));
|
bytes4 selector = bytes4(data[20:24]);
|
||||||
bytes memory protocolData = data[:data.length - 24];
|
bytes memory protocolData = data[24:];
|
||||||
|
|
||||||
if (!executors[executor]) {
|
if (!executors[executor]) {
|
||||||
revert ExecutionDispatcher__UnapprovedExecutor();
|
revert ExecutionDispatcher__UnapprovedExecutor();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {V4Router} from "@uniswap/v4-periphery/src/V4Router.sol";
|
|||||||
import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
|
import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
|
||||||
import {IV4Router} from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol";
|
import {IV4Router} from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol";
|
||||||
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
||||||
|
import "lib/forge-std/src/console.sol";
|
||||||
|
|
||||||
error UniswapV4Executor__InvalidDataLength();
|
error UniswapV4Executor__InvalidDataLength();
|
||||||
error UniswapV4Executor__SwapFailed();
|
error UniswapV4Executor__SwapFailed();
|
||||||
@@ -24,108 +25,128 @@ contract UniswapV4Executor is IExecutor, V4Router {
|
|||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
using CurrencyLibrary for Currency;
|
using CurrencyLibrary for Currency;
|
||||||
|
|
||||||
|
struct UniswapV4Pool {
|
||||||
|
address intermediaryToken;
|
||||||
|
uint24 fee;
|
||||||
|
int24 tickSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(IPoolManager _poolManager) V4Router(_poolManager) {}
|
constructor(IPoolManager _poolManager) V4Router(_poolManager) {}
|
||||||
|
|
||||||
function swap(uint256, bytes calldata data)
|
function swap(uint256 amountIn, bytes calldata data)
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
returns (uint256 calculatedAmount)
|
returns (uint256 calculatedAmount)
|
||||||
{
|
{
|
||||||
(address tokenIn, address tokenOut, bool isExactInput, uint256 amount) =
|
(
|
||||||
_decodeData(data);
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
uint256 amountOutMin,
|
||||||
|
bool zeroForOne,
|
||||||
|
address callbackExecutor,
|
||||||
|
bytes4 callbackSelector,
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||||
|
) = _decodeData(data);
|
||||||
|
|
||||||
|
bytes memory fullData;
|
||||||
|
if (pools.length == 0) {
|
||||||
|
console.log("problem"); // raise error
|
||||||
|
} else if (pools.length == 1) {
|
||||||
|
PoolKey memory key = PoolKey({
|
||||||
|
currency0: Currency.wrap(zeroForOne ? tokenIn : tokenOut),
|
||||||
|
currency1: Currency.wrap(zeroForOne ? tokenOut : tokenIn),
|
||||||
|
fee: pools[0].fee,
|
||||||
|
tickSpacing: pools[0].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: uint128(amountIn),
|
||||||
|
amountOutMinimum: uint128(amountOutMin),
|
||||||
|
hookData: bytes("")
|
||||||
|
})
|
||||||
|
);
|
||||||
|
params[1] = abi.encode(key.currency0, amountIn);
|
||||||
|
params[2] = abi.encode(key.currency1, 0);
|
||||||
|
bytes memory swapData = abi.encode(actions, params);
|
||||||
|
fullData =
|
||||||
|
abi.encodePacked(callbackExecutor, callbackSelector, swapData);
|
||||||
|
} else {
|
||||||
|
console.log("do later");
|
||||||
|
}
|
||||||
|
|
||||||
uint256 tokenOutBalanceBefore;
|
uint256 tokenOutBalanceBefore;
|
||||||
uint256 tokenInBalanceBefore;
|
|
||||||
|
|
||||||
tokenOutBalanceBefore = tokenOut == address(0)
|
tokenOutBalanceBefore = tokenOut == address(0)
|
||||||
? address(this).balance
|
? address(this).balance
|
||||||
: IERC20(tokenOut).balanceOf(address(this));
|
: IERC20(tokenOut).balanceOf(address(this));
|
||||||
|
|
||||||
tokenInBalanceBefore = tokenIn == address(0)
|
executeActions(fullData);
|
||||||
? address(this).balance
|
|
||||||
: IERC20(tokenIn).balanceOf(address(this));
|
|
||||||
|
|
||||||
_executeActions(data);
|
|
||||||
|
|
||||||
uint256 tokenOutBalanceAfter;
|
uint256 tokenOutBalanceAfter;
|
||||||
uint256 tokenInBalanceAfter;
|
|
||||||
|
|
||||||
tokenOutBalanceAfter = tokenOut == address(0)
|
tokenOutBalanceAfter = tokenOut == address(0)
|
||||||
? address(this).balance
|
? address(this).balance
|
||||||
: IERC20(tokenOut).balanceOf(address(this));
|
: IERC20(tokenOut).balanceOf(address(this));
|
||||||
|
|
||||||
tokenInBalanceAfter = tokenIn == address(0)
|
|
||||||
? address(this).balance
|
|
||||||
: IERC20(tokenIn).balanceOf(address(this));
|
|
||||||
|
|
||||||
if (isExactInput) {
|
|
||||||
calculatedAmount = tokenOutBalanceAfter - tokenOutBalanceBefore;
|
calculatedAmount = tokenOutBalanceAfter - tokenOutBalanceBefore;
|
||||||
} else {
|
|
||||||
calculatedAmount = tokenInBalanceBefore - tokenInBalanceAfter;
|
|
||||||
}
|
|
||||||
|
|
||||||
return calculatedAmount;
|
return calculatedAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// necessary to convert bytes memory to bytes calldata
|
||||||
|
function executeActions(bytes memory unlockData) public {
|
||||||
|
poolManager.unlock(unlockData);
|
||||||
|
}
|
||||||
|
|
||||||
function _decodeData(bytes calldata data)
|
function _decodeData(bytes calldata data)
|
||||||
internal
|
public
|
||||||
pure
|
pure
|
||||||
returns (
|
returns (
|
||||||
address tokenIn,
|
address tokenIn,
|
||||||
address tokenOut,
|
address tokenOut,
|
||||||
bool isExactInput,
|
uint256 amountOutMin,
|
||||||
uint256 amount
|
bool zeroForOne,
|
||||||
|
address callbackExecutor,
|
||||||
|
bytes4 callbackSelector,
|
||||||
|
UniswapV4Pool[] memory pools
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
(bytes memory actions, bytes[] memory params) =
|
require(data.length >= 97, "Invalid data length");
|
||||||
abi.decode(data, (bytes, bytes[]));
|
|
||||||
|
|
||||||
// First byte of actions determines the swap type
|
tokenIn = address(bytes20(data[0:20]));
|
||||||
uint8 action = uint8(bytes1(actions[0]));
|
tokenOut = address(bytes20(data[20:40]));
|
||||||
|
amountOutMin = uint256(bytes32(data[40:72]));
|
||||||
|
zeroForOne = (data[72] != 0);
|
||||||
|
callbackExecutor = address(bytes20(data[73:93]));
|
||||||
|
callbackSelector = bytes4(data[93:97]);
|
||||||
|
|
||||||
if (action == uint8(Actions.SWAP_EXACT_IN_SINGLE)) {
|
uint256 poolsLength = (data.length - 97) / 26; // 26 bytes per pool object
|
||||||
IV4Router.ExactInputSingleParams memory swapParams =
|
pools = new UniswapV4Pool[](poolsLength);
|
||||||
abi.decode(params[0], (IV4Router.ExactInputSingleParams));
|
bytes memory poolsData = data[97:];
|
||||||
|
uint256 offset = 0;
|
||||||
|
for (uint256 i = 0; i < poolsLength; i++) {
|
||||||
|
address intermediaryToken;
|
||||||
|
uint24 fee;
|
||||||
|
int24 tickSpacing;
|
||||||
|
|
||||||
tokenIn = swapParams.zeroForOne
|
assembly {
|
||||||
? address(uint160(swapParams.poolKey.currency0.toId()))
|
intermediaryToken := mload(add(poolsData, add(offset, 20)))
|
||||||
: address(uint160(swapParams.poolKey.currency1.toId()));
|
fee := shr(232, mload(add(poolsData, add(offset, 52))))
|
||||||
tokenOut = swapParams.zeroForOne
|
tickSpacing := shr(232, mload(add(poolsData, add(offset, 55))))
|
||||||
? address(uint160(swapParams.poolKey.currency1.toId()))
|
}
|
||||||
: address(uint160(swapParams.poolKey.currency0.toId()));
|
pools[i] = UniswapV4Pool(intermediaryToken, fee, tickSpacing);
|
||||||
isExactInput = true;
|
offset += 26;
|
||||||
amount = swapParams.amountIn;
|
|
||||||
} else if (action == uint8(Actions.SWAP_EXACT_OUT_SINGLE)) {
|
|
||||||
IV4Router.ExactOutputSingleParams memory swapParams =
|
|
||||||
abi.decode(params[0], (IV4Router.ExactOutputSingleParams));
|
|
||||||
|
|
||||||
tokenIn = swapParams.zeroForOne
|
|
||||||
? address(uint160(swapParams.poolKey.currency0.toId()))
|
|
||||||
: address(uint160(swapParams.poolKey.currency1.toId()));
|
|
||||||
tokenOut = swapParams.zeroForOne
|
|
||||||
? address(uint160(swapParams.poolKey.currency1.toId()))
|
|
||||||
: address(uint160(swapParams.poolKey.currency0.toId()));
|
|
||||||
isExactInput = false;
|
|
||||||
amount = swapParams.amountOut;
|
|
||||||
} else if (action == uint8(Actions.SWAP_EXACT_IN)) {
|
|
||||||
IV4Router.ExactInputParams memory swapParams =
|
|
||||||
abi.decode(params[0], (IV4Router.ExactInputParams));
|
|
||||||
|
|
||||||
tokenIn = address(uint160(swapParams.currencyIn.toId()));
|
|
||||||
PathKey memory lastPath =
|
|
||||||
swapParams.path[swapParams.path.length - 1];
|
|
||||||
tokenOut = address(uint160(lastPath.intermediateCurrency.toId()));
|
|
||||||
isExactInput = true;
|
|
||||||
amount = swapParams.amountIn;
|
|
||||||
} else if (action == uint8(Actions.SWAP_EXACT_OUT)) {
|
|
||||||
IV4Router.ExactOutputParams memory swapParams =
|
|
||||||
abi.decode(params[0], (IV4Router.ExactOutputParams));
|
|
||||||
|
|
||||||
PathKey memory firstPath = swapParams.path[0];
|
|
||||||
tokenIn = address(uint160(firstPath.intermediateCurrency.toId()));
|
|
||||||
tokenOut = address(uint160(swapParams.currencyOut.toId()));
|
|
||||||
isExactInput = false;
|
|
||||||
amount = swapParams.amountOut;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -857,15 +857,22 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, tychoRouterAddr, amountIn);
|
deal(USDE_ADDR, tychoRouterAddr, amountIn);
|
||||||
|
|
||||||
bytes memory protocolData = UniswapV4Utils.encodeExactInputSingle(
|
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||||
USDE_ADDR, USDT_ADDR, 100, true, 1, uint128(amountIn)
|
new UniswapV4Executor.UniswapV4Pool[](1);
|
||||||
);
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: USDT_ADDR,
|
||||||
|
fee: uint24(100),
|
||||||
|
tickSpacing: int24(1)
|
||||||
|
});
|
||||||
|
|
||||||
// add executor and selector for callback
|
bytes memory protocolData = UniswapV4Utils.encodeExactInputSingle(
|
||||||
bytes memory protocolDataWithCallBack = abi.encodePacked(
|
USDE_ADDR,
|
||||||
protocolData,
|
USDT_ADDR,
|
||||||
|
uint256(1),
|
||||||
|
true,
|
||||||
|
address(usv4Executor),
|
||||||
SafeCallback.unlockCallback.selector,
|
SafeCallback.unlockCallback.selector,
|
||||||
address(usv4Executor)
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes memory swap = encodeSwap(
|
bytes memory swap = encodeSwap(
|
||||||
@@ -874,7 +881,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
uint24(0),
|
uint24(0),
|
||||||
address(usv4Executor),
|
address(usv4Executor),
|
||||||
bytes4(0),
|
bytes4(0),
|
||||||
protocolDataWithCallBack
|
protocolData
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes[] memory swaps = new bytes[](1);
|
bytes[] memory swaps = new bytes[](1);
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity ^0.8.26;
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
import "../../src/executors/UniswapV4Executor.sol";
|
||||||
import "./UniswapV4Utils.sol";
|
import "./UniswapV4Utils.sol";
|
||||||
import "@src/executors/UniswapV4Executor.sol";
|
import "@src/executors/UniswapV4Executor.sol";
|
||||||
import {Constants} from "../Constants.sol";
|
import {Constants} from "../Constants.sol";
|
||||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||||
import {console} from "forge-std/console.sol";
|
import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
||||||
|
|
||||||
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
||||||
constructor(IPoolManager _poolManager) UniswapV4Executor(_poolManager) {}
|
constructor(IPoolManager _poolManager) UniswapV4Executor(_poolManager) {}
|
||||||
@@ -16,8 +17,11 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
|||||||
returns (
|
returns (
|
||||||
address tokenIn,
|
address tokenIn,
|
||||||
address tokenOut,
|
address tokenOut,
|
||||||
bool isExactInput,
|
uint256 amountOutMin,
|
||||||
uint256 amount
|
bool zeroForOne,
|
||||||
|
address callbackExecutor,
|
||||||
|
bytes4 callbackSelector,
|
||||||
|
UniswapV4Pool[] memory pools
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return _decodeData(data);
|
return _decodeData(data);
|
||||||
@@ -40,39 +44,58 @@ contract UniswapV4ExecutorTest is Test, Constants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testDecodeParams() public view {
|
function testDecodeParams() public view {
|
||||||
uint24 expectedPoolFee = 500;
|
uint256 minAmountOut = 100;
|
||||||
uint128 expectedAmount = 100;
|
bool zeroForOne = true;
|
||||||
|
uint24 pool1Fee = 500;
|
||||||
|
int24 tickSpacing1 = 60;
|
||||||
|
uint24 pool2Fee = 1000;
|
||||||
|
int24 tickSpacing2 = -10;
|
||||||
|
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||||
|
new UniswapV4Executor.UniswapV4Pool[](2);
|
||||||
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: USDT_ADDR,
|
||||||
|
fee: pool1Fee,
|
||||||
|
tickSpacing: tickSpacing1
|
||||||
|
});
|
||||||
|
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: USDE_ADDR,
|
||||||
|
fee: pool2Fee,
|
||||||
|
tickSpacing: tickSpacing2
|
||||||
|
});
|
||||||
|
|
||||||
bytes memory data = UniswapV4Utils.encodeExactInputSingle(
|
bytes memory data = UniswapV4Utils.encodeExactInputSingle(
|
||||||
USDE_ADDR, USDT_ADDR, expectedPoolFee, false, 1, expectedAmount
|
USDE_ADDR,
|
||||||
|
USDT_ADDR,
|
||||||
|
minAmountOut,
|
||||||
|
zeroForOne,
|
||||||
|
address(uniswapV4Exposed),
|
||||||
|
SafeCallback.unlockCallback.selector,
|
||||||
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
(address tokenIn, address tokenOut, bool isExactInput, uint256 amount) =
|
(
|
||||||
uniswapV4Exposed.decodeData(data);
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
uint256 amountOutMin,
|
||||||
|
bool zeroForOneDecoded,
|
||||||
|
address callbackExecutor,
|
||||||
|
bytes4 callbackSelector,
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
|
||||||
|
) = uniswapV4Exposed.decodeData(data);
|
||||||
|
|
||||||
assertEq(tokenIn, USDE_ADDR);
|
assertEq(tokenIn, USDE_ADDR);
|
||||||
assertEq(tokenOut, USDT_ADDR);
|
assertEq(tokenOut, USDT_ADDR);
|
||||||
assertTrue(isExactInput);
|
assertEq(amountOutMin, minAmountOut);
|
||||||
assertEq(amount, expectedAmount);
|
assertEq(zeroForOneDecoded, zeroForOne);
|
||||||
}
|
assertEq(callbackExecutor, address(uniswapV4Exposed));
|
||||||
|
assertEq(callbackSelector, SafeCallback.unlockCallback.selector);
|
||||||
function testSwap() public {
|
assertEq(decodedPools.length, 2);
|
||||||
uint256 amountIn = 100 ether;
|
assertEq(decodedPools[0].intermediaryToken, USDT_ADDR);
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
assertEq(decodedPools[0].fee, pool1Fee);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
assertEq(decodedPools[0].tickSpacing, tickSpacing1);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
assertEq(decodedPools[1].intermediaryToken, USDE_ADDR);
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
assertEq(decodedPools[1].fee, pool2Fee);
|
||||||
|
assertEq(decodedPools[1].tickSpacing, tickSpacing2);
|
||||||
bytes memory data = UniswapV4Utils.encodeExactInputSingle(
|
|
||||||
USDE_ADDR, USDT_ADDR, 100, true, 1, uint128(amountIn)
|
|
||||||
);
|
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
|
||||||
assertEq(
|
|
||||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
|
||||||
usdeBalanceBeforeSwapExecutor - amountIn
|
|
||||||
);
|
|
||||||
assertTrue(USDT.balanceOf(address(uniswapV4Exposed)) == amountOut);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,40 +7,31 @@ library UniswapV4Utils {
|
|||||||
function encodeExactInputSingle(
|
function encodeExactInputSingle(
|
||||||
address tokenIn,
|
address tokenIn,
|
||||||
address tokenOut,
|
address tokenOut,
|
||||||
uint24 fee,
|
uint256 amountOutMin,
|
||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
uint24 tickSpacing,
|
address callbackExecutor,
|
||||||
uint128 amountIn
|
bytes4 callbackSelector,
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||||
) public pure returns (bytes memory) {
|
) public pure returns (bytes memory) {
|
||||||
PoolKey memory key = PoolKey({
|
bytes memory encodedPools;
|
||||||
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(
|
for (uint256 i = 0; i < pools.length; i++) {
|
||||||
uint8(Actions.SWAP_EXACT_IN_SINGLE),
|
encodedPools = abi.encodePacked(
|
||||||
uint8(Actions.SETTLE_ALL),
|
encodedPools,
|
||||||
uint8(Actions.TAKE_ALL)
|
pools[i].intermediaryToken,
|
||||||
|
bytes3(pools[i].fee),
|
||||||
|
pools[i].tickSpacing
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bytes[] memory params = new bytes[](3);
|
return abi.encodePacked(
|
||||||
|
tokenIn,
|
||||||
params[0] = abi.encode(
|
tokenOut,
|
||||||
IV4Router.ExactInputSingleParams({
|
amountOutMin,
|
||||||
poolKey: key,
|
zeroForOne,
|
||||||
zeroForOne: zeroForOne,
|
callbackExecutor,
|
||||||
amountIn: amountIn,
|
bytes4(callbackSelector),
|
||||||
amountOutMinimum: 0,
|
encodedPools
|
||||||
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