@seljs/runtime 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  3. package/dist/analysis/call-collector.cjs +233 -0
  4. package/dist/analysis/call-collector.mjs +232 -0
  5. package/dist/analysis/dependency-analyzer.cjs +68 -0
  6. package/dist/analysis/dependency-analyzer.mjs +68 -0
  7. package/dist/analysis/round-planner.cjs +65 -0
  8. package/dist/analysis/round-planner.mjs +65 -0
  9. package/dist/analysis/types.d.cts +115 -0
  10. package/dist/analysis/types.d.mts +115 -0
  11. package/dist/debug.cjs +17 -0
  12. package/dist/debug.mjs +15 -0
  13. package/dist/environment/context.cjs +11 -0
  14. package/dist/environment/context.mjs +11 -0
  15. package/dist/environment/contract-caller.cjs +81 -0
  16. package/dist/environment/contract-caller.mjs +77 -0
  17. package/dist/environment/environment.cjs +254 -0
  18. package/dist/environment/environment.d.cts +84 -0
  19. package/dist/environment/environment.d.mts +84 -0
  20. package/dist/environment/environment.mjs +252 -0
  21. package/dist/environment/error-wrapper.cjs +29 -0
  22. package/dist/environment/error-wrapper.mjs +27 -0
  23. package/dist/environment/index.cjs +1 -0
  24. package/dist/environment/index.d.mts +2 -0
  25. package/dist/environment/index.mjs +2 -0
  26. package/dist/environment/replay-cache.cjs +48 -0
  27. package/dist/environment/replay-cache.mjs +47 -0
  28. package/dist/environment/types.d.cts +60 -0
  29. package/dist/environment/types.d.mts +60 -0
  30. package/dist/errors/errors.cjs +68 -0
  31. package/dist/errors/errors.d.cts +64 -0
  32. package/dist/errors/errors.d.mts +64 -0
  33. package/dist/errors/errors.mjs +63 -0
  34. package/dist/errors/index.cjs +3 -0
  35. package/dist/errors/index.d.mts +1 -0
  36. package/dist/errors/index.mjs +2 -0
  37. package/dist/execution/multi-round-executor.cjs +45 -0
  38. package/dist/execution/multi-round-executor.mjs +44 -0
  39. package/dist/execution/multicall-batcher.cjs +51 -0
  40. package/dist/execution/multicall-batcher.mjs +50 -0
  41. package/dist/execution/multicall.cjs +39 -0
  42. package/dist/execution/multicall.mjs +38 -0
  43. package/dist/execution/result-cache.cjs +63 -0
  44. package/dist/execution/result-cache.mjs +63 -0
  45. package/dist/execution/round-executor.cjs +81 -0
  46. package/dist/execution/round-executor.mjs +80 -0
  47. package/dist/execution/types.d.cts +58 -0
  48. package/dist/execution/types.d.mts +58 -0
  49. package/dist/factory.cjs +6 -0
  50. package/dist/factory.d.cts +7 -0
  51. package/dist/factory.d.mts +6 -0
  52. package/dist/factory.mjs +6 -0
  53. package/dist/index.cjs +18 -0
  54. package/dist/index.d.cts +7 -0
  55. package/dist/index.d.mts +7 -0
  56. package/dist/index.mjs +6 -0
  57. package/package.json +26 -19
  58. package/dist/analysis/call-collector.d.ts +0 -20
  59. package/dist/analysis/call-collector.d.ts.map +0 -1
  60. package/dist/analysis/call-collector.js +0 -272
  61. package/dist/analysis/dependency-analyzer.d.ts +0 -14
  62. package/dist/analysis/dependency-analyzer.d.ts.map +0 -1
  63. package/dist/analysis/dependency-analyzer.js +0 -76
  64. package/dist/analysis/index.d.ts +0 -2
  65. package/dist/analysis/index.d.ts.map +0 -1
  66. package/dist/analysis/index.js +0 -1
  67. package/dist/analysis/round-planner.d.ts +0 -32
  68. package/dist/analysis/round-planner.d.ts.map +0 -1
  69. package/dist/analysis/round-planner.js +0 -69
  70. package/dist/analysis/types.d.ts +0 -113
  71. package/dist/analysis/types.d.ts.map +0 -1
  72. package/dist/analysis/types.js +0 -1
  73. package/dist/debug.d.ts +0 -13
  74. package/dist/debug.d.ts.map +0 -1
  75. package/dist/debug.js +0 -12
  76. package/dist/environment/context.d.ts +0 -3
  77. package/dist/environment/context.d.ts.map +0 -1
  78. package/dist/environment/context.js +0 -8
  79. package/dist/environment/contract-caller.d.ts +0 -25
  80. package/dist/environment/contract-caller.d.ts.map +0 -1
  81. package/dist/environment/contract-caller.js +0 -85
  82. package/dist/environment/environment.d.ts +0 -81
  83. package/dist/environment/environment.d.ts.map +0 -1
  84. package/dist/environment/environment.js +0 -279
  85. package/dist/environment/error-wrapper.d.ts +0 -11
  86. package/dist/environment/error-wrapper.d.ts.map +0 -1
  87. package/dist/environment/error-wrapper.js +0 -33
  88. package/dist/environment/index.d.ts +0 -3
  89. package/dist/environment/index.d.ts.map +0 -1
  90. package/dist/environment/index.js +0 -2
  91. package/dist/environment/replay-cache.d.ts +0 -23
  92. package/dist/environment/replay-cache.d.ts.map +0 -1
  93. package/dist/environment/replay-cache.js +0 -51
  94. package/dist/environment/types.d.ts +0 -57
  95. package/dist/environment/types.d.ts.map +0 -1
  96. package/dist/environment/types.js +0 -1
  97. package/dist/errors/errors.d.ts +0 -63
  98. package/dist/errors/errors.d.ts.map +0 -1
  99. package/dist/errors/errors.js +0 -63
  100. package/dist/errors/index.d.ts +0 -2
  101. package/dist/errors/index.d.ts.map +0 -1
  102. package/dist/errors/index.js +0 -1
  103. package/dist/execution/index.d.ts +0 -2
  104. package/dist/execution/index.d.ts.map +0 -1
  105. package/dist/execution/index.js +0 -1
  106. package/dist/execution/multi-round-executor.d.ts +0 -17
  107. package/dist/execution/multi-round-executor.d.ts.map +0 -1
  108. package/dist/execution/multi-round-executor.js +0 -47
  109. package/dist/execution/multicall-batcher.d.ts +0 -14
  110. package/dist/execution/multicall-batcher.d.ts.map +0 -1
  111. package/dist/execution/multicall-batcher.js +0 -53
  112. package/dist/execution/multicall.d.ts +0 -42
  113. package/dist/execution/multicall.d.ts.map +0 -1
  114. package/dist/execution/multicall.js +0 -29
  115. package/dist/execution/result-cache.d.ts +0 -47
  116. package/dist/execution/result-cache.d.ts.map +0 -1
  117. package/dist/execution/result-cache.js +0 -65
  118. package/dist/execution/round-executor.d.ts +0 -18
  119. package/dist/execution/round-executor.d.ts.map +0 -1
  120. package/dist/execution/round-executor.js +0 -95
  121. package/dist/execution/types.d.ts +0 -55
  122. package/dist/execution/types.d.ts.map +0 -1
  123. package/dist/execution/types.js +0 -1
  124. package/dist/factory.d.ts +0 -3
  125. package/dist/factory.d.ts.map +0 -1
  126. package/dist/factory.js +0 -2
  127. package/dist/index.d.ts +0 -10
  128. package/dist/index.d.ts.map +0 -1
  129. package/dist/index.js +0 -7
@@ -0,0 +1,65 @@
1
+ const require_debug = require("../debug.cjs");
2
+ const require_errors = require("../errors/errors.cjs");
3
+ require("../errors/index.cjs");
4
+ //#region src/analysis/round-planner.ts
5
+ const debug = require_debug.createLogger("analysis:plan");
6
+ /**
7
+ * Plans execution rounds from a dependency graph using topological sorting.
8
+ *
9
+ * Groups independent calls into parallel rounds, ensuring all dependencies
10
+ * of a call are executed in previous rounds. Enforces limits on the number
11
+ * of rounds and total calls to prevent excessive execution.
12
+ *
13
+ * @param graph Dependency graph representing calls and their relationships.
14
+ * @param limits Optional limits for maximum rounds and total calls.
15
+ * @returns Execution plan containing planned rounds, total calls, and maximum depth.
16
+ * @throws ExecutionLimitError If total calls exceed maxCalls or rounds exceed maxRounds.
17
+ */
18
+ const planRounds = (graph, limits = {}) => {
19
+ const maxRounds = limits.maxRounds ?? 10;
20
+ const maxCalls = limits.maxCalls ?? 100;
21
+ const totalCalls = graph.nodes.size;
22
+ if (totalCalls > maxCalls) throw new require_errors.ExecutionLimitError(`Execution limit exceeded: ${String(totalCalls)} calls exceeds maxCalls (${String(maxCalls)})`, {
23
+ limitType: "maxCalls",
24
+ limit: maxCalls,
25
+ actual: totalCalls
26
+ });
27
+ if (totalCalls === 0) return {
28
+ rounds: [],
29
+ totalCalls: 0,
30
+ maxDepth: 0
31
+ };
32
+ const inDegree = /* @__PURE__ */ new Map();
33
+ for (const nodeId of graph.nodes.keys()) inDegree.set(nodeId, graph.edges.get(nodeId)?.size ?? 0);
34
+ const rounds = [];
35
+ const processed = /* @__PURE__ */ new Set();
36
+ while (processed.size < totalCalls) {
37
+ const readyNodes = [];
38
+ for (const [nodeId, degree] of inDegree) if (degree === 0 && !processed.has(nodeId)) readyNodes.push(nodeId);
39
+ if (readyNodes.length === 0) break;
40
+ if (rounds.length >= maxRounds) throw new require_errors.ExecutionLimitError(`Execution limit exceeded: requires more than maxRounds (${String(maxRounds)}) rounds`, {
41
+ limitType: "maxRounds",
42
+ limit: maxRounds,
43
+ actual: rounds.length + 1
44
+ });
45
+ const round = {
46
+ roundNumber: rounds.length,
47
+ calls: readyNodes.map((id) => graph.nodes.get(id)?.call).filter((c) => c !== void 0)
48
+ };
49
+ rounds.push(round);
50
+ for (const nodeId of readyNodes) {
51
+ processed.add(nodeId);
52
+ const node = graph.nodes.get(nodeId);
53
+ if (!node) continue;
54
+ for (const dependentId of node.dependedOnBy) inDegree.set(dependentId, (inDegree.get(dependentId) ?? 0) - 1);
55
+ }
56
+ }
57
+ debug("planned %d rounds for %d calls (max depth=%d)", rounds.length, totalCalls, rounds.length);
58
+ return {
59
+ rounds,
60
+ totalCalls,
61
+ maxDepth: rounds.length
62
+ };
63
+ };
64
+ //#endregion
65
+ exports.planRounds = planRounds;
@@ -0,0 +1,65 @@
1
+ import { createLogger } from "../debug.mjs";
2
+ import { ExecutionLimitError } from "../errors/errors.mjs";
3
+ import "../errors/index.mjs";
4
+ //#region src/analysis/round-planner.ts
5
+ const debug = createLogger("analysis:plan");
6
+ /**
7
+ * Plans execution rounds from a dependency graph using topological sorting.
8
+ *
9
+ * Groups independent calls into parallel rounds, ensuring all dependencies
10
+ * of a call are executed in previous rounds. Enforces limits on the number
11
+ * of rounds and total calls to prevent excessive execution.
12
+ *
13
+ * @param graph Dependency graph representing calls and their relationships.
14
+ * @param limits Optional limits for maximum rounds and total calls.
15
+ * @returns Execution plan containing planned rounds, total calls, and maximum depth.
16
+ * @throws ExecutionLimitError If total calls exceed maxCalls or rounds exceed maxRounds.
17
+ */
18
+ const planRounds = (graph, limits = {}) => {
19
+ const maxRounds = limits.maxRounds ?? 10;
20
+ const maxCalls = limits.maxCalls ?? 100;
21
+ const totalCalls = graph.nodes.size;
22
+ if (totalCalls > maxCalls) throw new ExecutionLimitError(`Execution limit exceeded: ${String(totalCalls)} calls exceeds maxCalls (${String(maxCalls)})`, {
23
+ limitType: "maxCalls",
24
+ limit: maxCalls,
25
+ actual: totalCalls
26
+ });
27
+ if (totalCalls === 0) return {
28
+ rounds: [],
29
+ totalCalls: 0,
30
+ maxDepth: 0
31
+ };
32
+ const inDegree = /* @__PURE__ */ new Map();
33
+ for (const nodeId of graph.nodes.keys()) inDegree.set(nodeId, graph.edges.get(nodeId)?.size ?? 0);
34
+ const rounds = [];
35
+ const processed = /* @__PURE__ */ new Set();
36
+ while (processed.size < totalCalls) {
37
+ const readyNodes = [];
38
+ for (const [nodeId, degree] of inDegree) if (degree === 0 && !processed.has(nodeId)) readyNodes.push(nodeId);
39
+ if (readyNodes.length === 0) break;
40
+ if (rounds.length >= maxRounds) throw new ExecutionLimitError(`Execution limit exceeded: requires more than maxRounds (${String(maxRounds)}) rounds`, {
41
+ limitType: "maxRounds",
42
+ limit: maxRounds,
43
+ actual: rounds.length + 1
44
+ });
45
+ const round = {
46
+ roundNumber: rounds.length,
47
+ calls: readyNodes.map((id) => graph.nodes.get(id)?.call).filter((c) => c !== void 0)
48
+ };
49
+ rounds.push(round);
50
+ for (const nodeId of readyNodes) {
51
+ processed.add(nodeId);
52
+ const node = graph.nodes.get(nodeId);
53
+ if (!node) continue;
54
+ for (const dependentId of node.dependedOnBy) inDegree.set(dependentId, (inDegree.get(dependentId) ?? 0) - 1);
55
+ }
56
+ }
57
+ debug("planned %d rounds for %d calls (max depth=%d)", rounds.length, totalCalls, rounds.length);
58
+ return {
59
+ rounds,
60
+ totalCalls,
61
+ maxDepth: rounds.length
62
+ };
63
+ };
64
+ //#endregion
65
+ export { planRounds };
@@ -0,0 +1,115 @@
1
+ //#region src/analysis/types.d.ts
2
+ /**
3
+ * Represents a contract call found in AST traversal.
4
+ * Contains all information needed to execute the call and track dependencies.
5
+ */
6
+ interface CollectedCall {
7
+ /**
8
+ * Unique identifier: "contract:method:serializedArgs"
9
+ */
10
+ id: string;
11
+ /**
12
+ * Contract name: e.g., "erc20_usdc"
13
+ */
14
+ contract: string;
15
+ /**
16
+ * Method name: e.g., "balanceOf"
17
+ */
18
+ method: string;
19
+ /**
20
+ * Arguments with type and dependency information
21
+ */
22
+ args: CallArgument[];
23
+ /**
24
+ * Reference to original AST node for error reporting
25
+ */
26
+ astNode: unknown;
27
+ }
28
+ /**
29
+ * Argument to a contract call with type information.
30
+ * Tracks whether the argument is a literal, variable, or result of another call.
31
+ */
32
+ interface CallArgument {
33
+ /**
34
+ * Type of argument: literal value, variable reference, or call result
35
+ */
36
+ type: "literal" | "variable" | "call_result";
37
+ /**
38
+ * Literal value (when type is "literal")
39
+ */
40
+ value?: unknown;
41
+ /**
42
+ * Variable name (when type is "variable")
43
+ */
44
+ variableName?: string;
45
+ /**
46
+ * Call ID this depends on (when type is "call_result")
47
+ */
48
+ dependsOnCallId?: string;
49
+ }
50
+ /**
51
+ * Dependency graph for all collected calls.
52
+ * Maps call IDs to their nodes and dependency relationships.
53
+ */
54
+ interface DependencyGraph {
55
+ /**
56
+ * Call ID to graph node mapping
57
+ */
58
+ nodes: Map<string, GraphNode>;
59
+ /**
60
+ * Call ID to set of call IDs it depends on
61
+ */
62
+ edges: Map<string, Set<string>>;
63
+ }
64
+ /**
65
+ * Single node in the dependency graph.
66
+ * Tracks both incoming and outgoing dependencies.
67
+ */
68
+ interface GraphNode {
69
+ /**
70
+ * The original collected call
71
+ */
72
+ call: CollectedCall;
73
+ /**
74
+ * Call IDs this call depends on (must complete first)
75
+ */
76
+ dependsOn: string[];
77
+ /**
78
+ * Call IDs that depend on this call (will execute after)
79
+ */
80
+ dependedOnBy: string[];
81
+ }
82
+ /**
83
+ * Execution plan with rounds of parallel calls.
84
+ * Result of topological sorting the dependency graph.
85
+ */
86
+ interface ExecutionPlan {
87
+ /**
88
+ * Rounds of calls to execute in sequence
89
+ */
90
+ rounds: ExecutionRound[];
91
+ /**
92
+ * Total number of calls across all rounds
93
+ */
94
+ totalCalls: number;
95
+ /**
96
+ * Number of rounds (maximum dependency depth)
97
+ */
98
+ maxDepth: number;
99
+ }
100
+ /**
101
+ * Single round of calls that can execute in parallel.
102
+ * All calls in a round have no dependencies on each other.
103
+ */
104
+ interface ExecutionRound {
105
+ /**
106
+ * Round number (0-indexed)
107
+ */
108
+ roundNumber: number;
109
+ /**
110
+ * Calls that can execute in parallel this round
111
+ */
112
+ calls: CollectedCall[];
113
+ }
114
+ //#endregion
115
+ export { CallArgument, CollectedCall, DependencyGraph, ExecutionPlan, ExecutionRound, GraphNode };
@@ -0,0 +1,115 @@
1
+ //#region src/analysis/types.d.ts
2
+ /**
3
+ * Represents a contract call found in AST traversal.
4
+ * Contains all information needed to execute the call and track dependencies.
5
+ */
6
+ interface CollectedCall {
7
+ /**
8
+ * Unique identifier: "contract:method:serializedArgs"
9
+ */
10
+ id: string;
11
+ /**
12
+ * Contract name: e.g., "erc20_usdc"
13
+ */
14
+ contract: string;
15
+ /**
16
+ * Method name: e.g., "balanceOf"
17
+ */
18
+ method: string;
19
+ /**
20
+ * Arguments with type and dependency information
21
+ */
22
+ args: CallArgument[];
23
+ /**
24
+ * Reference to original AST node for error reporting
25
+ */
26
+ astNode: unknown;
27
+ }
28
+ /**
29
+ * Argument to a contract call with type information.
30
+ * Tracks whether the argument is a literal, variable, or result of another call.
31
+ */
32
+ interface CallArgument {
33
+ /**
34
+ * Type of argument: literal value, variable reference, or call result
35
+ */
36
+ type: "literal" | "variable" | "call_result";
37
+ /**
38
+ * Literal value (when type is "literal")
39
+ */
40
+ value?: unknown;
41
+ /**
42
+ * Variable name (when type is "variable")
43
+ */
44
+ variableName?: string;
45
+ /**
46
+ * Call ID this depends on (when type is "call_result")
47
+ */
48
+ dependsOnCallId?: string;
49
+ }
50
+ /**
51
+ * Dependency graph for all collected calls.
52
+ * Maps call IDs to their nodes and dependency relationships.
53
+ */
54
+ interface DependencyGraph {
55
+ /**
56
+ * Call ID to graph node mapping
57
+ */
58
+ nodes: Map<string, GraphNode>;
59
+ /**
60
+ * Call ID to set of call IDs it depends on
61
+ */
62
+ edges: Map<string, Set<string>>;
63
+ }
64
+ /**
65
+ * Single node in the dependency graph.
66
+ * Tracks both incoming and outgoing dependencies.
67
+ */
68
+ interface GraphNode {
69
+ /**
70
+ * The original collected call
71
+ */
72
+ call: CollectedCall;
73
+ /**
74
+ * Call IDs this call depends on (must complete first)
75
+ */
76
+ dependsOn: string[];
77
+ /**
78
+ * Call IDs that depend on this call (will execute after)
79
+ */
80
+ dependedOnBy: string[];
81
+ }
82
+ /**
83
+ * Execution plan with rounds of parallel calls.
84
+ * Result of topological sorting the dependency graph.
85
+ */
86
+ interface ExecutionPlan {
87
+ /**
88
+ * Rounds of calls to execute in sequence
89
+ */
90
+ rounds: ExecutionRound[];
91
+ /**
92
+ * Total number of calls across all rounds
93
+ */
94
+ totalCalls: number;
95
+ /**
96
+ * Number of rounds (maximum dependency depth)
97
+ */
98
+ maxDepth: number;
99
+ }
100
+ /**
101
+ * Single round of calls that can execute in parallel.
102
+ * All calls in a round have no dependencies on each other.
103
+ */
104
+ interface ExecutionRound {
105
+ /**
106
+ * Round number (0-indexed)
107
+ */
108
+ roundNumber: number;
109
+ /**
110
+ * Calls that can execute in parallel this round
111
+ */
112
+ calls: CollectedCall[];
113
+ }
114
+ //#endregion
115
+ export { CallArgument, CollectedCall, DependencyGraph, ExecutionPlan, ExecutionRound, GraphNode };
package/dist/debug.cjs ADDED
@@ -0,0 +1,17 @@
1
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
2
+ let debug = require("debug");
3
+ debug = require_runtime.__toESM(debug);
4
+ //#region src/debug.ts
5
+ /**
6
+ * Creates a namespaced debug logger for a SEL module.
7
+ *
8
+ * Enable via the `DEBUG` environment variable:
9
+ * - `DEBUG=sel:*` — all SEL logging
10
+ * - `DEBUG=sel:evaluate` — only evaluate flow
11
+ * - `DEBUG=sel:execute:*` — all execution sub-loggers
12
+ *
13
+ * @param namespace - Module namespace (e.g. "evaluate", "execute:round")
14
+ */
15
+ const createLogger = (namespace) => (0, debug.default)(`sel:${namespace}`);
16
+ //#endregion
17
+ exports.createLogger = createLogger;
package/dist/debug.mjs ADDED
@@ -0,0 +1,15 @@
1
+ import createDebug from "debug";
2
+ //#region src/debug.ts
3
+ /**
4
+ * Creates a namespaced debug logger for a SEL module.
5
+ *
6
+ * Enable via the `DEBUG` environment variable:
7
+ * - `DEBUG=sel:*` — all SEL logging
8
+ * - `DEBUG=sel:evaluate` — only evaluate flow
9
+ * - `DEBUG=sel:execute:*` — all execution sub-loggers
10
+ *
11
+ * @param namespace - Module namespace (e.g. "evaluate", "execute:round")
12
+ */
13
+ const createLogger = (namespace) => createDebug(`sel:${namespace}`);
14
+ //#endregion
15
+ export { createLogger };
@@ -0,0 +1,11 @@
1
+ //#region src/environment/context.ts
2
+ const normalizeContextForEvaluation = (context, variableTypes, codecRegistry) => {
3
+ const normalized = {};
4
+ for (const [key, value] of Object.entries(context)) {
5
+ const type = variableTypes.get(key);
6
+ normalized[key] = codecRegistry.resolve(type ?? "dyn").parse(value);
7
+ }
8
+ return normalized;
9
+ };
10
+ //#endregion
11
+ exports.normalizeContextForEvaluation = normalizeContextForEvaluation;
@@ -0,0 +1,11 @@
1
+ //#region src/environment/context.ts
2
+ const normalizeContextForEvaluation = (context, variableTypes, codecRegistry) => {
3
+ const normalized = {};
4
+ for (const [key, value] of Object.entries(context)) {
5
+ const type = variableTypes.get(key);
6
+ normalized[key] = codecRegistry.resolve(type ?? "dyn").parse(value);
7
+ }
8
+ return normalized;
9
+ };
10
+ //#endregion
11
+ export { normalizeContextForEvaluation };
@@ -0,0 +1,81 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_replay_cache = require("./replay-cache.cjs");
3
+ const require_debug = require("../debug.cjs");
4
+ const require_errors = require("../errors/errors.cjs");
5
+ require("../errors/index.cjs");
6
+ let viem_actions = require("viem/actions");
7
+ //#region src/environment/contract-caller.ts
8
+ const debug = require_debug.createLogger("contract-caller");
9
+ /**
10
+ * Tracks total RPC calls (pre-executed + live) across a single evaluation.
11
+ * Create one per `evaluate()` call and pass it through the handler closure.
12
+ */
13
+ var CallCounter = class {
14
+ count = 0;
15
+ constructor(maxCalls, initialCount = 0) {
16
+ this.maxCalls = maxCalls;
17
+ this.count = initialCount;
18
+ }
19
+ increment(contractName, methodName) {
20
+ this.count++;
21
+ if (this.count > this.maxCalls) throw new require_errors.ExecutionLimitError(`Execution limit exceeded: ${String(this.count)} calls exceeds maxCalls (${String(this.maxCalls)})`, {
22
+ limitType: "maxCalls",
23
+ limit: this.maxCalls,
24
+ actual: this.count
25
+ });
26
+ debug("call count: %d/%d (%s.%s)", this.count, this.maxCalls, contractName, methodName);
27
+ }
28
+ };
29
+ const executeContractCall = async (contract, method, args, options) => {
30
+ const normalizedArgs = options.codecRegistry ? args.map((arg, i) => options.codecRegistry.encode(method.params[i]?.type ?? "dyn", arg)) : args;
31
+ if (options.executionCache) {
32
+ const callId = require_replay_cache.createReplayCallId(contract.name, method.name, normalizedArgs);
33
+ if (options.executionCache.has(callId)) {
34
+ debug("cache hit: %s.%s", contract.name, method.name);
35
+ return options.executionCache.get(callId);
36
+ }
37
+ debug("cache miss: %s.%s — executing live", contract.name, method.name);
38
+ }
39
+ if (!options.client) throw new require_errors.SELContractError("No client provided for contract call. Provide a client in SELRuntime config or evaluate() context.", {
40
+ contractName: contract.name,
41
+ methodName: method.name
42
+ });
43
+ options.callCounter?.increment(contract.name, method.name);
44
+ try {
45
+ return await (0, viem_actions.readContract)(options.client, {
46
+ address: contract.address,
47
+ abi: [method.abi],
48
+ functionName: method.name,
49
+ args: normalizedArgs
50
+ });
51
+ } catch (error) {
52
+ if (error instanceof require_errors.SELContractError) throw error;
53
+ throw new require_errors.SELContractError(`Contract call failed: ${contract.name}.${method.name}`, {
54
+ cause: error,
55
+ contractName: contract.name,
56
+ methodName: method.name
57
+ });
58
+ }
59
+ };
60
+ const resolveExecutionBlockNumber = async (client) => {
61
+ const clientWithGetBlockNumber = client;
62
+ if (typeof clientWithGetBlockNumber.getBlockNumber === "function") return await clientWithGetBlockNumber.getBlockNumber();
63
+ if (typeof clientWithGetBlockNumber.request === "function") return;
64
+ return 0n;
65
+ };
66
+ const buildContractInfoMap = (contracts) => {
67
+ const map = /* @__PURE__ */ new Map();
68
+ for (const contract of contracts) {
69
+ const abi = contract.methods.map((m) => m.abi);
70
+ map.set(contract.name, {
71
+ abi,
72
+ address: contract.address
73
+ });
74
+ }
75
+ return map;
76
+ };
77
+ //#endregion
78
+ exports.CallCounter = CallCounter;
79
+ exports.buildContractInfoMap = buildContractInfoMap;
80
+ exports.executeContractCall = executeContractCall;
81
+ exports.resolveExecutionBlockNumber = resolveExecutionBlockNumber;
@@ -0,0 +1,77 @@
1
+ import { createReplayCallId } from "./replay-cache.mjs";
2
+ import { createLogger } from "../debug.mjs";
3
+ import { ExecutionLimitError, SELContractError } from "../errors/errors.mjs";
4
+ import "../errors/index.mjs";
5
+ import { readContract } from "viem/actions";
6
+ //#region src/environment/contract-caller.ts
7
+ const debug = createLogger("contract-caller");
8
+ /**
9
+ * Tracks total RPC calls (pre-executed + live) across a single evaluation.
10
+ * Create one per `evaluate()` call and pass it through the handler closure.
11
+ */
12
+ var CallCounter = class {
13
+ count = 0;
14
+ constructor(maxCalls, initialCount = 0) {
15
+ this.maxCalls = maxCalls;
16
+ this.count = initialCount;
17
+ }
18
+ increment(contractName, methodName) {
19
+ this.count++;
20
+ if (this.count > this.maxCalls) throw new ExecutionLimitError(`Execution limit exceeded: ${String(this.count)} calls exceeds maxCalls (${String(this.maxCalls)})`, {
21
+ limitType: "maxCalls",
22
+ limit: this.maxCalls,
23
+ actual: this.count
24
+ });
25
+ debug("call count: %d/%d (%s.%s)", this.count, this.maxCalls, contractName, methodName);
26
+ }
27
+ };
28
+ const executeContractCall = async (contract, method, args, options) => {
29
+ const normalizedArgs = options.codecRegistry ? args.map((arg, i) => options.codecRegistry.encode(method.params[i]?.type ?? "dyn", arg)) : args;
30
+ if (options.executionCache) {
31
+ const callId = createReplayCallId(contract.name, method.name, normalizedArgs);
32
+ if (options.executionCache.has(callId)) {
33
+ debug("cache hit: %s.%s", contract.name, method.name);
34
+ return options.executionCache.get(callId);
35
+ }
36
+ debug("cache miss: %s.%s — executing live", contract.name, method.name);
37
+ }
38
+ if (!options.client) throw new SELContractError("No client provided for contract call. Provide a client in SELRuntime config or evaluate() context.", {
39
+ contractName: contract.name,
40
+ methodName: method.name
41
+ });
42
+ options.callCounter?.increment(contract.name, method.name);
43
+ try {
44
+ return await readContract(options.client, {
45
+ address: contract.address,
46
+ abi: [method.abi],
47
+ functionName: method.name,
48
+ args: normalizedArgs
49
+ });
50
+ } catch (error) {
51
+ if (error instanceof SELContractError) throw error;
52
+ throw new SELContractError(`Contract call failed: ${contract.name}.${method.name}`, {
53
+ cause: error,
54
+ contractName: contract.name,
55
+ methodName: method.name
56
+ });
57
+ }
58
+ };
59
+ const resolveExecutionBlockNumber = async (client) => {
60
+ const clientWithGetBlockNumber = client;
61
+ if (typeof clientWithGetBlockNumber.getBlockNumber === "function") return await clientWithGetBlockNumber.getBlockNumber();
62
+ if (typeof clientWithGetBlockNumber.request === "function") return;
63
+ return 0n;
64
+ };
65
+ const buildContractInfoMap = (contracts) => {
66
+ const map = /* @__PURE__ */ new Map();
67
+ for (const contract of contracts) {
68
+ const abi = contract.methods.map((m) => m.abi);
69
+ map.set(contract.name, {
70
+ abi,
71
+ address: contract.address
72
+ });
73
+ }
74
+ return map;
75
+ };
76
+ //#endregion
77
+ export { CallCounter, buildContractInfoMap, executeContractCall, resolveExecutionBlockNumber };