@steerprotocol/data-connector-loader 0.1.5 → 2.0.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/lib/base/bundleBase.d.ts +27 -0
- package/lib/base/bundleBase.js +119 -0
- package/lib/base/loaderBase.d.ts +14 -0
- package/lib/base/loaderBase.js +111 -0
- package/lib/bundle/bundleApp.d.ts +6 -0
- package/lib/bundle/bundleApp.js +11 -0
- package/lib/bundle/bundleBase.d.ts +29 -0
- package/lib/bundle/bundleBase.js +150 -0
- package/lib/bundle/bundleDataConnector.d.ts +8 -0
- package/lib/bundle/bundleDataConnector.js +73 -0
- package/lib/bundle/bundleExecution.d.ts +7 -0
- package/lib/bundle/bundleExecution.js +42 -0
- package/lib/bundleDataConnector.d.ts +7 -0
- package/lib/bundleDataConnector.js +42 -0
- package/lib/bundleExecution.d.ts +7 -0
- package/lib/bundleExecution.js +42 -0
- package/lib/execution/executionApp.d.ts +17 -0
- package/lib/execution/executionApp.js +325 -0
- package/lib/execution/executionBase.d.ts +14 -0
- package/lib/execution/executionBase.js +111 -0
- package/lib/execution/executionBundle.d.ts +5 -0
- package/lib/execution/executionBundle.js +29 -0
- package/lib/execution/executionDataConnector.d.ts +9 -0
- package/lib/execution/executionDataConnector.js +110 -0
- package/lib/index.d.ts +4 -15
- package/lib/index.js +7 -198
- package/lib/loader/loaderBase.d.ts +14 -0
- package/lib/loader/loaderBase.js +111 -0
- package/lib/types.d.ts +39 -0
- package/lib/utils/blockFinder.d.ts +8 -0
- package/lib/utils/blockFinder.js +36 -0
- package/lib/utils/consts.d.ts +4 -0
- package/lib/utils/consts.js +7 -0
- package/lib/utils/pluginManager.d.ts +11 -0
- package/lib/utils/pluginManager.js +84 -0
- package/lib/utils/utils.d.ts +16 -0
- package/lib/utils/utils.js +146 -0
- package/package.json +3 -3
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.PluginManager = void 0;
|
|
16
|
+
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
class PluginManager {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.registeredPluginConfigs = [];
|
|
20
|
+
}
|
|
21
|
+
registerFromPublicConfig() {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const { data: dataConnectorPlugins } = yield axios_1.default.get('https://ro81h8hq6b.execute-api.us-east-1.amazonaws.com/data-connector-plugins');
|
|
24
|
+
for (const plugin of dataConnectorPlugins) {
|
|
25
|
+
this.registerPluginConfig(plugin.name, plugin.domain, plugin.headers);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
registerPluginConfig(name, domain, headers) {
|
|
30
|
+
this.registeredPluginConfigs.push({ name, domain, headers });
|
|
31
|
+
}
|
|
32
|
+
findMatchingPluginConfigs(url) {
|
|
33
|
+
const matchingHeaders = {};
|
|
34
|
+
for (const pluginConfig of this.registeredPluginConfigs) {
|
|
35
|
+
if (url.includes(pluginConfig.domain)) {
|
|
36
|
+
Object.assign(matchingHeaders, pluginConfig.headers);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return matchingHeaders;
|
|
40
|
+
}
|
|
41
|
+
interpolateEnvVars(str) {
|
|
42
|
+
return str.replace(/\{\{(\w+)\}\}/g, (_match, envVar) => {
|
|
43
|
+
const envValue = process.env[envVar];
|
|
44
|
+
if (envValue === undefined) {
|
|
45
|
+
console.log(`Environment variable '${envVar}' not found.`);
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
return envValue;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
interpolateHeaders(headers) {
|
|
52
|
+
const interpolatedHeaders = {};
|
|
53
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
54
|
+
interpolatedHeaders[key] = typeof value === 'string' ? this.interpolateEnvVars(value) : value;
|
|
55
|
+
}
|
|
56
|
+
return interpolatedHeaders;
|
|
57
|
+
}
|
|
58
|
+
process(config) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
if (!config.url) {
|
|
61
|
+
throw new Error('Request URL is not provided');
|
|
62
|
+
}
|
|
63
|
+
const matchingHeaders = this.findMatchingPluginConfigs(config.url);
|
|
64
|
+
const interpolatedHeaders = this.interpolateHeaders(matchingHeaders);
|
|
65
|
+
const updatedHeaders = Object.assign(Object.assign({}, config.headers), interpolatedHeaders);
|
|
66
|
+
const updatedConfig = Object.assign(Object.assign({}, config), { headers: updatedHeaders });
|
|
67
|
+
return updatedConfig;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.PluginManager = PluginManager;
|
|
72
|
+
// // Usage example:
|
|
73
|
+
// (async () => {
|
|
74
|
+
// const headerMerger = new PluginManager();
|
|
75
|
+
// headerMerger.registerPluginConfig('Example API', 'example.com', { 'X-Custom-Header': '{{MY_ENV_VAR}}' });
|
|
76
|
+
// process.env['MY_ENV_VAR'] = 'ExampleValue';
|
|
77
|
+
// const requestConfig: AxiosRequestConfig = {
|
|
78
|
+
// url: 'https://example.com/api/some-resource',
|
|
79
|
+
// method: 'GET',
|
|
80
|
+
// };
|
|
81
|
+
// const updatedRequestConfig = await headerMerger.process(requestConfig);
|
|
82
|
+
// const response = await axios(updatedRequestConfig);
|
|
83
|
+
// console.log(response.data);
|
|
84
|
+
// })();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Candle } from "@steerprotocol/app-loader";
|
|
2
|
+
import { AxiosRequestConfig } from "axios";
|
|
3
|
+
import { ExecutionContext, ViewContractConfig } from "../types";
|
|
4
|
+
import { providers } from "ethers";
|
|
5
|
+
export declare const extendOHLCV: (rawData: Candle[], period: number, endTimestamp: number) => Candle[];
|
|
6
|
+
export declare function fetchData(request: AxiosRequestConfig, attempts: number): Promise<any>;
|
|
7
|
+
export declare function readSlot0Contract({ address, abi, currentExecutionContext, functionName, provider, rpc, chainId }: {
|
|
8
|
+
address: string;
|
|
9
|
+
abi: any;
|
|
10
|
+
currentExecutionContext: ExecutionContext;
|
|
11
|
+
functionName: string;
|
|
12
|
+
provider: providers.Provider;
|
|
13
|
+
rpc: string;
|
|
14
|
+
chainId: number;
|
|
15
|
+
}): Promise<any>;
|
|
16
|
+
export declare function callWithRetry(config: ViewContractConfig): Promise<any>;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.callWithRetry = exports.readSlot0Contract = exports.fetchData = exports.extendOHLCV = void 0;
|
|
16
|
+
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
const ethers_1 = require("ethers");
|
|
18
|
+
const consts_1 = require("./consts");
|
|
19
|
+
// The purpose of this function is to look at the end timestamp and extend the current candle array so that it backfills any missing candles up to the end timestamp
|
|
20
|
+
const extendOHLCV = (rawData, period, endTimestamp) => {
|
|
21
|
+
// Sort rawData by timestamp
|
|
22
|
+
const sortedData = rawData.sort((a, b) => a.timestamp - b.timestamp);
|
|
23
|
+
const lastCandle = sortedData[sortedData.length - 1];
|
|
24
|
+
const startTimestamp = lastCandle ? lastCandle.timestamp : endTimestamp;
|
|
25
|
+
// Calculate the number of periods to backfill
|
|
26
|
+
const periodsToBackfill = Math.floor((endTimestamp - startTimestamp) / 1000 / period);
|
|
27
|
+
// Backfill the candles
|
|
28
|
+
for (let i = 0; i < periodsToBackfill; i++) {
|
|
29
|
+
const candleWidth = period * (i + 1);
|
|
30
|
+
const newCandle = {
|
|
31
|
+
timestamp: startTimestamp + candleWidth,
|
|
32
|
+
open: lastCandle.open,
|
|
33
|
+
high: lastCandle.open,
|
|
34
|
+
low: lastCandle.open,
|
|
35
|
+
close: lastCandle.open,
|
|
36
|
+
volume: 0, // You may need to replace these with appropriate logic
|
|
37
|
+
};
|
|
38
|
+
sortedData.push(newCandle);
|
|
39
|
+
}
|
|
40
|
+
// Return the converted data
|
|
41
|
+
return sortedData;
|
|
42
|
+
};
|
|
43
|
+
exports.extendOHLCV = extendOHLCV;
|
|
44
|
+
function delay(ms) {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function exponentialBackoffDelay(attempts) {
|
|
50
|
+
const baseDelay = 1000; // 1 second
|
|
51
|
+
const maxDelay = 30000; // 30 seconds
|
|
52
|
+
const delay = Math.min(baseDelay * Math.pow(2, attempts - 1), maxDelay);
|
|
53
|
+
return delay;
|
|
54
|
+
}
|
|
55
|
+
function fetchData(request, attempts) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
log("Request payload: =============", request);
|
|
58
|
+
attempts = attempts + 1;
|
|
59
|
+
try {
|
|
60
|
+
const response = (yield (0, axios_1.default)(request)).data;
|
|
61
|
+
// this.log('API Response: =============', response);
|
|
62
|
+
if ("errors" in response && response["errors"].length > 0) {
|
|
63
|
+
if (attempts < 3) {
|
|
64
|
+
yield delay(exponentialBackoffDelay(attempts));
|
|
65
|
+
return yield fetchData(request, attempts);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw new Error("Maximum retry attempts reached");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return response;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (attempts < 3) {
|
|
75
|
+
yield delay(exponentialBackoffDelay(attempts));
|
|
76
|
+
return yield fetchData(request, attempts);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
exports.fetchData = fetchData;
|
|
85
|
+
function readSlot0Contract({ address, abi, currentExecutionContext, functionName, provider, rpc, chainId }) {
|
|
86
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
// Create a new contract instance
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
+
if (!provider)
|
|
90
|
+
throw new Error("Provider not initialized");
|
|
91
|
+
const contract = new ethers_1.Contract(address, abi, provider);
|
|
92
|
+
// For formatting the parameters if needed
|
|
93
|
+
const paramsArray = [];
|
|
94
|
+
// Calculate block number for determinism
|
|
95
|
+
// Best way to handle contextually non-null values?
|
|
96
|
+
// const syncedBlock = await getBlockFromTimestamp(this.executionContext!.epochTimestamp, this.providerIdentity!) //@note uncomment for deterministic blocks
|
|
97
|
+
// Call the desired method with the specified arguments at synced block
|
|
98
|
+
// , {blockTag: syncedBlock}
|
|
99
|
+
let result;
|
|
100
|
+
if (consts_1.CENTRALIZE_CONTRACT_INTERACTION_CHAINS.indexOf(chainId.toString()) === -1) {
|
|
101
|
+
result = yield contract[functionName](...paramsArray, {
|
|
102
|
+
blockTag: currentExecutionContext === null || currentExecutionContext === void 0 ? void 0 : currentExecutionContext.blockNumber,
|
|
103
|
+
}); //@note uncomment for deterministic blocks
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const viewContractConfig = {
|
|
107
|
+
epochTimestamp: currentExecutionContext.epochTimestamp,
|
|
108
|
+
address: address,
|
|
109
|
+
abi: abi,
|
|
110
|
+
arguments: paramsArray,
|
|
111
|
+
method: functionName,
|
|
112
|
+
rpcUrl: rpc,
|
|
113
|
+
};
|
|
114
|
+
result = yield callWithRetry(viewContractConfig);
|
|
115
|
+
}
|
|
116
|
+
// This gets stringified before calling main
|
|
117
|
+
return result;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
exports.readSlot0Contract = readSlot0Contract;
|
|
121
|
+
function callWithRetry(config) {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
let retries = 0;
|
|
124
|
+
let delay = consts_1.API_INITIAL_DELAY;
|
|
125
|
+
while (retries < consts_1.API_MAX_RETRIES) {
|
|
126
|
+
try {
|
|
127
|
+
const response = yield axios_1.default.post(consts_1.API_BASE_URL, config);
|
|
128
|
+
return JSON.parse(response.data); // Return the data if the call was successful
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error(`Attempt ${retries + 1} failed. Retrying in to fetch the contract method ${config.method} ${delay / 1000} seconds...`);
|
|
132
|
+
yield new Promise((resolve) => setTimeout(resolve, delay));
|
|
133
|
+
delay *= 2; // Double the delay for the next retry
|
|
134
|
+
retries++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
throw new Error(`Failed after ${consts_1.API_MAX_RETRIES} attempts. to call method ${config.method}`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
exports.callWithRetry = callWithRetry;
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
+
function log(...args) {
|
|
143
|
+
if (process.env.DEBUG === 'true') {
|
|
144
|
+
console.log(...args);
|
|
145
|
+
}
|
|
146
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerprotocol/data-connector-loader",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "App Loader for Steer Protocol",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"lib/**/*"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@steerprotocol/app-loader": "1.0
|
|
50
|
-
"@steerprotocol/strategy-utils": "^3.1.
|
|
49
|
+
"@steerprotocol/app-loader": "1.1.0",
|
|
50
|
+
"@steerprotocol/strategy-utils": "^3.1.2",
|
|
51
51
|
"axios": "^1.3.4",
|
|
52
52
|
"ethers": "^5.1.0",
|
|
53
53
|
"timestring": "^7.0.0"
|