@debridge-finance/borderless-validation-sdk 0.4.1 → 0.5.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/README.md +15 -0
- package/dist/cjs/helpers/solana-hook-instructions.js +66 -0
- package/dist/cjs/helpers/solana-hook-instructions.js.map +1 -0
- package/dist/cjs/helpers/solana-instruction.js +5 -1
- package/dist/cjs/helpers/solana-instruction.js.map +1 -1
- package/dist/cjs/helpers/solana-intent-id.js +11 -1
- package/dist/cjs/helpers/solana-intent-id.js.map +1 -1
- package/dist/cjs/helpers/solana-intent-instruction.js +106 -0
- package/dist/cjs/helpers/solana-intent-instruction.js.map +1 -0
- package/dist/cjs/types/enums.js +2 -8
- package/dist/cjs/types/enums.js.map +1 -1
- package/dist/cjs/types/response.js +12 -2
- package/dist/cjs/types/response.js.map +1 -1
- package/dist/cjs/types/validation.js +30 -0
- package/dist/cjs/types/validation.js.map +1 -1
- package/dist/cjs/validations/evm-required-actions.js +2 -0
- package/dist/cjs/validations/evm-required-actions.js.map +1 -1
- package/dist/cjs/validations/evm-sign712-shared.js +2 -5
- package/dist/cjs/validations/evm-sign712-shared.js.map +1 -1
- package/dist/cjs/validations/evm-transaction-scope.js +29 -0
- package/dist/cjs/validations/evm-transaction-scope.js.map +1 -0
- package/dist/cjs/validations/hooks-common.js +18 -15
- package/dist/cjs/validations/hooks-common.js.map +1 -1
- package/dist/cjs/validations/low-input-sweep-bundle.js +94 -0
- package/dist/cjs/validations/low-input-sweep-bundle.js.map +1 -0
- package/dist/cjs/validations/low-input-sweep-hook.js +204 -0
- package/dist/cjs/validations/low-input-sweep-hook.js.map +1 -0
- package/dist/cjs/validations/low-input-sweep.js +294 -0
- package/dist/cjs/validations/low-input-sweep.js.map +1 -0
- package/dist/cjs/validations/on-chain-intent-submission.js +16 -5
- package/dist/cjs/validations/on-chain-intent-submission.js.map +1 -1
- package/dist/cjs/validations/ptp.js +13 -15
- package/dist/cjs/validations/ptp.js.map +1 -1
- package/dist/cjs/validations/solana-embedded-intent.js +187 -0
- package/dist/cjs/validations/solana-embedded-intent.js.map +1 -0
- package/dist/cjs/validations/solana-hook.js +1 -1
- package/dist/cjs/validations/solana-hook.js.map +1 -1
- package/dist/cjs/validations/solana-intent-accounts.js +99 -0
- package/dist/cjs/validations/solana-intent-accounts.js.map +1 -0
- package/dist/cjs/validations/solana-low-input-sweep.js +251 -0
- package/dist/cjs/validations/solana-low-input-sweep.js.map +1 -0
- package/dist/cjs/validations/solana-required-actions.js +6 -2
- package/dist/cjs/validations/solana-required-actions.js.map +1 -1
- package/dist/cjs/validations/solana-transaction.js +25 -5
- package/dist/cjs/validations/solana-transaction.js.map +1 -1
- package/dist/cjs/validations/trade-grouping.js +23 -26
- package/dist/cjs/validations/trade-grouping.js.map +1 -1
- package/dist/cjs/validations/trade-matching.js +21 -13
- package/dist/cjs/validations/trade-matching.js.map +1 -1
- package/dist/cjs/validations/trades.js +33 -20
- package/dist/cjs/validations/trades.js.map +1 -1
- package/dist/cjs/validator.js +14 -5
- package/dist/cjs/validator.js.map +1 -1
- package/dist/cjs/whitelists/environment-config.js +45 -3
- package/dist/cjs/whitelists/environment-config.js.map +1 -1
- package/dist/cjs/whitelists/types.js.map +1 -1
- package/dist/esm/helpers/solana-hook-instructions.js +63 -0
- package/dist/esm/helpers/solana-hook-instructions.js.map +1 -0
- package/dist/esm/helpers/solana-instruction.js +4 -0
- package/dist/esm/helpers/solana-instruction.js.map +1 -1
- package/dist/esm/helpers/solana-intent-id.js +10 -1
- package/dist/esm/helpers/solana-intent-id.js.map +1 -1
- package/dist/esm/helpers/solana-intent-instruction.js +98 -0
- package/dist/esm/helpers/solana-intent-instruction.js.map +1 -0
- package/dist/esm/types/enums.js +0 -6
- package/dist/esm/types/enums.js.map +1 -1
- package/dist/esm/types/response.js +12 -2
- package/dist/esm/types/response.js.map +1 -1
- package/dist/esm/types/validation.js +30 -0
- package/dist/esm/types/validation.js.map +1 -1
- package/dist/esm/validations/evm-required-actions.js +2 -0
- package/dist/esm/validations/evm-required-actions.js.map +1 -1
- package/dist/esm/validations/evm-sign712-shared.js +2 -5
- package/dist/esm/validations/evm-sign712-shared.js.map +1 -1
- package/dist/esm/validations/evm-transaction-scope.js +26 -0
- package/dist/esm/validations/evm-transaction-scope.js.map +1 -0
- package/dist/esm/validations/hooks-common.js +18 -15
- package/dist/esm/validations/hooks-common.js.map +1 -1
- package/dist/esm/validations/low-input-sweep-bundle.js +91 -0
- package/dist/esm/validations/low-input-sweep-bundle.js.map +1 -0
- package/dist/esm/validations/low-input-sweep-hook.js +201 -0
- package/dist/esm/validations/low-input-sweep-hook.js.map +1 -0
- package/dist/esm/validations/low-input-sweep.js +291 -0
- package/dist/esm/validations/low-input-sweep.js.map +1 -0
- package/dist/esm/validations/on-chain-intent-submission.js +17 -6
- package/dist/esm/validations/on-chain-intent-submission.js.map +1 -1
- package/dist/esm/validations/ptp.js +13 -15
- package/dist/esm/validations/ptp.js.map +1 -1
- package/dist/esm/validations/solana-embedded-intent.js +184 -0
- package/dist/esm/validations/solana-embedded-intent.js.map +1 -0
- package/dist/esm/validations/solana-hook.js +2 -2
- package/dist/esm/validations/solana-hook.js.map +1 -1
- package/dist/esm/validations/solana-intent-accounts.js +95 -0
- package/dist/esm/validations/solana-intent-accounts.js.map +1 -0
- package/dist/esm/validations/solana-low-input-sweep.js +248 -0
- package/dist/esm/validations/solana-low-input-sweep.js.map +1 -0
- package/dist/esm/validations/solana-required-actions.js +6 -2
- package/dist/esm/validations/solana-required-actions.js.map +1 -1
- package/dist/esm/validations/solana-transaction.js +25 -5
- package/dist/esm/validations/solana-transaction.js.map +1 -1
- package/dist/esm/validations/trade-grouping.js +23 -26
- package/dist/esm/validations/trade-grouping.js.map +1 -1
- package/dist/esm/validations/trade-matching.js +21 -13
- package/dist/esm/validations/trade-matching.js.map +1 -1
- package/dist/esm/validations/trades.js +33 -20
- package/dist/esm/validations/trades.js.map +1 -1
- package/dist/esm/validator.js +14 -5
- package/dist/esm/validator.js.map +1 -1
- package/dist/esm/whitelists/environment-config.js +45 -3
- package/dist/esm/whitelists/environment-config.js.map +1 -1
- package/dist/esm/whitelists/types.js.map +1 -1
- package/dist/types/helpers/solana-hook-instructions.d.ts +14 -0
- package/dist/types/helpers/solana-hook-instructions.d.ts.map +1 -0
- package/dist/types/helpers/solana-instruction.d.ts +3 -0
- package/dist/types/helpers/solana-instruction.d.ts.map +1 -1
- package/dist/types/helpers/solana-intent-id.d.ts +5 -0
- package/dist/types/helpers/solana-intent-id.d.ts.map +1 -1
- package/dist/types/helpers/solana-intent-instruction.d.ts +34 -0
- package/dist/types/helpers/solana-intent-instruction.d.ts.map +1 -0
- package/dist/types/types/enums.d.ts +0 -4
- package/dist/types/types/enums.d.ts.map +1 -1
- package/dist/types/types/response.d.ts +71 -1
- package/dist/types/types/response.d.ts.map +1 -1
- package/dist/types/types/validation.d.ts +29 -0
- package/dist/types/types/validation.d.ts.map +1 -1
- package/dist/types/validations/evm-required-actions.d.ts.map +1 -1
- package/dist/types/validations/evm-sign712-shared.d.ts.map +1 -1
- package/dist/types/validations/evm-transaction-scope.d.ts +3 -0
- package/dist/types/validations/evm-transaction-scope.d.ts.map +1 -0
- package/dist/types/validations/hooks-common.d.ts +2 -1
- package/dist/types/validations/hooks-common.d.ts.map +1 -1
- package/dist/types/validations/low-input-sweep-bundle.d.ts +3 -0
- package/dist/types/validations/low-input-sweep-bundle.d.ts.map +1 -0
- package/dist/types/validations/low-input-sweep-hook.d.ts +9 -0
- package/dist/types/validations/low-input-sweep-hook.d.ts.map +1 -0
- package/dist/types/validations/low-input-sweep.d.ts +15 -0
- package/dist/types/validations/low-input-sweep.d.ts.map +1 -0
- package/dist/types/validations/on-chain-intent-submission.d.ts.map +1 -1
- package/dist/types/validations/ptp.d.ts +3 -2
- package/dist/types/validations/ptp.d.ts.map +1 -1
- package/dist/types/validations/solana-embedded-intent.d.ts +3 -0
- package/dist/types/validations/solana-embedded-intent.d.ts.map +1 -0
- package/dist/types/validations/solana-hook.d.ts.map +1 -1
- package/dist/types/validations/solana-intent-accounts.d.ts +14 -0
- package/dist/types/validations/solana-intent-accounts.d.ts.map +1 -0
- package/dist/types/validations/solana-low-input-sweep.d.ts +9 -0
- package/dist/types/validations/solana-low-input-sweep.d.ts.map +1 -0
- package/dist/types/validations/solana-required-actions.d.ts.map +1 -1
- package/dist/types/validations/solana-transaction.d.ts.map +1 -1
- package/dist/types/validations/trade-grouping.d.ts +1 -1
- package/dist/types/validations/trade-grouping.d.ts.map +1 -1
- package/dist/types/validations/trade-matching.d.ts +1 -1
- package/dist/types/validations/trade-matching.d.ts.map +1 -1
- package/dist/types/validations/trades.d.ts +1 -1
- package/dist/types/validations/trades.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/types.d.ts +1 -0
- package/dist/types/whitelists/types.d.ts.map +1 -1
- package/package.json +70 -66
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { maxUint64, maxUint256 } from "viem";
|
|
2
|
+
import { ChainEngine, SpecialAmount, ViolationCode, } from "../types/index.js";
|
|
3
|
+
import { addressEquals, getChainEngine, isAddressRepresentationValidForChain } from "../helpers/address.js";
|
|
4
|
+
import { parseIntegerString } from "../helpers/integer.js";
|
|
5
|
+
import { resolveChainId } from "../whitelists/subscription-to-chain-id.js";
|
|
6
|
+
import { interactionsEqual } from "../helpers/interactions.js";
|
|
7
|
+
import { hexEquals } from "../helpers/hex.js";
|
|
8
|
+
import { isMaxAmountMode } from "../helpers/amount.js";
|
|
9
|
+
import { validateLowInputSweepBundle } from "./low-input-sweep-bundle.js";
|
|
10
|
+
// CRITICAL: a sweep may remove only its own request trade from intent validation.
|
|
11
|
+
// The economic decision to sweep is delegated to the API, not recomputed from
|
|
12
|
+
// response-supplied prices. Transfer authorization is checked separately.
|
|
13
|
+
export function validateLowInputSweeps(request, response, quoteMode) {
|
|
14
|
+
const result = {
|
|
15
|
+
sweeps: [],
|
|
16
|
+
violations: validateLowInputSweepBundle(request, response, quoteMode),
|
|
17
|
+
};
|
|
18
|
+
// Safe skip: prohibited modes or missing surviving trades/intents are
|
|
19
|
+
// reported above. Their metadata cannot remove request trades from the
|
|
20
|
+
// ordinary validation flow.
|
|
21
|
+
if (result.violations.length > 0)
|
|
22
|
+
return result;
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
const sweeps = response.lowInputSweeps ?? [];
|
|
25
|
+
for (let i = 0; i < sweeps.length; i++) {
|
|
26
|
+
const sweep = sweeps[i];
|
|
27
|
+
const violations = validateSweepIndex(sweep.tradeIndex, request.trades.length, seen, i);
|
|
28
|
+
seen.add(sweep.tradeIndex);
|
|
29
|
+
if (violations.length > 0) {
|
|
30
|
+
result.violations.push(...violations);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
// Only entries without metadata/amount violations become sweep contexts.
|
|
34
|
+
// Their transfer hooks and recipients are validated in a separate pass.
|
|
35
|
+
const entry = validateSweepTrade(sweep, request.trades[sweep.tradeIndex], i);
|
|
36
|
+
result.violations.push(...entry.violations);
|
|
37
|
+
result.sweeps.push(...entry.sweeps);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
// Checks that an index refers to an existing original request trade and has not
|
|
42
|
+
// appeared in earlier sweep metadata. It is not an index into response.trades.
|
|
43
|
+
function validateSweepIndex(tradeIndex, tradeCount, seen, sweepIndex) {
|
|
44
|
+
const violations = [];
|
|
45
|
+
const path = `response.lowInputSweeps[${sweepIndex}].tradeIndex`;
|
|
46
|
+
// CRITICAL: indices must identify distinct entries in the original request.
|
|
47
|
+
if (!Number.isSafeInteger(tradeIndex)
|
|
48
|
+
|| tradeIndex < 0
|
|
49
|
+
|| tradeIndex >= tradeCount) {
|
|
50
|
+
violations.push({
|
|
51
|
+
code: ViolationCode.LowInputSweepTradeIndexInvalid,
|
|
52
|
+
message: "lowInputSweeps.tradeIndex must identify an existing request trade",
|
|
53
|
+
path,
|
|
54
|
+
expected: { min: 0, maxExclusive: tradeCount },
|
|
55
|
+
actual: tradeIndex,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else if (seen.has(tradeIndex)) {
|
|
59
|
+
violations.push({
|
|
60
|
+
code: ViolationCode.LowInputSweepTradeDuplicate,
|
|
61
|
+
message: "a request trade must not be swept more than once",
|
|
62
|
+
path,
|
|
63
|
+
actual: tradeIndex,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return violations;
|
|
67
|
+
}
|
|
68
|
+
// Binds one sweep entry to its original request trade and parses its transfer amount.
|
|
69
|
+
// Returns no sweep context when any check fails, so the trade remains in ordinary validation.
|
|
70
|
+
function validateSweepTrade(sweep, trade, sweepIndex) {
|
|
71
|
+
const violations = [];
|
|
72
|
+
const realChainId = resolveChainId(trade.srcChainId);
|
|
73
|
+
const destinationChainId = resolveChainId(trade.dstChainId);
|
|
74
|
+
const basePath = `response.lowInputSweeps[${sweepIndex}]`;
|
|
75
|
+
// Both chains are needed to compare originalTrade addresses using the right formats.
|
|
76
|
+
if (realChainId === undefined || destinationChainId === undefined) {
|
|
77
|
+
violations.push({
|
|
78
|
+
code: ViolationCode.UnknownSubscriptionId,
|
|
79
|
+
message: "a swept request trade must use known source and destination subscriptionIds",
|
|
80
|
+
path: `request.trades[${sweep.tradeIndex}]`,
|
|
81
|
+
actual: { srcChainId: trade.srcChainId, dstChainId: trade.dstChainId },
|
|
82
|
+
});
|
|
83
|
+
return { sweeps: [], violations };
|
|
84
|
+
}
|
|
85
|
+
violations.push(...validateSweepIdentity(sweep, trade, realChainId, destinationChainId, basePath));
|
|
86
|
+
violations.push(...validateSweepSourceAuthority(trade, realChainId, basePath));
|
|
87
|
+
const amount = validateSweepAmount(sweep, trade, realChainId, basePath, violations);
|
|
88
|
+
if (violations.length > 0
|
|
89
|
+
|| amount === undefined
|
|
90
|
+
|| trade.srcChainAuthorityAddress === undefined) {
|
|
91
|
+
// Safe skip: invalid identity/authority and unbound amounts are reported
|
|
92
|
+
// above; these entries do not remove request trades.
|
|
93
|
+
return { sweeps: [], violations };
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
sweeps: [{
|
|
97
|
+
sweepIndex,
|
|
98
|
+
requestIndex: sweep.tradeIndex,
|
|
99
|
+
realChainId,
|
|
100
|
+
trade,
|
|
101
|
+
sender: trade.srcChainAuthorityAddress,
|
|
102
|
+
amount,
|
|
103
|
+
}],
|
|
104
|
+
violations,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Requires a valid source authority to bind the transfer.
|
|
108
|
+
function validateSweepSourceAuthority(trade, realChainId, basePath) {
|
|
109
|
+
const violations = [];
|
|
110
|
+
// CRITICAL: the transfer must be bound to a valid source authority for its chain.
|
|
111
|
+
if (trade.srcChainAuthorityAddress === undefined
|
|
112
|
+
|| !isAddressRepresentationValidForChain(realChainId, trade.srcChainAuthorityAddress)) {
|
|
113
|
+
violations.push({
|
|
114
|
+
code: ViolationCode.LowInputSweepSourceAuthorityInvalid,
|
|
115
|
+
message: "a swept trade must have a valid source authority",
|
|
116
|
+
path: `${basePath}.originalTrade.srcChainAuthorityAddress`,
|
|
117
|
+
actual: { address: trade.srcChainAuthorityAddress, realChainId },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return violations;
|
|
121
|
+
}
|
|
122
|
+
// Compares duplicated sweep metadata with the request, using chain-aware address
|
|
123
|
+
// comparison and field-specific equivalence rules for the originalTrade echo.
|
|
124
|
+
function validateSweepIdentity(sweep, trade, sourceChainId, destinationChainId, basePath) {
|
|
125
|
+
const violations = [];
|
|
126
|
+
// CRITICAL: the duplicated metadata cannot redefine the asset or source chain.
|
|
127
|
+
if (sweep.chainId !== trade.srcChainId) {
|
|
128
|
+
violations.push(sweepMismatch(ViolationCode.LowInputSweepChainIdMismatch, `${basePath}.chainId`, trade.srcChainId, sweep.chainId));
|
|
129
|
+
}
|
|
130
|
+
if (!addressEquals(sourceChainId, sweep.tokenAddress, trade.srcChainTokenIn)) {
|
|
131
|
+
violations.push(sweepMismatch(ViolationCode.LowInputSweepTokenAddressMismatch, `${basePath}.tokenAddress`, trade.srcChainTokenIn, sweep.tokenAddress));
|
|
132
|
+
}
|
|
133
|
+
return [
|
|
134
|
+
...violations,
|
|
135
|
+
...validateOriginalTrade(sweep, trade, sourceChainId, destinationChainId, basePath),
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
// CRITICAL: preserve every request field except the explicitly supported
|
|
139
|
+
// amount/default normalization.
|
|
140
|
+
function validateOriginalTrade(sweep, trade, sourceChainId, destinationChainId, basePath) {
|
|
141
|
+
const violations = [];
|
|
142
|
+
// Max-like amounts are resolved before sweep planning, with operating expenses
|
|
143
|
+
// no longer prepended to that trade. All other fields retain their request values.
|
|
144
|
+
const expectedTrade = isMaxAmountMode(trade.srcChainTokenInAmount)
|
|
145
|
+
? { ...trade, srcChainTokenInAmount: sweep.amount, prependOperatingExpenses: false }
|
|
146
|
+
: trade;
|
|
147
|
+
// Inspect fields from both objects so an added response field is not silently ignored.
|
|
148
|
+
// A sweep replaces the whole trade, including its trade-level calls. Echoing
|
|
149
|
+
// those calls does not authorize executing them: the hook matcher accepts only
|
|
150
|
+
// the bound transfer and permitted auxiliary instructions. Bundle hooks are unchanged.
|
|
151
|
+
const fields = new Set([
|
|
152
|
+
...Object.keys(expectedTrade),
|
|
153
|
+
...Object.keys(sweep.originalTrade),
|
|
154
|
+
]);
|
|
155
|
+
for (const field of fields) {
|
|
156
|
+
if (!originalTradeFieldMatches(field, expectedTrade, sweep.originalTrade, sourceChainId, destinationChainId)) {
|
|
157
|
+
violations.push(sweepMismatch(ViolationCode.LowInputSweepOriginalTradeMismatch, `${basePath}.originalTrade.${field}`, expectedTrade[field], sweep.originalTrade[field]));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return violations;
|
|
161
|
+
}
|
|
162
|
+
const SOURCE_ADDRESS_FIELDS = [
|
|
163
|
+
"srcChainTokenIn", "srcChainAuthorityAddress", "srcAllowedCancelBeneficiary",
|
|
164
|
+
];
|
|
165
|
+
const DESTINATION_ADDRESS_FIELDS = [
|
|
166
|
+
"dstChainTokenOut", "dstChainTokenOutRecipient", "dstChainAuthorityAddress", "allowedTaker",
|
|
167
|
+
];
|
|
168
|
+
// Compares one echoed request field without requiring identical address casing or
|
|
169
|
+
// numeric spelling. Returns false for any difference outside the accepted field rules.
|
|
170
|
+
function originalTradeFieldMatches(field, requestTrade, originalTrade, sourceChainId, destinationChainId) {
|
|
171
|
+
const expected = requestTrade[field];
|
|
172
|
+
const actual = originalTrade[field];
|
|
173
|
+
// These request flags default to false; explicit false and omission are equivalent.
|
|
174
|
+
if (field === "prependOperatingExpenses" || field === "ptp") {
|
|
175
|
+
return (expected ?? false) === (actual ?? false);
|
|
176
|
+
}
|
|
177
|
+
if (field === "preInteractions" || field === "postInteractions") {
|
|
178
|
+
return interactionsEqual(sourceChainId, requestTrade[field], originalTrade[field]);
|
|
179
|
+
}
|
|
180
|
+
// An omitted destination amount means 'auto', including in the echoed original trade.
|
|
181
|
+
if (field === "dstChainTokenOutAmount") {
|
|
182
|
+
return integerOrLiteralEquals(expected === undefined ? SpecialAmount.Auto : String(expected), actual === undefined ? SpecialAmount.Auto : String(actual));
|
|
183
|
+
}
|
|
184
|
+
// For other fields, omission must match before applying representation equivalence.
|
|
185
|
+
if (expected === undefined || actual === undefined)
|
|
186
|
+
return expected === actual;
|
|
187
|
+
if (SOURCE_ADDRESS_FIELDS.includes(field) || DESTINATION_ADDRESS_FIELDS.includes(field)) {
|
|
188
|
+
const chainId = SOURCE_ADDRESS_FIELDS.includes(field) ? sourceChainId : destinationChainId;
|
|
189
|
+
return typeof expected === "string"
|
|
190
|
+
&& typeof actual === "string"
|
|
191
|
+
&& addressEquals(chainId, expected, actual);
|
|
192
|
+
}
|
|
193
|
+
if (field === "srcChainTokenInAmount") {
|
|
194
|
+
return integerOrLiteralEquals(String(expected), String(actual));
|
|
195
|
+
}
|
|
196
|
+
if (field === "dlnMetadata")
|
|
197
|
+
return hexEquals(requestTrade[field], originalTrade[field]);
|
|
198
|
+
return expected === actual;
|
|
199
|
+
}
|
|
200
|
+
// Accepts identical literals or equal parsed integers. Different non-numeric
|
|
201
|
+
// amount modes cannot be treated as equivalent and return false.
|
|
202
|
+
function integerOrLiteralEquals(expected, actual) {
|
|
203
|
+
if (expected === actual)
|
|
204
|
+
return true;
|
|
205
|
+
try {
|
|
206
|
+
return parseIntegerString(expected) === parseIntegerString(actual);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// Non-numeric amount modes must have the same literal spelling.
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Returns the parsed transfer amount and records violations through the supplied array.
|
|
214
|
+
// Auto is rejected; fixed amounts must match the request; max-like amounts are API-resolved.
|
|
215
|
+
// A returned amount does not imply success: the caller must also check accumulated violations.
|
|
216
|
+
function validateSweepAmount(sweep, trade, realChainId, basePath, violations) {
|
|
217
|
+
// HIGH: auto source amounts cannot be replaced by sweep transfers.
|
|
218
|
+
if (trade.srcChainTokenInAmount === SpecialAmount.Auto) {
|
|
219
|
+
violations.push({
|
|
220
|
+
code: ViolationCode.LowInputSweepWithAutoSourceAmount,
|
|
221
|
+
message: "low-input sweeps are not allowed when srcChainTokenInAmount=auto",
|
|
222
|
+
path: `request.trades[${sweep.tradeIndex}].srcChainTokenInAmount`,
|
|
223
|
+
actual: trade.srcChainTokenInAmount,
|
|
224
|
+
});
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
const amount = parseSweepTransferAmount(sweep.amount, realChainId, `${basePath}.amount`, violations);
|
|
228
|
+
// Safe skip: malformed/out-of-range metadata amounts are reported by the parser.
|
|
229
|
+
if (amount === undefined)
|
|
230
|
+
return undefined;
|
|
231
|
+
// Max-like requests authorize API balance resolution, not a fixed number.
|
|
232
|
+
// The SDK cannot independently verify the balance or reserved portion.
|
|
233
|
+
// Hook validation still requires the exact resolved amount and whitelisted recipient.
|
|
234
|
+
if (isMaxAmountMode(trade.srcChainTokenInAmount))
|
|
235
|
+
return amount;
|
|
236
|
+
violations.push(...validateFixedSweepAmount(amount, trade.srcChainTokenInAmount, sweep, realChainId, basePath));
|
|
237
|
+
return amount;
|
|
238
|
+
}
|
|
239
|
+
// Parses a positive transfer amount within the source engine's integer range.
|
|
240
|
+
// Returns `undefined` and appends a violation for malformed or out-of-range values.
|
|
241
|
+
function parseSweepTransferAmount(value, realChainId, path, violations) {
|
|
242
|
+
try {
|
|
243
|
+
const amount = parseIntegerString(value);
|
|
244
|
+
// Solana transfer instructions encode uint64; EVM/Tron amounts use uint256.
|
|
245
|
+
const maximum = getChainEngine(realChainId) === ChainEngine.Solana ? maxUint64 : maxUint256;
|
|
246
|
+
// CRITICAL: fixed and API-resolved transfer amounts must be positive and encodable.
|
|
247
|
+
if (amount > 0n && amount <= maximum)
|
|
248
|
+
return amount;
|
|
249
|
+
violations.push({
|
|
250
|
+
code: ViolationCode.LowInputSweepAmountOutOfRange,
|
|
251
|
+
message: "amount must be > 0 and fit the source chain transfer amount",
|
|
252
|
+
path,
|
|
253
|
+
expected: { min: "1", max: maximum.toString() },
|
|
254
|
+
actual: value,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
violations.push({
|
|
259
|
+
code: ViolationCode.LowInputSweepAmountNotInteger,
|
|
260
|
+
message: "amount must be an integer",
|
|
261
|
+
path,
|
|
262
|
+
actual: value,
|
|
263
|
+
details: error,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
// Checks a fixed request amount's format/range and requires exact numeric equality
|
|
269
|
+
// with the metadata amount. The transfer instruction is compared separately.
|
|
270
|
+
function validateFixedSweepAmount(amount, requestAmount, sweep, realChainId, basePath) {
|
|
271
|
+
const violations = [];
|
|
272
|
+
const requested = parseSweepTransferAmount(requestAmount, realChainId, `request.trades[${sweep.tradeIndex}].srcChainTokenInAmount`, violations);
|
|
273
|
+
// Safe skip: an invalid fixed request amount is reported by the parser.
|
|
274
|
+
if (requested === undefined)
|
|
275
|
+
return violations;
|
|
276
|
+
// CRITICAL: fixed requests retain their exact spending limit.
|
|
277
|
+
if (amount !== requested) {
|
|
278
|
+
violations.push({
|
|
279
|
+
code: ViolationCode.LowInputSweepAmountMismatch,
|
|
280
|
+
message: "sweep amount must equal the fixed request source amount",
|
|
281
|
+
path: `${basePath}.amount`,
|
|
282
|
+
expected: requested.toString(),
|
|
283
|
+
actual: sweep.amount,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return violations;
|
|
287
|
+
}
|
|
288
|
+
function sweepMismatch(code, path, expected, actual, message = "low-input sweep metadata must match the original request trade") {
|
|
289
|
+
return { code, message, path, expected, actual };
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=low-input-sweep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"low-input-sweep.js","sourceRoot":"","sources":["../../../src/validations/low-input-sweep.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ChainEngine, ViolationCode, } from "../types/index.js";
|
|
1
|
+
import { ApprovalMode, ChainEngine, ViolationCode, } from "../types/index.js";
|
|
2
2
|
import { getChainEngine } from "../helpers/address.js";
|
|
3
3
|
import { resolveChainId } from "../whitelists/subscription-to-chain-id.js";
|
|
4
4
|
const SUPPORTED_ON_CHAIN_INTENT_SOURCE_ENGINES = [
|
|
5
5
|
ChainEngine.EVM,
|
|
6
|
-
ChainEngine.
|
|
6
|
+
ChainEngine.Solana,
|
|
7
7
|
];
|
|
8
8
|
export function validateOnChainIntentSubmission(request) {
|
|
9
9
|
if (request.intentOnChainSubmission !== true)
|
|
@@ -11,9 +11,22 @@ export function validateOnChainIntentSubmission(request) {
|
|
|
11
11
|
return [
|
|
12
12
|
...validateAccountAbstractionDisabled(request),
|
|
13
13
|
...validateRefillDisabled(request),
|
|
14
|
+
...validatePermitDisabled(request),
|
|
14
15
|
...validateSupportedSourceChains(request),
|
|
15
16
|
];
|
|
16
17
|
}
|
|
18
|
+
// HIGH: on-chain intent submission does not use a permit action.
|
|
19
|
+
function validatePermitDisabled(request) {
|
|
20
|
+
if (request.approvalMode !== ApprovalMode.Permit)
|
|
21
|
+
return [];
|
|
22
|
+
return [{
|
|
23
|
+
code: ViolationCode.OnChainIntentForbiddenWithPermit,
|
|
24
|
+
message: "on-chain intent submission is incompatible with permit approval mode",
|
|
25
|
+
path: "request.approvalMode",
|
|
26
|
+
expected: ApprovalMode.Approve,
|
|
27
|
+
actual: request.approvalMode,
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
17
30
|
// HIGH: on-chain intent submission is incompatible with account abstraction.
|
|
18
31
|
function validateAccountAbstractionDisabled(request) {
|
|
19
32
|
if (!request.enableAccountAbstraction)
|
|
@@ -38,9 +51,7 @@ function validateRefillDisabled(request) {
|
|
|
38
51
|
actual: request.useRefill,
|
|
39
52
|
}];
|
|
40
53
|
}
|
|
41
|
-
// HIGH:
|
|
42
|
-
// EVM and Tron source chains (Solana is not currently supported by the API for
|
|
43
|
-
// this flow).
|
|
54
|
+
// HIGH: reject only source engines for which the API has no on-chain submission flow.
|
|
44
55
|
function validateSupportedSourceChains(request) {
|
|
45
56
|
const violations = [];
|
|
46
57
|
for (let i = 0; i < request.trades.length; i++) {
|
|
@@ -54,7 +65,7 @@ function validateSupportedSourceChains(request) {
|
|
|
54
65
|
continue;
|
|
55
66
|
violations.push({
|
|
56
67
|
code: ViolationCode.OnChainIntentUnsupportedSourceChain,
|
|
57
|
-
message: "on-chain intent submission requires an EVM or
|
|
68
|
+
message: "on-chain intent submission requires an EVM or Solana source chain",
|
|
58
69
|
path: `request.trades[${i}].srcChainId`,
|
|
59
70
|
expected: {
|
|
60
71
|
chainEngines: SUPPORTED_ON_CHAIN_INTENT_SOURCE_ENGINES.map(engine => ChainEngine[engine]),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-chain-intent-submission.js","sourceRoot":"","sources":["../../../src/validations/on-chain-intent-submission.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,WAAW,EAEX,aAAa,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE,MAAM,wCAAwC,GAA2B;IACrE,WAAW,CAAC,GAAG;IACf,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"on-chain-intent-submission.js","sourceRoot":"","sources":["../../../src/validations/on-chain-intent-submission.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,30 +1,28 @@
|
|
|
1
1
|
import { ViolationCode, SpecialAmount, } from "../types/index.js";
|
|
2
2
|
import { parseIntegerString } from "../helpers/integer.js";
|
|
3
3
|
import { isMaxAmountMode } from "../helpers/amount.js";
|
|
4
|
-
// CRITICAL: for each trade where
|
|
4
|
+
// CRITICAL: for each matched trade pair where requestTrade.ptp === true:
|
|
5
5
|
// - for fixed (not max-like/auto) source amounts,
|
|
6
|
-
//
|
|
7
|
-
// -
|
|
8
|
-
// -
|
|
9
|
-
// ===
|
|
6
|
+
// responseTrade.srcChainTokenIn.amount === requestTrade.srcChainTokenInAmount
|
|
7
|
+
// - responseTrade.dstChainTokenOut.amount === requestTrade.dstChainTokenOutAmount
|
|
8
|
+
// - responseTrade.dstChainTokenOut.recommendedAmount
|
|
9
|
+
// === responseTrade.dstChainTokenOut.amount
|
|
10
10
|
//
|
|
11
11
|
// The source request may use a max-like mode that the API resolves before
|
|
12
12
|
// building the response. Other accepted PTP amounts must be numeric.
|
|
13
13
|
// Parse errors are still reported defensively for malformed input or response data.
|
|
14
|
-
export function validatePtp(
|
|
14
|
+
export function validatePtp(tradePairs) {
|
|
15
15
|
const violations = [];
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const reqTrade = request.trades[i];
|
|
19
|
-
if (reqTrade.ptp !== true)
|
|
16
|
+
for (const pair of tradePairs) {
|
|
17
|
+
if (pair.requestTrade.ptp !== true)
|
|
20
18
|
continue;
|
|
21
|
-
violations.push(...validatePtpTrade(
|
|
19
|
+
violations.push(...validatePtpTrade(pair.requestTrade, pair.responseTrade, pair.requestIndex, pair.responseIndex));
|
|
22
20
|
}
|
|
23
21
|
return violations;
|
|
24
22
|
}
|
|
25
|
-
function validatePtpTrade(reqTrade, resTrade,
|
|
23
|
+
function validatePtpTrade(reqTrade, resTrade, requestIndex, responseIndex) {
|
|
26
24
|
const violations = [];
|
|
27
|
-
const basePath = `response.trades[${
|
|
25
|
+
const basePath = `response.trades[${responseIndex}]`;
|
|
28
26
|
// CRITICAL: srcChainTokenIn.amount must mirror request srcChainTokenInAmount.
|
|
29
27
|
const reqSrc = reqTrade.srcChainTokenInAmount;
|
|
30
28
|
// Safe skip: max-like source amounts are resolved from dynamic wallet state.
|
|
@@ -40,7 +38,7 @@ function validatePtpTrade(reqTrade, resTrade, tradeIndex) {
|
|
|
40
38
|
violations.push({
|
|
41
39
|
code: ViolationCode.PtpAmountInvalid,
|
|
42
40
|
message: "request.trades[].srcChainTokenInAmount is not a valid integer",
|
|
43
|
-
path: `request.trades[${
|
|
41
|
+
path: `request.trades[${requestIndex}].srcChainTokenInAmount`,
|
|
44
42
|
actual: reqSrc,
|
|
45
43
|
details: error,
|
|
46
44
|
});
|
|
@@ -82,7 +80,7 @@ function validatePtpTrade(reqTrade, resTrade, tradeIndex) {
|
|
|
82
80
|
violations.push({
|
|
83
81
|
code: ViolationCode.PtpAmountInvalid,
|
|
84
82
|
message: "request.trades[].dstChainTokenOutAmount is not a valid integer",
|
|
85
|
-
path: `request.trades[${
|
|
83
|
+
path: `request.trades[${requestIndex}].dstChainTokenOutAmount`,
|
|
86
84
|
actual: reqDst,
|
|
87
85
|
details: error,
|
|
88
86
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ptp.js","sourceRoot":"","sources":["../../../src/validations/ptp.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,aAAa,
|
|
1
|
+
{"version":3,"file":"ptp.js","sourceRoot":"","sources":["../../../src/validations/ptp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { bytesToHex } from "viem";
|
|
3
|
+
import { SolanaActionKind, SolanaActionOperation, ViolationCode, } from "../types/index.js";
|
|
4
|
+
import { encodeSolanaIntent } from "../helpers/solana-intent-id.js";
|
|
5
|
+
import { decodeSolanaVersionedTransaction } from "../helpers/solana-transaction-decode.js";
|
|
6
|
+
import { resolveInstructions, isInitIntentInstruction, isUpdateIntentInstruction, } from "../helpers/solana-instruction.js";
|
|
7
|
+
import { getSolanaIntentInitialSpace, readSolanaInitIntent, readSolanaUpdateIntent, SolanaIntentDataOrigin, } from "../helpers/solana-intent-instruction.js";
|
|
8
|
+
import { validateSolanaInitIntentAccounts, validateSolanaUpdateIntentAccounts, } from "./solana-intent-accounts.js";
|
|
9
|
+
// CRITICAL: embedded transactions must create the same intent that was validated
|
|
10
|
+
// against the request. This also checks a quote's executable payload when present;
|
|
11
|
+
// an absent quote payload or a separate Sign action does not require an embedded one.
|
|
12
|
+
export function validateSolanaEmbeddedIntentActions(actions, basePath, realChainId, intent, intentProgram) {
|
|
13
|
+
return actions.flatMap((action, i) => {
|
|
14
|
+
if (!action.actions.includes(SolanaActionOperation.Intent)
|
|
15
|
+
|| (action.type !== SolanaActionKind.Transaction
|
|
16
|
+
&& action.type !== SolanaActionKind.SignTransaction))
|
|
17
|
+
return [];
|
|
18
|
+
return validateEmbeddedIntentAction(action, `${basePath}.requiredActions[${i}].data.data`, realChainId, intent, intentProgram);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function validateEmbeddedIntentAction(action, path, realChainId, intent, intentProgram) {
|
|
22
|
+
const decoded = decodeSolanaVersionedTransaction(action.data.data, path, ViolationCode.SolanaTransactionDecodeError, ViolationCode.SolanaTransactionVersionInvalid);
|
|
23
|
+
if (!decoded.ok)
|
|
24
|
+
return decoded.violations;
|
|
25
|
+
let expected;
|
|
26
|
+
try {
|
|
27
|
+
expected = buildExpectedIntent(intent, realChainId, intentProgram);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return [{
|
|
31
|
+
code: ViolationCode.SolanaEmbeddedIntentComputeError,
|
|
32
|
+
message: "cannot derive the expected embedded Solana intent",
|
|
33
|
+
path,
|
|
34
|
+
details: error,
|
|
35
|
+
}];
|
|
36
|
+
}
|
|
37
|
+
return validateInstructions(decoded.transaction, expected, path);
|
|
38
|
+
}
|
|
39
|
+
function buildExpectedIntent(intent, realChainId, intentProgram) {
|
|
40
|
+
const encoded = encodeSolanaIntent(intent, realChainId, intentProgram);
|
|
41
|
+
const program = new PublicKey(intentProgram);
|
|
42
|
+
const maker = new PublicKey(intent.intentOwner);
|
|
43
|
+
const derive = (seed, ...seeds) => PublicKey.findProgramAddressSync([new TextEncoder().encode(seed), ...seeds], program)[0].toBase58();
|
|
44
|
+
return {
|
|
45
|
+
...encoded,
|
|
46
|
+
space: getSolanaIntentInitialSpace(encoded.serializedIntent.length, (intent.inputToken ?? []).length),
|
|
47
|
+
accounts: {
|
|
48
|
+
program: program.toBase58(),
|
|
49
|
+
maker: maker.toBase58(),
|
|
50
|
+
makerInfo: derive("maker_info", maker.toBytes()),
|
|
51
|
+
state: derive("state"),
|
|
52
|
+
intentStorage: derive("intent_storage", encoded.intentId),
|
|
53
|
+
eventAuthority: derive("__event_authority"),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// HIGH: initialize exactly once, then seal exactly once. An inline update carries
|
|
58
|
+
// the entire intent; it is not a chunk to be appended. Repeated updates cannot
|
|
59
|
+
// overwrite a sealed intent, and extra initializations can allocate other storage.
|
|
60
|
+
function validateInstructions(transaction, expected, path) {
|
|
61
|
+
const unresolvedPrograms = transaction.message.compiledInstructions.flatMap((instruction, i) => transaction.message.staticAccountKeys[instruction.programIdIndex] === undefined
|
|
62
|
+
? [i]
|
|
63
|
+
: []);
|
|
64
|
+
// HIGH: do not classify instructions whose program is unresolved. This also
|
|
65
|
+
// preserves the original instruction indices when resolving account operands.
|
|
66
|
+
if (unresolvedPrograms.length > 0) {
|
|
67
|
+
return [{
|
|
68
|
+
code: ViolationCode.SolanaIntentInstructionAccountsUnresolved,
|
|
69
|
+
message: "embedded intent transaction contains unresolved instruction programs",
|
|
70
|
+
path,
|
|
71
|
+
actual: { instructionIndices: unresolvedPrograms },
|
|
72
|
+
}];
|
|
73
|
+
}
|
|
74
|
+
const resolved = resolveInstructions(transaction.message.compiledInstructions, transaction.message.staticAccountKeys.map(key => key.toBase58()));
|
|
75
|
+
const program = expected.accounts.program;
|
|
76
|
+
const initIndices = resolved.flatMap((instruction, i) => isInitIntentInstruction(instruction, program) ? [i] : []);
|
|
77
|
+
const updateIndices = resolved.flatMap((instruction, i) => isUpdateIntentInstruction(instruction, program) ? [i] : []);
|
|
78
|
+
return validateIntentSequence(transaction, resolved, initIndices, updateIndices, expected, path);
|
|
79
|
+
}
|
|
80
|
+
function validateIntentSequence(transaction, resolved, initIndices, updateIndices, expected, path) {
|
|
81
|
+
if (initIndices.length !== 1
|
|
82
|
+
|| updateIndices.length !== 1
|
|
83
|
+
|| initIndices[0] >= updateIndices[0]) {
|
|
84
|
+
return [{
|
|
85
|
+
code: ViolationCode.SolanaIntentInstructionSequenceInvalid,
|
|
86
|
+
message: "embedded intent requires one initialization followed by one complete update",
|
|
87
|
+
path,
|
|
88
|
+
actual: { initIndices, updateIndices },
|
|
89
|
+
}];
|
|
90
|
+
}
|
|
91
|
+
return [
|
|
92
|
+
...validateInit(transaction, resolved[initIndices[0]], initIndices[0], expected, path),
|
|
93
|
+
...validateUpdate(transaction, resolved[updateIndices[0]], updateIndices[0], expected, path),
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
function validateInit(transaction, instruction, instructionIndex, expected, path) {
|
|
97
|
+
try {
|
|
98
|
+
const data = readSolanaInitIntent(instruction);
|
|
99
|
+
return [
|
|
100
|
+
...validateInitData(data, expected, path, instructionIndex),
|
|
101
|
+
...validateSolanaInitIntentAccounts(transaction, instructionIndex, data.kind, expected.accounts, path),
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return invalidInstruction(path, instructionIndex, error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// CRITICAL: the initialization hash commits to the checked intent. Its allocation
|
|
109
|
+
// must match that intent's storage budget, not an arbitrary rent expense.
|
|
110
|
+
function validateInitData(data, expected, path, instructionIndex) {
|
|
111
|
+
const violations = [];
|
|
112
|
+
if (!bytesEqual(data.encodedIntentHash, expected.encodedIntentHash)) {
|
|
113
|
+
violations.push({
|
|
114
|
+
code: ViolationCode.SolanaIntentInstructionHashMismatch,
|
|
115
|
+
message: "initialization hash does not match the checked intent",
|
|
116
|
+
path,
|
|
117
|
+
expected: bytesToHex(expected.encodedIntentHash),
|
|
118
|
+
actual: bytesToHex(data.encodedIntentHash),
|
|
119
|
+
details: { instructionIndex },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (data.space !== expected.space) {
|
|
123
|
+
violations.push({
|
|
124
|
+
code: ViolationCode.SolanaIntentInstructionSpaceMismatch,
|
|
125
|
+
message: "initialization allocation does not match the checked intent storage budget",
|
|
126
|
+
path,
|
|
127
|
+
expected: expected.space,
|
|
128
|
+
actual: data.space,
|
|
129
|
+
details: { instructionIndex },
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return violations;
|
|
133
|
+
}
|
|
134
|
+
function validateUpdate(transaction, instruction, instructionIndex, expected, path) {
|
|
135
|
+
try {
|
|
136
|
+
const data = readSolanaUpdateIntent(instruction);
|
|
137
|
+
// CRITICAL: a reference to external account bytes is not proof of their
|
|
138
|
+
// contents. Reject this unverifiable representation rather than accepting
|
|
139
|
+
// a hash alone; inline updates supply the full payload for comparison.
|
|
140
|
+
if (data.origin === SolanaIntentDataOrigin.Buffer) {
|
|
141
|
+
return [{
|
|
142
|
+
code: ViolationCode.SolanaIntentUpdateBufferUnverifiable,
|
|
143
|
+
message: "external-buffer intent update cannot be verified from the bundle response",
|
|
144
|
+
path,
|
|
145
|
+
actual: { origin: SolanaIntentDataOrigin[data.origin], offset: data.offset, length: data.length },
|
|
146
|
+
details: { instructionIndex },
|
|
147
|
+
}];
|
|
148
|
+
}
|
|
149
|
+
return [
|
|
150
|
+
...validateUpdateData(data.data, expected.serializedIntent, path, instructionIndex),
|
|
151
|
+
...validateSolanaUpdateIntentAccounts(transaction, instructionIndex, expected.accounts, path),
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return invalidInstruction(path, instructionIndex, error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// CRITICAL: the complete update payload must encode the checked intent, not just
|
|
159
|
+
// start with an allowed discriminator. Chunking into different transactions is
|
|
160
|
+
// not inferred from labels; the update must itself contain the complete bytes.
|
|
161
|
+
function validateUpdateData(actual, expected, path, instructionIndex) {
|
|
162
|
+
if (bytesEqual(actual, expected))
|
|
163
|
+
return [];
|
|
164
|
+
return [{
|
|
165
|
+
code: ViolationCode.SolanaIntentInstructionDataMismatch,
|
|
166
|
+
message: "embedded update payload does not match the checked intent",
|
|
167
|
+
path,
|
|
168
|
+
expected: bytesToHex(expected),
|
|
169
|
+
actual: bytesToHex(actual),
|
|
170
|
+
details: { instructionIndex },
|
|
171
|
+
}];
|
|
172
|
+
}
|
|
173
|
+
function invalidInstruction(path, instructionIndex, error) {
|
|
174
|
+
return [{
|
|
175
|
+
code: ViolationCode.SolanaIntentInstructionDataInvalid,
|
|
176
|
+
message: "embedded intent instruction data or accounts could not be decoded",
|
|
177
|
+
path,
|
|
178
|
+
details: { instructionIndex, error },
|
|
179
|
+
}];
|
|
180
|
+
}
|
|
181
|
+
function bytesEqual(actual, expected) {
|
|
182
|
+
return actual.length === expected.length && actual.every((byte, i) => byte === expected[i]);
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=solana-embedded-intent.js.map
|