Skip to content

Commit 74721c3

Browse files
authored
Merge pull request #58 from KyberNetwork/cantina-ai-iteration-1
fix: findings from Cantina AI
2 parents 64aef49 + c608a0b commit 74721c3

File tree

6 files changed

+192
-1
lines changed

6 files changed

+192
-1
lines changed

src/KSSmartIntentRouterAccounting.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ abstract contract KSSmartIntentRouterAccounting is KSSmartIntentStorage, Managem
7272
actionData.feeInfo.protocolRecipient
7373
);
7474
}
75-
approvalFlags >>= tokenData.erc20Data.length;
75+
approvalFlags >>= actionData.erc20Ids.length;
7676

7777
for (uint256 i = 0; i < actionData.erc721Ids.length; i++) {
7878
address token = tokenData.erc721Data[actionData.erc721Ids[i]].token;

src/hooks/base/BaseTickBasedRemoveLiquidityHook.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ abstract contract BaseTickBasedRemoveLiquidityHook is BaseConditionalHook {
2323
error NotEnoughOutputAmount();
2424
error NotEnoughFeesReceived();
2525
error ExceedMaxFeesPercent();
26+
error InvalidERC721Data();
2627

2728
uint256 public constant Q128 = 1 << 128;
2829
address public immutable WETH;
@@ -303,6 +304,15 @@ abstract contract BaseTickBasedRemoveLiquidityHook is BaseConditionalHook {
303304
);
304305
}
305306

307+
function _validateERC721Data(
308+
address nftAddress,
309+
uint256 nftId,
310+
address tokenAddress,
311+
uint256 tokenId
312+
) internal view virtual {
313+
require(nftAddress == tokenAddress && nftId == tokenId, InvalidERC721Data());
314+
}
315+
306316
function _getPositionLiquidity(address nftAddress, uint256 nftId)
307317
internal
308318
view

src/hooks/remove-liq/KSRemoveLiquidityPancakeV4CLHook.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ contract KSRemoveLiquidityPancakeV4CLHook is BaseTickBasedRemoveLiquidityHook {
4747

4848
_cacheValidationData(pancakeCL, validationData, actionData.hookActionData);
4949

50+
_validateERC721Data(
51+
pancakeCL.removeLiqParams.positionInfo.nftAddress,
52+
pancakeCL.removeLiqParams.positionInfo.nftId,
53+
intentData.tokenData.erc721Data[actionData.erc721Ids[0]].token,
54+
intentData.tokenData.erc721Data[actionData.erc721Ids[0]].tokenId
55+
);
56+
5057
_validateConditions(
5158
validationData.nodes[pancakeCL.removeLiqParams.index],
5259
pancakeCL.removeLiqParams.positionInfo.feesGenerated[0],

src/hooks/remove-liq/KSRemoveLiquidityUniswapV3Hook.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ contract KSRemoveLiquidityUniswapV3Hook is BaseTickBasedRemoveLiquidityHook {
4646

4747
_cacheValidationData(uniswapV3, validationData, actionData.hookActionData);
4848

49+
_validateERC721Data(
50+
uniswapV3.removeLiqParams.positionInfo.nftAddress,
51+
uniswapV3.removeLiqParams.positionInfo.nftId,
52+
intentData.tokenData.erc721Data[actionData.erc721Ids[0]].token,
53+
intentData.tokenData.erc721Data[actionData.erc721Ids[0]].tokenId
54+
);
55+
4956
_validateConditions(
5057
validationData.nodes[uniswapV3.removeLiqParams.index],
5158
uniswapV3.removeLiqParams.positionInfo.feesGenerated[0],

src/hooks/remove-liq/KSRemoveLiquidityUniswapV4Hook.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ contract KSRemoveLiquidityUniswapV4Hook is BaseTickBasedRemoveLiquidityHook {
4646

4747
_cacheValidationData(uniswapV4, validationData, actionData.hookActionData);
4848

49+
_validateERC721Data(
50+
uniswapV4.removeLiqParams.positionInfo.nftAddress,
51+
uniswapV4.removeLiqParams.positionInfo.nftId,
52+
intentData.tokenData.erc721Data[actionData.erc721Ids[0]].token,
53+
intentData.tokenData.erc721Data[actionData.erc721Ids[0]].tokenId
54+
);
55+
4956
_validateConditions(
5057
validationData.nodes[uniswapV4.removeLiqParams.index],
5158
uniswapV4.removeLiqParams.positionInfo.feesGenerated[0],

test/RemoveLiquidityUniswapV3.t.sol

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,166 @@ contract RemoveLiquidityUniswapV3Test is BaseTest {
412412
}
413413
}
414414

415+
function testRevert_InvalidERC721Data() public {
416+
{
417+
address posOwner = IUniswapV3PM(pm).ownerOf(963_350);
418+
vm.prank(posOwner);
419+
IERC721(pm).safeTransferFrom(posOwner, mainAddress, 963_350);
420+
}
421+
{ // Claim all fees of two positions
422+
vm.startPrank(mainAddress);
423+
bytes[] memory claimCalldata = new bytes[](2);
424+
claimCalldata[0] = abi.encodeWithSelector(
425+
IUniswapV3PM.collect.selector,
426+
IUniswapV3PM.CollectParams({
427+
tokenId: tokenId,
428+
recipient: mainAddress,
429+
amount0Max: type(uint128).max,
430+
amount1Max: type(uint128).max
431+
})
432+
);
433+
claimCalldata[1] = abi.encodeWithSelector(
434+
IUniswapV3PM.collect.selector,
435+
IUniswapV3PM.CollectParams({
436+
tokenId: 963_350,
437+
recipient: mainAddress,
438+
amount0Max: type(uint128).max,
439+
amount1Max: type(uint128).max
440+
})
441+
);
442+
443+
IUniswapV3PM(pm).multicall(claimCalldata);
444+
vm.stopPrank();
445+
}
446+
447+
bytes[] memory multiCalldata;
448+
449+
multiCalldata = new bytes[](5);
450+
multiCalldata[0] = abi.encodeWithSelector(
451+
IUniswapV3PM.decreaseLiquidity.selector,
452+
IUniswapV3PM.DecreaseLiquidityParams({
453+
tokenId: tokenId,
454+
liquidity: uint128(uniswapV3.removeLiqParams.liquidityToRemove),
455+
amount0Min: 0,
456+
amount1Min: 0,
457+
deadline: block.timestamp + 1 days
458+
})
459+
);
460+
multiCalldata[1] = abi.encodeWithSelector(
461+
IUniswapV3PM.collect.selector,
462+
IUniswapV3PM.CollectParams({
463+
tokenId: tokenId,
464+
recipient: address(pm),
465+
amount0Max: type(uint128).max,
466+
amount1Max: type(uint128).max
467+
})
468+
);
469+
multiCalldata[2] = abi.encodeWithSelector(
470+
IERC721.transferFrom.selector, address(forwarder), mainAddress, tokenId
471+
);
472+
multiCalldata[3] = abi.encodeWithSelector(IUniswapV3PM.unwrapWETH9.selector, 0, address(router));
473+
multiCalldata[4] = abi.encodeWithSelector(
474+
IUniswapV3PM.sweepToken.selector, uniswapV3.outputParams.tokens[0], 0, address(router)
475+
);
476+
477+
IntentData memory intentData;
478+
{
479+
KSRemoveLiquidityUniswapV3Hook.RemoveLiquidityHookData memory hookData;
480+
hookData.nftAddresses = new address[](1);
481+
hookData.nftAddresses[0] = address(pm);
482+
hookData.nftIds = new uint256[](1);
483+
hookData.nftIds[0] = 963_350;
484+
hookData.maxFees = new uint256[](1);
485+
hookData.maxFees[0] = (maxFeePercents << 128) | maxFeePercents;
486+
hookData.recipient = mainAddress;
487+
488+
address[] memory pools = new address[](1);
489+
pools[0] = address(pool);
490+
hookData.additionalData = abi.encode(pools);
491+
492+
hookData.nodes = new Node[][](1);
493+
// Only one time condition true, no child conditions needed
494+
Condition memory condition = _createTimeCondition(true);
495+
496+
Node[] memory nodes = new Node[](1);
497+
nodes[0] = _createLeafNode(condition); // leaf node with time condition true
498+
499+
hookData.nodes[0] = nodes;
500+
501+
hookData.recipient = mainAddress;
502+
503+
address[] memory actionContracts = new address[](2);
504+
actionContracts[0] = address(mockActionContract);
505+
actionContracts[1] = address(pm);
506+
507+
bytes4[] memory actionSelectors = new bytes4[](2);
508+
actionSelectors[0] = MockActionContract.removeUniswapV3.selector;
509+
actionSelectors[1] = IUniswapV3PM.multicall.selector;
510+
511+
IntentCoreData memory coreData = IntentCoreData({
512+
mainAddress: mainAddress,
513+
signatureVerifier: address(0),
514+
delegatedKey: delegatedPublicKey,
515+
actionContracts: actionContracts,
516+
actionSelectors: actionSelectors,
517+
hook: address(rmLqValidator),
518+
hookIntentData: abi.encode(hookData)
519+
});
520+
521+
TokenData memory tokenData;
522+
tokenData.erc721Data = new ERC721Data[](1);
523+
tokenData.erc721Data[0] = ERC721Data({token: address(pm), tokenId: tokenId, permitData: ''});
524+
525+
intentData = IntentData({coreData: coreData, tokenData: tokenData, extraData: ''});
526+
}
527+
528+
_setUpMainAddress(intentData, false, tokenId);
529+
530+
FeeInfo memory feeInfo;
531+
{
532+
feeInfo.protocolRecipient = protocolRecipient;
533+
feeInfo.partnerFeeConfigs = new FeeConfig[][](2);
534+
feeInfo.partnerFeeConfigs[0] = _buildPartnersConfigs(
535+
PartnersFeeConfigBuildParams({
536+
feeModes: [false, true].toMemoryArray(),
537+
partnerFees: [0.25e6, 0.25e6].toMemoryArray(),
538+
partnerRecipients: [partnerRecipient, makeAddr('partnerRecipient2')].toMemoryArray()
539+
})
540+
);
541+
542+
feeInfo.partnerFeeConfigs[1] = feeInfo.partnerFeeConfigs[0];
543+
}
544+
545+
ActionData memory actionData;
546+
{
547+
actionData = ActionData({
548+
erc20Ids: new uint256[](0),
549+
erc20Amounts: new uint256[](0),
550+
erc721Ids: [uint256(0)].toMemoryArray(),
551+
feeInfo: feeInfo,
552+
actionSelectorId: 1,
553+
approvalFlags: type(uint256).max,
554+
actionCalldata: abi.encode(multiCalldata),
555+
hookActionData: abi.encode(
556+
0,
557+
uniswapV3.removeLiqParams.positionInfo.unclaimedFees[0],
558+
uniswapV3.removeLiqParams.positionInfo.unclaimedFees[1],
559+
0,
560+
false,
561+
(intentFeesPercent0 << 128) | intentFeesPercent1
562+
),
563+
extraData: '',
564+
deadline: block.timestamp + 1 days,
565+
nonce: 0
566+
});
567+
}
568+
569+
(, bytes memory dkSignature, bytes memory gdSignature) =
570+
_getCallerAndSignatures(0, intentData, actionData);
571+
vm.expectRevert(BaseTickBasedRemoveLiquidityHook.InvalidERC721Data.selector);
572+
router.execute(intentData, dkSignature, guardian, gdSignature, actionData);
573+
}
574+
415575
function buildConditionTree(
416576
Node[] calldata nodes,
417577
uint256 fee0Collected,

0 commit comments

Comments
 (0)