@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
package/README.md CHANGED
@@ -22,13 +22,7 @@ import {
22
22
 
23
23
  const config: SimulationConfig = {
24
24
  chains: new Map([
25
- [
26
- 1,
27
- {
28
- tenderlyRpc: { rpcUrl: process.env.TENDERLY_RPC_URL! },
29
- simulateV1Url: process.env.MAINNET_RPC_URL,
30
- },
31
- ],
25
+ [1, { simulateV1Url: process.env.MAINNET_RPC_URL! }],
32
26
  ]),
33
27
  timeoutMs: 5000,
34
28
  };
@@ -38,35 +32,133 @@ try {
38
32
  config,
39
33
  {
40
34
  chainId: 1,
35
+ // mode: "final" (default) executes the signed calldata against actual
36
+ // permissions; mode: "preview" accepts typed authorization descriptors.
41
37
  transactions: [{ from: user, to: vault, data: encodedDeposit }],
42
- authorizations: [{ type: "signature", token: usdc, spender: vault }],
38
+ // limits: { operations: [{ type: "vaultV1Deposit", vault, quote: { sharesMinted: 1000n }, slippageTolerance: 10_000000000000000n }] },
43
39
  },
44
40
  );
45
41
  } catch (err) {
46
42
  if (err instanceof SimulationRevertedError) {
47
- // show err.reason to the user
43
+ // show err.reason to the user; err.reasonCode is currently always "UNKNOWN_REVERT"
48
44
  }
49
45
  throw err;
50
46
  }
51
47
  ```
52
48
 
53
- Each chain entry must declare at least one backend — `tenderlyRpc` (primary), `simulateV1Url` (fallback), or both. The type system enforces this.
49
+ Every chain entry requires `simulateV1Url`, pointing to a JSON-RPC endpoint that supports `eth_simulateV1`. Execution uses the full `timeoutMs` budget (default 5000 ms), with no retries or provider fallback. RPC failures, timeouts and reverts throw typed errors. The optional logger still reports parsing and retention warnings.
50
+
51
+ Native-ETH movements are observed through `traceTransfers` logs on the simulated calls — no `stateOverrides` or helper contracts are injected.
52
+
53
+ This is the unreleased v5 integration stack. See the [v4 → v5 migration guide](../../docs/migrations/evm-simulation-v4-to-v5.md) for the backend cutover and remaining release gates.
54
54
 
55
55
  ### API surface
56
56
 
57
57
  All symbols below are re-exported from the package root.
58
58
 
59
59
  - `simulate(config, params)` — run a bundle through the simulation pipeline.
60
- - Config types: `SimulationConfig`, `TenderlyRpcConfig`, `ChainSimulationConfig`, `SimulationLogger`.
61
- - Input types: `SimulateParams`, `SimulationTransaction`, `SimulationAuthorization`.
60
+ - Config types: `SimulationConfig`, `ChainSimulationConfig`, `SimulationLogger`.
61
+ - Input types: `SimulateParams` (v5 shape: `mode`, `SimulationAuthorization` requests, `SimulationLimits`), `SimulationMode`, `SimulationTransaction`.
62
+ - `toSimulationAuthorizations({ chainId, mode, blockNumber, owner, requirements })` — map morpho-sdk `ActionRequirement[]` onto `SimulationAuthorization[]` straight from `action.args` — nothing is decoded or validated; validation happens in `simulate()`'s request parser.
63
+ - Authorizations and limits: `SimulationAuthorization` and its members (`Erc20ApprovalAuthorization`, `Erc2612PermitAuthorization`, `Permit2TransferAuthorization`, `BlueAuthorization`, `BlueAuthorizationSignature`) with their EIP-712 payloads (`Eip712Domain`, `Eip712Field`, `Erc2612PermitTypedData`, `Permit2TransferTypedData`, `BlueAuthorizationTypedData`); `SimulationLimits`, `OperationLimit`, and the shared `SlippageLimits` and `SlippageQuote`.
64
+ - Slippage-check result types: `VerifiedSimulationResult`, `SimulationVerification`, `SimulatedOperation`, `AuthorizationPreparation`.
62
65
  - Result types: `SimulationResult`, `SimulationCall`, `Transfer`, `AccountAssetChanges`, `AssetChange`, `RawLog`.
63
- - Errors: `SimulationPackageError` (abstract base — `instanceof` it to catch any package error), `SimulationRevertedError`, `BlacklistViolationError`, `ExternalServiceError`, `SimulationValidationError`, `UnsupportedChainError`.
66
+ - Errors: `SimulationPackageError` (abstract base — `instanceof` it to catch any package error), `SimulationRevertedError`, `BlacklistViolationError`, `ExternalServiceError`, `SimulationValidationError`, `UnsupportedChainError`, and the verification errors `UnsupportedOperationError`, `InvalidSimulationResponseError`, `MissingVerificationEvidenceError`, `AuthorizationRequestMismatchError`, `ConsumerLimitViolationError`, `UnexpectedSimulationError`.
67
+ - Error helpers: `SIMULATION_ERROR_CODES` / `SimulationErrorCode` (every `error.code`), `SimulationErrorContext` (frozen `error.context`; union of the per-stage `SimulationValidationContext`, `SimulationTransportContext`, `SimulationPreparationContext`, `SimulationExecutionContext`, `SimulationVerificationContext`), `SimulationStage`, `SimulationExecutionReason` (`SimulationRevertedError.reasonCode`), `isSimulationPackageError` (structural guard narrowing to `SimulationPackageError`), `RetainedAsset`.
68
+ - Verification vocabulary: `SIMULATION_MODES` / `SimulationMode`, `OPERATION_TYPES` / `OperationType`, `BLUE_MARKET_OPERATION_TYPES` / `BlueMarketOperationType`, `VAULT_OPERATION_TYPES` / `VaultOperationType`, `SimulationOperationSubject` and its members `BlueMarketOperationSubject`, `BlueRefinanceSubject`, `VaultOperationSubject`, `VaultV1MigrateToV2Subject` (operation groups and the operation-keyed subject union that key limit-bound verification `SimulationErrorContext`s).
69
+ ### Optional limits
70
+
71
+ The caller chooses the action, subject, quote, and percentage tolerance. Each
72
+ entry supplies `quote` with at least one raw-unit amount: `assetsReceived`,
73
+ `sharesMinted`, `assetsPaid`, or `sharesBurned`, plus `slippageTolerance` as a
74
+ WAD-scaled fraction (`10_000000000000000n` = 1%, `1e18` = 100%). Tolerance must
75
+ be between 0 and 100% inclusive; it has no default. Omit `limits` to skip checks.
76
+ Unquoted amounts stay unchecked. The simulator never decodes calldata or fetches
77
+ a quote to infer constraints.
78
+ Unknown input keys are rejected as `SimulationValidationError` rather than
79
+ silently ignored.
80
+
81
+ Non-empty `limits.operations` and preview authorizations require a Morpho Blue
82
+ address registered in blue-sdk's `getChainAddresses`; otherwise
83
+ `UnsupportedChainError` is thrown.
84
+
85
+ Received assets and minted supply shares must be at least
86
+ `ceil(quote * (1 - tolerance))`; paid assets and burned supply shares must be at
87
+ most `floor(quote * (1 + tolerance))`. For debt shares, minting is capped at
88
+ `floor(quote * (1 + tolerance))` and burning must be at least
89
+ `ceil(quote * (1 - tolerance))`. Equality and favorable movement pass.
64
90
 
65
- ### Deeper docs
91
+ ```ts
92
+ const result = await simulate(config, {
93
+ chainId: 1,
94
+ transactions,
95
+ limits: {
96
+ operations: [{
97
+ type: "vaultV1Deposit",
98
+ vault,
99
+ account: user, // Share/position owner; defaults to transaction sender.
100
+ receiver: user, // Asset recipient; defaults to transaction sender.
101
+ quote: { sharesMinted: 1_000n, assetsPaid: 1_000n },
102
+ slippageTolerance: 10_000000000000000n, // 1%
103
+ }],
104
+ },
105
+ });
106
+ // result.verification.operations[0].checkedLimits contains this quote and tolerance.
107
+ ```
66
108
 
67
- See [`CLAUDE.md`](./CLAUDE.md) in this directory for the execution flow diagram,
68
- backend tradeoffs, authorizations model, error-handling table,
69
- and recipes for adding a chain or a new backend.
109
+ Measurements cover the named subject over the **whole bundle**. Entries do not
110
+ attribute aggregate state changes to individual transactions. Use separate
111
+ simulations for per-transaction limits. Asset bounds use net wallet balance
112
+ changes (gas excluded). For native ETH, set `assetPaid` or `assetReceived` to
113
+ viem's `ethAddress` (`0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`); other
114
+ sentinels such as `zeroAddress` are read as ERC-20 tokens. Share bounds use the
115
+ selected account's vault shares or Blue position shares. Borrow/repay entries
116
+ use debt shares; refinance selects
117
+ source shares burned and target shares minted; migration selects source vault
118
+ shares burned and target vault shares minted. Combined collateral/borrow actions
119
+ measure collateral paid and loan assets received; repay/collateral-withdraw actions
120
+ measure loan assets paid and collateral received.
121
+
122
+ For `vaultV1InKindRedeem` and `vaultV2InKindRedeem`, `assetsReceived` measures
123
+ only the receiver's wallet balance of the vault asset (the idle portion); it
124
+ does not measure in-kind Morpho positions.
125
+
126
+ Only slippage is checked; there are no separate refund or penalty checks.
127
+ `sharesMinted` and `sharesBurned` quotes for `blueSupplyCollateral` and
128
+ `blueWithdrawCollateral` are invalid and throw `SimulationValidationError`
129
+ during request parsing. `MissingVerificationEvidenceError` reports runtime
130
+ evidence gaps: a failed state read, native outgoing traces that do not cover
131
+ value sent, or a metadata read that reverts or returns empty/invalid
132
+ data. A quoted balance/position read whose non-empty return data cannot be
133
+ decoded throws `InvalidSimulationResponseError`. Excessive slippage throws
134
+ `ConsumerLimitViolationError`.
135
+
136
+ Only quoted amounts are observed. ERC-20 assets and vault shares use `balanceOf`;
137
+ Blue shares use `position`. Each distinct call runs before and after the bundle.
138
+ Native amounts use the existing transfer traces. Without limits there are no
139
+ slippage reads. Quotes with an asset amount resolve `asset()` or market parameters only
140
+ when an explicit `assetPaid` or `assetReceived` was not supplied; share-only quotes need no metadata
141
+ reads. No vault factories, full entities, allocations, risk metrics, allowances,
142
+ or nonces are fetched for slippage.
143
+
144
+ For example, a Vault V2 deposit quoting assets paid and shares minted makes four
145
+ in-bundle view calls — the sender's asset balance and the `account`'s share balance, each before
146
+ and after execution — plus one `asset()` read unless `assetPaid` is supplied. `verification.operations[].checkedLimits` records the quote
147
+ and tolerance checked. The result does not include broad state snapshots or diffs;
148
+ `transfers` and `assetChanges` remain available.
149
+
150
+ The result's historical `VerifiedSimulationResult` name does not imply that
151
+ unchecked outcomes are economically verified.
152
+
153
+ Preview authorizations remain an adapter from `getRequirements()` through
154
+ `toSimulationAuthorizations` into preparation calls. No permission or nonce
155
+ read-back policy runs. Successful execution establishes success under the
156
+ simulated permissions; it does not prove that a future signature is valid.
157
+ Preparation and user-transaction failures still propagate. Final mode executes
158
+ actual calldata without preparation. State reads and preparation calls do not
159
+ receive public transaction indices.
160
+
161
+ See [AGENTS.md](./AGENTS.md) for pipeline conventions and retention rules.
70
162
 
71
163
  ## Development
72
164
 
@@ -0,0 +1,95 @@
1
+ import type { Address, Hex } from "viem";
2
+ /** EIP-712 domain separator fields of a signature payload. */
3
+ export interface Eip712Domain {
4
+ readonly name?: string;
5
+ readonly version?: string;
6
+ readonly chainId: number | bigint;
7
+ readonly verifyingContract: Address;
8
+ readonly salt?: Hex;
9
+ }
10
+ /** One entry of an EIP-712 `types` field list. */
11
+ export interface Eip712Field {
12
+ readonly name: string;
13
+ readonly type: string;
14
+ }
15
+ /** ERC-2612 `Permit` typed data; `domain.verifyingContract` is the token. */
16
+ export interface Erc2612PermitTypedData {
17
+ readonly domain: Eip712Domain;
18
+ readonly primaryType: "Permit";
19
+ readonly types: {
20
+ readonly Permit: readonly Eip712Field[];
21
+ };
22
+ readonly message: {
23
+ readonly owner: Address;
24
+ readonly spender: Address;
25
+ readonly value: bigint;
26
+ readonly nonce: bigint;
27
+ readonly deadline: bigint;
28
+ };
29
+ }
30
+ /** Permit2 `PermitTransferFrom` typed data; the owner is not in the message. */
31
+ export interface Permit2TransferTypedData {
32
+ readonly domain: Eip712Domain;
33
+ readonly primaryType: "PermitTransferFrom";
34
+ readonly types: {
35
+ readonly PermitTransferFrom: readonly Eip712Field[];
36
+ readonly TokenPermissions: readonly Eip712Field[];
37
+ };
38
+ readonly message: {
39
+ readonly permitted: {
40
+ readonly token: Address;
41
+ readonly amount: bigint;
42
+ };
43
+ readonly spender: Address;
44
+ readonly nonce: bigint;
45
+ readonly deadline: bigint;
46
+ };
47
+ }
48
+ /** Morpho `Authorization` typed data consumed by `setAuthorizationWithSig`. */
49
+ export interface BlueAuthorizationTypedData {
50
+ readonly domain: Eip712Domain;
51
+ readonly primaryType: "Authorization";
52
+ readonly types: {
53
+ readonly Authorization: readonly Eip712Field[];
54
+ };
55
+ readonly message: {
56
+ readonly authorizer: Address;
57
+ readonly authorized: Address;
58
+ readonly isAuthorized: boolean;
59
+ readonly nonce: bigint;
60
+ readonly deadline: bigint;
61
+ };
62
+ }
63
+ /** Raw ERC-20 `approve` request. */
64
+ export interface Erc20ApprovalAuthorization {
65
+ readonly type: "erc20Approval";
66
+ readonly token: Address;
67
+ readonly owner: Address;
68
+ readonly spender: Address;
69
+ readonly amount: bigint;
70
+ }
71
+ /** ERC-2612 permit request. */
72
+ export interface Erc2612PermitAuthorization {
73
+ readonly type: "erc2612Permit";
74
+ readonly typedData: Erc2612PermitTypedData;
75
+ }
76
+ /** Permit2 signature transfer request. */
77
+ export interface Permit2TransferAuthorization {
78
+ readonly type: "permit2SignatureTransfer";
79
+ readonly owner: Address;
80
+ readonly typedData: Permit2TransferTypedData;
81
+ }
82
+ /** Direct Morpho `setAuthorization` request. */
83
+ export interface BlueAuthorization {
84
+ readonly type: "blueAuthorization";
85
+ readonly authorizer: Address;
86
+ readonly authorized: Address;
87
+ readonly isAuthorized: boolean;
88
+ }
89
+ /** Morpho `setAuthorizationWithSig` request. */
90
+ export interface BlueAuthorizationSignature {
91
+ readonly type: "blueAuthorizationSignature";
92
+ readonly typedData: BlueAuthorizationTypedData;
93
+ }
94
+ /** A pending wallet request passed to `simulate()` — the five typed variants. */
95
+ export type SimulationAuthorization = Erc20ApprovalAuthorization | Erc2612PermitAuthorization | Permit2TransferAuthorization | BlueAuthorization | BlueAuthorizationSignature;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,44 +1,238 @@
1
+ import type { Address, Hash } from "viem";
2
+ import type { SimulationOperationSubject } from "./limits.js";
3
+ import type { SimulationMode } from "./params.js";
4
+ /** Stable error codes thrown by this package, part of the public API. */
5
+ export declare const SIMULATION_ERROR_CODES: readonly ["VALIDATION_ERROR", "UNSUPPORTED_CHAIN", "EXTERNAL_SERVICE_ERROR", "SIMULATION_REVERTED", "BLACKLIST_ERROR", "UNSUPPORTED_OPERATION", "INVALID_SIMULATION_RESPONSE", "MISSING_VERIFICATION_EVIDENCE", "AUTHORIZATION_REQUEST_MISMATCH", "CONSUMER_LIMIT_VIOLATION", "UNEXPECTED_SIMULATION_ERROR"];
6
+ /** Stable string discriminator for log aggregation and external mapping. */
7
+ export type SimulationErrorCode = (typeof SIMULATION_ERROR_CODES)[number];
8
+ /**
9
+ * Machine-readable cause of an execution failure (ADR-2026-09-18 §Errors).
10
+ * `UNKNOWN_REVERT` is used when the revert maps to no known Morpho condition;
11
+ * consumers branch on it and never parse `reason`.
12
+ */
13
+ export type SimulationExecutionReason = "INSUFFICIENT_BALANCE" | "INSUFFICIENT_ALLOWANCE" | "INSUFFICIENT_LIQUIDITY" | "POSITION_UNHEALTHY" | "SLIPPAGE_EXCEEDED" | "SIGNATURE_EXPIRED" | "SIGNATURE_INVALID" | "NONCE_ALREADY_USED" | "CAP_EXCEEDED" | "ACCESS_RESTRICTED" | "UNKNOWN_REVERT";
14
+ declare const SIMULATION_STAGES: readonly ["validation", "preparation", "execution", "verification", "transport"];
15
+ /** Pipeline stage a failure belongs to; `transport` covers RPC and response failures. */
16
+ export type SimulationStage = (typeof SIMULATION_STAGES)[number];
17
+ interface SimulationContextBase {
18
+ /** Request mode the failure happened in. */
19
+ readonly mode: SimulationMode;
20
+ /** Chain the request targeted. */
21
+ readonly chainId: number;
22
+ /** Block the simulation was pinned to. */
23
+ readonly blockNumber: bigint;
24
+ }
25
+ interface SimulationCheckContext extends SimulationContextBase {
26
+ /** Token whose balance, allowance or transfer was checked. */
27
+ readonly token?: Address;
28
+ /** Account whose position, balance or authorization was checked (usually the sender). */
29
+ readonly account?: Address;
30
+ /** Spender or operator granted by the checked permission. */
31
+ readonly spender?: Address;
32
+ /**
33
+ * Name of the checked field (e.g. `"assetsReceived"`). Unbound verification
34
+ * contexts instead carry the failed state read's id
35
+ * (`balance:<token>:<account>` or `position:<marketId>:<account>`).
36
+ */
37
+ readonly field?: string;
38
+ /** Bound or value `field` was checked against; 32-byte hashes only, never calldata or signatures. */
39
+ readonly expected?: bigint | boolean | Address | Hash;
40
+ /** Value observed in the simulation, same domain as `expected`. */
41
+ readonly observed?: bigint | boolean | Address | Hash;
42
+ /**
43
+ * Reserved; not populated for caller-transaction reverts, whose index is on
44
+ * `SimulationRevertedError.details[].transactionIndex`.
45
+ */
46
+ readonly failedTransactionIndex?: number;
47
+ }
48
+ /** Context of a failure while validating the request. */
49
+ export type SimulationValidationContext = SimulationContextBase & {
50
+ readonly stage: "validation";
51
+ };
52
+ /** Context of a failure while reaching the `eth_simulateV1` backend. */
53
+ export type SimulationTransportContext = SimulationContextBase & {
54
+ readonly stage: "transport";
55
+ };
56
+ /** Context of a failure while preparing a preview authorization. */
57
+ export type SimulationPreparationContext = SimulationContextBase & {
58
+ readonly stage: "preparation";
59
+ /** Index into `authorizations`. */
60
+ readonly authorizationIndex: number;
61
+ /** Index into that authorization's preparation calls. */
62
+ readonly preparationCallIndex?: number;
63
+ };
64
+ /**
65
+ * Reserved execution-stage context; `SimulationRevertedError` carries none for
66
+ * caller-transaction or node reverts (see `details[].transactionIndex`).
67
+ */
68
+ export type SimulationExecutionContext = SimulationCheckContext & SimulationOperationSubject & {
69
+ readonly stage: "execution";
70
+ };
71
+ /** Context of a verification check that did not hold. */
72
+ export type SimulationVerificationContext = SimulationCheckContext & SimulationOperationSubject & {
73
+ readonly stage: "verification";
74
+ };
75
+ /**
76
+ * Where and why a simulation failed, keyed by `stage`
77
+ * (ADR-2026-10-01-evm-simulation-quoted-slippage-limits).
78
+ * Every stage carries `mode`, `chainId` and `blockNumber`. Preparation
79
+ * contexts carry `authorizationIndex` and `preparationCallIndex`. Verification
80
+ * contexts bound to a limit take `operation` and the subject fields
81
+ * (`marketId`, `sourceMarketId`/`targetMarketId`, `vault`,
82
+ * `sourceVault`/`targetVault`) from that `OperationLimit`, not
83
+ * from decoded calldata; unbound ones carry only a `field` string naming the
84
+ * failed state read (`balance:<token>:<account>` or
85
+ * `position:<marketId>:<account>`).
86
+ * `"execution"` stays in the union, but `SimulationRevertedError` carries no
87
+ * `context` for caller-transaction or node reverts — the failing
88
+ * `transactionIndex` is reported on the error's `details`. Never contains
89
+ * signatures, RPC URLs, credentials, raw calldata or raw causes (`cause`
90
+ * stays on the error).
91
+ */
92
+ export type SimulationErrorContext = SimulationValidationContext | SimulationTransportContext | SimulationPreparationContext | SimulationExecutionContext | SimulationVerificationContext | (SimulationCheckContext & {
93
+ readonly stage: "verification";
94
+ readonly field: string;
95
+ readonly operation?: never;
96
+ });
1
97
  /**
2
98
  * Base class for every error this package throws. Transport-agnostic — no HTTP status codes.
3
99
  * Consumers pattern-match with `instanceof` on the concrete subclass.
4
100
  */
5
101
  export declare abstract class SimulationPackageError extends Error {
6
102
  /** Stable string discriminator for log aggregation and external mapping. */
7
- abstract readonly code: string;
8
- constructor(message: string, options?: ErrorOptions);
103
+ abstract readonly code: SimulationErrorCode;
104
+ /** Frozen copy of the failure context, when one was supplied. */
105
+ readonly context?: SimulationErrorContext;
106
+ constructor(message: string, options?: ErrorOptions & {
107
+ context?: SimulationErrorContext;
108
+ });
9
109
  }
10
110
  /** Transaction would revert on-chain. Not bypassable. */
11
111
  export declare class SimulationRevertedError extends SimulationPackageError {
112
+ /** Raw revert string as reported by the backend, when any. */
12
113
  readonly reason: string | undefined;
13
114
  readonly details?: unknown;
115
+ /** Machine-readable cause; `UNKNOWN_REVERT` when the revert maps to no known Morpho condition. */
116
+ readonly reasonCode: SimulationExecutionReason;
117
+ readonly name = "SimulationRevertedError";
14
118
  readonly code = "SIMULATION_REVERTED";
15
- constructor(reason: string | undefined, details?: unknown);
119
+ readonly context?: SimulationPreparationContext | SimulationExecutionContext;
120
+ constructor(
121
+ /** Raw revert string as reported by the backend, when any. */
122
+ reason: string | undefined, details?: unknown,
123
+ /** Machine-readable cause; `UNKNOWN_REVERT` when the revert maps to no known Morpho condition. */
124
+ reasonCode?: SimulationExecutionReason,
125
+ /** Which transaction/authorization reverted and the operation it belonged to. */
126
+ context?: SimulationPreparationContext | SimulationExecutionContext);
16
127
  }
17
- interface RetainedAsset {
18
- address: string | undefined;
19
- token: string | undefined;
20
- netRetained: string;
128
+ /** Per-asset net retained amount keyed by restricted contract and token. */
129
+ export interface RetainedAsset {
130
+ /** Restricted bundles contract that ended up holding the token. */
131
+ readonly address: Address;
132
+ /** Token retained (native ETH uses viem's `ethAddress`). */
133
+ readonly token: Address;
134
+ /** Net balance increase in the token's base units; always positive. */
135
+ readonly netRetained: bigint;
21
136
  }
22
- /** Funds would flow to restricted standalone bundles contract addresses. Never bypassable. */
137
+ /**
138
+ * Thrown when net value above the dust threshold is retained by a restricted bundles contract
139
+ * (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1 or MidnightBundlesV1) after simulation;
140
+ * pass-through flows and net outflows are allowed. Never bypassable.
141
+ */
23
142
  export declare class BlacklistViolationError extends SimulationPackageError {
24
- readonly assetChanges?: RetainedAsset[] | undefined;
143
+ /** Per-asset net retained amounts keyed by restricted contract and token. */
144
+ readonly assetChanges?: readonly RetainedAsset[] | undefined;
145
+ readonly name = "BlacklistViolationError";
25
146
  readonly code = "BLACKLIST_ERROR";
26
- constructor(message: string, assetChanges?: RetainedAsset[] | undefined);
147
+ constructor(message: string,
148
+ /** Per-asset net retained amounts keyed by restricted contract and token. */
149
+ assetChanges?: readonly RetainedAsset[] | undefined, context?: SimulationErrorContext);
27
150
  }
28
- /** Tenderly or RPC service is down. Bypassable — user can proceed. */
151
+ /** RPC service is down or unreachable. Bypassable — user can proceed. */
29
152
  export declare class ExternalServiceError extends SimulationPackageError {
153
+ readonly name = "ExternalServiceError";
30
154
  readonly code = "EXTERNAL_SERVICE_ERROR";
31
155
  }
32
156
  /** Bad input to the simulation functions. Not bypassable. */
33
157
  export declare class SimulationValidationError extends SimulationPackageError {
34
158
  readonly fieldErrors?: string[] | undefined;
159
+ readonly name = "SimulationValidationError";
35
160
  readonly code = "VALIDATION_ERROR";
36
- constructor(message: string, fieldErrors?: string[] | undefined);
161
+ constructor(message: string, fieldErrors?: string[] | undefined, context?: SimulationErrorContext);
37
162
  }
38
163
  /** Chain ID not configured for any simulation method. Not bypassable. */
39
164
  export declare class UnsupportedChainError extends SimulationPackageError {
40
165
  readonly chainId: number;
166
+ readonly name = "UnsupportedChainError";
41
167
  readonly code = "UNSUPPORTED_CHAIN";
42
- constructor(chainId: number);
168
+ constructor(chainId: number, context?: SimulationErrorContext);
43
169
  }
170
+ /**
171
+ * An SDK requirement passed to `toSimulationAuthorizations` cannot be
172
+ * converted into a simulation authorization.
173
+ */
174
+ export declare class UnsupportedOperationError extends SimulationPackageError {
175
+ readonly name = "UnsupportedOperationError";
176
+ readonly code = "UNSUPPORTED_OPERATION";
177
+ }
178
+ /** The simulation backend returned a response that cannot be parsed. */
179
+ export declare class InvalidSimulationResponseError extends SimulationPackageError {
180
+ readonly name = "InvalidSimulationResponseError";
181
+ readonly code = "INVALID_SIMULATION_RESPONSE";
182
+ }
183
+ /** State needed to verify an operation could not be fetched or derived. */
184
+ export declare class MissingVerificationEvidenceError extends SimulationPackageError {
185
+ readonly name = "MissingVerificationEvidenceError";
186
+ readonly code = "MISSING_VERIFICATION_EVIDENCE";
187
+ }
188
+ /** A pending authorization does not match the request it was prepared for. */
189
+ export declare class AuthorizationRequestMismatchError extends SimulationPackageError {
190
+ readonly name = "AuthorizationRequestMismatchError";
191
+ readonly code = "AUTHORIZATION_REQUEST_MISMATCH";
192
+ }
193
+ /** A consumer-supplied limit was violated. */
194
+ export declare class ConsumerLimitViolationError extends SimulationPackageError {
195
+ readonly name = "ConsumerLimitViolationError";
196
+ readonly code = "CONSUMER_LIMIT_VIOLATION";
197
+ }
198
+ /** The simulation failed for a reason that fits no other code. */
199
+ export declare class UnexpectedSimulationError extends SimulationPackageError {
200
+ readonly name = "UnexpectedSimulationError";
201
+ readonly code = "UNEXPECTED_SIMULATION_ERROR";
202
+ }
203
+ /**
204
+ * Structural guard for consumers where `instanceof` fails across bundles.
205
+ *
206
+ * @param value - Anything caught.
207
+ * @returns `true` for `SimulationPackageError` instances and for objects
208
+ * carrying a `message` string, a known `code`, the `name` of the class
209
+ * owning that `code`, and an absent or
210
+ * well-formed `context` (known `stage`, `mode`, numeric `chainId`,
211
+ * `bigint` `blockNumber`, `authorizationIndex` for `preparation`, and a
212
+ * known `operation` with its subject fields for `execution`/`verification`,
213
+ * or a verification `field` string with no `operation`).
214
+ * @example
215
+ * ```ts
216
+ * import { isSimulationPackageError, simulate } from "@morpho-org/evm-simulation";
217
+ * import type { Address, Hex } from "viem";
218
+ *
219
+ * declare const user: Address;
220
+ * declare const vault: Address;
221
+ * declare const encodedDeposit: Hex;
222
+ * const config = {
223
+ * chains: new Map([[1, { simulateV1Url: "https://rpc.example" }]]),
224
+ * };
225
+ * try {
226
+ * await simulate(config, {
227
+ * chainId: 1,
228
+ * transactions: [{ from: user, to: vault, data: encodedDeposit }],
229
+ * });
230
+ * } catch (e) {
231
+ * if (!isSimulationPackageError(e)) throw e;
232
+ * if (e.code !== "SIMULATION_REVERTED") throw e;
233
+ * console.log(e.context?.stage, e.message);
234
+ * }
235
+ * ```
236
+ */
237
+ export declare function isSimulationPackageError(value: unknown): value is SimulationPackageError;
44
238
  export {};