@seljs/runtime 1.3.0-beta.1 → 1.3.0-beta.12

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 (43) hide show
  1. package/dist/analysis/call-collector.cjs +19 -0
  2. package/dist/analysis/call-collector.mjs +19 -0
  3. package/dist/analysis/dependency-analyzer.cjs +1 -2
  4. package/dist/analysis/dependency-analyzer.mjs +1 -1
  5. package/dist/analysis/round-planner.cjs +1 -2
  6. package/dist/analysis/round-planner.mjs +1 -1
  7. package/dist/environment/builtin-contracts.cjs +22 -0
  8. package/dist/environment/builtin-contracts.mjs +22 -0
  9. package/dist/environment/client.cjs +16 -0
  10. package/dist/environment/client.d.cts +33 -0
  11. package/dist/environment/client.d.mts +33 -0
  12. package/dist/environment/client.mjs +16 -0
  13. package/dist/environment/contract-caller.cjs +13 -12
  14. package/dist/environment/contract-caller.mjs +13 -12
  15. package/dist/environment/environment.cjs +35 -10
  16. package/dist/environment/environment.d.cts +5 -0
  17. package/dist/environment/environment.d.mts +5 -0
  18. package/dist/environment/environment.mjs +35 -10
  19. package/dist/environment/index.cjs +1 -0
  20. package/dist/environment/index.d.mts +1 -0
  21. package/dist/environment/index.mjs +1 -0
  22. package/dist/environment/types.d.cts +4 -4
  23. package/dist/environment/types.d.mts +4 -4
  24. package/dist/errors/errors.cjs +7 -0
  25. package/dist/errors/errors.d.cts +7 -1
  26. package/dist/errors/errors.d.mts +7 -1
  27. package/dist/errors/errors.mjs +7 -1
  28. package/dist/errors/index.d.mts +1 -1
  29. package/dist/execution/multi-round-executor.cjs +1 -4
  30. package/dist/execution/multi-round-executor.mjs +1 -3
  31. package/dist/execution/multicall-batcher.cjs +11 -9
  32. package/dist/execution/multicall-batcher.mjs +11 -9
  33. package/dist/execution/multicall.cjs +28 -4
  34. package/dist/execution/multicall.mjs +26 -4
  35. package/dist/execution/round-executor.cjs +4 -12
  36. package/dist/execution/round-executor.mjs +4 -12
  37. package/dist/execution/types.d.cts +40 -16
  38. package/dist/execution/types.d.mts +40 -16
  39. package/dist/index.cjs +3 -0
  40. package/dist/index.d.cts +3 -2
  41. package/dist/index.d.mts +3 -2
  42. package/dist/index.mjs +3 -2
  43. package/package.json +7 -7
@@ -196,6 +196,25 @@ const collectCalls = (ast, registry) => {
196
196
  return;
197
197
  }
198
198
  if (!receiverName || !registry.get(receiverName)) {
199
+ if (method === "balance" && args.length === 0) {
200
+ const receiverNode = node.args[1];
201
+ const { arg: receiverArg, traversed } = classifyArg(receiverNode);
202
+ if (receiverArg) {
203
+ const call = {
204
+ id: generateCallId("__multicall3", "getEthBalance", [receiverArg]),
205
+ contract: "__multicall3",
206
+ method: "getEthBalance",
207
+ args: [receiverArg],
208
+ astNode: node
209
+ };
210
+ calls.push(call);
211
+ debug("found address accessor %s (id=%s)", "balance", call.id);
212
+ return call;
213
+ }
214
+ deferredNodes.add(node);
215
+ if (!traversed) traverse(receiverNode);
216
+ return;
217
+ }
199
218
  handleNonContractRCall(node, method, args);
200
219
  return;
201
220
  }
@@ -195,6 +195,25 @@ const collectCalls = (ast, registry) => {
195
195
  return;
196
196
  }
197
197
  if (!receiverName || !registry.get(receiverName)) {
198
+ if (method === "balance" && args.length === 0) {
199
+ const receiverNode = node.args[1];
200
+ const { arg: receiverArg, traversed } = classifyArg(receiverNode);
201
+ if (receiverArg) {
202
+ const call = {
203
+ id: generateCallId("__multicall3", "getEthBalance", [receiverArg]),
204
+ contract: "__multicall3",
205
+ method: "getEthBalance",
206
+ args: [receiverArg],
207
+ astNode: node
208
+ };
209
+ calls.push(call);
210
+ debug("found address accessor %s (id=%s)", "balance", call.id);
211
+ return call;
212
+ }
213
+ deferredNodes.add(node);
214
+ if (!traversed) traverse(receiverNode);
215
+ return;
216
+ }
198
217
  handleNonContractRCall(node, method, args);
199
218
  return;
200
219
  }
@@ -1,8 +1,7 @@
1
- const require_debug = require("../debug.cjs");
2
1
  const require_errors = require("../errors/errors.cjs");
3
2
  require("../errors/index.cjs");
4
3
  //#region src/analysis/dependency-analyzer.ts
5
- const debug = require_debug.createLogger("analysis:dependency");
4
+ const debug = require("../debug.cjs").createLogger("analysis:dependency");
6
5
  /**
7
6
  * Detects circular dependencies in the graph using DFS with a recursion stack.
8
7
  *
@@ -1,6 +1,6 @@
1
- import { createLogger } from "../debug.mjs";
2
1
  import { CircularDependencyError } from "../errors/errors.mjs";
3
2
  import "../errors/index.mjs";
3
+ import { createLogger } from "../debug.mjs";
4
4
  //#region src/analysis/dependency-analyzer.ts
5
5
  const debug = createLogger("analysis:dependency");
6
6
  /**
@@ -1,8 +1,7 @@
1
- const require_debug = require("../debug.cjs");
2
1
  const require_errors = require("../errors/errors.cjs");
3
2
  require("../errors/index.cjs");
4
3
  //#region src/analysis/round-planner.ts
5
- const debug = require_debug.createLogger("analysis:plan");
4
+ const debug = require("../debug.cjs").createLogger("analysis:plan");
6
5
  /**
7
6
  * Plans execution rounds from a dependency graph using topological sorting.
8
7
  *
@@ -1,6 +1,6 @@
1
- import { createLogger } from "../debug.mjs";
2
1
  import { ExecutionLimitError } from "../errors/errors.mjs";
3
2
  import "../errors/index.mjs";
3
+ import { createLogger } from "../debug.mjs";
4
4
  //#region src/analysis/round-planner.ts
5
5
  const debug = createLogger("analysis:plan");
6
6
  /**
@@ -0,0 +1,22 @@
1
+ const require_multicall = require("../execution/multicall.cjs");
2
+ //#region src/environment/builtin-contracts.ts
3
+ /**
4
+ * Returns built-in contract schemas that are always available in the runtime.
5
+ * These are internal contracts not defined by the user schema but required
6
+ * for built-in features like address accessors.
7
+ */
8
+ const getBuiltinContracts = (multicall) => [{
9
+ name: "__multicall3",
10
+ address: multicall?.address ?? "0xcA11bde05977b3631167028862bE2a173976CA11",
11
+ methods: [{
12
+ name: "getEthBalance",
13
+ params: [{
14
+ name: "addr",
15
+ type: "sol_address"
16
+ }],
17
+ returns: "sol_int",
18
+ abi: require_multicall.getEthBalanceAbi
19
+ }]
20
+ }];
21
+ //#endregion
22
+ exports.getBuiltinContracts = getBuiltinContracts;
@@ -0,0 +1,22 @@
1
+ import { getEthBalanceAbi } from "../execution/multicall.mjs";
2
+ //#region src/environment/builtin-contracts.ts
3
+ /**
4
+ * Returns built-in contract schemas that are always available in the runtime.
5
+ * These are internal contracts not defined by the user schema but required
6
+ * for built-in features like address accessors.
7
+ */
8
+ const getBuiltinContracts = (multicall) => [{
9
+ name: "__multicall3",
10
+ address: multicall?.address ?? "0xcA11bde05977b3631167028862bE2a173976CA11",
11
+ methods: [{
12
+ name: "getEthBalance",
13
+ params: [{
14
+ name: "addr",
15
+ type: "sol_address"
16
+ }],
17
+ returns: "sol_int",
18
+ abi: getEthBalanceAbi
19
+ }]
20
+ }];
21
+ //#endregion
22
+ export { getBuiltinContracts };
@@ -0,0 +1,16 @@
1
+ const require_errors = require("../errors/errors.cjs");
2
+ require("../errors/index.cjs");
3
+ //#region src/environment/client.ts
4
+ /**
5
+ * Validates that an object satisfies the SELClient interface.
6
+ * Throws SELClientError if validation fails.
7
+ * Called at construction time for fail-fast behavior.
8
+ */
9
+ function validateClient(client) {
10
+ if (!client || typeof client !== "object") throw new require_errors.SELClientError("Client must be an object");
11
+ const c = client;
12
+ if (typeof c["call"] !== "function") throw new require_errors.SELClientError("Client must implement call({ to, data, blockNumber? }). ");
13
+ if (typeof c["getBlockNumber"] !== "function") throw new require_errors.SELClientError("Client must implement getBlockNumber(). ");
14
+ }
15
+ //#endregion
16
+ exports.validateClient = validateClient;
@@ -0,0 +1,33 @@
1
+ //#region src/environment/client.d.ts
2
+ /**
3
+ * Minimal client interface for SEL runtime contract execution.
4
+ *
5
+ * Framework-agnostic: works with viem PublicClient, ethers.js Provider,
6
+ * web3.js, or any object that can make eth_call and eth_blockNumber requests.
7
+ *
8
+ * A viem PublicClient structurally satisfies this interface.
9
+ */
10
+ interface SELClient {
11
+ /**
12
+ * Execute an eth_call against a contract
13
+ */
14
+ call: (params: {
15
+ to: `0x${string}`;
16
+ data: `0x${string}`;
17
+ blockNumber?: bigint;
18
+ }) => Promise<{
19
+ data?: `0x${string}` | undefined;
20
+ }>;
21
+ /**
22
+ * Get the current block number (eth_blockNumber) for pinning reads
23
+ */
24
+ getBlockNumber: () => Promise<bigint>;
25
+ }
26
+ /**
27
+ * Validates that an object satisfies the SELClient interface.
28
+ * Throws SELClientError if validation fails.
29
+ * Called at construction time for fail-fast behavior.
30
+ */
31
+ declare function validateClient(client: unknown): asserts client is SELClient;
32
+ //#endregion
33
+ export { SELClient, validateClient };
@@ -0,0 +1,33 @@
1
+ //#region src/environment/client.d.ts
2
+ /**
3
+ * Minimal client interface for SEL runtime contract execution.
4
+ *
5
+ * Framework-agnostic: works with viem PublicClient, ethers.js Provider,
6
+ * web3.js, or any object that can make eth_call and eth_blockNumber requests.
7
+ *
8
+ * A viem PublicClient structurally satisfies this interface.
9
+ */
10
+ interface SELClient {
11
+ /**
12
+ * Execute an eth_call against a contract
13
+ */
14
+ call: (params: {
15
+ to: `0x${string}`;
16
+ data: `0x${string}`;
17
+ blockNumber?: bigint;
18
+ }) => Promise<{
19
+ data?: `0x${string}` | undefined;
20
+ }>;
21
+ /**
22
+ * Get the current block number (eth_blockNumber) for pinning reads
23
+ */
24
+ getBlockNumber: () => Promise<bigint>;
25
+ }
26
+ /**
27
+ * Validates that an object satisfies the SELClient interface.
28
+ * Throws SELClientError if validation fails.
29
+ * Called at construction time for fail-fast behavior.
30
+ */
31
+ declare function validateClient(client: unknown): asserts client is SELClient;
32
+ //#endregion
33
+ export { SELClient, validateClient };
@@ -0,0 +1,16 @@
1
+ import { SELClientError } from "../errors/errors.mjs";
2
+ import "../errors/index.mjs";
3
+ //#region src/environment/client.ts
4
+ /**
5
+ * Validates that an object satisfies the SELClient interface.
6
+ * Throws SELClientError if validation fails.
7
+ * Called at construction time for fail-fast behavior.
8
+ */
9
+ function validateClient(client) {
10
+ if (!client || typeof client !== "object") throw new SELClientError("Client must be an object");
11
+ const c = client;
12
+ if (typeof c["call"] !== "function") throw new SELClientError("Client must implement call({ to, data, blockNumber? }). ");
13
+ if (typeof c["getBlockNumber"] !== "function") throw new SELClientError("Client must implement getBlockNumber(). ");
14
+ }
15
+ //#endregion
16
+ export { validateClient };
@@ -1,9 +1,9 @@
1
1
  require("../_virtual/_rolldown/runtime.cjs");
2
- const require_replay_cache = require("./replay-cache.cjs");
3
- const require_debug = require("../debug.cjs");
4
2
  const require_errors = require("../errors/errors.cjs");
5
3
  require("../errors/index.cjs");
6
- let viem_actions = require("viem/actions");
4
+ const require_replay_cache = require("./replay-cache.cjs");
5
+ const require_debug = require("../debug.cjs");
6
+ let ox = require("ox");
7
7
  //#region src/environment/contract-caller.ts
8
8
  const debug = require_debug.createLogger("contract-caller");
9
9
  /**
@@ -42,12 +42,16 @@ const executeContractCall = async (contract, method, args, options) => {
42
42
  });
43
43
  options.callCounter?.increment(contract.name, method.name);
44
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
45
+ const data = ox.AbiFunction.encodeData([method.abi], method.name, normalizedArgs);
46
+ const result = await options.client.call({
47
+ to: contract.address,
48
+ data
49
+ });
50
+ if (!result.data) throw new require_errors.SELContractError(`Contract call returned no data: ${contract.name}.${method.name}`, {
51
+ contractName: contract.name,
52
+ methodName: method.name
50
53
  });
54
+ return ox.AbiFunction.decodeResult([method.abi], method.name, result.data);
51
55
  } catch (error) {
52
56
  if (error instanceof require_errors.SELContractError) throw error;
53
57
  throw new require_errors.SELContractError(`Contract call failed: ${contract.name}.${method.name}`, {
@@ -58,10 +62,7 @@ const executeContractCall = async (contract, method, args, options) => {
58
62
  }
59
63
  };
60
64
  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
+ return await client.getBlockNumber();
65
66
  };
66
67
  const buildContractInfoMap = (contracts) => {
67
68
  const map = /* @__PURE__ */ new Map();
@@ -1,8 +1,8 @@
1
- import { createReplayCallId } from "./replay-cache.mjs";
2
- import { createLogger } from "../debug.mjs";
3
1
  import { ExecutionLimitError, SELContractError } from "../errors/errors.mjs";
4
2
  import "../errors/index.mjs";
5
- import { readContract } from "viem/actions";
3
+ import { createReplayCallId } from "./replay-cache.mjs";
4
+ import { createLogger } from "../debug.mjs";
5
+ import { AbiFunction } from "ox";
6
6
  //#region src/environment/contract-caller.ts
7
7
  const debug = createLogger("contract-caller");
8
8
  /**
@@ -41,12 +41,16 @@ const executeContractCall = async (contract, method, args, options) => {
41
41
  });
42
42
  options.callCounter?.increment(contract.name, method.name);
43
43
  try {
44
- return await readContract(options.client, {
45
- address: contract.address,
46
- abi: [method.abi],
47
- functionName: method.name,
48
- args: normalizedArgs
44
+ const data = AbiFunction.encodeData([method.abi], method.name, normalizedArgs);
45
+ const result = await options.client.call({
46
+ to: contract.address,
47
+ data
48
+ });
49
+ if (!result.data) throw new SELContractError(`Contract call returned no data: ${contract.name}.${method.name}`, {
50
+ contractName: contract.name,
51
+ methodName: method.name
49
52
  });
53
+ return AbiFunction.decodeResult([method.abi], method.name, result.data);
50
54
  } catch (error) {
51
55
  if (error instanceof SELContractError) throw error;
52
56
  throw new SELContractError(`Contract call failed: ${contract.name}.${method.name}`, {
@@ -57,10 +61,7 @@ const executeContractCall = async (contract, method, args, options) => {
57
61
  }
58
62
  };
59
63
  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
+ return await client.getBlockNumber();
64
65
  };
65
66
  const buildContractInfoMap = (contracts) => {
66
67
  const map = /* @__PURE__ */ new Map();
@@ -1,9 +1,11 @@
1
1
  require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_builtin_contracts = require("./builtin-contracts.cjs");
3
+ const require_errors = require("../errors/errors.cjs");
4
+ require("../errors/index.cjs");
5
+ const require_client = require("./client.cjs");
2
6
  const require_context = require("./context.cjs");
3
7
  const require_replay_cache = require("./replay-cache.cjs");
4
8
  const require_debug = require("../debug.cjs");
5
- const require_errors = require("../errors/errors.cjs");
6
- require("../errors/index.cjs");
7
9
  const require_contract_caller = require("./contract-caller.cjs");
8
10
  const require_error_wrapper = require("./error-wrapper.cjs");
9
11
  const require_call_collector = require("../analysis/call-collector.cjs");
@@ -70,17 +72,22 @@ var SELRuntime = class {
70
72
  this.maxRounds = limits?.maxRounds ?? 10;
71
73
  this.maxCalls = limits?.maxCalls ?? 100;
72
74
  this.multicallOptions = config.multicall;
75
+ if (config.client) require_client.validateClient(config.client);
73
76
  this.client = config.client;
74
- this.schema = config.schema;
77
+ this.schema = {
78
+ ...config.schema,
79
+ contracts: [...config.schema.contracts, ...require_builtin_contracts.getBuiltinContracts(config.multicall)]
80
+ };
75
81
  const handler = (contractName, methodName, args) => {
76
- const contract = this.findContract(contractName);
77
- if (!contract) throw new require_errors.SELContractError(`Unknown contract "${contractName}"`, { contractName });
78
- const method = contract.methods.find((m) => m.name === methodName);
79
- if (!method) throw new require_errors.SELContractError(`Unknown method "${contractName}.${methodName}"`, {
80
- contractName,
81
- methodName
82
+ const resolved = this.resolveCall(contractName, methodName, args);
83
+ const contract = this.findContract(resolved.contractName);
84
+ if (!contract) throw new require_errors.SELContractError(`Unknown contract "${resolved.contractName}"`, { contractName: resolved.contractName });
85
+ const method = contract.methods.find((m) => m.name === resolved.methodName);
86
+ if (!method) throw new require_errors.SELContractError(`Unknown method "${resolved.contractName}.${resolved.methodName}"`, {
87
+ contractName: resolved.contractName,
88
+ methodName: resolved.methodName
82
89
  });
83
- return require_contract_caller.executeContractCall(contract, method, args, {
90
+ return require_contract_caller.executeContractCall(contract, method, resolved.args, {
84
91
  executionCache: this.currentCache,
85
92
  client: this.currentClient ?? this.client,
86
93
  codecRegistry: this.codecRegistry,
@@ -207,6 +214,7 @@ var SELRuntime = class {
207
214
  async doEvaluate(expression, context, options) {
208
215
  debug("evaluate: %s", expression);
209
216
  const resolvedClient = options?.client ?? this.client;
217
+ if (resolvedClient && resolvedClient !== this.client) require_client.validateClient(resolvedClient);
210
218
  try {
211
219
  const { parseResult, collectedCalls, normalizedContext, executionVariables, typeCheckResult, diagnostics } = this.planExecution(expression, context ?? {});
212
220
  let executionMeta;
@@ -238,12 +246,29 @@ var SELRuntime = class {
238
246
  value,
239
247
  meta: executionMeta
240
248
  } : { value };
249
+ if (typeCheckResult.type) evalResult.type = typeCheckResult.type;
241
250
  if (diagnostics.length > 0) evalResult.diagnostics = diagnostics;
242
251
  return evalResult;
243
252
  } catch (error) {
244
253
  throw require_error_wrapper.wrapError(error);
245
254
  }
246
255
  }
256
+ /**
257
+ * Remaps receiver method calls to their underlying contract calls.
258
+ * e.g., sol_address.balance() → __multicall3.getEthBalance(addr)
259
+ */
260
+ resolveCall(contractName, methodName, args) {
261
+ if (contractName === "sol_address" && methodName === "balance") return {
262
+ contractName: "__multicall3",
263
+ methodName: "getEthBalance",
264
+ args
265
+ };
266
+ return {
267
+ contractName,
268
+ methodName,
269
+ args
270
+ };
271
+ }
247
272
  findContract(name) {
248
273
  return this.schema.contracts.find((c) => c.name === name);
249
274
  }
@@ -79,6 +79,11 @@ declare class SELRuntime {
79
79
  private planExecution;
80
80
  private executeContractCalls;
81
81
  private doEvaluate;
82
+ /**
83
+ * Remaps receiver method calls to their underlying contract calls.
84
+ * e.g., sol_address.balance() → __multicall3.getEthBalance(addr)
85
+ */
86
+ private resolveCall;
82
87
  private findContract;
83
88
  }
84
89
  //#endregion
@@ -79,6 +79,11 @@ declare class SELRuntime {
79
79
  private planExecution;
80
80
  private executeContractCalls;
81
81
  private doEvaluate;
82
+ /**
83
+ * Remaps receiver method calls to their underlying contract calls.
84
+ * e.g., sol_address.balance() → __multicall3.getEthBalance(addr)
85
+ */
86
+ private resolveCall;
82
87
  private findContract;
83
88
  }
84
89
  //#endregion
@@ -1,8 +1,10 @@
1
+ import { getBuiltinContracts } from "./builtin-contracts.mjs";
2
+ import { MulticallBatchError, SELContractError, SELLintError, SELTypeError } from "../errors/errors.mjs";
3
+ import "../errors/index.mjs";
4
+ import { validateClient } from "./client.mjs";
1
5
  import { normalizeContextForEvaluation } from "./context.mjs";
2
6
  import { buildExecutionReplayCache } from "./replay-cache.mjs";
3
7
  import { createLogger } from "../debug.mjs";
4
- import { MulticallBatchError, SELContractError, SELLintError, SELTypeError } from "../errors/errors.mjs";
5
- import "../errors/index.mjs";
6
8
  import { CallCounter, buildContractInfoMap, executeContractCall, resolveExecutionBlockNumber } from "./contract-caller.mjs";
7
9
  import { wrapError } from "./error-wrapper.mjs";
8
10
  import { collectCalls } from "../analysis/call-collector.mjs";
@@ -68,17 +70,22 @@ var SELRuntime = class {
68
70
  this.maxRounds = limits?.maxRounds ?? 10;
69
71
  this.maxCalls = limits?.maxCalls ?? 100;
70
72
  this.multicallOptions = config.multicall;
73
+ if (config.client) validateClient(config.client);
71
74
  this.client = config.client;
72
- this.schema = config.schema;
75
+ this.schema = {
76
+ ...config.schema,
77
+ contracts: [...config.schema.contracts, ...getBuiltinContracts(config.multicall)]
78
+ };
73
79
  const handler = (contractName, methodName, args) => {
74
- const contract = this.findContract(contractName);
75
- if (!contract) throw new SELContractError(`Unknown contract "${contractName}"`, { contractName });
76
- const method = contract.methods.find((m) => m.name === methodName);
77
- if (!method) throw new SELContractError(`Unknown method "${contractName}.${methodName}"`, {
78
- contractName,
79
- methodName
80
+ const resolved = this.resolveCall(contractName, methodName, args);
81
+ const contract = this.findContract(resolved.contractName);
82
+ if (!contract) throw new SELContractError(`Unknown contract "${resolved.contractName}"`, { contractName: resolved.contractName });
83
+ const method = contract.methods.find((m) => m.name === resolved.methodName);
84
+ if (!method) throw new SELContractError(`Unknown method "${resolved.contractName}.${resolved.methodName}"`, {
85
+ contractName: resolved.contractName,
86
+ methodName: resolved.methodName
80
87
  });
81
- return executeContractCall(contract, method, args, {
88
+ return executeContractCall(contract, method, resolved.args, {
82
89
  executionCache: this.currentCache,
83
90
  client: this.currentClient ?? this.client,
84
91
  codecRegistry: this.codecRegistry,
@@ -205,6 +212,7 @@ var SELRuntime = class {
205
212
  async doEvaluate(expression, context, options) {
206
213
  debug("evaluate: %s", expression);
207
214
  const resolvedClient = options?.client ?? this.client;
215
+ if (resolvedClient && resolvedClient !== this.client) validateClient(resolvedClient);
208
216
  try {
209
217
  const { parseResult, collectedCalls, normalizedContext, executionVariables, typeCheckResult, diagnostics } = this.planExecution(expression, context ?? {});
210
218
  let executionMeta;
@@ -236,12 +244,29 @@ var SELRuntime = class {
236
244
  value,
237
245
  meta: executionMeta
238
246
  } : { value };
247
+ if (typeCheckResult.type) evalResult.type = typeCheckResult.type;
239
248
  if (diagnostics.length > 0) evalResult.diagnostics = diagnostics;
240
249
  return evalResult;
241
250
  } catch (error) {
242
251
  throw wrapError(error);
243
252
  }
244
253
  }
254
+ /**
255
+ * Remaps receiver method calls to their underlying contract calls.
256
+ * e.g., sol_address.balance() → __multicall3.getEthBalance(addr)
257
+ */
258
+ resolveCall(contractName, methodName, args) {
259
+ if (contractName === "sol_address" && methodName === "balance") return {
260
+ contractName: "__multicall3",
261
+ methodName: "getEthBalance",
262
+ args
263
+ };
264
+ return {
265
+ contractName,
266
+ methodName,
267
+ args
268
+ };
269
+ }
245
270
  findContract(name) {
246
271
  return this.schema.contracts.find((c) => c.name === name);
247
272
  }
@@ -1 +1,2 @@
1
+ require("./client.cjs");
1
2
  require("./environment.cjs");
@@ -1,2 +1,3 @@
1
+ import { SELClient, validateClient } from "./client.mjs";
1
2
  import { MulticallOptions, SELLimits, SELRuntimeConfig } from "./types.mjs";
2
3
  import { SELRuntime } from "./environment.mjs";
@@ -1,2 +1,3 @@
1
+ import "./client.mjs";
1
2
  import "./environment.mjs";
2
3
  export {};
@@ -1,6 +1,6 @@
1
+ import { SELClient } from "./client.cjs";
1
2
  import { SELCheckerOptions } from "@seljs/checker";
2
3
  import { SELSchema } from "@seljs/schema";
3
- import { Address, PublicClient } from "viem";
4
4
 
5
5
  //#region src/environment/types.d.ts
6
6
  /**
@@ -13,7 +13,7 @@ interface MulticallOptions {
13
13
  /** Maximum number of calls per multicall3 batch. Unbounded if omitted. */
14
14
  batchSize?: number;
15
15
  /** Custom multicall3 contract address. Defaults to the canonical deployment. */
16
- address?: Address;
16
+ address?: `0x${string}`;
17
17
  }
18
18
  /**
19
19
  * Limits for contract call execution.
@@ -36,8 +36,8 @@ interface SELLimits {
36
36
  interface SELRuntimeConfig extends SELCheckerOptions {
37
37
  /** SEL schema describing contracts, variables, types, functions, and macros */
38
38
  schema: SELSchema;
39
- /** Viem public client for executing on-chain contract reads */
40
- client?: PublicClient;
39
+ /** Client for on-chain reads. Any viem PublicClient or SELClient-compatible object. */
40
+ client?: SELClient;
41
41
  /** Multicall3 batching options for contract call execution */
42
42
  multicall?: MulticallOptions;
43
43
  /** Execution limits for contract call rounds and total calls */
@@ -1,5 +1,5 @@
1
+ import { SELClient } from "./client.mjs";
1
2
  import { SELCheckerOptions } from "@seljs/checker";
2
- import { Address, PublicClient } from "viem";
3
3
  import { SELSchema } from "@seljs/schema";
4
4
 
5
5
  //#region src/environment/types.d.ts
@@ -13,7 +13,7 @@ interface MulticallOptions {
13
13
  /** Maximum number of calls per multicall3 batch. Unbounded if omitted. */
14
14
  batchSize?: number;
15
15
  /** Custom multicall3 contract address. Defaults to the canonical deployment. */
16
- address?: Address;
16
+ address?: `0x${string}`;
17
17
  }
18
18
  /**
19
19
  * Limits for contract call execution.
@@ -36,8 +36,8 @@ interface SELLimits {
36
36
  interface SELRuntimeConfig extends SELCheckerOptions {
37
37
  /** SEL schema describing contracts, variables, types, functions, and macros */
38
38
  schema: SELSchema;
39
- /** Viem public client for executing on-chain contract reads */
40
- client?: PublicClient;
39
+ /** Client for on-chain reads. Any viem PublicClient or SELClient-compatible object. */
40
+ client?: SELClient;
41
41
  /** Multicall3 batching options for contract call execution */
42
42
  multicall?: MulticallOptions;
43
43
  /** Execution limits for contract call rounds and total calls */
@@ -61,6 +61,12 @@ var MulticallBatchError = class extends _seljs_common.SELError {
61
61
  }
62
62
  };
63
63
  /**
64
+ * Thrown when the provided client fails validation or is misconfigured.
65
+ */
66
+ var SELClientError = class extends _seljs_common.SELError {
67
+ name = "SELClientError";
68
+ };
69
+ /**
64
70
  * Thrown when lint rules with error severity detect violations.
65
71
  * Contains the diagnostics that caused the failure.
66
72
  */
@@ -76,6 +82,7 @@ var SELLintError = class extends _seljs_common.SELError {
76
82
  exports.CircularDependencyError = CircularDependencyError;
77
83
  exports.ExecutionLimitError = ExecutionLimitError;
78
84
  exports.MulticallBatchError = MulticallBatchError;
85
+ exports.SELClientError = SELClientError;
79
86
  exports.SELContractError = SELContractError;
80
87
  exports.SELEvaluationError = SELEvaluationError;
81
88
  exports.SELLintError = SELLintError;
@@ -61,6 +61,12 @@ declare class MulticallBatchError extends SELError {
61
61
  methodName?: string;
62
62
  });
63
63
  }
64
+ /**
65
+ * Thrown when the provided client fails validation or is misconfigured.
66
+ */
67
+ declare class SELClientError extends SELError {
68
+ readonly name = "SELClientError";
69
+ }
64
70
  /**
65
71
  * Thrown when lint rules with error severity detect violations.
66
72
  * Contains the diagnostics that caused the failure.
@@ -72,4 +78,4 @@ declare class SELLintError extends SELError {
72
78
  });
73
79
  }
74
80
  //#endregion
75
- export { CircularDependencyError, ExecutionLimitError, MulticallBatchError, SELContractError, SELError, SELEvaluationError, SELLintError, SELParseError, SELTypeError };
81
+ export { CircularDependencyError, ExecutionLimitError, MulticallBatchError, SELClientError, SELContractError, SELError, SELEvaluationError, SELLintError, SELParseError, SELTypeError };