@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
@@ -1,23 +0,0 @@
1
- import { type BlockTag } from "viem";
2
- import type { RawSimulationResult, SimulationTransaction, TenderlyRpcConfig } from "../../types.js";
3
- /**
4
- * Simulate one or more transactions via Tenderly's Node Web3 Gateway.
5
- * Dispatches to `tenderly_simulateTransaction` for a single call and
6
- * `tenderly_simulateBundle` for a multi-call bundle.
7
- *
8
- * @param params.config - Tenderly RPC config (URL with access key embedded).
9
- * @param params.transactions - Bundle to simulate, in execution order.
10
- * @param params.blockNumber - Optional pinned block number or `BlockTag`. Defaults to `latest`.
11
- * @param params.signal - Optional `AbortSignal` for cancellation / timeout.
12
- * @returns A {@link RawSimulationResult} with one `RawCall` per input transaction.
13
- * @throws {SimulationValidationError} when `transactions` is empty.
14
- * @throws {SimulationRevertedError} when any simulated tx reports `status: false`.
15
- * @throws {ExternalServiceError} on non-2xx response, JSON-RPC envelope error,
16
- * schema-validation failure, or fetch-layer failure.
17
- */
18
- export declare function simulateTenderlyRpc(params: {
19
- config: TenderlyRpcConfig;
20
- transactions: SimulationTransaction[];
21
- blockNumber?: bigint | BlockTag;
22
- signal?: AbortSignal;
23
- }): Promise<RawSimulationResult>;
@@ -1,231 +0,0 @@
1
- import { ethAddress, isAddress, isHex, maxUint256, numberToHex, } from "viem";
2
- import { z } from "zod";
3
- import { ExternalServiceError, SimulationRevertedError, SimulationValidationError, } from "../../errors.js";
4
- import { groupAssetChanges, normalizeAssetToken, } from "../asset-changes.js";
5
- const addressSchema = z.custom((val) => typeof val === "string" && isAddress(val));
6
- const hexSchema = z.custom((val) => typeof val === "string" && isHex(val));
7
- const traceFrameSchema = z
8
- .object({
9
- output: hexSchema.optional(),
10
- error: z.string().optional(),
11
- errorReason: z.string().optional(),
12
- })
13
- .passthrough();
14
- const logSchema = z
15
- .object({
16
- raw: z
17
- .object({
18
- address: addressSchema,
19
- topics: z.array(hexSchema).optional(),
20
- data: hexSchema.optional(),
21
- })
22
- .passthrough()
23
- .optional(),
24
- })
25
- .passthrough();
26
- const assetChangeSchema = z
27
- .object({
28
- assetInfo: z
29
- .object({
30
- contractAddress: addressSchema.optional(),
31
- symbol: z.string().optional(),
32
- decimals: z.number().optional(),
33
- })
34
- .passthrough(),
35
- from: addressSchema.optional(),
36
- to: addressSchema.optional(),
37
- rawAmount: hexSchema,
38
- })
39
- .passthrough();
40
- const simResultSchema = z
41
- .object({
42
- status: z.boolean(),
43
- gasUsed: hexSchema,
44
- logs: z.array(logSchema).optional(),
45
- trace: z.array(traceFrameSchema).optional(),
46
- assetChanges: z.array(assetChangeSchema).optional(),
47
- error: z.string().optional(),
48
- errorMessage: z.string().optional(),
49
- })
50
- .passthrough();
51
- const rpcErrorSchema = z
52
- .object({
53
- code: z.number().optional(),
54
- message: z.string(),
55
- data: z.unknown().optional(),
56
- })
57
- .passthrough();
58
- function rpcEnvelope(result) {
59
- return z
60
- .object({ result: result.optional(), error: rpcErrorSchema.optional() })
61
- .passthrough();
62
- }
63
- const singleEnvelope = rpcEnvelope(simResultSchema);
64
- const bundleEnvelope = rpcEnvelope(z.array(simResultSchema).min(1));
65
- /**
66
- * Simulate one or more transactions via Tenderly's Node Web3 Gateway.
67
- * Dispatches to `tenderly_simulateTransaction` for a single call and
68
- * `tenderly_simulateBundle` for a multi-call bundle.
69
- *
70
- * @param params.config - Tenderly RPC config (URL with access key embedded).
71
- * @param params.transactions - Bundle to simulate, in execution order.
72
- * @param params.blockNumber - Optional pinned block number or `BlockTag`. Defaults to `latest`.
73
- * @param params.signal - Optional `AbortSignal` for cancellation / timeout.
74
- * @returns A {@link RawSimulationResult} with one `RawCall` per input transaction.
75
- * @throws {SimulationValidationError} when `transactions` is empty.
76
- * @throws {SimulationRevertedError} when any simulated tx reports `status: false`.
77
- * @throws {ExternalServiceError} on non-2xx response, JSON-RPC envelope error,
78
- * schema-validation failure, or fetch-layer failure.
79
- */
80
- export async function simulateTenderlyRpc(params) {
81
- const { config, transactions, blockNumber, signal } = params;
82
- const firstTx = transactions[0];
83
- if (!firstTx) {
84
- throw new SimulationValidationError("At least one transaction is required", []);
85
- }
86
- const block = encodeBlock(blockNumber);
87
- // Inflate sender ETH balance to avoid false "insufficient funds for gas"
88
- // reverts on wallets low on native gas token — mirrors simulateV1.
89
- const stateOverrides = buildStateOverrides(firstTx.from);
90
- try {
91
- if (transactions.length === 1) {
92
- const json = await rpcRequest({
93
- rpcUrl: config.rpcUrl,
94
- method: "tenderly_simulateTransaction",
95
- params: [buildCall(firstTx), block, stateOverrides],
96
- signal,
97
- });
98
- const result = unwrapResult(singleEnvelope.parse(json));
99
- return {
100
- calls: [toRawCall(result)],
101
- assetChanges: toAssetChanges([result]),
102
- };
103
- }
104
- const json = await rpcRequest({
105
- rpcUrl: config.rpcUrl,
106
- method: "tenderly_simulateBundle",
107
- params: [transactions.map(buildCall), block, stateOverrides],
108
- signal,
109
- });
110
- const results = unwrapResult(bundleEnvelope.parse(json));
111
- return {
112
- calls: results.map(toRawCall),
113
- assetChanges: toAssetChanges(results),
114
- };
115
- }
116
- catch (error) {
117
- if (error instanceof SimulationRevertedError)
118
- throw error;
119
- if (error instanceof ExternalServiceError)
120
- throw error;
121
- throw new ExternalServiceError(`Tenderly RPC error: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
122
- }
123
- }
124
- async function rpcRequest(params) {
125
- const { rpcUrl, method, params: rpcParams, signal } = params;
126
- const response = await fetch(rpcUrl, {
127
- method: "POST",
128
- headers: { "Content-Type": "application/json" },
129
- body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params: rpcParams }),
130
- signal,
131
- });
132
- if (!response.ok) {
133
- throw new ExternalServiceError(`Tenderly RPC returned ${response.status}: ${response.statusText}`);
134
- }
135
- return await response.json();
136
- }
137
- function unwrapResult(envelope) {
138
- if (envelope.error) {
139
- throw new ExternalServiceError(`Tenderly RPC error: ${envelope.error.message}`);
140
- }
141
- if (envelope.result === undefined) {
142
- throw new ExternalServiceError("Tenderly RPC returned no result");
143
- }
144
- return envelope.result;
145
- }
146
- function buildCall(tx) {
147
- return {
148
- from: tx.from,
149
- to: tx.to,
150
- input: tx.data,
151
- data: tx.data,
152
- value: numberToHex(tx.value ?? 0n),
153
- };
154
- }
155
- function buildStateOverrides(sender) {
156
- // Half of uint256 (not the ceiling) so the override leaves headroom for
157
- // inbound native ETH — a WETH.withdraw refund or swap payout would overflow
158
- // the recipient balance and revert the value transfer if the sender were
159
- // pinned at maxUint256.
160
- return { [sender]: { balance: numberToHex(maxUint256 / 2n) } };
161
- }
162
- function encodeBlock(blockNumber) {
163
- if (blockNumber === undefined)
164
- return "latest";
165
- return typeof blockNumber === "bigint"
166
- ? numberToHex(blockNumber)
167
- : blockNumber;
168
- }
169
- function toRawCall(data) {
170
- if (data.status !== true) {
171
- // Tenderly RPC surfaces the revert reason on the trace frame; the
172
- // top-level fields are checked as a defensive fallback.
173
- const traceError = data.trace?.find((f) => f.errorReason || f.error);
174
- const message = traceError?.errorReason ||
175
- traceError?.error ||
176
- data.errorMessage ||
177
- data.error ||
178
- "Transaction simulation reverted";
179
- throw new SimulationRevertedError(message, data);
180
- }
181
- const logs = [];
182
- for (const log of data.logs ?? []) {
183
- if (log.raw) {
184
- logs.push({
185
- address: log.raw.address,
186
- topics: log.raw.topics ?? [],
187
- data: log.raw.data ?? "0x",
188
- });
189
- }
190
- }
191
- return {
192
- logs,
193
- status: true,
194
- returnData: data.trace?.[0]?.output ?? "0x",
195
- gasUsed: BigInt(data.gasUsed),
196
- };
197
- }
198
- /**
199
- * Reduce Tenderly's per-transfer asset changes to net per-token balance changes
200
- * grouped by account. Both endpoints of each transfer are accounted (sender,
201
- * counterparties, and the zero address for mints/burns).
202
- */
203
- function toAssetChanges(results) {
204
- const entries = [];
205
- for (const result of results) {
206
- for (const change of result.assetChanges ?? []) {
207
- const amount = BigInt(change.rawAmount);
208
- const token = change.assetInfo.contractAddress
209
- ? normalizeAssetToken(change.assetInfo.contractAddress)
210
- : ethAddress;
211
- const { symbol, decimals } = change.assetInfo;
212
- if (change.to)
213
- entries.push({
214
- account: change.to,
215
- token,
216
- diff: amount,
217
- symbol,
218
- decimals,
219
- });
220
- if (change.from)
221
- entries.push({
222
- account: change.from,
223
- token,
224
- diff: -amount,
225
- symbol,
226
- decimals,
227
- });
228
- }
229
- }
230
- return groupAssetChanges(entries);
231
- }
@@ -1,17 +0,0 @@
1
- import type { SimulateParams, SimulationTransaction } from "../../types.js";
2
- /**
3
- * Stage 3 of the simulate() pipeline.
4
- *
5
- * Prepends any resolved authorizations (e.g. ERC20 `approve` calls generated from
6
- * `{ type: 'signature' }` entries) to the user's transactions. If no authorizations
7
- * were supplied, returns the user's transactions unchanged.
8
- *
9
- * Defense-in-depth: after prepending, verifies EVERY resolved tx shares the user
10
- * sender. This catches an `{ type: 'approval', transaction }` auth where the
11
- * caller-provided `transaction.from` does not match the bundle sender — the
12
- * `approval` variant otherwise passes through without validation. Rejecting here
13
- * keeps the "all simulationTxs share one `from`" invariant the downstream
14
- * backends (eth_simulateV1) assume, and prevents a malicious or buggy caller
15
- * from prepending a tx on behalf of an unexpected address.
16
- */
17
- export declare function buildSimulationTxs(params: SimulateParams): SimulationTransaction[];
@@ -1,47 +0,0 @@
1
- import { getAddress } from "viem";
2
- import { SimulationValidationError } from "../../errors.js";
3
- import { resolveAuthorizations } from "../authorizations/index.js";
4
- /**
5
- * Stage 3 of the simulate() pipeline.
6
- *
7
- * Prepends any resolved authorizations (e.g. ERC20 `approve` calls generated from
8
- * `{ type: 'signature' }` entries) to the user's transactions. If no authorizations
9
- * were supplied, returns the user's transactions unchanged.
10
- *
11
- * Defense-in-depth: after prepending, verifies EVERY resolved tx shares the user
12
- * sender. This catches an `{ type: 'approval', transaction }` auth where the
13
- * caller-provided `transaction.from` does not match the bundle sender — the
14
- * `approval` variant otherwise passes through without validation. Rejecting here
15
- * keeps the "all simulationTxs share one `from`" invariant the downstream
16
- * backends (eth_simulateV1) assume, and prevents a malicious or buggy caller
17
- * from prepending a tx on behalf of an unexpected address.
18
- */
19
- export function buildSimulationTxs(params) {
20
- const { transactions, authorizations } = params;
21
- if (!authorizations || authorizations.length === 0) {
22
- return transactions;
23
- }
24
- const sender = transactions[0].from;
25
- const resolved = resolveAuthorizations(authorizations, sender);
26
- const simulationTxs = [...resolved, ...transactions];
27
- const senderChecksum = getAddress(sender);
28
- const mismatches = [];
29
- for (let i = 0; i < simulationTxs.length; i++) {
30
- const tx = simulationTxs[i];
31
- let txFromChecksum;
32
- try {
33
- txFromChecksum = getAddress(tx.from);
34
- }
35
- catch {
36
- mismatches.push(`simulationTxs[${i}].from: not a valid address (${tx.from})`);
37
- continue;
38
- }
39
- if (txFromChecksum !== senderChecksum) {
40
- mismatches.push(`simulationTxs[${i}].from: expected ${senderChecksum}, got ${txFromChecksum}`);
41
- }
42
- }
43
- if (mismatches.length > 0) {
44
- throw new SimulationValidationError("Authorization transaction(s) must share the user sender", mismatches);
45
- }
46
- return simulationTxs;
47
- }
@@ -1,22 +0,0 @@
1
- import type { Address, BlockTag } from "viem";
2
- import type { RawSimulationResult, SimulationConfig, SimulationTransaction } from "../../types.js";
3
- /**
4
- * Stage 4 of the simulate() pipeline.
5
- *
6
- * Dispatches the transaction bundle to the available backend with a shared timeout
7
- * budget:
8
- * - If a Tenderly RPC URL is configured for the chain, it gets
9
- * `TENDERLY_BUDGET_RATIO` (60%) of the timeout. On `ExternalServiceError`,
10
- * falls back to `eth_simulateV1` with `max(remaining, FALLBACK_MIN_BUDGET_MS)`
11
- * so the fallback still has a viable window when Tenderly eats its full slice.
12
- * `SimulationRevertedError` (contract revert) propagates immediately without
13
- * retry — a revert is a property of the bundle, not the backend.
14
- * - If only `eth_simulateV1` is configured, it gets the full timeout.
15
- */
16
- export declare function executeSimulation(params: {
17
- config: SimulationConfig;
18
- chainId: number;
19
- transactions: SimulationTransaction[];
20
- blockNumber?: bigint | BlockTag;
21
- wNative?: Address | null;
22
- }): Promise<RawSimulationResult>;
@@ -1,79 +0,0 @@
1
- import { ExternalServiceError, UnsupportedChainError } from "../../errors.js";
2
- import { simulateTenderlyRpc, simulateV1 } from "../backends/index.js";
3
- import { resolveChain } from "./resolve-chain.js";
4
- /** Total budget for a single `simulate()` call across both backends. */
5
- const DEFAULT_TIMEOUT_MS = 5000;
6
- /** Fraction of `timeoutMs` given to Tenderly before falling back to `eth_simulateV1`. */
7
- const TENDERLY_BUDGET_RATIO = 0.6;
8
- /**
9
- * Minimum budget (ms) handed to the `eth_simulateV1` fallback when Tenderly fails.
10
- *
11
- * Without a floor, if Tenderly exhausts its slice and dies at the deadline, the
12
- * fallback would get ~0 ms and abort immediately — turning a degraded-Tenderly
13
- * scenario into a total outage even when the fallback backend is healthy. A
14
- * modest floor keeps the fallback viable at the cost of the overall
15
- * `timeoutMs` being treated as a soft ceiling.
16
- */
17
- const FALLBACK_MIN_BUDGET_MS = 1500;
18
- /**
19
- * Stage 4 of the simulate() pipeline.
20
- *
21
- * Dispatches the transaction bundle to the available backend with a shared timeout
22
- * budget:
23
- * - If a Tenderly RPC URL is configured for the chain, it gets
24
- * `TENDERLY_BUDGET_RATIO` (60%) of the timeout. On `ExternalServiceError`,
25
- * falls back to `eth_simulateV1` with `max(remaining, FALLBACK_MIN_BUDGET_MS)`
26
- * so the fallback still has a viable window when Tenderly eats its full slice.
27
- * `SimulationRevertedError` (contract revert) propagates immediately without
28
- * retry — a revert is a property of the bundle, not the backend.
29
- * - If only `eth_simulateV1` is configured, it gets the full timeout.
30
- */
31
- export async function executeSimulation(params) {
32
- const { config, chainId, transactions, blockNumber, wNative } = params;
33
- const chain = resolveChain(config, chainId);
34
- const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
35
- const deadline = Date.now() + timeoutMs;
36
- if (chain.tenderlyRpc) {
37
- const tenderlyTimeout = Math.floor(timeoutMs * TENDERLY_BUDGET_RATIO);
38
- try {
39
- return await simulateTenderlyRpc({
40
- config: chain.tenderlyRpc,
41
- transactions,
42
- blockNumber,
43
- signal: AbortSignal.timeout(tenderlyTimeout),
44
- });
45
- }
46
- catch (error) {
47
- if (!(error instanceof ExternalServiceError))
48
- throw error;
49
- config.logger?.warn("Tenderly simulation failed, attempting fallback", {
50
- chainId,
51
- error: error.message,
52
- cause: error.cause,
53
- });
54
- if (!chain.simulateV1Url)
55
- throw error;
56
- const fallbackBudget = Math.max(deadline - Date.now(), FALLBACK_MIN_BUDGET_MS);
57
- return await simulateV1({
58
- rpcUrl: chain.simulateV1Url,
59
- chainId,
60
- transactions,
61
- blockNumber,
62
- wNative,
63
- signal: AbortSignal.timeout(fallbackBudget),
64
- });
65
- }
66
- }
67
- /* v8 ignore next: resolveChain rejects this state before executeSimulation reaches the fallback path. */
68
- if (!chain.simulateV1Url) {
69
- throw new UnsupportedChainError(chainId);
70
- }
71
- return await simulateV1({
72
- rpcUrl: chain.simulateV1Url,
73
- chainId,
74
- transactions,
75
- blockNumber,
76
- wNative,
77
- signal: AbortSignal.timeout(timeoutMs),
78
- });
79
- }
@@ -1,4 +0,0 @@
1
- export { buildSimulationTxs } from "./build-simulation-txs.js";
2
- export { assertNoBundlesRetention } from "./bundles-retention.js";
3
- export { executeSimulation } from "./execute-simulation.js";
4
- export { validateInput } from "./validate-input.js";
@@ -1,4 +0,0 @@
1
- export { buildSimulationTxs } from "./build-simulation-txs.js";
2
- export { assertNoBundlesRetention } from "./bundles-retention.js";
3
- export { executeSimulation } from "./execute-simulation.js";
4
- export { validateInput } from "./validate-input.js";
@@ -1,10 +0,0 @@
1
- import type { SimulateParams } from "../../types.js";
2
- /**
3
- * Stage 1 of the simulate() pipeline.
4
- *
5
- * Throws `SimulationValidationError` with a `fieldErrors[]` list on any invalid input:
6
- * empty transactions, malformed / zero-addr fields, missing `data`, negative `value`,
7
- * bad `chainId`, or mixed senders (all txs in a bundle must share the same `from`).
8
- * Also runs `validateAuthorizations` on the optional authorizations array.
9
- */
10
- export declare function validateInput(params: SimulateParams): void;
@@ -1,52 +0,0 @@
1
- import { isAddress, zeroAddress } from "viem";
2
- import { SimulationValidationError } from "../../errors.js";
3
- import { validateAuthorizations } from "../authorizations/index.js";
4
- /**
5
- * Stage 1 of the simulate() pipeline.
6
- *
7
- * Throws `SimulationValidationError` with a `fieldErrors[]` list on any invalid input:
8
- * empty transactions, malformed / zero-addr fields, missing `data`, negative `value`,
9
- * bad `chainId`, or mixed senders (all txs in a bundle must share the same `from`).
10
- * Also runs `validateAuthorizations` on the optional authorizations array.
11
- */
12
- export function validateInput(params) {
13
- const errors = [];
14
- if (!Number.isInteger(params.chainId) || params.chainId <= 0) {
15
- errors.push(`chainId: must be a positive integer (got ${params.chainId})`);
16
- }
17
- const transactions = params.transactions ?? [];
18
- if (transactions.length === 0) {
19
- errors.push("transactions: must contain at least 1 transaction");
20
- }
21
- for (let i = 0; i < transactions.length; i++) {
22
- const tx = transactions[i];
23
- if (!tx.from || !isAddress(tx.from) || tx.from === zeroAddress) {
24
- errors.push(`transactions[${i}].from: must be a valid non-zero address`);
25
- }
26
- if (!tx.to || !isAddress(tx.to) || tx.to === zeroAddress) {
27
- errors.push(`transactions[${i}].to: must be a valid non-zero address`);
28
- }
29
- if (!tx.data) {
30
- errors.push(`transactions[${i}].data: must be provided`);
31
- }
32
- if (tx.value !== undefined && tx.value < 0n) {
33
- errors.push(`transactions[${i}].value: must be non-negative`);
34
- }
35
- }
36
- // Same-sender check uses RAW `.from` strings (lowercased) so the invariant
37
- // fires even when some tx `from`s are malformed — individual txs already
38
- // flagged above, but we still want one loud signal that the bundle's
39
- // senders don't match.
40
- if (transactions.length > 1) {
41
- const distinct = new Set(transactions.map((tx) => (tx.from ?? "").toLowerCase()));
42
- if (distinct.size > 1) {
43
- errors.push("transactions: all transactions must share the same from address");
44
- }
45
- }
46
- if (params.authorizations) {
47
- errors.push(...validateAuthorizations(params.authorizations));
48
- }
49
- if (errors.length > 0) {
50
- throw new SimulationValidationError("Invalid simulation input", errors);
51
- }
52
- }