@morpho-org/evm-simulation 4.1.0 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,19 +2,13 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- EVM transaction simulation engine for Morpho — bundle execution preview
6
- and transfer parsing.
5
+ EVM transaction simulation engine for Morpho — bundle execution preview,
6
+ transfer parsing, and net per-account balance changes.
7
7
 
8
8
  ## Installation
9
9
 
10
- Add to the consuming app's `package.json`:
11
-
12
- ```jsonc
13
- {
14
- "dependencies": {
15
- "@morpho-org/evm-simulation": "workspace:*"
16
- }
17
- }
10
+ ```bash
11
+ pnpm add @morpho-org/evm-simulation
18
12
  ```
19
13
 
20
14
  ## Usage
@@ -40,11 +34,14 @@ const config: SimulationConfig = {
40
34
  };
41
35
 
42
36
  try {
43
- const { transfers, simulationTxs } = await simulate(config, {
44
- chainId: 1,
45
- transactions: [{ from: user, to: vault, data: encodedDeposit }],
46
- authorizations: [{ type: "signature", token: usdc, spender: vault }],
47
- });
37
+ const { simulationTxs, calls, transfers, assetChanges } = await simulate(
38
+ config,
39
+ {
40
+ chainId: 1,
41
+ transactions: [{ from: user, to: vault, data: encodedDeposit }],
42
+ authorizations: [{ type: "signature", token: usdc, spender: vault }],
43
+ },
44
+ );
48
45
  } catch (err) {
49
46
  if (err instanceof SimulationRevertedError) {
50
47
  // show err.reason to the user
@@ -61,7 +58,8 @@ All symbols below are re-exported from the package root.
61
58
 
62
59
  - `simulate(config, params)` — run a bundle through the simulation pipeline.
63
60
  - Config types: `SimulationConfig`, `TenderlyRpcConfig`, `ChainSimulationConfig`, `SimulationLogger`.
64
- - Data types: `SimulationTransaction`, `SimulationAuthorization`, `Transfer`, `SimulateParams`, `SimulationResult`.
61
+ - Input types: `SimulateParams`, `SimulationTransaction`, `SimulationAuthorization`.
62
+ - Result types: `SimulationResult`, `SimulationCall`, `Transfer`, `AccountAssetChanges`, `AssetChange`, `RawLog`.
65
63
  - Errors: `SimulationPackageError` (abstract base — `instanceof` it to catch any package error), `SimulationRevertedError`, `BlacklistViolationError`, `ExternalServiceError`, `SimulationValidationError`, `UnsupportedChainError`.
66
64
 
67
65
  ### Deeper docs
@@ -10,9 +10,9 @@ export declare abstract class SimulationPackageError extends Error {
10
10
  /** Transaction would revert on-chain. Not bypassable. */
11
11
  export declare class SimulationRevertedError extends SimulationPackageError {
12
12
  readonly reason: string | undefined;
13
- readonly details?: unknown | undefined;
13
+ readonly details?: unknown;
14
14
  readonly code = "SIMULATION_REVERTED";
15
- constructor(reason: string | undefined, details?: unknown | undefined);
15
+ constructor(reason: string | undefined, details?: unknown);
16
16
  }
17
17
  interface RetainedAsset {
18
18
  address: string | undefined;
@@ -1,7 +1,8 @@
1
- import type { SimulationLogger, Transfer } from "../../types.js";
1
+ import type { AccountAssetChanges, SimulationLogger, Transfer } from "../../types.js";
2
2
  interface AssertNoBundlerRetentionParams {
3
3
  chainId: number;
4
4
  transfers: Transfer[];
5
+ assetChanges: readonly AccountAssetChanges[];
5
6
  logger?: SimulationLogger;
6
7
  }
7
8
  /**
@@ -10,10 +11,25 @@ interface AssertNoBundlerRetentionParams {
10
11
  * Uses net flow (inbound minus outbound) per (bundler address, token) pair.
11
12
  * Bundler3 legitimately receives tokens as an intermediary (user → bundler3 →
12
13
  * vault), so gross inbound would fire false positives. We only block positive
13
- * net flow above `DUST_THRESHOLD` — tokens stuck in a bundler address. Negative
14
+ * net flow above `DUST_THRESHOLD` — value stuck in a bundler address. Negative
14
15
  * net flow means the simulated bundle sweeps a pre-existing or state-overridden
15
16
  * bundler balance. That is useful telemetry, but not current-bundle retention,
16
17
  * so it is warned instead of raising a blacklist violation.
18
+ *
19
+ * **Two flow sources, one per asset class.** ERC20 / WETH9 retention is read
20
+ * from parsed `transfers` (log-derived, identical across backends). Native ETH
21
+ * emits no event log, so it can never appear in `transfers` on the Tenderly
22
+ * primary backend — Tenderly derives native moves into `assetChanges` instead.
23
+ * Native ETH is therefore read from `assetChanges`, the cross-backend source of
24
+ * truth for native value: Tenderly derives it from its trace, and
25
+ * `eth_simulateV1` derives it from the synthetic `traceTransfers` logs. Without
26
+ * this, native ETH stuck in bundler3 would pass the guard undetected on the
27
+ * Tenderly path (Cantina finding 1440).
28
+ *
29
+ * To avoid double-counting native ETH on `eth_simulateV1` — where the same
30
+ * native move exists both as a synthetic `ethAddress` transfer log *and* in the
31
+ * `assetChanges` derived from it — native transfer logs are only used as a
32
+ * fallback for bundler addresses that carry no native `assetChanges` entry.
17
33
  */
18
34
  export declare function assertNoBundlerRetention(params: AssertNoBundlerRetentionParams): void;
19
35
  export {};
@@ -48,13 +48,28 @@ function getBundlerAddresses(chainId, logger) {
48
48
  * Uses net flow (inbound minus outbound) per (bundler address, token) pair.
49
49
  * Bundler3 legitimately receives tokens as an intermediary (user → bundler3 →
50
50
  * vault), so gross inbound would fire false positives. We only block positive
51
- * net flow above `DUST_THRESHOLD` — tokens stuck in a bundler address. Negative
51
+ * net flow above `DUST_THRESHOLD` — value stuck in a bundler address. Negative
52
52
  * net flow means the simulated bundle sweeps a pre-existing or state-overridden
53
53
  * bundler balance. That is useful telemetry, but not current-bundle retention,
54
54
  * so it is warned instead of raising a blacklist violation.
55
+ *
56
+ * **Two flow sources, one per asset class.** ERC20 / WETH9 retention is read
57
+ * from parsed `transfers` (log-derived, identical across backends). Native ETH
58
+ * emits no event log, so it can never appear in `transfers` on the Tenderly
59
+ * primary backend — Tenderly derives native moves into `assetChanges` instead.
60
+ * Native ETH is therefore read from `assetChanges`, the cross-backend source of
61
+ * truth for native value: Tenderly derives it from its trace, and
62
+ * `eth_simulateV1` derives it from the synthetic `traceTransfers` logs. Without
63
+ * this, native ETH stuck in bundler3 would pass the guard undetected on the
64
+ * Tenderly path (Cantina finding 1440).
65
+ *
66
+ * To avoid double-counting native ETH on `eth_simulateV1` — where the same
67
+ * native move exists both as a synthetic `ethAddress` transfer log *and* in the
68
+ * `assetChanges` derived from it — native transfer logs are only used as a
69
+ * fallback for bundler addresses that carry no native `assetChanges` entry.
55
70
  */
56
71
  function assertNoBundlerRetention(params) {
57
- const { chainId, transfers, logger } = params;
72
+ const { chainId, transfers, assetChanges, logger } = params;
58
73
  const bundlerAddresses = getBundlerAddresses(chainId, logger);
59
74
  if (bundlerAddresses.size === 0)
60
75
  return;
@@ -71,10 +86,29 @@ function assertNoBundlerRetention(params) {
71
86
  flow.set(key, { address, token, netChange: delta });
72
87
  }
73
88
  };
89
+ // Native ETH from `assetChanges` (authoritative, cross-backend). Track which
90
+ // bundlers already have a native entry here so the transfer-log pass below
91
+ // does not re-add the same native move on `eth_simulateV1`.
92
+ const nativeFromAssetChanges = new Set();
93
+ for (const { account, changes } of assetChanges) {
94
+ if (!bundlerAddresses.has(account))
95
+ continue;
96
+ for (const change of changes) {
97
+ if (change.token !== viem_1.ethAddress)
98
+ continue;
99
+ recordFlow(account, viem_1.ethAddress, change.diff);
100
+ nativeFromAssetChanges.add(account.toLowerCase());
101
+ }
102
+ }
103
+ // ERC20 / WETH9 retention from transfer logs. Native ETH transfers (the
104
+ // `eth_simulateV1` synthetic sentinel) only backfill a bundler endpoint that
105
+ // is absent from `nativeFromAssetChanges`, so native is never summed on top
106
+ // of the `assetChanges` value already recorded for that address.
107
+ const usesAssetChangeNative = (t, addr) => t.token === viem_1.ethAddress && nativeFromAssetChanges.has(addr.toLowerCase());
74
108
  for (const t of transfers) {
75
- if (bundlerAddresses.has(t.to))
109
+ if (bundlerAddresses.has(t.to) && !usesAssetChangeNative(t, t.to))
76
110
  recordFlow(t.to, t.token, t.amount);
77
- if (bundlerAddresses.has(t.from))
111
+ if (bundlerAddresses.has(t.from) && !usesAssetChangeNative(t, t.from))
78
112
  recordFlow(t.from, t.token, -t.amount);
79
113
  }
80
114
  const entries = [...flow.values()];
@@ -10,7 +10,8 @@ import type { SimulateParams, SimulationConfig, SimulationResult } from "../type
10
10
  * - `transfers` → user-facing preview / server-side verification.
11
11
  * - `simulationTxs` + `transfers` → server-side verification before broadcast.
12
12
  * - `calls[i]` → per-tx raw backend output (`logs`, `status`, `returnData`, `gasUsed`).
13
- * Aligned 1:1 with `simulationTxs[i]`.
13
+ * Aligned 1:1 with `simulationTxs[i]`. `gasUsed` is not a safe gas limit; consumers
14
+ * deriving one must add their own headroom.
14
15
  * - `assetChanges` → net per-asset balance changes grouped by account (sender and
15
16
  * counterparties) over the whole bundle, normalized to the same shape across backends.
16
17
  * - `transfers[k].txIdx` → index into `simulationTxs` of the tx that emitted the
@@ -15,7 +15,8 @@ const index_js_2 = require("./pipeline/index.js");
15
15
  * - `transfers` → user-facing preview / server-side verification.
16
16
  * - `simulationTxs` + `transfers` → server-side verification before broadcast.
17
17
  * - `calls[i]` → per-tx raw backend output (`logs`, `status`, `returnData`, `gasUsed`).
18
- * Aligned 1:1 with `simulationTxs[i]`.
18
+ * Aligned 1:1 with `simulationTxs[i]`. `gasUsed` is not a safe gas limit; consumers
19
+ * deriving one must add their own headroom.
19
20
  * - `assetChanges` → net per-asset balance changes grouped by account (sender and
20
21
  * counterparties) over the whole bundle, normalized to the same shape across backends.
21
22
  * - `transfers[k].txIdx` → index into `simulationTxs` of the tx that emitted the
@@ -80,6 +81,7 @@ async function simulate(config, params) {
80
81
  (0, index_js_2.assertNoBundlerRetention)({
81
82
  chainId: params.chainId,
82
83
  transfers,
84
+ assetChanges: result.assetChanges,
83
85
  logger: config.logger,
84
86
  });
85
87
  return {
@@ -201,6 +201,11 @@ export interface SimulationCall {
201
201
  readonly status: boolean;
202
202
  /** Return data from the top-level call. */
203
203
  readonly returnData: Hex;
204
- /** Gas used by this call (root frame). */
204
+ /**
205
+ * Gas consumed by this call's root frame, not a safe gas limit. This is
206
+ * post-refund consumption and does not account for EIP-150's 63/64 rule in
207
+ * nested calls, so consumers deriving a limit must add their own headroom,
208
+ * larger than headroom derived from `eth_estimateGas`.
209
+ */
205
210
  readonly gasUsed: bigint;
206
211
  }
@@ -10,9 +10,9 @@ export declare abstract class SimulationPackageError extends Error {
10
10
  /** Transaction would revert on-chain. Not bypassable. */
11
11
  export declare class SimulationRevertedError extends SimulationPackageError {
12
12
  readonly reason: string | undefined;
13
- readonly details?: unknown | undefined;
13
+ readonly details?: unknown;
14
14
  readonly code = "SIMULATION_REVERTED";
15
- constructor(reason: string | undefined, details?: unknown | undefined);
15
+ constructor(reason: string | undefined, details?: unknown);
16
16
  }
17
17
  interface RetainedAsset {
18
18
  address: string | undefined;
@@ -1,7 +1,8 @@
1
- import type { SimulationLogger, Transfer } from "../../types.js";
1
+ import type { AccountAssetChanges, SimulationLogger, Transfer } from "../../types.js";
2
2
  interface AssertNoBundlerRetentionParams {
3
3
  chainId: number;
4
4
  transfers: Transfer[];
5
+ assetChanges: readonly AccountAssetChanges[];
5
6
  logger?: SimulationLogger;
6
7
  }
7
8
  /**
@@ -10,10 +11,25 @@ interface AssertNoBundlerRetentionParams {
10
11
  * Uses net flow (inbound minus outbound) per (bundler address, token) pair.
11
12
  * Bundler3 legitimately receives tokens as an intermediary (user → bundler3 →
12
13
  * vault), so gross inbound would fire false positives. We only block positive
13
- * net flow above `DUST_THRESHOLD` — tokens stuck in a bundler address. Negative
14
+ * net flow above `DUST_THRESHOLD` — value stuck in a bundler address. Negative
14
15
  * net flow means the simulated bundle sweeps a pre-existing or state-overridden
15
16
  * bundler balance. That is useful telemetry, but not current-bundle retention,
16
17
  * so it is warned instead of raising a blacklist violation.
18
+ *
19
+ * **Two flow sources, one per asset class.** ERC20 / WETH9 retention is read
20
+ * from parsed `transfers` (log-derived, identical across backends). Native ETH
21
+ * emits no event log, so it can never appear in `transfers` on the Tenderly
22
+ * primary backend — Tenderly derives native moves into `assetChanges` instead.
23
+ * Native ETH is therefore read from `assetChanges`, the cross-backend source of
24
+ * truth for native value: Tenderly derives it from its trace, and
25
+ * `eth_simulateV1` derives it from the synthetic `traceTransfers` logs. Without
26
+ * this, native ETH stuck in bundler3 would pass the guard undetected on the
27
+ * Tenderly path (Cantina finding 1440).
28
+ *
29
+ * To avoid double-counting native ETH on `eth_simulateV1` — where the same
30
+ * native move exists both as a synthetic `ethAddress` transfer log *and* in the
31
+ * `assetChanges` derived from it — native transfer logs are only used as a
32
+ * fallback for bundler addresses that carry no native `assetChanges` entry.
17
33
  */
18
34
  export declare function assertNoBundlerRetention(params: AssertNoBundlerRetentionParams): void;
19
35
  export {};
@@ -1,6 +1,6 @@
1
1
  import { getChainAddresses, UnsupportedChainIdError, } from "@morpho-org/blue-sdk";
2
2
  import { isDefined } from "@morpho-org/morpho-ts";
3
- import { getAddress } from "viem";
3
+ import { ethAddress, getAddress } from "viem";
4
4
  import { BlacklistViolationError } from "../../errors.js";
5
5
  /**
6
6
  * Dust tolerance for bundler retention, in raw token units.
@@ -45,13 +45,28 @@ function getBundlerAddresses(chainId, logger) {
45
45
  * Uses net flow (inbound minus outbound) per (bundler address, token) pair.
46
46
  * Bundler3 legitimately receives tokens as an intermediary (user → bundler3 →
47
47
  * vault), so gross inbound would fire false positives. We only block positive
48
- * net flow above `DUST_THRESHOLD` — tokens stuck in a bundler address. Negative
48
+ * net flow above `DUST_THRESHOLD` — value stuck in a bundler address. Negative
49
49
  * net flow means the simulated bundle sweeps a pre-existing or state-overridden
50
50
  * bundler balance. That is useful telemetry, but not current-bundle retention,
51
51
  * so it is warned instead of raising a blacklist violation.
52
+ *
53
+ * **Two flow sources, one per asset class.** ERC20 / WETH9 retention is read
54
+ * from parsed `transfers` (log-derived, identical across backends). Native ETH
55
+ * emits no event log, so it can never appear in `transfers` on the Tenderly
56
+ * primary backend — Tenderly derives native moves into `assetChanges` instead.
57
+ * Native ETH is therefore read from `assetChanges`, the cross-backend source of
58
+ * truth for native value: Tenderly derives it from its trace, and
59
+ * `eth_simulateV1` derives it from the synthetic `traceTransfers` logs. Without
60
+ * this, native ETH stuck in bundler3 would pass the guard undetected on the
61
+ * Tenderly path (Cantina finding 1440).
62
+ *
63
+ * To avoid double-counting native ETH on `eth_simulateV1` — where the same
64
+ * native move exists both as a synthetic `ethAddress` transfer log *and* in the
65
+ * `assetChanges` derived from it — native transfer logs are only used as a
66
+ * fallback for bundler addresses that carry no native `assetChanges` entry.
52
67
  */
53
68
  export function assertNoBundlerRetention(params) {
54
- const { chainId, transfers, logger } = params;
69
+ const { chainId, transfers, assetChanges, logger } = params;
55
70
  const bundlerAddresses = getBundlerAddresses(chainId, logger);
56
71
  if (bundlerAddresses.size === 0)
57
72
  return;
@@ -68,10 +83,29 @@ export function assertNoBundlerRetention(params) {
68
83
  flow.set(key, { address, token, netChange: delta });
69
84
  }
70
85
  };
86
+ // Native ETH from `assetChanges` (authoritative, cross-backend). Track which
87
+ // bundlers already have a native entry here so the transfer-log pass below
88
+ // does not re-add the same native move on `eth_simulateV1`.
89
+ const nativeFromAssetChanges = new Set();
90
+ for (const { account, changes } of assetChanges) {
91
+ if (!bundlerAddresses.has(account))
92
+ continue;
93
+ for (const change of changes) {
94
+ if (change.token !== ethAddress)
95
+ continue;
96
+ recordFlow(account, ethAddress, change.diff);
97
+ nativeFromAssetChanges.add(account.toLowerCase());
98
+ }
99
+ }
100
+ // ERC20 / WETH9 retention from transfer logs. Native ETH transfers (the
101
+ // `eth_simulateV1` synthetic sentinel) only backfill a bundler endpoint that
102
+ // is absent from `nativeFromAssetChanges`, so native is never summed on top
103
+ // of the `assetChanges` value already recorded for that address.
104
+ const usesAssetChangeNative = (t, addr) => t.token === ethAddress && nativeFromAssetChanges.has(addr.toLowerCase());
71
105
  for (const t of transfers) {
72
- if (bundlerAddresses.has(t.to))
106
+ if (bundlerAddresses.has(t.to) && !usesAssetChangeNative(t, t.to))
73
107
  recordFlow(t.to, t.token, t.amount);
74
- if (bundlerAddresses.has(t.from))
108
+ if (bundlerAddresses.has(t.from) && !usesAssetChangeNative(t, t.from))
75
109
  recordFlow(t.from, t.token, -t.amount);
76
110
  }
77
111
  const entries = [...flow.values()];
@@ -10,7 +10,8 @@ import type { SimulateParams, SimulationConfig, SimulationResult } from "../type
10
10
  * - `transfers` → user-facing preview / server-side verification.
11
11
  * - `simulationTxs` + `transfers` → server-side verification before broadcast.
12
12
  * - `calls[i]` → per-tx raw backend output (`logs`, `status`, `returnData`, `gasUsed`).
13
- * Aligned 1:1 with `simulationTxs[i]`.
13
+ * Aligned 1:1 with `simulationTxs[i]`. `gasUsed` is not a safe gas limit; consumers
14
+ * deriving one must add their own headroom.
14
15
  * - `assetChanges` → net per-asset balance changes grouped by account (sender and
15
16
  * counterparties) over the whole bundle, normalized to the same shape across backends.
16
17
  * - `transfers[k].txIdx` → index into `simulationTxs` of the tx that emitted the
@@ -12,7 +12,8 @@ import { assertNoBundlerRetention, buildSimulationTxs, executeSimulation, valida
12
12
  * - `transfers` → user-facing preview / server-side verification.
13
13
  * - `simulationTxs` + `transfers` → server-side verification before broadcast.
14
14
  * - `calls[i]` → per-tx raw backend output (`logs`, `status`, `returnData`, `gasUsed`).
15
- * Aligned 1:1 with `simulationTxs[i]`.
15
+ * Aligned 1:1 with `simulationTxs[i]`. `gasUsed` is not a safe gas limit; consumers
16
+ * deriving one must add their own headroom.
16
17
  * - `assetChanges` → net per-asset balance changes grouped by account (sender and
17
18
  * counterparties) over the whole bundle, normalized to the same shape across backends.
18
19
  * - `transfers[k].txIdx` → index into `simulationTxs` of the tx that emitted the
@@ -77,6 +78,7 @@ export async function simulate(config, params) {
77
78
  assertNoBundlerRetention({
78
79
  chainId: params.chainId,
79
80
  transfers,
81
+ assetChanges: result.assetChanges,
80
82
  logger: config.logger,
81
83
  });
82
84
  return {
@@ -201,6 +201,11 @@ export interface SimulationCall {
201
201
  readonly status: boolean;
202
202
  /** Return data from the top-level call. */
203
203
  readonly returnData: Hex;
204
- /** Gas used by this call (root frame). */
204
+ /**
205
+ * Gas consumed by this call's root frame, not a safe gas limit. This is
206
+ * post-refund consumption and does not account for EIP-150's 63/64 rule in
207
+ * nested calls, so consumers deriving a limit must add their own headroom,
208
+ * larger than headroom derived from `eth_estimateGas`.
209
+ */
205
210
  readonly gasUsed: bigint;
206
211
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@morpho-org/evm-simulation",
3
3
  "description": "EVM simulation engine for Morpho transactions.",
4
- "version": "4.1.0",
4
+ "version": "4.1.3",
5
5
  "author": "Morpho Association <contact@morpho.org>",
6
6
  "contributors": [
7
7
  "Rubilmax <rmilon@gmail.com>"
@@ -19,23 +19,23 @@
19
19
  "license": "MIT",
20
20
  "type": "module",
21
21
  "sideEffects": false,
22
- "main": "src/index.ts",
22
+ "main": "./lib/cjs/index.js",
23
23
  "files": [
24
24
  "lib"
25
25
  ],
26
26
  "dependencies": {
27
27
  "zod": "^4.4.3",
28
- "@morpho-org/blue-sdk": "^6.3.0",
29
- "@morpho-org/morpho-ts": "^2.7.0"
28
+ "@morpho-org/blue-sdk": "^6.5.0",
29
+ "@morpho-org/morpho-ts": "^2.9.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "viem": "^2.0.0"
33
33
  },
34
34
  "devDependencies": {
35
- "typescript": "^6.0.3",
36
- "viem": "^2.54.2",
37
- "vitest": "^4.1.9",
38
- "@morpho-org/test": "^2.8.1"
35
+ "typescript": "^7.0.2",
36
+ "viem": "^2.55.10",
37
+ "vitest": "^4.1.10",
38
+ "@morpho-org/test": "^2.8.4"
39
39
  },
40
40
  "scripts": {
41
41
  "prepublish": "$npm_execpath build",
@@ -45,6 +45,7 @@
45
45
  "test": "vitest --root ../.. --project evm-simulation",
46
46
  "test:fork": "dotenv -- vitest --root ../.. --project evm-simulation-fork"
47
47
  },
48
+ "types": "./lib/esm/index.d.ts",
48
49
  "exports": {
49
50
  ".": {
50
51
  "types": "./lib/esm/index.d.ts",
package/src/index.ts DELETED
@@ -1,28 +0,0 @@
1
- // Public fns (via feature-folder barrels)
2
-
3
- // Errors (for instanceof checks by consumers)
4
- export {
5
- BlacklistViolationError,
6
- ExternalServiceError,
7
- SimulationPackageError,
8
- SimulationRevertedError,
9
- SimulationValidationError,
10
- UnsupportedChainError,
11
- } from "./errors.js";
12
- export { simulate } from "./simulate/index.js";
13
- // Types
14
- export type {
15
- AccountAssetChanges,
16
- AssetChange,
17
- ChainSimulationConfig,
18
- RawLog,
19
- SimulateParams,
20
- SimulationAuthorization,
21
- SimulationCall,
22
- SimulationConfig,
23
- SimulationLogger,
24
- SimulationResult,
25
- SimulationTransaction,
26
- TenderlyRpcConfig,
27
- Transfer,
28
- } from "./types.js";