@morpho-org/evm-simulation 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,11 +9,11 @@ interface AssertNoBundlerRetentionParams {
9
9
  *
10
10
  * Uses net flow (inbound minus outbound) per (bundler address, token) pair.
11
11
  * Bundler3 legitimately receives tokens as an intermediary (user → bundler3 →
12
- * vault), so gross inbound would fire false positives. We only flag when
13
- * `|net| > DUST_THRESHOLD` — catching both **retention** (tokens stuck in
14
- * bundler, positive net) and **drain** (bundler sending more than it received
15
- * in this bundle, negative net — which implies a pre-existing or state-override
16
- * balance is being drawn down, a red flag for pre-broadcast integrity).
12
+ * 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 means the simulated bundle sweeps a pre-existing or state-overridden
15
+ * bundler balance. That is useful telemetry, but not current-bundle retention,
16
+ * so it is warned instead of raising a blacklist violation.
17
17
  */
18
18
  export declare function assertNoBundlerRetention(params: AssertNoBundlerRetentionParams): void;
19
19
  export {};
@@ -47,11 +47,11 @@ function getBundlerAddresses(chainId, logger) {
47
47
  *
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
- * vault), so gross inbound would fire false positives. We only flag when
51
- * `|net| > DUST_THRESHOLD` — catching both **retention** (tokens stuck in
52
- * bundler, positive net) and **drain** (bundler sending more than it received
53
- * in this bundle, negative net — which implies a pre-existing or state-override
54
- * balance is being drawn down, a red flag for pre-broadcast integrity).
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
52
+ * net flow means the simulated bundle sweeps a pre-existing or state-overridden
53
+ * bundler balance. That is useful telemetry, but not current-bundle retention,
54
+ * so it is warned instead of raising a blacklist violation.
55
55
  */
56
56
  function assertNoBundlerRetention(params) {
57
57
  const { chainId, transfers, logger } = params;
@@ -77,15 +77,23 @@ function assertNoBundlerRetention(params) {
77
77
  if (bundlerAddresses.has(t.from))
78
78
  recordFlow(t.from, t.token, -t.amount);
79
79
  }
80
- const flagged = [...flow.values()].filter((e) => absBigInt(e.netChange) > DUST_THRESHOLD);
81
- if (flagged.length > 0) {
82
- throw new errors_js_1.BlacklistViolationError("Simulation detected asset transfers retained or drained by restricted bundler contracts", flagged.map((e) => ({
80
+ const entries = [...flow.values()];
81
+ const drained = entries.filter((e) => e.netChange < -DUST_THRESHOLD);
82
+ if (drained.length > 0) {
83
+ logger?.warn("Simulation detected pre-existing bundler balance being swept", {
84
+ changes: drained.map((e) => ({
85
+ address: e.address,
86
+ token: e.token,
87
+ netSwept: (-e.netChange).toString(),
88
+ })),
89
+ });
90
+ }
91
+ const retained = entries.filter((e) => e.netChange > DUST_THRESHOLD);
92
+ if (retained.length > 0) {
93
+ throw new errors_js_1.BlacklistViolationError("Simulation detected asset transfers retained by restricted bundler contracts", retained.map((e) => ({
83
94
  address: e.address,
84
95
  token: e.token,
85
96
  netRetained: e.netChange.toString(),
86
97
  })));
87
98
  }
88
99
  }
89
- function absBigInt(x) {
90
- return x < 0n ? -x : x;
91
- }
@@ -29,8 +29,8 @@ import type { SimulateParams, SimulationConfig, SimulationResult } from "../type
29
29
  * empty transactions, malformed authorizations).
30
30
  * @throws {UnsupportedChainError} when the chain is not configured for any backend.
31
31
  * @throws {SimulationRevertedError} when the bundle reverts on either backend.
32
- * @throws {BlacklistViolationError} when the simulation leaves value retained by or
33
- * drained from a `bundler3` address beyond the dust threshold.
32
+ * @throws {BlacklistViolationError} when the simulation leaves value retained by
33
+ * a `bundler3` address beyond the dust threshold.
34
34
  * @throws {ExternalServiceError} (a) when both backends are unavailable within the
35
35
  * timeout budget, or (b) when a backend returns a `calls` array whose length does
36
36
  * not match the resolved `simulationTxs` — refusing to map transfers with mismatched
@@ -34,8 +34,8 @@ const index_js_2 = require("./pipeline/index.js");
34
34
  * empty transactions, malformed authorizations).
35
35
  * @throws {UnsupportedChainError} when the chain is not configured for any backend.
36
36
  * @throws {SimulationRevertedError} when the bundle reverts on either backend.
37
- * @throws {BlacklistViolationError} when the simulation leaves value retained by or
38
- * drained from a `bundler3` address beyond the dust threshold.
37
+ * @throws {BlacklistViolationError} when the simulation leaves value retained by
38
+ * a `bundler3` address beyond the dust threshold.
39
39
  * @throws {ExternalServiceError} (a) when both backends are unavailable within the
40
40
  * timeout budget, or (b) when a backend returns a `calls` array whose length does
41
41
  * not match the resolved `simulationTxs` — refusing to map transfers with mismatched
@@ -9,11 +9,11 @@ interface AssertNoBundlerRetentionParams {
9
9
  *
10
10
  * Uses net flow (inbound minus outbound) per (bundler address, token) pair.
11
11
  * Bundler3 legitimately receives tokens as an intermediary (user → bundler3 →
12
- * vault), so gross inbound would fire false positives. We only flag when
13
- * `|net| > DUST_THRESHOLD` — catching both **retention** (tokens stuck in
14
- * bundler, positive net) and **drain** (bundler sending more than it received
15
- * in this bundle, negative net — which implies a pre-existing or state-override
16
- * balance is being drawn down, a red flag for pre-broadcast integrity).
12
+ * 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 means the simulated bundle sweeps a pre-existing or state-overridden
15
+ * bundler balance. That is useful telemetry, but not current-bundle retention,
16
+ * so it is warned instead of raising a blacklist violation.
17
17
  */
18
18
  export declare function assertNoBundlerRetention(params: AssertNoBundlerRetentionParams): void;
19
19
  export {};
@@ -44,11 +44,11 @@ function getBundlerAddresses(chainId, logger) {
44
44
  *
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
- * vault), so gross inbound would fire false positives. We only flag when
48
- * `|net| > DUST_THRESHOLD` — catching both **retention** (tokens stuck in
49
- * bundler, positive net) and **drain** (bundler sending more than it received
50
- * in this bundle, negative net — which implies a pre-existing or state-override
51
- * balance is being drawn down, a red flag for pre-broadcast integrity).
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
49
+ * net flow means the simulated bundle sweeps a pre-existing or state-overridden
50
+ * bundler balance. That is useful telemetry, but not current-bundle retention,
51
+ * so it is warned instead of raising a blacklist violation.
52
52
  */
53
53
  export function assertNoBundlerRetention(params) {
54
54
  const { chainId, transfers, logger } = params;
@@ -74,15 +74,23 @@ export function assertNoBundlerRetention(params) {
74
74
  if (bundlerAddresses.has(t.from))
75
75
  recordFlow(t.from, t.token, -t.amount);
76
76
  }
77
- const flagged = [...flow.values()].filter((e) => absBigInt(e.netChange) > DUST_THRESHOLD);
78
- if (flagged.length > 0) {
79
- throw new BlacklistViolationError("Simulation detected asset transfers retained or drained by restricted bundler contracts", flagged.map((e) => ({
77
+ const entries = [...flow.values()];
78
+ const drained = entries.filter((e) => e.netChange < -DUST_THRESHOLD);
79
+ if (drained.length > 0) {
80
+ logger?.warn("Simulation detected pre-existing bundler balance being swept", {
81
+ changes: drained.map((e) => ({
82
+ address: e.address,
83
+ token: e.token,
84
+ netSwept: (-e.netChange).toString(),
85
+ })),
86
+ });
87
+ }
88
+ const retained = entries.filter((e) => e.netChange > DUST_THRESHOLD);
89
+ if (retained.length > 0) {
90
+ throw new BlacklistViolationError("Simulation detected asset transfers retained by restricted bundler contracts", retained.map((e) => ({
80
91
  address: e.address,
81
92
  token: e.token,
82
93
  netRetained: e.netChange.toString(),
83
94
  })));
84
95
  }
85
96
  }
86
- function absBigInt(x) {
87
- return x < 0n ? -x : x;
88
- }
@@ -29,8 +29,8 @@ import type { SimulateParams, SimulationConfig, SimulationResult } from "../type
29
29
  * empty transactions, malformed authorizations).
30
30
  * @throws {UnsupportedChainError} when the chain is not configured for any backend.
31
31
  * @throws {SimulationRevertedError} when the bundle reverts on either backend.
32
- * @throws {BlacklistViolationError} when the simulation leaves value retained by or
33
- * drained from a `bundler3` address beyond the dust threshold.
32
+ * @throws {BlacklistViolationError} when the simulation leaves value retained by
33
+ * a `bundler3` address beyond the dust threshold.
34
34
  * @throws {ExternalServiceError} (a) when both backends are unavailable within the
35
35
  * timeout budget, or (b) when a backend returns a `calls` array whose length does
36
36
  * not match the resolved `simulationTxs` — refusing to map transfers with mismatched
@@ -31,8 +31,8 @@ import { assertNoBundlerRetention, buildSimulationTxs, executeSimulation, valida
31
31
  * empty transactions, malformed authorizations).
32
32
  * @throws {UnsupportedChainError} when the chain is not configured for any backend.
33
33
  * @throws {SimulationRevertedError} when the bundle reverts on either backend.
34
- * @throws {BlacklistViolationError} when the simulation leaves value retained by or
35
- * drained from a `bundler3` address beyond the dust threshold.
34
+ * @throws {BlacklistViolationError} when the simulation leaves value retained by
35
+ * a `bundler3` address beyond the dust threshold.
36
36
  * @throws {ExternalServiceError} (a) when both backends are unavailable within the
37
37
  * timeout budget, or (b) when a backend returns a `calls` array whose length does
38
38
  * not match the resolved `simulationTxs` — refusing to map transfers with mismatched
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.0.0",
4
+ "version": "4.0.1",
5
5
  "author": "Morpho Association <contact@morpho.org>",
6
6
  "contributors": [
7
7
  "Rubilmax <rmilon@gmail.com>"
@@ -25,16 +25,16 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "zod": "^4.4.3",
28
- "@morpho-org/blue-sdk": "^6.1.0",
29
- "@morpho-org/morpho-ts": "^2.6.0"
28
+ "@morpho-org/morpho-ts": "^2.6.0",
29
+ "@morpho-org/blue-sdk": "^6.2.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "viem": "^2.0.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "typescript": "^6.0.3",
36
- "viem": "^2.50.4",
37
- "vitest": "^4.1.7",
36
+ "viem": "2.52.2",
37
+ "vitest": "^4.1.8",
38
38
  "@morpho-org/test": "^2.8.1"
39
39
  },
40
40
  "scripts": {