Merge pull request #279 from propeller-heads/euler/dc/ENG-5076-fix-uniswapv4-executor
feat: Support Euler low balance swaps (univ4)
This commit is contained in:
@@ -59,6 +59,8 @@ contract UniswapV4Executor is
|
|||||||
address intermediaryToken;
|
address intermediaryToken;
|
||||||
uint24 fee;
|
uint24 fee;
|
||||||
int24 tickSpacing;
|
int24 tickSpacing;
|
||||||
|
address hook;
|
||||||
|
bytes hookData;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(IPoolManager _poolManager, address _permit2)
|
constructor(IPoolManager _poolManager, address _permit2)
|
||||||
@@ -89,8 +91,6 @@ contract UniswapV4Executor is
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
address hook,
|
|
||||||
bytes memory hookData,
|
|
||||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||||
) = _decodeData(data);
|
) = _decodeData(data);
|
||||||
bytes memory swapData;
|
bytes memory swapData;
|
||||||
@@ -100,7 +100,7 @@ contract UniswapV4Executor is
|
|||||||
currency1: Currency.wrap(zeroForOne ? tokenOut : tokenIn),
|
currency1: Currency.wrap(zeroForOne ? tokenOut : tokenIn),
|
||||||
fee: pools[0].fee,
|
fee: pools[0].fee,
|
||||||
tickSpacing: pools[0].tickSpacing,
|
tickSpacing: pools[0].tickSpacing,
|
||||||
hooks: IHooks(hook)
|
hooks: IHooks(pools[0].hook)
|
||||||
});
|
});
|
||||||
swapData = abi.encodeWithSelector(
|
swapData = abi.encodeWithSelector(
|
||||||
this.swapExactInputSingle.selector,
|
this.swapExactInputSingle.selector,
|
||||||
@@ -109,7 +109,7 @@ contract UniswapV4Executor is
|
|||||||
amountIn,
|
amountIn,
|
||||||
transferType,
|
transferType,
|
||||||
receiver,
|
receiver,
|
||||||
hookData
|
pools[0].hookData
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
PathKey[] memory path = new PathKey[](pools.length);
|
PathKey[] memory path = new PathKey[](pools.length);
|
||||||
@@ -118,8 +118,8 @@ contract UniswapV4Executor is
|
|||||||
intermediateCurrency: Currency.wrap(pools[i].intermediaryToken),
|
intermediateCurrency: Currency.wrap(pools[i].intermediaryToken),
|
||||||
fee: pools[i].fee,
|
fee: pools[i].fee,
|
||||||
tickSpacing: pools[i].tickSpacing,
|
tickSpacing: pools[i].tickSpacing,
|
||||||
hooks: IHooks(hook),
|
hooks: IHooks(pools[i].hook),
|
||||||
hookData: hookData
|
hookData: pools[i].hookData
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,8 +149,6 @@ contract UniswapV4Executor is
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
address hook,
|
|
||||||
bytes memory hookData,
|
|
||||||
UniswapV4Pool[] memory pools
|
UniswapV4Pool[] memory pools
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -163,42 +161,71 @@ contract UniswapV4Executor is
|
|||||||
zeroForOne = data[40] != 0;
|
zeroForOne = data[40] != 0;
|
||||||
transferType = TransferType(uint8(data[41]));
|
transferType = TransferType(uint8(data[41]));
|
||||||
receiver = address(bytes20(data[42:62]));
|
receiver = address(bytes20(data[42:62]));
|
||||||
hook = address(bytes20(data[62:82]));
|
|
||||||
|
|
||||||
bytes calldata remaining = data[82:];
|
bytes calldata remaining = data[62:];
|
||||||
|
|
||||||
|
// Decode first pool with hook data
|
||||||
|
if (remaining.length < 48) {
|
||||||
|
// 20 + 3 + 3 + 20 + 2 = 48 minimum
|
||||||
|
revert UniswapV4Executor__InvalidDataLength();
|
||||||
|
}
|
||||||
|
|
||||||
address firstToken = address(bytes20(remaining[0:20]));
|
address firstToken = address(bytes20(remaining[0:20]));
|
||||||
uint24 firstFee = uint24(bytes3(remaining[20:23]));
|
uint24 firstFee = uint24(bytes3(remaining[20:23]));
|
||||||
int24 firstTickSpacing = int24(uint24(bytes3(remaining[23:26])));
|
int24 firstTickSpacing = int24(uint24(bytes3(remaining[23:26])));
|
||||||
UniswapV4Pool memory firstPool =
|
address firstHook = address(bytes20(remaining[26:46]));
|
||||||
UniswapV4Pool(firstToken, firstFee, firstTickSpacing);
|
uint16 firstHookDataLength = uint16(bytes2(remaining[46:48]));
|
||||||
|
|
||||||
|
uint256 firstPoolTotalLength = 48 + firstHookDataLength;
|
||||||
|
if (remaining.length < firstPoolTotalLength) {
|
||||||
|
revert UniswapV4Executor__InvalidDataLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes memory firstHookData = remaining[48:48 + firstHookDataLength];
|
||||||
|
|
||||||
// Remaining after first pool are ple encoded
|
// Remaining after first pool are ple encoded
|
||||||
bytes[] memory encodedPools =
|
bytes[] memory encodedPools = LibPrefixLengthEncodedByteArray.toArray(
|
||||||
LibPrefixLengthEncodedByteArray.toArray(remaining[26:]);
|
remaining[firstPoolTotalLength:]
|
||||||
|
);
|
||||||
|
|
||||||
pools = new UniswapV4Pool[](1 + encodedPools.length);
|
pools = new UniswapV4Pool[](1 + encodedPools.length);
|
||||||
pools[0] = firstPool;
|
pools[0] = UniswapV4Pool(
|
||||||
|
firstToken, firstFee, firstTickSpacing, firstHook, firstHookData
|
||||||
uint256 encodedPoolsLength = 26;
|
);
|
||||||
uint256 plePoolsTotalLength;
|
|
||||||
|
|
||||||
|
// Decode subsequent pools
|
||||||
for (uint256 i = 0; i < encodedPools.length; i++) {
|
for (uint256 i = 0; i < encodedPools.length; i++) {
|
||||||
bytes memory poolsData = encodedPools[i];
|
bytes memory poolData = encodedPools[i];
|
||||||
|
|
||||||
address intermediaryToken;
|
address intermediaryToken;
|
||||||
uint24 fee;
|
uint24 fee;
|
||||||
int24 tickSpacing;
|
int24 tickSpacing;
|
||||||
|
address hook;
|
||||||
|
uint16 hookDataLength;
|
||||||
|
|
||||||
// slither-disable-next-line assembly
|
// slither-disable-next-line assembly
|
||||||
assembly {
|
assembly {
|
||||||
intermediaryToken := mload(add(poolsData, add(0, 20)))
|
let dataPtr := add(poolData, 0x20)
|
||||||
fee := shr(232, mload(add(poolsData, add(0, 52))))
|
intermediaryToken := shr(96, mload(dataPtr))
|
||||||
tickSpacing := shr(232, mload(add(poolsData, add(0, 55))))
|
fee := and(shr(232, mload(add(dataPtr, 20))), 0xffffff)
|
||||||
}
|
tickSpacing := and(shr(208, mload(add(dataPtr, 20))), 0xffffff)
|
||||||
pools[i + 1] = UniswapV4Pool(intermediaryToken, fee, tickSpacing);
|
hook := shr(96, mload(add(dataPtr, 26)))
|
||||||
plePoolsTotalLength += 2 + encodedPoolsLength; // 2 bytes prefix + data
|
hookDataLength := and(shr(240, mload(add(dataPtr, 46))), 0xffff)
|
||||||
}
|
}
|
||||||
|
|
||||||
hookData = remaining[26 + plePoolsTotalLength:];
|
if (poolData.length < 48 + hookDataLength) {
|
||||||
|
revert UniswapV4Executor__InvalidDataLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes memory hookData = new bytes(hookDataLength);
|
||||||
|
for (uint256 j = 0; j < hookDataLength; j++) {
|
||||||
|
hookData[j] = poolData[48 + j];
|
||||||
|
}
|
||||||
|
|
||||||
|
pools[i + 1] = UniswapV4Pool(
|
||||||
|
intermediaryToken, fee, tickSpacing, hook, hookData
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,17 +302,12 @@ contract UniswapV4Executor is
|
|||||||
address receiver,
|
address receiver,
|
||||||
bytes calldata hookData
|
bytes calldata hookData
|
||||||
) external returns (uint128) {
|
) external returns (uint128) {
|
||||||
|
Currency currencyIn = zeroForOne ? poolKey.currency0 : poolKey.currency1;
|
||||||
|
_settle(currencyIn, amountIn, transferType);
|
||||||
uint128 amountOut = _swap(
|
uint128 amountOut = _swap(
|
||||||
poolKey, zeroForOne, -int256(uint256(amountIn)), hookData
|
poolKey, zeroForOne, -int256(uint256(amountIn)), hookData
|
||||||
).toUint128();
|
).toUint128();
|
||||||
|
|
||||||
Currency currencyIn = zeroForOne ? poolKey.currency0 : poolKey.currency1;
|
|
||||||
uint256 amount = _getFullDebt(currencyIn);
|
|
||||||
if (amount > amountIn) {
|
|
||||||
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
|
|
||||||
}
|
|
||||||
_settle(currencyIn, amount, transferType);
|
|
||||||
|
|
||||||
Currency currencyOut =
|
Currency currencyOut =
|
||||||
zeroForOne ? poolKey.currency1 : poolKey.currency0;
|
zeroForOne ? poolKey.currency1 : poolKey.currency0;
|
||||||
_take(currencyOut, receiver, _mapTakeAmount(amountOut, currencyOut));
|
_take(currencyOut, receiver, _mapTakeAmount(amountOut, currencyOut));
|
||||||
@@ -310,6 +332,7 @@ contract UniswapV4Executor is
|
|||||||
uint128 amountOut = 0;
|
uint128 amountOut = 0;
|
||||||
Currency swapCurrencyIn = currencyIn;
|
Currency swapCurrencyIn = currencyIn;
|
||||||
uint256 swapAmountIn = amountIn;
|
uint256 swapAmountIn = amountIn;
|
||||||
|
_settle(currencyIn, amountIn, transferType);
|
||||||
unchecked {
|
unchecked {
|
||||||
uint256 pathLength = path.length;
|
uint256 pathLength = path.length;
|
||||||
PathKey calldata pathKey;
|
PathKey calldata pathKey;
|
||||||
@@ -331,12 +354,6 @@ contract UniswapV4Executor is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 amount = _getFullDebt(currencyIn);
|
|
||||||
if (amount > amountIn) {
|
|
||||||
revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount);
|
|
||||||
}
|
|
||||||
_settle(currencyIn, amount, transferType);
|
|
||||||
|
|
||||||
_take(
|
_take(
|
||||||
swapCurrencyIn, // at the end of the loop this is actually currency out
|
swapCurrencyIn, // at the end of the loop this is actually currency out
|
||||||
receiver,
|
receiver,
|
||||||
@@ -387,21 +404,6 @@ contract UniswapV4Executor is
|
|||||||
amount = uint256(_amount);
|
amount = uint256(_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Obtain the full amount owed by this contract (negative delta)
|
|
||||||
/// @param currency Currency to get the delta for
|
|
||||||
/// @return amount The amount owed by this contract as a uint256
|
|
||||||
function _getFullDebt(Currency currency)
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (uint256 amount)
|
|
||||||
{
|
|
||||||
int256 _amount = poolManager.currencyDelta(address(this), currency);
|
|
||||||
// If the amount is positive, it should be taken not settled.
|
|
||||||
if (_amount > 0) revert UniswapV4Executor__DeltaNotNegative(currency);
|
|
||||||
// Casting is safe due to limits on the total supply of a pool
|
|
||||||
amount = uint256(-_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Pays and settles a currency to the pool manager.
|
* @notice Pays and settles a currency to the pool manager.
|
||||||
* @dev The implementing contract must ensure that the `payer` is a secure address.
|
* @dev The implementing contract must ensure that the `payer` is a secure address.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ contract Constants is Test, BaseConstants {
|
|||||||
address BSGG_ADDR = address(0xdA16Cf041E2780618c49Dbae5d734B89a6Bac9b3);
|
address BSGG_ADDR = address(0xdA16Cf041E2780618c49Dbae5d734B89a6Bac9b3);
|
||||||
address GHO_ADDR = address(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f);
|
address GHO_ADDR = address(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f);
|
||||||
address ONDO_ADDR = address(0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3);
|
address ONDO_ADDR = address(0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3);
|
||||||
|
address RLUSD_ADDR = address(0x8292Bb45bf1Ee4d140127049757C2E0fF06317eD);
|
||||||
|
|
||||||
// Maverick v2
|
// Maverick v2
|
||||||
address MAVERICK_V2_FACTORY = 0x0A7e848Aca42d879EF06507Fca0E7b33A0a63c1e;
|
address MAVERICK_V2_FACTORY = 0x0A7e848Aca42d879EF06507Fca0E7b33A0a63c1e;
|
||||||
@@ -146,6 +147,9 @@ contract Constants is Test, BaseConstants {
|
|||||||
// Curve meta registry
|
// Curve meta registry
|
||||||
address CURVE_META_REGISTRY = 0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC;
|
address CURVE_META_REGISTRY = 0xF98B45FA17DE75FB1aD0e7aFD971b0ca00e379fC;
|
||||||
|
|
||||||
|
// Uniswap v4 pool manager
|
||||||
|
address POOL_MANAGER = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Deploys a dummy contract with non-empty bytecode
|
* @dev Deploys a dummy contract with non-empty bytecode
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@ import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol";
|
|||||||
import {Test} from "../../lib/forge-std/src/Test.sol";
|
import {Test} from "../../lib/forge-std/src/Test.sol";
|
||||||
|
|
||||||
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
||||||
constructor(IPoolManager _poolManager, address _permit2)
|
constructor(IPoolManager _POOL_MANAGER, address _permit2)
|
||||||
UniswapV4Executor(_poolManager, _permit2)
|
UniswapV4Executor(_POOL_MANAGER, _permit2)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
function decodeData(bytes calldata data)
|
function decodeData(bytes calldata data)
|
||||||
@@ -24,8 +24,6 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
RestrictTransferFrom.TransferType transferType,
|
RestrictTransferFrom.TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
address hook,
|
|
||||||
bytes memory hookData,
|
|
||||||
UniswapV4Pool[] memory pools
|
UniswapV4Pool[] memory pools
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -41,13 +39,11 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
IERC20 USDT = IERC20(USDT_ADDR);
|
IERC20 USDT = IERC20(USDT_ADDR);
|
||||||
IERC20 USDC = IERC20(USDC_ADDR);
|
IERC20 USDC = IERC20(USDC_ADDR);
|
||||||
|
|
||||||
address poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
uint256 forkBlock = 22689128;
|
uint256 forkBlock = 22689128;
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
uniswapV4Exposed = new UniswapV4ExecutorExposed(
|
uniswapV4Exposed = new UniswapV4ExecutorExposed(
|
||||||
IPoolManager(poolManager), PERMIT2_ADDRESS
|
IPoolManager(POOL_MANAGER), PERMIT2_ADDRESS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,12 +59,16 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: USDT_ADDR,
|
intermediaryToken: USDT_ADDR,
|
||||||
fee: pool1Fee,
|
fee: pool1Fee,
|
||||||
tickSpacing: tickSpacing1
|
tickSpacing: tickSpacing1,
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: USDE_ADDR,
|
intermediaryToken: USDE_ADDR,
|
||||||
fee: pool2Fee,
|
fee: pool2Fee,
|
||||||
tickSpacing: tickSpacing2
|
tickSpacing: tickSpacing2,
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("0x12345")
|
||||||
});
|
});
|
||||||
|
|
||||||
bytes memory data = UniswapV4Utils.encodeExactInput(
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
@@ -77,8 +77,6 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
zeroForOne,
|
zeroForOne,
|
||||||
RestrictTransferFrom.TransferType.Transfer,
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
ALICE,
|
ALICE,
|
||||||
address(0),
|
|
||||||
bytes(""),
|
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -88,8 +86,6 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
bool zeroForOneDecoded,
|
bool zeroForOneDecoded,
|
||||||
RestrictTransferFrom.TransferType transferType,
|
RestrictTransferFrom.TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
address hook,
|
|
||||||
bytes memory hookData,
|
|
||||||
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
|
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
|
||||||
) = uniswapV4Exposed.decodeData(data);
|
) = uniswapV4Exposed.decodeData(data);
|
||||||
|
|
||||||
@@ -101,7 +97,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
uint8(RestrictTransferFrom.TransferType.Transfer)
|
uint8(RestrictTransferFrom.TransferType.Transfer)
|
||||||
);
|
);
|
||||||
assertEq(receiver, ALICE);
|
assertEq(receiver, ALICE);
|
||||||
assertEq(hook, address(0));
|
assertEq(decodedPools[0].hook, address(0));
|
||||||
assertEq(decodedPools.length, 2);
|
assertEq(decodedPools.length, 2);
|
||||||
assertEq(decodedPools[0].intermediaryToken, USDT_ADDR);
|
assertEq(decodedPools[0].intermediaryToken, USDT_ADDR);
|
||||||
assertEq(decodedPools[0].fee, pool1Fee);
|
assertEq(decodedPools[0].fee, pool1Fee);
|
||||||
@@ -109,12 +105,13 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
assertEq(decodedPools[1].intermediaryToken, USDE_ADDR);
|
assertEq(decodedPools[1].intermediaryToken, USDE_ADDR);
|
||||||
assertEq(decodedPools[1].fee, pool2Fee);
|
assertEq(decodedPools[1].fee, pool2Fee);
|
||||||
assertEq(decodedPools[1].tickSpacing, tickSpacing2);
|
assertEq(decodedPools[1].tickSpacing, tickSpacing2);
|
||||||
|
assertEq(decodedPools[1].hookData, bytes("0x12345"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSingleSwap() public {
|
function testSingleSwap() public {
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
@@ -123,7 +120,9 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: USDT_ADDR,
|
intermediaryToken: USDT_ADDR,
|
||||||
fee: uint24(100),
|
fee: uint24(100),
|
||||||
tickSpacing: int24(1)
|
tickSpacing: int24(1),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
|
|
||||||
bytes memory data = UniswapV4Utils.encodeExactInput(
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
@@ -132,13 +131,11 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
true,
|
true,
|
||||||
RestrictTransferFrom.TransferType.Transfer,
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
ALICE,
|
ALICE,
|
||||||
address(0),
|
|
||||||
bytes(""),
|
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
USDE.balanceOf(address(uniswapV4Exposed)),
|
||||||
usdeBalanceBeforeSwapExecutor - amountIn
|
usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
@@ -152,12 +149,12 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
loadCallDataFromFile("test_encode_uniswap_v4_simple_swap");
|
loadCallDataFromFile("test_encode_uniswap_v4_simple_swap");
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(ALICE), usdeBalanceBeforeSwapExecutor - amountIn
|
USDE.balanceOf(ALICE), usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
);
|
);
|
||||||
@@ -168,7 +165,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
// USDE -> USDT -> WBTC
|
// USDE -> USDT -> WBTC
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
@@ -177,12 +174,16 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: USDT_ADDR,
|
intermediaryToken: USDT_ADDR,
|
||||||
fee: uint24(100),
|
fee: uint24(100),
|
||||||
tickSpacing: int24(1)
|
tickSpacing: int24(1),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: WBTC_ADDR,
|
intermediaryToken: WBTC_ADDR,
|
||||||
fee: uint24(3000),
|
fee: uint24(3000),
|
||||||
tickSpacing: int24(60)
|
tickSpacing: int24(60),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
|
|
||||||
bytes memory data = UniswapV4Utils.encodeExactInput(
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
@@ -191,13 +192,11 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
true,
|
true,
|
||||||
RestrictTransferFrom.TransferType.Transfer,
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
ALICE,
|
ALICE,
|
||||||
address(0),
|
|
||||||
bytes(""),
|
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
USDE.balanceOf(address(uniswapV4Exposed)),
|
||||||
usdeBalanceBeforeSwapExecutor - amountIn
|
usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
@@ -212,12 +211,12 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
|
|
||||||
uint256 amountIn = 100 ether;
|
uint256 amountIn = 100 ether;
|
||||||
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
deal(USDE_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager);
|
uint256 usdeBalanceBeforePool = USDE.balanceOf(POOL_MANAGER);
|
||||||
uint256 usdeBalanceBeforeSwapExecutor =
|
uint256 usdeBalanceBeforeSwapExecutor =
|
||||||
USDE.balanceOf(address(uniswapV4Exposed));
|
USDE.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, protocolData);
|
||||||
assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn);
|
assertEq(USDE.balanceOf(POOL_MANAGER), usdeBalanceBeforePool + amountIn);
|
||||||
assertEq(
|
assertEq(
|
||||||
USDE.balanceOf(address(uniswapV4Exposed)),
|
USDE.balanceOf(address(uniswapV4Exposed)),
|
||||||
usdeBalanceBeforeSwapExecutor - amountIn
|
usdeBalanceBeforeSwapExecutor - amountIn
|
||||||
@@ -239,7 +238,9 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: WETH_ADDR,
|
intermediaryToken: WETH_ADDR,
|
||||||
fee: uint24(500),
|
fee: uint24(500),
|
||||||
tickSpacing: int24(1)
|
tickSpacing: int24(1),
|
||||||
|
hook: hook,
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
|
|
||||||
bytes memory data = UniswapV4Utils.encodeExactInput(
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
@@ -248,8 +249,6 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
true,
|
true,
|
||||||
RestrictTransferFrom.TransferType.Transfer,
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
ALICE,
|
ALICE,
|
||||||
hook,
|
|
||||||
bytes(""),
|
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -267,6 +266,141 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contract UniswapV4ExecutorTestForEuler is Constants, TestUtils {
|
||||||
|
/* These tests are necessary because Euler works a little differently from general UniswapV4 logic.
|
||||||
|
In the previous version of the UniswapV4Executor we are only sending the user's tokens into the Pool Manager
|
||||||
|
after we call swap on it. This is ok because the Pool Manager tracks the debts and accepts everything as long
|
||||||
|
as the tokens are transfers inside of the unlock callback. However, Euler expects the funds to already be
|
||||||
|
in the Pool Manager when beforeSwap is called. This is not a problem for tokens that the Pool Manager has a
|
||||||
|
lot of, but for tokens with low balances this makes the tx fail. We need to transfer the tokens into
|
||||||
|
the Pool Manager before we call swap on it.
|
||||||
|
The only risk here is that we are assuming that the amount_in will never change. In the previous version, we
|
||||||
|
were confirming this amount with the currencyDelta of the Pool Manager. Now we pray.
|
||||||
|
*/
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
UniswapV4ExecutorExposed uniswapV4Exposed;
|
||||||
|
IERC20 USDT = IERC20(USDT_ADDR);
|
||||||
|
IERC20 RLUSD = IERC20(RLUSD_ADDR);
|
||||||
|
IERC20 WBTC = IERC20(WBTC_ADDR);
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
uint256 forkBlock = 23535338;
|
||||||
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
|
uniswapV4Exposed = new UniswapV4ExecutorExposed(
|
||||||
|
IPoolManager(POOL_MANAGER), PERMIT2_ADDRESS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSingleSwapEulerLowBalance() public {
|
||||||
|
uint256 amountIn = 134187695711754971245517404;
|
||||||
|
deal(RLUSD_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
|
address eulerProxy = 0xe1Ce9AF672f8854845E5474400B6ddC7AE458a10;
|
||||||
|
uint256 rlusdEulerBalanceBefore = RLUSD.balanceOf(eulerProxy);
|
||||||
|
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||||
|
new UniswapV4Executor.UniswapV4Pool[](1);
|
||||||
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: USDT_ADDR,
|
||||||
|
fee: uint24(50),
|
||||||
|
tickSpacing: int24(1),
|
||||||
|
hook: address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8),
|
||||||
|
hookData: bytes("")
|
||||||
|
});
|
||||||
|
|
||||||
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
|
RLUSD_ADDR,
|
||||||
|
USDT_ADDR,
|
||||||
|
true,
|
||||||
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
|
ALICE,
|
||||||
|
pools
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
|
assertEq(
|
||||||
|
RLUSD.balanceOf(eulerProxy), rlusdEulerBalanceBefore + amountIn
|
||||||
|
);
|
||||||
|
assertTrue(USDT.balanceOf(ALICE) == amountOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMultipleSwapEulerLowBalance() public {
|
||||||
|
// RLUSD -(euler)-> USDT -> WBTC
|
||||||
|
uint256 amountIn = 134187695711754971245517404;
|
||||||
|
deal(RLUSD_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
|
address eulerProxy = 0xe1Ce9AF672f8854845E5474400B6ddC7AE458a10;
|
||||||
|
uint256 rlusdEulerBalanceBefore = RLUSD.balanceOf(eulerProxy);
|
||||||
|
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||||
|
new UniswapV4Executor.UniswapV4Pool[](2);
|
||||||
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: USDT_ADDR,
|
||||||
|
fee: uint24(50),
|
||||||
|
tickSpacing: int24(1),
|
||||||
|
hook: address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8),
|
||||||
|
hookData: bytes("")
|
||||||
|
});
|
||||||
|
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: WBTC_ADDR,
|
||||||
|
fee: uint24(3000),
|
||||||
|
tickSpacing: int24(60),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
|
});
|
||||||
|
|
||||||
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
|
RLUSD_ADDR,
|
||||||
|
WBTC_ADDR,
|
||||||
|
true,
|
||||||
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
|
ALICE,
|
||||||
|
pools
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
|
assertEq(
|
||||||
|
RLUSD.balanceOf(eulerProxy), rlusdEulerBalanceBefore + amountIn
|
||||||
|
);
|
||||||
|
assertTrue(WBTC.balanceOf(ALICE) == amountOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMultipleSwapLastSwapEuler() public {
|
||||||
|
// USDC -> RLUSD -(euler)- > USDT
|
||||||
|
// Sanity check to see if a grouped swap with Euler in the last hop works
|
||||||
|
uint256 amountIn = 134187695711754971245517404;
|
||||||
|
deal(USDC_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
|
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||||
|
new UniswapV4Executor.UniswapV4Pool[](2);
|
||||||
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: RLUSD_ADDR,
|
||||||
|
fee: uint24(500),
|
||||||
|
tickSpacing: int24(10),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
|
});
|
||||||
|
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: USDT_ADDR,
|
||||||
|
fee: uint24(50),
|
||||||
|
tickSpacing: int24(1),
|
||||||
|
hook: address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8),
|
||||||
|
hookData: bytes("")
|
||||||
|
});
|
||||||
|
|
||||||
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
|
USDC_ADDR,
|
||||||
|
USDT_ADDR,
|
||||||
|
false,
|
||||||
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
|
ALICE,
|
||||||
|
pools
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
|
assertTrue(USDT.balanceOf(ALICE) == amountOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||||
function testSingleSwapUSV4CallbackPermit2() public {
|
function testSingleSwapUSV4CallbackPermit2() public {
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
@@ -282,7 +416,9 @@ contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
|||||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: USDT_ADDR,
|
intermediaryToken: USDT_ADDR,
|
||||||
fee: uint24(100),
|
fee: uint24(100),
|
||||||
tickSpacing: int24(1)
|
tickSpacing: int24(1),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
|
|
||||||
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
||||||
@@ -291,8 +427,6 @@ contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
|||||||
true,
|
true,
|
||||||
RestrictTransferFrom.TransferType.TransferFrom,
|
RestrictTransferFrom.TransferType.TransferFrom,
|
||||||
ALICE,
|
ALICE,
|
||||||
address(0),
|
|
||||||
bytes(""),
|
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -327,12 +461,16 @@ contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
|||||||
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: USDT_ADDR,
|
intermediaryToken: USDT_ADDR,
|
||||||
fee: uint24(100),
|
fee: uint24(100),
|
||||||
tickSpacing: int24(1)
|
tickSpacing: int24(1),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
pools[1] = UniswapV4Executor.UniswapV4Pool({
|
||||||
intermediaryToken: WBTC_ADDR,
|
intermediaryToken: WBTC_ADDR,
|
||||||
fee: uint24(3000),
|
fee: uint24(3000),
|
||||||
tickSpacing: int24(60)
|
tickSpacing: int24(60),
|
||||||
|
hook: address(0),
|
||||||
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
|
|
||||||
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
bytes memory protocolData = UniswapV4Utils.encodeExactInput(
|
||||||
@@ -341,8 +479,6 @@ contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
|||||||
true,
|
true,
|
||||||
RestrictTransferFrom.TransferType.TransferFrom,
|
RestrictTransferFrom.TransferType.TransferFrom,
|
||||||
ALICE,
|
ALICE,
|
||||||
address(0),
|
|
||||||
bytes(""),
|
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ library UniswapV4Utils {
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
RestrictTransferFrom.TransferType transferType,
|
RestrictTransferFrom.TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
address hook,
|
|
||||||
bytes memory hookData,
|
|
||||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||||
) public pure returns (bytes memory) {
|
) public pure returns (bytes memory) {
|
||||||
require(pools.length > 0, "Must have at least one pool");
|
require(pools.length > 0, "Must have at least one pool");
|
||||||
@@ -19,7 +17,10 @@ library UniswapV4Utils {
|
|||||||
bytes memory firstPool = abi.encodePacked(
|
bytes memory firstPool = abi.encodePacked(
|
||||||
pools[0].intermediaryToken,
|
pools[0].intermediaryToken,
|
||||||
bytes3(pools[0].fee),
|
bytes3(pools[0].fee),
|
||||||
pools[0].tickSpacing
|
pools[0].tickSpacing,
|
||||||
|
pools[0].hook,
|
||||||
|
bytes2(uint16(pools[0].hookData.length)),
|
||||||
|
pools[0].hookData
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes[] memory encodedExtraPools = new bytes[](pools.length - 1);
|
bytes[] memory encodedExtraPools = new bytes[](pools.length - 1);
|
||||||
@@ -27,7 +28,10 @@ library UniswapV4Utils {
|
|||||||
encodedExtraPools[i - 1] = abi.encodePacked(
|
encodedExtraPools[i - 1] = abi.encodePacked(
|
||||||
pools[i].intermediaryToken,
|
pools[i].intermediaryToken,
|
||||||
bytes3(pools[i].fee),
|
bytes3(pools[i].fee),
|
||||||
pools[i].tickSpacing
|
pools[i].tickSpacing,
|
||||||
|
pools[i].hook,
|
||||||
|
bytes2(uint16(pools[i].hookData.length)),
|
||||||
|
pools[i].hookData
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,10 +41,8 @@ library UniswapV4Utils {
|
|||||||
zeroForOne,
|
zeroForOne,
|
||||||
transferType,
|
transferType,
|
||||||
receiver,
|
receiver,
|
||||||
hook,
|
|
||||||
firstPool,
|
firstPool,
|
||||||
pleEncode(encodedExtraPools),
|
pleEncode(encodedExtraPools)
|
||||||
hookData
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,23 +189,24 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
Ok(hook) => Address::from_slice(&hook),
|
Ok(hook) => Address::from_slice(&hook),
|
||||||
Err(_) => Address::ZERO,
|
Err(_) => Address::ZERO,
|
||||||
};
|
};
|
||||||
let mut hook_data = AlloyBytes::new();
|
|
||||||
if encoding_context.group_token_out == swap.token_out {
|
let hook_data = swap
|
||||||
// Add hook data if it's only the last swap
|
.user_data
|
||||||
hook_data = AlloyBytes::from(
|
|
||||||
swap.user_data
|
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_vec(),
|
.to_vec();
|
||||||
);
|
|
||||||
}
|
let hook_data_length = (hook_data.len() as u16).to_be_bytes();
|
||||||
|
|
||||||
// Early check if this is not the first swap
|
// Early check if this is not the first swap
|
||||||
if encoding_context.group_token_in != swap.token_in {
|
if encoding_context.group_token_in != swap.token_in {
|
||||||
return Ok((
|
return Ok((
|
||||||
bytes_to_address(&swap.token_out)?,
|
bytes_to_address(&swap.token_out)?,
|
||||||
pool_fee_u24,
|
pool_fee_u24,
|
||||||
pool_tick_spacing_u24,
|
pool_tick_spacing_u24,
|
||||||
hook_data,
|
hook_address,
|
||||||
|
hook_data_length,
|
||||||
|
AlloyBytes::from(hook_data),
|
||||||
)
|
)
|
||||||
.abi_encode_packed());
|
.abi_encode_packed());
|
||||||
}
|
}
|
||||||
@@ -218,8 +219,15 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
|
|
||||||
let zero_to_one = Self::get_zero_to_one(token_in_address, token_out_address);
|
let zero_to_one = Self::get_zero_to_one(token_in_address, token_out_address);
|
||||||
|
|
||||||
let pool_params =
|
let pool_params = (
|
||||||
(token_out_address, pool_fee_u24, pool_tick_spacing_u24).abi_encode_packed();
|
token_out_address,
|
||||||
|
pool_fee_u24,
|
||||||
|
pool_tick_spacing_u24,
|
||||||
|
hook_address,
|
||||||
|
hook_data_length,
|
||||||
|
AlloyBytes::from(hook_data),
|
||||||
|
)
|
||||||
|
.abi_encode_packed();
|
||||||
|
|
||||||
let args = (
|
let args = (
|
||||||
group_token_in_address,
|
group_token_in_address,
|
||||||
@@ -227,9 +235,7 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
zero_to_one,
|
zero_to_one,
|
||||||
(encoding_context.transfer_type as u8).to_be_bytes(),
|
(encoding_context.transfer_type as u8).to_be_bytes(),
|
||||||
bytes_to_address(&encoding_context.receiver)?,
|
bytes_to_address(&encoding_context.receiver)?,
|
||||||
hook_address,
|
|
||||||
pool_params,
|
pool_params,
|
||||||
hook_data,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(args.abi_encode_packed())
|
Ok(args.abi_encode_packed())
|
||||||
@@ -1249,15 +1255,17 @@ mod tests {
|
|||||||
"01",
|
"01",
|
||||||
// receiver
|
// receiver
|
||||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
||||||
// hook address (not set, so zero)
|
|
||||||
"0000000000000000000000000000000000000000",
|
|
||||||
// pool params:
|
// pool params:
|
||||||
// - intermediary token
|
// - intermediary token
|
||||||
"dac17f958d2ee523a2206206994597c13d831ec7",
|
"dac17f958d2ee523a2206206994597c13d831ec7",
|
||||||
// - fee
|
// - fee
|
||||||
"000064",
|
"000064",
|
||||||
// - tick spacing
|
// - tick spacing
|
||||||
"000001"
|
"000001",
|
||||||
|
// hook address (not set, so zero)
|
||||||
|
"0000000000000000000000000000000000000000",
|
||||||
|
// hook data length (0)
|
||||||
|
"0000"
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
write_calldata_to_file("test_encode_uniswap_v4_simple_swap", hex_swap.as_str());
|
write_calldata_to_file("test_encode_uniswap_v4_simple_swap", hex_swap.as_str());
|
||||||
@@ -1315,7 +1323,11 @@ mod tests {
|
|||||||
// - fee (3 bytes)
|
// - fee (3 bytes)
|
||||||
"000bb8",
|
"000bb8",
|
||||||
// - tick spacing (3 bytes)
|
// - tick spacing (3 bytes)
|
||||||
"00003c"
|
"00003c",
|
||||||
|
// hook address (not set, so zero)
|
||||||
|
"0000000000000000000000000000000000000000",
|
||||||
|
// hook data length (0)
|
||||||
|
"0000"
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1414,8 +1426,6 @@ mod tests {
|
|||||||
"01",
|
"01",
|
||||||
// receiver
|
// receiver
|
||||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
||||||
// hook address (not set, so zero)
|
|
||||||
"0000000000000000000000000000000000000000",
|
|
||||||
// pool params:
|
// pool params:
|
||||||
// - intermediary token USDT
|
// - intermediary token USDT
|
||||||
"dac17f958d2ee523a2206206994597c13d831ec7",
|
"dac17f958d2ee523a2206206994597c13d831ec7",
|
||||||
@@ -1423,15 +1433,23 @@ mod tests {
|
|||||||
"000064",
|
"000064",
|
||||||
// - tick spacing
|
// - tick spacing
|
||||||
"000001",
|
"000001",
|
||||||
|
// hook address (not set, so zero)
|
||||||
|
"0000000000000000000000000000000000000000",
|
||||||
|
// hook data length (0)
|
||||||
|
"0000",
|
||||||
// Second swap
|
// Second swap
|
||||||
// ple encoding
|
// ple encoding
|
||||||
"001a",
|
"0030",
|
||||||
// - intermediary token WBTC
|
// - intermediary token WBTC
|
||||||
"2260fac5e5542a773aa44fbcfedf7c193bc2c599",
|
"2260fac5e5542a773aa44fbcfedf7c193bc2c599",
|
||||||
// - fee
|
// - fee
|
||||||
"000bb8",
|
"000bb8",
|
||||||
// - tick spacing
|
// - tick spacing
|
||||||
"00003c"
|
"00003c",
|
||||||
|
// hook address (not set, so zero)
|
||||||
|
"0000000000000000000000000000000000000000",
|
||||||
|
// hook data length (0)
|
||||||
|
"0000"
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
write_calldata_to_file("test_encode_uniswap_v4_sequential_swap", combined_hex.as_str());
|
write_calldata_to_file("test_encode_uniswap_v4_sequential_swap", combined_hex.as_str());
|
||||||
|
|||||||
@@ -1247,22 +1247,28 @@ mod tests {
|
|||||||
"01",
|
"01",
|
||||||
// receiver
|
// receiver
|
||||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
||||||
// hook address (not set, so zero)
|
|
||||||
"0000000000000000000000000000000000000000",
|
|
||||||
// first pool intermediary token (ETH)
|
// first pool intermediary token (ETH)
|
||||||
"0000000000000000000000000000000000000000",
|
"0000000000000000000000000000000000000000",
|
||||||
// fee
|
// fee
|
||||||
"000bb8",
|
"000bb8",
|
||||||
// tick spacing
|
// tick spacing
|
||||||
"00003c",
|
"00003c",
|
||||||
|
// hook address (not set, so zero)
|
||||||
|
"0000000000000000000000000000000000000000",
|
||||||
|
// hook data length (0)
|
||||||
|
"0000",
|
||||||
// ple encoding
|
// ple encoding
|
||||||
"001a",
|
"0030",
|
||||||
// second pool intermediary token (PEPE)
|
// second pool intermediary token (PEPE)
|
||||||
"6982508145454ce325ddbe47a25d4ec3d2311933",
|
"6982508145454ce325ddbe47a25d4ec3d2311933",
|
||||||
// fee
|
// fee
|
||||||
"0061a8",
|
"0061a8",
|
||||||
// tick spacing
|
// tick spacing
|
||||||
"0001f4"
|
"0001f4",
|
||||||
|
// hook address (not set, so zero)
|
||||||
|
"0000000000000000000000000000000000000000",
|
||||||
|
// hook data length (0)
|
||||||
|
"0000",
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ fn test_single_encoding_strategy_usv4_grouped_swap() {
|
|||||||
|
|
||||||
let expected_swaps = String::from(concat!(
|
let expected_swaps = String::from(concat!(
|
||||||
// length of ple encoded swaps without padding
|
// length of ple encoded swaps without padding
|
||||||
"000000000000000000000000000000000000000000000000000000000000009c",
|
"00000000000000000000000000000000000000000000000000000000000000b4",
|
||||||
// Swap data header
|
// Swap data header
|
||||||
"f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address
|
"f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address
|
||||||
// Protocol data
|
// Protocol data
|
||||||
@@ -366,18 +366,21 @@ fn test_single_encoding_strategy_usv4_grouped_swap() {
|
|||||||
"00", // zero2one
|
"00", // zero2one
|
||||||
"00", // transfer type TransferFrom
|
"00", // transfer type TransferFrom
|
||||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||||
"0000000000000000000000000000000000000000", // hook address
|
|
||||||
// First pool params
|
// First pool params
|
||||||
"0000000000000000000000000000000000000000", // intermediary token (ETH)
|
"0000000000000000000000000000000000000000", // intermediary token (ETH)
|
||||||
"000bb8", // fee
|
"000bb8", // fee
|
||||||
"00003c", // tick spacing
|
"00003c", // tick spacing
|
||||||
|
"0000000000000000000000000000000000000000", // hook address
|
||||||
|
"0000", // hook data length
|
||||||
// ple encoding
|
// ple encoding
|
||||||
"001a",
|
"0030",
|
||||||
// Second pool params
|
// Second pool params
|
||||||
"6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE)
|
"6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE)
|
||||||
"0061a8", // fee
|
"0061a8", // fee
|
||||||
"0001f4", // tick spacing
|
"0001f4", // tick spacing
|
||||||
"00000000" // padding
|
"0000000000000000000000000000000000000000", // hook address
|
||||||
|
"0000", // hook data length
|
||||||
|
"000000000000000000000000" // padding
|
||||||
));
|
));
|
||||||
|
|
||||||
let hex_calldata = encode(&calldata);
|
let hex_calldata = encode(&calldata);
|
||||||
|
|||||||
Reference in New Issue
Block a user