@morpho-org/evm-simulation 4.2.1 → 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 +199 -12
  5. package/lib/cjs/errors.js +190 -12
  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 -51
  55. package/lib/cjs/simulate/simulate.js +99 -82
  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 +199 -12
  68. package/lib/esm/errors.js +182 -11
  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 -51
  118. package/lib/esm/simulate/simulate.js +99 -82
  119. package/lib/esm/simulate/slippage.d.ts +18 -0
  120. package/lib/esm/simulate/slippage.js +38 -0
  121. package/lib/esm/simulate/state/contract.d.ts +19 -0
  122. package/lib/esm/simulate/state/contract.js +1 -0
  123. package/lib/esm/simulate/state/read-state.d.ts +60 -0
  124. package/lib/esm/simulate/state/read-state.js +144 -0
  125. package/lib/esm/simulate/verify-slippage.d.ts +24 -0
  126. package/lib/esm/simulate/verify-slippage.js +82 -0
  127. package/lib/esm/types.d.ts +35 -95
  128. package/package.json +7 -6
  129. package/lib/cjs/simulate/authorizations/index.d.ts +0 -2
  130. package/lib/cjs/simulate/authorizations/index.js +0 -7
  131. package/lib/cjs/simulate/authorizations/resolve.d.ts +0 -9
  132. package/lib/cjs/simulate/authorizations/resolve.js +0 -29
  133. package/lib/cjs/simulate/authorizations/validate.d.ts +0 -6
  134. package/lib/cjs/simulate/authorizations/validate.js +0 -23
  135. package/lib/cjs/simulate/backends/tenderly-rpc.d.ts +0 -23
  136. package/lib/cjs/simulate/backends/tenderly-rpc.js +0 -234
  137. package/lib/cjs/simulate/pipeline/build-simulation-txs.d.ts +0 -17
  138. package/lib/cjs/simulate/pipeline/build-simulation-txs.js +0 -50
  139. package/lib/cjs/simulate/pipeline/execute-simulation.d.ts +0 -22
  140. package/lib/cjs/simulate/pipeline/execute-simulation.js +0 -82
  141. package/lib/cjs/simulate/pipeline/index.d.ts +0 -4
  142. package/lib/cjs/simulate/pipeline/index.js +0 -11
  143. package/lib/cjs/simulate/pipeline/validate-input.d.ts +0 -10
  144. package/lib/cjs/simulate/pipeline/validate-input.js +0 -55
  145. package/lib/esm/simulate/authorizations/index.d.ts +0 -2
  146. package/lib/esm/simulate/authorizations/index.js +0 -2
  147. package/lib/esm/simulate/authorizations/resolve.d.ts +0 -9
  148. package/lib/esm/simulate/authorizations/resolve.js +0 -26
  149. package/lib/esm/simulate/authorizations/validate.d.ts +0 -6
  150. package/lib/esm/simulate/authorizations/validate.js +0 -20
  151. package/lib/esm/simulate/backends/tenderly-rpc.d.ts +0 -23
  152. package/lib/esm/simulate/backends/tenderly-rpc.js +0 -231
  153. package/lib/esm/simulate/pipeline/build-simulation-txs.d.ts +0 -17
  154. package/lib/esm/simulate/pipeline/build-simulation-txs.js +0 -47
  155. package/lib/esm/simulate/pipeline/execute-simulation.d.ts +0 -22
  156. package/lib/esm/simulate/pipeline/execute-simulation.js +0 -79
  157. package/lib/esm/simulate/pipeline/index.d.ts +0 -4
  158. package/lib/esm/simulate/pipeline/index.js +0 -4
  159. package/lib/esm/simulate/pipeline/validate-input.d.ts +0 -10
  160. package/lib/esm/simulate/pipeline/validate-input.js +0 -52
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkSlippage = checkSlippage;
4
+ const morpho_ts_1 = require("@morpho-org/morpho-ts");
5
+ const errors_js_1 = require("../errors.js");
6
+ /**
7
+ * Compare observed amounts against the caller's quote and percentage tolerance.
8
+ * Minimum outputs round up and maximum inputs round down, so rounding never
9
+ * permits a deviation greater than the requested percentage.
10
+ * @param params - Parsed quote/tolerance, matching observations, and error context.
11
+ * @returns The checked quote and tolerance, deep-frozen.
12
+ * @throws {ConsumerLimitViolationError} When adverse slippage exceeds tolerance.
13
+ * @throws {MissingVerificationEvidenceError} When a quoted amount has no observation.
14
+ * @internal
15
+ */
16
+ function checkSlippage(params) {
17
+ const { limits, observed, context, debtShares = false } = params;
18
+ const quote = {};
19
+ for (const [field, output] of [
20
+ ["assetsReceived", true],
21
+ ["sharesMinted", !debtShares],
22
+ ["assetsPaid", false],
23
+ ["sharesBurned", debtShares],
24
+ ]) {
25
+ const expected = limits.quote[field];
26
+ if (expected === undefined)
27
+ continue;
28
+ const amount = observed[field];
29
+ if (amount === undefined) {
30
+ throw new errors_js_1.MissingVerificationEvidenceError(`Missing observation for "${field}". Supply the matching asset or position.`, { context: { ...context, field } });
31
+ }
32
+ const bound = output
33
+ ? morpho_ts_1.MathLib.wMulUp(expected, morpho_ts_1.MathLib.WAD - limits.slippageTolerance)
34
+ : morpho_ts_1.MathLib.wMulDown(expected, morpho_ts_1.MathLib.WAD + limits.slippageTolerance);
35
+ if (output ? amount < bound : amount > bound) {
36
+ throw new errors_js_1.ConsumerLimitViolationError(`Slippage for "${field}" exceeds tolerance "${limits.slippageTolerance}" against quote "${expected}"; allowed bound "${bound}", got "${amount}". Adjust the transaction or tolerance.`, { context: { ...context, field, expected: bound, observed: amount } });
37
+ }
38
+ quote[field] = expected;
39
+ }
40
+ return (0, morpho_ts_1.deepFreeze)({ quote, slippageTolerance: limits.slippageTolerance });
41
+ }
@@ -0,0 +1,19 @@
1
+ import type { MarketId } from "@morpho-org/blue-sdk";
2
+ import type { Address, Hex } from "viem";
3
+ /** Minimal view call replayed before and after the user bundle. @internal */
4
+ export type StateRead = {
5
+ readonly id: string;
6
+ readonly to: Address;
7
+ readonly data: Hex;
8
+ } & ({
9
+ readonly kind: "erc20.balance";
10
+ readonly token: Address;
11
+ readonly account: Address;
12
+ } | {
13
+ readonly kind: "morpho.position";
14
+ readonly morpho: Address;
15
+ readonly marketId: MarketId;
16
+ readonly owner: Address;
17
+ });
18
+ /** Phase at which a slippage observation is read. @internal */
19
+ export type ReadPhase = "before" | "after";
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,60 @@
1
+ import { type Address, type Hex } from "viem";
2
+ import type { OperationLimit, SlippageQuote } from "../../limits.js";
3
+ import type { SimulationMode } from "../../params.js";
4
+ import type { ResolvedSlippageOperation } from "../backends/resolve-assets.js";
5
+ import type { StateRead } from "./contract.js";
6
+ /** One quoted amount's observation source. @internal */
7
+ type SlippageMeasurement = {
8
+ readonly field: keyof SlippageQuote;
9
+ } & ({
10
+ readonly type: "native";
11
+ readonly account: Address;
12
+ } | {
13
+ readonly type: "balance";
14
+ readonly readId: string;
15
+ } | {
16
+ readonly type: "position";
17
+ readonly readId: string;
18
+ readonly shares: "supplyShares" | "borrowShares";
19
+ });
20
+ /** Caller quote paired with exactly the observations it needs. @internal */
21
+ export interface SlippageOperationReads {
22
+ readonly limit: OperationLimit;
23
+ readonly measurements: readonly SlippageMeasurement[];
24
+ }
25
+ /** Scalar balance or the two share counts returned by Morpho.position. @internal */
26
+ export type StateValue = bigint | {
27
+ readonly supplyShares: bigint;
28
+ readonly borrowShares: bigint;
29
+ };
30
+ /**
31
+ * Plan only quoted balance and position reads, deduplicating contract calls.
32
+ * Native deltas use the execution's transfer traces and require no view call.
33
+ * @param params - Resolved caller operations and the transaction sender/Morpho address.
34
+ * @returns Read calls and their mapping to each quoted amount; empty without limits.
35
+ * @internal
36
+ */
37
+ export declare function planStateReads(params: {
38
+ readonly operations: readonly ResolvedSlippageOperation[];
39
+ readonly owner: Address;
40
+ readonly morpho: Address;
41
+ }): {
42
+ readonly reads: readonly StateRead[];
43
+ readonly operations: readonly SlippageOperationReads[];
44
+ };
45
+ /**
46
+ * Decode one requested balance or position without reconstructing protocol entities.
47
+ * @param read - Planned view call.
48
+ * @param data - Return data from its simulated execution.
49
+ * @param context - Request chain, mode and pinned block, attached to thrown errors.
50
+ * @returns Raw balance or supply/borrow share counts.
51
+ * @throws {MissingVerificationEvidenceError} When the selected subject returned no data.
52
+ * @throws {InvalidSimulationResponseError} When non-empty return data cannot be decoded.
53
+ * @internal
54
+ */
55
+ export declare function decodeStateRead(read: StateRead, data: Hex, context: {
56
+ readonly chainId: number;
57
+ readonly mode: SimulationMode;
58
+ readonly blockNumber: bigint;
59
+ }): StateValue;
60
+ export {};
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.planStateReads = planStateReads;
4
+ exports.decodeStateRead = decodeStateRead;
5
+ const abis_1 = require("@morpho-org/morpho-sdk/abis");
6
+ const viem_1 = require("viem");
7
+ const errors_js_1 = require("../../errors.js");
8
+ const measurement_plan_js_1 = require("../measurement-plan.js");
9
+ function planBalanceRead(params) {
10
+ const { reads, token, account } = params;
11
+ const id = `balance:${token}:${account}`.toLowerCase();
12
+ reads.set(id, {
13
+ kind: "erc20.balance",
14
+ id,
15
+ to: token,
16
+ token,
17
+ account,
18
+ data: (0, viem_1.encodeFunctionData)({
19
+ abi: viem_1.erc20Abi,
20
+ functionName: "balanceOf",
21
+ args: [account],
22
+ }),
23
+ });
24
+ return id;
25
+ }
26
+ function planPositionRead(params) {
27
+ const { reads, morpho, marketId, account } = params;
28
+ const id = `position:${marketId}:${account}`.toLowerCase();
29
+ reads.set(id, {
30
+ kind: "morpho.position",
31
+ id,
32
+ to: morpho,
33
+ morpho,
34
+ marketId,
35
+ owner: account,
36
+ data: (0, viem_1.encodeFunctionData)({
37
+ abi: abis_1.blueAbi,
38
+ functionName: "position",
39
+ args: [marketId, account],
40
+ }),
41
+ });
42
+ return id;
43
+ }
44
+ /**
45
+ * Plan only quoted balance and position reads, deduplicating contract calls.
46
+ * Native deltas use the execution's transfer traces and require no view call.
47
+ * @param params - Resolved caller operations and the transaction sender/Morpho address.
48
+ * @returns Read calls and their mapping to each quoted amount; empty without limits.
49
+ * @internal
50
+ */
51
+ function planStateReads(params) {
52
+ const reads = new Map();
53
+ const operations = params.operations.map(({ limit, assetsPaid, assetsReceived }) => {
54
+ const measurements = [];
55
+ const account = limit.account ?? params.owner;
56
+ const plan = (0, measurement_plan_js_1.operationMeasurementPlan)(limit);
57
+ for (const field of [
58
+ "assetsReceived",
59
+ "assetsPaid",
60
+ "sharesMinted",
61
+ "sharesBurned",
62
+ ]) {
63
+ if (limit.quote[field] === undefined)
64
+ continue;
65
+ const assetField = field === "assetsReceived" || field === "assetsPaid";
66
+ if (!assetField) {
67
+ const source = field === "sharesMinted" ? plan.sharesMinted : plan.sharesBurned;
68
+ if (source === undefined)
69
+ continue;
70
+ if (source.type === "position") {
71
+ measurements.push({
72
+ field,
73
+ type: "position",
74
+ readId: planPositionRead({
75
+ reads,
76
+ morpho: params.morpho,
77
+ marketId: source.marketId,
78
+ account,
79
+ }),
80
+ shares: source.shares,
81
+ });
82
+ continue;
83
+ }
84
+ measurements.push({
85
+ field,
86
+ type: "balance",
87
+ readId: planBalanceRead({
88
+ reads,
89
+ token: source.token,
90
+ account,
91
+ }),
92
+ });
93
+ continue;
94
+ }
95
+ const token = field === "assetsReceived" ? assetsReceived : assetsPaid;
96
+ const holder = field === "assetsReceived"
97
+ ? (limit.receiver ?? params.owner)
98
+ : params.owner;
99
+ if (token === undefined) {
100
+ continue;
101
+ }
102
+ if ((0, viem_1.isAddressEqual)(token, viem_1.ethAddress)) {
103
+ measurements.push({ field, type: "native", account: holder });
104
+ continue;
105
+ }
106
+ measurements.push({
107
+ field,
108
+ type: "balance",
109
+ readId: planBalanceRead({ reads, token, account: holder }),
110
+ });
111
+ }
112
+ return { limit, measurements };
113
+ });
114
+ return { reads: [...reads.values()], operations };
115
+ }
116
+ /**
117
+ * Decode one requested balance or position without reconstructing protocol entities.
118
+ * @param read - Planned view call.
119
+ * @param data - Return data from its simulated execution.
120
+ * @param context - Request chain, mode and pinned block, attached to thrown errors.
121
+ * @returns Raw balance or supply/borrow share counts.
122
+ * @throws {MissingVerificationEvidenceError} When the selected subject returned no data.
123
+ * @throws {InvalidSimulationResponseError} When non-empty return data cannot be decoded.
124
+ * @internal
125
+ */
126
+ // biome-ignore lint/complexity/useMaxParams: the call mirrors the failure context it attaches
127
+ function decodeStateRead(read, data, context) {
128
+ if (data === "0x") {
129
+ throw new errors_js_1.MissingVerificationEvidenceError(`Cannot observe slippage read "${read.id}": the selected subject returned no data (likely not a contract).`, { context: { stage: "verification", ...context, field: read.id } });
130
+ }
131
+ try {
132
+ if (read.kind === "erc20.balance")
133
+ return (0, viem_1.decodeFunctionResult)({
134
+ abi: viem_1.erc20Abi,
135
+ functionName: "balanceOf",
136
+ data,
137
+ });
138
+ const [supplyShares, borrowShares] = (0, viem_1.decodeFunctionResult)({
139
+ abi: abis_1.blueAbi,
140
+ functionName: "position",
141
+ data,
142
+ });
143
+ return { supplyShares, borrowShares };
144
+ }
145
+ catch (cause) {
146
+ throw new errors_js_1.InvalidSimulationResponseError(`Cannot decode slippage observation "${read.id}". Check the selected contract and RPC response.`, { cause });
147
+ }
148
+ }
@@ -0,0 +1,24 @@
1
+ import { type Address } from "viem";
2
+ import type { SimulatedOperation } from "../result.js";
3
+ import type { Transfer } from "../types.js";
4
+ import type { CheckContext } from "./context.js";
5
+ import type { SlippageOperationReads, StateValue } from "./state/read-state.js";
6
+ /**
7
+ * Measure only caller-quoted amounts and compare their percentage slippage.
8
+ * @param params - Planned observations, decoded before/after values, and user transfer traces.
9
+ * @returns Each caller subject and its checked quote/tolerance.
10
+ * @throws {ConsumerLimitViolationError} When adverse slippage exceeds tolerance.
11
+ * @throws {MissingVerificationEvidenceError} When a requested observation is absent.
12
+ * @internal
13
+ */
14
+ export declare function verifySlippage(params: {
15
+ readonly ctx: CheckContext;
16
+ readonly operations: readonly SlippageOperationReads[];
17
+ readonly before: ReadonlyMap<string, StateValue>;
18
+ readonly after: ReadonlyMap<string, StateValue>;
19
+ readonly transfers: readonly Transfer[];
20
+ readonly requestTransactions: readonly {
21
+ readonly from: Address;
22
+ readonly value?: bigint;
23
+ }[];
24
+ }): readonly SimulatedOperation[];
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifySlippage = verifySlippage;
4
+ const blue_sdk_1 = require("@morpho-org/blue-sdk");
5
+ const viem_1 = require("viem");
6
+ const errors_js_1 = require("../errors.js");
7
+ const measurement_plan_js_1 = require("./measurement-plan.js");
8
+ const slippage_js_1 = require("./slippage.js");
9
+ /**
10
+ * Measure only caller-quoted amounts and compare their percentage slippage.
11
+ * @param params - Planned observations, decoded before/after values, and user transfer traces.
12
+ * @returns Each caller subject and its checked quote/tolerance.
13
+ * @throws {ConsumerLimitViolationError} When adverse slippage exceeds tolerance.
14
+ * @throws {MissingVerificationEvidenceError} When a requested observation is absent.
15
+ * @internal
16
+ */
17
+ function verifySlippage(params) {
18
+ const { ctx, before, after, transfers, requestTransactions } = params;
19
+ return params.operations.map(({ limit, measurements }) => {
20
+ const subject = (0, measurement_plan_js_1.operationMeasurementPlan)(limit).subject;
21
+ const context = {
22
+ stage: "verification",
23
+ chainId: ctx.chainId,
24
+ mode: ctx.mode,
25
+ blockNumber: ctx.block.stateBlockNumber,
26
+ ...subject,
27
+ };
28
+ const observed = {};
29
+ for (const measurement of measurements) {
30
+ let delta;
31
+ if (measurement.type === "native") {
32
+ const tracedOutgoing = transfers.reduce((total, transfer) => (0, viem_1.isAddressEqual)(transfer.token, viem_1.ethAddress) &&
33
+ (0, viem_1.isAddressEqual)(transfer.from, measurement.account)
34
+ ? total + transfer.amount
35
+ : total, 0n);
36
+ const sentValue = requestTransactions.reduce((total, transaction) => (0, viem_1.isAddressEqual)(transaction.from, measurement.account)
37
+ ? total + (transaction.value ?? 0n)
38
+ : total, 0n);
39
+ if (tracedOutgoing < sentValue) {
40
+ throw new errors_js_1.MissingVerificationEvidenceError(`Missing native transfer evidence for "${measurement.field}" from a transaction with value.`, { context: { ...context, field: measurement.field } });
41
+ }
42
+ delta = 0n;
43
+ for (const transfer of transfers) {
44
+ if (!(0, viem_1.isAddressEqual)(transfer.token, viem_1.ethAddress))
45
+ continue;
46
+ if ((0, viem_1.isAddressEqual)(transfer.to, measurement.account))
47
+ delta += transfer.amount;
48
+ if ((0, viem_1.isAddressEqual)(transfer.from, measurement.account))
49
+ delta -= transfer.amount;
50
+ }
51
+ }
52
+ else {
53
+ const b = before.get(measurement.readId);
54
+ const a = after.get(measurement.readId);
55
+ if (measurement.type === "balance" &&
56
+ typeof b === "bigint" &&
57
+ typeof a === "bigint")
58
+ delta = a - b;
59
+ else if (measurement.type === "position" &&
60
+ typeof b === "object" &&
61
+ typeof a === "object")
62
+ delta = a[measurement.shares] - b[measurement.shares];
63
+ else
64
+ throw new errors_js_1.MissingVerificationEvidenceError(`Missing observation for "${measurement.field}". Check the selected subject.`, { context: { ...context, field: measurement.field } });
65
+ }
66
+ observed[measurement.field] =
67
+ measurement.field === "assetsPaid" ||
68
+ measurement.field === "sharesBurned"
69
+ ? blue_sdk_1.MathLib.max(0n, -delta)
70
+ : delta;
71
+ }
72
+ return {
73
+ ...subject,
74
+ account: limit.account ?? ctx.owner,
75
+ receiver: limit.receiver ?? ctx.owner,
76
+ checkedLimits: (0, slippage_js_1.checkSlippage)({
77
+ limits: limit,
78
+ observed,
79
+ context,
80
+ debtShares: measurements.some((measurement) => measurement.type === "position" &&
81
+ measurement.shares === "borrowShares"),
82
+ }),
83
+ };
84
+ });
85
+ }
@@ -1,41 +1,21 @@
1
- import type { Address, BlockTag, Hex } from "viem";
2
- /**
3
- * Credentials for the Tenderly Node Web3 Gateway. The `rpcUrl` is the
4
- * chain-specific gateway URL with the access key embedded in the path,
5
- * e.g. `https://mainnet.gateway.tenderly.co/{ACCESS_KEY}`.
6
- */
7
- export interface TenderlyRpcConfig {
8
- rpcUrl: string;
1
+ import type { Address, Hex } from "viem";
2
+ /** Per-chain endpoint for the sole supported simulation method, `eth_simulateV1`. */
3
+ export interface ChainSimulationConfig {
4
+ /** JSON-RPC URL supporting `eth_simulateV1`. Required for every configured chain. */
5
+ readonly simulateV1Url: string;
9
6
  }
10
- /**
11
- * Per-chain backend capabilities. Exactly one configuration shape per chain;
12
- * the discriminated union enforces that **at least one** of `tenderlyRpc`
13
- * (primary) or `simulateV1Url` (fallback) is supplied.
14
- */
15
- export type ChainSimulationConfig = {
16
- /** Tenderly RPC config — `tenderly_simulateTransaction` / `tenderly_simulateBundle`. */
17
- tenderlyRpc: TenderlyRpcConfig;
18
- /** JSON-RPC URL supporting `eth_simulateV1`. Optional when Tenderly is set. */
19
- simulateV1Url?: string;
20
- } | {
21
- tenderlyRpc?: TenderlyRpcConfig;
22
- simulateV1Url: string;
23
- };
24
7
  /**
25
8
  * Top-level configuration for `simulate`.
26
9
  *
27
- * Every chain entry must declare at least one backend (Tenderly RPC primary,
28
- * `eth_simulateV1` fallback, or both). Calling `simulate` for a `chainId`
29
- * missing from `chains` throws `UnsupportedChainError`.
10
+ * Every chain entry must supply an `eth_simulateV1` endpoint. An absent chain
11
+ * or missing endpoint throws `UnsupportedChainError`; execution never selects
12
+ * another provider after a failure.
30
13
  */
31
14
  export interface SimulationConfig {
32
- /** Per-chain simulation capabilities. */
15
+ /** Per-chain simulation endpoints. */
33
16
  chains: Map<number, ChainSimulationConfig>;
34
17
  logger?: SimulationLogger;
35
- /**
36
- * Overall timeout budget in ms (default 5000). Tenderly gets ~60% of budget.
37
- * On timeout/failure, fallback gets remaining time (deadline - now).
38
- */
18
+ /** Overall execution timeout budget in ms (default 5000). */
39
19
  timeoutMs?: number;
40
20
  }
41
21
  /**
@@ -43,31 +23,15 @@ export interface SimulationConfig {
43
23
  * in a bundle — the orchestrator rejects mixed senders with `SimulationValidationError`.
44
24
  */
45
25
  export interface SimulationTransaction {
46
- from: Address;
47
- to: Address;
48
- data: Hex;
49
- value?: bigint;
26
+ readonly from: Address;
27
+ readonly to: Address;
28
+ readonly data: Hex;
29
+ readonly value?: bigint;
50
30
  }
51
- /**
52
- * How the caller expresses a token authorization that must be in place before the
53
- * main transactions run. The package decides HOW to simulate each one:
54
- * - "approval" → prepend tx as-is
55
- * - "signature" → today: encode approve(spender, amount); future: ecrecover override?
56
- */
57
- export type SimulationAuthorization = {
58
- type: "approval";
59
- transaction: SimulationTransaction;
60
- } | {
61
- type: "signature";
62
- token: Address;
63
- spender: Address;
64
- /** Defaults to maxUint256. Caller can set explicit amount for tokens that don't support max approval. */
65
- amount?: bigint;
66
- };
67
31
  /**
68
32
  * Net balance change for a single asset (one token) within an account's entry.
69
33
  * Native ETH uses viem's `ethAddress` sentinel as `token`. `symbol`/`decimals`
70
- * are best-effort and may be absent, notably on the `eth_simulateV1` fallback.
34
+ * are optional metadata; the log-derived `eth_simulateV1` output omits them.
71
35
  */
72
36
  export interface AssetChange {
73
37
  readonly token: Address;
@@ -80,12 +44,11 @@ export interface AssetChange {
80
44
  * Net per-token balance changes for one account over the whole bundle. Returned
81
45
  * for every account that nets a non-zero change, the sender and counterparties
82
46
  * alike (the zero address is kept for mints/burns). Accounts and their `changes`
83
- * are sorted by address for deterministic, cross-backend output.
47
+ * are sorted by address for deterministic output.
84
48
  *
85
- * Both backends report the full net native-ETH delta, including ETH moved via
86
- * internal calls (e.g. a `WETH.withdraw` refund): Tenderly derives it from its
87
- * trace, and the `eth_simulateV1` fallback runs with `traceTransfers` so the
88
- * node synthesizes native moves as transfer logs under viem's `ethAddress`.
49
+ * The backend reports the full net native-ETH delta, including ETH moved via
50
+ * internal calls (e.g. a `WETH.withdraw` refund). `traceTransfers` makes the
51
+ * node synthesize native moves as transfer logs under viem's `ethAddress`.
89
52
  */
90
53
  export interface AccountAssetChanges {
91
54
  readonly account: Address;
@@ -101,29 +64,27 @@ export interface Transfer {
101
64
  readonly to: Address;
102
65
  readonly amount: bigint;
103
66
  /**
104
- * Index into `SimulationResult.simulationTxs` of the transaction that
105
- * emitted the underlying log. For bundles with prepended authorization
106
- * approvals, indices `[0, simulationTxs.length - params.transactions.length)`
107
- * are authorization txs; the remainder are caller-supplied.
67
+ * Index into `SimulationResult.simulationTxs` of the user transaction that
68
+ * emitted the underlying log. `txIdx` only ever indexes caller-supplied
69
+ * transactions.
108
70
  */
109
71
  readonly txIdx: number;
110
72
  }
111
73
  /**
112
74
  * Happy-path return of `simulate`. All failures throw typed errors.
113
75
  *
114
- * - `simulationTxs` is the full resolved transaction list (including
115
- * prepended authorization txs).
76
+ * - `simulationTxs` are exactly the caller's ordered transactions, normalized
77
+ * (checksummed `from`/`to`, `value` defaulted to `0n`).
116
78
  * - `calls[i]` corresponds 1:1 with `simulationTxs[i]` — read raw logs,
117
79
  * status, returnData/gasUsed.
118
80
  * - `assetChanges` is the net per-asset balance change over the whole bundle,
119
- * grouped by account (sender and counterparties), normalized to the same
120
- * shape across backends — see `AccountAssetChanges`.
81
+ * grouped by account (sender and counterparties) — see `AccountAssetChanges`.
121
82
  * - `transfers[k].txIdx` indexes into `simulationTxs` to attribute each
122
- * transfer to its emitting transaction.
83
+ * transfer to its emitting user transaction.
123
84
  */
124
85
  export interface SimulationResult {
125
- /** The full resolved transaction list (including prepended authorization txs). */
126
- readonly simulationTxs: readonly SimulationTransaction[];
86
+ /** Exactly the caller's ordered transactions, normalized. */
87
+ readonly simulationTxs: readonly Readonly<SimulationTransaction>[];
127
88
  /**
128
89
  * Per-transaction normalized output. `calls[i]` corresponds 1:1 with
129
90
  * `simulationTxs[i]`. Use this to read raw logs, status, return data, gas used.
@@ -140,29 +101,9 @@ export interface SimulationLogger {
140
101
  warn(message: string, data?: Record<string, unknown>): void;
141
102
  error(message: string, data?: Record<string, unknown>): void;
142
103
  }
143
- /**
144
- * Input to `simulate`. Pin a `blockNumber` for deterministic / historical
145
- * simulation; omit to simulate against `latest`.
146
- */
147
- export interface SimulateParams {
148
- chainId: number;
149
- transactions: SimulationTransaction[];
150
- authorizations?: SimulationAuthorization[];
151
- blockNumber?: bigint | BlockTag;
152
- }
153
- /**
154
- * Internal raw result from a simulation adapter before normalization.
155
- * `calls[i]` corresponds 1:1 with the i-th transaction passed to the
156
- * backend; `assetChanges` is the bundle-level aggregate grouped by account.
157
- */
158
- export interface RawSimulationResult {
159
- calls: RawCall[];
160
- assetChanges: AccountAssetChanges[];
161
- }
162
104
  /**
163
105
  * Normalized EVM log emitted by a single simulated call. The shape is the
164
- * common subset both backends (`eth_simulateV1` via viem and Tenderly RPC)
165
- * produce after schema validation. Returned indirectly via
106
+ * log shape produced by `eth_simulateV1` via viem. Returned indirectly via
166
107
  * `SimulationCall.logs` and consumed by the SDK's transfer parser.
167
108
  */
168
109
  export interface RawLog {
@@ -171,16 +112,15 @@ export interface RawLog {
171
112
  readonly data: Hex;
172
113
  }
173
114
  /**
174
- * Internal mirror of `SimulationCall`, mutable during construction by the
175
- * simulation backends.
115
+ * Input-side mirror of `SimulationCall` accepted by the transfer parser.
176
116
  *
177
117
  * @internal
178
118
  */
179
119
  export interface RawCall {
180
- logs: RawLog[];
181
- status: boolean;
182
- returnData: Hex;
183
- gasUsed: bigint;
120
+ readonly logs: readonly RawLog[];
121
+ readonly status: boolean;
122
+ readonly returnData: Hex;
123
+ readonly gasUsed: bigint;
184
124
  }
185
125
  /**
186
126
  * Per-transaction normalized output from the simulation backend.
@@ -196,7 +136,7 @@ export interface SimulationCall {
196
136
  * True iff the call succeeded. The bundle as a whole reverts via
197
137
  * `SimulationRevertedError` before the result is returned, so on a
198
138
  * successful `simulate()` every entry is `true` today. Field kept for
199
- * forward-compatibility with backends that may surface per-call status.
139
+ * per-call inspection alongside logs and return data.
200
140
  */
201
141
  readonly status: boolean;
202
142
  /** Return data from the top-level call. */