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