@steerprotocol/data-connector-loader 3.1.0 → 3.2.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 +30 -1
- package/lib/bundle/bundleBase.d.ts +10 -5
- package/lib/bundle/bundleBase.js +65 -11
- package/lib/bundle/bundleDataConnector.d.ts +11 -4
- package/lib/bundle/bundleDataConnector.js +49 -13
- package/lib/esm/bundle/bundleBase.d.ts +10 -5
- package/lib/esm/bundle/bundleBase.js +60 -11
- package/lib/esm/bundle/bundleDataConnector.d.ts +11 -4
- package/lib/esm/bundle/bundleDataConnector.js +46 -11
- package/lib/esm/execution/executionApp.d.ts +52 -7
- package/lib/esm/execution/executionApp.js +557 -113
- package/lib/esm/execution/executionBase.d.ts +4 -4
- package/lib/esm/execution/executionBase.js +11 -12
- package/lib/esm/execution/executionDataConnector.d.ts +4 -2
- package/lib/esm/execution/executionDataConnector.js +56 -25
- package/lib/esm/index.d.ts +6 -4
- package/lib/esm/index.js +3 -4
- package/lib/esm/types.d.ts +17 -4
- package/lib/esm/utils/blockFinder.d.ts +1 -1
- package/lib/esm/utils/consts.js +43 -1
- package/lib/esm/utils/pluginManager.d.ts +1 -1
- package/lib/esm/utils/providerUtils.d.ts +4 -0
- package/lib/esm/utils/providerUtils.js +37 -0
- package/lib/esm/utils/replace-old-subgraphs.d.ts +23 -0
- package/lib/esm/utils/replace-old-subgraphs.js +123 -0
- package/lib/esm/utils/utils.d.ts +7 -6
- package/lib/esm/utils/utils.js +8 -9
- package/lib/execution/executionApp.d.ts +52 -7
- package/lib/execution/executionApp.js +556 -117
- package/lib/execution/executionBase.d.ts +4 -4
- package/lib/execution/executionBase.js +11 -12
- package/lib/execution/executionDataConnector.d.ts +4 -2
- package/lib/execution/executionDataConnector.js +61 -29
- package/lib/index.d.ts +6 -4
- package/lib/index.js +6 -21
- package/lib/types.d.ts +17 -4
- package/lib/utils/blockFinder.d.ts +1 -1
- package/lib/utils/consts.js +43 -1
- package/lib/utils/pluginManager.d.ts +1 -1
- package/lib/utils/providerUtils.d.ts +4 -0
- package/lib/utils/providerUtils.js +43 -0
- package/lib/utils/replace-old-subgraphs.d.ts +23 -0
- package/lib/utils/replace-old-subgraphs.js +129 -0
- package/lib/utils/utils.d.ts +7 -6
- package/lib/utils/utils.js +15 -16
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -1 +1,30 @@
|
|
|
1
|
-
# Steer Protocol App Loader
|
|
1
|
+
# Steer Protocol App Loader
|
|
2
|
+
|
|
3
|
+
## Public API
|
|
4
|
+
- `ExecutionAppConfig`: Configuration object for `ExecutionApp` (vault, chainId, subgraph URL, pool type flags, API keys, retries).
|
|
5
|
+
- `ExecutionApp`: High-level execution entrypoint for running bundles with a config-based constructor.
|
|
6
|
+
- `DataConnectorExecution`: Executes data connector bundles and handles request/response flow.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install test-data-connector-loader
|
|
12
|
+
# or
|
|
13
|
+
yarn add test-data-connector-loader
|
|
14
|
+
# or
|
|
15
|
+
pnpm add test-data-connector-loader
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The package includes an automatic postinstall script that fixes ESM/CJS interoperability issues with dependencies. No additional configuration is needed.
|
|
19
|
+
|
|
20
|
+
## ESM Support
|
|
21
|
+
|
|
22
|
+
This package is an ESM module (`"type": "module"`). It automatically fixes ESM compatibility issues in its dependencies during installation.
|
|
23
|
+
|
|
24
|
+
If you encounter import errors after installation, you can manually run the fix script:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm run postinstall
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For more details about the ESM fixes, see [docs/esm-fixes.md](docs/esm-fixes.md).
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { WasmModule } from '@steerprotocol/app-loader';
|
|
1
|
+
import type { WasmModule } from '@steerprotocol/app-loader';
|
|
2
2
|
import { ethers } from 'ethers';
|
|
3
|
-
import {
|
|
3
|
+
import type { ExecutionAppConfig } from '../execution/executionApp.js';
|
|
4
|
+
import type { EtherRpcConfig, ExecutionContext } from '../types.js';
|
|
4
5
|
export declare class BundleBase {
|
|
5
6
|
module?: WasmModule;
|
|
6
7
|
protected wasmFile: ArrayBuffer | string;
|
|
@@ -13,13 +14,17 @@ export declare class BundleBase {
|
|
|
13
14
|
executionContext?: ExecutionContext & {
|
|
14
15
|
epochTimestamp: number;
|
|
15
16
|
};
|
|
16
|
-
protected scheduleEpochTimestamp
|
|
17
|
-
|
|
17
|
+
protected scheduleEpochTimestamp?: number;
|
|
18
|
+
executionAppConfig: ExecutionAppConfig;
|
|
19
|
+
constructor(wasmFile: string | ArrayBuffer, _config: string, executionAppConfig: ExecutionAppConfig, identity?: ethers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
18
20
|
load(): Promise<WasmModule>;
|
|
19
21
|
loadSync(): WasmModule;
|
|
20
22
|
execute(response: string): Promise<string>;
|
|
23
|
+
transform(): Promise<string>;
|
|
21
24
|
executeBundle(data: Array<any>): Promise<string>;
|
|
22
|
-
|
|
25
|
+
executeDynamicJobBundle(data: Array<any>): Promise<string>;
|
|
26
|
+
reset(): void;
|
|
27
|
+
setExecutionContextStrategyExecution(executionContext: ExecutionContext, forceDeposit?: boolean): void;
|
|
23
28
|
setExecutionContextDataConnector(executionContext: ExecutionContext): void;
|
|
24
29
|
protected executeModule(response: string): Promise<string>;
|
|
25
30
|
getProvider(config: any): ethers.Provider | undefined;
|
package/lib/bundle/bundleBase.js
CHANGED
|
@@ -11,14 +11,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.BundleBase = void 0;
|
|
13
13
|
const app_loader_1 = require("@steerprotocol/app-loader");
|
|
14
|
-
const
|
|
14
|
+
const providerUtils_js_1 = require("../utils/providerUtils.js");
|
|
15
|
+
function bigIntReplacer(_key, value) {
|
|
16
|
+
return typeof value === 'bigint' ? value.toString() : value;
|
|
17
|
+
}
|
|
15
18
|
class BundleBase {
|
|
16
|
-
constructor(wasmFile, _config, identity, etherRpcConfig) {
|
|
19
|
+
constructor(wasmFile, _config, executionAppConfig, identity, etherRpcConfig) {
|
|
17
20
|
this.wasmFile = wasmFile;
|
|
18
21
|
this.config = _config;
|
|
19
22
|
this.configObj = JSON.parse(_config);
|
|
20
23
|
this.providerIdentity = identity;
|
|
21
24
|
this.etherRpcConfig = etherRpcConfig;
|
|
25
|
+
this.executionAppConfig = executionAppConfig;
|
|
22
26
|
}
|
|
23
27
|
load() {
|
|
24
28
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -29,8 +33,9 @@ class BundleBase {
|
|
|
29
33
|
});
|
|
30
34
|
}
|
|
31
35
|
loadSync() {
|
|
32
|
-
if (typeof this.wasmFile === 'string')
|
|
36
|
+
if (typeof this.wasmFile === 'string') {
|
|
33
37
|
throw new Error('Cannot load module synchronously unless wasmFile is of type ArrayBuffer');
|
|
38
|
+
}
|
|
34
39
|
this.module = (0, app_loader_1.loadWasmSync)(this.wasmFile, {});
|
|
35
40
|
return this.module;
|
|
36
41
|
}
|
|
@@ -55,6 +60,28 @@ class BundleBase {
|
|
|
55
60
|
return yield this.module.execute(response);
|
|
56
61
|
});
|
|
57
62
|
}
|
|
63
|
+
// For dynamic job data connector
|
|
64
|
+
transform() {
|
|
65
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
if (!this.executionContext) {
|
|
67
|
+
throw new Error('Execution context missing');
|
|
68
|
+
}
|
|
69
|
+
// If the module is not initialized, try to load it
|
|
70
|
+
if (!this.module) {
|
|
71
|
+
try {
|
|
72
|
+
yield this.load();
|
|
73
|
+
}
|
|
74
|
+
catch (_a) {
|
|
75
|
+
throw new Error('Module not initialized');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// If the module is still not initialized, throw an error
|
|
79
|
+
if (!this.module)
|
|
80
|
+
throw new Error('Module not initialized');
|
|
81
|
+
// Call the main function with the response
|
|
82
|
+
return yield this.module.transform();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
58
85
|
executeBundle(data) {
|
|
59
86
|
return __awaiter(this, void 0, void 0, function* () {
|
|
60
87
|
if (!this.executionContext) {
|
|
@@ -76,7 +103,33 @@ class BundleBase {
|
|
|
76
103
|
return this.module.execute(...data);
|
|
77
104
|
});
|
|
78
105
|
}
|
|
79
|
-
|
|
106
|
+
executeDynamicJobBundle(data) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
if (!this.executionContext) {
|
|
109
|
+
throw new Error('Execution context missing');
|
|
110
|
+
}
|
|
111
|
+
// If the module is not initialized, try to load it
|
|
112
|
+
if (!this.module) {
|
|
113
|
+
try {
|
|
114
|
+
yield this.load();
|
|
115
|
+
}
|
|
116
|
+
catch (_a) {
|
|
117
|
+
throw new Error('Module not initialized');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// If the module is still not initialized, throw an error
|
|
121
|
+
if (!this.module)
|
|
122
|
+
throw new Error('Module not initialized');
|
|
123
|
+
// Call the main function with the response
|
|
124
|
+
return this.module.execute(...data);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
reset() {
|
|
128
|
+
if (this.module && this.module['reset']) {
|
|
129
|
+
this.module.reset();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
setExecutionContextStrategyExecution(executionContext, forceDeposit) {
|
|
80
133
|
var _a;
|
|
81
134
|
// Determine the timestamp for epoch
|
|
82
135
|
const timestamp = this.subtractDate(executionContext.executionTimestamp, Number(executionContext.epochLength)).getTime();
|
|
@@ -85,8 +138,10 @@ class BundleBase {
|
|
|
85
138
|
this.executionContext = Object.assign(Object.assign({}, executionContext), { epochTimestamp: timestampInSecs });
|
|
86
139
|
const completeConfig = Object.assign(Object.assign({}, JSON.parse(this.config)), this.executionContext);
|
|
87
140
|
this.scheduleEpochTimestamp = executionContext.epochTimestamp;
|
|
88
|
-
|
|
89
|
-
|
|
141
|
+
if (completeConfig.triggerStyle && forceDeposit === true) {
|
|
142
|
+
completeConfig.triggerStyle = 'None';
|
|
143
|
+
}
|
|
144
|
+
(_a = this.module) === null || _a === void 0 ? void 0 : _a.initialize(JSON.stringify(completeConfig, bigIntReplacer));
|
|
90
145
|
}
|
|
91
146
|
setExecutionContextDataConnector(executionContext) {
|
|
92
147
|
var _a;
|
|
@@ -97,8 +152,7 @@ class BundleBase {
|
|
|
97
152
|
this.executionContext = Object.assign(Object.assign({}, executionContext), { epochTimestamp: timestampInSecs });
|
|
98
153
|
const completeConfig = Object.assign(Object.assign({}, JSON.parse(this.config)), { executionContext: this.executionContext });
|
|
99
154
|
this.scheduleEpochTimestamp = executionContext.epochTimestamp;
|
|
100
|
-
|
|
101
|
-
(_a = this.module) === null || _a === void 0 ? void 0 : _a.initialize(JSON.stringify(completeConfig));
|
|
155
|
+
(_a = this.module) === null || _a === void 0 ? void 0 : _a.initialize(JSON.stringify(completeConfig, bigIntReplacer));
|
|
102
156
|
}
|
|
103
157
|
executeModule(response) {
|
|
104
158
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -112,8 +166,8 @@ class BundleBase {
|
|
|
112
166
|
// The chainId is first checked in the data connector config, and if not found
|
|
113
167
|
// it is checked in the request config. If the chainId is found in either of
|
|
114
168
|
// these places, the provider is returned from the etherRpcConfig object
|
|
115
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
116
169
|
getProvider(config) {
|
|
170
|
+
var _a;
|
|
117
171
|
let chainId;
|
|
118
172
|
// check if chainId is configured in data connector config
|
|
119
173
|
if (this.configObj.chainId) {
|
|
@@ -124,8 +178,8 @@ class BundleBase {
|
|
|
124
178
|
chainId = config.chainId;
|
|
125
179
|
}
|
|
126
180
|
if (chainId && this.etherRpcConfig && this.etherRpcConfig[chainId]) {
|
|
127
|
-
const
|
|
128
|
-
return
|
|
181
|
+
const rpcConfig = this.etherRpcConfig[chainId];
|
|
182
|
+
return (0, providerUtils_js_1.createFallbackRpcProvider)((_a = rpcConfig.urls) !== null && _a !== void 0 ? _a : [rpcConfig.url]);
|
|
129
183
|
}
|
|
130
184
|
else {
|
|
131
185
|
return this.providerIdentity;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { ethers } from 'ethers';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { ethers } from 'ethers';
|
|
2
|
+
import type { ExecutionAppConfig } from '../execution/executionApp.js';
|
|
3
|
+
import type { EtherRpcConfig, ViewConfig } from '../types.js';
|
|
4
|
+
import { BundleBase } from './bundleBase.js';
|
|
4
5
|
export declare class DataConnector extends BundleBase {
|
|
5
|
-
constructor(wasmFile: string | ArrayBuffer, _config: string, identity?: ethers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
6
|
+
constructor(wasmFile: string | ArrayBuffer, _config: string, executionAppConfig: ExecutionAppConfig, identity?: ethers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
6
7
|
getRpcUrl(config: any): string | undefined;
|
|
7
8
|
view(config: ViewConfig): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Attempts to decode raw return data when the declared ABI doesn't match the actual
|
|
11
|
+
* contract output. Tries removing trailing outputs one-by-one until decoding succeeds.
|
|
12
|
+
* This handles pool variants that omit optional fields (e.g. feeProtocol in slot0).
|
|
13
|
+
*/
|
|
14
|
+
private decodeFallback;
|
|
8
15
|
}
|
|
@@ -11,13 +11,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.DataConnector = void 0;
|
|
13
13
|
const ethers_1 = require("ethers");
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
class DataConnector extends
|
|
14
|
+
const consts_js_1 = require("../utils/consts.js");
|
|
15
|
+
const utils_js_1 = require("../utils/utils.js");
|
|
16
|
+
const bundleBase_js_1 = require("./bundleBase.js");
|
|
17
|
+
class DataConnector extends bundleBase_js_1.BundleBase {
|
|
18
18
|
// Identity should come from the node's IdentityManager.wallet
|
|
19
|
-
constructor(wasmFile, _config, identity, etherRpcConfig) {
|
|
20
|
-
super(wasmFile, _config, identity, etherRpcConfig);
|
|
19
|
+
constructor(wasmFile, _config, executionAppConfig, identity, etherRpcConfig) {
|
|
20
|
+
super(wasmFile, _config, executionAppConfig, identity, etherRpcConfig);
|
|
21
21
|
}
|
|
22
22
|
// get rpc url from configuration
|
|
23
23
|
getRpcUrl(config) {
|
|
@@ -38,7 +38,6 @@ class DataConnector extends bundleBase_1.BundleBase {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
// Makes contract instance and calls, returns stringified result
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
41
|
view(config) {
|
|
43
42
|
var _a;
|
|
44
43
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -46,13 +45,25 @@ class DataConnector extends bundleBase_1.BundleBase {
|
|
|
46
45
|
if (!provider)
|
|
47
46
|
throw new Error('Provider not initialized');
|
|
48
47
|
const contract = new ethers_1.Contract(config.address, config.abi, provider);
|
|
49
|
-
// For formatting the parameters if needed
|
|
50
48
|
const paramsArray = config.arguments;
|
|
51
49
|
let result;
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
if (consts_js_1.CENTRALIZE_CONTRACT_INTERACTION_CHAINS.indexOf(this.configObj.chainId.toString()) === -1) {
|
|
51
|
+
try {
|
|
52
|
+
result = yield contract[config.method](...paramsArray, {
|
|
53
|
+
blockTag: (_a = this.executionContext) === null || _a === void 0 ? void 0 : _a.blockNumber,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
// Some pool variants return fewer ABI outputs than the standard spec (e.g. Aerodrome/
|
|
58
|
+
// Velodrome slot0 has 6 fields instead of 7 — no feeProtocol). When ethers fails to
|
|
59
|
+
// decode the raw return data, fall back to decoding with progressively fewer outputs.
|
|
60
|
+
if ((err === null || err === void 0 ? void 0 : err.code) === 'BAD_DATA' && (err === null || err === void 0 ? void 0 : err.value)) {
|
|
61
|
+
result = this.decodeFallback(err.value, config);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
56
67
|
}
|
|
57
68
|
else {
|
|
58
69
|
const viewContractConfig = {
|
|
@@ -63,11 +74,36 @@ class DataConnector extends bundleBase_1.BundleBase {
|
|
|
63
74
|
method: config.method,
|
|
64
75
|
rpcUrl: this.getRpcUrl(config),
|
|
65
76
|
};
|
|
66
|
-
result = yield (0,
|
|
77
|
+
result = yield (0, utils_js_1.callWithRetry)(viewContractConfig);
|
|
67
78
|
}
|
|
68
79
|
// This gets stringified before calling main
|
|
69
80
|
return result;
|
|
70
81
|
});
|
|
71
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Attempts to decode raw return data when the declared ABI doesn't match the actual
|
|
85
|
+
* contract output. Tries removing trailing outputs one-by-one until decoding succeeds.
|
|
86
|
+
* This handles pool variants that omit optional fields (e.g. feeProtocol in slot0).
|
|
87
|
+
*/
|
|
88
|
+
decodeFallback(rawData, config) {
|
|
89
|
+
var _a;
|
|
90
|
+
const iface = new ethers_1.Interface(config.abi);
|
|
91
|
+
const fragment = iface.getFunction(config.method);
|
|
92
|
+
if (!((_a = fragment === null || fragment === void 0 ? void 0 : fragment.outputs) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
93
|
+
throw new Error(`Cannot find outputs for method "${config.method}" in ABI`);
|
|
94
|
+
}
|
|
95
|
+
const coder = ethers_1.AbiCoder.defaultAbiCoder();
|
|
96
|
+
for (let numOutputs = fragment.outputs.length - 1; numOutputs >= 1; numOutputs--) {
|
|
97
|
+
try {
|
|
98
|
+
const types = fragment.outputs.slice(0, numOutputs).map((o) => o.format());
|
|
99
|
+
const decoded = coder.decode(types, rawData);
|
|
100
|
+
return decoded;
|
|
101
|
+
}
|
|
102
|
+
catch (_b) {
|
|
103
|
+
// try with one fewer output
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Could not decode result data for method "${config.method}" — no matching ABI shape found`);
|
|
107
|
+
}
|
|
72
108
|
}
|
|
73
109
|
exports.DataConnector = DataConnector;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { WasmModule } from '@steerprotocol/app-loader';
|
|
1
|
+
import type { WasmModule } from '@steerprotocol/app-loader';
|
|
2
2
|
import { ethers } from 'ethers';
|
|
3
|
-
import {
|
|
3
|
+
import type { ExecutionAppConfig } from '../execution/executionApp.js';
|
|
4
|
+
import type { EtherRpcConfig, ExecutionContext } from '../types.js';
|
|
4
5
|
export declare class BundleBase {
|
|
5
6
|
module?: WasmModule;
|
|
6
7
|
protected wasmFile: ArrayBuffer | string;
|
|
@@ -13,13 +14,17 @@ export declare class BundleBase {
|
|
|
13
14
|
executionContext?: ExecutionContext & {
|
|
14
15
|
epochTimestamp: number;
|
|
15
16
|
};
|
|
16
|
-
protected scheduleEpochTimestamp
|
|
17
|
-
|
|
17
|
+
protected scheduleEpochTimestamp?: number;
|
|
18
|
+
executionAppConfig: ExecutionAppConfig;
|
|
19
|
+
constructor(wasmFile: string | ArrayBuffer, _config: string, executionAppConfig: ExecutionAppConfig, identity?: ethers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
18
20
|
load(): Promise<WasmModule>;
|
|
19
21
|
loadSync(): WasmModule;
|
|
20
22
|
execute(response: string): Promise<string>;
|
|
23
|
+
transform(): Promise<string>;
|
|
21
24
|
executeBundle(data: Array<any>): Promise<string>;
|
|
22
|
-
|
|
25
|
+
executeDynamicJobBundle(data: Array<any>): Promise<string>;
|
|
26
|
+
reset(): void;
|
|
27
|
+
setExecutionContextStrategyExecution(executionContext: ExecutionContext, forceDeposit?: boolean): void;
|
|
23
28
|
setExecutionContextDataConnector(executionContext: ExecutionContext): void;
|
|
24
29
|
protected executeModule(response: string): Promise<string>;
|
|
25
30
|
getProvider(config: any): ethers.Provider | undefined;
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { loadWasm, loadWasmSync } from '@steerprotocol/app-loader';
|
|
2
|
-
import {
|
|
2
|
+
import { createFallbackRpcProvider } from '../utils/providerUtils.js';
|
|
3
|
+
function bigIntReplacer(_key, value) {
|
|
4
|
+
return typeof value === 'bigint' ? value.toString() : value;
|
|
5
|
+
}
|
|
3
6
|
export class BundleBase {
|
|
4
|
-
constructor(wasmFile, _config, identity, etherRpcConfig) {
|
|
7
|
+
constructor(wasmFile, _config, executionAppConfig, identity, etherRpcConfig) {
|
|
5
8
|
this.wasmFile = wasmFile;
|
|
6
9
|
this.config = _config;
|
|
7
10
|
this.configObj = JSON.parse(_config);
|
|
8
11
|
this.providerIdentity = identity;
|
|
9
12
|
this.etherRpcConfig = etherRpcConfig;
|
|
13
|
+
this.executionAppConfig = executionAppConfig;
|
|
10
14
|
}
|
|
11
15
|
async load() {
|
|
12
16
|
const instance = await loadWasm(this.wasmFile, {});
|
|
@@ -15,8 +19,9 @@ export class BundleBase {
|
|
|
15
19
|
return this.module;
|
|
16
20
|
}
|
|
17
21
|
loadSync() {
|
|
18
|
-
if (typeof this.wasmFile === 'string')
|
|
22
|
+
if (typeof this.wasmFile === 'string') {
|
|
19
23
|
throw new Error('Cannot load module synchronously unless wasmFile is of type ArrayBuffer');
|
|
24
|
+
}
|
|
20
25
|
this.module = loadWasmSync(this.wasmFile, {});
|
|
21
26
|
return this.module;
|
|
22
27
|
}
|
|
@@ -39,6 +44,26 @@ export class BundleBase {
|
|
|
39
44
|
// Call the main function with the response
|
|
40
45
|
return await this.module.execute(response);
|
|
41
46
|
}
|
|
47
|
+
// For dynamic job data connector
|
|
48
|
+
async transform() {
|
|
49
|
+
if (!this.executionContext) {
|
|
50
|
+
throw new Error('Execution context missing');
|
|
51
|
+
}
|
|
52
|
+
// If the module is not initialized, try to load it
|
|
53
|
+
if (!this.module) {
|
|
54
|
+
try {
|
|
55
|
+
await this.load();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new Error('Module not initialized');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// If the module is still not initialized, throw an error
|
|
62
|
+
if (!this.module)
|
|
63
|
+
throw new Error('Module not initialized');
|
|
64
|
+
// Call the main function with the response
|
|
65
|
+
return await this.module.transform();
|
|
66
|
+
}
|
|
42
67
|
async executeBundle(data) {
|
|
43
68
|
if (!this.executionContext) {
|
|
44
69
|
throw new Error('Execution context missing');
|
|
@@ -58,7 +83,31 @@ export class BundleBase {
|
|
|
58
83
|
// Call the main function with the response
|
|
59
84
|
return this.module.execute(...data);
|
|
60
85
|
}
|
|
61
|
-
|
|
86
|
+
async executeDynamicJobBundle(data) {
|
|
87
|
+
if (!this.executionContext) {
|
|
88
|
+
throw new Error('Execution context missing');
|
|
89
|
+
}
|
|
90
|
+
// If the module is not initialized, try to load it
|
|
91
|
+
if (!this.module) {
|
|
92
|
+
try {
|
|
93
|
+
await this.load();
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw new Error('Module not initialized');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// If the module is still not initialized, throw an error
|
|
100
|
+
if (!this.module)
|
|
101
|
+
throw new Error('Module not initialized');
|
|
102
|
+
// Call the main function with the response
|
|
103
|
+
return this.module.execute(...data);
|
|
104
|
+
}
|
|
105
|
+
reset() {
|
|
106
|
+
if (this.module && this.module['reset']) {
|
|
107
|
+
this.module.reset();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
setExecutionContextStrategyExecution(executionContext, forceDeposit) {
|
|
62
111
|
// Determine the timestamp for epoch
|
|
63
112
|
const timestamp = this.subtractDate(executionContext.executionTimestamp, Number(executionContext.epochLength)).getTime();
|
|
64
113
|
// Divide milliseconds data to seconds for transformation bundle
|
|
@@ -66,8 +115,10 @@ export class BundleBase {
|
|
|
66
115
|
this.executionContext = { ...executionContext, epochTimestamp: timestampInSecs };
|
|
67
116
|
const completeConfig = { ...JSON.parse(this.config), ...this.executionContext };
|
|
68
117
|
this.scheduleEpochTimestamp = executionContext.epochTimestamp;
|
|
69
|
-
|
|
70
|
-
|
|
118
|
+
if (completeConfig.triggerStyle && forceDeposit === true) {
|
|
119
|
+
completeConfig.triggerStyle = 'None';
|
|
120
|
+
}
|
|
121
|
+
this.module?.initialize(JSON.stringify(completeConfig, bigIntReplacer));
|
|
71
122
|
}
|
|
72
123
|
setExecutionContextDataConnector(executionContext) {
|
|
73
124
|
// Determine the timestamp for epoch
|
|
@@ -77,8 +128,7 @@ export class BundleBase {
|
|
|
77
128
|
this.executionContext = { ...executionContext, epochTimestamp: timestampInSecs };
|
|
78
129
|
const completeConfig = { ...JSON.parse(this.config), executionContext: this.executionContext };
|
|
79
130
|
this.scheduleEpochTimestamp = executionContext.epochTimestamp;
|
|
80
|
-
|
|
81
|
-
this.module?.initialize(JSON.stringify(completeConfig));
|
|
131
|
+
this.module?.initialize(JSON.stringify(completeConfig, bigIntReplacer));
|
|
82
132
|
}
|
|
83
133
|
async executeModule(response) {
|
|
84
134
|
if (!this.module) {
|
|
@@ -90,7 +140,6 @@ export class BundleBase {
|
|
|
90
140
|
// The chainId is first checked in the data connector config, and if not found
|
|
91
141
|
// it is checked in the request config. If the chainId is found in either of
|
|
92
142
|
// these places, the provider is returned from the etherRpcConfig object
|
|
93
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
143
|
getProvider(config) {
|
|
95
144
|
let chainId;
|
|
96
145
|
// check if chainId is configured in data connector config
|
|
@@ -102,8 +151,8 @@ export class BundleBase {
|
|
|
102
151
|
chainId = config.chainId;
|
|
103
152
|
}
|
|
104
153
|
if (chainId && this.etherRpcConfig && this.etherRpcConfig[chainId]) {
|
|
105
|
-
const
|
|
106
|
-
return
|
|
154
|
+
const rpcConfig = this.etherRpcConfig[chainId];
|
|
155
|
+
return createFallbackRpcProvider(rpcConfig.urls ?? [rpcConfig.url]);
|
|
107
156
|
}
|
|
108
157
|
else {
|
|
109
158
|
return this.providerIdentity;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { ethers } from 'ethers';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { ethers } from 'ethers';
|
|
2
|
+
import type { ExecutionAppConfig } from '../execution/executionApp.js';
|
|
3
|
+
import type { EtherRpcConfig, ViewConfig } from '../types.js';
|
|
4
|
+
import { BundleBase } from './bundleBase.js';
|
|
4
5
|
export declare class DataConnector extends BundleBase {
|
|
5
|
-
constructor(wasmFile: string | ArrayBuffer, _config: string, identity?: ethers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
6
|
+
constructor(wasmFile: string | ArrayBuffer, _config: string, executionAppConfig: ExecutionAppConfig, identity?: ethers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
6
7
|
getRpcUrl(config: any): string | undefined;
|
|
7
8
|
view(config: ViewConfig): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Attempts to decode raw return data when the declared ABI doesn't match the actual
|
|
11
|
+
* contract output. Tries removing trailing outputs one-by-one until decoding succeeds.
|
|
12
|
+
* This handles pool variants that omit optional fields (e.g. feeProtocol in slot0).
|
|
13
|
+
*/
|
|
14
|
+
private decodeFallback;
|
|
8
15
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Contract } from 'ethers';
|
|
2
|
-
import { CENTRALIZE_CONTRACT_INTERACTION_CHAINS } from '../utils/consts';
|
|
3
|
-
import { callWithRetry } from '../utils/utils';
|
|
4
|
-
import { BundleBase } from './bundleBase';
|
|
1
|
+
import { AbiCoder, Contract, Interface } from 'ethers';
|
|
2
|
+
import { CENTRALIZE_CONTRACT_INTERACTION_CHAINS } from '../utils/consts.js';
|
|
3
|
+
import { callWithRetry } from '../utils/utils.js';
|
|
4
|
+
import { BundleBase } from './bundleBase.js';
|
|
5
5
|
export class DataConnector extends BundleBase {
|
|
6
6
|
// Identity should come from the node's IdentityManager.wallet
|
|
7
|
-
constructor(wasmFile, _config, identity, etherRpcConfig) {
|
|
8
|
-
super(wasmFile, _config, identity, etherRpcConfig);
|
|
7
|
+
constructor(wasmFile, _config, executionAppConfig, identity, etherRpcConfig) {
|
|
8
|
+
super(wasmFile, _config, executionAppConfig, identity, etherRpcConfig);
|
|
9
9
|
}
|
|
10
10
|
// get rpc url from configuration
|
|
11
11
|
getRpcUrl(config) {
|
|
@@ -26,19 +26,30 @@ export class DataConnector extends BundleBase {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
// Makes contract instance and calls, returns stringified result
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
29
|
async view(config) {
|
|
31
30
|
const provider = this.getProvider(config);
|
|
32
31
|
if (!provider)
|
|
33
32
|
throw new Error('Provider not initialized');
|
|
34
33
|
const contract = new Contract(config.address, config.abi, provider);
|
|
35
|
-
// For formatting the parameters if needed
|
|
36
34
|
const paramsArray = config.arguments;
|
|
37
35
|
let result;
|
|
38
36
|
if (CENTRALIZE_CONTRACT_INTERACTION_CHAINS.indexOf(this.configObj.chainId.toString()) === -1) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
try {
|
|
38
|
+
result = await contract[config.method](...paramsArray, {
|
|
39
|
+
blockTag: this.executionContext?.blockNumber,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
// Some pool variants return fewer ABI outputs than the standard spec (e.g. Aerodrome/
|
|
44
|
+
// Velodrome slot0 has 6 fields instead of 7 — no feeProtocol). When ethers fails to
|
|
45
|
+
// decode the raw return data, fall back to decoding with progressively fewer outputs.
|
|
46
|
+
if (err?.code === 'BAD_DATA' && err?.value) {
|
|
47
|
+
result = this.decodeFallback(err.value, config);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
42
53
|
}
|
|
43
54
|
else {
|
|
44
55
|
const viewContractConfig = {
|
|
@@ -54,4 +65,28 @@ export class DataConnector extends BundleBase {
|
|
|
54
65
|
// This gets stringified before calling main
|
|
55
66
|
return result;
|
|
56
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Attempts to decode raw return data when the declared ABI doesn't match the actual
|
|
70
|
+
* contract output. Tries removing trailing outputs one-by-one until decoding succeeds.
|
|
71
|
+
* This handles pool variants that omit optional fields (e.g. feeProtocol in slot0).
|
|
72
|
+
*/
|
|
73
|
+
decodeFallback(rawData, config) {
|
|
74
|
+
const iface = new Interface(config.abi);
|
|
75
|
+
const fragment = iface.getFunction(config.method);
|
|
76
|
+
if (!fragment?.outputs?.length) {
|
|
77
|
+
throw new Error(`Cannot find outputs for method "${config.method}" in ABI`);
|
|
78
|
+
}
|
|
79
|
+
const coder = AbiCoder.defaultAbiCoder();
|
|
80
|
+
for (let numOutputs = fragment.outputs.length - 1; numOutputs >= 1; numOutputs--) {
|
|
81
|
+
try {
|
|
82
|
+
const types = fragment.outputs.slice(0, numOutputs).map((o) => o.format());
|
|
83
|
+
const decoded = coder.decode(types, rawData);
|
|
84
|
+
return decoded;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// try with one fewer output
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`Could not decode result data for method "${config.method}" — no matching ABI shape found`);
|
|
91
|
+
}
|
|
57
92
|
}
|
|
@@ -1,17 +1,62 @@
|
|
|
1
|
-
import { ethers } from 'ethers';
|
|
2
|
-
import { EtherRpcConfig } from '../types';
|
|
3
|
-
import { ExecutionBase } from './executionBase';
|
|
4
|
-
export
|
|
1
|
+
import type { ethers } from 'ethers';
|
|
2
|
+
import type { EtherRpcConfig, JobDetails } from '../types.js';
|
|
3
|
+
import { ExecutionBase } from './executionBase.js';
|
|
4
|
+
export interface ExecutionAppConfig {
|
|
5
|
+
/** Vault address used for execution context and subgraph queries. */
|
|
6
|
+
vault: string;
|
|
7
|
+
/** Chain ID used to resolve RPC configuration and on-chain reads. */
|
|
8
|
+
chainId: number;
|
|
9
|
+
/** Subgraph endpoint used for protocol data queries. */
|
|
5
10
|
steerSubgraph: string;
|
|
11
|
+
poolType: {
|
|
12
|
+
/** Set true when the pool uses Algebra v1 semantics. */
|
|
13
|
+
isAlgebraPool: boolean;
|
|
14
|
+
/** Set true when the pool uses Algebra Integral semantics. */
|
|
15
|
+
isAlgebraIntegralPool: boolean;
|
|
16
|
+
/** Set true when the pool uses directional pool semantics. */
|
|
17
|
+
isDirectionalPool: boolean;
|
|
18
|
+
/** Set true when the pool uses PoolShark semantics. */
|
|
19
|
+
isPoolSharkPool: boolean;
|
|
20
|
+
/** Set true when the pool uses Aerodrome semantics. */
|
|
21
|
+
isAerodromePool: boolean;
|
|
22
|
+
};
|
|
23
|
+
apiKeys: {
|
|
24
|
+
/** API key for The Graph Studio / gateway endpoints. */
|
|
25
|
+
subgraphStudioKey: string;
|
|
26
|
+
/** API key for Sentio endpoints, if applicable. */
|
|
27
|
+
sentioApiKey: string;
|
|
28
|
+
};
|
|
29
|
+
/** Maximum number of retries for execution operations. */
|
|
30
|
+
maxRetries?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class ExecutionApp extends ExecutionBase {
|
|
6
33
|
vault: string;
|
|
34
|
+
steerSubgraph: string;
|
|
35
|
+
chainId: number;
|
|
7
36
|
isAlgebraPool: boolean;
|
|
37
|
+
isAlgebraIntegralPool: boolean;
|
|
8
38
|
isDirectionalPool: boolean;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
39
|
+
isPoolSharkPool: boolean;
|
|
40
|
+
isAerodromePool: boolean;
|
|
41
|
+
subgraphStudioKey: string;
|
|
42
|
+
sentioApiKey: string;
|
|
43
|
+
config: ExecutionAppConfig;
|
|
44
|
+
constructor(config: ExecutionAppConfig);
|
|
45
|
+
execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
|
|
46
|
+
executeStabilityVault<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
|
|
47
|
+
executeDynamicJobs<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _job: JobDetails, _identity?: ethers.Provider, _etherRpcConfig?: EtherRpcConfig, forceDeposit?: boolean): Promise<Array<T>>;
|
|
12
48
|
private getSmartPoolExecutionContext;
|
|
13
49
|
private getPoolCurrentTick;
|
|
50
|
+
private getTickSpacing;
|
|
51
|
+
/**
|
|
52
|
+
* Converts a value to string, handling both native bigint and serialized bigint objects
|
|
53
|
+
* from the centralized API
|
|
54
|
+
*/
|
|
55
|
+
private convertToString;
|
|
56
|
+
private getTotalAmounts;
|
|
14
57
|
private getVaultPoolAddress;
|
|
15
58
|
private getSlot0;
|
|
59
|
+
private getSlot0Aerodrome;
|
|
60
|
+
private getGlobalStateAbi;
|
|
16
61
|
private getGlobalState;
|
|
17
62
|
}
|