@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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { WasmModule } from '@steerprotocol/app-loader';
|
|
2
|
-
import { EtherRpcConfig, ExecutionContext } from '../types';
|
|
3
2
|
import { providers } from 'ethers';
|
|
3
|
+
import { EtherRpcConfig, ExecutionContext } from '../types';
|
|
4
4
|
export declare class BundleBase {
|
|
5
|
-
|
|
5
|
+
module?: WasmModule;
|
|
6
6
|
protected wasmFile: ArrayBuffer | string;
|
|
7
7
|
protected config: string;
|
|
8
|
-
|
|
8
|
+
configObj: {
|
|
9
9
|
[key: string]: any;
|
|
10
10
|
};
|
|
11
11
|
protected etherRpcConfig?: EtherRpcConfig;
|
|
@@ -13,12 +13,14 @@ export declare class BundleBase {
|
|
|
13
13
|
executionContext?: ExecutionContext & {
|
|
14
14
|
epochTimestamp: number;
|
|
15
15
|
};
|
|
16
|
+
protected scheduleEpochTimestamp: number | undefined;
|
|
16
17
|
constructor(wasmFile: string | ArrayBuffer, _config: string, identity?: providers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
17
18
|
load(): Promise<WasmModule>;
|
|
18
19
|
loadSync(): WasmModule;
|
|
19
20
|
execute(response: string): Promise<string>;
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
executeBundle(data: Array<any>): Promise<string>;
|
|
22
|
+
setExecutionContextStrategyExecution(executionContext: ExecutionContext): void;
|
|
23
|
+
setExecutionContextDataConnector(executionContext: ExecutionContext): void;
|
|
22
24
|
protected executeModule(response: string): Promise<string>;
|
|
23
25
|
getProvider(config: any): providers.Provider | undefined;
|
|
24
26
|
getResults(): string;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { loadWasm, loadWasmSync } from '@steerprotocol/app-loader';
|
|
2
|
+
import { providers } from 'ethers';
|
|
3
|
+
export class BundleBase {
|
|
4
|
+
constructor(wasmFile, _config, identity, etherRpcConfig) {
|
|
5
|
+
this.wasmFile = wasmFile;
|
|
6
|
+
this.config = _config;
|
|
7
|
+
this.configObj = JSON.parse(_config);
|
|
8
|
+
this.providerIdentity = identity;
|
|
9
|
+
this.etherRpcConfig = etherRpcConfig;
|
|
10
|
+
}
|
|
11
|
+
async load() {
|
|
12
|
+
const instance = await loadWasm(this.wasmFile, {});
|
|
13
|
+
this.module = instance;
|
|
14
|
+
this.module.initialize(this.config);
|
|
15
|
+
return this.module;
|
|
16
|
+
}
|
|
17
|
+
loadSync() {
|
|
18
|
+
if (typeof this.wasmFile === 'string')
|
|
19
|
+
throw new Error('Cannot load module synchronously unless wasmFile is of type ArrayBuffer');
|
|
20
|
+
this.module = loadWasmSync(this.wasmFile, {});
|
|
21
|
+
return this.module;
|
|
22
|
+
}
|
|
23
|
+
async execute(response) {
|
|
24
|
+
if (!this.executionContext) {
|
|
25
|
+
throw new Error('Execution context missing');
|
|
26
|
+
}
|
|
27
|
+
// If the module is not initialized, try to load it
|
|
28
|
+
if (!this.module) {
|
|
29
|
+
try {
|
|
30
|
+
await this.load();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new Error('Module not initialized');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// If the module is still not initialized, throw an error
|
|
37
|
+
if (!this.module)
|
|
38
|
+
throw new Error('Module not initialized');
|
|
39
|
+
// Call the main function with the response
|
|
40
|
+
return await this.module.execute(response);
|
|
41
|
+
}
|
|
42
|
+
async executeBundle(data) {
|
|
43
|
+
if (!this.executionContext) {
|
|
44
|
+
throw new Error('Execution context missing');
|
|
45
|
+
}
|
|
46
|
+
// If the module is not initialized, try to load it
|
|
47
|
+
if (!this.module) {
|
|
48
|
+
try {
|
|
49
|
+
await this.load();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
throw new Error('Module not initialized');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// If the module is still not initialized, throw an error
|
|
56
|
+
if (!this.module)
|
|
57
|
+
throw new Error('Module not initialized');
|
|
58
|
+
// Call the main function with the response
|
|
59
|
+
return this.module.execute(...data);
|
|
60
|
+
}
|
|
61
|
+
setExecutionContextStrategyExecution(executionContext) {
|
|
62
|
+
// Determine the timestamp for epoch
|
|
63
|
+
const timestamp = this.subtractDate(executionContext.executionTimestamp, Number(executionContext.epochLength)).getTime();
|
|
64
|
+
// Divide milliseconds data to seconds for transformation bundle
|
|
65
|
+
const timestampInSecs = Math.floor(timestamp / 1000);
|
|
66
|
+
this.executionContext = { ...executionContext, epochTimestamp: timestampInSecs };
|
|
67
|
+
const completeConfig = { ...JSON.parse(this.config), ...this.executionContext };
|
|
68
|
+
this.scheduleEpochTimestamp = executionContext.epochTimestamp;
|
|
69
|
+
console.log('Complete execution config : ', completeConfig);
|
|
70
|
+
this.module?.initialize(JSON.stringify(completeConfig));
|
|
71
|
+
}
|
|
72
|
+
setExecutionContextDataConnector(executionContext) {
|
|
73
|
+
// Determine the timestamp for epoch
|
|
74
|
+
const timestamp = this.subtractDate(executionContext.executionTimestamp, Number(executionContext.epochLength)).getTime();
|
|
75
|
+
// Divide milliseconds data to seconds for transformation bundle
|
|
76
|
+
const timestampInSecs = Math.floor(timestamp / 1000);
|
|
77
|
+
this.executionContext = { ...executionContext, epochTimestamp: timestampInSecs };
|
|
78
|
+
const completeConfig = { ...JSON.parse(this.config), executionContext: this.executionContext };
|
|
79
|
+
this.scheduleEpochTimestamp = executionContext.epochTimestamp;
|
|
80
|
+
console.log('Complete execution config : ', completeConfig);
|
|
81
|
+
this.module?.initialize(JSON.stringify(completeConfig));
|
|
82
|
+
}
|
|
83
|
+
async executeModule(response) {
|
|
84
|
+
if (!this.module) {
|
|
85
|
+
throw new Error('Module not initialized');
|
|
86
|
+
}
|
|
87
|
+
return await this.module.execute(response);
|
|
88
|
+
}
|
|
89
|
+
// This function returns a provider based on the chainId of the request
|
|
90
|
+
// The chainId is first checked in the data connector config, and if not found
|
|
91
|
+
// it is checked in the request config. If the chainId is found in either of
|
|
92
|
+
// these places, the provider is returned from the etherRpcConfig object
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
getProvider(config) {
|
|
95
|
+
let chainId;
|
|
96
|
+
// check if chainId is configured in data connector config
|
|
97
|
+
if (this.configObj.chainId) {
|
|
98
|
+
chainId = this.configObj.chainId;
|
|
99
|
+
}
|
|
100
|
+
else if (config && config.chainId) {
|
|
101
|
+
// if chainId is in the request config from dataconnector execution
|
|
102
|
+
chainId = config.chainId;
|
|
103
|
+
}
|
|
104
|
+
if (chainId && this.etherRpcConfig && this.etherRpcConfig[chainId]) {
|
|
105
|
+
const provider = new providers.JsonRpcProvider(this.etherRpcConfig[chainId].url);
|
|
106
|
+
return provider;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
return this.providerIdentity;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
getResults() {
|
|
113
|
+
if (!this.module)
|
|
114
|
+
throw new Error('Module not initialized');
|
|
115
|
+
return this.module.transform();
|
|
116
|
+
}
|
|
117
|
+
version() {
|
|
118
|
+
if (!this.module)
|
|
119
|
+
throw new Error('Module not initialized');
|
|
120
|
+
return Number(this.module.version());
|
|
121
|
+
}
|
|
122
|
+
subtractDate(timestamp, epochLength) {
|
|
123
|
+
const date = new Date(timestamp);
|
|
124
|
+
date.setSeconds(date.getSeconds() - epochLength);
|
|
125
|
+
return date;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { providers } from 'ethers';
|
|
2
|
-
import { BundleBase } from './bundleBase';
|
|
3
2
|
import { EtherRpcConfig, ViewConfig } from '../types';
|
|
3
|
+
import { BundleBase } from './bundleBase';
|
|
4
4
|
export declare class DataConnector extends BundleBase {
|
|
5
5
|
constructor(wasmFile: string | ArrayBuffer, _config: string, identity?: providers.Provider, etherRpcConfig?: EtherRpcConfig);
|
|
6
|
+
getRpcUrl(config: any): string | undefined;
|
|
6
7
|
view(config: ViewConfig): Promise<string>;
|
|
7
8
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Contract } from 'ethers';
|
|
2
|
+
import { CENTRALIZE_CONTRACT_INTERACTION_CHAINS } from '../utils/consts';
|
|
3
|
+
import { callWithRetry } from '../utils/utils';
|
|
4
|
+
import { BundleBase } from './bundleBase';
|
|
5
|
+
export class DataConnector extends BundleBase {
|
|
6
|
+
// Identity should come from the node's IdentityManager.wallet
|
|
7
|
+
constructor(wasmFile, _config, identity, etherRpcConfig) {
|
|
8
|
+
super(wasmFile, _config, identity, etherRpcConfig);
|
|
9
|
+
}
|
|
10
|
+
// get rpc url from configuration
|
|
11
|
+
getRpcUrl(config) {
|
|
12
|
+
let chainId;
|
|
13
|
+
// check if chainId is configured in data connector config
|
|
14
|
+
if (this.configObj.chainId) {
|
|
15
|
+
chainId = this.configObj.chainId;
|
|
16
|
+
}
|
|
17
|
+
else if (config && config.chainId) {
|
|
18
|
+
// if chainId is in the request config from dataconnector execution
|
|
19
|
+
chainId = config.chainId;
|
|
20
|
+
}
|
|
21
|
+
if (chainId && this.etherRpcConfig && this.etherRpcConfig[chainId]) {
|
|
22
|
+
return this.etherRpcConfig[chainId].url;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Makes contract instance and calls, returns stringified result
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
async view(config) {
|
|
31
|
+
const provider = this.getProvider(config);
|
|
32
|
+
if (!provider)
|
|
33
|
+
throw new Error('Provider not initialized');
|
|
34
|
+
const contract = new Contract(config.address, config.abi, provider);
|
|
35
|
+
// For formatting the parameters if needed
|
|
36
|
+
const paramsArray = config.arguments;
|
|
37
|
+
let result;
|
|
38
|
+
if (CENTRALIZE_CONTRACT_INTERACTION_CHAINS.indexOf(this.configObj.chainId.toString()) === -1) {
|
|
39
|
+
result = await contract[config.method](...paramsArray, {
|
|
40
|
+
blockTag: this.executionContext?.blockNumber,
|
|
41
|
+
}); //@note uncomment for deterministic blocks
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const viewContractConfig = {
|
|
45
|
+
epochTimestamp: this.scheduleEpochTimestamp,
|
|
46
|
+
address: this.configObj.address || config.address,
|
|
47
|
+
abi: config.abi,
|
|
48
|
+
arguments: paramsArray,
|
|
49
|
+
method: config.method,
|
|
50
|
+
rpcUrl: this.getRpcUrl(config),
|
|
51
|
+
};
|
|
52
|
+
result = await callWithRetry(viewContractConfig);
|
|
53
|
+
}
|
|
54
|
+
// This gets stringified before calling main
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -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,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
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ethers } from 'ethers';
|
|
2
2
|
import { DataConnector } from '../bundle/bundleDataConnector';
|
|
3
|
-
import { VaultConfigPayload, ExecutionContext } from '../types';
|
|
3
|
+
import { VaultConfigPayload, ExecutionContext, EtherRpcConfig } from '../types';
|
|
4
4
|
export declare class ExecutionBase {
|
|
5
5
|
maxRetries: number;
|
|
6
6
|
constructor(maxRetries?: number);
|
|
7
7
|
protected delay(ms: number): Promise<void>;
|
|
8
8
|
protected log(...args: any[]): void;
|
|
9
9
|
protected getExecutionContext(dataConnector: DataConnector, vaultConfig: VaultConfigPayload): Promise<ExecutionContext>;
|
|
10
|
-
execute<T>(_vaultConfig: string): Promise<Array<T>>;
|
|
10
|
+
execute<T>(_wasmFile: string | ArrayBuffer, _vaultConfig: string, _data: Array<any>, _identity?: ethers.providers.Provider, _etherRpcConfig?: EtherRpcConfig): Promise<Array<T>>;
|
|
11
11
|
protected getDataConnectorVersion(dataConnector: DataConnector): Promise<number>;
|
|
12
12
|
getAverageBlockTime(provider: ethers.providers.Provider, blockCount?: number): Promise<number>;
|
|
13
13
|
getBlockNumberFromAvgBlockTime(provider: ethers.providers.Provider, targetTimestamp: number, avgBlockTime: number): Promise<number>;
|
|
@@ -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
|
+
}
|