@steerprotocol/data-connector-loader 0.0.2 → 0.0.4
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/blockFinder.d.ts +8 -0
- package/lib/blockFinder.js +36 -0
- package/lib/dataConnector.d.ts +5 -2
- package/lib/dataConnector.js +20 -13
- package/lib/index.js +12 -8
- package/lib/pluginManager.d.ts +11 -0
- package/lib/pluginManager.js +84 -0
- package/package.json +4 -4
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Provider } from 'ethers';
|
|
2
|
+
/**
|
|
3
|
+
* Iterates down from current block to find the block previous or equal to the given timestamp
|
|
4
|
+
* Not the most efficient for far past timestamps, a binary search could be used for better performance
|
|
5
|
+
* @param timestamp
|
|
6
|
+
* @param Idenity
|
|
7
|
+
*/
|
|
8
|
+
export declare function getBlockFromTimestamp(timestamp: number, provider: Provider): Promise<number>;
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getBlockFromTimestamp = void 0;
|
|
13
|
+
// import { providers, } from "ethers";
|
|
14
|
+
/**
|
|
15
|
+
* Iterates down from current block to find the block previous or equal to the given timestamp
|
|
16
|
+
* Not the most efficient for far past timestamps, a binary search could be used for better performance
|
|
17
|
+
* @param timestamp
|
|
18
|
+
* @param Idenity
|
|
19
|
+
*/
|
|
20
|
+
function getBlockFromTimestamp(timestamp, provider) {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
let currentBlock = (yield provider.getBlockNumber()) - 1;
|
|
24
|
+
let runningTimestamp = (_a = (yield provider.getBlock(currentBlock))) === null || _a === void 0 ? void 0 : _a.timestamp;
|
|
25
|
+
if (!runningTimestamp)
|
|
26
|
+
return currentBlock;
|
|
27
|
+
while (runningTimestamp > timestamp) {
|
|
28
|
+
currentBlock -= 1;
|
|
29
|
+
runningTimestamp = (_b = (yield provider.getBlock(currentBlock))) === null || _b === void 0 ? void 0 : _b.timestamp;
|
|
30
|
+
if (!runningTimestamp)
|
|
31
|
+
return currentBlock;
|
|
32
|
+
}
|
|
33
|
+
return currentBlock;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
exports.getBlockFromTimestamp = getBlockFromTimestamp;
|
package/lib/dataConnector.d.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { WasmModule } from '@steerprotocol/app-loader';
|
|
2
2
|
import { ExecutionContext } from './types';
|
|
3
|
+
import { Provider } from 'ethers';
|
|
3
4
|
export declare class DataConnector {
|
|
4
5
|
private wasmFile;
|
|
5
6
|
module?: WasmModule;
|
|
6
7
|
config: string;
|
|
7
8
|
configObj: {
|
|
8
|
-
[key: string]:
|
|
9
|
+
[key: string]: any;
|
|
9
10
|
};
|
|
10
11
|
executionContext?: ExecutionContext & {
|
|
11
12
|
epochTimestamp: number;
|
|
12
13
|
};
|
|
13
|
-
|
|
14
|
+
providerIdentity?: Provider;
|
|
15
|
+
constructor(wasmFile: string | ArrayBuffer, _config: string, identity?: Provider);
|
|
14
16
|
load(): Promise<WasmModule>;
|
|
15
17
|
setExecutionContext(executionContext: ExecutionContext): void;
|
|
16
18
|
execute(response: string): Promise<string>;
|
|
19
|
+
view(config: any): Promise<string>;
|
|
17
20
|
getResults(): string;
|
|
18
21
|
version(): number;
|
|
19
22
|
private subtractDate;
|
package/lib/dataConnector.js
CHANGED
|
@@ -11,11 +11,15 @@ 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 app_loader_1 = require("@steerprotocol/app-loader");
|
|
14
|
+
const ethers_1 = require("ethers");
|
|
15
|
+
const blockFinder_1 = require("./blockFinder");
|
|
14
16
|
class DataConnector {
|
|
15
|
-
|
|
17
|
+
// Identity should come from the node's IdentityManager.wallet
|
|
18
|
+
constructor(wasmFile, _config, identity) {
|
|
16
19
|
this.wasmFile = wasmFile;
|
|
17
20
|
this.config = _config;
|
|
18
21
|
this.configObj = JSON.parse(_config);
|
|
22
|
+
this.providerIdentity = identity;
|
|
19
23
|
}
|
|
20
24
|
load() {
|
|
21
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -52,21 +56,24 @@ class DataConnector {
|
|
|
52
56
|
if (!this.module)
|
|
53
57
|
throw new Error('Module not initialized');
|
|
54
58
|
// Call the main function with the response
|
|
55
|
-
return this.module
|
|
59
|
+
return this.module["execute(param_1: string)"](response);
|
|
56
60
|
});
|
|
57
61
|
}
|
|
58
62
|
// Makes contract instance and calls, returns stringified result
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
view(config) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
const contract = new ethers_1.ethers.Contract(config.address, config.abi, this.providerIdentity);
|
|
66
|
+
// For formatting the parameters if needed
|
|
67
|
+
const paramsArray = config.arguments;
|
|
68
|
+
// Calculate block number for determinism
|
|
69
|
+
// Best way to handle contextually non-null values?
|
|
70
|
+
const syncedBlock = yield (0, blockFinder_1.getBlockFromTimestamp)(this.executionContext.epochTimestamp, this.providerIdentity); //@note uncomment for deterministic blocks
|
|
71
|
+
// Call the desired method with the specified arguments at synced block
|
|
72
|
+
const result = yield contract[config.method](...paramsArray, { blockTag: syncedBlock }); //@note uncomment for deterministic blocks
|
|
73
|
+
// This gets stringified before calling main
|
|
74
|
+
return result;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
70
77
|
getResults() {
|
|
71
78
|
if (!this.module)
|
|
72
79
|
throw new Error('Module not initialized');
|
package/lib/index.js
CHANGED
|
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.DataConnectorExecution = void 0;
|
|
16
16
|
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
const pluginManager_1 = require("./pluginManager");
|
|
17
18
|
class DataConnectorExecution {
|
|
18
19
|
constructor(dataConnector) {
|
|
19
20
|
this.dataConnector = dataConnector;
|
|
@@ -29,6 +30,8 @@ class DataConnectorExecution {
|
|
|
29
30
|
// Each data connector results will be pushed into single array
|
|
30
31
|
// they will passed to execution bundle
|
|
31
32
|
const transformationData = [];
|
|
33
|
+
const pluginManager = new pluginManager_1.PluginManager();
|
|
34
|
+
yield pluginManager.registerFromPublicConfig();
|
|
32
35
|
// Iterate over each data connector
|
|
33
36
|
for (const connectorConfig of dataConnectorConfigs) {
|
|
34
37
|
// If bundle hash is not present, skip the data connector
|
|
@@ -51,15 +54,16 @@ class DataConnectorExecution {
|
|
|
51
54
|
let request = yield this.dataConnector.execute('');
|
|
52
55
|
let response;
|
|
53
56
|
while (request != 'true') {
|
|
54
|
-
const parsedRequest = JSON.parse(request);
|
|
57
|
+
const parsedRequest = yield pluginManager.process(JSON.parse(request));
|
|
55
58
|
// On Chain
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
if (this.dataConnector.configObj.isChainRead == true) {
|
|
60
|
+
response = yield this.dataConnector.view(parsedRequest);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Axios logic (off-chain)
|
|
64
|
+
const ax_instance = axios_1.default.create();
|
|
65
|
+
response = (yield ax_instance.request(parsedRequest)).data;
|
|
66
|
+
}
|
|
63
67
|
// break;
|
|
64
68
|
request = yield this.dataConnector.execute(JSON.stringify(response));
|
|
65
69
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from 'axios';
|
|
2
|
+
export declare class PluginManager {
|
|
3
|
+
private registeredPluginConfigs;
|
|
4
|
+
constructor();
|
|
5
|
+
registerFromPublicConfig(): Promise<void>;
|
|
6
|
+
registerPluginConfig(name: string, domain: string, headers: object): void;
|
|
7
|
+
private findMatchingPluginConfigs;
|
|
8
|
+
private interpolateEnvVars;
|
|
9
|
+
private interpolateHeaders;
|
|
10
|
+
process(config: AxiosRequestConfig): Promise<AxiosRequestConfig>;
|
|
11
|
+
}
|
|
@@ -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://igk02ovpui.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
|
+
// })();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerprotocol/data-connector-loader",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "App Loader for Steer Protocol",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"lib/**/*"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@steerprotocol/app-loader": "
|
|
49
|
+
"@steerprotocol/app-loader": "0.1.6",
|
|
50
50
|
"@steerprotocol/strategy-utils": "^2.1.0",
|
|
51
|
-
"axios": "^1.3.
|
|
52
|
-
"ethers": "^6.0
|
|
51
|
+
"axios": "^1.3.4",
|
|
52
|
+
"ethers": "^6.1.0",
|
|
53
53
|
"pondjs": "1.0.0-alpha.10"
|
|
54
54
|
}
|
|
55
55
|
}
|