feat: Support hooks (without special calldata)
Add hook address to encoded data and then use it in execution Add execution test for Euler Took 1 hour 19 minutes Took 2 hours 40 minutes Took 3 minutes Took 2 minutes
This commit is contained in:
committed by
Diana Carvalho
parent
7b48cab3cd
commit
a0581773cd
@@ -85,6 +85,7 @@ contract UniswapV4Executor is
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
|
address hook,
|
||||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||||
) = _decodeData(data);
|
) = _decodeData(data);
|
||||||
bytes memory swapData;
|
bytes memory swapData;
|
||||||
@@ -94,7 +95,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(address(0))
|
hooks: IHooks(hook)
|
||||||
});
|
});
|
||||||
swapData = abi.encodeWithSelector(
|
swapData = abi.encodeWithSelector(
|
||||||
this.swapExactInputSingle.selector,
|
this.swapExactInputSingle.selector,
|
||||||
@@ -112,7 +113,7 @@ 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(address(0)),
|
hooks: IHooks(hook),
|
||||||
hookData: bytes("")
|
hookData: bytes("")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -143,10 +144,11 @@ contract UniswapV4Executor is
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
TransferType transferType,
|
TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
|
address hook,
|
||||||
UniswapV4Pool[] memory pools
|
UniswapV4Pool[] memory pools
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (data.length < 88) {
|
if (data.length < 108) {
|
||||||
revert UniswapV4Executor__InvalidDataLength();
|
revert UniswapV4Executor__InvalidDataLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +157,11 @@ 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]));
|
||||||
|
|
||||||
uint256 poolsLength = (data.length - 62) / 26; // 26 bytes per pool object
|
uint256 poolsLength = (data.length - 82) / 26; // 26 bytes per pool object
|
||||||
pools = new UniswapV4Pool[](poolsLength);
|
pools = new UniswapV4Pool[](poolsLength);
|
||||||
bytes memory poolsData = data[62:];
|
bytes memory poolsData = data[82:];
|
||||||
uint256 offset = 0;
|
uint256 offset = 0;
|
||||||
for (uint256 i = 0; i < poolsLength; i++) {
|
for (uint256 i = 0; i < poolsLength; i++) {
|
||||||
address intermediaryToken;
|
address intermediaryToken;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
RestrictTransferFrom.TransferType transferType,
|
RestrictTransferFrom.TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
|
address hook,
|
||||||
UniswapV4Pool[] memory pools
|
UniswapV4Pool[] memory pools
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -37,10 +38,12 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
UniswapV4ExecutorExposed uniswapV4Exposed;
|
UniswapV4ExecutorExposed uniswapV4Exposed;
|
||||||
IERC20 USDE = IERC20(USDE_ADDR);
|
IERC20 USDE = IERC20(USDE_ADDR);
|
||||||
IERC20 USDT = IERC20(USDT_ADDR);
|
IERC20 USDT = IERC20(USDT_ADDR);
|
||||||
|
IERC20 USDC = IERC20(USDC_ADDR);
|
||||||
|
|
||||||
address poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
address poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
uint256 forkBlock = 21817316;
|
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(poolManager), PERMIT2_ADDRESS
|
||||||
@@ -73,6 +76,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
zeroForOne,
|
zeroForOne,
|
||||||
RestrictTransferFrom.TransferType.Transfer,
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
ALICE,
|
ALICE,
|
||||||
|
address(0),
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -82,6 +86,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
bool zeroForOneDecoded,
|
bool zeroForOneDecoded,
|
||||||
RestrictTransferFrom.TransferType transferType,
|
RestrictTransferFrom.TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
|
address hook,
|
||||||
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
|
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
|
||||||
) = uniswapV4Exposed.decodeData(data);
|
) = uniswapV4Exposed.decodeData(data);
|
||||||
|
|
||||||
@@ -93,6 +98,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.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);
|
||||||
@@ -123,6 +129,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
true,
|
true,
|
||||||
RestrictTransferFrom.TransferType.Transfer,
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
ALICE,
|
ALICE,
|
||||||
|
address(0),
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -180,6 +187,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
true,
|
true,
|
||||||
RestrictTransferFrom.TransferType.Transfer,
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
ALICE,
|
ALICE,
|
||||||
|
address(0),
|
||||||
pools
|
pools
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -211,6 +219,42 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
|||||||
);
|
);
|
||||||
assertTrue(IERC20(WBTC_ADDR).balanceOf(ALICE) == amountOut);
|
assertTrue(IERC20(WBTC_ADDR).balanceOf(ALICE) == amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testSingleSwapEulerHook() public {
|
||||||
|
// Replicating tx: 0xb372306a81c6e840f4ec55f006da6b0b097f435802a2e6fd216998dd12fb4aca
|
||||||
|
address hook = address(0x69058613588536167BA0AA94F0CC1Fe420eF28a8);
|
||||||
|
|
||||||
|
uint256 amountIn = 7407000000;
|
||||||
|
deal(USDC_ADDR, address(uniswapV4Exposed), amountIn);
|
||||||
|
uint256 usdcBalanceBeforeSwapExecutor =
|
||||||
|
USDC.balanceOf(address(uniswapV4Exposed));
|
||||||
|
|
||||||
|
UniswapV4Executor.UniswapV4Pool[] memory pools =
|
||||||
|
new UniswapV4Executor.UniswapV4Pool[](1);
|
||||||
|
pools[0] = UniswapV4Executor.UniswapV4Pool({
|
||||||
|
intermediaryToken: WETH_ADDR,
|
||||||
|
fee: uint24(500),
|
||||||
|
tickSpacing: int24(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
bytes memory data = UniswapV4Utils.encodeExactInput(
|
||||||
|
USDC_ADDR,
|
||||||
|
WETH_ADDR,
|
||||||
|
true,
|
||||||
|
RestrictTransferFrom.TransferType.Transfer,
|
||||||
|
ALICE,
|
||||||
|
hook,
|
||||||
|
pools
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);
|
||||||
|
assertEq(amountOut, 2681115183499232721);
|
||||||
|
assertEq(
|
||||||
|
USDC.balanceOf(address(uniswapV4Exposed)),
|
||||||
|
usdcBalanceBeforeSwapExecutor - amountIn
|
||||||
|
);
|
||||||
|
assertTrue(IERC20(WETH_ADDR).balanceOf(ALICE) == amountOut);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ library UniswapV4Utils {
|
|||||||
bool zeroForOne,
|
bool zeroForOne,
|
||||||
RestrictTransferFrom.TransferType transferType,
|
RestrictTransferFrom.TransferType transferType,
|
||||||
address receiver,
|
address receiver,
|
||||||
|
address hook,
|
||||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||||
) public pure returns (bytes memory) {
|
) public pure returns (bytes memory) {
|
||||||
bytes memory encodedPools;
|
bytes memory encodedPools;
|
||||||
@@ -24,7 +25,13 @@ library UniswapV4Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return abi.encodePacked(
|
return abi.encodePacked(
|
||||||
tokenIn, tokenOut, zeroForOne, transferType, receiver, encodedPools
|
tokenIn,
|
||||||
|
tokenOut,
|
||||||
|
zeroForOne,
|
||||||
|
transferType,
|
||||||
|
receiver,
|
||||||
|
hook,
|
||||||
|
encodedPools
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,6 +176,11 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
EncodingError::FatalError("Failed to pad tick spacing bytes".to_string())
|
EncodingError::FatalError("Failed to pad tick spacing bytes".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let hook_address = match get_static_attribute(&swap, "hook") {
|
||||||
|
Ok(hook) => Address::from_slice(&hook),
|
||||||
|
Err(_) => Address::ZERO,
|
||||||
|
};
|
||||||
|
|
||||||
// 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((bytes_to_address(&swap.token_out)?, pool_fee_u24, pool_tick_spacing_u24)
|
return Ok((bytes_to_address(&swap.token_out)?, pool_fee_u24, pool_tick_spacing_u24)
|
||||||
@@ -199,6 +204,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,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -897,6 +903,8 @@ 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",
|
||||||
@@ -1070,6 +1078,8 @@ 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",
|
||||||
|
|||||||
@@ -1253,6 +1253,8 @@ 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
|
||||||
|
|||||||
Reference in New Issue
Block a user