@steerprotocol/data-connector-loader 0.1.5 → 1.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/bundle/bundleBase.d.ts +29 -0
- package/lib/{dataConnector.js → bundle/bundleBase.js} +59 -42
- package/lib/bundle/bundleDataConnector.d.ts +8 -0
- package/lib/bundle/bundleDataConnector.js +73 -0
- package/lib/esm/bundle/bundleBase.d.ts +29 -0
- package/lib/esm/bundle/bundleBase.js +127 -0
- package/lib/esm/bundle/bundleDataConnector.d.ts +8 -0
- 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/esm/execution/executionBase.d.ts +14 -0
- 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 +17 -0
- package/lib/execution/executionApp.js +323 -0
- package/lib/execution/executionBase.d.ts +14 -0
- package/lib/execution/executionBase.js +118 -0
- package/lib/execution/executionDataConnector.d.ts +9 -0
- package/lib/execution/executionDataConnector.js +110 -0
- package/lib/index.d.ts +5 -15
- package/lib/index.js +21 -197
- package/lib/types.d.ts +40 -0
- package/lib/utils/blockFinder.d.ts +8 -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/utils.d.ts +16 -0
- package/lib/utils/utils.js +146 -0
- package/package.json +35 -5
- package/lib/dataConnector.d.ts +0 -27
- 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
- /package/lib/{blockFinder.js → utils/blockFinder.js} +0 -0
- /package/lib/{pluginManager.js → utils/pluginManager.js} +0 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { EtherRpcConfig } from '../types';
|
|
3
|
+
import { ExecutionBase } from './executionBase';
|
|
4
|
+
export declare class ExecutionApp extends ExecutionBase {
|
|
5
|
+
steerSubgraph: string;
|
|
6
|
+
vault: string;
|
|
7
|
+
isAlgebraPool: boolean;
|
|
8
|
+
isDirectionalPool: boolean;
|
|
9
|
+
chainId: number;
|
|
10
|
+
constructor(vault: string, steerSubgraph: string, isAlgebraPool: boolean, isDirectionalPool: boolean, chainId: number, maxRetries?: number);
|
|
11
|
+
execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.providers.Provider, _etherRpcConfig?: EtherRpcConfig): Promise<Array<T>>;
|
|
12
|
+
private getSmartPoolExecutionContext;
|
|
13
|
+
private getPoolCurrentTick;
|
|
14
|
+
private getVaultPoolAddress;
|
|
15
|
+
private getSlot0;
|
|
16
|
+
private getGlobalState;
|
|
17
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
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.ExecutionApp = void 0;
|
|
13
|
+
const bundleDataConnector_1 = require("../bundle/bundleDataConnector");
|
|
14
|
+
const utils_1 = require("../utils/utils");
|
|
15
|
+
const executionBase_1 = require("./executionBase");
|
|
16
|
+
class ExecutionApp extends executionBase_1.ExecutionBase {
|
|
17
|
+
constructor(vault, steerSubgraph, isAlgebraPool, isDirectionalPool, chainId, maxRetries = 3) {
|
|
18
|
+
super(maxRetries);
|
|
19
|
+
this.getSlot0 = (pool, currentExecutionContext, provider, rpc) => __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
const slot0Abi = [
|
|
21
|
+
{
|
|
22
|
+
inputs: [],
|
|
23
|
+
name: 'slot0',
|
|
24
|
+
outputs: [
|
|
25
|
+
{ internalType: 'uint160', name: 'sqrtPriceX96', type: 'uint160' },
|
|
26
|
+
{ internalType: 'int24', name: 'tick', type: 'int24' },
|
|
27
|
+
{ internalType: 'uint16', name: 'observationIndex', type: 'uint16' },
|
|
28
|
+
{
|
|
29
|
+
internalType: 'uint16',
|
|
30
|
+
name: 'observationCardinality',
|
|
31
|
+
type: 'uint16',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
internalType: 'uint16',
|
|
35
|
+
name: 'observationCardinalityNext',
|
|
36
|
+
type: 'uint16',
|
|
37
|
+
},
|
|
38
|
+
{ internalType: 'uint8', name: 'feeProtocol', type: 'uint8' },
|
|
39
|
+
{ internalType: 'bool', name: 'unlocked', type: 'bool' },
|
|
40
|
+
],
|
|
41
|
+
stateMutability: 'view',
|
|
42
|
+
type: 'function',
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
const slot0 = yield (0, utils_1.readSlot0Contract)({
|
|
46
|
+
address: pool,
|
|
47
|
+
abi: slot0Abi,
|
|
48
|
+
currentExecutionContext,
|
|
49
|
+
functionName: 'slot0',
|
|
50
|
+
provider: provider,
|
|
51
|
+
rpc,
|
|
52
|
+
chainId: this.chainId,
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
sqrtPriceX96: slot0[0],
|
|
56
|
+
tick: slot0[1],
|
|
57
|
+
observationIndex: slot0[2],
|
|
58
|
+
observationCardinality: slot0[3],
|
|
59
|
+
observationCardinalityNext: slot0[4],
|
|
60
|
+
feeProtocol: slot0[5],
|
|
61
|
+
unlocked: slot0[6],
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
this.getGlobalState = (pool, currentExecutionContext, provider, rpc) => __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
const slot0Abi = [
|
|
66
|
+
!this.isDirectionalPool
|
|
67
|
+
? {
|
|
68
|
+
inputs: [],
|
|
69
|
+
name: 'globalState',
|
|
70
|
+
outputs: [
|
|
71
|
+
{
|
|
72
|
+
internalType: 'uint160',
|
|
73
|
+
name: 'price',
|
|
74
|
+
type: 'uint160',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
internalType: 'int24',
|
|
78
|
+
name: 'tick',
|
|
79
|
+
type: 'int24',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
internalType: 'int24',
|
|
83
|
+
name: 'prevInitializedTick',
|
|
84
|
+
type: 'int24',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
internalType: 'uint16',
|
|
88
|
+
name: 'fee',
|
|
89
|
+
type: 'uint16',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
internalType: 'uint16',
|
|
93
|
+
name: 'timepointIndex',
|
|
94
|
+
type: 'uint16',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
internalType: 'uint8',
|
|
98
|
+
name: 'communityFee',
|
|
99
|
+
type: 'uint8',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
internalType: 'bool',
|
|
103
|
+
name: 'unlocked',
|
|
104
|
+
type: 'bool',
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
stateMutability: 'view',
|
|
108
|
+
type: 'function',
|
|
109
|
+
}
|
|
110
|
+
: {
|
|
111
|
+
inputs: [],
|
|
112
|
+
name: 'globalState',
|
|
113
|
+
outputs: [
|
|
114
|
+
{
|
|
115
|
+
internalType: 'uint160',
|
|
116
|
+
name: 'price',
|
|
117
|
+
type: 'uint160',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
internalType: 'int24',
|
|
121
|
+
name: 'tick',
|
|
122
|
+
type: 'int24',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
internalType: 'uint16',
|
|
126
|
+
name: 'feeZto',
|
|
127
|
+
type: 'uint16',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
internalType: 'uint16',
|
|
131
|
+
name: 'feeOtz',
|
|
132
|
+
type: 'uint16',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
internalType: 'uint16',
|
|
136
|
+
name: 'timepointIndex',
|
|
137
|
+
type: 'uint16',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
internalType: 'uint8',
|
|
141
|
+
name: 'communityFeeToken0',
|
|
142
|
+
type: 'uint8',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
internalType: 'uint8',
|
|
146
|
+
name: 'communityFeeToken1',
|
|
147
|
+
type: 'uint8',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
internalType: 'bool',
|
|
151
|
+
name: 'unlocked',
|
|
152
|
+
type: 'bool',
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
stateMutability: 'view',
|
|
156
|
+
type: 'function',
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
const slot0 = yield (0, utils_1.readSlot0Contract)({
|
|
160
|
+
address: pool,
|
|
161
|
+
abi: slot0Abi,
|
|
162
|
+
currentExecutionContext,
|
|
163
|
+
functionName: 'globalState',
|
|
164
|
+
provider: provider,
|
|
165
|
+
rpc,
|
|
166
|
+
chainId: this.chainId,
|
|
167
|
+
});
|
|
168
|
+
return this.isDirectionalPool
|
|
169
|
+
? {
|
|
170
|
+
price: slot0[0],
|
|
171
|
+
tick: slot0[1],
|
|
172
|
+
observationIndex: slot0[2],
|
|
173
|
+
observationCardinality: slot0[3],
|
|
174
|
+
observationCardinalityNext: slot0[4],
|
|
175
|
+
feeProtocol: slot0[5],
|
|
176
|
+
unlocked: slot0[6],
|
|
177
|
+
}
|
|
178
|
+
: {
|
|
179
|
+
price: slot0[0],
|
|
180
|
+
tick: slot0[1],
|
|
181
|
+
feeProtocol: slot0[2],
|
|
182
|
+
observationIndex: slot0[2],
|
|
183
|
+
observationCardinality: slot0[3],
|
|
184
|
+
observationCardinalityNext: slot0[4],
|
|
185
|
+
unlocked: slot0[6],
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
this.steerSubgraph = steerSubgraph;
|
|
189
|
+
this.vault = vault;
|
|
190
|
+
this.isAlgebraPool = isAlgebraPool;
|
|
191
|
+
this.isDirectionalPool = isDirectionalPool;
|
|
192
|
+
this.chainId = chainId;
|
|
193
|
+
}
|
|
194
|
+
execute(_wasmFile, _vaultConfig, _data, _identity, _etherRpcConfig) {
|
|
195
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
196
|
+
const dataConnector = new bundleDataConnector_1.DataConnector(_wasmFile, _vaultConfig, _identity, _etherRpcConfig);
|
|
197
|
+
yield dataConnector.loadSync();
|
|
198
|
+
const executionContext = yield this.getExecutionContext(dataConnector, JSON.parse(_vaultConfig));
|
|
199
|
+
let rpc = '';
|
|
200
|
+
if (_etherRpcConfig) {
|
|
201
|
+
rpc = _etherRpcConfig[this.chainId.toString()].url;
|
|
202
|
+
}
|
|
203
|
+
const smartPoolExecutionContext = yield this.getSmartPoolExecutionContext(executionContext, dataConnector, JSON.parse(_vaultConfig), rpc);
|
|
204
|
+
dataConnector.setExecutionContextStrategyExecution(Object.assign(Object.assign({}, executionContext), smartPoolExecutionContext));
|
|
205
|
+
_data = _data.map((element) => JSON.stringify(element));
|
|
206
|
+
const result = yield dataConnector.executeBundle(_data);
|
|
207
|
+
return JSON.parse(result);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
getSmartPoolExecutionContext(currentExecutionContext, dataConnector, vaultConfig, rpc) {
|
|
211
|
+
var _a, _b, _c, _d, _e, _f;
|
|
212
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
213
|
+
const smartPoolExecutionContext = {};
|
|
214
|
+
const request = {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
url: this.steerSubgraph,
|
|
217
|
+
data: {
|
|
218
|
+
query: `
|
|
219
|
+
{
|
|
220
|
+
vaultPositions(where:{
|
|
221
|
+
vault: "${this.vault}",
|
|
222
|
+
timestamp_lt: "${currentExecutionContext.epochTimestamp}"
|
|
223
|
+
}, orderDirection: desc, orderBy: timestamp, first: 1){
|
|
224
|
+
id
|
|
225
|
+
timestamp
|
|
226
|
+
upperTick
|
|
227
|
+
lowerTick
|
|
228
|
+
relativeWeight
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
`,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
const response = yield (0, utils_1.fetchData)(request, 4);
|
|
235
|
+
this.log('Subgraph response vault positions :', response);
|
|
236
|
+
const requestSnapshot = {
|
|
237
|
+
method: 'POST',
|
|
238
|
+
url: this.steerSubgraph,
|
|
239
|
+
data: {
|
|
240
|
+
query: `
|
|
241
|
+
{
|
|
242
|
+
vaultSnapshots(where:{
|
|
243
|
+
vaultAddress: "${this.vault}",
|
|
244
|
+
timestamp_lt: "${currentExecutionContext.epochTimestamp}"
|
|
245
|
+
}, orderDirection: desc, orderBy: timestamp, first: 1){
|
|
246
|
+
id
|
|
247
|
+
timestamp
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
`,
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
const responseSnapshot = yield (0, utils_1.fetchData)(requestSnapshot, 4);
|
|
254
|
+
this.log('Subgraph response current snapshot', responseSnapshot);
|
|
255
|
+
const positions = [];
|
|
256
|
+
if (((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.vaultPositions) && ((_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.vaultPositions.length) > 0) {
|
|
257
|
+
const positionResponse = (_c = response === null || response === void 0 ? void 0 : response.data) === null || _c === void 0 ? void 0 : _c.vaultPositions[0];
|
|
258
|
+
for (let i = 0; i < positionResponse.upperTick.length; i++) {
|
|
259
|
+
const position = positionResponse;
|
|
260
|
+
positions.push({
|
|
261
|
+
startTick: Number(position.lowerTick[i]),
|
|
262
|
+
endTick: Number(position.upperTick[i]),
|
|
263
|
+
weight: Number(position.relativeWeight[i]),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
let lastSnapshot;
|
|
268
|
+
if (((_d = responseSnapshot === null || responseSnapshot === void 0 ? void 0 : responseSnapshot.data) === null || _d === void 0 ? void 0 : _d.vaultSnapshots) && ((_e = responseSnapshot === null || responseSnapshot === void 0 ? void 0 : responseSnapshot.data) === null || _e === void 0 ? void 0 : _e.vaultSnapshots.length) > 0) {
|
|
269
|
+
lastSnapshot = (_f = responseSnapshot === null || responseSnapshot === void 0 ? void 0 : responseSnapshot.data) === null || _f === void 0 ? void 0 : _f.vaultSnapshots[0];
|
|
270
|
+
}
|
|
271
|
+
this.log('Subgraph response vault positions', positions);
|
|
272
|
+
smartPoolExecutionContext.positions = positions;
|
|
273
|
+
if (lastSnapshot) {
|
|
274
|
+
smartPoolExecutionContext.lastExecutionTime = (Date.now() - Number(lastSnapshot.timestamp) * 1000) / 1000;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
smartPoolExecutionContext.lastExecutionTime = currentExecutionContext.epochTimestamp;
|
|
278
|
+
}
|
|
279
|
+
const provider = dataConnector.getProvider(vaultConfig);
|
|
280
|
+
if (!provider) {
|
|
281
|
+
throw new Error('Provider configuration issue, unable to process Smart Pool execution context.');
|
|
282
|
+
}
|
|
283
|
+
const poolAddress = yield this.getVaultPoolAddress();
|
|
284
|
+
const currentTick = yield this.getPoolCurrentTick(poolAddress, provider, currentExecutionContext, rpc);
|
|
285
|
+
smartPoolExecutionContext.currentTick = currentTick;
|
|
286
|
+
// "currentTick": 10,
|
|
287
|
+
return smartPoolExecutionContext;
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
getPoolCurrentTick(poolAddress, provider, currentExecutionContext, rpc) {
|
|
291
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
292
|
+
if (!this.isAlgebraPool) {
|
|
293
|
+
const slot0 = yield this.getSlot0(poolAddress, currentExecutionContext, provider, rpc);
|
|
294
|
+
return slot0.tick;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
const slot0 = yield this.getGlobalState(poolAddress, currentExecutionContext, provider, rpc);
|
|
298
|
+
return slot0.tick;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
getVaultPoolAddress() {
|
|
303
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
304
|
+
const requestSnapshot = {
|
|
305
|
+
method: 'POST',
|
|
306
|
+
url: this.steerSubgraph,
|
|
307
|
+
data: {
|
|
308
|
+
query: `
|
|
309
|
+
{
|
|
310
|
+
vault(id: "${this.vault}"){
|
|
311
|
+
id
|
|
312
|
+
pool
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
`,
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
const response = yield (0, utils_1.fetchData)(requestSnapshot, 4);
|
|
319
|
+
return response.data.vault.pool;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
exports.ExecutionApp = ExecutionApp;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { DataConnector } from '../bundle/bundleDataConnector';
|
|
3
|
+
import { VaultConfigPayload, ExecutionContext, EtherRpcConfig } from '../types';
|
|
4
|
+
export declare class ExecutionBase {
|
|
5
|
+
maxRetries: number;
|
|
6
|
+
constructor(maxRetries?: number);
|
|
7
|
+
protected delay(ms: number): Promise<void>;
|
|
8
|
+
protected log(...args: any[]): void;
|
|
9
|
+
protected getExecutionContext(dataConnector: DataConnector, vaultConfig: VaultConfigPayload): Promise<ExecutionContext>;
|
|
10
|
+
execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.providers.Provider, _etherRpcConfig?: EtherRpcConfig): Promise<Array<T>>;
|
|
11
|
+
protected getDataConnectorVersion(dataConnector: DataConnector): Promise<number>;
|
|
12
|
+
getAverageBlockTime(provider: ethers.providers.Provider, blockCount?: number): Promise<number>;
|
|
13
|
+
getBlockNumberFromAvgBlockTime(provider: ethers.providers.Provider, targetTimestamp: number, avgBlockTime: number): Promise<number>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
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.ExecutionBase = void 0;
|
|
13
|
+
class ExecutionBase {
|
|
14
|
+
constructor(maxRetries = 3) {
|
|
15
|
+
this.maxRetries = 0;
|
|
16
|
+
this.maxRetries = maxRetries;
|
|
17
|
+
}
|
|
18
|
+
delay(ms) {
|
|
19
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
20
|
+
}
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
log(...args) {
|
|
23
|
+
if (process.env.DEBUG === 'true') {
|
|
24
|
+
console.log(...args);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
getExecutionContext(dataConnector, vaultConfig) {
|
|
28
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
const provider = dataConnector.getProvider({});
|
|
30
|
+
let blockTime = 0;
|
|
31
|
+
let blockNumber = 9;
|
|
32
|
+
const anchorBlockNumber = vaultConfig.anchorBlockNumber;
|
|
33
|
+
const anchorProvided = typeof anchorBlockNumber === 'number' && anchorBlockNumber >= 0;
|
|
34
|
+
if (provider) {
|
|
35
|
+
blockTime = yield this.getAverageBlockTime(provider);
|
|
36
|
+
if (!anchorProvided) {
|
|
37
|
+
blockNumber = yield this.getBlockNumberFromAvgBlockTime(provider, vaultConfig.epochTimestamp / 1000, // convert to seconds
|
|
38
|
+
blockTime);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (anchorProvided) {
|
|
42
|
+
blockNumber = anchorBlockNumber;
|
|
43
|
+
}
|
|
44
|
+
const context = {
|
|
45
|
+
executionTimestamp: new Date().getTime(),
|
|
46
|
+
epochTimestamp: vaultConfig.epochTimestamp,
|
|
47
|
+
epochLength: Number(vaultConfig.strategyConfigData.epochLength),
|
|
48
|
+
vaultAddress: vaultConfig.vaultAddress,
|
|
49
|
+
blockTime: blockTime,
|
|
50
|
+
blockNumber: blockNumber,
|
|
51
|
+
};
|
|
52
|
+
return context;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
execute(_wasmFile, _vaultConfig, _data, _identity, _etherRpcConfig) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
throw new Error('Not implemented');
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
getDataConnectorVersion(dataConnector) {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
let version = 1;
|
|
63
|
+
try {
|
|
64
|
+
version = yield dataConnector.version();
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
// Data connector is missing version function, fallback to v1 logic
|
|
68
|
+
}
|
|
69
|
+
return version;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
getAverageBlockTime(provider, blockCount = 5) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
const latestBlock = yield provider.getBlock('latest');
|
|
75
|
+
if (latestBlock) {
|
|
76
|
+
const startBlockNumber = Math.max(0, latestBlock.number - blockCount + 1);
|
|
77
|
+
let totalTimeDiff = 0;
|
|
78
|
+
let previousBlock = yield provider.getBlock(startBlockNumber);
|
|
79
|
+
if (!previousBlock) {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
for (let i = startBlockNumber + 1; i <= latestBlock.number; i++) {
|
|
83
|
+
const currentBlock = yield provider.getBlock(i);
|
|
84
|
+
if (!currentBlock || !previousBlock || !currentBlock.timestamp || !previousBlock.timestamp) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const timeDiff = currentBlock.timestamp - previousBlock.timestamp;
|
|
88
|
+
totalTimeDiff += timeDiff;
|
|
89
|
+
previousBlock = currentBlock;
|
|
90
|
+
}
|
|
91
|
+
const averageBlockTime = totalTimeDiff / (blockCount - 1);
|
|
92
|
+
return averageBlockTime;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
getBlockNumberFromAvgBlockTime(provider, targetTimestamp, avgBlockTime) {
|
|
100
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
const latestBlock = yield provider.getBlock('latest');
|
|
102
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
103
|
+
const timeDifference = currentTime - targetTimestamp;
|
|
104
|
+
if (!latestBlock) {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
if (avgBlockTime === 0) {
|
|
108
|
+
return latestBlock.number;
|
|
109
|
+
}
|
|
110
|
+
// Calculate the estimated number of blocks based on the average block time
|
|
111
|
+
const estimatedBlockCount = Math.floor(timeDifference / avgBlockTime);
|
|
112
|
+
// Calculate the estimated block number
|
|
113
|
+
const estimatedBlockNumber = latestBlock.number - estimatedBlockCount;
|
|
114
|
+
return Math.max(0, estimatedBlockNumber);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.ExecutionBase = ExecutionBase;
|