@steerprotocol/data-connector-loader 2.1.1 → 3.0.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/lib/{base → esm/bundle}/bundleBase.d.ts +7 -5
- package/lib/esm/bundle/bundleBase.js +127 -0
- package/lib/{bundle/bundleExecution.d.ts → esm/bundle/bundleDataConnector.d.ts} +2 -1
- package/lib/esm/bundle/bundleDataConnector.js +57 -0
- package/lib/esm/execution/executionApp.d.ts +17 -0
- package/lib/esm/execution/executionApp.js +304 -0
- package/lib/{loader/loaderBase.d.ts → esm/execution/executionBase.d.ts} +2 -2
- package/lib/esm/execution/executionBase.js +95 -0
- package/lib/esm/execution/executionDataConnector.d.ts +9 -0
- package/lib/esm/execution/executionDataConnector.js +90 -0
- package/lib/esm/index.d.ts +5 -0
- package/lib/esm/index.js +5 -0
- package/lib/esm/package.json +1 -0
- package/lib/esm/types.d.ts +104 -0
- package/lib/esm/types.js +1 -0
- package/lib/esm/utils/blockFinder.js +20 -0
- package/lib/esm/utils/consts.d.ts +4 -0
- package/lib/esm/utils/consts.js +4 -0
- package/lib/esm/utils/pluginManager.js +67 -0
- package/lib/esm/utils/utils.d.ts +16 -0
- package/lib/esm/utils/utils.js +119 -0
- package/lib/execution/executionApp.d.ts +3 -3
- package/lib/execution/executionApp.js +78 -80
- package/lib/execution/executionDataConnector.d.ts +2 -2
- package/lib/types.d.ts +1 -1
- package/lib/utils/consts.js +2 -2
- package/lib/utils/utils.d.ts +5 -5
- package/lib/utils/utils.js +5 -5
- package/package.json +34 -4
- package/lib/base/bundleBase.js +0 -119
- package/lib/base/loaderBase.d.ts +0 -14
- package/lib/base/loaderBase.js +0 -111
- package/lib/blockFinder.js +0 -36
- package/lib/bundle/bundleApp.d.ts +0 -6
- package/lib/bundle/bundleApp.js +0 -11
- package/lib/bundle/bundleExecution.js +0 -42
- package/lib/bundleDataConnector.d.ts +0 -7
- package/lib/bundleDataConnector.js +0 -42
- package/lib/bundleExecution.d.ts +0 -7
- package/lib/bundleExecution.js +0 -42
- package/lib/dataConnector.d.ts +0 -27
- package/lib/dataConnector.js +0 -133
- package/lib/execution/executionBundle.d.ts +0 -5
- package/lib/execution/executionBundle.js +0 -29
- package/lib/loader/loaderBase.js +0 -111
- package/lib/pluginManager.js +0 -84
- package/lib/utils.d.ts +0 -2
- package/lib/utils.js +0 -28
- /package/lib/{blockFinder.d.ts → esm/utils/blockFinder.d.ts} +0 -0
- /package/lib/{pluginManager.d.ts → esm/utils/pluginManager.d.ts} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ExecutionBase } from './executionBase';
|
|
2
|
+
import { PluginManager } from '../utils/pluginManager';
|
|
3
|
+
import { extendOHLCV, fetchData } from '../utils/utils';
|
|
4
|
+
export class DataConnectorExecution extends ExecutionBase {
|
|
5
|
+
constructor(dataConnectors, maxRetries = 3) {
|
|
6
|
+
super(maxRetries);
|
|
7
|
+
this.dataConnectors = dataConnectors;
|
|
8
|
+
}
|
|
9
|
+
async execute(_vaultConfig) {
|
|
10
|
+
const vaultConfig = JSON.parse(_vaultConfig);
|
|
11
|
+
const transformationData = [];
|
|
12
|
+
const pluginManager = new PluginManager();
|
|
13
|
+
await pluginManager.registerFromPublicConfig();
|
|
14
|
+
for (const dataConnector of this.dataConnectors) {
|
|
15
|
+
if (!dataConnector.module) {
|
|
16
|
+
await dataConnector.load();
|
|
17
|
+
}
|
|
18
|
+
const executionContext = await this.getExecutionContext(dataConnector, vaultConfig);
|
|
19
|
+
dataConnector.setExecutionContextDataConnector(executionContext);
|
|
20
|
+
const version = await this.getDataConnectorVersion(dataConnector);
|
|
21
|
+
if (version === 1) {
|
|
22
|
+
const initialRequest = await dataConnector.execute('');
|
|
23
|
+
const finalResponse = await this.handleRequestResponse(dataConnector, pluginManager, initialRequest);
|
|
24
|
+
const transformedData = await this.transformData(dataConnector, finalResponse);
|
|
25
|
+
transformationData.push(transformedData);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const finalResponse = await dataConnector.execute('');
|
|
29
|
+
const transformedData = await this.transformData(dataConnector, finalResponse);
|
|
30
|
+
transformationData.push(transformedData);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return transformationData;
|
|
34
|
+
}
|
|
35
|
+
async handleRequestResponse(dataConnector, pluginManager, initialRequest) {
|
|
36
|
+
let request = initialRequest;
|
|
37
|
+
let response;
|
|
38
|
+
try {
|
|
39
|
+
while (request !== 'true') {
|
|
40
|
+
if (dataConnector.configObj.isChainRead == true) {
|
|
41
|
+
response = await dataConnector.view(JSON.parse(request));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const parsedRequest = await pluginManager.process(typeof request == 'string' ? JSON.parse(request) : request);
|
|
45
|
+
await this.delay(1000);
|
|
46
|
+
response = await fetchData(parsedRequest, 0);
|
|
47
|
+
}
|
|
48
|
+
request = await dataConnector.execute(JSON.stringify(response));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
// process.env.SILENT_ERRORS
|
|
53
|
+
if (process.env.DATA_LOADER_SILENT_ERRORS !== 'true') {
|
|
54
|
+
console.error('Data connector failed to process.', e);
|
|
55
|
+
}
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
return dataConnector.getResults();
|
|
59
|
+
}
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
async transformData(dataConnector, rawData) {
|
|
62
|
+
let transformedData = typeof rawData === 'string' ? JSON.parse(rawData) : rawData;
|
|
63
|
+
if (transformedData == null || transformedData.length == 0) {
|
|
64
|
+
const results = dataConnector.getResults();
|
|
65
|
+
const parsedResults = JSON.parse(results);
|
|
66
|
+
if (parsedResults.length == null) {
|
|
67
|
+
transformedData = results;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
transformedData = parsedResults;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const version = await this.getDataConnectorVersion(dataConnector);
|
|
74
|
+
if (version == 1 && transformedData.length > 0) {
|
|
75
|
+
const keys = Object.keys(transformedData[0]);
|
|
76
|
+
const requiredKeys = ['timestamp', 'open', 'high', 'low', 'close'];
|
|
77
|
+
const hasAllKeys = requiredKeys.every((key) => keys.includes(key));
|
|
78
|
+
// has the 'lookback' key
|
|
79
|
+
const lookback = dataConnector?.configObj?.lookback;
|
|
80
|
+
if (hasAllKeys && !!lookback) {
|
|
81
|
+
// Now that we have identified a v1 data connector, we need to transform the data so that the data is up to the current execution epoch timestamp
|
|
82
|
+
if (dataConnector?.executionContext?.epochTimestamp) {
|
|
83
|
+
const extended = extendOHLCV(transformedData, lookback, dataConnector?.executionContext?.epochTimestamp * 1000);
|
|
84
|
+
transformedData = extended;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return transformedData;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DataConnector } from './bundle/bundleDataConnector';
|
|
2
|
+
import { DataConnectorExecution } from './execution/executionDataConnector';
|
|
3
|
+
import { ExecutionApp } from './execution/executionApp';
|
|
4
|
+
export * from './types';
|
|
5
|
+
export { DataConnector, DataConnectorExecution, ExecutionApp };
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DataConnector } from './bundle/bundleDataConnector';
|
|
2
|
+
import { DataConnectorExecution } from './execution/executionDataConnector';
|
|
3
|
+
import { ExecutionApp } from './execution/executionApp';
|
|
4
|
+
export * from './types';
|
|
5
|
+
export { DataConnector, DataConnectorExecution, ExecutionApp };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type": "module"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ContractInterface } from 'ethers';
|
|
2
|
+
export type VaultInput = {
|
|
3
|
+
_id?: string;
|
|
4
|
+
tokenId: string;
|
|
5
|
+
vaultAddress: string;
|
|
6
|
+
state: string;
|
|
7
|
+
token0: string;
|
|
8
|
+
token1: string;
|
|
9
|
+
name: string;
|
|
10
|
+
symbol: string;
|
|
11
|
+
beaconName: string;
|
|
12
|
+
token0Name: string;
|
|
13
|
+
token1Name: string;
|
|
14
|
+
token0Symbol: string;
|
|
15
|
+
token1Symbol: string;
|
|
16
|
+
decimals: string;
|
|
17
|
+
token0Decimals: string;
|
|
18
|
+
token1Decimals: string;
|
|
19
|
+
payloadIpfs: string;
|
|
20
|
+
timestamp: number;
|
|
21
|
+
vaultConfigPayload: VaultConfigPayload;
|
|
22
|
+
};
|
|
23
|
+
export interface VaultConfigPayload {
|
|
24
|
+
strategyConfigData: StrategyConfigData;
|
|
25
|
+
vaultPayload: VaultPayload;
|
|
26
|
+
dataConnectorsData: DataConnectorsDaum[];
|
|
27
|
+
vaultAddress: string;
|
|
28
|
+
epochTimestamp: number;
|
|
29
|
+
anchorBlockNumber?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface StrategyConfigData {
|
|
32
|
+
[key: string]: string | number;
|
|
33
|
+
epochStart: number;
|
|
34
|
+
epochLength: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ConcentratedLiquidityManagerPayload {
|
|
37
|
+
maxTotalSupply: string;
|
|
38
|
+
ratioErrorTolerance: number;
|
|
39
|
+
maxTickChange: number;
|
|
40
|
+
twapInterval: number;
|
|
41
|
+
fee: number;
|
|
42
|
+
slippage: number;
|
|
43
|
+
}
|
|
44
|
+
export type VaultPayload = ConcentratedLiquidityManagerPayload;
|
|
45
|
+
export interface DataConnectorsDaum {
|
|
46
|
+
bundleHash: string;
|
|
47
|
+
configData: ConfigData;
|
|
48
|
+
}
|
|
49
|
+
export interface ConfigData {
|
|
50
|
+
[key: string]: string | number;
|
|
51
|
+
}
|
|
52
|
+
export interface ExecutionContext {
|
|
53
|
+
executionTimestamp: number;
|
|
54
|
+
epochLength: number;
|
|
55
|
+
vaultAddress: string;
|
|
56
|
+
blockTime: number;
|
|
57
|
+
blockNumber: number;
|
|
58
|
+
epochTimestamp: number;
|
|
59
|
+
}
|
|
60
|
+
export interface Position {
|
|
61
|
+
startTick: number;
|
|
62
|
+
endTick: number;
|
|
63
|
+
weight: number;
|
|
64
|
+
}
|
|
65
|
+
export interface SmartPoolExecutionContext extends ExecutionContext {
|
|
66
|
+
positions: Position[];
|
|
67
|
+
currentTick: number;
|
|
68
|
+
lastExecutionTime: number;
|
|
69
|
+
}
|
|
70
|
+
export type RpcConfig = {
|
|
71
|
+
url: string;
|
|
72
|
+
network?: string;
|
|
73
|
+
};
|
|
74
|
+
export type EtherRpcConfig = {
|
|
75
|
+
[key: string]: RpcConfig;
|
|
76
|
+
};
|
|
77
|
+
export interface ViewConfig {
|
|
78
|
+
address: string;
|
|
79
|
+
abi: ContractInterface;
|
|
80
|
+
method: string;
|
|
81
|
+
arguments: unknown[];
|
|
82
|
+
}
|
|
83
|
+
export interface Slot0 {
|
|
84
|
+
sqrtPriceX96?: bigint;
|
|
85
|
+
tick: number;
|
|
86
|
+
observationIndex?: number;
|
|
87
|
+
observationCardinality?: number;
|
|
88
|
+
observationCardinalityNext?: number;
|
|
89
|
+
feeProtocol?: number;
|
|
90
|
+
unlocked: boolean;
|
|
91
|
+
price?: bigint;
|
|
92
|
+
prevInitializedTick?: number;
|
|
93
|
+
fee?: number;
|
|
94
|
+
timepointIndex?: number;
|
|
95
|
+
communityFee?: number;
|
|
96
|
+
}
|
|
97
|
+
export type ViewContractConfig = {
|
|
98
|
+
epochTimestamp?: number;
|
|
99
|
+
address: string;
|
|
100
|
+
abi: any;
|
|
101
|
+
arguments: Array<any>;
|
|
102
|
+
method: string;
|
|
103
|
+
rpcUrl?: string;
|
|
104
|
+
};
|
package/lib/esm/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// import { providers, } 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 async function getBlockFromTimestamp(timestamp, provider) {
|
|
9
|
+
let currentBlock = (await provider.getBlockNumber()) - 1;
|
|
10
|
+
let runningTimestamp = (await provider.getBlock(currentBlock))?.timestamp;
|
|
11
|
+
if (!runningTimestamp)
|
|
12
|
+
return currentBlock;
|
|
13
|
+
while (runningTimestamp > timestamp) {
|
|
14
|
+
currentBlock -= 1;
|
|
15
|
+
runningTimestamp = (await provider.getBlock(currentBlock))?.timestamp;
|
|
16
|
+
if (!runningTimestamp)
|
|
17
|
+
return currentBlock;
|
|
18
|
+
}
|
|
19
|
+
return currentBlock;
|
|
20
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const API_BASE_URL = "https://cvrlulno2m.execute-api.ap-southeast-1.amazonaws.com/dev/contract-view-call";
|
|
2
|
+
export declare const API_MAX_RETRIES = 3;
|
|
3
|
+
export declare const API_INITIAL_DELAY = 1000;
|
|
4
|
+
export declare const CENTRALIZE_CONTRACT_INTERACTION_CHAINS: string[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const API_BASE_URL = 'https://cvrlulno2m.execute-api.ap-southeast-1.amazonaws.com/dev/contract-view-call';
|
|
2
|
+
export const API_MAX_RETRIES = 3;
|
|
3
|
+
export const API_INITIAL_DELAY = 1000;
|
|
4
|
+
export const CENTRALIZE_CONTRACT_INTERACTION_CHAINS = ['2222', '42161', '9001', '137', '56'];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class PluginManager {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.registeredPluginConfigs = [];
|
|
5
|
+
}
|
|
6
|
+
async registerFromPublicConfig() {
|
|
7
|
+
const { data: dataConnectorPlugins } = await axios.get('https://ro81h8hq6b.execute-api.us-east-1.amazonaws.com/data-connector-plugins');
|
|
8
|
+
for (const plugin of dataConnectorPlugins) {
|
|
9
|
+
this.registerPluginConfig(plugin.name, plugin.domain, plugin.headers);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
registerPluginConfig(name, domain, headers) {
|
|
13
|
+
this.registeredPluginConfigs.push({ name, domain, headers });
|
|
14
|
+
}
|
|
15
|
+
findMatchingPluginConfigs(url) {
|
|
16
|
+
const matchingHeaders = {};
|
|
17
|
+
for (const pluginConfig of this.registeredPluginConfigs) {
|
|
18
|
+
if (url.includes(pluginConfig.domain)) {
|
|
19
|
+
Object.assign(matchingHeaders, pluginConfig.headers);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return matchingHeaders;
|
|
23
|
+
}
|
|
24
|
+
interpolateEnvVars(str) {
|
|
25
|
+
return str.replace(/\{\{(\w+)\}\}/g, (_match, envVar) => {
|
|
26
|
+
const envValue = process.env[envVar];
|
|
27
|
+
if (envValue === undefined) {
|
|
28
|
+
console.log(`Environment variable '${envVar}' not found.`);
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
return envValue;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
interpolateHeaders(headers) {
|
|
35
|
+
const interpolatedHeaders = {};
|
|
36
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
37
|
+
interpolatedHeaders[key] = typeof value === 'string' ? this.interpolateEnvVars(value) : value;
|
|
38
|
+
}
|
|
39
|
+
return interpolatedHeaders;
|
|
40
|
+
}
|
|
41
|
+
async process(config) {
|
|
42
|
+
if (!config.url) {
|
|
43
|
+
throw new Error('Request URL is not provided');
|
|
44
|
+
}
|
|
45
|
+
const matchingHeaders = this.findMatchingPluginConfigs(config.url);
|
|
46
|
+
const interpolatedHeaders = this.interpolateHeaders(matchingHeaders);
|
|
47
|
+
const updatedHeaders = { ...config.headers, ...interpolatedHeaders };
|
|
48
|
+
const updatedConfig = {
|
|
49
|
+
...config,
|
|
50
|
+
headers: updatedHeaders,
|
|
51
|
+
};
|
|
52
|
+
return updatedConfig;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// // Usage example:
|
|
56
|
+
// (async () => {
|
|
57
|
+
// const headerMerger = new PluginManager();
|
|
58
|
+
// headerMerger.registerPluginConfig('Example API', 'example.com', { 'X-Custom-Header': '{{MY_ENV_VAR}}' });
|
|
59
|
+
// process.env['MY_ENV_VAR'] = 'ExampleValue';
|
|
60
|
+
// const requestConfig: AxiosRequestConfig = {
|
|
61
|
+
// url: 'https://example.com/api/some-resource',
|
|
62
|
+
// method: 'GET',
|
|
63
|
+
// };
|
|
64
|
+
// const updatedRequestConfig = await headerMerger.process(requestConfig);
|
|
65
|
+
// const response = await axios(updatedRequestConfig);
|
|
66
|
+
// console.log(response.data);
|
|
67
|
+
// })();
|
|
@@ -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,119 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { Contract } from 'ethers';
|
|
3
|
+
import { API_BASE_URL, API_INITIAL_DELAY, API_MAX_RETRIES, CENTRALIZE_CONTRACT_INTERACTION_CHAINS } from './consts';
|
|
4
|
+
// 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
|
|
5
|
+
export const extendOHLCV = (rawData, period, endTimestamp) => {
|
|
6
|
+
// Sort rawData by timestamp
|
|
7
|
+
const sortedData = rawData.sort((a, b) => a.timestamp - b.timestamp);
|
|
8
|
+
const lastCandle = sortedData[sortedData.length - 1];
|
|
9
|
+
const startTimestamp = lastCandle ? lastCandle.timestamp : endTimestamp;
|
|
10
|
+
// Calculate the number of periods to backfill
|
|
11
|
+
const periodsToBackfill = Math.floor((endTimestamp - startTimestamp) / 1000 / period);
|
|
12
|
+
// Backfill the candles
|
|
13
|
+
for (let i = 0; i < periodsToBackfill; i++) {
|
|
14
|
+
const candleWidth = period * (i + 1);
|
|
15
|
+
const newCandle = {
|
|
16
|
+
timestamp: startTimestamp + candleWidth,
|
|
17
|
+
open: lastCandle.open,
|
|
18
|
+
high: lastCandle.open,
|
|
19
|
+
low: lastCandle.open,
|
|
20
|
+
close: lastCandle.open,
|
|
21
|
+
volume: 0, // You may need to replace these with appropriate logic
|
|
22
|
+
};
|
|
23
|
+
sortedData.push(newCandle);
|
|
24
|
+
}
|
|
25
|
+
// Return the converted data
|
|
26
|
+
return sortedData;
|
|
27
|
+
};
|
|
28
|
+
async function delay(ms) {
|
|
29
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
30
|
+
}
|
|
31
|
+
function exponentialBackoffDelay(attempts) {
|
|
32
|
+
const baseDelay = 1000; // 1 second
|
|
33
|
+
const maxDelay = 30000; // 30 seconds
|
|
34
|
+
const delay = Math.min(baseDelay * Math.pow(2, attempts - 1), maxDelay);
|
|
35
|
+
return delay;
|
|
36
|
+
}
|
|
37
|
+
export async function fetchData(request, attempts) {
|
|
38
|
+
log('Request payload: =============', request);
|
|
39
|
+
attempts = attempts + 1;
|
|
40
|
+
try {
|
|
41
|
+
const response = (await axios(request)).data;
|
|
42
|
+
// this.log('API Response: =============', response);
|
|
43
|
+
if ('errors' in response && response['errors'].length > 0) {
|
|
44
|
+
if (attempts < 3) {
|
|
45
|
+
await delay(exponentialBackoffDelay(attempts));
|
|
46
|
+
return await fetchData(request, attempts);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
throw new Error('Maximum retry attempts reached');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return response;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (attempts < 3) {
|
|
56
|
+
await delay(exponentialBackoffDelay(attempts));
|
|
57
|
+
return await fetchData(request, attempts);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function readSlot0Contract({ address, abi, currentExecutionContext, functionName, provider, rpc, chainId, }) {
|
|
65
|
+
// Create a new contract instance
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
if (!provider)
|
|
68
|
+
throw new Error('Provider not initialized');
|
|
69
|
+
const contract = new Contract(address, abi, provider);
|
|
70
|
+
// For formatting the parameters if needed
|
|
71
|
+
const paramsArray = [];
|
|
72
|
+
// Calculate block number for determinism
|
|
73
|
+
// Best way to handle contextually non-null values?
|
|
74
|
+
// const syncedBlock = await getBlockFromTimestamp(this.executionContext!.epochTimestamp, this.providerIdentity!) //@note uncomment for deterministic blocks
|
|
75
|
+
// Call the desired method with the specified arguments at synced block
|
|
76
|
+
// , {blockTag: syncedBlock}
|
|
77
|
+
let result;
|
|
78
|
+
if (CENTRALIZE_CONTRACT_INTERACTION_CHAINS.indexOf(chainId.toString()) === -1) {
|
|
79
|
+
result = await contract[functionName](...paramsArray, {
|
|
80
|
+
blockTag: currentExecutionContext?.blockNumber,
|
|
81
|
+
}); //@note uncomment for deterministic blocks
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const viewContractConfig = {
|
|
85
|
+
epochTimestamp: currentExecutionContext.epochTimestamp,
|
|
86
|
+
address: address,
|
|
87
|
+
abi: abi,
|
|
88
|
+
arguments: paramsArray,
|
|
89
|
+
method: functionName,
|
|
90
|
+
rpcUrl: rpc,
|
|
91
|
+
};
|
|
92
|
+
result = await callWithRetry(viewContractConfig);
|
|
93
|
+
}
|
|
94
|
+
// This gets stringified before calling main
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
export async function callWithRetry(config) {
|
|
98
|
+
let retries = 0;
|
|
99
|
+
let delay = API_INITIAL_DELAY;
|
|
100
|
+
while (retries < API_MAX_RETRIES) {
|
|
101
|
+
try {
|
|
102
|
+
const response = await axios.post(API_BASE_URL, config);
|
|
103
|
+
return JSON.parse(response.data); // Return the data if the call was successful
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error(`Attempt ${retries + 1} failed. Retrying in to fetch the contract method ${config.method} ${delay / 1000} seconds...`);
|
|
107
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
108
|
+
delay *= 2; // Double the delay for the next retry
|
|
109
|
+
retries++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw new Error(`Failed after ${API_MAX_RETRIES} attempts. to call method ${config.method}`);
|
|
113
|
+
}
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
115
|
+
function log(...args) {
|
|
116
|
+
if (process.env.DEBUG === 'true') {
|
|
117
|
+
console.log(...args);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ethers } from
|
|
2
|
-
import { EtherRpcConfig } from
|
|
3
|
-
import { ExecutionBase } from
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { EtherRpcConfig } from '../types';
|
|
3
|
+
import { ExecutionBase } from './executionBase';
|
|
4
4
|
export declare class ExecutionApp extends ExecutionBase {
|
|
5
5
|
steerSubgraph: string;
|
|
6
6
|
vault: string;
|