@steerprotocol/data-connector-loader 4.0.3 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -1
- package/lib/bundle/bundleBase.d.ts +7 -0
- package/lib/bundle/bundleBase.js +22 -0
- package/lib/bundle/bundleDataConnector.js +2 -1
- package/lib/esm/bundle/bundleBase.d.ts +7 -0
- package/lib/esm/bundle/bundleBase.js +20 -0
- package/lib/esm/bundle/bundleDataConnector.js +1 -0
- package/lib/esm/execution/executionApp.d.ts +10 -1
- package/lib/esm/execution/executionApp.js +112 -0
- package/lib/esm/execution/executionDataConnector.d.ts +7 -0
- package/lib/esm/execution/executionDataConnector.js +49 -1
- package/lib/esm/index.d.ts +3 -2
- package/lib/esm/index.js +1 -0
- package/lib/esm/types.d.ts +29 -1
- package/lib/esm/types.js +24 -1
- package/lib/esm/utils/utils.js +1 -0
- package/lib/execution/executionApp.d.ts +10 -1
- package/lib/execution/executionApp.js +115 -0
- package/lib/execution/executionDataConnector.d.ts +7 -0
- package/lib/execution/executionDataConnector.js +51 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.js +5 -1
- package/lib/types.d.ts +29 -1
- package/lib/types.js +28 -0
- package/lib/utils/utils.js +1 -0
- package/package.json +4 -8
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Public API
|
|
4
4
|
|
|
5
|
-
- `ExecutionAppConfig`: Configuration object for `ExecutionApp
|
|
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,38 @@ 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
|
+
## Block-pinned strategy execution (recommended)
|
|
26
|
+
|
|
27
|
+
Resolve the vault context with `@steerprotocol/sdk` 3.3+ and pass its successful
|
|
28
|
+
`VaultExecutionContext` straight to `executeWithContext`. This path makes no
|
|
29
|
+
pool, vault, subgraph, latest-block, average-block-time, or wall-clock reads.
|
|
30
|
+
Uniswap v3-compatible, Algebra Integral, Aerodrome CL, and normalized Uniswap
|
|
31
|
+
v4 contexts are supported. PoolShark remains unsupported by the SDK context API.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { ExecutionApp } from '@steerprotocol/data-connector-loader';
|
|
35
|
+
import { getVaultExecutionContext } from '@steerprotocol/sdk';
|
|
36
|
+
|
|
37
|
+
const response = await getVaultExecutionContext({ publicClient, vaultAddress, blockNumber, epochTimestamp });
|
|
38
|
+
if (!response.success || !response.data) throw new Error(response.error ?? 'Context resolution failed');
|
|
39
|
+
|
|
40
|
+
const app = new ExecutionApp(legacyCompatibleConfig);
|
|
41
|
+
const actions = await app.executeWithContext({
|
|
42
|
+
wasmFile: strategyWasmBytes,
|
|
43
|
+
vaultConfig: JSON.stringify({ vaultAddress, anchorBlockNumber: Number(blockNumber), strategyConfigData: { epochLength: '3600' } }),
|
|
44
|
+
data: connectorOutputs,
|
|
45
|
+
executionContext: response.data,
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`vaultConfig` must include `vaultAddress`, `anchorBlockNumber`, and
|
|
50
|
+
`strategyConfigData.epochLength`. Positions are translated at the WASM boundary
|
|
51
|
+
from SDK `lowerTick`/`upperTick`/`relativeWeight` to
|
|
52
|
+
`startTick`/`endTick`/`weight`; totals remain decimal strings. If SDK reports no
|
|
53
|
+
last execution, WASM receives `lastExecutionTime: 0` (deterministic, no
|
|
54
|
+
wall-clock fallback). Errors are `ExecutionContextError`
|
|
55
|
+
(`INVALID_EXECUTION_CONTEXT` or `EXECUTION_CONTEXT_MISMATCH`) and
|
|
56
|
+
`StrategyExecutionError` (`STRATEGY_EXECUTION_ERROR`); neither is retryable
|
|
57
|
+
until its input or strategy failure is corrected. External connector HTTP data
|
|
58
|
+
remains external and unpinned.
|
|
@@ -25,7 +25,14 @@ export declare class BundleBase {
|
|
|
25
25
|
executeDynamicJobBundle(data: Array<any>): Promise<string>;
|
|
26
26
|
reset(): void;
|
|
27
27
|
setExecutionContextStrategyExecution(executionContext: ExecutionContext, 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
|
+
setExecutionContextStrategyExecutionPinned(executionContext: ExecutionContext, forceDeposit?: boolean): void;
|
|
28
33
|
setExecutionContextDataConnector(executionContext: ExecutionContext): void;
|
|
34
|
+
/** Initializes a data connector with an immutable SDK anchor. */
|
|
35
|
+
setExecutionContextDataConnectorPinned(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;
|
package/lib/bundle/bundleBase.js
CHANGED
|
@@ -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
|
+
setExecutionContextStrategyExecutionPinned(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
|
+
setExecutionContextDataConnectorPinned(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,
|
|
@@ -25,7 +25,14 @@ export declare class BundleBase {
|
|
|
25
25
|
executeDynamicJobBundle(data: Array<any>): Promise<string>;
|
|
26
26
|
reset(): void;
|
|
27
27
|
setExecutionContextStrategyExecution(executionContext: ExecutionContext, 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
|
+
setExecutionContextStrategyExecutionPinned(executionContext: ExecutionContext, forceDeposit?: boolean): void;
|
|
28
33
|
setExecutionContextDataConnector(executionContext: ExecutionContext): void;
|
|
34
|
+
/** Initializes a data connector with an immutable SDK anchor. */
|
|
35
|
+
setExecutionContextDataConnectorPinned(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
|
+
setExecutionContextStrategyExecutionPinned(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
|
+
setExecutionContextDataConnectorPinned(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 the immutable context resolved by SDK 3.3+.
|
|
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 mapSdkExecutionContext;
|
|
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,117 @@ 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 the immutable context resolved by SDK 3.3+.
|
|
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 = this.mapSdkExecutionContext(options.executionContext, vaultConfig);
|
|
496
|
+
const dataConnector = new DataConnector(options.wasmFile, options.vaultConfig, this.config, options.identity, options.etherRpcConfig);
|
|
497
|
+
try {
|
|
498
|
+
await dataConnector.loadSync();
|
|
499
|
+
dataConnector.setExecutionContextStrategyExecutionPinned(executionContext, options.forceDeposit);
|
|
500
|
+
const result = await dataConnector.executeBundle(options.data.map((item) => JSON.stringify(item)));
|
|
501
|
+
return JSON.parse(result);
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
if (error instanceof ExecutionContextError)
|
|
505
|
+
throw error;
|
|
506
|
+
throw new StrategyExecutionError(this.redactError(error));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
parseVaultConfig(vaultConfig) {
|
|
510
|
+
try {
|
|
511
|
+
return JSON.parse(vaultConfig);
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'vaultConfig must be valid JSON.');
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
mapSdkExecutionContext(context, vaultConfig) {
|
|
518
|
+
if (!context || typeof context !== 'object') {
|
|
519
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'A successful SDK VaultExecutionContext is required.');
|
|
520
|
+
}
|
|
521
|
+
const requiredStringFields = [
|
|
522
|
+
'blockNumber',
|
|
523
|
+
'epochTimestamp',
|
|
524
|
+
'vaultAddress',
|
|
525
|
+
'totalAmount0',
|
|
526
|
+
'totalAmount1',
|
|
527
|
+
];
|
|
528
|
+
for (const field of requiredStringFields) {
|
|
529
|
+
if (typeof context[field] !== 'string' || context[field].length === 0) {
|
|
530
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', `SDK execution context has invalid ${field}.`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (!Number.isInteger(context.chainId) ||
|
|
534
|
+
!Number.isInteger(context.currentTick) ||
|
|
535
|
+
!Number.isInteger(context.tickSpacing)) {
|
|
536
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context has invalid chain or pool state.');
|
|
537
|
+
}
|
|
538
|
+
if (!Array.isArray(context.positions) ||
|
|
539
|
+
!/^\d+$/.test(context.blockNumber) ||
|
|
540
|
+
!/^\d+$/.test(context.epochTimestamp)) {
|
|
541
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context has malformed anchor fields.');
|
|
542
|
+
}
|
|
543
|
+
if (!/^\d+$/.test(context.totalAmount0) || !/^\d+$/.test(context.totalAmount1)) {
|
|
544
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context totals must be decimal strings.');
|
|
545
|
+
}
|
|
546
|
+
const sameAddress = (left, right) => left?.toLowerCase() === right?.toLowerCase();
|
|
547
|
+
if (!sameAddress(this.vault, context.vaultAddress) ||
|
|
548
|
+
!sameAddress(vaultConfig.vaultAddress, context.vaultAddress)) {
|
|
549
|
+
throw new ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context vault does not match the execution request.');
|
|
550
|
+
}
|
|
551
|
+
if (this.chainId !== context.chainId ||
|
|
552
|
+
(vaultConfig.chainId !== undefined && Number(vaultConfig.chainId) !== context.chainId)) {
|
|
553
|
+
throw new ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context chain does not match the execution request.');
|
|
554
|
+
}
|
|
555
|
+
if (vaultConfig.anchorBlockNumber !== undefined && String(vaultConfig.anchorBlockNumber) !== context.blockNumber) {
|
|
556
|
+
throw new ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context block does not match anchorBlockNumber.');
|
|
557
|
+
}
|
|
558
|
+
const epochLength = Number(vaultConfig.strategyConfigData?.epochLength);
|
|
559
|
+
if (!Number.isFinite(epochLength)) {
|
|
560
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'vaultConfig.strategyConfigData.epochLength is required.');
|
|
561
|
+
}
|
|
562
|
+
for (const position of context.positions) {
|
|
563
|
+
if (!Number.isInteger(position.lowerTick) ||
|
|
564
|
+
!Number.isInteger(position.upperTick) ||
|
|
565
|
+
!Number.isFinite(position.relativeWeight)) {
|
|
566
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context contains an invalid position.');
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
// Null is deterministic: WASM receives zero elapsed seconds, never wall-clock time.
|
|
570
|
+
const lastExecutionTime = context.lastExecutionTimeSeconds === null ? 0 : Number(context.lastExecutionTimeSeconds);
|
|
571
|
+
if (!Number.isFinite(lastExecutionTime) || lastExecutionTime < 0) {
|
|
572
|
+
throw new ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context has invalid lastExecutionTimeSeconds.');
|
|
573
|
+
}
|
|
574
|
+
return {
|
|
575
|
+
executionTimestamp: Number(context.epochTimestamp) * 1000,
|
|
576
|
+
epochTimestamp: Number(context.epochTimestamp),
|
|
577
|
+
epochLength,
|
|
578
|
+
vaultAddress: context.vaultAddress,
|
|
579
|
+
blockTime: 0,
|
|
580
|
+
blockNumber: Number(context.blockNumber),
|
|
581
|
+
currentTick: context.currentTick,
|
|
582
|
+
tickSpacing: context.tickSpacing,
|
|
583
|
+
positions: context.positions.map(({ lowerTick, upperTick, relativeWeight }) => ({
|
|
584
|
+
startTick: lowerTick,
|
|
585
|
+
endTick: upperTick,
|
|
586
|
+
weight: relativeWeight,
|
|
587
|
+
})),
|
|
588
|
+
totalAmount0: context.totalAmount0,
|
|
589
|
+
totalAmount1: context.totalAmount1,
|
|
590
|
+
lastExecutionTime,
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
redactError(error) {
|
|
594
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
595
|
+
return message
|
|
596
|
+
.replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@')
|
|
597
|
+
.replace(/([?&](?:key|token|api[_-]?key|password)=)[^&\s]+/gi, '$1[REDACTED]');
|
|
598
|
+
}
|
|
487
599
|
async executeStabilityVault(_wasmFile, _vaultConfig, _data, _identity, _etherRpcConfig, forceDeposit) {
|
|
488
600
|
const dataConnector = new DataConnector(_wasmFile, _vaultConfig, this.config, _identity, _etherRpcConfig);
|
|
489
601
|
await dataConnector.loadSync();
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { DataConnector } from '../bundle/bundleDataConnector.js';
|
|
2
|
+
import type { VaultExecutionContext } from '@steerprotocol/sdk';
|
|
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: VaultExecutionContext): 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,47 @@ 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 || !/^\d+$/.test(context.blockNumber) || !/^\d+$/.test(context.epochTimestamp)) {
|
|
59
|
+
throw new Error('INVALID_EXECUTION_CONTEXT: supplied SDK execution context is malformed.');
|
|
60
|
+
}
|
|
61
|
+
if (context.vaultAddress.toLowerCase() !== vaultConfig.vaultAddress.toLowerCase()) {
|
|
62
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context vault does not match vaultConfig.vaultAddress.');
|
|
63
|
+
}
|
|
64
|
+
const requestChainId = vaultConfig.chainId;
|
|
65
|
+
if (requestChainId !== undefined && requestChainId !== context.chainId) {
|
|
66
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match vaultConfig.chainId.');
|
|
67
|
+
}
|
|
68
|
+
if (vaultConfig.anchorBlockNumber !== undefined && String(vaultConfig.anchorBlockNumber) !== context.blockNumber) {
|
|
69
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context block does not match anchorBlockNumber.');
|
|
70
|
+
}
|
|
71
|
+
const executionContext = {
|
|
72
|
+
executionTimestamp: Number(context.epochTimestamp) * 1000,
|
|
73
|
+
epochTimestamp: Number(context.epochTimestamp),
|
|
74
|
+
epochLength: Number(vaultConfig.strategyConfigData.epochLength),
|
|
75
|
+
vaultAddress: context.vaultAddress,
|
|
76
|
+
blockTime: 0,
|
|
77
|
+
blockNumber: Number(context.blockNumber),
|
|
78
|
+
};
|
|
79
|
+
const transformationData = [];
|
|
80
|
+
for (const dataConnector of this.dataConnectors) {
|
|
81
|
+
if (dataConnector.executionAppConfig.chainId !== context.chainId) {
|
|
82
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match connector configuration.');
|
|
83
|
+
}
|
|
84
|
+
if (!dataConnector.module)
|
|
85
|
+
await dataConnector.load();
|
|
86
|
+
dataConnector.setExecutionContextDataConnectorPinned(executionContext);
|
|
87
|
+
await dataConnector.execute();
|
|
88
|
+
transformationData.push(JSON.parse(await dataConnector.transform()));
|
|
89
|
+
}
|
|
90
|
+
return transformationData;
|
|
91
|
+
}
|
|
50
92
|
async executeV2Bundle(dataConnector, vaultConfig) {
|
|
51
93
|
const executionContext = await this.getExecutionContext(dataConnector, vaultConfig);
|
|
52
94
|
dataConnector.setExecutionContextDataConnector(executionContext);
|
|
@@ -88,7 +130,13 @@ export class DataConnectorExecution extends ExecutionBase {
|
|
|
88
130
|
...(url?.includes('sentio') ? { 'api-key': dataConnector.executionAppConfig.apiKeys.sentioApiKey } : {}),
|
|
89
131
|
};
|
|
90
132
|
await this.delay(1000);
|
|
91
|
-
|
|
133
|
+
try {
|
|
134
|
+
response = await fetchData(parsedRequest, 0);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
138
|
+
throw new DataConnectorTransportError(message.replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@'));
|
|
139
|
+
}
|
|
92
140
|
}
|
|
93
141
|
const nextRequest = await dataConnector.execute(JSON.stringify(response));
|
|
94
142
|
if (nextRequest === null) {
|
package/lib/esm/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
|
|
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';
|
package/lib/esm/types.d.ts
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
|
-
import type { InterfaceAbi } from 'ethers';
|
|
1
|
+
import type { ethers, InterfaceAbi } from 'ethers';
|
|
2
|
+
import type { VaultExecutionContext } from '@steerprotocol/sdk';
|
|
3
|
+
/** Error raised before a strategy is invoked when its supplied SDK anchor is unusable. */
|
|
4
|
+
export declare class ExecutionContextError extends Error {
|
|
5
|
+
readonly code: 'INVALID_EXECUTION_CONTEXT' | 'EXECUTION_CONTEXT_MISMATCH';
|
|
6
|
+
constructor(code: ExecutionContextError['code'], message: string);
|
|
7
|
+
}
|
|
8
|
+
/** Error classification for failures in a connector transport request. */
|
|
9
|
+
export declare class DataConnectorTransportError extends Error {
|
|
10
|
+
readonly code = "DATA_CONNECTOR_TRANSPORT_ERROR";
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/** Error classification for a WASM strategy failure. */
|
|
14
|
+
export declare class StrategyExecutionError extends Error {
|
|
15
|
+
readonly code = "STRATEGY_EXECUTION_ERROR";
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
/** Options for the block-pinned SDK execution path. */
|
|
19
|
+
export interface ExecuteWithContextOptions {
|
|
20
|
+
wasmFile: string | ArrayBuffer;
|
|
21
|
+
vaultConfig: string;
|
|
22
|
+
data: Array<unknown>;
|
|
23
|
+
/** Successful, normalized context returned by @steerprotocol/sdk 3.3+. */
|
|
24
|
+
executionContext: VaultExecutionContext;
|
|
25
|
+
identity?: ethers.Provider;
|
|
26
|
+
etherRpcConfig?: EtherRpcConfig;
|
|
27
|
+
forceDeposit?: boolean;
|
|
28
|
+
}
|
|
2
29
|
export type VaultInput = {
|
|
3
30
|
_id?: string;
|
|
4
31
|
tokenId: string;
|
|
@@ -100,6 +127,7 @@ export interface Slot0 {
|
|
|
100
127
|
}
|
|
101
128
|
export type ViewContractConfig = {
|
|
102
129
|
epochTimestamp?: number;
|
|
130
|
+
blockNumber?: number;
|
|
103
131
|
address: string;
|
|
104
132
|
abi: any;
|
|
105
133
|
arguments: Array<any>;
|
package/lib/esm/types.js
CHANGED
|
@@ -1 +1,24 @@
|
|
|
1
|
-
|
|
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
|
+
}
|
package/lib/esm/utils/utils.js
CHANGED
|
@@ -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 the immutable context resolved by SDK 3.3+.
|
|
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 mapSdkExecutionContext;
|
|
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,120 @@ class ExecutionApp extends executionBase_js_1.ExecutionBase {
|
|
|
496
497
|
return JSON.parse(result);
|
|
497
498
|
});
|
|
498
499
|
}
|
|
500
|
+
/**
|
|
501
|
+
* Executes a strategy against the immutable context resolved by SDK 3.3+.
|
|
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 = this.mapSdkExecutionContext(options.executionContext, vaultConfig);
|
|
509
|
+
const dataConnector = new bundleDataConnector_js_1.DataConnector(options.wasmFile, options.vaultConfig, this.config, options.identity, options.etherRpcConfig);
|
|
510
|
+
try {
|
|
511
|
+
yield dataConnector.loadSync();
|
|
512
|
+
dataConnector.setExecutionContextStrategyExecutionPinned(executionContext, options.forceDeposit);
|
|
513
|
+
const result = yield dataConnector.executeBundle(options.data.map((item) => JSON.stringify(item)));
|
|
514
|
+
return JSON.parse(result);
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
if (error instanceof types_js_1.ExecutionContextError)
|
|
518
|
+
throw error;
|
|
519
|
+
throw new types_js_1.StrategyExecutionError(this.redactError(error));
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
parseVaultConfig(vaultConfig) {
|
|
524
|
+
try {
|
|
525
|
+
return JSON.parse(vaultConfig);
|
|
526
|
+
}
|
|
527
|
+
catch (_a) {
|
|
528
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'vaultConfig must be valid JSON.');
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
mapSdkExecutionContext(context, vaultConfig) {
|
|
532
|
+
var _a;
|
|
533
|
+
if (!context || typeof context !== 'object') {
|
|
534
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'A successful SDK VaultExecutionContext is required.');
|
|
535
|
+
}
|
|
536
|
+
const requiredStringFields = [
|
|
537
|
+
'blockNumber',
|
|
538
|
+
'epochTimestamp',
|
|
539
|
+
'vaultAddress',
|
|
540
|
+
'totalAmount0',
|
|
541
|
+
'totalAmount1',
|
|
542
|
+
];
|
|
543
|
+
for (const field of requiredStringFields) {
|
|
544
|
+
if (typeof context[field] !== 'string' || context[field].length === 0) {
|
|
545
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', `SDK execution context has invalid ${field}.`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (!Number.isInteger(context.chainId) ||
|
|
549
|
+
!Number.isInteger(context.currentTick) ||
|
|
550
|
+
!Number.isInteger(context.tickSpacing)) {
|
|
551
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context has invalid chain or pool state.');
|
|
552
|
+
}
|
|
553
|
+
if (!Array.isArray(context.positions) ||
|
|
554
|
+
!/^\d+$/.test(context.blockNumber) ||
|
|
555
|
+
!/^\d+$/.test(context.epochTimestamp)) {
|
|
556
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context has malformed anchor fields.');
|
|
557
|
+
}
|
|
558
|
+
if (!/^\d+$/.test(context.totalAmount0) || !/^\d+$/.test(context.totalAmount1)) {
|
|
559
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context totals must be decimal strings.');
|
|
560
|
+
}
|
|
561
|
+
const sameAddress = (left, right) => (left === null || left === void 0 ? void 0 : left.toLowerCase()) === (right === null || right === void 0 ? void 0 : right.toLowerCase());
|
|
562
|
+
if (!sameAddress(this.vault, context.vaultAddress) ||
|
|
563
|
+
!sameAddress(vaultConfig.vaultAddress, context.vaultAddress)) {
|
|
564
|
+
throw new types_js_1.ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context vault does not match the execution request.');
|
|
565
|
+
}
|
|
566
|
+
if (this.chainId !== context.chainId ||
|
|
567
|
+
(vaultConfig.chainId !== undefined && Number(vaultConfig.chainId) !== context.chainId)) {
|
|
568
|
+
throw new types_js_1.ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context chain does not match the execution request.');
|
|
569
|
+
}
|
|
570
|
+
if (vaultConfig.anchorBlockNumber !== undefined && String(vaultConfig.anchorBlockNumber) !== context.blockNumber) {
|
|
571
|
+
throw new types_js_1.ExecutionContextError('EXECUTION_CONTEXT_MISMATCH', 'Supplied context block does not match anchorBlockNumber.');
|
|
572
|
+
}
|
|
573
|
+
const epochLength = Number((_a = vaultConfig.strategyConfigData) === null || _a === void 0 ? void 0 : _a.epochLength);
|
|
574
|
+
if (!Number.isFinite(epochLength)) {
|
|
575
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'vaultConfig.strategyConfigData.epochLength is required.');
|
|
576
|
+
}
|
|
577
|
+
for (const position of context.positions) {
|
|
578
|
+
if (!Number.isInteger(position.lowerTick) ||
|
|
579
|
+
!Number.isInteger(position.upperTick) ||
|
|
580
|
+
!Number.isFinite(position.relativeWeight)) {
|
|
581
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context contains an invalid position.');
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Null is deterministic: WASM receives zero elapsed seconds, never wall-clock time.
|
|
585
|
+
const lastExecutionTime = context.lastExecutionTimeSeconds === null ? 0 : Number(context.lastExecutionTimeSeconds);
|
|
586
|
+
if (!Number.isFinite(lastExecutionTime) || lastExecutionTime < 0) {
|
|
587
|
+
throw new types_js_1.ExecutionContextError('INVALID_EXECUTION_CONTEXT', 'SDK execution context has invalid lastExecutionTimeSeconds.');
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
executionTimestamp: Number(context.epochTimestamp) * 1000,
|
|
591
|
+
epochTimestamp: Number(context.epochTimestamp),
|
|
592
|
+
epochLength,
|
|
593
|
+
vaultAddress: context.vaultAddress,
|
|
594
|
+
blockTime: 0,
|
|
595
|
+
blockNumber: Number(context.blockNumber),
|
|
596
|
+
currentTick: context.currentTick,
|
|
597
|
+
tickSpacing: context.tickSpacing,
|
|
598
|
+
positions: context.positions.map(({ lowerTick, upperTick, relativeWeight }) => ({
|
|
599
|
+
startTick: lowerTick,
|
|
600
|
+
endTick: upperTick,
|
|
601
|
+
weight: relativeWeight,
|
|
602
|
+
})),
|
|
603
|
+
totalAmount0: context.totalAmount0,
|
|
604
|
+
totalAmount1: context.totalAmount1,
|
|
605
|
+
lastExecutionTime,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
redactError(error) {
|
|
609
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
610
|
+
return message
|
|
611
|
+
.replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@')
|
|
612
|
+
.replace(/([?&](?:key|token|api[_-]?key|password)=)[^&\s]+/gi, '$1[REDACTED]');
|
|
613
|
+
}
|
|
499
614
|
executeStabilityVault(_wasmFile, _vaultConfig, _data, _identity, _etherRpcConfig, forceDeposit) {
|
|
500
615
|
return __awaiter(this, void 0, void 0, function* () {
|
|
501
616
|
const dataConnector = new bundleDataConnector_js_1.DataConnector(_wasmFile, _vaultConfig, this.config, _identity, _etherRpcConfig);
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { DataConnector } from '../bundle/bundleDataConnector.js';
|
|
2
|
+
import type { VaultExecutionContext } from '@steerprotocol/sdk';
|
|
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: VaultExecutionContext): 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,49 @@ 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
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
const vaultConfig = JSON.parse(_vaultConfig);
|
|
73
|
+
if (!context || !/^\d+$/.test(context.blockNumber) || !/^\d+$/.test(context.epochTimestamp)) {
|
|
74
|
+
throw new Error('INVALID_EXECUTION_CONTEXT: supplied SDK execution context is malformed.');
|
|
75
|
+
}
|
|
76
|
+
if (context.vaultAddress.toLowerCase() !== vaultConfig.vaultAddress.toLowerCase()) {
|
|
77
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context vault does not match vaultConfig.vaultAddress.');
|
|
78
|
+
}
|
|
79
|
+
const requestChainId = vaultConfig.chainId;
|
|
80
|
+
if (requestChainId !== undefined && requestChainId !== context.chainId) {
|
|
81
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match vaultConfig.chainId.');
|
|
82
|
+
}
|
|
83
|
+
if (vaultConfig.anchorBlockNumber !== undefined && String(vaultConfig.anchorBlockNumber) !== context.blockNumber) {
|
|
84
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context block does not match anchorBlockNumber.');
|
|
85
|
+
}
|
|
86
|
+
const executionContext = {
|
|
87
|
+
executionTimestamp: Number(context.epochTimestamp) * 1000,
|
|
88
|
+
epochTimestamp: Number(context.epochTimestamp),
|
|
89
|
+
epochLength: Number(vaultConfig.strategyConfigData.epochLength),
|
|
90
|
+
vaultAddress: context.vaultAddress,
|
|
91
|
+
blockTime: 0,
|
|
92
|
+
blockNumber: Number(context.blockNumber),
|
|
93
|
+
};
|
|
94
|
+
const transformationData = [];
|
|
95
|
+
for (const dataConnector of this.dataConnectors) {
|
|
96
|
+
if (dataConnector.executionAppConfig.chainId !== context.chainId) {
|
|
97
|
+
throw new Error('EXECUTION_CONTEXT_MISMATCH: supplied context chain does not match connector configuration.');
|
|
98
|
+
}
|
|
99
|
+
if (!dataConnector.module)
|
|
100
|
+
yield dataConnector.load();
|
|
101
|
+
dataConnector.setExecutionContextDataConnectorPinned(executionContext);
|
|
102
|
+
yield dataConnector.execute();
|
|
103
|
+
transformationData.push(JSON.parse(yield dataConnector.transform()));
|
|
104
|
+
}
|
|
105
|
+
return transformationData;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
64
108
|
executeV2Bundle(dataConnector, vaultConfig) {
|
|
65
109
|
return __awaiter(this, void 0, void 0, function* () {
|
|
66
110
|
const executionContext = yield this.getExecutionContext(dataConnector, vaultConfig);
|
|
@@ -104,7 +148,13 @@ class DataConnectorExecution extends executionBase_js_1.ExecutionBase {
|
|
|
104
148
|
parsedRequest.url = url;
|
|
105
149
|
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
150
|
yield this.delay(1000);
|
|
107
|
-
|
|
151
|
+
try {
|
|
152
|
+
response = yield (0, utils_js_1.fetchData)(parsedRequest, 0);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
156
|
+
throw new types_js_1.DataConnectorTransportError(message.replace(/https?:\/\/[^\s@/]+(?::[^\s@/]+)?@/gi, 'https://[REDACTED]@'));
|
|
157
|
+
}
|
|
108
158
|
}
|
|
109
159
|
const nextRequest = yield dataConnector.execute(JSON.stringify(response));
|
|
110
160
|
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
|
|
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; } });
|
package/lib/types.d.ts
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
|
-
import type { InterfaceAbi } from 'ethers';
|
|
1
|
+
import type { ethers, InterfaceAbi } from 'ethers';
|
|
2
|
+
import type { VaultExecutionContext } from '@steerprotocol/sdk';
|
|
3
|
+
/** Error raised before a strategy is invoked when its supplied SDK anchor is unusable. */
|
|
4
|
+
export declare class ExecutionContextError extends Error {
|
|
5
|
+
readonly code: 'INVALID_EXECUTION_CONTEXT' | 'EXECUTION_CONTEXT_MISMATCH';
|
|
6
|
+
constructor(code: ExecutionContextError['code'], message: string);
|
|
7
|
+
}
|
|
8
|
+
/** Error classification for failures in a connector transport request. */
|
|
9
|
+
export declare class DataConnectorTransportError extends Error {
|
|
10
|
+
readonly code = "DATA_CONNECTOR_TRANSPORT_ERROR";
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/** Error classification for a WASM strategy failure. */
|
|
14
|
+
export declare class StrategyExecutionError extends Error {
|
|
15
|
+
readonly code = "STRATEGY_EXECUTION_ERROR";
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
/** Options for the block-pinned SDK execution path. */
|
|
19
|
+
export interface ExecuteWithContextOptions {
|
|
20
|
+
wasmFile: string | ArrayBuffer;
|
|
21
|
+
vaultConfig: string;
|
|
22
|
+
data: Array<unknown>;
|
|
23
|
+
/** Successful, normalized context returned by @steerprotocol/sdk 3.3+. */
|
|
24
|
+
executionContext: VaultExecutionContext;
|
|
25
|
+
identity?: ethers.Provider;
|
|
26
|
+
etherRpcConfig?: EtherRpcConfig;
|
|
27
|
+
forceDeposit?: boolean;
|
|
28
|
+
}
|
|
2
29
|
export type VaultInput = {
|
|
3
30
|
_id?: string;
|
|
4
31
|
tokenId: string;
|
|
@@ -100,6 +127,7 @@ export interface Slot0 {
|
|
|
100
127
|
}
|
|
101
128
|
export type ViewContractConfig = {
|
|
102
129
|
epochTimestamp?: number;
|
|
130
|
+
blockNumber?: number;
|
|
103
131
|
address: string;
|
|
104
132
|
abi: any;
|
|
105
133
|
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;
|
package/lib/utils/utils.js
CHANGED
|
@@ -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
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "App Loader for Steer Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -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",
|
|
@@ -82,10 +83,6 @@
|
|
|
82
83
|
"@types/jest": "29.2.4",
|
|
83
84
|
"@typescript-eslint/eslint-plugin": "5.46.0",
|
|
84
85
|
"@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
86
|
"eslint": "8.30.0",
|
|
90
87
|
"eslint-plugin-jest": "27.1.7",
|
|
91
88
|
"husky": "^9.1.7",
|
|
@@ -93,15 +90,14 @@
|
|
|
93
90
|
"prettier": "2.8.1",
|
|
94
91
|
"semantic-release": "^25.0.3",
|
|
95
92
|
"ts-jest": "29.0.3",
|
|
96
|
-
"typescript": "5.0.4"
|
|
97
|
-
"viem": "^2.42.1"
|
|
93
|
+
"typescript": "5.0.4"
|
|
98
94
|
},
|
|
99
95
|
"files": [
|
|
100
96
|
"lib/**/*"
|
|
101
97
|
],
|
|
102
98
|
"dependencies": {
|
|
103
99
|
"@steerprotocol/app-loader": "^3.0.6",
|
|
104
|
-
"@steerprotocol/sdk": "^
|
|
100
|
+
"@steerprotocol/sdk": "^3.3.0",
|
|
105
101
|
"@steerprotocol/strategy-utils": "^3.1.2",
|
|
106
102
|
"axios": "^1.3.4",
|
|
107
103
|
"ethers": "^6.16.0",
|