@seljs/runtime 1.3.0-beta.3 → 1.3.0-beta.33
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/dist/analysis/dependency-analyzer.cjs +3 -3
- package/dist/analysis/dependency-analyzer.mjs +4 -4
- package/dist/analysis/round-planner.cjs +3 -3
- package/dist/analysis/round-planner.mjs +4 -4
- package/dist/environment/client.d.cts +1 -1
- package/dist/environment/client.d.mts +1 -1
- package/dist/environment/contract-caller.cjs +1 -1
- package/dist/environment/contract-caller.mjs +2 -2
- package/dist/environment/environment.cjs +25 -15
- package/dist/environment/environment.d.cts +15 -4
- package/dist/environment/environment.d.mts +15 -4
- package/dist/environment/environment.mjs +26 -15
- package/dist/environment/error-wrapper.cjs +71 -13
- package/dist/environment/error-wrapper.mjs +71 -12
- package/dist/environment/types.d.cts +1 -1
- package/dist/environment/types.d.mts +1 -1
- package/dist/errors/errors.cjs +102 -33
- package/dist/errors/errors.d.cts +98 -31
- package/dist/errors/errors.d.mts +98 -31
- package/dist/errors/errors.mjs +96 -31
- package/dist/errors/index.d.mts +1 -1
- package/dist/execution/decode-revert.cjs +67 -0
- package/dist/execution/decode-revert.mjs +66 -0
- package/dist/execution/multicall-batcher.cjs +3 -3
- package/dist/execution/multicall-batcher.mjs +4 -4
- package/dist/execution/round-executor.cjs +18 -10
- package/dist/execution/round-executor.mjs +19 -11
- package/dist/execution/types.d.cts +37 -11
- package/dist/execution/types.d.mts +37 -11
- package/dist/index.cjs +11 -6
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +8 -7
|
@@ -5,7 +5,7 @@ const debug = require("../debug.cjs").createLogger("analysis:dependency");
|
|
|
5
5
|
/**
|
|
6
6
|
* Detects circular dependencies in the graph using DFS with a recursion stack.
|
|
7
7
|
*
|
|
8
|
-
* @throws {@link
|
|
8
|
+
* @throws {@link SELCircularDependencyError} If a cycle is found, including the call IDs forming the cycle
|
|
9
9
|
*/
|
|
10
10
|
const detectCycles = (nodes, edges) => {
|
|
11
11
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -14,7 +14,7 @@ const detectCycles = (nodes, edges) => {
|
|
|
14
14
|
if (inStack.has(nodeId)) {
|
|
15
15
|
const cycleStart = path.indexOf(nodeId);
|
|
16
16
|
const cycle = path.slice(cycleStart);
|
|
17
|
-
throw new require_errors.
|
|
17
|
+
throw new require_errors.SELCircularDependencyError(`Circular dependency detected: ${cycle.join(" -> ")} -> ${nodeId}`, { callIds: cycle });
|
|
18
18
|
}
|
|
19
19
|
if (visited.has(nodeId)) return;
|
|
20
20
|
visited.add(nodeId);
|
|
@@ -35,7 +35,7 @@ const detectCycles = (nodes, edges) => {
|
|
|
35
35
|
*
|
|
36
36
|
* @param calls - Array of collected contract calls from AST traversal
|
|
37
37
|
* @returns A dependency graph with nodes and directed edges
|
|
38
|
-
* @throws {@link
|
|
38
|
+
* @throws {@link SELCircularDependencyError} If a circular dependency is detected between calls
|
|
39
39
|
*/
|
|
40
40
|
const analyzeDependencies = (calls) => {
|
|
41
41
|
const nodes = /* @__PURE__ */ new Map();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SELCircularDependencyError } from "../errors/errors.mjs";
|
|
2
2
|
import "../errors/index.mjs";
|
|
3
3
|
import { createLogger } from "../debug.mjs";
|
|
4
4
|
//#region src/analysis/dependency-analyzer.ts
|
|
@@ -6,7 +6,7 @@ const debug = createLogger("analysis:dependency");
|
|
|
6
6
|
/**
|
|
7
7
|
* Detects circular dependencies in the graph using DFS with a recursion stack.
|
|
8
8
|
*
|
|
9
|
-
* @throws {@link
|
|
9
|
+
* @throws {@link SELCircularDependencyError} If a cycle is found, including the call IDs forming the cycle
|
|
10
10
|
*/
|
|
11
11
|
const detectCycles = (nodes, edges) => {
|
|
12
12
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -15,7 +15,7 @@ const detectCycles = (nodes, edges) => {
|
|
|
15
15
|
if (inStack.has(nodeId)) {
|
|
16
16
|
const cycleStart = path.indexOf(nodeId);
|
|
17
17
|
const cycle = path.slice(cycleStart);
|
|
18
|
-
throw new
|
|
18
|
+
throw new SELCircularDependencyError(`Circular dependency detected: ${cycle.join(" -> ")} -> ${nodeId}`, { callIds: cycle });
|
|
19
19
|
}
|
|
20
20
|
if (visited.has(nodeId)) return;
|
|
21
21
|
visited.add(nodeId);
|
|
@@ -36,7 +36,7 @@ const detectCycles = (nodes, edges) => {
|
|
|
36
36
|
*
|
|
37
37
|
* @param calls - Array of collected contract calls from AST traversal
|
|
38
38
|
* @returns A dependency graph with nodes and directed edges
|
|
39
|
-
* @throws {@link
|
|
39
|
+
* @throws {@link SELCircularDependencyError} If a circular dependency is detected between calls
|
|
40
40
|
*/
|
|
41
41
|
const analyzeDependencies = (calls) => {
|
|
42
42
|
const nodes = /* @__PURE__ */ new Map();
|
|
@@ -12,13 +12,13 @@ const debug = require("../debug.cjs").createLogger("analysis:plan");
|
|
|
12
12
|
* @param graph Dependency graph representing calls and their relationships.
|
|
13
13
|
* @param limits Optional limits for maximum rounds and total calls.
|
|
14
14
|
* @returns Execution plan containing planned rounds, total calls, and maximum depth.
|
|
15
|
-
* @throws
|
|
15
|
+
* @throws {@link SELExecutionLimitError} If total calls exceed maxCalls or rounds exceed maxRounds.
|
|
16
16
|
*/
|
|
17
17
|
const planRounds = (graph, limits = {}) => {
|
|
18
18
|
const maxRounds = limits.maxRounds ?? 10;
|
|
19
19
|
const maxCalls = limits.maxCalls ?? 100;
|
|
20
20
|
const totalCalls = graph.nodes.size;
|
|
21
|
-
if (totalCalls > maxCalls) throw new require_errors.
|
|
21
|
+
if (totalCalls > maxCalls) throw new require_errors.SELExecutionLimitError(`Execution limit exceeded: ${String(totalCalls)} calls exceeds maxCalls (${String(maxCalls)})`, {
|
|
22
22
|
limitType: "maxCalls",
|
|
23
23
|
limit: maxCalls,
|
|
24
24
|
actual: totalCalls
|
|
@@ -36,7 +36,7 @@ const planRounds = (graph, limits = {}) => {
|
|
|
36
36
|
const readyNodes = [];
|
|
37
37
|
for (const [nodeId, degree] of inDegree) if (degree === 0 && !processed.has(nodeId)) readyNodes.push(nodeId);
|
|
38
38
|
if (readyNodes.length === 0) break;
|
|
39
|
-
if (rounds.length >= maxRounds) throw new require_errors.
|
|
39
|
+
if (rounds.length >= maxRounds) throw new require_errors.SELExecutionLimitError(`Execution limit exceeded: requires more than maxRounds (${String(maxRounds)}) rounds`, {
|
|
40
40
|
limitType: "maxRounds",
|
|
41
41
|
limit: maxRounds,
|
|
42
42
|
actual: rounds.length + 1
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SELExecutionLimitError } from "../errors/errors.mjs";
|
|
2
2
|
import "../errors/index.mjs";
|
|
3
3
|
import { createLogger } from "../debug.mjs";
|
|
4
4
|
//#region src/analysis/round-planner.ts
|
|
@@ -13,13 +13,13 @@ const debug = createLogger("analysis:plan");
|
|
|
13
13
|
* @param graph Dependency graph representing calls and their relationships.
|
|
14
14
|
* @param limits Optional limits for maximum rounds and total calls.
|
|
15
15
|
* @returns Execution plan containing planned rounds, total calls, and maximum depth.
|
|
16
|
-
* @throws
|
|
16
|
+
* @throws {@link SELExecutionLimitError} If total calls exceed maxCalls or rounds exceed maxRounds.
|
|
17
17
|
*/
|
|
18
18
|
const planRounds = (graph, limits = {}) => {
|
|
19
19
|
const maxRounds = limits.maxRounds ?? 10;
|
|
20
20
|
const maxCalls = limits.maxCalls ?? 100;
|
|
21
21
|
const totalCalls = graph.nodes.size;
|
|
22
|
-
if (totalCalls > maxCalls) throw new
|
|
22
|
+
if (totalCalls > maxCalls) throw new SELExecutionLimitError(`Execution limit exceeded: ${String(totalCalls)} calls exceeds maxCalls (${String(maxCalls)})`, {
|
|
23
23
|
limitType: "maxCalls",
|
|
24
24
|
limit: maxCalls,
|
|
25
25
|
actual: totalCalls
|
|
@@ -37,7 +37,7 @@ const planRounds = (graph, limits = {}) => {
|
|
|
37
37
|
const readyNodes = [];
|
|
38
38
|
for (const [nodeId, degree] of inDegree) if (degree === 0 && !processed.has(nodeId)) readyNodes.push(nodeId);
|
|
39
39
|
if (readyNodes.length === 0) break;
|
|
40
|
-
if (rounds.length >= maxRounds) throw new
|
|
40
|
+
if (rounds.length >= maxRounds) throw new SELExecutionLimitError(`Execution limit exceeded: requires more than maxRounds (${String(maxRounds)}) rounds`, {
|
|
41
41
|
limitType: "maxRounds",
|
|
42
42
|
limit: maxRounds,
|
|
43
43
|
actual: rounds.length + 1
|
|
@@ -18,7 +18,7 @@ var CallCounter = class {
|
|
|
18
18
|
}
|
|
19
19
|
increment(contractName, methodName) {
|
|
20
20
|
this.count++;
|
|
21
|
-
if (this.count > this.maxCalls) throw new require_errors.
|
|
21
|
+
if (this.count > this.maxCalls) throw new require_errors.SELExecutionLimitError(`Execution limit exceeded: ${String(this.count)} calls exceeds maxCalls (${String(this.maxCalls)})`, {
|
|
22
22
|
limitType: "maxCalls",
|
|
23
23
|
limit: this.maxCalls,
|
|
24
24
|
actual: this.count
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SELContractError, SELExecutionLimitError } from "../errors/errors.mjs";
|
|
2
2
|
import "../errors/index.mjs";
|
|
3
3
|
import { createReplayCallId } from "./replay-cache.mjs";
|
|
4
4
|
import { createLogger } from "../debug.mjs";
|
|
@@ -17,7 +17,7 @@ var CallCounter = class {
|
|
|
17
17
|
}
|
|
18
18
|
increment(contractName, methodName) {
|
|
19
19
|
this.count++;
|
|
20
|
-
if (this.count > this.maxCalls) throw new
|
|
20
|
+
if (this.count > this.maxCalls) throw new SELExecutionLimitError(`Execution limit exceeded: ${String(this.count)} calls exceeds maxCalls (${String(this.maxCalls)})`, {
|
|
21
21
|
limitType: "maxCalls",
|
|
22
22
|
limit: this.maxCalls,
|
|
23
23
|
actual: this.count
|
|
@@ -13,7 +13,6 @@ const require_dependency_analyzer = require("../analysis/dependency-analyzer.cjs
|
|
|
13
13
|
const require_round_planner = require("../analysis/round-planner.cjs");
|
|
14
14
|
const require_multi_round_executor = require("../execution/multi-round-executor.cjs");
|
|
15
15
|
let _seljs_checker = require("@seljs/checker");
|
|
16
|
-
let _seljs_common = require("@seljs/common");
|
|
17
16
|
//#region src/environment/environment.ts
|
|
18
17
|
/**
|
|
19
18
|
* Core SEL runtime that bridges CEL expression evaluation with EVM contract reads.
|
|
@@ -106,7 +105,9 @@ var SELRuntime = class {
|
|
|
106
105
|
*
|
|
107
106
|
* @param expression - A CEL expression string to type-check
|
|
108
107
|
* @returns The type-check result containing validity, inferred type, and any errors
|
|
109
|
-
* @throws {@link
|
|
108
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
109
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
110
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
110
111
|
*/
|
|
111
112
|
check(expression) {
|
|
112
113
|
try {
|
|
@@ -130,10 +131,19 @@ var SELRuntime = class {
|
|
|
130
131
|
* @param context Variable bindings for evaluation
|
|
131
132
|
* @param options Optional client override
|
|
132
133
|
* @returns An {@link EvaluateResult} containing the value and optional execution metadata
|
|
133
|
-
* @throws {@link SELParseError} If the expression
|
|
134
|
-
* @throws {@link
|
|
135
|
-
* @throws {@link
|
|
134
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
135
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
136
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
137
|
+
* @throws {@link SELConfigError} If an ABI / codec registry entry is missing
|
|
138
|
+
* @throws {@link SELContractError} If a contract call fails (non-revert)
|
|
139
|
+
* @throws {@link SELContractRevertError} If a contract call reverted
|
|
140
|
+
* @throws {@link SELMulticallBatchError} If the Multicall3 batch itself failed
|
|
141
|
+
* @throws {@link SELProviderTransportError} If the JSON-RPC transport failed
|
|
142
|
+
* @throws {@link SELProviderRpcError} If the node returned a JSON-RPC error
|
|
143
|
+
* @throws {@link SELExecutionLimitError} If maxRounds / maxCalls were exceeded
|
|
144
|
+
* @throws {@link SELCircularDependencyError} If the call graph has a cycle
|
|
136
145
|
* @throws {@link SELEvaluationError} If CEL evaluation fails
|
|
146
|
+
* @throws {@link SELTypeConversionError} If a Solidity type wrapper rejected an input
|
|
137
147
|
*/
|
|
138
148
|
async evaluate(expression, context, options) {
|
|
139
149
|
let release;
|
|
@@ -149,23 +159,21 @@ var SELRuntime = class {
|
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
161
|
planExecution(expression, evaluationContext) {
|
|
162
|
+
const checkResult = this.checker.check(expression);
|
|
163
|
+
if (!checkResult.valid) throw new require_errors.SELLintError(checkResult.diagnostics.filter((d) => d.severity === "error"));
|
|
152
164
|
const parseResult = this.env.parse(expression);
|
|
153
165
|
const collectedCalls = require_call_collector.collectCalls(parseResult.ast, { get: (name) => this.findContract(name) });
|
|
154
166
|
debug("evaluate: collected %d calls", collectedCalls.length);
|
|
155
167
|
const normalizedContext = Object.keys(evaluationContext).length ? require_context.normalizeContextForEvaluation(evaluationContext, this.variableTypes, this.codecRegistry) : void 0;
|
|
156
168
|
const executionVariables = Object.keys(evaluationContext).length ? evaluationContext : {};
|
|
157
|
-
const
|
|
158
|
-
if (!typeCheckResult.valid) throw new _seljs_common.SELTypeError(typeCheckResult.error?.message ?? "Type check failed", { cause: typeCheckResult.error });
|
|
159
|
-
const lintResult = this.checker.check(expression);
|
|
160
|
-
const lintErrors = lintResult.diagnostics.filter((d) => d.severity === "error");
|
|
161
|
-
if (lintErrors.length > 0) throw new require_errors.SELLintError(lintErrors);
|
|
169
|
+
const diagnostics = checkResult.diagnostics.filter((d) => d.severity !== "error");
|
|
162
170
|
return {
|
|
163
171
|
parseResult,
|
|
164
172
|
collectedCalls,
|
|
165
173
|
normalizedContext,
|
|
166
174
|
executionVariables,
|
|
167
|
-
|
|
168
|
-
diagnostics
|
|
175
|
+
resolvedType: checkResult.type,
|
|
176
|
+
diagnostics
|
|
169
177
|
};
|
|
170
178
|
}
|
|
171
179
|
async executeContractCalls(collectedCalls, executionVariables, resolvedClient) {
|
|
@@ -179,8 +187,9 @@ var SELRuntime = class {
|
|
|
179
187
|
try {
|
|
180
188
|
executionResult = await executor.execute(plan, executionVariables, await require_contract_caller.resolveExecutionBlockNumber(resolvedClient));
|
|
181
189
|
} catch (error) {
|
|
190
|
+
if (error instanceof require_errors.SELContractRevertError) throw error;
|
|
182
191
|
let failedCall = collectedCalls[0];
|
|
183
|
-
if (error instanceof require_errors.
|
|
192
|
+
if (error instanceof require_errors.SELMulticallBatchError) {
|
|
184
193
|
if (error.contractName && error.methodName) {
|
|
185
194
|
const contractName = error.contractName;
|
|
186
195
|
const methodName = error.methodName;
|
|
@@ -216,7 +225,7 @@ var SELRuntime = class {
|
|
|
216
225
|
const resolvedClient = options?.client ?? this.client;
|
|
217
226
|
if (resolvedClient && resolvedClient !== this.client) require_client.validateClient(resolvedClient);
|
|
218
227
|
try {
|
|
219
|
-
const { parseResult, collectedCalls, normalizedContext, executionVariables,
|
|
228
|
+
const { parseResult, collectedCalls, normalizedContext, executionVariables, resolvedType, diagnostics } = this.planExecution(expression, context ?? {});
|
|
220
229
|
let executionMeta;
|
|
221
230
|
let executionCache;
|
|
222
231
|
if (collectedCalls.length > 0) {
|
|
@@ -240,12 +249,13 @@ var SELRuntime = class {
|
|
|
240
249
|
this.currentClient = void 0;
|
|
241
250
|
this.currentCallCounter = void 0;
|
|
242
251
|
}
|
|
243
|
-
const value =
|
|
252
|
+
const value = resolvedType ? this.codecRegistry.encode(resolvedType, result) : result;
|
|
244
253
|
debug("evaluate: result type=%s", typeof value);
|
|
245
254
|
const evalResult = executionMeta ? {
|
|
246
255
|
value,
|
|
247
256
|
meta: executionMeta
|
|
248
257
|
} : { value };
|
|
258
|
+
if (resolvedType) evalResult.type = resolvedType;
|
|
249
259
|
if (diagnostics.length > 0) evalResult.diagnostics = diagnostics;
|
|
250
260
|
return evalResult;
|
|
251
261
|
} catch (error) {
|
|
@@ -52,7 +52,9 @@ declare class SELRuntime {
|
|
|
52
52
|
*
|
|
53
53
|
* @param expression - A CEL expression string to type-check
|
|
54
54
|
* @returns The type-check result containing validity, inferred type, and any errors
|
|
55
|
-
* @throws {@link
|
|
55
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
56
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
57
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
56
58
|
*/
|
|
57
59
|
check(expression: string): TypeCheckResult;
|
|
58
60
|
/**
|
|
@@ -70,10 +72,19 @@ declare class SELRuntime {
|
|
|
70
72
|
* @param context Variable bindings for evaluation
|
|
71
73
|
* @param options Optional client override
|
|
72
74
|
* @returns An {@link EvaluateResult} containing the value and optional execution metadata
|
|
73
|
-
* @throws {@link SELParseError} If the expression
|
|
74
|
-
* @throws {@link
|
|
75
|
-
* @throws {@link
|
|
75
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
76
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
77
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
78
|
+
* @throws {@link SELConfigError} If an ABI / codec registry entry is missing
|
|
79
|
+
* @throws {@link SELContractError} If a contract call fails (non-revert)
|
|
80
|
+
* @throws {@link SELContractRevertError} If a contract call reverted
|
|
81
|
+
* @throws {@link SELMulticallBatchError} If the Multicall3 batch itself failed
|
|
82
|
+
* @throws {@link SELProviderTransportError} If the JSON-RPC transport failed
|
|
83
|
+
* @throws {@link SELProviderRpcError} If the node returned a JSON-RPC error
|
|
84
|
+
* @throws {@link SELExecutionLimitError} If maxRounds / maxCalls were exceeded
|
|
85
|
+
* @throws {@link SELCircularDependencyError} If the call graph has a cycle
|
|
76
86
|
* @throws {@link SELEvaluationError} If CEL evaluation fails
|
|
87
|
+
* @throws {@link SELTypeConversionError} If a Solidity type wrapper rejected an input
|
|
77
88
|
*/
|
|
78
89
|
evaluate<T = unknown>(expression: string, context?: Record<string, unknown>, options?: EvaluateOptions): Promise<EvaluateResult<T>>;
|
|
79
90
|
private planExecution;
|
|
@@ -52,7 +52,9 @@ declare class SELRuntime {
|
|
|
52
52
|
*
|
|
53
53
|
* @param expression - A CEL expression string to type-check
|
|
54
54
|
* @returns The type-check result containing validity, inferred type, and any errors
|
|
55
|
-
* @throws {@link
|
|
55
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
56
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
57
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
56
58
|
*/
|
|
57
59
|
check(expression: string): TypeCheckResult;
|
|
58
60
|
/**
|
|
@@ -70,10 +72,19 @@ declare class SELRuntime {
|
|
|
70
72
|
* @param context Variable bindings for evaluation
|
|
71
73
|
* @param options Optional client override
|
|
72
74
|
* @returns An {@link EvaluateResult} containing the value and optional execution metadata
|
|
73
|
-
* @throws {@link SELParseError} If the expression
|
|
74
|
-
* @throws {@link
|
|
75
|
-
* @throws {@link
|
|
75
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
76
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
77
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
78
|
+
* @throws {@link SELConfigError} If an ABI / codec registry entry is missing
|
|
79
|
+
* @throws {@link SELContractError} If a contract call fails (non-revert)
|
|
80
|
+
* @throws {@link SELContractRevertError} If a contract call reverted
|
|
81
|
+
* @throws {@link SELMulticallBatchError} If the Multicall3 batch itself failed
|
|
82
|
+
* @throws {@link SELProviderTransportError} If the JSON-RPC transport failed
|
|
83
|
+
* @throws {@link SELProviderRpcError} If the node returned a JSON-RPC error
|
|
84
|
+
* @throws {@link SELExecutionLimitError} If maxRounds / maxCalls were exceeded
|
|
85
|
+
* @throws {@link SELCircularDependencyError} If the call graph has a cycle
|
|
76
86
|
* @throws {@link SELEvaluationError} If CEL evaluation fails
|
|
87
|
+
* @throws {@link SELTypeConversionError} If a Solidity type wrapper rejected an input
|
|
77
88
|
*/
|
|
78
89
|
evaluate<T = unknown>(expression: string, context?: Record<string, unknown>, options?: EvaluateOptions): Promise<EvaluateResult<T>>;
|
|
79
90
|
private planExecution;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getBuiltinContracts } from "./builtin-contracts.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { SELContractError, SELContractRevertError, SELLintError, SELMulticallBatchError } from "../errors/errors.mjs";
|
|
3
3
|
import "../errors/index.mjs";
|
|
4
4
|
import { validateClient } from "./client.mjs";
|
|
5
5
|
import { normalizeContextForEvaluation } from "./context.mjs";
|
|
@@ -104,7 +104,9 @@ var SELRuntime = class {
|
|
|
104
104
|
*
|
|
105
105
|
* @param expression - A CEL expression string to type-check
|
|
106
106
|
* @returns The type-check result containing validity, inferred type, and any errors
|
|
107
|
-
* @throws {@link
|
|
107
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
108
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
109
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
108
110
|
*/
|
|
109
111
|
check(expression) {
|
|
110
112
|
try {
|
|
@@ -128,10 +130,19 @@ var SELRuntime = class {
|
|
|
128
130
|
* @param context Variable bindings for evaluation
|
|
129
131
|
* @param options Optional client override
|
|
130
132
|
* @returns An {@link EvaluateResult} containing the value and optional execution metadata
|
|
131
|
-
* @throws {@link SELParseError} If the expression
|
|
132
|
-
* @throws {@link
|
|
133
|
-
* @throws {@link
|
|
133
|
+
* @throws {@link SELParseError} If the expression cannot be parsed
|
|
134
|
+
* @throws {@link SELTypeCheckError} If the CEL type-checker rejects the expression
|
|
135
|
+
* @throws {@link SELLintError} If lint rules with error severity match
|
|
136
|
+
* @throws {@link SELConfigError} If an ABI / codec registry entry is missing
|
|
137
|
+
* @throws {@link SELContractError} If a contract call fails (non-revert)
|
|
138
|
+
* @throws {@link SELContractRevertError} If a contract call reverted
|
|
139
|
+
* @throws {@link SELMulticallBatchError} If the Multicall3 batch itself failed
|
|
140
|
+
* @throws {@link SELProviderTransportError} If the JSON-RPC transport failed
|
|
141
|
+
* @throws {@link SELProviderRpcError} If the node returned a JSON-RPC error
|
|
142
|
+
* @throws {@link SELExecutionLimitError} If maxRounds / maxCalls were exceeded
|
|
143
|
+
* @throws {@link SELCircularDependencyError} If the call graph has a cycle
|
|
134
144
|
* @throws {@link SELEvaluationError} If CEL evaluation fails
|
|
145
|
+
* @throws {@link SELTypeConversionError} If a Solidity type wrapper rejected an input
|
|
135
146
|
*/
|
|
136
147
|
async evaluate(expression, context, options) {
|
|
137
148
|
let release;
|
|
@@ -147,23 +158,21 @@ var SELRuntime = class {
|
|
|
147
158
|
}
|
|
148
159
|
}
|
|
149
160
|
planExecution(expression, evaluationContext) {
|
|
161
|
+
const checkResult = this.checker.check(expression);
|
|
162
|
+
if (!checkResult.valid) throw new SELLintError(checkResult.diagnostics.filter((d) => d.severity === "error"));
|
|
150
163
|
const parseResult = this.env.parse(expression);
|
|
151
164
|
const collectedCalls = collectCalls(parseResult.ast, { get: (name) => this.findContract(name) });
|
|
152
165
|
debug("evaluate: collected %d calls", collectedCalls.length);
|
|
153
166
|
const normalizedContext = Object.keys(evaluationContext).length ? normalizeContextForEvaluation(evaluationContext, this.variableTypes, this.codecRegistry) : void 0;
|
|
154
167
|
const executionVariables = Object.keys(evaluationContext).length ? evaluationContext : {};
|
|
155
|
-
const
|
|
156
|
-
if (!typeCheckResult.valid) throw new SELTypeError(typeCheckResult.error?.message ?? "Type check failed", { cause: typeCheckResult.error });
|
|
157
|
-
const lintResult = this.checker.check(expression);
|
|
158
|
-
const lintErrors = lintResult.diagnostics.filter((d) => d.severity === "error");
|
|
159
|
-
if (lintErrors.length > 0) throw new SELLintError(lintErrors);
|
|
168
|
+
const diagnostics = checkResult.diagnostics.filter((d) => d.severity !== "error");
|
|
160
169
|
return {
|
|
161
170
|
parseResult,
|
|
162
171
|
collectedCalls,
|
|
163
172
|
normalizedContext,
|
|
164
173
|
executionVariables,
|
|
165
|
-
|
|
166
|
-
diagnostics
|
|
174
|
+
resolvedType: checkResult.type,
|
|
175
|
+
diagnostics
|
|
167
176
|
};
|
|
168
177
|
}
|
|
169
178
|
async executeContractCalls(collectedCalls, executionVariables, resolvedClient) {
|
|
@@ -177,8 +186,9 @@ var SELRuntime = class {
|
|
|
177
186
|
try {
|
|
178
187
|
executionResult = await executor.execute(plan, executionVariables, await resolveExecutionBlockNumber(resolvedClient));
|
|
179
188
|
} catch (error) {
|
|
189
|
+
if (error instanceof SELContractRevertError) throw error;
|
|
180
190
|
let failedCall = collectedCalls[0];
|
|
181
|
-
if (error instanceof
|
|
191
|
+
if (error instanceof SELMulticallBatchError) {
|
|
182
192
|
if (error.contractName && error.methodName) {
|
|
183
193
|
const contractName = error.contractName;
|
|
184
194
|
const methodName = error.methodName;
|
|
@@ -214,7 +224,7 @@ var SELRuntime = class {
|
|
|
214
224
|
const resolvedClient = options?.client ?? this.client;
|
|
215
225
|
if (resolvedClient && resolvedClient !== this.client) validateClient(resolvedClient);
|
|
216
226
|
try {
|
|
217
|
-
const { parseResult, collectedCalls, normalizedContext, executionVariables,
|
|
227
|
+
const { parseResult, collectedCalls, normalizedContext, executionVariables, resolvedType, diagnostics } = this.planExecution(expression, context ?? {});
|
|
218
228
|
let executionMeta;
|
|
219
229
|
let executionCache;
|
|
220
230
|
if (collectedCalls.length > 0) {
|
|
@@ -238,12 +248,13 @@ var SELRuntime = class {
|
|
|
238
248
|
this.currentClient = void 0;
|
|
239
249
|
this.currentCallCounter = void 0;
|
|
240
250
|
}
|
|
241
|
-
const value =
|
|
251
|
+
const value = resolvedType ? this.codecRegistry.encode(resolvedType, result) : result;
|
|
242
252
|
debug("evaluate: result type=%s", typeof value);
|
|
243
253
|
const evalResult = executionMeta ? {
|
|
244
254
|
value,
|
|
245
255
|
meta: executionMeta
|
|
246
256
|
} : { value };
|
|
257
|
+
if (resolvedType) evalResult.type = resolvedType;
|
|
247
258
|
if (diagnostics.length > 0) evalResult.diagnostics = diagnostics;
|
|
248
259
|
return evalResult;
|
|
249
260
|
} catch (error) {
|
|
@@ -1,29 +1,87 @@
|
|
|
1
1
|
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
const require_errors = require("../errors/errors.cjs");
|
|
3
3
|
require("../errors/index.cjs");
|
|
4
|
-
let _marcbachmann_cel_js = require("@marcbachmann/cel-js");
|
|
5
4
|
let _seljs_common = require("@seljs/common");
|
|
5
|
+
let _marcbachmann_cel_js = require("@marcbachmann/cel-js");
|
|
6
|
+
let viem = require("viem");
|
|
6
7
|
//#region src/environment/error-wrapper.ts
|
|
7
8
|
/**
|
|
9
|
+
* Compose a human-readable message from a viem error. Uses `shortMessage`
|
|
10
|
+
* when present and appends any `metaMessages` lines so context like the
|
|
11
|
+
* contract address, function call, and chain is not lost.
|
|
12
|
+
*/
|
|
13
|
+
const extractViemMessage = (error) => {
|
|
14
|
+
const shortMessage = error.shortMessage || error.message;
|
|
15
|
+
const meta = error.metaMessages;
|
|
16
|
+
if (!meta || meta.length === 0) return shortMessage;
|
|
17
|
+
return `${shortMessage}\n${meta.join("\n")}`;
|
|
18
|
+
};
|
|
19
|
+
const wrapContractRevert = (error) => {
|
|
20
|
+
const decoded = error.data ? {
|
|
21
|
+
name: error.data.errorName,
|
|
22
|
+
args: error.data.args ?? []
|
|
23
|
+
} : void 0;
|
|
24
|
+
return new require_errors.SELContractRevertError(extractViemMessage(error), {
|
|
25
|
+
cause: error,
|
|
26
|
+
revertReason: error.reason,
|
|
27
|
+
revertData: error.raw,
|
|
28
|
+
decodedError: decoded
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const wrapViemError = (error) => {
|
|
32
|
+
const nestedRevert = error.walk((e) => e instanceof viem.ContractFunctionRevertedError);
|
|
33
|
+
if (nestedRevert) return wrapContractRevert(nestedRevert);
|
|
34
|
+
if (error instanceof viem.ContractFunctionRevertedError) return wrapContractRevert(error);
|
|
35
|
+
if (error instanceof viem.ContractFunctionExecutionError) return new require_errors.SELContractError(extractViemMessage(error), {
|
|
36
|
+
cause: error,
|
|
37
|
+
contractName: error.contractAddress,
|
|
38
|
+
methodName: error.functionName
|
|
39
|
+
});
|
|
40
|
+
if (error instanceof viem.AbiDecodingDataSizeInvalidError || error instanceof viem.AbiDecodingZeroDataError || error instanceof viem.AbiErrorSignatureNotFoundError) return new require_errors.SELContractError(extractViemMessage(error), { cause: error });
|
|
41
|
+
if (error instanceof viem.HttpRequestError) return new require_errors.SELProviderTransportError(extractViemMessage(error), {
|
|
42
|
+
cause: error,
|
|
43
|
+
httpStatus: error.status,
|
|
44
|
+
url: error.url,
|
|
45
|
+
body: typeof error.body === "string" ? error.body : error.body !== void 0 ? JSON.stringify(error.body) : void 0
|
|
46
|
+
});
|
|
47
|
+
if (error instanceof viem.TimeoutError || error instanceof viem.WebSocketRequestError) return new require_errors.SELProviderTransportError(extractViemMessage(error), {
|
|
48
|
+
cause: error,
|
|
49
|
+
url: error instanceof viem.WebSocketRequestError ? error.url : void 0
|
|
50
|
+
});
|
|
51
|
+
if (error instanceof viem.RpcError) {
|
|
52
|
+
const rpc = error;
|
|
53
|
+
return new require_errors.SELProviderRpcError(extractViemMessage(error), {
|
|
54
|
+
cause: error,
|
|
55
|
+
rpcCode: rpc.code,
|
|
56
|
+
rpcData: rpc.data
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return new require_errors.SELProviderError(extractViemMessage(error), { cause: error });
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
8
62
|
* Wraps an unknown error into a known SEL error type.
|
|
9
|
-
* - If the error is already a SEL error, it is returned as is.
|
|
10
|
-
* - If the error is a CEL error, it is wrapped into the corresponding SEL error type.
|
|
11
|
-
* - If the error is an unknown type, it is wrapped into a generic Error.
|
|
12
63
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
64
|
+
* - A `SELError` is returned as-is.
|
|
65
|
+
* - cel-js errors (`ParseError`, `TypeError`, `EvaluationError`) become the
|
|
66
|
+
* matching `SEL*Error` and preserve `cause`.
|
|
67
|
+
* - viem errors are routed by concrete class: reverts → `SELContractRevertError`,
|
|
68
|
+
* transport/timeout → `SELProviderTransportError`, JSON-RPC → `SELProviderRpcError`,
|
|
69
|
+
* contract execution / ABI decode → `SELContractError`, everything else → `SELProviderError`.
|
|
70
|
+
* - Any other `Error` becomes `SELEvaluationError` with the original as `cause`.
|
|
15
71
|
*/
|
|
16
72
|
const wrapError = (error) => {
|
|
17
|
-
if (error instanceof
|
|
18
|
-
if (error instanceof _marcbachmann_cel_js.ParseError) return new _seljs_common.SELParseError(error.message, { cause: error });
|
|
73
|
+
if (error instanceof _seljs_common.SELError) return error;
|
|
19
74
|
if (error instanceof _marcbachmann_cel_js.EvaluationError) {
|
|
20
75
|
const cause = error.cause;
|
|
21
|
-
if (cause instanceof
|
|
22
|
-
|
|
76
|
+
if (cause instanceof _seljs_common.SELError) return cause;
|
|
77
|
+
if (cause instanceof viem.BaseError) return wrapViemError(cause);
|
|
78
|
+
return new _seljs_common.SELEvaluationError(error.message, { cause: error });
|
|
23
79
|
}
|
|
24
|
-
if (error instanceof _marcbachmann_cel_js.
|
|
25
|
-
if (error instanceof
|
|
26
|
-
return
|
|
80
|
+
if (error instanceof _marcbachmann_cel_js.ParseError) return new _seljs_common.SELParseError(error.message, { cause: error });
|
|
81
|
+
if (error instanceof _marcbachmann_cel_js.TypeError) return new _seljs_common.SELTypeCheckError(error.message, { cause: error });
|
|
82
|
+
if (error instanceof viem.BaseError) return wrapViemError(error);
|
|
83
|
+
if (error instanceof Error) return new _seljs_common.SELEvaluationError(error.message, { cause: error });
|
|
84
|
+
return new _seljs_common.SELEvaluationError(String(error));
|
|
27
85
|
};
|
|
28
86
|
//#endregion
|
|
29
87
|
exports.wrapError = wrapError;
|
|
@@ -1,27 +1,86 @@
|
|
|
1
|
-
import { SELContractError,
|
|
1
|
+
import { SELContractError, SELContractRevertError, SELProviderError, SELProviderRpcError, SELProviderTransportError } from "../errors/errors.mjs";
|
|
2
2
|
import "../errors/index.mjs";
|
|
3
|
+
import { SELError, SELEvaluationError, SELParseError, SELTypeCheckError } from "@seljs/common";
|
|
3
4
|
import { EvaluationError, ParseError, TypeError } from "@marcbachmann/cel-js";
|
|
5
|
+
import { AbiDecodingDataSizeInvalidError, AbiDecodingZeroDataError, AbiErrorSignatureNotFoundError, BaseError, ContractFunctionExecutionError, ContractFunctionRevertedError, HttpRequestError, RpcError, TimeoutError, WebSocketRequestError } from "viem";
|
|
4
6
|
//#region src/environment/error-wrapper.ts
|
|
5
7
|
/**
|
|
8
|
+
* Compose a human-readable message from a viem error. Uses `shortMessage`
|
|
9
|
+
* when present and appends any `metaMessages` lines so context like the
|
|
10
|
+
* contract address, function call, and chain is not lost.
|
|
11
|
+
*/
|
|
12
|
+
const extractViemMessage = (error) => {
|
|
13
|
+
const shortMessage = error.shortMessage || error.message;
|
|
14
|
+
const meta = error.metaMessages;
|
|
15
|
+
if (!meta || meta.length === 0) return shortMessage;
|
|
16
|
+
return `${shortMessage}\n${meta.join("\n")}`;
|
|
17
|
+
};
|
|
18
|
+
const wrapContractRevert = (error) => {
|
|
19
|
+
const decoded = error.data ? {
|
|
20
|
+
name: error.data.errorName,
|
|
21
|
+
args: error.data.args ?? []
|
|
22
|
+
} : void 0;
|
|
23
|
+
return new SELContractRevertError(extractViemMessage(error), {
|
|
24
|
+
cause: error,
|
|
25
|
+
revertReason: error.reason,
|
|
26
|
+
revertData: error.raw,
|
|
27
|
+
decodedError: decoded
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const wrapViemError = (error) => {
|
|
31
|
+
const nestedRevert = error.walk((e) => e instanceof ContractFunctionRevertedError);
|
|
32
|
+
if (nestedRevert) return wrapContractRevert(nestedRevert);
|
|
33
|
+
if (error instanceof ContractFunctionRevertedError) return wrapContractRevert(error);
|
|
34
|
+
if (error instanceof ContractFunctionExecutionError) return new SELContractError(extractViemMessage(error), {
|
|
35
|
+
cause: error,
|
|
36
|
+
contractName: error.contractAddress,
|
|
37
|
+
methodName: error.functionName
|
|
38
|
+
});
|
|
39
|
+
if (error instanceof AbiDecodingDataSizeInvalidError || error instanceof AbiDecodingZeroDataError || error instanceof AbiErrorSignatureNotFoundError) return new SELContractError(extractViemMessage(error), { cause: error });
|
|
40
|
+
if (error instanceof HttpRequestError) return new SELProviderTransportError(extractViemMessage(error), {
|
|
41
|
+
cause: error,
|
|
42
|
+
httpStatus: error.status,
|
|
43
|
+
url: error.url,
|
|
44
|
+
body: typeof error.body === "string" ? error.body : error.body !== void 0 ? JSON.stringify(error.body) : void 0
|
|
45
|
+
});
|
|
46
|
+
if (error instanceof TimeoutError || error instanceof WebSocketRequestError) return new SELProviderTransportError(extractViemMessage(error), {
|
|
47
|
+
cause: error,
|
|
48
|
+
url: error instanceof WebSocketRequestError ? error.url : void 0
|
|
49
|
+
});
|
|
50
|
+
if (error instanceof RpcError) {
|
|
51
|
+
const rpc = error;
|
|
52
|
+
return new SELProviderRpcError(extractViemMessage(error), {
|
|
53
|
+
cause: error,
|
|
54
|
+
rpcCode: rpc.code,
|
|
55
|
+
rpcData: rpc.data
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return new SELProviderError(extractViemMessage(error), { cause: error });
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
6
61
|
* Wraps an unknown error into a known SEL error type.
|
|
7
|
-
* - If the error is already a SEL error, it is returned as is.
|
|
8
|
-
* - If the error is a CEL error, it is wrapped into the corresponding SEL error type.
|
|
9
|
-
* - If the error is an unknown type, it is wrapped into a generic Error.
|
|
10
62
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
63
|
+
* - A `SELError` is returned as-is.
|
|
64
|
+
* - cel-js errors (`ParseError`, `TypeError`, `EvaluationError`) become the
|
|
65
|
+
* matching `SEL*Error` and preserve `cause`.
|
|
66
|
+
* - viem errors are routed by concrete class: reverts → `SELContractRevertError`,
|
|
67
|
+
* transport/timeout → `SELProviderTransportError`, JSON-RPC → `SELProviderRpcError`,
|
|
68
|
+
* contract execution / ABI decode → `SELContractError`, everything else → `SELProviderError`.
|
|
69
|
+
* - Any other `Error` becomes `SELEvaluationError` with the original as `cause`.
|
|
13
70
|
*/
|
|
14
71
|
const wrapError = (error) => {
|
|
15
|
-
if (error instanceof
|
|
16
|
-
if (error instanceof ParseError) return new SELParseError(error.message, { cause: error });
|
|
72
|
+
if (error instanceof SELError) return error;
|
|
17
73
|
if (error instanceof EvaluationError) {
|
|
18
74
|
const cause = error.cause;
|
|
19
|
-
if (cause instanceof
|
|
75
|
+
if (cause instanceof SELError) return cause;
|
|
76
|
+
if (cause instanceof BaseError) return wrapViemError(cause);
|
|
20
77
|
return new SELEvaluationError(error.message, { cause: error });
|
|
21
78
|
}
|
|
22
|
-
if (error instanceof
|
|
23
|
-
if (error instanceof
|
|
24
|
-
return
|
|
79
|
+
if (error instanceof ParseError) return new SELParseError(error.message, { cause: error });
|
|
80
|
+
if (error instanceof TypeError) return new SELTypeCheckError(error.message, { cause: error });
|
|
81
|
+
if (error instanceof BaseError) return wrapViemError(error);
|
|
82
|
+
if (error instanceof Error) return new SELEvaluationError(error.message, { cause: error });
|
|
83
|
+
return new SELEvaluationError(String(error));
|
|
25
84
|
};
|
|
26
85
|
//#endregion
|
|
27
86
|
export { wrapError };
|