feat: Decode single and sequential swaps in LibSwap
- The old way was useful when we just had split swaps. Unfortunately, we now have split, sequential, and single swaps, which don't always require token indices or split percentages, so we need to decode differently for each case.
This commit is contained in:
committed by
Diana Carvalho
parent
3ae9d3ad67
commit
1dad4afb6b
@@ -2,36 +2,42 @@
|
|||||||
pragma solidity ^0.8.26;
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
library LibSwap {
|
library LibSwap {
|
||||||
/// Returns the InToken index into an array of tokens
|
/**
|
||||||
function tokenInIndex(
|
* @dev Returns arguments required to perform a single swap
|
||||||
bytes calldata swap
|
*/
|
||||||
) internal pure returns (uint8 res) {
|
function decodeSingleSwap(bytes calldata swap)
|
||||||
res = uint8(swap[0]);
|
internal
|
||||||
|
pure
|
||||||
|
returns (address executor, bytes calldata protocolData)
|
||||||
|
{
|
||||||
|
executor = address(uint160(bytes20(swap[0:20])));
|
||||||
|
protocolData = swap[20:];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The OutToken index into an array of tokens
|
/**
|
||||||
function tokenOutIndex(
|
* @dev Returns arguments required to perform a sequential swap
|
||||||
bytes calldata swap
|
*/
|
||||||
) internal pure returns (uint8 res) {
|
function decodeSequentialSwap(bytes calldata swap)
|
||||||
res = uint8(swap[1]);
|
internal
|
||||||
|
pure
|
||||||
|
returns (address executor, bytes calldata protocolData)
|
||||||
|
{
|
||||||
|
executor = address(uint160(bytes20(swap[0:20])));
|
||||||
|
protocolData = swap[20:];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The relative amount of token quantity routed into this swap
|
/**
|
||||||
function splitPercentage(
|
* @dev Returns arguments required to perform a split swap
|
||||||
bytes calldata swap
|
*/
|
||||||
) internal pure returns (uint24 res) {
|
function decodeSplitSwap(bytes calldata swap)
|
||||||
res = uint24(bytes3(swap[2:5]));
|
internal
|
||||||
}
|
pure
|
||||||
|
returns (uint8 tokenInIndex, uint8 tokenOutIndex, uint24 split, address executor, bytes calldata protocolData)
|
||||||
/// The address of the executor contract
|
{
|
||||||
function executor(bytes calldata swap) internal pure returns (address res) {
|
tokenInIndex = uint8(swap[0]);
|
||||||
res = address(uint160(bytes20(swap[5:25])));
|
tokenOutIndex = uint8(swap[1]);
|
||||||
}
|
split = uint24(bytes3(swap[2:5]));
|
||||||
|
executor = address(uint160(bytes20(swap[5:25])));
|
||||||
/// Remaining bytes are interpreted as protocol data
|
protocolData = swap[25:];
|
||||||
function protocolData(
|
|
||||||
bytes calldata swap
|
|
||||||
) internal pure returns (bytes calldata res) {
|
|
||||||
res = swap[25:];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,7 +345,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
* @param minAmountOut The minimum acceptable amount of the output token. Reverts if this condition is not met. This should always be set to avoid losing funds due to slippage.
|
* @param minAmountOut The minimum acceptable amount of the output token. Reverts if this condition is not met. This should always be set to avoid losing funds due to slippage.
|
||||||
* @param wrapEth If true, wraps the input token (native ETH) into WETH.
|
* @param wrapEth If true, wraps the input token (native ETH) into WETH.
|
||||||
* @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver.
|
* @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver.
|
||||||
* @param nTokens The total number of tokens involved in the swap graph (used to initialize arrays for internal calculations).
|
|
||||||
* @param receiver The address to receive the output tokens.
|
* @param receiver The address to receive the output tokens.
|
||||||
* @param swapData Encoded swap details.
|
* @param swapData Encoded swap details.
|
||||||
*
|
*
|
||||||
@@ -358,7 +357,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
uint256 minAmountOut,
|
uint256 minAmountOut,
|
||||||
bool wrapEth,
|
bool wrapEth,
|
||||||
bool unwrapEth,
|
bool unwrapEth,
|
||||||
uint256 nTokens,
|
|
||||||
address receiver,
|
address receiver,
|
||||||
bytes calldata swapData
|
bytes calldata swapData
|
||||||
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
|
) public payable whenNotPaused nonReentrant returns (uint256 amountOut) {
|
||||||
@@ -370,7 +368,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
minAmountOut,
|
minAmountOut,
|
||||||
wrapEth,
|
wrapEth,
|
||||||
unwrapEth,
|
unwrapEth,
|
||||||
nTokens,
|
|
||||||
receiver,
|
receiver,
|
||||||
swapData
|
swapData
|
||||||
);
|
);
|
||||||
@@ -394,7 +391,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
* @param minAmountOut The minimum acceptable amount of the output token. Reverts if this condition is not met. This should always be set to avoid losing funds due to slippage.
|
* @param minAmountOut The minimum acceptable amount of the output token. Reverts if this condition is not met. This should always be set to avoid losing funds due to slippage.
|
||||||
* @param wrapEth If true, wraps the input token (native ETH) into WETH.
|
* @param wrapEth If true, wraps the input token (native ETH) into WETH.
|
||||||
* @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver.
|
* @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver.
|
||||||
* @param nTokens The total number of tokens involved in the swap graph (used to initialize arrays for internal calculations).
|
|
||||||
* @param receiver The address to receive the output tokens.
|
* @param receiver The address to receive the output tokens.
|
||||||
* @param permitSingle A Permit2 structure containing token approval details for the input token. Ignored if `wrapEth` is true.
|
* @param permitSingle A Permit2 structure containing token approval details for the input token. Ignored if `wrapEth` is true.
|
||||||
* @param signature A valid signature authorizing the Permit2 approval. Ignored if `wrapEth` is true.
|
* @param signature A valid signature authorizing the Permit2 approval. Ignored if `wrapEth` is true.
|
||||||
@@ -409,7 +405,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
uint256 minAmountOut,
|
uint256 minAmountOut,
|
||||||
bool wrapEth,
|
bool wrapEth,
|
||||||
bool unwrapEth,
|
bool unwrapEth,
|
||||||
uint256 nTokens,
|
|
||||||
address receiver,
|
address receiver,
|
||||||
IAllowanceTransfer.PermitSingle calldata permitSingle,
|
IAllowanceTransfer.PermitSingle calldata permitSingle,
|
||||||
bytes calldata signature,
|
bytes calldata signature,
|
||||||
@@ -433,7 +428,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
minAmountOut,
|
minAmountOut,
|
||||||
wrapEth,
|
wrapEth,
|
||||||
unwrapEth,
|
unwrapEth,
|
||||||
nTokens,
|
|
||||||
receiver,
|
receiver,
|
||||||
swapData
|
swapData
|
||||||
);
|
);
|
||||||
@@ -523,7 +517,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
uint256 minAmountOut,
|
uint256 minAmountOut,
|
||||||
bool wrapEth,
|
bool wrapEth,
|
||||||
bool unwrapEth,
|
bool unwrapEth,
|
||||||
uint256 nTokens,
|
|
||||||
address receiver,
|
address receiver,
|
||||||
bytes calldata swap_
|
bytes calldata swap_
|
||||||
) internal returns (uint256 amountOut) {
|
) internal returns (uint256 amountOut) {
|
||||||
@@ -544,8 +537,10 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
? address(this).balance
|
? address(this).balance
|
||||||
: IERC20(tokenIn).balanceOf(address(this));
|
: IERC20(tokenIn).balanceOf(address(this));
|
||||||
|
|
||||||
amountOut =
|
(address executor, bytes calldata protocolData) =
|
||||||
_callExecutor(swap_.executor(), amountIn, swap_.protocolData());
|
swap_.decodeSingleSwap();
|
||||||
|
|
||||||
|
amountOut = _callExecutor(executor, amountIn, protocolData);
|
||||||
uint256 currentBalance = tokenIn == address(0)
|
uint256 currentBalance = tokenIn == address(0)
|
||||||
? address(this).balance
|
? address(this).balance
|
||||||
: IERC20(tokenIn).balanceOf(address(this));
|
: IERC20(tokenIn).balanceOf(address(this));
|
||||||
@@ -684,6 +679,8 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
uint8 tokenInIndex = 0;
|
uint8 tokenInIndex = 0;
|
||||||
uint8 tokenOutIndex = 0;
|
uint8 tokenOutIndex = 0;
|
||||||
uint24 split;
|
uint24 split;
|
||||||
|
address executor;
|
||||||
|
bytes calldata protocolData;
|
||||||
bytes calldata swapData;
|
bytes calldata swapData;
|
||||||
|
|
||||||
uint256[] memory remainingAmounts = new uint256[](nTokens);
|
uint256[] memory remainingAmounts = new uint256[](nTokens);
|
||||||
@@ -694,17 +691,16 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
|
|
||||||
while (swaps_.length > 0) {
|
while (swaps_.length > 0) {
|
||||||
(swapData, swaps_) = swaps_.next();
|
(swapData, swaps_) = swaps_.next();
|
||||||
tokenInIndex = swapData.tokenInIndex();
|
|
||||||
tokenOutIndex = swapData.tokenOutIndex();
|
(tokenInIndex, tokenOutIndex, split, executor, protocolData) =
|
||||||
split = swapData.splitPercentage();
|
swapData.decodeSplitSwap();
|
||||||
|
|
||||||
currentAmountIn = split > 0
|
currentAmountIn = split > 0
|
||||||
? (amounts[tokenInIndex] * split) / 0xffffff
|
? (amounts[tokenInIndex] * split) / 0xffffff
|
||||||
: remainingAmounts[tokenInIndex];
|
: remainingAmounts[tokenInIndex];
|
||||||
|
|
||||||
currentAmountOut = _callExecutor(
|
currentAmountOut =
|
||||||
swapData.executor(), currentAmountIn, swapData.protocolData()
|
_callExecutor(executor, currentAmountIn, protocolData);
|
||||||
);
|
|
||||||
// Checks if the output token is the same as the input token
|
// Checks if the output token is the same as the input token
|
||||||
if (tokenOutIndex == 0) {
|
if (tokenOutIndex == 0) {
|
||||||
cyclicSwapAmountOut += currentAmountOut;
|
cyclicSwapAmountOut += currentAmountOut;
|
||||||
@@ -725,16 +721,20 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
*
|
*
|
||||||
* @return calculatedAmount The total amount of the buy token obtained after all swaps have been executed.
|
* @return calculatedAmount The total amount of the buy token obtained after all swaps have been executed.
|
||||||
*/
|
*/
|
||||||
function _sequentialSwap(
|
function _sequentialSwap(uint256 amountIn, bytes calldata swaps_)
|
||||||
uint256 amountIn,
|
internal
|
||||||
bytes calldata swaps_
|
returns (uint256 calculatedAmount)
|
||||||
) internal returns (uint256 calculatedAmount) {
|
{
|
||||||
bytes calldata swap;
|
bytes calldata swap;
|
||||||
calculatedAmount = amountIn;
|
calculatedAmount = amountIn;
|
||||||
while (swaps_.length > 0) {
|
while (swaps_.length > 0) {
|
||||||
(swap, swaps_) = swaps_.next();
|
(swap, swaps_) = swaps_.next();
|
||||||
|
|
||||||
|
(address executor, bytes calldata protocolData) =
|
||||||
|
swap.decodeSingleSwap();
|
||||||
|
|
||||||
calculatedAmount =
|
calculatedAmount =
|
||||||
_callExecutor(swap.executor(), calculatedAmount, swap.protocolData());
|
_callExecutor(executor, calculatedAmount, protocolData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,45 @@ import "../lib/LibSwap.sol";
|
|||||||
contract LibSwapTest is Test {
|
contract LibSwapTest is Test {
|
||||||
using LibSwap for bytes;
|
using LibSwap for bytes;
|
||||||
|
|
||||||
function testSwap() public view {
|
function testSingleSwap() public view {
|
||||||
|
address executor = 0x1234567890123456789012345678901234567890;
|
||||||
|
bytes memory protocolData = abi.encodePacked(uint256(123));
|
||||||
|
|
||||||
|
bytes memory swap = abi.encodePacked(executor, protocolData);
|
||||||
|
this.assertSingleSwap(swap, executor, protocolData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertSingleSwap(
|
||||||
|
bytes calldata swap,
|
||||||
|
address executor,
|
||||||
|
bytes calldata protocolData
|
||||||
|
) public pure {
|
||||||
|
(address decodedExecutor, bytes memory decodedProtocolData) =
|
||||||
|
swap.decodeSingleSwap();
|
||||||
|
assertEq(decodedExecutor, executor);
|
||||||
|
assertEq(decodedProtocolData, protocolData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSequentialSwap() public view {
|
||||||
|
address executor = 0x1234567890123456789012345678901234567890;
|
||||||
|
bytes memory protocolData = abi.encodePacked(uint256(234));
|
||||||
|
|
||||||
|
bytes memory swap = abi.encodePacked(executor, protocolData);
|
||||||
|
this.assertSequentialSwap(swap, executor, protocolData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertSequentialSwap(
|
||||||
|
bytes calldata swap,
|
||||||
|
address executor,
|
||||||
|
bytes calldata protocolData
|
||||||
|
) public pure {
|
||||||
|
(address decodedExecutor, bytes memory decodedProtocolData) =
|
||||||
|
swap.decodeSequentialSwap();
|
||||||
|
assertEq(decodedExecutor, executor);
|
||||||
|
assertEq(decodedProtocolData, protocolData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSplitSwap() public view {
|
||||||
uint8 tokenInIndex = 1;
|
uint8 tokenInIndex = 1;
|
||||||
uint8 tokenOutIndex = 2;
|
uint8 tokenOutIndex = 2;
|
||||||
uint24 split = 3;
|
uint24 split = 3;
|
||||||
@@ -17,20 +55,32 @@ contract LibSwapTest is Test {
|
|||||||
bytes memory swap = abi.encodePacked(
|
bytes memory swap = abi.encodePacked(
|
||||||
tokenInIndex, tokenOutIndex, split, executor, protocolData
|
tokenInIndex, tokenOutIndex, split, executor, protocolData
|
||||||
);
|
);
|
||||||
this.assertSwap(swap, tokenInIndex, tokenOutIndex, split, executor);
|
this.assertSplitSwap(
|
||||||
|
swap, tokenInIndex, tokenOutIndex, split, executor, protocolData
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is necessary so that the compiler accepts bytes as a LibSwap.sol
|
// This is necessary so that the compiler accepts bytes as a LibSwap.sol for testing
|
||||||
function assertSwap(
|
// This is because this function takes calldata as input
|
||||||
|
function assertSplitSwap(
|
||||||
bytes calldata swap,
|
bytes calldata swap,
|
||||||
uint8 tokenInIndex,
|
uint8 tokenInIndex,
|
||||||
uint8 tokenOutIndex,
|
uint8 tokenOutIndex,
|
||||||
uint24 split,
|
uint24 split,
|
||||||
address executor
|
address executor,
|
||||||
|
bytes calldata protocolData
|
||||||
) public pure {
|
) public pure {
|
||||||
assert(swap.tokenInIndex() == tokenInIndex);
|
(
|
||||||
assert(swap.tokenOutIndex() == tokenOutIndex);
|
uint8 decodedTokenInIndex,
|
||||||
assert(swap.splitPercentage() == split);
|
uint8 decodedTokenOutIndex,
|
||||||
assert(swap.executor() == executor);
|
uint24 decodedSplit,
|
||||||
|
address decodedExecutor,
|
||||||
|
bytes memory decodedProtocolData
|
||||||
|
) = swap.decodeSplitSwap();
|
||||||
|
assertEq(decodedTokenInIndex, tokenInIndex);
|
||||||
|
assertEq(decodedTokenOutIndex, tokenOutIndex);
|
||||||
|
assertEq(decodedSplit, split);
|
||||||
|
assertEq(decodedExecutor, executor);
|
||||||
|
assertEq(decodedProtocolData, protocolData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSingleSwapSimplePermit2() public {
|
function testSingleSwapPermit2() public {
|
||||||
// Trade 1 WETH for DAI with 1 swap on Uniswap V2 using Permit2
|
// Trade 1 WETH for DAI with 1 swap on Uniswap V2 using Permit2
|
||||||
// 1 WETH -> DAI
|
// 1 WETH -> DAI
|
||||||
// (USV2)
|
// (USV2)
|
||||||
@@ -247,24 +247,20 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes memory swap = encodeSplitSwap(
|
bytes memory swap =
|
||||||
uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData
|
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||||
);
|
|
||||||
bytes[] memory swaps = new bytes[](1);
|
|
||||||
swaps[0] = swap;
|
|
||||||
|
|
||||||
tychoRouter.splitSwapPermit2(
|
tychoRouter.singleSwapPermit2(
|
||||||
amountIn,
|
amountIn,
|
||||||
WETH_ADDR,
|
WETH_ADDR,
|
||||||
DAI_ADDR,
|
DAI_ADDR,
|
||||||
2659881924818443699786,
|
2659881924818443699786,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
2,
|
|
||||||
ALICE,
|
ALICE,
|
||||||
permitSingle,
|
permitSingle,
|
||||||
signature,
|
signature,
|
||||||
pleEncode(swaps)
|
swap
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||||
@@ -274,7 +270,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSequentialSwapMultipleHops() public {
|
function testSequentialSwap() public {
|
||||||
// Trade 1 WETH for USDC through DAI with 2 swaps on Uniswap V2
|
// Trade 1 WETH for USDC through DAI with 2 swaps on Uniswap V2
|
||||||
// 1 WETH -> DAI -> USDC
|
// 1 WETH -> DAI -> USDC
|
||||||
// (univ2) (univ2)
|
// (univ2) (univ2)
|
||||||
@@ -283,10 +279,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
|
|
||||||
bytes[] memory swaps = new bytes[](2);
|
bytes[] memory swaps = new bytes[](2);
|
||||||
// WETH -> DAI
|
// WETH -> DAI
|
||||||
swaps[0] = encodeSplitSwap(
|
swaps[0] = encodeSequentialSwap(
|
||||||
uint8(0),
|
|
||||||
uint8(1),
|
|
||||||
uint24(0),
|
|
||||||
address(usv2Executor),
|
address(usv2Executor),
|
||||||
encodeUniswapV2Swap(
|
encodeUniswapV2Swap(
|
||||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||||
@@ -294,10 +287,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// DAI -> USDC
|
// DAI -> USDC
|
||||||
swaps[1] = encodeSplitSwap(
|
swaps[1] = encodeSequentialSwap(
|
||||||
uint8(1),
|
|
||||||
uint8(2),
|
|
||||||
uint24(0),
|
|
||||||
address(usv2Executor),
|
address(usv2Executor),
|
||||||
encodeUniswapV2Swap(DAI_ADDR, DAI_USDC_POOL, tychoRouterAddr, true)
|
encodeUniswapV2Swap(DAI_ADDR, DAI_USDC_POOL, tychoRouterAddr, true)
|
||||||
);
|
);
|
||||||
@@ -384,9 +374,8 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes memory swap = encodeSplitSwap(
|
bytes memory swap =
|
||||||
uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData
|
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||||
);
|
|
||||||
|
|
||||||
uint256 minAmountOut = 2600 * 1e18;
|
uint256 minAmountOut = 2600 * 1e18;
|
||||||
uint256 amountOut = tychoRouter.singleSwapPermit2(
|
uint256 amountOut = tychoRouter.singleSwapPermit2(
|
||||||
@@ -396,7 +385,6 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
minAmountOut,
|
minAmountOut,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
2,
|
|
||||||
ALICE,
|
ALICE,
|
||||||
permitSingle,
|
permitSingle,
|
||||||
signature,
|
signature,
|
||||||
@@ -466,9 +454,8 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes memory swap = encodeSplitSwap(
|
bytes memory swap =
|
||||||
uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData
|
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||||
);
|
|
||||||
|
|
||||||
uint256 minAmountOut = 2600 * 1e18;
|
uint256 minAmountOut = 2600 * 1e18;
|
||||||
uint256 amountOut = tychoRouter.singleSwap(
|
uint256 amountOut = tychoRouter.singleSwap(
|
||||||
@@ -478,7 +465,6 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
minAmountOut,
|
minAmountOut,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
2,
|
|
||||||
ALICE,
|
ALICE,
|
||||||
swap
|
swap
|
||||||
);
|
);
|
||||||
@@ -979,27 +965,18 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
|
|
||||||
bytes[] memory swaps = new bytes[](2);
|
bytes[] memory swaps = new bytes[](2);
|
||||||
// USDC -> WETH
|
// USDC -> WETH
|
||||||
swaps[0] = encodeSplitSwap(
|
swaps[0] = encodeSequentialSwap(
|
||||||
uint8(0),
|
address(usv3Executor), usdcWethV3Pool1ZeroOneData
|
||||||
uint8(1),
|
|
||||||
uint24(0),
|
|
||||||
address(usv3Executor),
|
|
||||||
usdcWethV3Pool1ZeroOneData
|
|
||||||
);
|
);
|
||||||
// WETH -> USDC
|
// WETH -> USDC
|
||||||
swaps[1] = encodeSplitSwap(
|
swaps[1] = encodeSequentialSwap(
|
||||||
uint8(1),
|
address(usv3Executor), usdcWethV3Pool2OneZeroData
|
||||||
uint8(0),
|
|
||||||
uint24(0),
|
|
||||||
address(usv3Executor),
|
|
||||||
usdcWethV3Pool2OneZeroData
|
|
||||||
);
|
);
|
||||||
|
|
||||||
tychoRouter.exposedSequentialSwap(amountIn, pleEncode(swaps));
|
tychoRouter.exposedSequentialSwap(amountIn, pleEncode(swaps));
|
||||||
assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99889294);
|
assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99889294);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function testSplitInputCyclicSwap() public {
|
function testSplitInputCyclicSwap() public {
|
||||||
// This test has start and end tokens that are the same
|
// This test has start and end tokens that are the same
|
||||||
// The flow is:
|
// The flow is:
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ contract TychoRouterExposed is TychoRouter {
|
|||||||
return _splitSwap(amountIn, nTokens, swaps);
|
return _splitSwap(amountIn, nTokens, swaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
function exposedSequentialSwap(
|
function exposedSequentialSwap(uint256 amountIn, bytes calldata swaps)
|
||||||
uint256 amountIn,
|
external
|
||||||
bytes calldata swaps
|
returns (uint256)
|
||||||
) external returns (uint256) {
|
{
|
||||||
return _sequentialSwap(amountIn, swaps);
|
return _sequentialSwap(amountIn, swaps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,19 +188,23 @@ contract TychoRouterTestSetup is Constants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeSplitSwap(
|
function encodeSingleSwap(address executor, bytes memory protocolData)
|
||||||
uint8 tokenInIndex,
|
internal
|
||||||
uint8 tokenOutIndex,
|
pure
|
||||||
uint24 split,
|
returns (bytes memory)
|
||||||
address executor,
|
{
|
||||||
bytes memory protocolData
|
return abi.encodePacked(executor, protocolData);
|
||||||
) internal pure returns (bytes memory) {
|
|
||||||
return abi.encodePacked(
|
|
||||||
tokenInIndex, tokenOutIndex, split, executor, protocolData
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeSequentialSwap(
|
function encodeSequentialSwap(address executor, bytes memory protocolData)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodePacked(executor, protocolData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeSplitSwap(
|
||||||
uint8 tokenInIndex,
|
uint8 tokenInIndex,
|
||||||
uint8 tokenOutIndex,
|
uint8 tokenOutIndex,
|
||||||
uint24 split,
|
uint24 split,
|
||||||
|
|||||||
Reference in New Issue
Block a user