Skip to content

Commit b8885c1

Browse files
authored
Merge pull request #52 from KyberNetwork/hexens_audit_iteration_2
fix: updates from Hexens audit
2 parents 6c0e85b + 2932bbb commit b8885c1

16 files changed

+284
-119
lines changed

src/KSSmartIntentHasher.sol

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@
22
pragma solidity ^0.8.0;
33

44
import {ActionData} from './types/ActionData.sol';
5+
import {ActionWitnessLibrary} from './types/ActionWitness.sol';
56
import {IntentData} from './types/IntentData.sol';
67

78
contract KSSmartIntentHasher {
8-
function hash(IntentData calldata intentData) public pure returns (bytes32) {
9+
function hashIntentData(IntentData calldata intentData) public pure returns (bytes32) {
910
return intentData.hash();
1011
}
1112

12-
function hash(ActionData calldata actionData) public pure returns (bytes32) {
13-
return actionData.hash();
13+
function hashActionWitness(bytes32 intentHash, ActionData calldata actionData)
14+
public
15+
pure
16+
returns (bytes32)
17+
{
18+
return keccak256(
19+
abi.encode(ActionWitnessLibrary.ACTION_WITNESS_TYPE_HASH, intentHash, actionData.hash())
20+
);
1421
}
1522
}

src/KSSmartIntentRouter.sol

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import {KSSmartIntentRouterAccounting} from './KSSmartIntentRouterAccounting.sol
66
import {KSSmartIntentRouterNonces} from './KSSmartIntentRouterNonces.sol';
77

88
import {IKSSmartIntentRouter} from './interfaces/IKSSmartIntentRouter.sol';
9+
import {IKSAllowanceHub} from './interfaces/actions/IKSAllowanceHub.sol';
910
import {IKSSwapRouterV2} from './interfaces/actions/IKSSwapRouterV2.sol';
1011
import {IKSSwapRouterV3} from './interfaces/actions/IKSSwapRouterV3.sol';
1112
import {IKSZapRouter} from './interfaces/actions/IKSZapRouter.sol';
12-
1313
import {HookLibrary} from './libraries/HookLibrary.sol';
1414

1515
import {ActionData} from './types/ActionData.sol';
16+
import {ActionWitness} from './types/ActionWitness.sol';
1617
import {IntentCoreData} from './types/IntentCoreData.sol';
1718
import {IntentData} from './types/IntentData.sol';
1819

@@ -78,7 +79,7 @@ contract KSSmartIntentRouter is
7879
revert NotMainAddress();
7980
}
8081

81-
_delegate(intentData, _hashTypedDataV4(hasher.hash(intentData)));
82+
_delegate(intentData, _hashTypedDataV4(hasher.hashIntentData(intentData)));
8283
}
8384

8485
/// @inheritdoc IKSSmartIntentRouter
@@ -87,7 +88,7 @@ contract KSSmartIntentRouter is
8788
revert NotMainAddress();
8889
}
8990

90-
bytes32 intentHash = _hashTypedDataV4(hasher.hash(intentData));
91+
bytes32 intentHash = _hashTypedDataV4(hasher.hashIntentData(intentData));
9192
intentStatuses[intentHash] = IntentStatus.REVOKED;
9293

9394
emit RevokeIntent(intentHash);
@@ -101,7 +102,7 @@ contract KSSmartIntentRouter is
101102
bytes calldata gdSignature,
102103
ActionData calldata actionData
103104
) public {
104-
bytes32 intentHash = _hashTypedDataV4(hasher.hash(intentData));
105+
bytes32 intentHash = _hashTypedDataV4(hasher.hashIntentData(intentData));
105106
_execute(intentHash, intentData, dkSignature, guardian, gdSignature, actionData);
106107
}
107108

@@ -114,7 +115,7 @@ contract KSSmartIntentRouter is
114115
bytes calldata gdSignature,
115116
ActionData calldata actionData
116117
) public {
117-
bytes32 intentHash = _hashTypedDataV4(hasher.hash(intentData));
118+
bytes32 intentHash = _hashTypedDataV4(hasher.hashIntentData(intentData));
118119
if (!intentData.coreData.mainAddress.isValidSignatureNowCalldata(intentHash, maSignature)) {
119120
revert InvalidMainAddressSignature();
120121
}
@@ -164,11 +165,7 @@ contract KSSmartIntentRouter is
164165
_useUnorderedNonce(intentHash, actionData.nonce);
165166

166167
_validateActionData(
167-
intentData.coreData,
168-
dkSignature,
169-
guardian,
170-
gdSignature,
171-
_hashTypedDataV4(hasher.hash(actionData))
168+
intentHash, intentData.coreData, actionData, dkSignature, guardian, gdSignature
172169
);
173170

174171
(uint256[] memory fees, bytes memory beforeExecutionData) =
@@ -212,40 +209,44 @@ contract KSSmartIntentRouter is
212209
selector == IKSSwapRouterV2.swap.selector
213210
|| selector == IKSSwapRouterV2.swapSimpleMode.selector
214211
|| selector == IKSSwapRouterV3.swap.selector || selector == IKSZapRouter.zap.selector
212+
|| selector == IKSAllowanceHub.permitTransferAndExecute.selector
215213
) {
216214
return IKSGenericForwarder(address(0));
217215
}
218216
return forwarder;
219217
}
220218

221219
function _validateActionData(
220+
bytes32 intentHash,
222221
IntentCoreData calldata coreData,
222+
ActionData calldata actionData,
223223
bytes calldata dkSignature,
224224
address guardian,
225-
bytes calldata gdSignature,
226-
bytes32 actionHash
225+
bytes calldata gdSignature
227226
) internal view {
227+
bytes32 witnessHash = _hashTypedDataV4(hasher.hashActionWitness(intentHash, actionData));
228+
228229
if (coreData.signatureVerifier == address(0)) {
229230
/// @dev use ECDSA scheme
230231
address delegatedAddress = coreData.delegatedKey.decodeAddress();
231232
if (
232233
msg.sender != delegatedAddress
233-
&& !delegatedAddress.isValidSignatureNowCalldata(actionHash, dkSignature)
234+
&& !delegatedAddress.isValidSignatureNowCalldata(witnessHash, dkSignature)
234235
) {
235236
revert InvalidDelegatedKeySignature();
236237
}
237238
} else {
238239
if (
239240
IERC7913SignatureVerifier(coreData.signatureVerifier)
240-
.verify(coreData.delegatedKey, actionHash, dkSignature)
241+
.verify(coreData.delegatedKey, witnessHash, dkSignature)
241242
!= IERC7913SignatureVerifier.verify.selector
242243
) {
243244
revert InvalidDelegatedKeySignature();
244245
}
245246
}
246247

247248
if (msg.sender != guardian) {
248-
if (!guardian.isValidSignatureNowCalldata(actionHash, gdSignature)) {
249+
if (!guardian.isValidSignatureNowCalldata(witnessHash, gdSignature)) {
249250
revert InvalidGuardianSignature();
250251
}
251252
}

src/hooks/base/BaseTickBasedRemoveLiquidityHook.sol

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ abstract contract BaseTickBasedRemoveLiquidityHook is BaseConditionalHook {
2222
error InvalidLiquidity();
2323
error NotEnoughOutputAmount();
2424
error NotEnoughFeesReceived();
25+
error ExceedMaxFeesPercent();
2526

2627
uint256 public constant Q128 = 1 << 128;
2728
address public immutable WETH;
@@ -255,16 +256,29 @@ abstract contract BaseTickBasedRemoveLiquidityHook is BaseConditionalHook {
255256
validationData.maxFees[removeLiqParams.index] >> 128,
256257
uint128(validationData.maxFees[removeLiqParams.index])
257258
];
259+
260+
require(
261+
outputParams.intentFeesPercent[0] <= outputParams.maxFees[0]
262+
&& outputParams.intentFeesPercent[1] <= outputParams.maxFees[1],
263+
ExceedMaxFeesPercent()
264+
);
258265
}
259266

267+
/**
268+
* @notice Validate the conditions of the liquidity removal operation
269+
* @param nodes The nodes of conditions (used to build the condition tree)
270+
* @param fee0Generated The fee0 generated - an offchain component that could contain claimed fees, unclaimed fees, and yield-based fees
271+
* @param fee1Generated The fee1 generated - an offchain component that could contain claimed fees, unclaimed fees, and yield-based fees
272+
* @param poolPrice The price of the pool
273+
*/
260274
function _validateConditions(
261275
Node[] calldata nodes,
262-
uint256 fee0Collected,
263-
uint256 fee1Collected,
276+
uint256 fee0Generated,
277+
uint256 fee1Generated,
264278
uint256 poolPrice
265279
) internal view virtual {
266280
this.validateConditionTree(
267-
_buildConditionTree(nodes, fee0Collected, fee1Collected, poolPrice), 0
281+
_buildConditionTree(nodes, fee0Generated, fee1Generated, poolPrice), 0
268282
);
269283
}
270284

@@ -318,8 +332,8 @@ abstract contract BaseTickBasedRemoveLiquidityHook is BaseConditionalHook {
318332

319333
function _buildConditionTree(
320334
Node[] calldata nodes,
321-
uint256 fee0Collected,
322-
uint256 fee1Collected,
335+
uint256 fee0Generated,
336+
uint256 fee1Generated,
323337
uint256 poolPrice
324338
) internal pure virtual returns (ConditionTree memory conditionTree) {
325339
conditionTree.nodes = nodes;
@@ -329,7 +343,7 @@ abstract contract BaseTickBasedRemoveLiquidityHook is BaseConditionalHook {
329343
continue;
330344
}
331345
if (nodes[i].condition.isType(YIELD_BASED)) {
332-
conditionTree.additionalData[i] = abi.encode(fee0Collected, fee1Collected, poolPrice);
346+
conditionTree.additionalData[i] = abi.encode(fee0Generated, fee1Generated, poolPrice);
333347
} else if (nodes[i].condition.isType(PRICE_BASED)) {
334348
conditionTree.additionalData[i] = abi.encode(poolPrice);
335349
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/// @title IKSAllowanceHub
2+
pragma solidity ^0.8.0;
3+
4+
/// @notice Interface for the KSAllowanceHub
5+
interface IKSAllowanceHub {
6+
/**
7+
* @notice Parameters for collecting ERC20 tokens from `msg.sender`
8+
* @param token The address of the tokens to collect
9+
* @param targets The addresses to transfer the tokens to
10+
* @param amounts The amounts to transfer to each target
11+
* @param permitData The permit data for the tokens
12+
*/
13+
struct ERC20Params {
14+
address token;
15+
address[] targets;
16+
uint256[] amounts;
17+
bytes permitData;
18+
}
19+
20+
/**
21+
* @notice Parameters for collecting an ERC721 token from `msg.sender`
22+
* @param token The address of the token to collect
23+
* @param tokenId The token ID to collect
24+
* @param target The address to transfer the token to
25+
* @param permitData The permit data for the token
26+
*/
27+
struct ERC721Params {
28+
address token;
29+
uint256 tokenId;
30+
address target;
31+
bytes permitData;
32+
}
33+
34+
/**
35+
* @notice Parameters for calling a generic router
36+
* @param router The address of the router
37+
* @param value The value to send along with the call
38+
* @param data The data to call the generic router with
39+
*/
40+
struct GenericCall {
41+
address router;
42+
uint256 value;
43+
bytes data;
44+
}
45+
46+
/**
47+
* @notice Permits, transfers ERC20 and ERC721 tokens, executes generic calls
48+
* @param erc20Params The ERC20 tokens to transfer
49+
* @param erc721Params The ERC721 tokens to transfer
50+
* @param genericCalls The generic calls to execute
51+
* @return results The results of the generic calls
52+
* @return gasUsed The amount of gas used
53+
*/
54+
function permitTransferAndExecute(
55+
ERC20Params[] calldata erc20Params,
56+
ERC721Params[] calldata erc721Params,
57+
GenericCall[] calldata genericCalls
58+
) external payable returns (bytes[] memory results, uint256 gasUsed);
59+
}

src/interfaces/actions/IKSSwapRouterV3.sol

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ interface IKSSwapRouterV3 {
99
/// @param targets The targets to transfer the input token to
1010
/// @param amounts The amounts to transfer to the targets
1111
struct InputTokenData {
12-
// length = 5 * 32: IERC20Permit, use ERC20 `transferFrom`
13-
// length = 6 * 32: IDaiLikePermit, use ERC20 `transferFrom`
14-
// length = 0: use ERC20 `transferFrom`
15-
// otherwise: use Permit2 `transferFrom`
12+
// Permit method selection:
13+
// length = 5 * 32: use ERC20 `permit`
14+
// length = 6 * 32: use DAI `permit`
15+
// Transfer method selection:
16+
// length == 0: use Permit2 `transferFrom`
17+
// length != 0: use ERC20 `transferFrom`
1618
bytes permitData;
1719
address[] feeRecipients;
1820
uint256[] fees;
@@ -31,32 +33,37 @@ interface IKSSwapRouterV3 {
3133
}
3234

3335
/// @notice Contains the parameters for a swap
34-
/// @param permit2Data The data to call permit2 with
3536
/// @param inputTokens The input tokens
36-
/// @param inputAmounts The input amounts
37+
/// @param inputAmounts The input amounts (only used for fee calculation)
3738
/// @param inputData The additional data for the input tokens
3839
/// @param outputTokens The output tokens
3940
/// @param outputData The additional data for the output tokens
41+
/// @param permit2Data The data to call permit2 with
4042
/// @param executor The executor to call
4143
/// @param executorData The data to pass to the executor
4244
/// @param recipient The recipient of the output tokens
45+
/// @param deadline The deadline for the swap
4346
/// @param clientData The client data
4447
struct SwapParams {
45-
bytes permit2Data;
4648
address[] inputTokens;
4749
uint256[] inputAmounts;
4850
InputTokenData[] inputData;
4951
address[] outputTokens;
5052
OutputTokenData[] outputData;
53+
bytes permit2Data;
5154
address executor;
5255
bytes executorData;
5356
address recipient;
57+
uint256 deadline;
5458
bytes clientData;
5559
}
5660

5761
/// @notice Entry point for swapping
5862
/// @param params The parameters for the swap
59-
function swap(SwapParams calldata params) external payable;
63+
function swap(SwapParams calldata params)
64+
external
65+
payable
66+
returns (uint256[] memory outputAmounts, uint256 gasUsed);
6067

6168
/// @notice Returns the address of who called the swap function
6269
function msgSender() external view returns (address);

src/types/ActionWitness.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
pragma solidity ^0.8.0;
3+
4+
import {ActionData} from './ActionData.sol';
5+
6+
struct ActionWitness {
7+
bytes32 intentHash;
8+
ActionData actionData;
9+
}
10+
11+
using ActionWitnessLibrary for ActionWitness global;
12+
13+
library ActionWitnessLibrary {
14+
bytes32 constant ACTION_WITNESS_TYPE_HASH = keccak256(
15+
abi.encodePacked(
16+
'ActionWitness(bytes32 intentHash,ActionData actionData)ActionData(uint256[] erc20Ids,uint256[] erc20Amounts,uint256[] erc721Ids,FeeInfo feeInfo,uint256 approvalFlags,uint256 actionSelectorId,bytes actionCalldata,bytes hookActionData,bytes extraData,uint256 deadline,uint256 nonce)FeeInfo(address protocolRecipient,uint256[][] partnerFeeConfigs)'
17+
)
18+
);
19+
20+
function hash(ActionWitness calldata self) internal pure returns (bytes32) {
21+
return keccak256(abi.encode(ACTION_WITNESS_TYPE_HASH, self.intentHash, self.actionData.hash()));
22+
}
23+
}

test/Base.t.sol

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,28 +127,40 @@ contract BaseTest is Test {
127127
return abi.encodePacked(r, s, v);
128128
}
129129

130-
function _getGDSignature(ActionData memory actionData) internal view returns (bytes memory) {
131-
bytes32 intentHash = router.hashTypedActionData(actionData);
132-
(uint8 v, bytes32 r, bytes32 s) = vm.sign(guardianKey, intentHash);
130+
function _getGDSignature(IntentData memory intentData, ActionData memory actionData)
131+
internal
132+
view
133+
returns (bytes memory)
134+
{
135+
bytes32 witnessHash = router.hashTypedActionWitness(
136+
ActionWitness(router.hashTypedIntentData(intentData), actionData)
137+
);
138+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(guardianKey, witnessHash);
133139
return abi.encodePacked(r, s, v);
134140
}
135141

136-
function _getDKSignature(ActionData memory actionData) internal view returns (bytes memory) {
137-
bytes32 actionHash = router.hashTypedActionData(actionData);
138-
return _getSignature(delegatedPrivateKey, actionHash);
139-
}
140-
141-
function _getCallerAndSignatures(uint256 mode, ActionData memory actionData)
142+
function _getDKSignature(IntentData memory intentData, ActionData memory actionData)
142143
internal
143144
view
144-
returns (address caller, bytes memory dkSignature, bytes memory gdSignature)
145+
returns (bytes memory)
145146
{
147+
bytes32 witnessHash = router.hashTypedActionWitness(
148+
ActionWitness(router.hashTypedIntentData(intentData), actionData)
149+
);
150+
return _getSignature(delegatedPrivateKey, witnessHash);
151+
}
152+
153+
function _getCallerAndSignatures(
154+
uint256 mode,
155+
IntentData memory intentData,
156+
ActionData memory actionData
157+
) internal view returns (address caller, bytes memory dkSignature, bytes memory gdSignature) {
146158
caller = mode == 0 ? randomCaller : (mode == 1 ? guardian : vm.addr(delegatedPrivateKey));
147159
if (mode == 0 || mode == 1) {
148-
dkSignature = _getDKSignature(actionData);
160+
dkSignature = _getDKSignature(intentData, actionData);
149161
}
150162
if (mode == 0 || mode == 2) {
151-
gdSignature = _getGDSignature(actionData);
163+
gdSignature = _getGDSignature(intentData, actionData);
152164
}
153165
}
154166

0 commit comments

Comments
 (0)