@morpho-org/evm-simulation 5.1.0 → 6.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.
- package/README.md +6 -4
- package/lib/cjs/errors.d.ts +17 -2
- package/lib/cjs/errors.js +19 -3
- package/lib/cjs/index.d.ts +2 -2
- package/lib/cjs/index.js +2 -1
- package/lib/cjs/params.d.ts +20 -1
- package/lib/cjs/requirements/requirements.js +2 -1
- package/lib/cjs/simulate/backends/eth-simulate-v1.d.ts +21 -34
- package/lib/cjs/simulate/backends/eth-simulate-v1.js +27 -73
- package/lib/cjs/simulate/backends/index.d.ts +1 -1
- package/lib/cjs/simulate/backends/index.js +1 -2
- package/lib/cjs/simulate/backends/parse-response.js +1 -1
- package/lib/cjs/simulate/backends/resolve-pinned-block.d.ts +6 -12
- package/lib/cjs/simulate/backends/resolve-pinned-block.js +3 -4
- package/lib/cjs/simulate/pipeline/bundles-retention.d.ts +3 -3
- package/lib/cjs/simulate/pipeline/bundles-retention.js +6 -6
- package/lib/cjs/simulate/request/parse-request.d.ts +2 -1
- package/lib/cjs/simulate/request/parse-request.js +21 -0
- package/lib/cjs/simulate/run-simulation.js +7 -12
- package/lib/cjs/simulate/simulate.d.ts +25 -17
- package/lib/cjs/simulate/simulate.js +25 -17
- package/lib/cjs/types.d.ts +1 -1
- package/lib/esm/errors.d.ts +17 -2
- package/lib/esm/errors.js +17 -2
- package/lib/esm/index.d.ts +2 -2
- package/lib/esm/index.js +1 -1
- package/lib/esm/params.d.ts +20 -1
- package/lib/esm/requirements/requirements.js +2 -1
- package/lib/esm/simulate/backends/eth-simulate-v1.d.ts +21 -34
- package/lib/esm/simulate/backends/eth-simulate-v1.js +29 -74
- package/lib/esm/simulate/backends/index.d.ts +1 -1
- package/lib/esm/simulate/backends/index.js +1 -1
- package/lib/esm/simulate/backends/parse-response.js +1 -1
- package/lib/esm/simulate/backends/resolve-pinned-block.d.ts +6 -12
- package/lib/esm/simulate/backends/resolve-pinned-block.js +3 -4
- package/lib/esm/simulate/pipeline/bundles-retention.d.ts +3 -3
- package/lib/esm/simulate/pipeline/bundles-retention.js +6 -6
- package/lib/esm/simulate/request/parse-request.d.ts +2 -1
- package/lib/esm/simulate/request/parse-request.js +21 -0
- package/lib/esm/simulate/run-simulation.js +8 -13
- package/lib/esm/simulate/simulate.d.ts +25 -17
- package/lib/esm/simulate/simulate.js +25 -17
- package/lib/esm/types.d.ts +1 -1
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Address, type BlockTag, type Hex } from "viem";
|
|
2
2
|
import type { SimulationAuthorization } from "../../authorizations.js";
|
|
3
3
|
import type { SimulationLimits } from "../../limits.js";
|
|
4
|
-
import { type SimulateParams, type SimulationMode } from "../../params.js";
|
|
4
|
+
import { type SimulateParams, type SimulationMode, type StateBlock } from "../../params.js";
|
|
5
5
|
/** A normalized user transaction: checksummed addresses, `value` defaulted to `0n`.
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
@@ -21,6 +21,7 @@ export interface ParsedRequest {
|
|
|
21
21
|
/** Always empty in final mode. */
|
|
22
22
|
readonly authorizations: readonly SimulationAuthorization[];
|
|
23
23
|
readonly blockNumber?: bigint | Exclude<BlockTag, "pending">;
|
|
24
|
+
readonly block?: StateBlock;
|
|
24
25
|
readonly limits?: SimulationLimits;
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
@@ -52,6 +52,7 @@ const PERMIT2_MESSAGE_KEYS = PERMIT2_TRANSFER_FIELDS.map((field) => field.name);
|
|
|
52
52
|
const PERMIT2_PERMITTED_KEYS = ["token", "amount"];
|
|
53
53
|
const BLUE_AUTHORIZATION_MESSAGE_KEYS = BLUE_AUTHORIZATION_FIELDS.map((field) => field.name);
|
|
54
54
|
const TRANSACTION_KEYS = ["from", "to", "data", "value"];
|
|
55
|
+
const BLOCK_KEYS = ["number", "hash", "timestamp"];
|
|
55
56
|
const LIMITS_KEYS = ["operations"];
|
|
56
57
|
const AUTHORIZATION_KEYS = {
|
|
57
58
|
erc20Approval: ["type", "token", "owner", "spender", "amount"],
|
|
@@ -570,6 +571,7 @@ function parseRequest(input) {
|
|
|
570
571
|
"chainId",
|
|
571
572
|
"transactions",
|
|
572
573
|
"blockNumber",
|
|
574
|
+
"block",
|
|
573
575
|
"mode",
|
|
574
576
|
"authorizations",
|
|
575
577
|
"limits",
|
|
@@ -652,6 +654,24 @@ function parseRequest(input) {
|
|
|
652
654
|
}
|
|
653
655
|
}
|
|
654
656
|
}
|
|
657
|
+
// block
|
|
658
|
+
const rawBlock = input.block;
|
|
659
|
+
let block;
|
|
660
|
+
if (rawBlock !== undefined) {
|
|
661
|
+
if (!isRecord(rawBlock)) {
|
|
662
|
+
fieldErrors.push("block: must be an object");
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
check.keys(rawBlock, { allow: BLOCK_KEYS, path: "block" });
|
|
666
|
+
const number = check.uint256(readField(rawBlock, "number"), "block.number");
|
|
667
|
+
const hash = check.bytes32(readField(rawBlock, "hash"), "block.hash");
|
|
668
|
+
const timestamp = check.uint256(readField(rawBlock, "timestamp"), "block.timestamp");
|
|
669
|
+
if (number !== undefined && hash !== undefined && timestamp !== undefined)
|
|
670
|
+
block = { number, hash: hash.toLowerCase(), timestamp };
|
|
671
|
+
}
|
|
672
|
+
if (rawBlockNumber !== undefined)
|
|
673
|
+
fieldErrors.push("block: cannot be combined with blockNumber");
|
|
674
|
+
}
|
|
655
675
|
// authorizations
|
|
656
676
|
const rawAuthorizations = input.authorizations;
|
|
657
677
|
const authorizations = [];
|
|
@@ -754,6 +774,7 @@ function parseRequest(input) {
|
|
|
754
774
|
transactions,
|
|
755
775
|
authorizations: mode === "preview" ? authorizations : [],
|
|
756
776
|
...(blockNumber !== undefined ? { blockNumber } : {}),
|
|
777
|
+
...(block !== undefined ? { block } : {}),
|
|
757
778
|
...(normalizedLimits !== undefined ? { limits: normalizedLimits } : {}),
|
|
758
779
|
});
|
|
759
780
|
}
|
|
@@ -45,21 +45,16 @@ async function runSimulation(params) {
|
|
|
45
45
|
if (requiresMorpho && addresses?.blue == null)
|
|
46
46
|
throw new errors_js_1.UnsupportedChainError(request.chainId);
|
|
47
47
|
const morpho = addresses?.blue ?? viem_1.zeroAddress;
|
|
48
|
-
await (0, index_js_1.assertEndpointChain)({
|
|
49
|
-
client,
|
|
50
|
-
chainId: request.chainId,
|
|
51
|
-
mode: request.mode,
|
|
52
|
-
blockNumber: typeof request.blockNumber === "bigint" ? request.blockNumber : undefined,
|
|
53
|
-
});
|
|
54
48
|
// Monad nodes reject `validation: false` and charge no gas in the simulated
|
|
55
49
|
// block either way. Its `latest` block is not final: simulating at its number
|
|
56
50
|
// runs on another parent, so default to `finalized`.
|
|
57
51
|
const monad = request.chainId === blue_sdk_1.ChainId.MonadMainnet;
|
|
58
|
-
const pinnedBlock =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
const pinnedBlock = request.block ??
|
|
53
|
+
(await (0, resolve_pinned_block_js_1.resolvePinnedBlock)({
|
|
54
|
+
client,
|
|
55
|
+
blockNumber: request.blockNumber ?? (monad ? "finalized" : undefined),
|
|
56
|
+
signal,
|
|
57
|
+
}));
|
|
63
58
|
// With no calldata decoding, the caller's declared `limits.operations`
|
|
64
59
|
// entries are the operations: subjects for state reads and per-operation
|
|
65
60
|
// checks come straight from the limit list.
|
|
@@ -85,7 +80,7 @@ async function runSimulation(params) {
|
|
|
85
80
|
preparations,
|
|
86
81
|
reads: observations.reads,
|
|
87
82
|
});
|
|
88
|
-
// Execute against the block resolved once above
|
|
83
|
+
// Execute against the block resolved once above.
|
|
89
84
|
const execution = await (0, index_js_1.executePlan)({
|
|
90
85
|
client,
|
|
91
86
|
plan,
|
|
@@ -4,11 +4,11 @@ import type { SimulationConfig } from "../types.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Simulate a bundle of EVM transactions.
|
|
6
6
|
*
|
|
7
|
-
* Parses and normalizes the request → resolves
|
|
8
|
-
*
|
|
9
|
-
* and the execution → executes once through `eth_simulateV1` under
|
|
10
|
-
* timeout budget (simulation with in-block state reads → response
|
|
11
|
-
*
|
|
7
|
+
* Parses and normalizes the request → resolves a single pinned block (skipped
|
|
8
|
+
* when `params.block` is supplied) → resolves quoted-asset metadata → plans
|
|
9
|
+
* state reads and the execution → executes once through `eth_simulateV1` under
|
|
10
|
+
* the full timeout budget (simulation with in-block state reads → response
|
|
11
|
+
* parsing) → derives ERC20/WETH9 transfers and net asset changes from the
|
|
12
12
|
* user calls only → decodes quoted balances/positions → runs slippage checks
|
|
13
13
|
* → asserts no funds are retained by standalone `bundles` periphery contracts
|
|
14
14
|
* → returns the result. The caller reads whichever fields they need:
|
|
@@ -41,7 +41,9 @@ import type { SimulationConfig } from "../types.js";
|
|
|
41
41
|
* `blockOverrides.gasLimit` and `parentHashCheck`, optional logger, and the
|
|
42
42
|
* overall timeout budget.
|
|
43
43
|
* @param params - Per-call simulation input.
|
|
44
|
-
* @param params.chainId - Chain id the bundle targets
|
|
44
|
+
* @param params.chainId - Chain id the bundle targets. Sent on every call inside
|
|
45
|
+
* `eth_simulateV1`, so nodes that check it reject a wrong-chain endpoint;
|
|
46
|
+
* there is no separate `eth_chainId` lookup.
|
|
45
47
|
* @param params.transactions - The bundle's transactions, in execution order.
|
|
46
48
|
* All must share the same `from`.
|
|
47
49
|
* @param params.mode - `"final"` (default) or `"preview"`.
|
|
@@ -51,9 +53,14 @@ import type { SimulationConfig } from "../types.js";
|
|
|
51
53
|
* than `"pending"`.
|
|
52
54
|
* Defaults to `latest` (`finalized` on Monad, whose `latest` block is not
|
|
53
55
|
* final), resolved exactly once.
|
|
56
|
+
* @param params.block - Optional caller-supplied state block (`number`,
|
|
57
|
+
* `hash`, `timestamp`). Skips the block lookup, so a simulation without
|
|
58
|
+
* asset metadata reads makes a single `eth_simulateV1` request. Cannot be
|
|
59
|
+
* combined with `blockNumber`.
|
|
54
60
|
* @throws {SimulationValidationError} for invalid input (mixed senders, bad
|
|
55
61
|
* addresses, empty transactions, malformed authorizations, final-mode
|
|
56
|
-
* authorizations, malformed limits, unknown fields, a `"pending"` block tag,
|
|
62
|
+
* authorizations, malformed limits, unknown fields, a `"pending"` block tag,
|
|
63
|
+
* a malformed `block` or one combined with `blockNumber`, a
|
|
57
64
|
* `blockOverrides.gasLimit` that is not a positive bigint, a `parentHashCheck`
|
|
58
65
|
* that is not a boolean, or share quotes for
|
|
59
66
|
* `blueSupplyCollateral` / `blueWithdrawCollateral`).
|
|
@@ -69,20 +76,21 @@ import type { SimulationConfig } from "../types.js";
|
|
|
69
76
|
* @throws {InvalidSimulationResponseError} when the node response cannot be
|
|
70
77
|
* trusted (bad shape, call-count mismatch, block that is neither the pinned
|
|
71
78
|
* state block nor its immediate successor, a successor with a mismatched
|
|
72
|
-
* `parentHash` (unless `parentHashCheck` is off; off by default on Stable,
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
79
|
+
* `parentHash` (unless `parentHashCheck` is off; off by default on Stable,
|
|
80
|
+
* chain 988), a block timestamp earlier than the pinned block's, a
|
|
81
|
+
* malformed per-call result, or a quoted balance/position read whose
|
|
82
|
+
* non-empty return data cannot be decoded).
|
|
83
|
+
* @throws {InvalidChainIdError} when the node rejects the request `chainId`
|
|
84
|
+
* because the endpoint serves another chain (a subclass of
|
|
85
|
+
* `InvalidSimulationResponseError` sharing its `name`/`code`;
|
|
86
|
+
* `instanceof InvalidChainIdError` tells them apart). Never bypassable.
|
|
78
87
|
* @throws {BlacklistViolationError} when the simulation leaves value retained
|
|
79
88
|
* beyond the dust threshold by a `bundles` periphery contract
|
|
80
|
-
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1,
|
|
89
|
+
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1, MidnightBundlesV2).
|
|
81
90
|
* Never bypassable.
|
|
82
91
|
* @throws {ExternalServiceError} when the RPC is unavailable within the
|
|
83
|
-
* timeout budget or returns a malformed JSON-RPC envelope.
|
|
84
|
-
*
|
|
85
|
-
* `InvalidSimulationResponseError`.
|
|
92
|
+
* timeout budget or returns a malformed JSON-RPC envelope. A looked-up state
|
|
93
|
+
* block without number/hash is reported as `InvalidSimulationResponseError`.
|
|
86
94
|
* @returns A frozen {@link VerifiedSimulationResult} carrying the normalized
|
|
87
95
|
* `simulationTxs`, per-tx `calls` (aligned 1:1), parsed `transfers` (each
|
|
88
96
|
* stamped with `txIdx`), per-account net `assetChanges`, and per-operation
|
|
@@ -6,11 +6,11 @@ const run_simulation_js_1 = require("./run-simulation.js");
|
|
|
6
6
|
/**
|
|
7
7
|
* Simulate a bundle of EVM transactions.
|
|
8
8
|
*
|
|
9
|
-
* Parses and normalizes the request → resolves
|
|
10
|
-
*
|
|
11
|
-
* and the execution → executes once through `eth_simulateV1` under
|
|
12
|
-
* timeout budget (simulation with in-block state reads → response
|
|
13
|
-
*
|
|
9
|
+
* Parses and normalizes the request → resolves a single pinned block (skipped
|
|
10
|
+
* when `params.block` is supplied) → resolves quoted-asset metadata → plans
|
|
11
|
+
* state reads and the execution → executes once through `eth_simulateV1` under
|
|
12
|
+
* the full timeout budget (simulation with in-block state reads → response
|
|
13
|
+
* parsing) → derives ERC20/WETH9 transfers and net asset changes from the
|
|
14
14
|
* user calls only → decodes quoted balances/positions → runs slippage checks
|
|
15
15
|
* → asserts no funds are retained by standalone `bundles` periphery contracts
|
|
16
16
|
* → returns the result. The caller reads whichever fields they need:
|
|
@@ -43,7 +43,9 @@ const run_simulation_js_1 = require("./run-simulation.js");
|
|
|
43
43
|
* `blockOverrides.gasLimit` and `parentHashCheck`, optional logger, and the
|
|
44
44
|
* overall timeout budget.
|
|
45
45
|
* @param params - Per-call simulation input.
|
|
46
|
-
* @param params.chainId - Chain id the bundle targets
|
|
46
|
+
* @param params.chainId - Chain id the bundle targets. Sent on every call inside
|
|
47
|
+
* `eth_simulateV1`, so nodes that check it reject a wrong-chain endpoint;
|
|
48
|
+
* there is no separate `eth_chainId` lookup.
|
|
47
49
|
* @param params.transactions - The bundle's transactions, in execution order.
|
|
48
50
|
* All must share the same `from`.
|
|
49
51
|
* @param params.mode - `"final"` (default) or `"preview"`.
|
|
@@ -53,9 +55,14 @@ const run_simulation_js_1 = require("./run-simulation.js");
|
|
|
53
55
|
* than `"pending"`.
|
|
54
56
|
* Defaults to `latest` (`finalized` on Monad, whose `latest` block is not
|
|
55
57
|
* final), resolved exactly once.
|
|
58
|
+
* @param params.block - Optional caller-supplied state block (`number`,
|
|
59
|
+
* `hash`, `timestamp`). Skips the block lookup, so a simulation without
|
|
60
|
+
* asset metadata reads makes a single `eth_simulateV1` request. Cannot be
|
|
61
|
+
* combined with `blockNumber`.
|
|
56
62
|
* @throws {SimulationValidationError} for invalid input (mixed senders, bad
|
|
57
63
|
* addresses, empty transactions, malformed authorizations, final-mode
|
|
58
|
-
* authorizations, malformed limits, unknown fields, a `"pending"` block tag,
|
|
64
|
+
* authorizations, malformed limits, unknown fields, a `"pending"` block tag,
|
|
65
|
+
* a malformed `block` or one combined with `blockNumber`, a
|
|
59
66
|
* `blockOverrides.gasLimit` that is not a positive bigint, a `parentHashCheck`
|
|
60
67
|
* that is not a boolean, or share quotes for
|
|
61
68
|
* `blueSupplyCollateral` / `blueWithdrawCollateral`).
|
|
@@ -71,20 +78,21 @@ const run_simulation_js_1 = require("./run-simulation.js");
|
|
|
71
78
|
* @throws {InvalidSimulationResponseError} when the node response cannot be
|
|
72
79
|
* trusted (bad shape, call-count mismatch, block that is neither the pinned
|
|
73
80
|
* state block nor its immediate successor, a successor with a mismatched
|
|
74
|
-
* `parentHash` (unless `parentHashCheck` is off; off by default on Stable,
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
81
|
+
* `parentHash` (unless `parentHashCheck` is off; off by default on Stable,
|
|
82
|
+
* chain 988), a block timestamp earlier than the pinned block's, a
|
|
83
|
+
* malformed per-call result, or a quoted balance/position read whose
|
|
84
|
+
* non-empty return data cannot be decoded).
|
|
85
|
+
* @throws {InvalidChainIdError} when the node rejects the request `chainId`
|
|
86
|
+
* because the endpoint serves another chain (a subclass of
|
|
87
|
+
* `InvalidSimulationResponseError` sharing its `name`/`code`;
|
|
88
|
+
* `instanceof InvalidChainIdError` tells them apart). Never bypassable.
|
|
80
89
|
* @throws {BlacklistViolationError} when the simulation leaves value retained
|
|
81
90
|
* beyond the dust threshold by a `bundles` periphery contract
|
|
82
|
-
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1,
|
|
91
|
+
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1, MidnightBundlesV2).
|
|
83
92
|
* Never bypassable.
|
|
84
93
|
* @throws {ExternalServiceError} when the RPC is unavailable within the
|
|
85
|
-
* timeout budget or returns a malformed JSON-RPC envelope.
|
|
86
|
-
*
|
|
87
|
-
* `InvalidSimulationResponseError`.
|
|
94
|
+
* timeout budget or returns a malformed JSON-RPC envelope. A looked-up state
|
|
95
|
+
* block without number/hash is reported as `InvalidSimulationResponseError`.
|
|
88
96
|
* @returns A frozen {@link VerifiedSimulationResult} carrying the normalized
|
|
89
97
|
* `simulationTxs`, per-tx `calls` (aligned 1:1), parsed `transfers` (each
|
|
90
98
|
* stamped with `txIdx`), per-account net `assetChanges`, and per-operation
|
package/lib/cjs/types.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface ChainSimulationConfig {
|
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
15
|
* Whether a simulated successor block's `parentHash` must equal the pinned
|
|
16
|
-
* state block hash. Block number
|
|
16
|
+
* state block hash. Block number and timestamp checks always run.
|
|
17
17
|
* Omitted: on everywhere except Stable (988), whose nodes report a
|
|
18
18
|
* `parentHash` that never matches the pinned block.
|
|
19
19
|
*/
|
package/lib/esm/errors.d.ts
CHANGED
|
@@ -136,7 +136,7 @@ export interface RetainedAsset {
|
|
|
136
136
|
}
|
|
137
137
|
/**
|
|
138
138
|
* Thrown when net value above the dust threshold is retained by a restricted bundles contract
|
|
139
|
-
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1 or
|
|
139
|
+
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1 or MidnightBundlesV2) after simulation;
|
|
140
140
|
* pass-through flows and net outflows are allowed. Never bypassable.
|
|
141
141
|
*/
|
|
142
142
|
export declare class BlacklistViolationError extends SimulationPackageError {
|
|
@@ -175,11 +175,26 @@ export declare class UnsupportedOperationError extends SimulationPackageError {
|
|
|
175
175
|
readonly name = "UnsupportedOperationError";
|
|
176
176
|
readonly code = "UNSUPPORTED_OPERATION";
|
|
177
177
|
}
|
|
178
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* The simulation backend returned a response that cannot be parsed.
|
|
180
|
+
* {@link InvalidChainIdError} subclasses this class and shares its `name`
|
|
181
|
+
* and `code`; check `instanceof InvalidChainIdError` first to tell the two
|
|
182
|
+
* apart.
|
|
183
|
+
*/
|
|
179
184
|
export declare class InvalidSimulationResponseError extends SimulationPackageError {
|
|
180
185
|
readonly name = "InvalidSimulationResponseError";
|
|
181
186
|
readonly code = "INVALID_SIMULATION_RESPONSE";
|
|
182
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* The node rejected the request `chainId`: the endpoint is on a different
|
|
190
|
+
* chain than configured. This is misconfiguration, not an outage, so unlike
|
|
191
|
+
* {@link ExternalServiceError} callers must not bypass it.
|
|
192
|
+
* Extends {@link InvalidSimulationResponseError} and shares its `name` and
|
|
193
|
+
* `code`, so v5 callers that match on either still catch it;
|
|
194
|
+
* `instanceof InvalidChainIdError` is how to tell the two apart.
|
|
195
|
+
*/
|
|
196
|
+
export declare class InvalidChainIdError extends InvalidSimulationResponseError {
|
|
197
|
+
}
|
|
183
198
|
/** State needed to verify an operation could not be fetched or derived. */
|
|
184
199
|
export declare class MissingVerificationEvidenceError extends SimulationPackageError {
|
|
185
200
|
readonly name = "MissingVerificationEvidenceError";
|
package/lib/esm/errors.js
CHANGED
|
@@ -60,7 +60,7 @@ export class SimulationRevertedError extends SimulationPackageError {
|
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Thrown when net value above the dust threshold is retained by a restricted bundles contract
|
|
63
|
-
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1 or
|
|
63
|
+
* (VaultExitBundlesV1, VaultBundlesV1, BlueBundlesV1 or MidnightBundlesV2) after simulation;
|
|
64
64
|
* pass-through flows and net outflows are allowed. Never bypassable.
|
|
65
65
|
*/
|
|
66
66
|
export class BlacklistViolationError extends SimulationPackageError {
|
|
@@ -109,11 +109,26 @@ export class UnsupportedOperationError extends SimulationPackageError {
|
|
|
109
109
|
name = "UnsupportedOperationError";
|
|
110
110
|
code = "UNSUPPORTED_OPERATION";
|
|
111
111
|
}
|
|
112
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* The simulation backend returned a response that cannot be parsed.
|
|
114
|
+
* {@link InvalidChainIdError} subclasses this class and shares its `name`
|
|
115
|
+
* and `code`; check `instanceof InvalidChainIdError` first to tell the two
|
|
116
|
+
* apart.
|
|
117
|
+
*/
|
|
113
118
|
export class InvalidSimulationResponseError extends SimulationPackageError {
|
|
114
119
|
name = "InvalidSimulationResponseError";
|
|
115
120
|
code = "INVALID_SIMULATION_RESPONSE";
|
|
116
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* The node rejected the request `chainId`: the endpoint is on a different
|
|
124
|
+
* chain than configured. This is misconfiguration, not an outage, so unlike
|
|
125
|
+
* {@link ExternalServiceError} callers must not bypass it.
|
|
126
|
+
* Extends {@link InvalidSimulationResponseError} and shares its `name` and
|
|
127
|
+
* `code`, so v5 callers that match on either still catch it;
|
|
128
|
+
* `instanceof InvalidChainIdError` is how to tell the two apart.
|
|
129
|
+
*/
|
|
130
|
+
export class InvalidChainIdError extends InvalidSimulationResponseError {
|
|
131
|
+
}
|
|
117
132
|
/** State needed to verify an operation could not be fetched or derived. */
|
|
118
133
|
export class MissingVerificationEvidenceError extends SimulationPackageError {
|
|
119
134
|
name = "MissingVerificationEvidenceError";
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export type { BlueAuthorization, BlueAuthorizationSignature, BlueAuthorizationTypedData, Eip712Domain, Eip712Field, Erc20ApprovalAuthorization, Erc2612PermitAuthorization, Erc2612PermitTypedData, Permit2TransferAuthorization, Permit2TransferTypedData, SimulationAuthorization, } from "./authorizations.js";
|
|
2
2
|
export type { RetainedAsset, SimulationErrorCode, SimulationErrorContext, SimulationExecutionContext, SimulationExecutionReason, SimulationPreparationContext, SimulationStage, SimulationTransportContext, SimulationValidationContext, SimulationVerificationContext, } from "./errors.js";
|
|
3
|
-
export { AuthorizationRequestMismatchError, BlacklistViolationError, ConsumerLimitViolationError, ExternalServiceError, InvalidSimulationResponseError, isSimulationPackageError, MissingVerificationEvidenceError, SIMULATION_ERROR_CODES, SimulationPackageError, SimulationRevertedError, SimulationValidationError, UnexpectedSimulationError, UnsupportedChainError, UnsupportedOperationError, } from "./errors.js";
|
|
3
|
+
export { AuthorizationRequestMismatchError, BlacklistViolationError, ConsumerLimitViolationError, ExternalServiceError, InvalidChainIdError, InvalidSimulationResponseError, isSimulationPackageError, MissingVerificationEvidenceError, SIMULATION_ERROR_CODES, SimulationPackageError, SimulationRevertedError, SimulationValidationError, UnexpectedSimulationError, UnsupportedChainError, UnsupportedOperationError, } from "./errors.js";
|
|
4
4
|
export type { BlueMarketOperationSubject, BlueMarketOperationType, BlueRefinanceSubject, OperationLimit, OperationType, SimulationLimits, SimulationOperationSubject, SlippageLimits, SlippageQuote, VaultOperationSubject, VaultOperationType, VaultV1MigrateToV2Subject, } from "./limits.js";
|
|
5
5
|
export { BLUE_MARKET_OPERATION_TYPES, OPERATION_TYPES, VAULT_OPERATION_TYPES, } from "./limits.js";
|
|
6
|
-
export type { SimulateParams, SimulationMode, } from "./params.js";
|
|
6
|
+
export type { SimulateParams, SimulationMode, StateBlock, } from "./params.js";
|
|
7
7
|
export { SIMULATION_MODES } from "./params.js";
|
|
8
8
|
export { toSimulationAuthorizations } from "./requirements/index.js";
|
|
9
9
|
export type { AuthorizationPreparation, SimulatedOperation, SimulationVerification, VerifiedSimulationResult, } from "./result.js";
|
package/lib/esm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Public fns (via feature-folder barrels)
|
|
2
2
|
// Errors (for instanceof checks by consumers)
|
|
3
|
-
export { AuthorizationRequestMismatchError, BlacklistViolationError, ConsumerLimitViolationError, ExternalServiceError, InvalidSimulationResponseError, isSimulationPackageError, MissingVerificationEvidenceError, SIMULATION_ERROR_CODES, SimulationPackageError, SimulationRevertedError, SimulationValidationError, UnexpectedSimulationError, UnsupportedChainError, UnsupportedOperationError, } from "./errors.js";
|
|
3
|
+
export { AuthorizationRequestMismatchError, BlacklistViolationError, ConsumerLimitViolationError, ExternalServiceError, InvalidChainIdError, InvalidSimulationResponseError, isSimulationPackageError, MissingVerificationEvidenceError, SIMULATION_ERROR_CODES, SimulationPackageError, SimulationRevertedError, SimulationValidationError, UnexpectedSimulationError, UnsupportedChainError, UnsupportedOperationError, } from "./errors.js";
|
|
4
4
|
export { BLUE_MARKET_OPERATION_TYPES, OPERATION_TYPES, VAULT_OPERATION_TYPES, } from "./limits.js";
|
|
5
5
|
export { SIMULATION_MODES } from "./params.js";
|
|
6
6
|
export { toSimulationAuthorizations } from "./requirements/index.js";
|
package/lib/esm/params.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BlockTag } from "viem";
|
|
1
|
+
import type { BlockTag, Hex } from "viem";
|
|
2
2
|
import type { SimulationAuthorization } from "./authorizations.js";
|
|
3
3
|
import type { SimulationLimits } from "./limits.js";
|
|
4
4
|
import type { SimulationTransaction } from "./types.js";
|
|
@@ -6,6 +6,15 @@ import type { SimulationTransaction } from "./types.js";
|
|
|
6
6
|
export declare const SIMULATION_MODES: readonly ["preview", "final"];
|
|
7
7
|
/** Simulation mode: `"preview"` accepts pending authorizations, `"final"` does not. */
|
|
8
8
|
export type SimulationMode = (typeof SIMULATION_MODES)[number];
|
|
9
|
+
/** A mined block the simulation runs on top of. */
|
|
10
|
+
export interface StateBlock {
|
|
11
|
+
/** Block number the simulation runs on top of. */
|
|
12
|
+
readonly number: bigint;
|
|
13
|
+
/** Hash of that block; compared to the next block's `parentHash`. */
|
|
14
|
+
readonly hash: Hex;
|
|
15
|
+
/** Block timestamp in Unix seconds; the simulated block must not be earlier. */
|
|
16
|
+
readonly timestamp: bigint;
|
|
17
|
+
}
|
|
9
18
|
/**
|
|
10
19
|
* Input to `simulate()`. `authorizations` is accepted only in "preview";
|
|
11
20
|
* `mode` defaults to "final".
|
|
@@ -21,6 +30,16 @@ export interface SimulateParams {
|
|
|
21
30
|
* "latest" block is not final). Only canonical (mined) blocks can be
|
|
22
31
|
* pinned; `pending` has no stable hash and is rejected at runtime. */
|
|
23
32
|
readonly blockNumber?: bigint | Exclude<BlockTag, "pending">;
|
|
33
|
+
/** State block supplied by the caller, so `simulate()` skips the block
|
|
34
|
+
* lookup; with no asset metadata reads it then sends only `eth_simulateV1`.
|
|
35
|
+
* Cannot be combined with `blockNumber`.
|
|
36
|
+
* Not checked against the endpoint: `hash` is compared only when the node
|
|
37
|
+
* reports the next block (its `parentHash`) and `parentHashCheck` is on (off
|
|
38
|
+
* by default on Stable, 988), and `timestamp` only bounds the simulated
|
|
39
|
+
* block's timestamp. On Monad (143), supply a finalized block: `latest` is
|
|
40
|
+
* not final there, so the block at that number can change and fail the
|
|
41
|
+
* `parentHash` check. */
|
|
42
|
+
readonly block?: StateBlock;
|
|
24
43
|
/** Caller-selected quotes and percentage tolerances. Omitted limits are unchecked. */
|
|
25
44
|
readonly limits?: SimulationLimits;
|
|
26
45
|
}
|
|
@@ -130,6 +130,7 @@ export function toSimulationAuthorizations(params) {
|
|
|
130
130
|
}
|
|
131
131
|
if (isRequirementSignature(requirement)) {
|
|
132
132
|
const { action } = requirement;
|
|
133
|
+
const actionType = action.type;
|
|
133
134
|
switch (action.type) {
|
|
134
135
|
case "permit":
|
|
135
136
|
return toErc2612Permit(action, ctx);
|
|
@@ -138,7 +139,7 @@ export function toSimulationAuthorizations(params) {
|
|
|
138
139
|
case "authorization":
|
|
139
140
|
return toBlueAuthorizationSignature(action, ctx);
|
|
140
141
|
default:
|
|
141
|
-
return unsupported(ctx)(`Signature requirement action type "${
|
|
142
|
+
return unsupported(ctx)(`Signature requirement action type "${actionType}" is not supported. Only permit, permit2SignatureTransfer, and authorization signatures are supported`);
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
return unsupported(ctx)(`Requirement action type "${requirement.action.type}" is not supported. Only erc20Approval, blueAuthorization, permit, permit2SignatureTransfer, and authorization requirements are supported`);
|
|
@@ -1,32 +1,20 @@
|
|
|
1
|
-
import { type Client
|
|
2
|
-
import type {
|
|
1
|
+
import { type Client } from "viem";
|
|
2
|
+
import type { StateBlock } from "../../params.js";
|
|
3
3
|
import type { ExecutionPlan } from "../plan/plan-execution.js";
|
|
4
4
|
import type { SimulationExecution } from "./parse-response.js";
|
|
5
|
-
import type { PinnedBlock } from "./resolve-pinned-block.js";
|
|
6
5
|
/** The JSON-RPC methods this boundary calls. */
|
|
7
|
-
type RpcLabel = "eth_getBlock" | "
|
|
6
|
+
type RpcLabel = "eth_getBlock" | "eth_simulateV1";
|
|
8
7
|
/** Run one RPC call; anything thrown becomes a typed boundary error. @internal */
|
|
9
8
|
export declare const rpc: <T>(label: RpcLabel, call: () => Promise<T>) => Promise<T>;
|
|
10
|
-
/** Validate the endpoint identity through the same URL-safe RPC boundary. @internal */
|
|
11
|
-
export declare function assertEndpointChain(params: {
|
|
12
|
-
readonly client: Pick<PublicClient, "getChainId">;
|
|
13
|
-
readonly chainId: number;
|
|
14
|
-
readonly mode: SimulationMode;
|
|
15
|
-
readonly blockNumber?: bigint;
|
|
16
|
-
}): Promise<void>;
|
|
17
9
|
/**
|
|
18
10
|
* Execute an {@link ExecutionPlan} through a single `eth_simulateV1` call and
|
|
19
|
-
* collect the pinned execution.
|
|
11
|
+
* collect the pinned execution. This is the boundary's only RPC request.
|
|
20
12
|
*
|
|
21
|
-
* The caller
|
|
22
|
-
* block before this boundary. This function uses that same block for the
|
|
23
|
-
* simulation and reorg check under the shared abort/timeout budget.
|
|
24
|
-
*
|
|
25
|
-
* The boundary performs these steps:
|
|
13
|
+
* The caller resolves (or supplies) the state block before this boundary.
|
|
26
14
|
*
|
|
27
15
|
* 1. **`eth_simulateV1`** — one `blockStateCalls` entry carrying the planned
|
|
28
|
-
* calls with their per-call `from`, `traceTransfers: true`
|
|
29
|
-
* synthesizes native-ETH moves as transfer logs, and `validation: false`
|
|
16
|
+
* calls with their per-call `from` and `chainId`, `traceTransfers: true`
|
|
17
|
+
* so the node synthesizes native-ETH moves as transfer logs, and `validation: false`
|
|
30
18
|
* (`true` on Monad, whose nodes reject `false` and charge no gas in the
|
|
31
19
|
* simulated block). Validation-off means gas is not charged, which is how
|
|
32
20
|
* gas is separated from economic effects. When `blockGasLimit` is set,
|
|
@@ -35,32 +23,31 @@ export declare function assertEndpointChain(params: {
|
|
|
35
23
|
* **No balance override is
|
|
36
24
|
* applied** — `value` transfers are funded by the sender's real native
|
|
37
25
|
* balance.
|
|
38
|
-
* 2. **Response validation** — the
|
|
39
|
-
* re-fetch so revert/mismatch evidence already in hand surfaces instead
|
|
40
|
-
* of being downgraded to a bypassable {@link ExternalServiceError} by a
|
|
41
|
-
* failing re-fetch. The simulated block must be exactly
|
|
26
|
+
* 2. **Response validation** — the simulated block must be exactly
|
|
42
27
|
* `stateBlockNumber` or `stateBlockNumber + 1`: geth-style nodes report
|
|
43
28
|
* the former's successor while Anvil reports the pinned block itself.
|
|
44
29
|
* The result records whatever the node returns; consumers must read
|
|
45
30
|
* {@link ExecutionBlock.blockNumber} and never assume +1.
|
|
46
|
-
* 3. **Reorg check** — the pinned state block is re-fetched last to detect a reorg
|
|
47
|
-
* that swapped its hash mid-flight (`InvalidSimulationResponseError`).
|
|
48
31
|
*
|
|
49
32
|
* The endpoint must support `eth_simulateV1` with per-call `from`; there is
|
|
50
33
|
* no fallback backend.
|
|
51
34
|
*
|
|
52
|
-
* @param params - Shared simulation client, execution plan
|
|
53
|
-
* state block, the `validation` flag to
|
|
54
|
-
* block gas limit, and whether to check the
|
|
35
|
+
* @param params - Shared simulation client, execution plan (its request
|
|
36
|
+
* `chainId` is sent on every call), state block, the `validation` flag to
|
|
37
|
+
* send, the optional simulated block gas limit, and whether to check the
|
|
38
|
+
* successor `parentHash`.
|
|
55
39
|
* @returns Deep-frozen {@link SimulationExecution} — per-transaction call
|
|
56
40
|
* results and the resolved {@link ExecutionBlock}.
|
|
57
41
|
* @throws {ExternalServiceError} For transport failures, timeouts,
|
|
58
42
|
* or malformed JSON-RPC envelopes.
|
|
59
43
|
* @throws {InvalidSimulationResponseError} For a response that cannot be
|
|
60
|
-
* trusted (bad shape, call-count mismatch, block
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
44
|
+
* trusted (bad shape, call-count mismatch, block other than the state block
|
|
45
|
+
* or its successor, a block timestamp earlier than the state block's, or a
|
|
46
|
+
* per-call result that fails normalization).
|
|
47
|
+
* @throws {InvalidChainIdError} When the node rejects the request `chainId`
|
|
48
|
+
* because it serves another chain (a subclass of
|
|
49
|
+
* `InvalidSimulationResponseError` sharing its `name`/`code`;
|
|
50
|
+
* `instanceof InvalidChainIdError` tells them apart).
|
|
64
51
|
* @throws {MissingVerificationEvidenceError} When a planned state read fails.
|
|
65
52
|
* @throws {SimulationRevertedError} When a preparation or user transaction
|
|
66
53
|
* reverts or the node reports a bundle-level revert (code 3 / insufficient
|
|
@@ -68,9 +55,9 @@ export declare function assertEndpointChain(params: {
|
|
|
68
55
|
* @internal
|
|
69
56
|
*/
|
|
70
57
|
export declare function executePlan(params: {
|
|
71
|
-
client: Client
|
|
58
|
+
client: Client;
|
|
72
59
|
plan: ExecutionPlan;
|
|
73
|
-
stateBlock:
|
|
60
|
+
stateBlock: StateBlock;
|
|
74
61
|
validation: boolean;
|
|
75
62
|
blockGasLimit?: bigint | undefined;
|
|
76
63
|
parentHashCheck: boolean;
|