@morpho-org/evm-simulation 4.2.0 → 5.0.0

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.
Files changed (160) hide show
  1. package/README.md +109 -17
  2. package/lib/cjs/authorizations.d.ts +95 -0
  3. package/lib/cjs/authorizations.js +2 -0
  4. package/lib/cjs/errors.d.ts +207 -13
  5. package/lib/cjs/errors.js +197 -13
  6. package/lib/cjs/index.d.ts +10 -2
  7. package/lib/cjs/index.js +19 -3
  8. package/lib/cjs/limits.d.ts +112 -0
  9. package/lib/cjs/limits.js +50 -0
  10. package/lib/cjs/params.d.ts +25 -0
  11. package/lib/cjs/params.js +5 -0
  12. package/lib/cjs/requirements/index.d.ts +1 -0
  13. package/lib/cjs/requirements/index.js +5 -0
  14. package/lib/cjs/requirements/requirements.d.ts +64 -0
  15. package/lib/cjs/requirements/requirements.js +149 -0
  16. package/lib/cjs/result.d.ts +49 -0
  17. package/lib/cjs/result.js +2 -0
  18. package/lib/cjs/simulate/asset-changes.d.ts +7 -5
  19. package/lib/cjs/simulate/asset-changes.js +6 -5
  20. package/lib/cjs/simulate/backends/client.d.ts +16 -0
  21. package/lib/cjs/simulate/backends/client.js +27 -0
  22. package/lib/cjs/simulate/backends/eth-simulate-v1.d.ts +66 -20
  23. package/lib/cjs/simulate/backends/eth-simulate-v1.js +172 -98
  24. package/lib/cjs/simulate/backends/index.d.ts +1 -2
  25. package/lib/cjs/simulate/backends/index.js +3 -4
  26. package/lib/cjs/simulate/backends/parse-response.d.ts +69 -0
  27. package/lib/cjs/simulate/backends/parse-response.js +168 -0
  28. package/lib/cjs/simulate/backends/resolve-assets.d.ts +26 -0
  29. package/lib/cjs/simulate/backends/resolve-assets.js +111 -0
  30. package/lib/cjs/simulate/backends/resolve-pinned-block.d.ts +33 -0
  31. package/lib/cjs/simulate/backends/resolve-pinned-block.js +46 -0
  32. package/lib/cjs/simulate/context.d.ts +14 -0
  33. package/lib/cjs/simulate/context.js +2 -0
  34. package/lib/cjs/simulate/measurement-plan.d.ts +29 -0
  35. package/lib/cjs/simulate/measurement-plan.js +115 -0
  36. package/lib/cjs/simulate/parsing/transfers.d.ts +4 -3
  37. package/lib/cjs/simulate/parsing/transfers.js +4 -6
  38. package/lib/cjs/simulate/pipeline/bundles-retention.d.ts +7 -10
  39. package/lib/cjs/simulate/pipeline/bundles-retention.js +11 -14
  40. package/lib/cjs/simulate/pipeline/resolve-chain.d.ts +5 -11
  41. package/lib/cjs/simulate/pipeline/resolve-chain.js +5 -10
  42. package/lib/cjs/simulate/plan/plan-execution.d.ts +58 -0
  43. package/lib/cjs/simulate/plan/plan-execution.js +75 -0
  44. package/lib/cjs/simulate/prepare.d.ts +30 -0
  45. package/lib/cjs/simulate/prepare.js +80 -0
  46. package/lib/cjs/simulate/request/index.d.ts +1 -0
  47. package/lib/cjs/simulate/request/index.js +5 -0
  48. package/lib/cjs/simulate/request/parse-request.d.ts +44 -0
  49. package/lib/cjs/simulate/request/parse-request.js +759 -0
  50. package/lib/cjs/simulate/result.d.ts +19 -0
  51. package/lib/cjs/simulate/result.js +29 -0
  52. package/lib/cjs/simulate/run-simulation.d.ts +18 -0
  53. package/lib/cjs/simulate/run-simulation.js +151 -0
  54. package/lib/cjs/simulate/simulate.d.ts +100 -50
  55. package/lib/cjs/simulate/simulate.js +99 -81
  56. package/lib/cjs/simulate/slippage.d.ts +18 -0
  57. package/lib/cjs/simulate/slippage.js +41 -0
  58. package/lib/cjs/simulate/state/contract.d.ts +19 -0
  59. package/lib/cjs/simulate/state/contract.js +2 -0
  60. package/lib/cjs/simulate/state/read-state.d.ts +60 -0
  61. package/lib/cjs/simulate/state/read-state.js +148 -0
  62. package/lib/cjs/simulate/verify-slippage.d.ts +24 -0
  63. package/lib/cjs/simulate/verify-slippage.js +85 -0
  64. package/lib/cjs/types.d.ts +35 -95
  65. package/lib/esm/authorizations.d.ts +95 -0
  66. package/lib/esm/authorizations.js +1 -0
  67. package/lib/esm/errors.d.ts +207 -13
  68. package/lib/esm/errors.js +189 -12
  69. package/lib/esm/index.d.ts +10 -2
  70. package/lib/esm/index.js +4 -1
  71. package/lib/esm/limits.d.ts +112 -0
  72. package/lib/esm/limits.js +47 -0
  73. package/lib/esm/params.d.ts +25 -0
  74. package/lib/esm/params.js +2 -0
  75. package/lib/esm/requirements/index.d.ts +1 -0
  76. package/lib/esm/requirements/index.js +1 -0
  77. package/lib/esm/requirements/requirements.d.ts +64 -0
  78. package/lib/esm/requirements/requirements.js +146 -0
  79. package/lib/esm/result.d.ts +49 -0
  80. package/lib/esm/result.js +1 -0
  81. package/lib/esm/simulate/asset-changes.d.ts +7 -5
  82. package/lib/esm/simulate/asset-changes.js +6 -5
  83. package/lib/esm/simulate/backends/client.d.ts +16 -0
  84. package/lib/esm/simulate/backends/client.js +24 -0
  85. package/lib/esm/simulate/backends/eth-simulate-v1.d.ts +66 -20
  86. package/lib/esm/simulate/backends/eth-simulate-v1.js +170 -99
  87. package/lib/esm/simulate/backends/index.d.ts +1 -2
  88. package/lib/esm/simulate/backends/index.js +1 -2
  89. package/lib/esm/simulate/backends/parse-response.d.ts +69 -0
  90. package/lib/esm/simulate/backends/parse-response.js +165 -0
  91. package/lib/esm/simulate/backends/resolve-assets.d.ts +26 -0
  92. package/lib/esm/simulate/backends/resolve-assets.js +108 -0
  93. package/lib/esm/simulate/backends/resolve-pinned-block.d.ts +33 -0
  94. package/lib/esm/simulate/backends/resolve-pinned-block.js +43 -0
  95. package/lib/esm/simulate/context.d.ts +14 -0
  96. package/lib/esm/simulate/context.js +1 -0
  97. package/lib/esm/simulate/measurement-plan.d.ts +29 -0
  98. package/lib/esm/simulate/measurement-plan.js +112 -0
  99. package/lib/esm/simulate/parsing/transfers.d.ts +4 -3
  100. package/lib/esm/simulate/parsing/transfers.js +4 -6
  101. package/lib/esm/simulate/pipeline/bundles-retention.d.ts +7 -10
  102. package/lib/esm/simulate/pipeline/bundles-retention.js +11 -14
  103. package/lib/esm/simulate/pipeline/resolve-chain.d.ts +5 -11
  104. package/lib/esm/simulate/pipeline/resolve-chain.js +5 -10
  105. package/lib/esm/simulate/plan/plan-execution.d.ts +58 -0
  106. package/lib/esm/simulate/plan/plan-execution.js +72 -0
  107. package/lib/esm/simulate/prepare.d.ts +30 -0
  108. package/lib/esm/simulate/prepare.js +77 -0
  109. package/lib/esm/simulate/request/index.d.ts +1 -0
  110. package/lib/esm/simulate/request/index.js +1 -0
  111. package/lib/esm/simulate/request/parse-request.d.ts +44 -0
  112. package/lib/esm/simulate/request/parse-request.js +756 -0
  113. package/lib/esm/simulate/result.d.ts +19 -0
  114. package/lib/esm/simulate/result.js +26 -0
  115. package/lib/esm/simulate/run-simulation.d.ts +18 -0
  116. package/lib/esm/simulate/run-simulation.js +148 -0
  117. package/lib/esm/simulate/simulate.d.ts +100 -50
  118. package/lib/esm/simulate/simulate.js +99 -81
  119. package/lib/esm/simulate/slippage.d.ts +18 -0
  120. package/lib/esm/simulate/slippage.js +38 -0
  121. package/lib/esm/simulate/state/contract.d.ts +19 -0
  122. package/lib/esm/simulate/state/contract.js +1 -0
  123. package/lib/esm/simulate/state/read-state.d.ts +60 -0
  124. package/lib/esm/simulate/state/read-state.js +144 -0
  125. package/lib/esm/simulate/verify-slippage.d.ts +24 -0
  126. package/lib/esm/simulate/verify-slippage.js +82 -0
  127. package/lib/esm/types.d.ts +35 -95
  128. package/package.json +7 -6
  129. package/lib/cjs/simulate/authorizations/index.d.ts +0 -2
  130. package/lib/cjs/simulate/authorizations/index.js +0 -7
  131. package/lib/cjs/simulate/authorizations/resolve.d.ts +0 -9
  132. package/lib/cjs/simulate/authorizations/resolve.js +0 -29
  133. package/lib/cjs/simulate/authorizations/validate.d.ts +0 -6
  134. package/lib/cjs/simulate/authorizations/validate.js +0 -23
  135. package/lib/cjs/simulate/backends/tenderly-rpc.d.ts +0 -23
  136. package/lib/cjs/simulate/backends/tenderly-rpc.js +0 -234
  137. package/lib/cjs/simulate/pipeline/build-simulation-txs.d.ts +0 -17
  138. package/lib/cjs/simulate/pipeline/build-simulation-txs.js +0 -50
  139. package/lib/cjs/simulate/pipeline/execute-simulation.d.ts +0 -22
  140. package/lib/cjs/simulate/pipeline/execute-simulation.js +0 -82
  141. package/lib/cjs/simulate/pipeline/index.d.ts +0 -4
  142. package/lib/cjs/simulate/pipeline/index.js +0 -11
  143. package/lib/cjs/simulate/pipeline/validate-input.d.ts +0 -10
  144. package/lib/cjs/simulate/pipeline/validate-input.js +0 -55
  145. package/lib/esm/simulate/authorizations/index.d.ts +0 -2
  146. package/lib/esm/simulate/authorizations/index.js +0 -2
  147. package/lib/esm/simulate/authorizations/resolve.d.ts +0 -9
  148. package/lib/esm/simulate/authorizations/resolve.js +0 -26
  149. package/lib/esm/simulate/authorizations/validate.d.ts +0 -6
  150. package/lib/esm/simulate/authorizations/validate.js +0 -20
  151. package/lib/esm/simulate/backends/tenderly-rpc.d.ts +0 -23
  152. package/lib/esm/simulate/backends/tenderly-rpc.js +0 -231
  153. package/lib/esm/simulate/pipeline/build-simulation-txs.d.ts +0 -17
  154. package/lib/esm/simulate/pipeline/build-simulation-txs.js +0 -47
  155. package/lib/esm/simulate/pipeline/execute-simulation.d.ts +0 -22
  156. package/lib/esm/simulate/pipeline/execute-simulation.js +0 -79
  157. package/lib/esm/simulate/pipeline/index.d.ts +0 -4
  158. package/lib/esm/simulate/pipeline/index.js +0 -4
  159. package/lib/esm/simulate/pipeline/validate-input.d.ts +0 -10
  160. package/lib/esm/simulate/pipeline/validate-input.js +0 -52
@@ -0,0 +1,759 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRequest = parseRequest;
4
+ const blue_sdk_1 = require("@morpho-org/blue-sdk");
5
+ const morpho_ts_1 = require("@morpho-org/morpho-ts");
6
+ const viem_1 = require("viem");
7
+ const errors_js_1 = require("../../errors.js");
8
+ const params_js_1 = require("../../params.js");
9
+ const measurement_plan_js_1 = require("../measurement-plan.js");
10
+ // ─── Scalar validators ────────────────────────────────────────────────────────
11
+ // `input` is the typed public request, but JS callers can still pass wrong
12
+ // values: every scalar is checked at runtime and reports into `fieldErrors`.
13
+ const readField = (object, key) => Reflect.get(object, key);
14
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
15
+ // "pending" is rejected: only canonical (mined) blocks can be pinned.
16
+ const BLOCK_TAGS = ["latest", "earliest", "safe", "finalized"];
17
+ // ─── Typed-data field lists ───────────────────────────────────────
18
+ const ERC2612_PERMIT_FIELDS = [
19
+ { name: "owner", type: "address" },
20
+ { name: "spender", type: "address" },
21
+ { name: "value", type: "uint256" },
22
+ { name: "nonce", type: "uint256" },
23
+ { name: "deadline", type: "uint256" },
24
+ ];
25
+ const PERMIT2_TRANSFER_FIELDS = [
26
+ { name: "permitted", type: "TokenPermissions" },
27
+ { name: "spender", type: "address" },
28
+ { name: "nonce", type: "uint256" },
29
+ { name: "deadline", type: "uint256" },
30
+ ];
31
+ const PERMIT2_TOKEN_PERMISSIONS_FIELDS = [
32
+ { name: "token", type: "address" },
33
+ { name: "amount", type: "uint256" },
34
+ ];
35
+ const BLUE_AUTHORIZATION_FIELDS = [
36
+ { name: "authorizer", type: "address" },
37
+ { name: "authorized", type: "address" },
38
+ { name: "isAuthorized", type: "bool" },
39
+ { name: "nonce", type: "uint256" },
40
+ { name: "deadline", type: "uint256" },
41
+ ];
42
+ const DOMAIN_KEYS = [
43
+ "name",
44
+ "version",
45
+ "chainId",
46
+ "verifyingContract",
47
+ "salt",
48
+ ];
49
+ const TYPED_DATA_KEYS = ["domain", "primaryType", "types", "message"];
50
+ const ERC2612_MESSAGE_KEYS = ERC2612_PERMIT_FIELDS.map((field) => field.name);
51
+ const PERMIT2_MESSAGE_KEYS = PERMIT2_TRANSFER_FIELDS.map((field) => field.name);
52
+ const PERMIT2_PERMITTED_KEYS = ["token", "amount"];
53
+ const BLUE_AUTHORIZATION_MESSAGE_KEYS = BLUE_AUTHORIZATION_FIELDS.map((field) => field.name);
54
+ const TRANSACTION_KEYS = ["from", "to", "data", "value"];
55
+ const LIMITS_KEYS = ["operations"];
56
+ const AUTHORIZATION_KEYS = {
57
+ erc20Approval: ["type", "token", "owner", "spender", "amount"],
58
+ erc2612Permit: ["type", "typedData"],
59
+ permit2SignatureTransfer: ["type", "owner", "typedData"],
60
+ blueAuthorization: ["type", "authorizer", "authorized", "isAuthorized"],
61
+ blueAuthorizationSignature: ["type", "typedData"],
62
+ };
63
+ /** Field validators closing over one `errors` accumulator. @internal */
64
+ const createChecks = () => {
65
+ const errors = [];
66
+ const check = {
67
+ errors,
68
+ address: (value, path) => {
69
+ if (typeof value === "string" && (0, viem_1.isAddress)(value))
70
+ return (0, viem_1.getAddress)(value);
71
+ errors.push(`${path}: must be a valid address`);
72
+ return undefined;
73
+ },
74
+ hex: (value, path) => {
75
+ if ((0, viem_1.isHex)(value))
76
+ return value;
77
+ errors.push(`${path}: must be a hex data`);
78
+ return undefined;
79
+ },
80
+ bytes32: (value, path) => {
81
+ if (typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value))
82
+ return value;
83
+ errors.push(`${path}: must be 32-byte hex`);
84
+ return undefined;
85
+ },
86
+ marketId: (value, path) => {
87
+ const id = check.bytes32(value, path);
88
+ return id === undefined ? undefined : id;
89
+ },
90
+ uint256: (value, path) => {
91
+ if (typeof value !== "bigint") {
92
+ errors.push(`${path}: must be a bigint`);
93
+ return undefined;
94
+ }
95
+ if (value < 0n) {
96
+ errors.push(`${path}: must be non-negative`);
97
+ return undefined;
98
+ }
99
+ if (value > viem_1.maxUint256) {
100
+ errors.push(`${path}: exceeds uint256`);
101
+ return undefined;
102
+ }
103
+ return value;
104
+ },
105
+ bool: (value, path) => {
106
+ if (typeof value === "boolean")
107
+ return value;
108
+ errors.push(`${path}: must be a boolean`);
109
+ return undefined;
110
+ },
111
+ nonNegInt: (value, path) => {
112
+ if (typeof value === "number" &&
113
+ Number.isInteger(value) &&
114
+ Number.isSafeInteger(value) &&
115
+ value >= 0)
116
+ return value;
117
+ errors.push(`${path}: must be a non-negative integer`);
118
+ return undefined;
119
+ },
120
+ keys: (value, { allow, path }) => {
121
+ if (!isRecord(value))
122
+ return;
123
+ for (const key of Object.keys(value)) {
124
+ if (!allow.includes(key))
125
+ errors.push(`${path}.${key}: unknown field`);
126
+ }
127
+ },
128
+ domain: (domain, path) => {
129
+ if (!isRecord(domain)) {
130
+ errors.push(`${path}: must be an object`);
131
+ return undefined;
132
+ }
133
+ check.keys(domain, {
134
+ allow: DOMAIN_KEYS,
135
+ path,
136
+ });
137
+ const name = readField(domain, "name");
138
+ if (name !== undefined && typeof name !== "string")
139
+ errors.push(`${path}.name: must be a string`);
140
+ const version = readField(domain, "version");
141
+ if (version !== undefined && typeof version !== "string")
142
+ errors.push(`${path}.version: must be a string`);
143
+ const chainId = readField(domain, "chainId");
144
+ const validChainId = (typeof chainId === "number" &&
145
+ Number.isInteger(chainId) &&
146
+ chainId > 0) ||
147
+ (typeof chainId === "bigint" && chainId >= 0n && chainId <= viem_1.maxUint256);
148
+ if (!validChainId)
149
+ errors.push(`${path}.chainId: must be a positive integer or uint256`);
150
+ const verifyingContract = check.address(readField(domain, "verifyingContract"), `${path}.verifyingContract`);
151
+ const rawSalt = readField(domain, "salt");
152
+ const salt = rawSalt !== undefined
153
+ ? check.bytes32(rawSalt, `${path}.salt`)
154
+ : undefined;
155
+ if (!validChainId || verifyingContract === undefined)
156
+ return undefined;
157
+ return {
158
+ ...(typeof name === "string" ? { name } : {}),
159
+ ...(typeof version === "string" ? { version } : {}),
160
+ chainId: chainId,
161
+ verifyingContract,
162
+ ...(salt !== undefined ? { salt } : {}),
163
+ };
164
+ },
165
+ fields: ({ actual, expected, path }) => {
166
+ const fields = actual;
167
+ const equal = Array.isArray(fields) &&
168
+ fields.length === expected.length &&
169
+ expected.every((field, i) => fields[i]?.name === field.name && fields[i]?.type === field.type);
170
+ if (!equal) {
171
+ errors.push(`${path}: must list exactly ${expected.map((f) => `${f.name}: ${f.type}`).join(", ")}`);
172
+ return undefined;
173
+ }
174
+ return fields;
175
+ },
176
+ authorization: (authorization, path) => {
177
+ const type = isRecord(authorization)
178
+ ? readField(authorization, "type")
179
+ : undefined;
180
+ if (!isRecord(authorization) || typeof type !== "string") {
181
+ errors.push(`${path}.type: must name an authorization type`);
182
+ return undefined;
183
+ }
184
+ if (!Object.hasOwn(AUTHORIZATION_KEYS, type)) {
185
+ errors.push(`${path}.type: unsupported authorization type "${type}"`);
186
+ return undefined;
187
+ }
188
+ check.keys(authorization, { allow: AUTHORIZATION_KEYS[type], path });
189
+ const errorsBefore = errors.length;
190
+ switch (type) {
191
+ case "erc20Approval": {
192
+ const token = check.address(readField(authorization, "token"), `${path}.token`);
193
+ const owner = check.address(readField(authorization, "owner"), `${path}.owner`);
194
+ const spender = check.address(readField(authorization, "spender"), `${path}.spender`);
195
+ const amount = check.uint256(readField(authorization, "amount"), `${path}.amount`);
196
+ if (token === undefined ||
197
+ owner === undefined ||
198
+ spender === undefined ||
199
+ amount === undefined)
200
+ return undefined;
201
+ return { type: "erc20Approval", token, owner, spender, amount };
202
+ }
203
+ case "erc2612Permit": {
204
+ const envelope = typedDataEnvelope({
205
+ value: readField(authorization, "typedData"),
206
+ path,
207
+ primaryType: "Permit",
208
+ fields: ERC2612_PERMIT_FIELDS,
209
+ });
210
+ if (envelope === undefined)
211
+ return undefined;
212
+ const { domain, message, fields: permitFields } = envelope;
213
+ const messagePath = `${path}.typedData.message`;
214
+ check.keys(message, {
215
+ allow: ERC2612_MESSAGE_KEYS,
216
+ path: messagePath,
217
+ });
218
+ const owner = check.address(readField(message, "owner"), `${messagePath}.owner`);
219
+ const spender = check.address(readField(message, "spender"), `${messagePath}.spender`);
220
+ const value = check.uint256(readField(message, "value"), `${messagePath}.value`);
221
+ const nonce = check.uint256(readField(message, "nonce"), `${messagePath}.nonce`);
222
+ const deadline = check.uint256(readField(message, "deadline"), `${messagePath}.deadline`);
223
+ if (errors.length !== errorsBefore ||
224
+ domain === undefined ||
225
+ owner === undefined ||
226
+ spender === undefined ||
227
+ value === undefined ||
228
+ nonce === undefined ||
229
+ deadline === undefined ||
230
+ permitFields === undefined)
231
+ return undefined;
232
+ return {
233
+ type: "erc2612Permit",
234
+ typedData: {
235
+ domain,
236
+ primaryType: "Permit",
237
+ types: {
238
+ Permit: copyFields(permitFields),
239
+ },
240
+ message: { owner, spender, value, nonce, deadline },
241
+ },
242
+ };
243
+ }
244
+ case "permit2SignatureTransfer": {
245
+ const owner = check.address(readField(authorization, "owner"), `${path}.owner`);
246
+ const envelope = typedDataEnvelope({
247
+ value: readField(authorization, "typedData"),
248
+ path,
249
+ primaryType: "PermitTransferFrom",
250
+ fields: PERMIT2_TRANSFER_FIELDS,
251
+ messageError: "must carry permitted token and amount",
252
+ });
253
+ if (envelope === undefined)
254
+ return undefined;
255
+ const { domain, message, fields: transferFields } = envelope;
256
+ const rawTypedData = readField(authorization, "typedData");
257
+ const types = isRecord(rawTypedData)
258
+ ? readField(rawTypedData, "types")
259
+ : undefined;
260
+ const permissionFields = isRecord(types)
261
+ ? readField(types, "TokenPermissions")
262
+ : undefined;
263
+ const checkedPermissionFields = check.fields({
264
+ actual: permissionFields,
265
+ expected: PERMIT2_TOKEN_PERMISSIONS_FIELDS,
266
+ path: `${path}.typedData.types.TokenPermissions`,
267
+ });
268
+ const permitted = readField(message, "permitted");
269
+ if (!isRecord(permitted)) {
270
+ errors.push(`${path}.typedData.message: must carry permitted token and amount`);
271
+ return undefined;
272
+ }
273
+ const messagePath = `${path}.typedData.message`;
274
+ check.keys(message, {
275
+ allow: PERMIT2_MESSAGE_KEYS,
276
+ path: messagePath,
277
+ });
278
+ check.keys(permitted, {
279
+ allow: PERMIT2_PERMITTED_KEYS,
280
+ path: `${messagePath}.permitted`,
281
+ });
282
+ const permittedToken = check.address(readField(permitted, "token"), `${messagePath}.permitted.token`);
283
+ const permittedAmount = check.uint256(readField(permitted, "amount"), `${messagePath}.permitted.amount`);
284
+ const spender = check.address(readField(message, "spender"), `${messagePath}.spender`);
285
+ const nonce = check.uint256(readField(message, "nonce"), `${messagePath}.nonce`);
286
+ const deadline = check.uint256(readField(message, "deadline"), `${messagePath}.deadline`);
287
+ if (errors.length !== errorsBefore ||
288
+ domain === undefined ||
289
+ owner === undefined ||
290
+ permittedToken === undefined ||
291
+ permittedAmount === undefined ||
292
+ spender === undefined ||
293
+ nonce === undefined ||
294
+ deadline === undefined ||
295
+ transferFields === undefined ||
296
+ checkedPermissionFields === undefined)
297
+ return undefined;
298
+ return {
299
+ type: "permit2SignatureTransfer",
300
+ owner,
301
+ typedData: {
302
+ domain,
303
+ primaryType: "PermitTransferFrom",
304
+ types: {
305
+ PermitTransferFrom: copyFields(transferFields),
306
+ TokenPermissions: copyFields(checkedPermissionFields),
307
+ },
308
+ message: {
309
+ permitted: {
310
+ token: permittedToken,
311
+ amount: permittedAmount,
312
+ },
313
+ spender,
314
+ nonce,
315
+ deadline,
316
+ },
317
+ },
318
+ };
319
+ }
320
+ case "blueAuthorization": {
321
+ const authorizer = check.address(readField(authorization, "authorizer"), `${path}.authorizer`);
322
+ const authorized = check.address(readField(authorization, "authorized"), `${path}.authorized`);
323
+ const isAuthorized = check.bool(readField(authorization, "isAuthorized"), `${path}.isAuthorized`);
324
+ if (authorizer === undefined ||
325
+ authorized === undefined ||
326
+ isAuthorized === undefined)
327
+ return undefined;
328
+ return {
329
+ type: "blueAuthorization",
330
+ authorizer,
331
+ authorized,
332
+ isAuthorized,
333
+ };
334
+ }
335
+ case "blueAuthorizationSignature": {
336
+ const envelope = typedDataEnvelope({
337
+ value: readField(authorization, "typedData"),
338
+ path,
339
+ primaryType: "Authorization",
340
+ fields: BLUE_AUTHORIZATION_FIELDS,
341
+ });
342
+ if (envelope === undefined)
343
+ return undefined;
344
+ const { domain, message, fields: authorizationFields } = envelope;
345
+ const messagePath = `${path}.typedData.message`;
346
+ check.keys(message, {
347
+ allow: BLUE_AUTHORIZATION_MESSAGE_KEYS,
348
+ path: messagePath,
349
+ });
350
+ const authorizer = check.address(readField(message, "authorizer"), `${messagePath}.authorizer`);
351
+ const authorized = check.address(readField(message, "authorized"), `${messagePath}.authorized`);
352
+ const isAuthorized = check.bool(readField(message, "isAuthorized"), `${messagePath}.isAuthorized`);
353
+ const nonce = check.uint256(readField(message, "nonce"), `${messagePath}.nonce`);
354
+ const deadline = check.uint256(readField(message, "deadline"), `${messagePath}.deadline`);
355
+ if (errors.length !== errorsBefore ||
356
+ domain === undefined ||
357
+ authorizer === undefined ||
358
+ authorized === undefined ||
359
+ isAuthorized === undefined ||
360
+ nonce === undefined ||
361
+ deadline === undefined ||
362
+ authorizationFields === undefined)
363
+ return undefined;
364
+ return {
365
+ type: "blueAuthorizationSignature",
366
+ typedData: {
367
+ domain,
368
+ primaryType: "Authorization",
369
+ types: {
370
+ Authorization: copyFields(authorizationFields),
371
+ },
372
+ message: {
373
+ authorizer,
374
+ authorized,
375
+ isAuthorized,
376
+ nonce,
377
+ deadline,
378
+ },
379
+ },
380
+ };
381
+ }
382
+ default:
383
+ errors.push(`${path}.type: unsupported authorization type "${type}"`);
384
+ return undefined;
385
+ }
386
+ },
387
+ operation: (operation, path) => {
388
+ const type = isRecord(operation)
389
+ ? readField(operation, "type")
390
+ : undefined;
391
+ if (!isRecord(operation) ||
392
+ typeof type !== "string" ||
393
+ !Object.hasOwn(OPERATION_SPECS, type)) {
394
+ errors.push(`${path}.type: unsupported operation type "${String(type)}"`);
395
+ return undefined;
396
+ }
397
+ const spec = OPERATION_SPECS[type];
398
+ const requiredAddresses = new Set(spec.requiredAddresses);
399
+ check.keys(operation, {
400
+ allow: [
401
+ "type",
402
+ ...spec.markets,
403
+ ...spec.addresses,
404
+ "quote",
405
+ "slippageTolerance",
406
+ ],
407
+ path,
408
+ });
409
+ const errorsBefore = errors.length;
410
+ const out = { type };
411
+ for (const field of spec.markets) {
412
+ const value = check.marketId(readField(operation, field), `${path}.${field}`);
413
+ if (value !== undefined)
414
+ Reflect.set(out, field, value);
415
+ }
416
+ for (const field of spec.addresses) {
417
+ const raw = readField(operation, field);
418
+ if (raw === undefined && !requiredAddresses.has(field))
419
+ continue;
420
+ const value = check.address(raw, `${path}.${field}`);
421
+ if (value !== undefined)
422
+ Reflect.set(out, field, value);
423
+ }
424
+ const tolerance = check.uint256(readField(operation, "slippageTolerance"), `${path}.slippageTolerance`);
425
+ if (tolerance !== undefined) {
426
+ if (tolerance > blue_sdk_1.MathLib.WAD)
427
+ errors.push(`${path}.slippageTolerance: must not exceed 100% (1e18)`);
428
+ Reflect.set(out, "slippageTolerance", tolerance);
429
+ }
430
+ const quote = readField(operation, "quote");
431
+ if (!isRecord(quote)) {
432
+ errors.push(`${path}.quote: must be an object containing at least one quoted amount`);
433
+ }
434
+ else {
435
+ check.keys(quote, { allow: QUOTE_FIELDS, path: `${path}.quote` });
436
+ const normalizedQuote = {};
437
+ for (const field of QUOTE_FIELDS) {
438
+ const raw = readField(quote, field);
439
+ if (raw === undefined)
440
+ continue;
441
+ const value = check.uint256(raw, `${path}.quote.${field}`);
442
+ if (value !== undefined)
443
+ normalizedQuote[field] = value;
444
+ }
445
+ if (Object.keys(normalizedQuote).length === 0)
446
+ errors.push(`${path}.quote: supply at least one quoted amount`);
447
+ const measurement = (0, measurement_plan_js_1.operationMeasurementPlan)(out);
448
+ for (const field of ["sharesMinted", "sharesBurned"]) {
449
+ if (normalizedQuote[field] !== undefined &&
450
+ measurement[field] === undefined)
451
+ errors.push(`${path}.quote.${field}: cannot be measured for "${type}"`);
452
+ }
453
+ Reflect.set(out, "quote", normalizedQuote);
454
+ }
455
+ return errors.length === errorsBefore ? out : undefined;
456
+ },
457
+ };
458
+ const copyFields = (fields) => fields.map(({ name, type: fieldType }) => ({ name, type: fieldType }));
459
+ /** Shared typed-data envelope guard: domain, primaryType, primary field list
460
+ * and message record; message-specific checks stay in each branch. */
461
+ const typedDataEnvelope = (options) => {
462
+ const typedDataPath = `${options.path}.typedData`;
463
+ const value = options.value;
464
+ if (!isRecord(value)) {
465
+ errors.push(`${typedDataPath}: must be an object`);
466
+ return undefined;
467
+ }
468
+ check.keys(value, { allow: TYPED_DATA_KEYS, path: typedDataPath });
469
+ const domain = check.domain(readField(value, "domain"), `${typedDataPath}.domain`);
470
+ if (readField(value, "primaryType") !== options.primaryType)
471
+ errors.push(`${typedDataPath}.primaryType: must be "${options.primaryType}" (got ${String(readField(value, "primaryType"))})`);
472
+ const types = readField(value, "types");
473
+ const fields = isRecord(types)
474
+ ? readField(types, options.primaryType)
475
+ : undefined;
476
+ const checkedFields = check.fields({
477
+ actual: fields,
478
+ expected: options.fields,
479
+ path: `${typedDataPath}.types.${options.primaryType}`,
480
+ });
481
+ const message = readField(value, "message");
482
+ if (!isRecord(message)) {
483
+ errors.push(`${typedDataPath}.message: ${options.messageError ?? "must be an object"}`);
484
+ return undefined;
485
+ }
486
+ return { domain, message, fields: checkedFields };
487
+ };
488
+ return check;
489
+ };
490
+ const QUOTE_FIELDS = [
491
+ "assetsReceived",
492
+ "sharesMinted",
493
+ "assetsPaid",
494
+ "sharesBurned",
495
+ ];
496
+ const MARKET_SUBJECT = {
497
+ markets: ["marketId"],
498
+ addresses: ["account", "receiver", "assetPaid", "assetReceived"],
499
+ requiredAddresses: [],
500
+ };
501
+ const VAULT_SUBJECT = {
502
+ markets: [],
503
+ addresses: ["vault", "account", "receiver", "assetPaid", "assetReceived"],
504
+ requiredAddresses: ["vault"],
505
+ };
506
+ const OPERATION_SPECS = {
507
+ blueSupply: MARKET_SUBJECT,
508
+ blueWithdraw: MARKET_SUBJECT,
509
+ blueSupplyCollateral: MARKET_SUBJECT,
510
+ blueBorrow: MARKET_SUBJECT,
511
+ blueSupplyCollateralBorrow: MARKET_SUBJECT,
512
+ blueRepay: MARKET_SUBJECT,
513
+ blueWithdrawCollateral: MARKET_SUBJECT,
514
+ blueRepayWithdrawCollateral: MARKET_SUBJECT,
515
+ blueRefinance: {
516
+ ...MARKET_SUBJECT,
517
+ markets: ["sourceMarketId", "targetMarketId"],
518
+ },
519
+ vaultV1Deposit: VAULT_SUBJECT,
520
+ vaultV2Deposit: VAULT_SUBJECT,
521
+ vaultV1Withdraw: VAULT_SUBJECT,
522
+ vaultV2Withdraw: VAULT_SUBJECT,
523
+ vaultV1Redeem: VAULT_SUBJECT,
524
+ vaultV2Redeem: VAULT_SUBJECT,
525
+ vaultV2ForceWithdraw: VAULT_SUBJECT,
526
+ vaultV2ForceRedeem: VAULT_SUBJECT,
527
+ vaultV1InKindRedeem: VAULT_SUBJECT,
528
+ vaultV2InKindRedeem: VAULT_SUBJECT,
529
+ vaultV1MigrateToV2: {
530
+ markets: [],
531
+ addresses: [
532
+ "sourceVault",
533
+ "targetVault",
534
+ "account",
535
+ "receiver",
536
+ "assetPaid",
537
+ "assetReceived",
538
+ ],
539
+ requiredAddresses: ["sourceVault", "targetVault"],
540
+ },
541
+ };
542
+ /**
543
+ * Parse and normalize raw `simulate` input into a {@link ParsedRequest}.
544
+ *
545
+ * The public types own the key shape; runtime validation rejects unknown keys
546
+ * (`<path>.<key>: unknown field`), checks values — address and hex formats,
547
+ * uint256 ranges, typed-data field lists — and rejects legacy `{type: "approval"}` / `{type: "signature"}` authorizations and Permit2
548
+ * `PermitSingle` payloads rather than silently reinterpreting them. Cross-field
549
+ * rules then pin a single owner: every transaction `from` and every
550
+ * authorization owner must be the same checksummed address, typed-data domains
551
+ * must bind to `chainId`, and `mode: "final"` rejects authorizations outright.
552
+ *
553
+ * @param input - Caller input (`SimulateParams`-shaped).
554
+ * @returns A deep-frozen, checksummed request: `mode` explicit (`"final"`
555
+ * default), `authorizations` always an array, `value` defaulted to `0n`.
556
+ * @throws {SimulationValidationError} On any unknown key, value or cross-field
557
+ * violation.
558
+ * @internal
559
+ */
560
+ function parseRequest(input) {
561
+ const check = createChecks();
562
+ const fieldErrors = check.errors;
563
+ if (!isRecord(input)) {
564
+ throw new errors_js_1.SimulationValidationError("Invalid simulation input", [
565
+ "input: must be a request object",
566
+ ]);
567
+ }
568
+ check.keys(input, {
569
+ allow: [
570
+ "chainId",
571
+ "transactions",
572
+ "blockNumber",
573
+ "mode",
574
+ "authorizations",
575
+ "limits",
576
+ ],
577
+ path: "input",
578
+ });
579
+ // chainId
580
+ const chainId = input.chainId;
581
+ if (typeof chainId !== "number" ||
582
+ !Number.isInteger(chainId) ||
583
+ !Number.isSafeInteger(chainId) ||
584
+ chainId <= 0) {
585
+ fieldErrors.push("chainId: must be a positive safe integer");
586
+ }
587
+ // transactions
588
+ const rawTransactions = input.transactions;
589
+ const transactions = [];
590
+ // Raw index of each accepted transaction, so cross-field errors name the
591
+ // caller's index rather than the filtered position.
592
+ const transactionIndices = [];
593
+ if (!Array.isArray(rawTransactions) || rawTransactions.length === 0) {
594
+ fieldErrors.push("transactions: must be a non-empty array");
595
+ }
596
+ else {
597
+ for (const [i, tx] of rawTransactions.entries()) {
598
+ const path = `transactions[${i}]`;
599
+ if (!isRecord(tx)) {
600
+ fieldErrors.push(`${path}: must be an object`);
601
+ continue;
602
+ }
603
+ check.keys(tx, {
604
+ allow: TRANSACTION_KEYS,
605
+ path,
606
+ });
607
+ const from = check.address(readField(tx, "from"), `${path}.from`);
608
+ const to = check.address(readField(tx, "to"), `${path}.to`);
609
+ if (from !== undefined && (0, viem_1.isAddressEqual)(from, viem_1.zeroAddress)) {
610
+ fieldErrors.push(`${path}.from: must be a non-zero address`);
611
+ }
612
+ if (to !== undefined && (0, viem_1.isAddressEqual)(to, viem_1.zeroAddress)) {
613
+ fieldErrors.push(`${path}.to: must be a non-zero address`);
614
+ }
615
+ const data = check.hex(readField(tx, "data"), `${path}.data`);
616
+ const rawValue = readField(tx, "value");
617
+ const value = rawValue === undefined ? 0n : check.uint256(rawValue, `${path}.value`);
618
+ if (from !== undefined && to !== undefined && data !== undefined) {
619
+ transactions.push({ from, to, data, value: value ?? 0n });
620
+ transactionIndices.push(i);
621
+ }
622
+ }
623
+ }
624
+ // mode
625
+ const rawMode = input.mode;
626
+ let mode = "final";
627
+ if (rawMode !== undefined) {
628
+ const found = params_js_1.SIMULATION_MODES.find((m) => m === rawMode);
629
+ if (found !== undefined) {
630
+ mode = found;
631
+ }
632
+ else {
633
+ fieldErrors.push(`mode: must be one of ${params_js_1.SIMULATION_MODES.map((m) => `"${m}"`).join(", ")} (got ${String(rawMode)})`);
634
+ }
635
+ }
636
+ // blockNumber
637
+ const rawBlockNumber = input.blockNumber;
638
+ let blockNumber;
639
+ if (rawBlockNumber !== undefined) {
640
+ if (typeof rawBlockNumber === "bigint" && rawBlockNumber >= 0n) {
641
+ blockNumber = rawBlockNumber;
642
+ }
643
+ else {
644
+ const tag = typeof rawBlockNumber === "string"
645
+ ? BLOCK_TAGS.find((t) => t === rawBlockNumber)
646
+ : undefined;
647
+ if (tag !== undefined) {
648
+ blockNumber = tag;
649
+ }
650
+ else {
651
+ fieldErrors.push(`blockNumber: must be a non-negative bigint or one of ${BLOCK_TAGS.map((t) => `"${t}"`).join(", ")}`);
652
+ }
653
+ }
654
+ }
655
+ // authorizations
656
+ const rawAuthorizations = input.authorizations;
657
+ const authorizations = [];
658
+ if (rawAuthorizations !== undefined) {
659
+ if (!Array.isArray(rawAuthorizations)) {
660
+ fieldErrors.push("authorizations: must be an array");
661
+ }
662
+ else {
663
+ if (mode === "final") {
664
+ fieldErrors.push("authorizations: are only accepted in preview mode; sign them and submit mode 'final' without authorizations");
665
+ }
666
+ for (const [i, authorization] of rawAuthorizations.entries()) {
667
+ const parsed = check.authorization(authorization, `authorizations[${i}]`);
668
+ if (parsed !== undefined)
669
+ authorizations.push(parsed);
670
+ }
671
+ }
672
+ }
673
+ // limits
674
+ const limits = input.limits;
675
+ const operations = [];
676
+ let normalizedLimits;
677
+ if (limits !== undefined) {
678
+ if (!isRecord(limits)) {
679
+ fieldErrors.push("limits: must be an object");
680
+ }
681
+ else {
682
+ check.keys(limits, { allow: LIMITS_KEYS, path: "limits" });
683
+ const rawOperations = readField(limits, "operations");
684
+ if (rawOperations !== undefined) {
685
+ if (!Array.isArray(rawOperations)) {
686
+ fieldErrors.push("limits.operations: must be an array");
687
+ }
688
+ else {
689
+ for (const [i, operation] of rawOperations.entries()) {
690
+ const parsedOperation = check.operation(operation, `limits.operations[${i}]`);
691
+ if (parsedOperation !== undefined)
692
+ operations.push(parsedOperation);
693
+ }
694
+ }
695
+ }
696
+ normalizedLimits = {
697
+ ...(Array.isArray(rawOperations) ? { operations } : {}),
698
+ };
699
+ }
700
+ }
701
+ // ─── Cross-field rules ──────────────────────────────────────────────────
702
+ const owner = transactions[0]?.from;
703
+ if (owner !== undefined) {
704
+ for (const [i, tx] of transactions.entries()) {
705
+ if (!(0, viem_1.isAddressEqual)(tx.from, owner)) {
706
+ fieldErrors.push(`transactions[${transactionIndices[i]}].from: all transactions must share the same from address (expected ${owner}, got ${tx.from})`);
707
+ }
708
+ }
709
+ }
710
+ for (const [i, authorization] of authorizations.entries()) {
711
+ const { owner: authorizationOwnerAddress, domainChainId } = (() => {
712
+ switch (authorization.type) {
713
+ case "erc20Approval":
714
+ return { owner: authorization.owner, domainChainId: undefined };
715
+ case "permit2SignatureTransfer":
716
+ return {
717
+ owner: authorization.owner,
718
+ domainChainId: authorization.typedData.domain.chainId,
719
+ };
720
+ case "erc2612Permit":
721
+ return {
722
+ owner: authorization.typedData.message.owner,
723
+ domainChainId: authorization.typedData.domain.chainId,
724
+ };
725
+ case "blueAuthorization":
726
+ return { owner: authorization.authorizer, domainChainId: undefined };
727
+ case "blueAuthorizationSignature":
728
+ return {
729
+ owner: authorization.typedData.message.authorizer,
730
+ domainChainId: authorization.typedData.domain.chainId,
731
+ };
732
+ default: {
733
+ const _exhaustive = authorization;
734
+ return _exhaustive;
735
+ }
736
+ }
737
+ })();
738
+ if (owner !== undefined &&
739
+ !(0, viem_1.isAddressEqual)(authorizationOwnerAddress, owner)) {
740
+ fieldErrors.push(`authorizations[${i}]: owner must equal the bundle sender ${owner} (got ${authorizationOwnerAddress})`);
741
+ }
742
+ if (domainChainId !== undefined &&
743
+ typeof chainId === "number" &&
744
+ BigInt(domainChainId) !== BigInt(chainId)) {
745
+ fieldErrors.push(`authorizations[${i}].typedData.domain.chainId: must equal the request chainId ${chainId} (got ${domainChainId})`);
746
+ }
747
+ }
748
+ if (fieldErrors.length > 0) {
749
+ throw new errors_js_1.SimulationValidationError("Invalid simulation input", fieldErrors);
750
+ }
751
+ return (0, morpho_ts_1.deepFreeze)({
752
+ chainId: chainId,
753
+ mode,
754
+ transactions,
755
+ authorizations: mode === "preview" ? authorizations : [],
756
+ ...(blockNumber !== undefined ? { blockNumber } : {}),
757
+ ...(normalizedLimits !== undefined ? { limits: normalizedLimits } : {}),
758
+ });
759
+ }