@debridge-finance/borderless-validation-sdk 0.0.0-dev-3dc1a82 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/helpers/address.js +18 -31
- package/dist/cjs/helpers/address.js.map +1 -1
- package/dist/cjs/helpers/constants.js +6 -0
- package/dist/cjs/helpers/constants.js.map +1 -0
- package/dist/cjs/helpers/evm-action.js +3 -3
- package/dist/cjs/helpers/evm-action.js.map +1 -1
- package/dist/cjs/helpers/evm-intent-id.js +1 -1
- package/dist/cjs/helpers/evm-intent-id.js.map +1 -1
- package/dist/cjs/helpers/evm-trade-amounts.js +1 -36
- package/dist/cjs/helpers/evm-trade-amounts.js.map +1 -1
- package/dist/cjs/types/enums.js +2 -26
- package/dist/cjs/types/enums.js.map +1 -1
- package/dist/cjs/types/request.js +2 -25
- package/dist/cjs/types/request.js.map +1 -1
- package/dist/cjs/types/response.js +4 -120
- package/dist/cjs/types/response.js.map +1 -1
- package/dist/cjs/types/validation.js +10 -74
- package/dist/cjs/types/validation.js.map +1 -1
- package/dist/cjs/validations/evm-caveats.js +14 -4
- package/dist/cjs/validations/evm-caveats.js.map +1 -1
- package/dist/cjs/validations/evm-op-budget.js +10 -13
- package/dist/cjs/validations/evm-op-budget.js.map +1 -1
- package/dist/cjs/validations/evm-op-gas-compensation.js +152 -0
- package/dist/cjs/validations/evm-op-gas-compensation.js.map +1 -0
- package/dist/cjs/validations/evm-op-hook.js +3 -410
- package/dist/cjs/validations/evm-op-hook.js.map +1 -1
- package/dist/cjs/validations/evm-op-wrap.js +7 -10
- package/dist/cjs/validations/evm-op-wrap.js.map +1 -1
- package/dist/cjs/validations/evm-required-actions.js +9 -7
- package/dist/cjs/validations/evm-required-actions.js.map +1 -1
- package/dist/cjs/validations/evm-sign712-metamask.js +94 -17
- package/dist/cjs/validations/evm-sign712-metamask.js.map +1 -1
- package/dist/cjs/validations/evm-sign7702.js +22 -34
- package/dist/cjs/validations/evm-sign7702.js.map +1 -1
- package/dist/cjs/validations/hooks-common.js +37 -252
- package/dist/cjs/validations/hooks-common.js.map +1 -1
- package/dist/cjs/validations/required-actions.js +7 -4
- package/dist/cjs/validations/required-actions.js.map +1 -1
- package/dist/cjs/validations/solana-hook.js +18 -32
- package/dist/cjs/validations/solana-hook.js.map +1 -1
- package/dist/cjs/validations/solana-op-budget.js +18 -56
- package/dist/cjs/validations/solana-op-budget.js.map +1 -1
- package/dist/cjs/validations/solana-op-wrap.js +13 -46
- package/dist/cjs/validations/solana-op-wrap.js.map +1 -1
- package/dist/cjs/validations/solana-required-actions.js +3 -3
- package/dist/cjs/validations/solana-required-actions.js.map +1 -1
- package/dist/cjs/validations/solana-transaction.js +5 -5
- package/dist/cjs/validations/solana-transaction.js.map +1 -1
- package/dist/cjs/validations/trade-grouping.js +1 -68
- package/dist/cjs/validations/trade-grouping.js.map +1 -1
- package/dist/cjs/validations/trades.js +37 -59
- package/dist/cjs/validations/trades.js.map +1 -1
- package/dist/cjs/validator.js +16 -11
- package/dist/cjs/validator.js.map +1 -1
- package/dist/cjs/whitelists/environment-config.js +28 -47
- package/dist/cjs/whitelists/environment-config.js.map +1 -1
- package/dist/cjs/whitelists/evm/caveat-enforcers.js +5 -21
- package/dist/cjs/whitelists/evm/caveat-enforcers.js.map +1 -1
- package/dist/cjs/whitelists/evm/delegation-manager.js +2 -7
- package/dist/cjs/whitelists/evm/delegation-manager.js.map +1 -1
- package/dist/cjs/whitelists/evm/eip7702-delegation.js +2 -7
- package/dist/cjs/whitelists/evm/eip7702-delegation.js.map +1 -1
- package/dist/cjs/whitelists/evm/index.js +1 -14
- package/dist/cjs/whitelists/evm/index.js.map +1 -1
- package/dist/cjs/whitelists/evm/permit2.js +12 -16
- package/dist/cjs/whitelists/evm/permit2.js.map +1 -1
- package/dist/cjs/whitelists/evm/weth.js +5 -16
- package/dist/cjs/whitelists/evm/weth.js.map +1 -1
- package/dist/cjs/whitelists/solana/intent-signature.js +1 -0
- package/dist/cjs/whitelists/solana/intent-signature.js.map +1 -1
- package/dist/cjs/whitelists/subscription-to-chain-id.js +0 -10
- package/dist/cjs/whitelists/subscription-to-chain-id.js.map +1 -1
- package/dist/cjs/whitelists/types.js +3 -13
- package/dist/cjs/whitelists/types.js.map +1 -1
- package/dist/esm/helpers/address.js +17 -28
- package/dist/esm/helpers/address.js.map +1 -1
- package/dist/esm/helpers/constants.js +3 -0
- package/dist/esm/helpers/constants.js.map +1 -0
- package/dist/esm/helpers/evm-action.js +3 -3
- package/dist/esm/helpers/evm-action.js.map +1 -1
- package/dist/esm/helpers/evm-intent-id.js +1 -1
- package/dist/esm/helpers/evm-intent-id.js.map +1 -1
- package/dist/esm/helpers/evm-trade-amounts.js +1 -36
- package/dist/esm/helpers/evm-trade-amounts.js.map +1 -1
- package/dist/esm/types/enums.js +1 -25
- package/dist/esm/types/enums.js.map +1 -1
- package/dist/esm/types/request.js +3 -26
- package/dist/esm/types/request.js.map +1 -1
- package/dist/esm/types/response.js +3 -119
- package/dist/esm/types/response.js.map +1 -1
- package/dist/esm/types/validation.js +10 -74
- package/dist/esm/types/validation.js.map +1 -1
- package/dist/esm/validations/evm-caveats.js +14 -4
- package/dist/esm/validations/evm-caveats.js.map +1 -1
- package/dist/esm/validations/evm-op-budget.js +10 -13
- package/dist/esm/validations/evm-op-budget.js.map +1 -1
- package/dist/esm/validations/evm-op-gas-compensation.js +149 -0
- package/dist/esm/validations/evm-op-gas-compensation.js.map +1 -0
- package/dist/esm/validations/evm-op-hook.js +2 -409
- package/dist/esm/validations/evm-op-hook.js.map +1 -1
- package/dist/esm/validations/evm-op-wrap.js +7 -10
- package/dist/esm/validations/evm-op-wrap.js.map +1 -1
- package/dist/esm/validations/evm-required-actions.js +9 -7
- package/dist/esm/validations/evm-required-actions.js.map +1 -1
- package/dist/esm/validations/evm-sign712-metamask.js +94 -17
- package/dist/esm/validations/evm-sign712-metamask.js.map +1 -1
- package/dist/esm/validations/evm-sign7702.js +22 -33
- package/dist/esm/validations/evm-sign7702.js.map +1 -1
- package/dist/esm/validations/hooks-common.js +38 -251
- package/dist/esm/validations/hooks-common.js.map +1 -1
- package/dist/esm/validations/required-actions.js +7 -4
- package/dist/esm/validations/required-actions.js.map +1 -1
- package/dist/esm/validations/solana-hook.js +20 -34
- package/dist/esm/validations/solana-hook.js.map +1 -1
- package/dist/esm/validations/solana-op-budget.js +18 -56
- package/dist/esm/validations/solana-op-budget.js.map +1 -1
- package/dist/esm/validations/solana-op-wrap.js +14 -47
- package/dist/esm/validations/solana-op-wrap.js.map +1 -1
- package/dist/esm/validations/solana-required-actions.js +3 -3
- package/dist/esm/validations/solana-required-actions.js.map +1 -1
- package/dist/esm/validations/solana-transaction.js +7 -7
- package/dist/esm/validations/solana-transaction.js.map +1 -1
- package/dist/esm/validations/trade-grouping.js +1 -68
- package/dist/esm/validations/trade-grouping.js.map +1 -1
- package/dist/esm/validations/trades.js +37 -59
- package/dist/esm/validations/trades.js.map +1 -1
- package/dist/esm/validator.js +16 -11
- package/dist/esm/validator.js.map +1 -1
- package/dist/esm/whitelists/environment-config.js +28 -47
- package/dist/esm/whitelists/environment-config.js.map +1 -1
- package/dist/esm/whitelists/evm/caveat-enforcers.js +4 -20
- package/dist/esm/whitelists/evm/caveat-enforcers.js.map +1 -1
- package/dist/esm/whitelists/evm/delegation-manager.js +1 -6
- package/dist/esm/whitelists/evm/delegation-manager.js.map +1 -1
- package/dist/esm/whitelists/evm/eip7702-delegation.js +1 -6
- package/dist/esm/whitelists/evm/eip7702-delegation.js.map +1 -1
- package/dist/esm/whitelists/evm/index.js +4 -4
- package/dist/esm/whitelists/evm/index.js.map +1 -1
- package/dist/esm/whitelists/evm/permit2.js +5 -9
- package/dist/esm/whitelists/evm/permit2.js.map +1 -1
- package/dist/esm/whitelists/evm/weth.js +5 -16
- package/dist/esm/whitelists/evm/weth.js.map +1 -1
- package/dist/esm/whitelists/solana/intent-signature.js +1 -0
- package/dist/esm/whitelists/solana/intent-signature.js.map +1 -1
- package/dist/esm/whitelists/subscription-to-chain-id.js +0 -9
- package/dist/esm/whitelists/subscription-to-chain-id.js.map +1 -1
- package/dist/esm/whitelists/types.js +5 -14
- package/dist/esm/whitelists/types.js.map +1 -1
- package/dist/types/helpers/address.d.ts +7 -5
- package/dist/types/helpers/address.d.ts.map +1 -1
- package/dist/types/helpers/constants.d.ts +2 -0
- package/dist/types/helpers/constants.d.ts.map +1 -0
- package/dist/types/helpers/evm-trade-amounts.d.ts +2 -2
- package/dist/types/helpers/evm-trade-amounts.d.ts.map +1 -1
- package/dist/types/types/enums.d.ts +1 -19
- package/dist/types/types/enums.d.ts.map +1 -1
- package/dist/types/types/request.d.ts +9 -19
- package/dist/types/types/request.d.ts.map +1 -1
- package/dist/types/types/response.d.ts +11 -367
- package/dist/types/types/response.d.ts.map +1 -1
- package/dist/types/types/validation.d.ts +11 -67
- package/dist/types/types/validation.d.ts.map +1 -1
- package/dist/types/validations/evm-caveats.d.ts.map +1 -1
- package/dist/types/validations/evm-op-budget.d.ts +2 -2
- package/dist/types/validations/evm-op-budget.d.ts.map +1 -1
- package/dist/types/validations/evm-op-gas-compensation.d.ts +3 -0
- package/dist/types/validations/evm-op-gas-compensation.d.ts.map +1 -0
- package/dist/types/validations/evm-op-hook.d.ts +2 -4
- package/dist/types/validations/evm-op-hook.d.ts.map +1 -1
- package/dist/types/validations/evm-op-wrap.d.ts +2 -2
- package/dist/types/validations/evm-op-wrap.d.ts.map +1 -1
- package/dist/types/validations/evm-required-actions.d.ts +2 -2
- package/dist/types/validations/evm-required-actions.d.ts.map +1 -1
- package/dist/types/validations/evm-sign712-metamask.d.ts.map +1 -1
- package/dist/types/validations/evm-sign7702.d.ts +1 -2
- package/dist/types/validations/evm-sign7702.d.ts.map +1 -1
- package/dist/types/validations/hooks-common.d.ts +0 -1
- package/dist/types/validations/hooks-common.d.ts.map +1 -1
- package/dist/types/validations/required-actions.d.ts +1 -2
- package/dist/types/validations/required-actions.d.ts.map +1 -1
- package/dist/types/validations/solana-hook.d.ts +2 -4
- package/dist/types/validations/solana-hook.d.ts.map +1 -1
- package/dist/types/validations/solana-op-budget.d.ts +2 -2
- package/dist/types/validations/solana-op-budget.d.ts.map +1 -1
- package/dist/types/validations/solana-op-wrap.d.ts +2 -2
- package/dist/types/validations/solana-op-wrap.d.ts.map +1 -1
- package/dist/types/validations/solana-required-actions.d.ts +2 -2
- package/dist/types/validations/solana-required-actions.d.ts.map +1 -1
- package/dist/types/validations/solana-transaction.d.ts +3 -3
- package/dist/types/validations/solana-transaction.d.ts.map +1 -1
- package/dist/types/validations/trade-grouping.d.ts +2 -3
- package/dist/types/validations/trade-grouping.d.ts.map +1 -1
- package/dist/types/validator.d.ts.map +1 -1
- package/dist/types/whitelists/environment-config.d.ts.map +1 -1
- package/dist/types/whitelists/evm/caveat-enforcers.d.ts +0 -8
- package/dist/types/whitelists/evm/caveat-enforcers.d.ts.map +1 -1
- package/dist/types/whitelists/evm/delegation-manager.d.ts +0 -2
- package/dist/types/whitelists/evm/delegation-manager.d.ts.map +1 -1
- package/dist/types/whitelists/evm/eip7702-delegation.d.ts +0 -2
- package/dist/types/whitelists/evm/eip7702-delegation.d.ts.map +1 -1
- package/dist/types/whitelists/evm/index.d.ts +4 -4
- package/dist/types/whitelists/evm/index.d.ts.map +1 -1
- package/dist/types/whitelists/evm/permit2.d.ts +0 -1
- package/dist/types/whitelists/evm/permit2.d.ts.map +1 -1
- package/dist/types/whitelists/evm/weth.d.ts.map +1 -1
- package/dist/types/whitelists/solana/intent-signature.d.ts.map +1 -1
- package/dist/types/whitelists/subscription-to-chain-id.d.ts +0 -1
- package/dist/types/whitelists/subscription-to-chain-id.d.ts.map +1 -1
- package/dist/types/whitelists/types.d.ts +1 -1
- package/dist/types/whitelists/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/README.md +0 -164
- package/dist/cjs/helpers/evm-gas-compensation-tx.js +0 -102
- package/dist/cjs/helpers/evm-gas-compensation-tx.js.map +0 -1
- package/dist/cjs/helpers/gas-compensation.js +0 -36
- package/dist/cjs/helpers/gas-compensation.js.map +0 -1
- package/dist/cjs/helpers/placeholder.js +0 -174
- package/dist/cjs/helpers/placeholder.js.map +0 -1
- package/dist/cjs/helpers/sets.js +0 -13
- package/dist/cjs/helpers/sets.js.map +0 -1
- package/dist/cjs/validations/compensation-hook.js +0 -203
- package/dist/cjs/validations/compensation-hook.js.map +0 -1
- package/dist/cjs/validations/evm-sign712-shared.js +0 -116
- package/dist/cjs/validations/evm-sign712-shared.js.map +0 -1
- package/dist/cjs/validations/intent-destinations.js +0 -197
- package/dist/cjs/validations/intent-destinations.js.map +0 -1
- package/dist/cjs/validations/intent-tokens.js +0 -341
- package/dist/cjs/validations/intent-tokens.js.map +0 -1
- package/dist/cjs/validations/intent.js +0 -288
- package/dist/cjs/validations/intent.js.map +0 -1
- package/dist/cjs/whitelists/evm/metamask-delegation-chains.js +0 -16
- package/dist/cjs/whitelists/evm/metamask-delegation-chains.js.map +0 -1
- package/dist/esm/helpers/evm-gas-compensation-tx.js +0 -99
- package/dist/esm/helpers/evm-gas-compensation-tx.js.map +0 -1
- package/dist/esm/helpers/gas-compensation.js +0 -33
- package/dist/esm/helpers/gas-compensation.js.map +0 -1
- package/dist/esm/helpers/placeholder.js +0 -168
- package/dist/esm/helpers/placeholder.js.map +0 -1
- package/dist/esm/helpers/sets.js +0 -10
- package/dist/esm/helpers/sets.js.map +0 -1
- package/dist/esm/validations/compensation-hook.js +0 -200
- package/dist/esm/validations/compensation-hook.js.map +0 -1
- package/dist/esm/validations/evm-sign712-shared.js +0 -110
- package/dist/esm/validations/evm-sign712-shared.js.map +0 -1
- package/dist/esm/validations/intent-destinations.js +0 -194
- package/dist/esm/validations/intent-destinations.js.map +0 -1
- package/dist/esm/validations/intent-tokens.js +0 -338
- package/dist/esm/validations/intent-tokens.js.map +0 -1
- package/dist/esm/validations/intent.js +0 -285
- package/dist/esm/validations/intent.js.map +0 -1
- package/dist/esm/whitelists/evm/metamask-delegation-chains.js +0 -13
- package/dist/esm/whitelists/evm/metamask-delegation-chains.js.map +0 -1
- package/dist/types/helpers/evm-gas-compensation-tx.d.ts +0 -13
- package/dist/types/helpers/evm-gas-compensation-tx.d.ts.map +0 -1
- package/dist/types/helpers/gas-compensation.d.ts +0 -10
- package/dist/types/helpers/gas-compensation.d.ts.map +0 -1
- package/dist/types/helpers/placeholder.d.ts +0 -41
- package/dist/types/helpers/placeholder.d.ts.map +0 -1
- package/dist/types/helpers/sets.d.ts +0 -2
- package/dist/types/helpers/sets.d.ts.map +0 -1
- package/dist/types/validations/compensation-hook.d.ts +0 -3
- package/dist/types/validations/compensation-hook.d.ts.map +0 -1
- package/dist/types/validations/evm-sign712-shared.d.ts +0 -7
- package/dist/types/validations/evm-sign712-shared.d.ts.map +0 -1
- package/dist/types/validations/intent-destinations.d.ts +0 -4
- package/dist/types/validations/intent-destinations.d.ts.map +0 -1
- package/dist/types/validations/intent-tokens.d.ts +0 -4
- package/dist/types/validations/intent-tokens.d.ts.map +0 -1
- package/dist/types/validations/intent.d.ts +0 -5
- package/dist/types/validations/intent.d.ts.map +0 -1
- package/dist/types/whitelists/evm/metamask-delegation-chains.d.ts +0 -3
- package/dist/types/whitelists/evm/metamask-delegation-chains.d.ts.map +0 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ViolationCode, EvmActionKind, EvmActionOperation, } from "../types/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isWhitelisted } from "../whitelists/types.js";
|
|
3
|
+
import { DELEGATION_MANAGER_CONTRACTS } from "../whitelists/evm/index.js";
|
|
4
|
+
import { addressEquals } from "../helpers/address.js";
|
|
5
|
+
import { classifyCaveats, validateExactExecutionPresence, validateExactExecutionContent, validateTimestampEnforcerValue, } from "./evm-caveats.js";
|
|
3
6
|
const SIGN712_METAMASK_ALLOWED_ACTIONS = [
|
|
4
7
|
EvmActionOperation.Budget,
|
|
5
8
|
EvmActionOperation.Wrap,
|
|
@@ -10,28 +13,23 @@ export function validateSign712MetaMaskActions(actions, basePath, realChainId, q
|
|
|
10
13
|
const violations = [];
|
|
11
14
|
for (let i = 0; i < actions.length; i++) {
|
|
12
15
|
const action = actions[i];
|
|
13
|
-
if (action.type !== EvmActionKind.
|
|
16
|
+
if (action.type !== EvmActionKind.Sign712MetaMask)
|
|
14
17
|
continue;
|
|
15
18
|
const actionPath = `${basePath}.requiredActions[${i}]`;
|
|
16
19
|
const data = action.data;
|
|
17
|
-
// CRITICAL: data
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
// CRITICAL: `data` must be present and contain `domain` and `message`
|
|
21
|
+
if (!data || !data.domain || !data.message) {
|
|
22
|
+
violations.push({
|
|
23
|
+
code: ViolationCode.Sign712MetaMaskDomainNameNotDelegationManager,
|
|
24
|
+
message: `Sign712MetaMask data is missing domain or message`,
|
|
25
|
+
path: `${actionPath}.data`,
|
|
26
|
+
actual: data,
|
|
27
|
+
});
|
|
22
28
|
continue;
|
|
23
29
|
}
|
|
24
|
-
violations.push(...validateSign712TransactionCalls(action, actionPath), ...validateSign712AllowedActions(action, actionPath), ...validateSign712Domain(data, actionPath, realChainId),
|
|
25
|
-
// Delegator: in quote mode we cannot determine the trust source —
|
|
26
|
-
// skip the check entirely. In execution mode the helper compares
|
|
27
|
-
// against `srcChainAuthorityAddress` (which may itself be
|
|
28
|
-
// `undefined` as a defensive check — preserved by passing through).
|
|
29
|
-
...(quoteMode
|
|
30
|
-
? []
|
|
31
|
-
: validateSign712Delegator(data, actionPath, realChainId, srcChainAuthorityAddress, "srcChainAuthorityAddress")));
|
|
30
|
+
violations.push(...validateSign712TransactionCalls(action, actionPath), ...validateSign712AllowedActions(action, actionPath), ...validateSign712Domain(data, actionPath, realChainId), ...validateSign712Delegator(data, actionPath, realChainId, quoteMode, srcChainAuthorityAddress));
|
|
32
31
|
const caveats = (data.message.caveats ?? []);
|
|
33
|
-
|
|
34
|
-
violations.push(...validateSign712Caveats(caveats, action.actionId, txCalls, actionPath, realChainId, expirationTimestamp));
|
|
32
|
+
violations.push(...validateSign712MetaMaskCaveats(caveats, action, actionPath, realChainId, expirationTimestamp));
|
|
35
33
|
}
|
|
36
34
|
return violations;
|
|
37
35
|
}
|
|
@@ -64,4 +62,83 @@ function validateSign712AllowedActions(action, actionPath) {
|
|
|
64
62
|
}
|
|
65
63
|
return violations;
|
|
66
64
|
}
|
|
65
|
+
// CRITICAL: domain.name must be "DelegationManager"
|
|
66
|
+
// CRITICAL: domain.chainId must equal the real chainId
|
|
67
|
+
// CRITICAL: domain.verifyingContract must be in the DelegationManager whitelist
|
|
68
|
+
function validateSign712Domain(data, actionPath, realChainId) {
|
|
69
|
+
const violations = [];
|
|
70
|
+
if (data.domain.name !== "DelegationManager") {
|
|
71
|
+
violations.push({
|
|
72
|
+
code: ViolationCode.Sign712MetaMaskDomainNameNotDelegationManager,
|
|
73
|
+
message: `Sign712MetaMask domain.name must be "DelegationManager"`,
|
|
74
|
+
path: `${actionPath}.data.domain.name`,
|
|
75
|
+
expected: "DelegationManager",
|
|
76
|
+
actual: data.domain.name,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (data.domain.chainId !== realChainId) {
|
|
80
|
+
violations.push({
|
|
81
|
+
code: ViolationCode.Sign712MetaMaskChainIdMismatch,
|
|
82
|
+
message: `Sign712MetaMask domain.chainId does not match the real chainId`,
|
|
83
|
+
path: `${actionPath}.data.domain.chainId`,
|
|
84
|
+
expected: realChainId,
|
|
85
|
+
actual: data.domain.chainId,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (data.domain.verifyingContract === undefined ||
|
|
89
|
+
!isWhitelisted(DELEGATION_MANAGER_CONTRACTS, realChainId, data.domain.verifyingContract)) {
|
|
90
|
+
violations.push({
|
|
91
|
+
code: ViolationCode.Sign712MetaMaskVerifyingContractNotWhitelisted,
|
|
92
|
+
message: `Sign712MetaMask domain.verifyingContract is not whitelisted`,
|
|
93
|
+
path: `${actionPath}.data.domain.verifyingContract`,
|
|
94
|
+
actual: data.domain.verifyingContract,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return violations;
|
|
98
|
+
}
|
|
99
|
+
// LOW: in execution mode, message.delegator must match srcChainAuthorityAddress
|
|
100
|
+
function validateSign712Delegator(data, actionPath, realChainId, quoteMode, srcChainAuthorityAddress) {
|
|
101
|
+
if (quoteMode)
|
|
102
|
+
return [];
|
|
103
|
+
const delegator = data.message.delegator;
|
|
104
|
+
if (srcChainAuthorityAddress === undefined ||
|
|
105
|
+
delegator === undefined ||
|
|
106
|
+
!addressEquals(realChainId, delegator, srcChainAuthorityAddress)) {
|
|
107
|
+
return [{
|
|
108
|
+
code: ViolationCode.Sign712MetaMaskDelegatorMismatch,
|
|
109
|
+
message: `Sign712MetaMask message.delegator must match srcChainAuthorityAddress`,
|
|
110
|
+
path: `${actionPath}.data.message.delegator`,
|
|
111
|
+
expected: srcChainAuthorityAddress,
|
|
112
|
+
actual: delegator,
|
|
113
|
+
}];
|
|
114
|
+
}
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
function validateSign712MetaMaskCaveats(caveats, action, actionPath, realChainId, expirationTimestamp) {
|
|
118
|
+
const violations = [];
|
|
119
|
+
const classified = classifyCaveats(caveats, action.actionId, actionPath, realChainId, violations);
|
|
120
|
+
const txCalls = action.details?.transactionCalls ?? [];
|
|
121
|
+
violations.push(...validateExactExecutionPresence(classified, txCalls, actionPath));
|
|
122
|
+
validateExactExecutionContent(classified, txCalls, actionPath, violations);
|
|
123
|
+
// CRITICAL: must have at least one IdEnforcer caveat (replay protection)
|
|
124
|
+
if (!classified.hasIdEnforcer) {
|
|
125
|
+
violations.push({
|
|
126
|
+
code: ViolationCode.Sign712MetaMaskMissingIdEnforcerCaveat,
|
|
127
|
+
message: `Sign712MetaMask must have an IdEnforcer caveat`,
|
|
128
|
+
path: `${actionPath}.data.message.caveats`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// CRITICAL: must have exactly one TimestampEnforcer caveat
|
|
132
|
+
if (classified.timestampCount === 0) {
|
|
133
|
+
violations.push({
|
|
134
|
+
code: ViolationCode.Sign712MetaMaskMissingTimestampEnforcerCaveat,
|
|
135
|
+
message: `Sign712MetaMask must have a TimestampEnforcer caveat`,
|
|
136
|
+
path: `${actionPath}.data.message.caveats`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (classified.firstTimestamp) {
|
|
140
|
+
validateTimestampEnforcerValue(classified.firstTimestamp.caveat, expirationTimestamp, classified.firstTimestamp.index, actionPath, violations);
|
|
141
|
+
}
|
|
142
|
+
return violations;
|
|
143
|
+
}
|
|
67
144
|
//# sourceMappingURL=evm-sign712-metamask.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evm-sign712-metamask.js","sourceRoot":"","sources":["../../../src/validations/evm-sign712-metamask.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,aAAa,EAEb,aAAa,EACb,kBAAkB,GAGrB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"evm-sign712-metamask.js","sourceRoot":"","sources":["../../../src/validations/evm-sign712-metamask.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,38 +4,6 @@ import { EIP7702_DELEGATION_CONTRACTS } from "../whitelists/evm/index.js";
|
|
|
4
4
|
// =============================================
|
|
5
5
|
// Sign7702Authorization constraints
|
|
6
6
|
// =============================================
|
|
7
|
-
// CRITICAL: per-action checks for a `Sign7702Authorization` action.
|
|
8
|
-
// Shared between intent-scope (`validateSign7702Actions`) and hook-scope
|
|
9
|
-
// (`validateHookSign7702Binding`); both wrap this with their scope-specific
|
|
10
|
-
// constraints (intent: position+duplicate; hook: type↔actions binding +
|
|
11
|
-
// `validateHookDelegateOp` for position/count).
|
|
12
|
-
export function validateSign7702Action(action, actionPath, realChainId) {
|
|
13
|
-
const violations = [];
|
|
14
|
-
// HIGH: actions[] must contain exactly one element — Delegate
|
|
15
|
-
const ops = action.actions;
|
|
16
|
-
if (ops.length !== 1 || ops[0] !== EvmActionOperation.Delegate) {
|
|
17
|
-
violations.push({
|
|
18
|
-
code: ViolationCode.Sign7702ActionsNotSingleDelegate,
|
|
19
|
-
message: `Sign7702Authorization actions must contain exactly [Delegate]`,
|
|
20
|
-
path: `${actionPath}.actions`,
|
|
21
|
-
expected: [EvmActionOperation.Delegate],
|
|
22
|
-
actual: action.actions,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
// CRITICAL: data.contractAddress must be in the whitelist
|
|
26
|
-
const data = action.data;
|
|
27
|
-
const contractAddress = data?.contractAddress;
|
|
28
|
-
if (!contractAddress
|
|
29
|
-
|| !isWhitelisted(EIP7702_DELEGATION_CONTRACTS, realChainId, contractAddress)) {
|
|
30
|
-
violations.push({
|
|
31
|
-
code: ViolationCode.Sign7702ContractAddressNotWhitelisted,
|
|
32
|
-
message: `Sign7702Authorization contractAddress is not in the whitelist`,
|
|
33
|
-
path: `${actionPath}.data.contractAddress`,
|
|
34
|
-
actual: contractAddress,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
return violations;
|
|
38
|
-
}
|
|
39
7
|
export function validateSign7702Actions(actions, basePath, realChainId) {
|
|
40
8
|
const violations = [];
|
|
41
9
|
let sign7702Count = 0;
|
|
@@ -45,7 +13,28 @@ export function validateSign7702Actions(actions, basePath, realChainId) {
|
|
|
45
13
|
if (action.type !== EvmActionKind.Sign7702Authorization)
|
|
46
14
|
continue;
|
|
47
15
|
const actionPath = `${basePath}.requiredActions[${i}]`;
|
|
48
|
-
|
|
16
|
+
// HIGH: actions[] must contain exactly one element — Delegate
|
|
17
|
+
if (action.actions.length !== 1 ||
|
|
18
|
+
action.actions[0] !== EvmActionOperation.Delegate) {
|
|
19
|
+
violations.push({
|
|
20
|
+
code: ViolationCode.Sign7702ActionsNotSingleDelegate,
|
|
21
|
+
message: `Sign7702Authorization actions must contain exactly [Delegate]`,
|
|
22
|
+
path: `${actionPath}.actions`,
|
|
23
|
+
expected: [EvmActionOperation.Delegate],
|
|
24
|
+
actual: action.actions,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// CRITICAL: data.contractAddress must be in the whitelist
|
|
28
|
+
const data = action.data;
|
|
29
|
+
const contractAddress = data?.contractAddress;
|
|
30
|
+
if (!contractAddress || !isWhitelisted(EIP7702_DELEGATION_CONTRACTS, realChainId, contractAddress)) {
|
|
31
|
+
violations.push({
|
|
32
|
+
code: ViolationCode.Sign7702ContractAddressNotWhitelisted,
|
|
33
|
+
message: `Sign7702Authorization contractAddress is not in the whitelist`,
|
|
34
|
+
path: `${actionPath}.data.contractAddress`,
|
|
35
|
+
actual: contractAddress,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
49
38
|
// HIGH: at most one Sign7702Authorization per intent
|
|
50
39
|
// Note: only the first duplicate will be reported, subsequent ones will be ignored
|
|
51
40
|
sign7702Count++;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evm-sign7702.js","sourceRoot":"","sources":["../../../src/validations/evm-sign7702.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,aAAa,EAEb,aAAa,EACb,kBAAkB,
|
|
1
|
+
{"version":3,"file":"evm-sign7702.js","sourceRoot":"","sources":["../../../src/validations/evm-sign7702.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,aAAa,EAEb,aAAa,EACb,kBAAkB,GAGrB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AAEjE,gDAAgD;AAChD,oCAAoC;AACpC,gDAAgD;AAEhD,MAAM,UAAU,uBAAuB,CACnC,OAA4B,EAC5B,QAAgB,EAChB,WAAoB;IAEpB,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC;IAE5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,CAAC,qBAAqB;YAAE,SAAS;QAElE,MAAM,UAAU,GAAG,GAAG,QAAQ,oBAAoB,CAAC,GAAG,CAAC;QAEvD,8DAA8D;QAC9D,IACI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAC3B,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,QAAQ,EACnD,CAAC;YACC,UAAU,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,aAAa,CAAC,gCAAgC;gBACpD,OAAO,EAAE,+DAA+D;gBACxE,IAAI,EAAE,GAAG,UAAU,UAAU;gBAC7B,QAAQ,EAAE,CAAC,kBAAkB,CAAC,QAAQ,CAAC;gBACvC,MAAM,EAAE,MAAM,CAAC,OAAO;aACzB,CAAC,CAAC;QACP,CAAC;QAED,0DAA0D;QAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAgD,CAAC;QACrE,MAAM,eAAe,GAAG,IAAI,EAAE,eAAe,CAAC;QAC9C,IAAI,CAAC,eAAe,IAAI,CAAC,aAAa,CAAC,4BAA4B,EAAE,WAAW,EAAE,eAAe,CAAC,EAAE,CAAC;YACjG,UAAU,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,aAAa,CAAC,qCAAqC;gBACzD,OAAO,EAAE,+DAA+D;gBACxE,IAAI,EAAE,GAAG,UAAU,uBAAuB;gBAC1C,MAAM,EAAE,eAAe;aAC1B,CAAC,CAAC;QACP,CAAC;QAED,qDAAqD;QACrD,mFAAmF;QACnF,aAAa,EAAE,CAAC;QAChB,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;YACtB,kBAAkB,GAAG,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;YAC7B,UAAU,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,aAAa,CAAC,8BAA8B;gBAClD,OAAO,EAAE,kEAAkE,kBAAkB,QAAQ,CAAC,EAAE;gBACxG,IAAI,EAAE,GAAG,UAAU,OAAO;gBAC1B,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,aAAa;aACxB,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC"}
|
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
import { ViolationCode, } from "../types/index.js";
|
|
2
|
-
import {
|
|
3
|
-
import { addressEquals, getChainEngine } from "../helpers/address.js";
|
|
4
|
-
import { extractGasCompensationGroups } from "../helpers/gas-compensation.js";
|
|
5
|
-
import { HookMode, PlaceholderResolutionErrorReason, resolveEagerPlaceholders, } from "../helpers/placeholder.js";
|
|
2
|
+
import { ChainId } from "../types/enums.js";
|
|
6
3
|
import { validateSolanaHookActions } from "./solana-hook.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// Re-exported so call sites can pass the mode without importing from helpers.
|
|
11
|
-
export { HookMode } from "../helpers/placeholder.js";
|
|
12
|
-
// Allowed hook-scope operations — each chain checked against its own enum.
|
|
13
|
-
const EVM_HOOK_OPERATIONS = [
|
|
14
|
-
EvmActionOperation.Delegate,
|
|
15
|
-
EvmActionOperation.Hook,
|
|
16
|
-
EvmActionOperation.GasCompensation,
|
|
17
|
-
];
|
|
18
|
-
const EVM_HOOK_OPERATIONS_SET = new Set(EVM_HOOK_OPERATIONS);
|
|
19
|
-
const SOLANA_HOOK_OPERATIONS = [
|
|
20
|
-
SolanaActionOperation.Hook,
|
|
21
|
-
SolanaActionOperation.GasCompensation,
|
|
22
|
-
];
|
|
23
|
-
const SOLANA_HOOK_OPERATIONS_SET = new Set(SOLANA_HOOK_OPERATIONS);
|
|
4
|
+
// Both EVM and Solana use identical string values for hook operations
|
|
5
|
+
const HOOK_OPERATIONS = ["Hook", "GasCompensation"];
|
|
6
|
+
const HOOK_OPERATIONS_SET = new Set(HOOK_OPERATIONS);
|
|
24
7
|
export function validateHooks(request, response, quoteMode) {
|
|
25
8
|
return [
|
|
26
9
|
...validateHookAAConstraint(request, response),
|
|
@@ -54,35 +37,26 @@ function validateHookAAConstraint(request, response) {
|
|
|
54
37
|
}
|
|
55
38
|
return violations;
|
|
56
39
|
}
|
|
57
|
-
// HIGH: preHooks
|
|
58
|
-
// gasCompensationInfo group (exact equality — API always adds one per group).
|
|
59
|
-
// postHooks must match exactly — the API never adds extras to postHooks.
|
|
40
|
+
// HIGH: response.preHooks.length must equal request.preHooks.length
|
|
60
41
|
function validateHookCounts(request, response) {
|
|
61
42
|
const violations = [];
|
|
62
43
|
const requestPreCount = request.preHooks?.length ?? 0;
|
|
63
44
|
const requestPostCount = request.postHooks?.length ?? 0;
|
|
64
|
-
|
|
65
|
-
// compensation group. If request has no gasCompensationInfo at all, extras
|
|
66
|
-
// are forbidden (this also guards against MITM adding preHook entries when
|
|
67
|
-
// the user never asked for compensation).
|
|
68
|
-
const compensationGroupCount = extractGasCompensationGroups(request).length;
|
|
69
|
-
const expectedPreCount = requestPreCount + compensationGroupCount;
|
|
70
|
-
if (response.preHooks.length !== expectedPreCount) {
|
|
45
|
+
if (response.preHooks.length !== requestPreCount) {
|
|
71
46
|
violations.push({
|
|
72
47
|
code: ViolationCode.HookCountMismatch,
|
|
73
|
-
message: "response.preHooks count does not match"
|
|
74
|
-
+ " request.preHooks.length + unique gasCompensationInfo groups",
|
|
48
|
+
message: "response.preHooks count does not match request.preHooks count",
|
|
75
49
|
path: "response.preHooks",
|
|
76
|
-
expected:
|
|
50
|
+
expected: requestPreCount,
|
|
77
51
|
actual: response.preHooks.length,
|
|
78
52
|
});
|
|
79
53
|
}
|
|
80
|
-
// postHooks
|
|
81
|
-
|
|
54
|
+
// postHooks may contain additional compensation hooks beyond what was in the request,
|
|
55
|
+
// so response count must be >= request count
|
|
56
|
+
if (response.postHooks.length < requestPostCount) {
|
|
82
57
|
violations.push({
|
|
83
58
|
code: ViolationCode.HookCountMismatch,
|
|
84
|
-
message: "response.postHooks count
|
|
85
|
-
+ " request.postHooks count",
|
|
59
|
+
message: "response.postHooks count is less than request.postHooks count",
|
|
86
60
|
path: "response.postHooks",
|
|
87
61
|
expected: requestPostCount,
|
|
88
62
|
actual: response.postHooks.length,
|
|
@@ -97,7 +71,7 @@ function validatePreHooks(request, response, quoteMode) {
|
|
|
97
71
|
const basePath = `response.preHooks[${i}]`;
|
|
98
72
|
const hookPayload = response.preHooks[i];
|
|
99
73
|
const requestHook = requestHooks[i];
|
|
100
|
-
violations.push(...validateSingleHookPayload(hookPayload, requestHook, basePath, request,
|
|
74
|
+
violations.push(...validateSingleHookPayload(hookPayload, requestHook, basePath, request, quoteMode));
|
|
101
75
|
}
|
|
102
76
|
return violations;
|
|
103
77
|
}
|
|
@@ -109,153 +83,31 @@ function validatePostHooks(request, response, quoteMode) {
|
|
|
109
83
|
const hookPayload = response.postHooks[i];
|
|
110
84
|
// User postHooks are matched by index; extra entries are compensation hooks
|
|
111
85
|
const requestHook = i < requestHooks.length ? requestHooks[i] : undefined;
|
|
112
|
-
violations.push(...validateSingleHookPayload(hookPayload, requestHook, basePath, request,
|
|
86
|
+
violations.push(...validateSingleHookPayload(hookPayload, requestHook, basePath, request, quoteMode));
|
|
113
87
|
}
|
|
114
88
|
return violations;
|
|
115
89
|
}
|
|
116
|
-
function validateSingleHookPayload(hookPayload, requestHook, basePath, request,
|
|
117
|
-
const isCompensationPreHook = requestHook === undefined && mode === HookMode.PreHooks;
|
|
90
|
+
function validateSingleHookPayload(hookPayload, requestHook, basePath, request, quoteMode) {
|
|
118
91
|
return [
|
|
119
|
-
...validateHookEcho(hookPayload, requestHook, basePath
|
|
92
|
+
...validateHookEcho(hookPayload, requestHook, basePath),
|
|
120
93
|
...validateHookUniqueActionIds(hookPayload, basePath),
|
|
121
94
|
...validateHookActionsList(hookPayload, basePath),
|
|
122
|
-
...validateHookDelegateOp(hookPayload, basePath),
|
|
123
|
-
...validateHookSolanaNotDirect(hookPayload, basePath),
|
|
124
95
|
...validateGasCompensationRequiresInfo(hookPayload, basePath, request),
|
|
125
|
-
...(
|
|
126
|
-
? validateCompensationHookStructure(hookPayload, basePath, request)
|
|
127
|
-
: []),
|
|
128
|
-
...dispatchChainSpecificValidation(hookPayload, requestHook, basePath, quoteMode, request, mode, responseTrades),
|
|
96
|
+
...dispatchChainSpecificValidation(hookPayload, requestHook, basePath, quoteMode, request),
|
|
129
97
|
];
|
|
130
98
|
}
|
|
131
|
-
//
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
if (getChainEngine(realChainId) !== ChainEngine.Solana)
|
|
99
|
+
// CRITICAL: response hook must echo back the request hook exactly
|
|
100
|
+
function validateHookEcho(hookPayload, requestHook, basePath) {
|
|
101
|
+
if (!requestHook)
|
|
135
102
|
return [];
|
|
136
|
-
if (hookPayload.hook
|
|
137
|
-
return [
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}];
|
|
145
|
-
}
|
|
146
|
-
// CRITICAL: response hook must echo back the request hook field-by-field.
|
|
147
|
-
// Notes:
|
|
148
|
-
// - hook.data in the response has eager placeholders resolved (deferred markers kept).
|
|
149
|
-
// We apply the same resolution to request.data and compare.
|
|
150
|
-
// - For Solana hooks the API deletes `to` and `value` in the echo; we skip those.
|
|
151
|
-
// - For EVM hooks `value` is copied from request as-is (eager resolution is not
|
|
152
|
-
// reflected in the echoed value — only in internal txData used by requiredActions).
|
|
153
|
-
function validateHookEcho(hookPayload, requestHook, basePath, responseTrades, mode) {
|
|
154
|
-
// Missing request counterpart — two shapes, both are MITM-safe:
|
|
155
|
-
// (a) preHook with `i >= request.preHooks.length`:
|
|
156
|
-
// legitimate case — compensation hook appended by the API.
|
|
157
|
-
// MITM case — attacker injects an arbitrary preHook.
|
|
158
|
-
// The echo-by-field check can't distinguish them (there's no request
|
|
159
|
-
// to compare against), so we delegate to the structural compensation
|
|
160
|
-
// check.
|
|
161
|
-
// (b) postHook with `i >= request.postHooks.length`:
|
|
162
|
-
// no legitimate case — the API never appends to postHooks. So we
|
|
163
|
-
// produce a violation.
|
|
164
|
-
if (!requestHook) {
|
|
165
|
-
if (mode === HookMode.PostHooks) {
|
|
166
|
-
return [{
|
|
167
|
-
code: ViolationCode.UnexpectedPostHook,
|
|
168
|
-
message: "response.postHooks contains an entry with no matching"
|
|
169
|
-
+ " request postHook",
|
|
170
|
-
path: `${basePath}.hook`,
|
|
171
|
-
actual: hookPayload.hook,
|
|
172
|
-
}];
|
|
173
|
-
}
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
const echoed = hookPayload.hook;
|
|
177
|
-
const chainId = requestHook.chainId;
|
|
178
|
-
const realChainId = resolveChainIdOrSelf(chainId);
|
|
179
|
-
const isSolana = getChainEngine(realChainId) === ChainEngine.Solana;
|
|
180
|
-
const hookPath = `${basePath}.hook`;
|
|
181
|
-
const violations = [];
|
|
182
|
-
// chainId — strict number equality (subscriptionId on both sides)
|
|
183
|
-
if (echoed.chainId !== chainId) {
|
|
184
|
-
violations.push(echoMismatch(`${hookPath}.chainId`, requestHook.chainId, echoed.chainId));
|
|
185
|
-
}
|
|
186
|
-
// from — address equality
|
|
187
|
-
if (!addressEquals(realChainId, echoed.from, requestHook.from)) {
|
|
188
|
-
violations.push(echoMismatch(`${hookPath}.from`, requestHook.from, echoed.from));
|
|
189
|
-
}
|
|
190
|
-
// isAtomic — strict boolean
|
|
191
|
-
if (echoed.isAtomic !== requestHook.isAtomic) {
|
|
192
|
-
violations.push(echoMismatch(`${hookPath}.isAtomic`, requestHook.isAtomic, echoed.isAtomic));
|
|
193
|
-
}
|
|
194
|
-
// to, value — chain-dependent.
|
|
195
|
-
// Solana: API deletes both from the echo; skip entirely.
|
|
196
|
-
// EVM: compare as-is (value is not modified by placeholder resolution).
|
|
197
|
-
if (!isSolana) {
|
|
198
|
-
violations.push(...compareOptionalAddress(realChainId, `${hookPath}.to`, requestHook.to, echoed.to));
|
|
199
|
-
violations.push(...compareOptionalString(`${hookPath}.value`, requestHook.value, echoed.value));
|
|
200
|
-
}
|
|
201
|
-
// data — eager placeholders are resolved by the API before echoing.
|
|
202
|
-
const { resolved: expectedData, errors } = resolveEagerPlaceholders(requestHook.data, realChainId, requestHook.placeHolders, responseTrades, mode);
|
|
203
|
-
for (const error of errors) {
|
|
204
|
-
const reasonText = PlaceholderResolutionErrorReason[error.reason];
|
|
205
|
-
violations.push({
|
|
206
|
-
code: ViolationCode.HookPlaceholderResolutionFailed,
|
|
207
|
-
message: `failed to resolve placeholder "${error.nameVariable}"`
|
|
208
|
-
+ ` (${reasonText})`,
|
|
209
|
-
path: `${hookPath}.data`,
|
|
210
|
-
actual: { nameVariable: error.nameVariable, reason: reasonText },
|
|
211
|
-
details: error.details,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
if (expectedData !== echoed.data) {
|
|
215
|
-
violations.push(echoMismatch(`${hookPath}.data`, expectedData, echoed.data));
|
|
216
|
-
}
|
|
217
|
-
// placeHolders — deep equality via stringify (same source → same serialization).
|
|
218
|
-
if (JSON.stringify(echoed.placeHolders)
|
|
219
|
-
!== JSON.stringify(requestHook.placeHolders)) {
|
|
220
|
-
violations.push(echoMismatch(`${hookPath}.placeHolders`, requestHook.placeHolders, echoed.placeHolders));
|
|
221
|
-
}
|
|
222
|
-
// gasCompensationInfo — deep equality or both undefined.
|
|
223
|
-
if (JSON.stringify(echoed.gasCompensationInfo)
|
|
224
|
-
!== JSON.stringify(requestHook.gasCompensationInfo)) {
|
|
225
|
-
violations.push(echoMismatch(`${hookPath}.gasCompensationInfo`, requestHook.gasCompensationInfo, echoed.gasCompensationInfo));
|
|
226
|
-
}
|
|
227
|
-
// type — strict equality (or both undefined).
|
|
228
|
-
if (echoed.type !== requestHook.type) {
|
|
229
|
-
violations.push(echoMismatch(`${hookPath}.type`, requestHook.type, echoed.type));
|
|
230
|
-
}
|
|
231
|
-
// gasLimit — strict string equality (or both undefined).
|
|
232
|
-
if (echoed.gasLimit !== requestHook.gasLimit) {
|
|
233
|
-
violations.push(echoMismatch(`${hookPath}.gasLimit`, requestHook.gasLimit, echoed.gasLimit));
|
|
234
|
-
}
|
|
235
|
-
return violations;
|
|
236
|
-
}
|
|
237
|
-
function echoMismatch(path, expected, actual) {
|
|
238
|
-
return {
|
|
239
|
-
code: ViolationCode.HookEchoMismatch,
|
|
240
|
-
message: "response hook field does not match request",
|
|
241
|
-
path,
|
|
242
|
-
expected,
|
|
243
|
-
actual,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
function compareOptionalString(path, expected, actual) {
|
|
247
|
-
if (expected === actual)
|
|
248
|
-
return [];
|
|
249
|
-
return [echoMismatch(path, expected, actual)];
|
|
250
|
-
}
|
|
251
|
-
function compareOptionalAddress(chainId, path, expected, actual) {
|
|
252
|
-
if (expected === undefined && actual === undefined)
|
|
253
|
-
return [];
|
|
254
|
-
if (expected === undefined || actual === undefined) {
|
|
255
|
-
return [echoMismatch(path, expected, actual)];
|
|
256
|
-
}
|
|
257
|
-
if (!addressEquals(chainId, expected, actual)) {
|
|
258
|
-
return [echoMismatch(path, expected, actual)];
|
|
103
|
+
if (JSON.stringify(hookPayload.hook) !== JSON.stringify(requestHook)) {
|
|
104
|
+
return [{
|
|
105
|
+
code: ViolationCode.HookEchoMismatch,
|
|
106
|
+
message: "response hook does not match request hook",
|
|
107
|
+
path: `${basePath}.hook`,
|
|
108
|
+
expected: requestHook,
|
|
109
|
+
actual: hookPayload.hook,
|
|
110
|
+
}];
|
|
259
111
|
}
|
|
260
112
|
return [];
|
|
261
113
|
}
|
|
@@ -283,10 +135,6 @@ function validateHookUniqueActionIds(hookPayload, basePath) {
|
|
|
283
135
|
}
|
|
284
136
|
// HIGH: each hook required action must have exactly one operation: [Hook] or [GasCompensation]
|
|
285
137
|
function validateHookActionsList(hookPayload, basePath) {
|
|
286
|
-
const realChainId = resolveChainIdOrSelf(hookPayload.hook.chainId);
|
|
287
|
-
const isSolana = getChainEngine(realChainId) === ChainEngine.Solana;
|
|
288
|
-
const allowedSet = isSolana ? SOLANA_HOOK_OPERATIONS_SET : EVM_HOOK_OPERATIONS_SET;
|
|
289
|
-
const allowedList = isSolana ? SOLANA_HOOK_OPERATIONS : EVM_HOOK_OPERATIONS;
|
|
290
138
|
const violations = [];
|
|
291
139
|
for (let i = 0; i < hookPayload.requiredActions.length; i++) {
|
|
292
140
|
const action = hookPayload.requiredActions[i];
|
|
@@ -301,85 +149,26 @@ function validateHookActionsList(hookPayload, basePath) {
|
|
|
301
149
|
});
|
|
302
150
|
continue;
|
|
303
151
|
}
|
|
304
|
-
if (!
|
|
152
|
+
if (!HOOK_OPERATIONS_SET.has(action.actions[0])) {
|
|
305
153
|
violations.push({
|
|
306
154
|
code: ViolationCode.HookActionsInvalid,
|
|
307
155
|
message: "hook required action operation must be Hook"
|
|
308
156
|
+ " or GasCompensation",
|
|
309
157
|
path: `${actionPath}.actions`,
|
|
310
|
-
expected:
|
|
158
|
+
expected: HOOK_OPERATIONS,
|
|
311
159
|
actual: action.actions,
|
|
312
160
|
});
|
|
313
161
|
}
|
|
314
162
|
}
|
|
315
163
|
return violations;
|
|
316
164
|
}
|
|
317
|
-
// HIGH: Delegate-op constraints for EVM hook requiredActions.
|
|
318
|
-
// * `Delegate` may appear at most once.
|
|
319
|
-
// * If present, it must be the first required action (the API emits
|
|
320
|
-
// Sign7702Authorization → Sign712MetaMaskCaveats* in this order).
|
|
321
|
-
// * Direct hooks (`hook.type === 'direct'`) must not contain `Delegate`
|
|
322
|
-
// (Direct mode skips EIP-7702 delegation entirely).
|
|
323
|
-
// Solana hooks bypass this check — `Delegate` is rejected upstream by
|
|
324
|
-
// `validateHookActionsList`.
|
|
325
|
-
function validateHookDelegateOp(hookPayload, basePath) {
|
|
326
|
-
const realChainId = resolveChainIdOrSelf(hookPayload.hook.chainId);
|
|
327
|
-
if (getChainEngine(realChainId) === ChainEngine.Solana)
|
|
328
|
-
return [];
|
|
329
|
-
const indices = [];
|
|
330
|
-
for (let i = 0; i < hookPayload.requiredActions.length; i++) {
|
|
331
|
-
const action = hookPayload.requiredActions[i];
|
|
332
|
-
const ops = action.actions;
|
|
333
|
-
if (ops.includes(EvmActionOperation.Delegate)) {
|
|
334
|
-
indices.push(i);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (indices.length === 0)
|
|
338
|
-
return [];
|
|
339
|
-
const violations = [];
|
|
340
|
-
if (hookPayload.hook.type === BundleHookType.Direct) {
|
|
341
|
-
violations.push({
|
|
342
|
-
code: ViolationCode.HookDelegateInDirect,
|
|
343
|
-
message: "Direct hook must not contain a Delegate operation"
|
|
344
|
-
+ " (Direct mode skips EIP-7702 delegation)",
|
|
345
|
-
path: `${basePath}.requiredActions`,
|
|
346
|
-
actual: indices,
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
if (indices.length > 1) {
|
|
350
|
-
violations.push({
|
|
351
|
-
code: ViolationCode.HookMultipleDelegate,
|
|
352
|
-
message: "hook requiredActions must contain at most one Delegate",
|
|
353
|
-
path: `${basePath}.requiredActions`,
|
|
354
|
-
expected: 1,
|
|
355
|
-
actual: indices,
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
if (indices[0] !== 0) {
|
|
359
|
-
violations.push({
|
|
360
|
-
code: ViolationCode.HookDelegateNotFirst,
|
|
361
|
-
message: "if Delegate is present in hook requiredActions, it must be"
|
|
362
|
-
+ " the first action",
|
|
363
|
-
path: `${basePath}.requiredActions[${indices[0]}]`,
|
|
364
|
-
expected: 0,
|
|
365
|
-
actual: indices[0],
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
return violations;
|
|
369
|
-
}
|
|
370
165
|
// CRITICAL: GasCompensation must only appear when gasCompensationInfo exists
|
|
371
166
|
function validateGasCompensationRequiresInfo(hookPayload, basePath, request) {
|
|
372
167
|
const hasGasCompensationInfo = (request.preHooks ?? []).some(h => h.gasCompensationInfo !== undefined);
|
|
373
|
-
const realChainId = resolveChainIdOrSelf(hookPayload.hook.chainId);
|
|
374
|
-
const isSolana = getChainEngine(realChainId) === ChainEngine.Solana;
|
|
375
|
-
const gasCompensationOp = isSolana
|
|
376
|
-
? SolanaActionOperation.GasCompensation
|
|
377
|
-
: EvmActionOperation.GasCompensation;
|
|
378
168
|
const violations = [];
|
|
379
169
|
for (let i = 0; i < hookPayload.requiredActions.length; i++) {
|
|
380
170
|
const action = hookPayload.requiredActions[i];
|
|
381
|
-
const hasGasCompensation = action.actions
|
|
382
|
-
.includes(gasCompensationOp);
|
|
171
|
+
const hasGasCompensation = action.actions.includes("GasCompensation");
|
|
383
172
|
if (hasGasCompensation && !hasGasCompensationInfo) {
|
|
384
173
|
violations.push({
|
|
385
174
|
code: ViolationCode.HookGasCompensationWithoutInfo,
|
|
@@ -392,20 +181,18 @@ function validateGasCompensationRequiresInfo(hookPayload, basePath, request) {
|
|
|
392
181
|
}
|
|
393
182
|
return violations;
|
|
394
183
|
}
|
|
395
|
-
function dispatchChainSpecificValidation(hookPayload, requestHook, basePath, quoteMode, request
|
|
396
|
-
const
|
|
397
|
-
if (
|
|
184
|
+
function dispatchChainSpecificValidation(hookPayload, requestHook, basePath, quoteMode, request) {
|
|
185
|
+
const chainId = hookPayload.hook.chainId;
|
|
186
|
+
if (chainId === ChainId.Solana) {
|
|
398
187
|
const srcChainAuthorityAddress = findSolanaSrcChainAuthority(request);
|
|
399
|
-
return validateSolanaHookActions(hookPayload, requestHook, basePath, quoteMode, srcChainAuthorityAddress, request
|
|
188
|
+
return validateSolanaHookActions(hookPayload, requestHook, basePath, quoteMode, srcChainAuthorityAddress, request);
|
|
400
189
|
}
|
|
401
|
-
|
|
190
|
+
// AI: EVM hook validation dispatch will go here when implemented
|
|
191
|
+
return [];
|
|
402
192
|
}
|
|
403
193
|
function findSolanaSrcChainAuthority(request) {
|
|
404
194
|
for (const trade of request.trades) {
|
|
405
|
-
|
|
406
|
-
if (realChainId === undefined)
|
|
407
|
-
continue;
|
|
408
|
-
if (getChainEngine(realChainId) === ChainEngine.Solana) {
|
|
195
|
+
if (trade.srcChainId === ChainId.Solana) {
|
|
409
196
|
return trade.srcChainAuthorityAddress;
|
|
410
197
|
}
|
|
411
198
|
}
|