feat(univ4): Pass user_data as hook_data in execution
Because we don't know the size of hook data, it needs to be at the end of the protocol data. But we also don't know the size of the intermediary swaps. To solve this, we are now ple encoding the intermediary swaps and only then appending the hook data Took 2 hours 50 minutes Took 40 seconds
This commit is contained in:
committed by
Diana Carvalho
parent
a0581773cd
commit
93678d9d19
@@ -24,6 +24,7 @@ import {TransientStateLibrary} from
|
||||
"@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
|
||||
import "../RestrictTransferFrom.sol";
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
import "../../lib/bytes/LibPrefixLengthEncodedByteArray.sol";
|
||||
|
||||
error UniswapV4Executor__InvalidDataLength();
|
||||
error UniswapV4Executor__NotPoolManager();
|
||||
@@ -44,6 +45,7 @@ contract UniswapV4Executor is
|
||||
using CurrencyLibrary for Currency;
|
||||
using SafeCast for *;
|
||||
using TransientStateLibrary for IPoolManager;
|
||||
using LibPrefixLengthEncodedByteArray for bytes;
|
||||
|
||||
IPoolManager public immutable poolManager;
|
||||
address private immutable _self;
|
||||
@@ -86,6 +88,7 @@ contract UniswapV4Executor is
|
||||
TransferType transferType,
|
||||
address receiver,
|
||||
address hook,
|
||||
bytes memory hookData,
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||
) = _decodeData(data);
|
||||
bytes memory swapData;
|
||||
@@ -104,7 +107,7 @@ contract UniswapV4Executor is
|
||||
amountIn,
|
||||
transferType,
|
||||
receiver,
|
||||
bytes("")
|
||||
hookData
|
||||
);
|
||||
} else {
|
||||
PathKey[] memory path = new PathKey[](pools.length);
|
||||
@@ -114,7 +117,7 @@ contract UniswapV4Executor is
|
||||
fee: pools[i].fee,
|
||||
tickSpacing: pools[i].tickSpacing,
|
||||
hooks: IHooks(hook),
|
||||
hookData: bytes("")
|
||||
hookData: hookData
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,6 +148,7 @@ contract UniswapV4Executor is
|
||||
TransferType transferType,
|
||||
address receiver,
|
||||
address hook,
|
||||
bytes memory hookData,
|
||||
UniswapV4Pool[] memory pools
|
||||
)
|
||||
{
|
||||
@@ -159,24 +163,40 @@ contract UniswapV4Executor is
|
||||
receiver = address(bytes20(data[42:62]));
|
||||
hook = address(bytes20(data[62:82]));
|
||||
|
||||
uint256 poolsLength = (data.length - 82) / 26; // 26 bytes per pool object
|
||||
pools = new UniswapV4Pool[](poolsLength);
|
||||
bytes memory poolsData = data[82:];
|
||||
uint256 offset = 0;
|
||||
for (uint256 i = 0; i < poolsLength; i++) {
|
||||
bytes calldata remaining = data[82:];
|
||||
address firstToken = address(bytes20(remaining[0:20]));
|
||||
uint24 firstFee = uint24(bytes3(remaining[20:23]));
|
||||
int24 firstTickSpacing = int24(uint24(bytes3(remaining[23:26])));
|
||||
UniswapV4Pool memory firstPool =
|
||||
UniswapV4Pool(firstToken, firstFee, firstTickSpacing);
|
||||
|
||||
// Remaining after first pool are ple encoded
|
||||
bytes[] memory encodedPools =
|
||||
LibPrefixLengthEncodedByteArray.toArray(remaining[26:]);
|
||||
|
||||
pools = new UniswapV4Pool[](1 + encodedPools.length);
|
||||
pools[0] = firstPool;
|
||||
|
||||
uint256 encodedPoolsLength = 26;
|
||||
uint256 plePoolsTotalLength;
|
||||
|
||||
for (uint256 i = 0; i < encodedPools.length; i++) {
|
||||
bytes memory poolsData = encodedPools[i];
|
||||
address intermediaryToken;
|
||||
uint24 fee;
|
||||
int24 tickSpacing;
|
||||
|
||||
// slither-disable-next-line assembly
|
||||
assembly {
|
||||
intermediaryToken := mload(add(poolsData, add(offset, 20)))
|
||||
fee := shr(232, mload(add(poolsData, add(offset, 52))))
|
||||
tickSpacing := shr(232, mload(add(poolsData, add(offset, 55))))
|
||||
intermediaryToken := mload(add(poolsData, add(0, 20)))
|
||||
fee := shr(232, mload(add(poolsData, add(0, 52))))
|
||||
tickSpacing := shr(232, mload(add(poolsData, add(0, 55))))
|
||||
}
|
||||
pools[i] = UniswapV4Pool(intermediaryToken, fee, tickSpacing);
|
||||
offset += 26;
|
||||
pools[i + 1] = UniswapV4Pool(intermediaryToken, fee, tickSpacing);
|
||||
plePoolsTotalLength += 2 + encodedPoolsLength; // 2 bytes prefix + data
|
||||
}
|
||||
|
||||
hookData = remaining[26 + plePoolsTotalLength:];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor {
|
||||
RestrictTransferFrom.TransferType transferType,
|
||||
address receiver,
|
||||
address hook,
|
||||
bytes memory hookData,
|
||||
UniswapV4Pool[] memory pools
|
||||
)
|
||||
{
|
||||
@@ -77,6 +78,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
||||
RestrictTransferFrom.TransferType.Transfer,
|
||||
ALICE,
|
||||
address(0),
|
||||
bytes(""),
|
||||
pools
|
||||
);
|
||||
|
||||
@@ -87,6 +89,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
||||
RestrictTransferFrom.TransferType transferType,
|
||||
address receiver,
|
||||
address hook,
|
||||
bytes memory hookData,
|
||||
UniswapV4Executor.UniswapV4Pool[] memory decodedPools
|
||||
) = uniswapV4Exposed.decodeData(data);
|
||||
|
||||
@@ -130,6 +133,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
||||
RestrictTransferFrom.TransferType.Transfer,
|
||||
ALICE,
|
||||
address(0),
|
||||
bytes(""),
|
||||
pools
|
||||
);
|
||||
|
||||
@@ -188,6 +192,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
||||
RestrictTransferFrom.TransferType.Transfer,
|
||||
ALICE,
|
||||
address(0),
|
||||
bytes(""),
|
||||
pools
|
||||
);
|
||||
|
||||
@@ -244,6 +249,7 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
||||
RestrictTransferFrom.TransferType.Transfer,
|
||||
ALICE,
|
||||
hook,
|
||||
bytes(""),
|
||||
pools
|
||||
);
|
||||
|
||||
|
||||
@@ -11,13 +11,20 @@ library UniswapV4Utils {
|
||||
RestrictTransferFrom.TransferType transferType,
|
||||
address receiver,
|
||||
address hook,
|
||||
bytes memory hookData,
|
||||
UniswapV4Executor.UniswapV4Pool[] memory pools
|
||||
) public pure returns (bytes memory) {
|
||||
bytes memory encodedPools;
|
||||
require(pools.length > 0, "Must have at least one pool");
|
||||
|
||||
for (uint256 i = 0; i < pools.length; i++) {
|
||||
encodedPools = abi.encodePacked(
|
||||
encodedPools,
|
||||
bytes memory firstPool = abi.encodePacked(
|
||||
pools[0].intermediaryToken,
|
||||
bytes3(pools[0].fee),
|
||||
pools[0].tickSpacing
|
||||
);
|
||||
|
||||
bytes[] memory encodedExtraPools = new bytes[](pools.length - 1);
|
||||
for (uint256 i = 1; i < pools.length; i++) {
|
||||
encodedExtraPools[i - 1] = abi.encodePacked(
|
||||
pools[i].intermediaryToken,
|
||||
bytes3(pools[i].fee),
|
||||
pools[i].tickSpacing
|
||||
@@ -31,7 +38,22 @@ library UniswapV4Utils {
|
||||
transferType,
|
||||
receiver,
|
||||
hook,
|
||||
encodedPools
|
||||
firstPool,
|
||||
pleEncode(encodedExtraPools),
|
||||
hookData
|
||||
);
|
||||
}
|
||||
|
||||
function pleEncode(bytes[] memory data)
|
||||
public
|
||||
pure
|
||||
returns (bytes memory encoded)
|
||||
{
|
||||
for (uint256 i = 0; i < data.length; i++) {
|
||||
encoded = bytes.concat(
|
||||
encoded,
|
||||
abi.encodePacked(bytes2(uint16(data[i].length)), data[i])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user