@steerprotocol/data-connector-loader 4.0.3 → 5.0.0-next.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Public API
4
4
 
5
- - `ExecutionAppConfig`: Configuration object for `ExecutionApp` (vault, chainId, subgraph URL, pool type flags, API keys, retries).
5
+ - `ExecutionAppConfig`: Configuration object for `ExecutionApp`. The legacy `poolType` flags are retained for compatibility only.
6
6
  - `ExecutionApp`: High-level execution entrypoint for running bundles with a config-based constructor.
7
7
  - `DataConnectorExecution`: Executes data connector bundles and handles request/response flow.
8
8
 
@@ -21,3 +21,52 @@ pnpm add @steerprotocol/data-connector-loader
21
21
  ## ESM Support
22
22
 
23
23
  This package publishes both CommonJS and ESM entrypoints via `exports`.
24
+
25
+ ## Prereleases
26
+
27
+ The `next` branch publishes prereleases under the npm `next` tag after CI passes.
28
+ The release workflow can also be run manually on `next` with `dry_run` enabled.
29
+ Versions are calculated from Git tags and conventional commits; a breaking
30
+ change must be marked as such for the first prerelease of a new major version.
31
+ The npm package must authorize `next-publish.yml` as a trusted publisher before
32
+ that workflow can publish.
33
+
34
+ ## Block-pinned strategy execution (recommended)
35
+
36
+ Supply a resolved `SmartPoolExecutionContext` to `ExecutionApp.executeWithContext`.
37
+ For connectors, pass an `ExecutionContext` to `DataConnectorExecution.executeWithContext`.
38
+ `SmartPoolExecutionContext` extends `ExecutionContext`, so the same resolved
39
+ context can be used for both. Both contexts include the chain ID for comparison
40
+ against the execution configuration. This path does not resolve vault or pool
41
+ state or infer the block or execution time.
42
+
43
+ ```ts
44
+ import { DataConnectorExecution, ExecutionApp } from '@steerprotocol/data-connector-loader';
45
+
46
+ // context is a previously resolved SmartPoolExecutionContext.
47
+ const vaultConfig = JSON.stringify({
48
+ vaultAddress: context.vaultAddress,
49
+ anchorBlockNumber: context.blockNumber,
50
+ strategyConfigData: { epochLength: String(context.epochLength) },
51
+ });
52
+ const connectorOutputs = await new DataConnectorExecution(connectors).executeWithContext(vaultConfig, context);
53
+ const actions = await new ExecutionApp(executionAppConfig).executeWithContext({
54
+ wasmFile: strategyWasmBytes,
55
+ vaultConfig,
56
+ data: connectorOutputs,
57
+ executionContext: context,
58
+ });
59
+ ```
60
+
61
+ Contexts must have matching vault, chain, block and epoch length values. Block
62
+ and timestamp fields are safe integer numbers, with `executionTimestamp` in
63
+ milliseconds and `epochTimestamp` in seconds. Positions use `startTick`,
64
+ `endTick`, and `weight`; totals remain decimal strings. The former stable SDK
65
+ `VaultExecutionContext` shape is not accepted by these entry points.
66
+ Callers migrating from that shape must supply the resolved chain ID and use
67
+ `SmartPoolExecutionContext` for strategies or `ExecutionContext` for connectors.
68
+ Errors are `ExecutionContextError`
69
+ (`INVALID_EXECUTION_CONTEXT` or `EXECUTION_CONTEXT_MISMATCH`) and
70
+ `StrategyExecutionError` (`STRATEGY_EXECUTION_ERROR`); neither is retryable
71
+ until its input or strategy failure is corrected. External connector HTTP data
72
+ remains external and unpinned.
@@ -1,7 +1,7 @@
1
1
  import type { WasmModule } from '@steerprotocol/app-loader';
2
2
  import { ethers } from 'ethers';
3
3
  import type { ExecutionAppConfig } from '../execution/executionApp.js';
4
- import type { EtherRpcConfig, ExecutionContext } from '../types.js';
4
+ import type { EtherRpcConfig, ExecutionContext, LegacyExecutionContext } from '../types.js';
5
5
  export declare class BundleBase {
6
6
  module?: WasmModule;
7
7
  protected wasmFile: ArrayBuffer | string;
@@ -11,7 +11,7 @@ export declare class BundleBase {
11
11
  };
12
12
  protected etherRpcConfig?: EtherRpcConfig;
13
13
  protected providerIdentity?: ethers.Provider;
14
- executionContext?: ExecutionContext & {
14
+ executionContext?: LegacyExecutionContext & {
15
15
  epochTimestamp: number;
16
16
  };
17
17
  protected scheduleEpochTimestamp?: number;
@@ -24,8 +24,15 @@ export declare class BundleBase {
24
24
  executeBundle(data: Array<any>): Promise<string>;
25
25
  executeDynamicJobBundle(data: Array<any>): Promise<string>;
26
26
  reset(): void;
27
- setExecutionContextStrategyExecution(executionContext: ExecutionContext, forceDeposit?: boolean): void;
28
- setExecutionContextDataConnector(executionContext: ExecutionContext): void;
27
+ setExecutionContextStrategyExecution(executionContext: LegacyExecutionContext, forceDeposit?: boolean): void;
28
+ /**
29
+ * Initializes a strategy from an already resolved SDK context. Unlike the
30
+ * legacy setter this deliberately does not derive a timestamp or block.
31
+ */
32
+ initializeStrategyWithContext(executionContext: ExecutionContext, forceDeposit?: boolean): void;
33
+ setExecutionContextDataConnector(executionContext: LegacyExecutionContext): void;
34
+ /** Initializes a data connector with an immutable SDK anchor. */
35
+ initializeDataConnectorWithContext(executionContext: ExecutionContext): void;
29
36
  protected executeModule(response: string): Promise<string | null>;
30
37
  protected executeModuleWithResult(...params: Array<number | string | ArrayBuffer | null>): Promise<string | null>;
31
38
  getProvider(config: any): ethers.Provider | undefined;
@@ -173,6 +173,20 @@ class BundleBase {
173
173
  }
174
174
  (_a = this.module) === null || _a === void 0 ? void 0 : _a.initialize(JSON.stringify(completeConfig, bigIntReplacer));
175
175
  }
176
+ /**
177
+ * Initializes a strategy from an already resolved SDK context. Unlike the
178
+ * legacy setter this deliberately does not derive a timestamp or block.
179
+ */
180
+ initializeStrategyWithContext(executionContext, forceDeposit) {
181
+ var _a;
182
+ this.executionContext = executionContext;
183
+ this.scheduleEpochTimestamp = executionContext.epochTimestamp;
184
+ const completeConfig = Object.assign(Object.assign({}, JSON.parse(this.config)), executionContext);
185
+ if (completeConfig.triggerStyle && forceDeposit === true) {
186
+ completeConfig.triggerStyle = 'None';
187
+ }
188
+ (_a = this.module) === null || _a === void 0 ? void 0 : _a.initialize(JSON.stringify(completeConfig, bigIntReplacer));
189
+ }
176
190
  setExecutionContextDataConnector(executionContext) {
177
191
  var _a;
178
192
  // Determine the timestamp for epoch
@@ -184,6 +198,14 @@ class BundleBase {
184
198
  this.scheduleEpochTimestamp = executionContext.epochTimestamp;
185
199
  (_a = this.module) === null || _a === void 0 ? void 0 : _a.initialize(JSON.stringify(completeConfig, bigIntReplacer));
186
200
  }
201
+ /** Initializes a data connector with an immutable SDK anchor. */
202
+ initializeDataConnectorWithContext(executionContext) {
203
+ var _a;
204
+ this.executionContext = executionContext;
205
+ this.scheduleEpochTimestamp = executionContext.epochTimestamp;
206
+ const completeConfig = Object.assign(Object.assign({}, JSON.parse(this.config)), { executionContext });
207
+ (_a = this.module) === null || _a === void 0 ? void 0 : _a.initialize(JSON.stringify(completeConfig, bigIntReplacer));
208
+ }
187
209
  executeModule(response) {
188
210
  return __awaiter(this, void 0, void 0, function* () {
189
211
  if (!this.module) {
@@ -39,7 +39,7 @@ class DataConnector extends bundleBase_js_1.BundleBase {
39
39
  }
40
40
  // Makes contract instance and calls, returns stringified result
41
41
  view(config) {
42
- var _a;
42
+ var _a, _b;
43
43
  return __awaiter(this, void 0, void 0, function* () {
44
44
  const provider = this.getProvider(config);
45
45
  if (!provider)
@@ -55,6 +55,7 @@ class DataConnector extends bundleBase_js_1.BundleBase {
55
55
  else {
56
56
  const viewContractConfig = {
57
57
  epochTimestamp: this.scheduleEpochTimestamp,
58
+ blockNumber: (_b = this.executionContext) === null || _b === void 0 ? void 0 : _b.blockNumber,
58
59
  address: this.configObj.address || config.address,
59
60
  abi: config.abi,
60
61
  arguments: paramsArray,
@@ -1,7 +1,7 @@
1
1
  import type { WasmModule } from '@steerprotocol/app-loader';
2
2
  import { ethers } from 'ethers';
3
3
  import type { ExecutionAppConfig } from '../execution/executionApp.js';
4
- import type { EtherRpcConfig, ExecutionContext } from '../types.js';
4
+ import type { EtherRpcConfig, ExecutionContext, LegacyExecutionContext } from '../types.js';
5
5
  export declare class BundleBase {
6
6
  module?: WasmModule;
7
7
  protected wasmFile: ArrayBuffer | string;
@@ -11,7 +11,7 @@ export declare class BundleBase {
11
11
  };
12
12
  protected etherRpcConfig?: EtherRpcConfig;
13
13
  protected providerIdentity?: ethers.Provider;
14
- executionContext?: ExecutionContext & {
14
+ executionContext?: LegacyExecutionContext & {
15
15
  epochTimestamp: number;
16
16
  };
17
17
  protected scheduleEpochTimestamp?: number;
@@ -24,8 +24,15 @@ export declare class BundleBase {
24
24
  executeBundle(data: Array<any>): Promise<string>;
25
25
  executeDynamicJobBundle(data: Array<any>): Promise<string>;
26
26
  reset(): void;
27
- setExecutionContextStrategyExecution(executionContext: ExecutionContext, forceDeposit?: boolean): void;
28
- setExecutionContextDataConnector(executionContext: ExecutionContext): void;
27
+ setExecutionContextStrategyExecution(executionContext: LegacyExecutionContext, forceDeposit?: boolean): void;
28
+ /**
29
+ * Initializes a strategy from an already resolved SDK context. Unlike the
30
+ * legacy setter this deliberately does not derive a timestamp or block.
31
+ */
32
+ initializeStrategyWithContext(executionContext: ExecutionContext, forceDeposit?: boolean): void;
33
+ setExecutionContextDataConnector(executionContext: LegacyExecutionContext): void;
34
+ /** Initializes a data connector with an immutable SDK anchor. */
35
+ initializeDataConnectorWithContext(executionContext: ExecutionContext): void;
29
36
  protected executeModule(response: string): Promise<string | null>;
30
37
  protected executeModuleWithResult(...params: Array<number | string | ArrayBuffer | null>): Promise<string | null>;
31
38
  getProvider(config: any): ethers.Provider | undefined;
@@ -150,6 +150,19 @@ export class BundleBase {
150
150
  }
151
151
  this.module?.initialize(JSON.stringify(completeConfig, bigIntReplacer));
152
152
  }
153
+ /**
154
+ * Initializes a strategy from an already resolved SDK context. Unlike the
155
+ * legacy setter this deliberately does not derive a timestamp or block.
156
+ */
157
+ initializeStrategyWithContext(executionContext, forceDeposit) {
158
+ this.executionContext = executionContext;
159
+ this.scheduleEpochTimestamp = executionContext.epochTimestamp;
160
+ const completeConfig = { ...JSON.parse(this.config), ...executionContext };
161
+ if (completeConfig.triggerStyle && forceDeposit === true) {
162
+ completeConfig.triggerStyle = 'None';
163
+ }
164
+ this.module?.initialize(JSON.stringify(completeConfig, bigIntReplacer));
165
+ }
153
166
  setExecutionContextDataConnector(executionContext) {
154
167
  // Determine the timestamp for epoch
155
168
  const timestamp = this.subtractDate(executionContext.executionTimestamp, Number(executionContext.epochLength)).getTime();
@@ -160,6 +173,13 @@ export class BundleBase {
160
173
  this.scheduleEpochTimestamp = executionContext.epochTimestamp;
161
174
  this.module?.initialize(JSON.stringify(completeConfig, bigIntReplacer));
162
175
  }
176
+ /** Initializes a data connector with an immutable SDK anchor. */
177
+ initializeDataConnectorWithContext(executionContext) {
178
+ this.executionContext = executionContext;
179
+ this.scheduleEpochTimestamp = executionContext.epochTimestamp;
180
+ const completeConfig = { ...JSON.parse(this.config), executionContext };
181
+ this.module?.initialize(JSON.stringify(completeConfig, bigIntReplacer));
182
+ }
163
183
  async executeModule(response) {
164
184
  if (!this.module) {
165
185
  throw new Error('Module not initialized');
@@ -41,6 +41,7 @@ export class DataConnector extends BundleBase {
41
41
  else {
42
42
  const viewContractConfig = {
43
43
  epochTimestamp: this.scheduleEpochTimestamp,
44
+ blockNumber: this.executionContext?.blockNumber,
44
45
  address: this.configObj.address || config.address,
45
46
  abi: config.abi,
46
47
  arguments: paramsArray,
@@ -1,5 +1,5 @@
1
1
  import type { ethers } from 'ethers';
2
- import type { EtherRpcConfig, JobDetails } from '../types.js';
2
+ import type { EtherRpcConfig, ExecuteWithContextOptions, JobDetails } from '../types.js';
3
3
  import { ExecutionBase } from './executionBase.js';
4
4
  export interface ExecutionAppConfig {
5
5
  /** Vault address used for execution context and subgraph queries. */
@@ -43,6 +43,15 @@ export declare class ExecutionApp extends ExecutionBase {
43
43
  config: ExecutionAppConfig;
44
44
  constructor(config: ExecutionAppConfig);
45
45
  execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
46
+ /**
47
+ * Executes a strategy against a supplied execution context.
48
+ * This is the recommended API for new consumers. It intentionally bypasses
49
+ * all legacy vault, pool, subgraph, clock, and block-estimation reads.
50
+ */
51
+ executeWithContext<T>(options: ExecuteWithContextOptions): Promise<Array<T>>;
52
+ private parseVaultConfig;
53
+ private validateExecutionContext;
54
+ private redactError;
46
55
  executeStabilityVault<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
47
56
  executeDynamicJobs<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _job: JobDetails, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
48
57
  private getSmartPoolExecutionContext;
@@ -1,4 +1,5 @@
1
1
  import { DataConnector } from '../bundle/bundleDataConnector.js';
2
+ import { ExecutionContextError, StrategyExecutionError } from '../types.js';
2
3
  import { fetchData, readContract } from '../utils/utils.js';
3
4
  import { ExecutionBase } from './executionBase.js';
4
5
  export class ExecutionApp extends ExecutionBase {
@@ -484,6 +485,94 @@ export class ExecutionApp extends ExecutionBase {
484
485
  const result = await dataConnector.executeBundle(_data);
485
486
  return JSON.parse(result);
486
487
  }
488
+ /**
489
+ * Executes a strategy against a supplied execution context.
490
+ * This is the recommended API for new consumers. It intentionally bypasses
491
+ * all legacy vault, pool, subgraph, clock, and block-estimation reads.
492
+ */
493
+ async executeWithContext(options) {
494
+ const vaultConfig = this.parseVaultConfig(options.vaultConfig);
495
+ const executionContext = options.executionContext;
496
+ this.validateExecutionContext(executionContext, vaultConfig);
497
+ const dataConnector = new DataConnector(options.wasmFile, options.vaultConfig, this.config, options.identity, options.etherRpcConfig);
498
+ try {
499
+ await dataConnector.loadSync();
500
+ dataConnector.initializeStrategyWithContext(executionContext, options.forceDeposit);
501
+ const result = await dataConnector.executeBundle(options.data.map((item) => JSON.stringify(item)));
502
+ return JSON.parse(result);
503
+ }
504
+ catch (error) {
505
+ if (error instanceof ExecutionContextError)
506
+ throw error;
507
+ throw new StrategyExecutionError(this.redactError(error));
508
+ }
509
+ }
510
+ parseVaultConfig(vaultConfig) {
511
+ try {
512
+ return JSON.parse(vaultConfig);
513
+ }
514
+ catch {
515
+ throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'vaultConfig must be valid JSON.');
516
+ }
517
+ }
518
+ validateExecutionContext(context, vaultConfig) {
519
+ if (!context || typeof context !== 'object') {
520
+ throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'An execution context is required.');
521
+ }
522
+ if (!Number.isSafeInteger(context.chainId) ||
523
+ !Number.isSafeInteger(context.blockNumber) ||
524
+ context.blockNumber < 0 ||
525
+ !Number.isSafeInteger(context.epochTimestamp) ||
526
+ context.epochTimestamp < 0 ||
527
+ !Number.isSafeInteger(context.executionTimestamp) ||
528
+ context.executionTimestamp < 0 ||
529
+ context.executionTimestamp !== context.epochTimestamp * 1000 ||
530
+ !Number.isFinite(context.epochLength) ||
531
+ !Number.isFinite(context.blockTime) ||
532
+ !Number.isInteger(context.currentTick) ||
533
+ !Number.isInteger(context.tickSpacing) ||
534
+ typeof context.vaultAddress !== 'string' ||
535
+ context.vaultAddress.length === 0 ||
536
+ typeof context.totalAmount0 !== 'string' ||
537
+ !/^\d+$/.test(context.totalAmount0) ||
538
+ typeof context.totalAmount1 !== 'string' ||
539
+ !/^\d+$/.test(context.totalAmount1) ||
540
+ !Array.isArray(context.positions) ||
541
+ !Number.isFinite(context.lastExecutionTime) ||
542
+ context.lastExecutionTime < 0) {
543
+ throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'Execution context is malformed.');
544
+ }
545
+ const sameAddress = (left, right) => left?.toLowerCase() === right?.toLowerCase();
546
+ if (!sameAddress(this.vault, context.vaultAddress) ||
547
+ !sameAddress(vaultConfig.vaultAddress, context.vaultAddress)) {
548
+ throw new ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context vault does not match the execution request.');
549
+ }
550
+ if (this.chainId !== context.chainId ||
551
+ (vaultConfig.chainId !== undefined && Number(vaultConfig.chainId) !== context.chainId)) {
552
+ throw new ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context chain does not match the execution request.');
553
+ }
554
+ if (vaultConfig.anchorBlockNumber !== undefined &&
555
+ String(vaultConfig.anchorBlockNumber) !== String(context.blockNumber)) {
556
+ throw new ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context block does not match anchorBlockNumber.');
557
+ }
558
+ if (Number(vaultConfig.strategyConfigData?.epochLength) !== context.epochLength) {
559
+ throw new ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context epoch length does not match vaultConfig.');
560
+ }
561
+ for (const position of context.positions) {
562
+ if (!position ||
563
+ !Number.isInteger(position.startTick) ||
564
+ !Number.isInteger(position.endTick) ||
565
+ !Number.isFinite(position.weight)) {
566
+ throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'Execution context contains an invalid position.');
567
+ }
568
+ }
569
+ }
570
+ redactError(error) {
571
+ const message = error instanceof Error ? error.message : String(error);
572
+ return message
573
+ .replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@')
574
+ .replace(/([?&](?:key|token|api[_-]?key|password)=)[^&\s]+/gi, '$1[REDACTED]');
575
+ }
487
576
  async executeStabilityVault(_wasmFile, _vaultConfig, _data, _identity, _etherRpcConfig, forceDeposit) {
488
577
  const dataConnector = new DataConnector(_wasmFile, _vaultConfig, this.config, _identity, _etherRpcConfig);
489
578
  await dataConnector.loadSync();
@@ -1,12 +1,12 @@
1
1
  import type { ethers } from 'ethers';
2
2
  import type { DataConnector } from '../bundle/bundleDataConnector.js';
3
- import type { EtherRpcConfig, ExecutionContext, VaultConfigPayload } from '../types.js';
3
+ import type { EtherRpcConfig, LegacyExecutionContext, VaultConfigPayload } from '../types.js';
4
4
  export declare class ExecutionBase {
5
5
  maxRetries: number;
6
6
  constructor(maxRetries?: number);
7
7
  protected delay(ms: number): Promise<void>;
8
8
  protected log(...args: Array<any>): void;
9
- protected getExecutionContext(dataConnector: DataConnector, vaultConfig: VaultConfigPayload): Promise<ExecutionContext>;
9
+ protected getExecutionContext(dataConnector: DataConnector, vaultConfig: VaultConfigPayload): Promise<LegacyExecutionContext>;
10
10
  execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig): Promise<Array<T>>;
11
11
  protected getDataConnectorVersion(dataConnector: DataConnector): Promise<number>;
12
12
  getAverageBlockTime(provider: ethers.Provider, blockCount?: number): Promise<number>;
@@ -1,9 +1,16 @@
1
1
  import type { DataConnector } from '../bundle/bundleDataConnector.js';
2
+ import { type ExecutionContext } from '../types.js';
2
3
  import { ExecutionBase } from './executionBase.js';
3
4
  export declare class DataConnectorExecution extends ExecutionBase {
4
5
  private dataConnectors;
5
6
  constructor(dataConnectors: Array<DataConnector>, maxRetries?: number);
6
7
  execute<T>(_vaultConfig: string): Promise<Array<T>>;
8
+ /**
9
+ * Runs connectors using the same immutable SDK anchor as strategy execution.
10
+ * Chain reads receive its exact blockNumber; HTTP and subgraph responses are
11
+ * intentionally not represented as block-pinned data.
12
+ */
13
+ executeWithContext<T>(_vaultConfig: string, context: ExecutionContext): Promise<Array<T>>;
7
14
  private executeV2Bundle;
8
15
  executeDynamicJob<T>(_vaultConfig: string): Promise<Array<T>>;
9
16
  private handleRequestResponse;
@@ -1,3 +1,4 @@
1
+ import { DataConnectorTransportError } from '../types.js';
1
2
  import { PluginManager } from '../utils/pluginManager.js';
2
3
  import { changeUrlIfOld } from '../utils/replace-old-subgraphs.js';
3
4
  import { extendOHLCV, fetchData } from '../utils/utils.js';
@@ -47,6 +48,55 @@ export class DataConnectorExecution extends ExecutionBase {
47
48
  }
48
49
  return transformationData;
49
50
  }
51
+ /**
52
+ * Runs connectors using the same immutable SDK anchor as strategy execution.
53
+ * Chain reads receive its exact blockNumber; HTTP and subgraph responses are
54
+ * intentionally not represented as block-pinned data.
55
+ */
56
+ async executeWithContext(_vaultConfig, context) {
57
+ const vaultConfig = JSON.parse(_vaultConfig);
58
+ if (!context ||
59
+ !Number.isSafeInteger(context.chainId) ||
60
+ !Number.isSafeInteger(context.blockNumber) ||
61
+ context.blockNumber < 0 ||
62
+ !Number.isSafeInteger(context.epochTimestamp) ||
63
+ context.epochTimestamp < 0 ||
64
+ !Number.isSafeInteger(context.executionTimestamp) ||
65
+ context.executionTimestamp < 0 ||
66
+ context.executionTimestamp !== context.epochTimestamp * 1000 ||
67
+ !Number.isFinite(context.epochLength) ||
68
+ !Number.isFinite(context.blockTime) ||
69
+ typeof context.vaultAddress !== 'string' ||
70
+ context.vaultAddress.length === 0) {
71
+ throw new Error('INVALID_EXECUTION_CONTEXT: supplied execution context is malformed.');
72
+ }
73
+ if (context.vaultAddress.toLowerCase() !== vaultConfig.vaultAddress.toLowerCase()) {
74
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context vault does not match vaultConfig.vaultAddress.');
75
+ }
76
+ const requestChainId = vaultConfig.chainId;
77
+ if (requestChainId !== undefined && requestChainId !== context.chainId) {
78
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match vaultConfig.chainId.');
79
+ }
80
+ if (vaultConfig.anchorBlockNumber !== undefined &&
81
+ String(vaultConfig.anchorBlockNumber) !== String(context.blockNumber)) {
82
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context block does not match anchorBlockNumber.');
83
+ }
84
+ if (Number(vaultConfig.strategyConfigData?.epochLength) !== context.epochLength) {
85
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context epoch length does not match vaultConfig.');
86
+ }
87
+ const transformationData = [];
88
+ for (const dataConnector of this.dataConnectors) {
89
+ if (dataConnector.executionAppConfig.chainId !== context.chainId) {
90
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match connector configuration.');
91
+ }
92
+ if (!dataConnector.module)
93
+ await dataConnector.load();
94
+ dataConnector.initializeDataConnectorWithContext(context);
95
+ await dataConnector.execute();
96
+ transformationData.push(JSON.parse(await dataConnector.transform()));
97
+ }
98
+ return transformationData;
99
+ }
50
100
  async executeV2Bundle(dataConnector, vaultConfig) {
51
101
  const executionContext = await this.getExecutionContext(dataConnector, vaultConfig);
52
102
  dataConnector.setExecutionContextDataConnector(executionContext);
@@ -88,7 +138,13 @@ export class DataConnectorExecution extends ExecutionBase {
88
138
  ...(url?.includes('sentio') ? { 'api-key': dataConnector.executionAppConfig.apiKeys.sentioApiKey } : {}),
89
139
  };
90
140
  await this.delay(1000);
91
- response = await fetchData(parsedRequest, 0);
141
+ try {
142
+ response = await fetchData(parsedRequest, 0);
143
+ }
144
+ catch (error) {
145
+ const message = error instanceof Error ? error.message : String(error);
146
+ throw new DataConnectorTransportError(message.replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@'));
147
+ }
92
148
  }
93
149
  const nextRequest = await dataConnector.execute(JSON.stringify(response));
94
150
  if (nextRequest === null) {
@@ -1,7 +1,8 @@
1
1
  import { DataConnector } from './bundle/bundleDataConnector.js';
2
2
  import { ExecutionApp, type ExecutionAppConfig } from './execution/executionApp.js';
3
3
  import { DataConnectorExecution } from './execution/executionDataConnector.js';
4
- import type { VaultConfigPayload } from './types.js';
4
+ import type { ExecuteWithContextOptions, VaultConfigPayload } from './types.js';
5
5
  export type * from './types.js';
6
6
  export { DataConnector, DataConnectorExecution, ExecutionApp };
7
- export type { ExecutionAppConfig, VaultConfigPayload };
7
+ export { DataConnectorTransportError, ExecutionContextError, StrategyExecutionError } from './types.js';
8
+ export type { ExecuteWithContextOptions, ExecutionAppConfig, VaultConfigPayload };
package/lib/esm/index.js CHANGED
@@ -2,3 +2,4 @@ import { DataConnector } from './bundle/bundleDataConnector.js';
2
2
  import { ExecutionApp } from './execution/executionApp.js';
3
3
  import { DataConnectorExecution } from './execution/executionDataConnector.js';
4
4
  export { DataConnector, DataConnectorExecution, ExecutionApp };
5
+ export { DataConnectorTransportError, ExecutionContextError, StrategyExecutionError } from './types.js';
@@ -1,4 +1,30 @@
1
- import type { InterfaceAbi } from 'ethers';
1
+ import type { ethers, InterfaceAbi } from 'ethers';
2
+ /** Error raised before a strategy is invoked when its supplied SDK anchor is unusable. */
3
+ export declare class ExecutionContextError extends Error {
4
+ readonly code: 'INVALID_EXECUTION_CONTEXT' | 'EXECUTION_CONTEXT_MISMATCH';
5
+ constructor(code: ExecutionContextError['code'], message: string);
6
+ }
7
+ /** Error classification for failures in a connector transport request. */
8
+ export declare class DataConnectorTransportError extends Error {
9
+ readonly code = "DATA_CONNECTOR_TRANSPORT_ERROR";
10
+ constructor(message: string);
11
+ }
12
+ /** Error classification for a WASM strategy failure. */
13
+ export declare class StrategyExecutionError extends Error {
14
+ readonly code = "STRATEGY_EXECUTION_ERROR";
15
+ constructor(message: string);
16
+ }
17
+ /** Options for the block-pinned SDK execution path. */
18
+ export interface ExecuteWithContextOptions {
19
+ wasmFile: string | ArrayBuffer;
20
+ vaultConfig: string;
21
+ data: Array<unknown>;
22
+ /** Resolved strategy execution context. */
23
+ executionContext: SmartPoolExecutionContext;
24
+ identity?: ethers.Provider;
25
+ etherRpcConfig?: EtherRpcConfig;
26
+ forceDeposit?: boolean;
27
+ }
2
28
  export type VaultInput = {
3
29
  _id?: string;
4
30
  tokenId: string;
@@ -50,6 +76,7 @@ export interface ConfigData {
50
76
  [key: string]: string | number;
51
77
  }
52
78
  export interface ExecutionContext {
79
+ chainId: number;
53
80
  executionTimestamp: number;
54
81
  epochLength: number;
55
82
  vaultAddress: string;
@@ -57,6 +84,8 @@ export interface ExecutionContext {
57
84
  blockNumber: number;
58
85
  epochTimestamp: number;
59
86
  }
87
+ /** Context produced by legacy execution paths, which may not have a chain binding. */
88
+ export type LegacyExecutionContext = Omit<ExecutionContext, 'chainId'> & Partial<Pick<ExecutionContext, 'chainId'>>;
60
89
  export interface Position {
61
90
  startTick: number;
62
91
  endTick: number;
@@ -100,6 +129,7 @@ export interface Slot0 {
100
129
  }
101
130
  export type ViewContractConfig = {
102
131
  epochTimestamp?: number;
132
+ blockNumber?: number;
103
133
  address: string;
104
134
  abi: any;
105
135
  arguments: Array<any>;
package/lib/esm/types.js CHANGED
@@ -1 +1,24 @@
1
- export {};
1
+ /** Error raised before a strategy is invoked when its supplied SDK anchor is unusable. */
2
+ export class ExecutionContextError extends Error {
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.name = 'ExecutionContextError';
6
+ this.code = code;
7
+ }
8
+ }
9
+ /** Error classification for failures in a connector transport request. */
10
+ export class DataConnectorTransportError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.code = 'DATA_CONNECTOR_TRANSPORT_ERROR';
14
+ this.name = 'DataConnectorTransportError';
15
+ }
16
+ }
17
+ /** Error classification for a WASM strategy failure. */
18
+ export class StrategyExecutionError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.code = 'STRATEGY_EXECUTION_ERROR';
22
+ this.name = 'StrategyExecutionError';
23
+ }
24
+ }
@@ -1,13 +1,13 @@
1
1
  import type { Candle } from '@steerprotocol/app-loader';
2
2
  import type { AxiosRequestConfig } from 'axios';
3
3
  import { type Provider } from 'ethers';
4
- import type { ExecutionContext, ViewContractConfig } from '../types.js';
4
+ import type { LegacyExecutionContext, ViewContractConfig } from '../types.js';
5
5
  export declare const extendOHLCV: (rawData: Array<Candle>, period: number, endTimestamp: number) => Array<Candle>;
6
6
  export declare function fetchData(request: AxiosRequestConfig, attempts: number): Promise<any>;
7
7
  export declare function readContract({ abi, address, chainId, contractArguments, currentExecutionContext, functionName, provider, rpc, }: {
8
8
  address: string;
9
9
  abi: any;
10
- currentExecutionContext: ExecutionContext;
10
+ currentExecutionContext: LegacyExecutionContext;
11
11
  functionName: string;
12
12
  provider: Provider;
13
13
  rpc: string;
@@ -83,6 +83,7 @@ export async function readContract({ abi, address, chainId, contractArguments =
83
83
  console.log('Calling the centralized view');
84
84
  const viewContractConfig = {
85
85
  epochTimestamp: currentExecutionContext.epochTimestamp,
86
+ blockNumber: currentExecutionContext.blockNumber,
86
87
  address,
87
88
  abi,
88
89
  arguments: paramsArray,
@@ -1,5 +1,5 @@
1
1
  import type { ethers } from 'ethers';
2
- import type { EtherRpcConfig, JobDetails } from '../types.js';
2
+ import type { EtherRpcConfig, ExecuteWithContextOptions, JobDetails } from '../types.js';
3
3
  import { ExecutionBase } from './executionBase.js';
4
4
  export interface ExecutionAppConfig {
5
5
  /** Vault address used for execution context and subgraph queries. */
@@ -43,6 +43,15 @@ export declare class ExecutionApp extends ExecutionBase {
43
43
  config: ExecutionAppConfig;
44
44
  constructor(config: ExecutionAppConfig);
45
45
  execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
46
+ /**
47
+ * Executes a strategy against a supplied execution context.
48
+ * This is the recommended API for new consumers. It intentionally bypasses
49
+ * all legacy vault, pool, subgraph, clock, and block-estimation reads.
50
+ */
51
+ executeWithContext<T>(options: ExecuteWithContextOptions): Promise<Array<T>>;
52
+ private parseVaultConfig;
53
+ private validateExecutionContext;
54
+ private redactError;
46
55
  executeStabilityVault<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
47
56
  executeDynamicJobs<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _job: JobDetails, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
48
57
  private getSmartPoolExecutionContext;
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.ExecutionApp = void 0;
13
13
  const bundleDataConnector_js_1 = require("../bundle/bundleDataConnector.js");
14
+ const types_js_1 = require("../types.js");
14
15
  const utils_js_1 = require("../utils/utils.js");
15
16
  const executionBase_js_1 = require("./executionBase.js");
16
17
  class ExecutionApp extends executionBase_js_1.ExecutionBase {
@@ -496,6 +497,97 @@ class ExecutionApp extends executionBase_js_1.ExecutionBase {
496
497
  return JSON.parse(result);
497
498
  });
498
499
  }
500
+ /**
501
+ * Executes a strategy against a supplied execution context.
502
+ * This is the recommended API for new consumers. It intentionally bypasses
503
+ * all legacy vault, pool, subgraph, clock, and block-estimation reads.
504
+ */
505
+ executeWithContext(options) {
506
+ return __awaiter(this, void 0, void 0, function* () {
507
+ const vaultConfig = this.parseVaultConfig(options.vaultConfig);
508
+ const executionContext = options.executionContext;
509
+ this.validateExecutionContext(executionContext, vaultConfig);
510
+ const dataConnector = new bundleDataConnector_js_1.DataConnector(options.wasmFile, options.vaultConfig, this.config, options.identity, options.etherRpcConfig);
511
+ try {
512
+ yield dataConnector.loadSync();
513
+ dataConnector.initializeStrategyWithContext(executionContext, options.forceDeposit);
514
+ const result = yield dataConnector.executeBundle(options.data.map((item) => JSON.stringify(item)));
515
+ return JSON.parse(result);
516
+ }
517
+ catch (error) {
518
+ if (error instanceof types_js_1.ExecutionContextError)
519
+ throw error;
520
+ throw new types_js_1.StrategyExecutionError(this.redactError(error));
521
+ }
522
+ });
523
+ }
524
+ parseVaultConfig(vaultConfig) {
525
+ try {
526
+ return JSON.parse(vaultConfig);
527
+ }
528
+ catch (_a) {
529
+ throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'vaultConfig must be valid JSON.');
530
+ }
531
+ }
532
+ validateExecutionContext(context, vaultConfig) {
533
+ var _a;
534
+ if (!context || typeof context !== 'object') {
535
+ throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'An execution context is required.');
536
+ }
537
+ if (!Number.isSafeInteger(context.chainId) ||
538
+ !Number.isSafeInteger(context.blockNumber) ||
539
+ context.blockNumber < 0 ||
540
+ !Number.isSafeInteger(context.epochTimestamp) ||
541
+ context.epochTimestamp < 0 ||
542
+ !Number.isSafeInteger(context.executionTimestamp) ||
543
+ context.executionTimestamp < 0 ||
544
+ context.executionTimestamp !== context.epochTimestamp * 1000 ||
545
+ !Number.isFinite(context.epochLength) ||
546
+ !Number.isFinite(context.blockTime) ||
547
+ !Number.isInteger(context.currentTick) ||
548
+ !Number.isInteger(context.tickSpacing) ||
549
+ typeof context.vaultAddress !== 'string' ||
550
+ context.vaultAddress.length === 0 ||
551
+ typeof context.totalAmount0 !== 'string' ||
552
+ !/^\d+$/.test(context.totalAmount0) ||
553
+ typeof context.totalAmount1 !== 'string' ||
554
+ !/^\d+$/.test(context.totalAmount1) ||
555
+ !Array.isArray(context.positions) ||
556
+ !Number.isFinite(context.lastExecutionTime) ||
557
+ context.lastExecutionTime < 0) {
558
+ throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'Execution context is malformed.');
559
+ }
560
+ const sameAddress = (left, right) => (left === null || left === void 0 ? void 0 : left.toLowerCase()) === (right === null || right === void 0 ? void 0 : right.toLowerCase());
561
+ if (!sameAddress(this.vault, context.vaultAddress) ||
562
+ !sameAddress(vaultConfig.vaultAddress, context.vaultAddress)) {
563
+ throw new types_js_1.ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context vault does not match the execution request.');
564
+ }
565
+ if (this.chainId !== context.chainId ||
566
+ (vaultConfig.chainId !== undefined && Number(vaultConfig.chainId) !== context.chainId)) {
567
+ throw new types_js_1.ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context chain does not match the execution request.');
568
+ }
569
+ if (vaultConfig.anchorBlockNumber !== undefined &&
570
+ String(vaultConfig.anchorBlockNumber) !== String(context.blockNumber)) {
571
+ throw new types_js_1.ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context block does not match anchorBlockNumber.');
572
+ }
573
+ if (Number((_a = vaultConfig.strategyConfigData) === null || _a === void 0 ? void 0 : _a.epochLength) !== context.epochLength) {
574
+ throw new types_js_1.ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context epoch length does not match vaultConfig.');
575
+ }
576
+ for (const position of context.positions) {
577
+ if (!position ||
578
+ !Number.isInteger(position.startTick) ||
579
+ !Number.isInteger(position.endTick) ||
580
+ !Number.isFinite(position.weight)) {
581
+ throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'Execution context contains an invalid position.');
582
+ }
583
+ }
584
+ }
585
+ redactError(error) {
586
+ const message = error instanceof Error ? error.message : String(error);
587
+ return message
588
+ .replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@')
589
+ .replace(/([?&](?:key|token|api[_-]?key|password)=)[^&\s]+/gi, '$1[REDACTED]');
590
+ }
499
591
  executeStabilityVault(_wasmFile, _vaultConfig, _data, _identity, _etherRpcConfig, forceDeposit) {
500
592
  return __awaiter(this, void 0, void 0, function* () {
501
593
  const dataConnector = new bundleDataConnector_js_1.DataConnector(_wasmFile, _vaultConfig, this.config, _identity, _etherRpcConfig);
@@ -1,12 +1,12 @@
1
1
  import type { ethers } from 'ethers';
2
2
  import type { DataConnector } from '../bundle/bundleDataConnector.js';
3
- import type { EtherRpcConfig, ExecutionContext, VaultConfigPayload } from '../types.js';
3
+ import type { EtherRpcConfig, LegacyExecutionContext, VaultConfigPayload } from '../types.js';
4
4
  export declare class ExecutionBase {
5
5
  maxRetries: number;
6
6
  constructor(maxRetries?: number);
7
7
  protected delay(ms: number): Promise<void>;
8
8
  protected log(...args: Array<any>): void;
9
- protected getExecutionContext(dataConnector: DataConnector, vaultConfig: VaultConfigPayload): Promise<ExecutionContext>;
9
+ protected getExecutionContext(dataConnector: DataConnector, vaultConfig: VaultConfigPayload): Promise<LegacyExecutionContext>;
10
10
  execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig): Promise<Array<T>>;
11
11
  protected getDataConnectorVersion(dataConnector: DataConnector): Promise<number>;
12
12
  getAverageBlockTime(provider: ethers.Provider, blockCount?: number): Promise<number>;
@@ -1,9 +1,16 @@
1
1
  import type { DataConnector } from '../bundle/bundleDataConnector.js';
2
+ import { type ExecutionContext } from '../types.js';
2
3
  import { ExecutionBase } from './executionBase.js';
3
4
  export declare class DataConnectorExecution extends ExecutionBase {
4
5
  private dataConnectors;
5
6
  constructor(dataConnectors: Array<DataConnector>, maxRetries?: number);
6
7
  execute<T>(_vaultConfig: string): Promise<Array<T>>;
8
+ /**
9
+ * Runs connectors using the same immutable SDK anchor as strategy execution.
10
+ * Chain reads receive its exact blockNumber; HTTP and subgraph responses are
11
+ * intentionally not represented as block-pinned data.
12
+ */
13
+ executeWithContext<T>(_vaultConfig: string, context: ExecutionContext): Promise<Array<T>>;
7
14
  private executeV2Bundle;
8
15
  executeDynamicJob<T>(_vaultConfig: string): Promise<Array<T>>;
9
16
  private handleRequestResponse;
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DataConnectorExecution = void 0;
13
+ const types_js_1 = require("../types.js");
13
14
  const pluginManager_js_1 = require("../utils/pluginManager.js");
14
15
  const replace_old_subgraphs_js_1 = require("../utils/replace-old-subgraphs.js");
15
16
  const utils_js_1 = require("../utils/utils.js");
@@ -61,6 +62,58 @@ class DataConnectorExecution extends executionBase_js_1.ExecutionBase {
61
62
  return transformationData;
62
63
  });
63
64
  }
65
+ /**
66
+ * Runs connectors using the same immutable SDK anchor as strategy execution.
67
+ * Chain reads receive its exact blockNumber; HTTP and subgraph responses are
68
+ * intentionally not represented as block-pinned data.
69
+ */
70
+ executeWithContext(_vaultConfig, context) {
71
+ var _a;
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ const vaultConfig = JSON.parse(_vaultConfig);
74
+ if (!context ||
75
+ !Number.isSafeInteger(context.chainId) ||
76
+ !Number.isSafeInteger(context.blockNumber) ||
77
+ context.blockNumber < 0 ||
78
+ !Number.isSafeInteger(context.epochTimestamp) ||
79
+ context.epochTimestamp < 0 ||
80
+ !Number.isSafeInteger(context.executionTimestamp) ||
81
+ context.executionTimestamp < 0 ||
82
+ context.executionTimestamp !== context.epochTimestamp * 1000 ||
83
+ !Number.isFinite(context.epochLength) ||
84
+ !Number.isFinite(context.blockTime) ||
85
+ typeof context.vaultAddress !== 'string' ||
86
+ context.vaultAddress.length === 0) {
87
+ throw new Error('INVALID_EXECUTION_CONTEXT: supplied execution context is malformed.');
88
+ }
89
+ if (context.vaultAddress.toLowerCase() !== vaultConfig.vaultAddress.toLowerCase()) {
90
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context vault does not match vaultConfig.vaultAddress.');
91
+ }
92
+ const requestChainId = vaultConfig.chainId;
93
+ if (requestChainId !== undefined && requestChainId !== context.chainId) {
94
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match vaultConfig.chainId.');
95
+ }
96
+ if (vaultConfig.anchorBlockNumber !== undefined &&
97
+ String(vaultConfig.anchorBlockNumber) !== String(context.blockNumber)) {
98
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context block does not match anchorBlockNumber.');
99
+ }
100
+ if (Number((_a = vaultConfig.strategyConfigData) === null || _a === void 0 ? void 0 : _a.epochLength) !== context.epochLength) {
101
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context epoch length does not match vaultConfig.');
102
+ }
103
+ const transformationData = [];
104
+ for (const dataConnector of this.dataConnectors) {
105
+ if (dataConnector.executionAppConfig.chainId !== context.chainId) {
106
+ throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match connector configuration.');
107
+ }
108
+ if (!dataConnector.module)
109
+ yield dataConnector.load();
110
+ dataConnector.initializeDataConnectorWithContext(context);
111
+ yield dataConnector.execute();
112
+ transformationData.push(JSON.parse(yield dataConnector.transform()));
113
+ }
114
+ return transformationData;
115
+ });
116
+ }
64
117
  executeV2Bundle(dataConnector, vaultConfig) {
65
118
  return __awaiter(this, void 0, void 0, function* () {
66
119
  const executionContext = yield this.getExecutionContext(dataConnector, vaultConfig);
@@ -104,7 +157,13 @@ class DataConnectorExecution extends executionBase_js_1.ExecutionBase {
104
157
  parsedRequest.url = url;
105
158
  parsedRequest.headers = Object.assign({ 'Content-Type': url && !url.includes('subgraph-proxy-server') ? 'application/json' : 'text/plain;charset=UTF-8' }, ((url === null || url === void 0 ? void 0 : url.includes('sentio')) ? { 'api-key': dataConnector.executionAppConfig.apiKeys.sentioApiKey } : {}));
106
159
  yield this.delay(1000);
107
- response = yield (0, utils_js_1.fetchData)(parsedRequest, 0);
160
+ try {
161
+ response = yield (0, utils_js_1.fetchData)(parsedRequest, 0);
162
+ }
163
+ catch (error) {
164
+ const message = error instanceof Error ? error.message : String(error);
165
+ throw new types_js_1.DataConnectorTransportError(message.replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@'));
166
+ }
108
167
  }
109
168
  const nextRequest = yield dataConnector.execute(JSON.stringify(response));
110
169
  if (nextRequest === null) {
package/lib/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { DataConnector } from './bundle/bundleDataConnector.js';
2
2
  import { ExecutionApp, type ExecutionAppConfig } from './execution/executionApp.js';
3
3
  import { DataConnectorExecution } from './execution/executionDataConnector.js';
4
- import type { VaultConfigPayload } from './types.js';
4
+ import type { ExecuteWithContextOptions, VaultConfigPayload } from './types.js';
5
5
  export type * from './types.js';
6
6
  export { DataConnector, DataConnectorExecution, ExecutionApp };
7
- export type { ExecutionAppConfig, VaultConfigPayload };
7
+ export { DataConnectorTransportError, ExecutionContextError, StrategyExecutionError } from './types.js';
8
+ export type { ExecuteWithContextOptions, ExecutionAppConfig, VaultConfigPayload };
package/lib/index.js CHANGED
@@ -1,9 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ExecutionApp = exports.DataConnectorExecution = exports.DataConnector = void 0;
3
+ exports.StrategyExecutionError = exports.ExecutionContextError = exports.DataConnectorTransportError = exports.ExecutionApp = exports.DataConnectorExecution = exports.DataConnector = void 0;
4
4
  const bundleDataConnector_js_1 = require("./bundle/bundleDataConnector.js");
5
5
  Object.defineProperty(exports, "DataConnector", { enumerable: true, get: function () { return bundleDataConnector_js_1.DataConnector; } });
6
6
  const executionApp_js_1 = require("./execution/executionApp.js");
7
7
  Object.defineProperty(exports, "ExecutionApp", { enumerable: true, get: function () { return executionApp_js_1.ExecutionApp; } });
8
8
  const executionDataConnector_js_1 = require("./execution/executionDataConnector.js");
9
9
  Object.defineProperty(exports, "DataConnectorExecution", { enumerable: true, get: function () { return executionDataConnector_js_1.DataConnectorExecution; } });
10
+ var types_js_1 = require("./types.js");
11
+ Object.defineProperty(exports, "DataConnectorTransportError", { enumerable: true, get: function () { return types_js_1.DataConnectorTransportError; } });
12
+ Object.defineProperty(exports, "ExecutionContextError", { enumerable: true, get: function () { return types_js_1.ExecutionContextError; } });
13
+ Object.defineProperty(exports, "StrategyExecutionError", { enumerable: true, get: function () { return types_js_1.StrategyExecutionError; } });
@@ -0,0 +1 @@
1
+ {"type": "commonjs"}
package/lib/types.d.ts CHANGED
@@ -1,4 +1,30 @@
1
- import type { InterfaceAbi } from 'ethers';
1
+ import type { ethers, InterfaceAbi } from 'ethers';
2
+ /** Error raised before a strategy is invoked when its supplied SDK anchor is unusable. */
3
+ export declare class ExecutionContextError extends Error {
4
+ readonly code: 'INVALID_EXECUTION_CONTEXT' | 'EXECUTION_CONTEXT_MISMATCH';
5
+ constructor(code: ExecutionContextError['code'], message: string);
6
+ }
7
+ /** Error classification for failures in a connector transport request. */
8
+ export declare class DataConnectorTransportError extends Error {
9
+ readonly code = "DATA_CONNECTOR_TRANSPORT_ERROR";
10
+ constructor(message: string);
11
+ }
12
+ /** Error classification for a WASM strategy failure. */
13
+ export declare class StrategyExecutionError extends Error {
14
+ readonly code = "STRATEGY_EXECUTION_ERROR";
15
+ constructor(message: string);
16
+ }
17
+ /** Options for the block-pinned SDK execution path. */
18
+ export interface ExecuteWithContextOptions {
19
+ wasmFile: string | ArrayBuffer;
20
+ vaultConfig: string;
21
+ data: Array<unknown>;
22
+ /** Resolved strategy execution context. */
23
+ executionContext: SmartPoolExecutionContext;
24
+ identity?: ethers.Provider;
25
+ etherRpcConfig?: EtherRpcConfig;
26
+ forceDeposit?: boolean;
27
+ }
2
28
  export type VaultInput = {
3
29
  _id?: string;
4
30
  tokenId: string;
@@ -50,6 +76,7 @@ export interface ConfigData {
50
76
  [key: string]: string | number;
51
77
  }
52
78
  export interface ExecutionContext {
79
+ chainId: number;
53
80
  executionTimestamp: number;
54
81
  epochLength: number;
55
82
  vaultAddress: string;
@@ -57,6 +84,8 @@ export interface ExecutionContext {
57
84
  blockNumber: number;
58
85
  epochTimestamp: number;
59
86
  }
87
+ /** Context produced by legacy execution paths, which may not have a chain binding. */
88
+ export type LegacyExecutionContext = Omit<ExecutionContext, 'chainId'> & Partial<Pick<ExecutionContext, 'chainId'>>;
60
89
  export interface Position {
61
90
  startTick: number;
62
91
  endTick: number;
@@ -100,6 +129,7 @@ export interface Slot0 {
100
129
  }
101
130
  export type ViewContractConfig = {
102
131
  epochTimestamp?: number;
132
+ blockNumber?: number;
103
133
  address: string;
104
134
  abi: any;
105
135
  arguments: Array<any>;
package/lib/types.js CHANGED
@@ -1,2 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StrategyExecutionError = exports.DataConnectorTransportError = exports.ExecutionContextError = void 0;
4
+ /** Error raised before a strategy is invoked when its supplied SDK anchor is unusable. */
5
+ class ExecutionContextError extends Error {
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.name = 'ExecutionContextError';
9
+ this.code = code;
10
+ }
11
+ }
12
+ exports.ExecutionContextError = ExecutionContextError;
13
+ /** Error classification for failures in a connector transport request. */
14
+ class DataConnectorTransportError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.code = 'DATA_CONNECTOR_TRANSPORT_ERROR';
18
+ this.name = 'DataConnectorTransportError';
19
+ }
20
+ }
21
+ exports.DataConnectorTransportError = DataConnectorTransportError;
22
+ /** Error classification for a WASM strategy failure. */
23
+ class StrategyExecutionError extends Error {
24
+ constructor(message) {
25
+ super(message);
26
+ this.code = 'STRATEGY_EXECUTION_ERROR';
27
+ this.name = 'StrategyExecutionError';
28
+ }
29
+ }
30
+ exports.StrategyExecutionError = StrategyExecutionError;
@@ -1,13 +1,13 @@
1
1
  import type { Candle } from '@steerprotocol/app-loader';
2
2
  import type { AxiosRequestConfig } from 'axios';
3
3
  import { type Provider } from 'ethers';
4
- import type { ExecutionContext, ViewContractConfig } from '../types.js';
4
+ import type { LegacyExecutionContext, ViewContractConfig } from '../types.js';
5
5
  export declare const extendOHLCV: (rawData: Array<Candle>, period: number, endTimestamp: number) => Array<Candle>;
6
6
  export declare function fetchData(request: AxiosRequestConfig, attempts: number): Promise<any>;
7
7
  export declare function readContract({ abi, address, chainId, contractArguments, currentExecutionContext, functionName, provider, rpc, }: {
8
8
  address: string;
9
9
  abi: any;
10
- currentExecutionContext: ExecutionContext;
10
+ currentExecutionContext: LegacyExecutionContext;
11
11
  functionName: string;
12
12
  provider: Provider;
13
13
  rpc: string;
@@ -105,6 +105,7 @@ function readContract({ abi, address, chainId, contractArguments = [], currentEx
105
105
  console.log('Calling the centralized view');
106
106
  const viewContractConfig = {
107
107
  epochTimestamp: currentExecutionContext.epochTimestamp,
108
+ blockNumber: currentExecutionContext.blockNumber,
108
109
  address,
109
110
  abi,
110
111
  arguments: paramsArray,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steerprotocol/data-connector-loader",
3
- "version": "4.0.3",
3
+ "version": "5.0.0-next.1",
4
4
  "description": "App Loader for Steer Protocol",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "scripts": {
18
18
  "build": "npm run build:cjs && npm run build:esm",
19
- "build:cjs": "tsc -p tsconfig.json",
19
+ "build:cjs": "tsc -p tsconfig.json && echo '{\"type\": \"commonjs\"}' > lib/package.json",
20
20
  "build:esm": "tsc -p tsconfig.esm.json && echo '{\"type\": \"module\"}' > lib/esm/package.json",
21
21
  "format": "prettier --write \"src/**/*.(js|ts)\"",
22
22
  "lint": "eslint src --ext .js,.ts",
@@ -24,6 +24,7 @@
24
24
  "lint:fix": "eslint src --fix --ext .js,.ts",
25
25
  "test": "jest --config jest.config.cjs",
26
26
  "pack:local": "npm pack",
27
+ "test:packed-consumer": "node scripts/packed-consumer-smoke.cjs",
27
28
  "publish:test": "node scripts/publish-test.cjs",
28
29
  "prepare": "husky && npm run build",
29
30
  "prepublishOnly": "npm test && npm run lint",
@@ -53,7 +54,12 @@
53
54
  "homepage": "https://github.com/SteerProtocol/data-connector-loader#readme",
54
55
  "release": {
55
56
  "branches": [
56
- "main"
57
+ "main",
58
+ {
59
+ "name": "next",
60
+ "prerelease": "next",
61
+ "channel": "next"
62
+ }
57
63
  ],
58
64
  "plugins": [
59
65
  [
@@ -82,10 +88,6 @@
82
88
  "@types/jest": "29.2.4",
83
89
  "@typescript-eslint/eslint-plugin": "5.46.0",
84
90
  "@typescript-eslint/parser": "5.47.1",
85
- "@uniswap/sdk-core": "^7.10.0",
86
- "@uniswap/v2-sdk": "^4.16.0",
87
- "@uniswap/v3-sdk": "^3.26.0",
88
- "date-fns": "^2.29.3",
89
91
  "eslint": "8.30.0",
90
92
  "eslint-plugin-jest": "27.1.7",
91
93
  "husky": "^9.1.7",
@@ -93,15 +95,14 @@
93
95
  "prettier": "2.8.1",
94
96
  "semantic-release": "^25.0.3",
95
97
  "ts-jest": "29.0.3",
96
- "typescript": "5.0.4",
97
- "viem": "^2.42.1"
98
+ "typescript": "5.0.4"
98
99
  },
99
100
  "files": [
100
101
  "lib/**/*"
101
102
  ],
102
103
  "dependencies": {
103
104
  "@steerprotocol/app-loader": "^3.0.6",
104
- "@steerprotocol/sdk": "^2.0.0",
105
+ "@steerprotocol/sdk": "^3.3.0",
105
106
  "@steerprotocol/strategy-utils": "^3.1.2",
106
107
  "axios": "^1.3.4",
107
108
  "ethers": "^6.16.0",