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