@marinade.finance/scoring 1.0.0 → 1.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/computing/cluster.ts +102 -0
- package/computing/eligibility.ts +163 -0
- package/computing/score.ts +164 -0
- package/computing/stake.ts +246 -0
- package/computing/validators.ts +133 -0
- package/{src/dto → dto}/bonds.dto.ts +0 -2
- package/{src/dto → dto}/cluster.dto.ts +0 -1
- package/{src/dto → dto}/eligibility.dto.ts +0 -5
- package/{src/dto → dto}/jito.dto.ts +1 -2
- package/{src/dto → dto}/marinade.dto.ts +1 -1
- package/{src/dto → dto}/rewards.dto.ts +1 -3
- package/{src/dto → dto}/scoring.dto.ts +1 -7
- package/{src/dto → dto}/snapshots.dto.ts +0 -3
- package/{src/dto → dto}/stakes.dto.ts +0 -4
- package/{src/dto → dto}/validators.dto.ts +1 -37
- package/package.json +17 -13
- package/{src/providers → providers}/api-data.provider.ts +58 -58
- package/{src/providers → providers}/file-data.provider.ts +7 -15
- package/{src/utils → utils}/csv.ts +3 -3
- package/src/cluster/cluster.module.ts +0 -8
- package/src/cluster/cluster.service.ts +0 -108
- package/src/config/config.module.ts +0 -8
- package/src/config/config.service.ts +0 -136
- package/src/eligibility/eligibility.module.ts +0 -11
- package/src/eligibility/eligibility.service.ts +0 -183
- package/src/scoring/scoring.module.ts +0 -11
- package/src/scoring/scoring.service.ts +0 -184
- package/src/stake/stake.module.ts +0 -10
- package/src/stake/stake.service.ts +0 -242
- package/src/validators/validators.module.ts +0 -48
- package/src/validators/validators.service.ts +0 -177
- /package/{src/constants → constants}/marinade.json +0 -0
- /package/{src/errors → errors}/fetching.ts +0 -0
- /package/{src/interfaces → interfaces}/data-provider.interface.ts +0 -0
- /package/{src/utils → utils}/maths.ts +0 -0
- /package/{src/utils → utils}/solana.ts +0 -0
- /package/{src/utils → utils}/zip.ts +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
import { Rewards, RewardsResponseDto } from '../dto/rewards.dto';
|
|
3
2
|
import { Votes, mSolSnapshotDto, veMNDESnapshotDto } from '../dto/snapshots.dto';
|
|
4
3
|
import { EpochStatDto, ValidatorDto, ValidatorsResponseDto } from '../dto/validators.dto';
|
|
@@ -10,6 +9,7 @@ import { writeFile } from 'fs/promises';
|
|
|
10
9
|
import { Bonds, BondsDto } from '../dto/bonds.dto';
|
|
11
10
|
import { TvlStats } from '../dto/marinade.dto';
|
|
12
11
|
import axios, { AxiosError } from 'axios';
|
|
12
|
+
import { error } from '../logger';
|
|
13
13
|
|
|
14
14
|
export class URLs {
|
|
15
15
|
validatorsURL: string;
|
|
@@ -23,7 +23,7 @@ export class URLs {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export class ApiDataProvider implements IDataProvider {
|
|
26
|
-
constructor (private urls: URLs) { }
|
|
26
|
+
constructor (private urls: URLs, private snapshotsBasePath?: string) { }
|
|
27
27
|
|
|
28
28
|
async fetchValidators (epochsToFetch: number, withSnapshot = false): Promise<ValidatorDto[]> {
|
|
29
29
|
try {
|
|
@@ -43,16 +43,16 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
return rawData.validators;
|
|
46
|
-
} catch (
|
|
46
|
+
} catch (err) {
|
|
47
47
|
if (axios.isAxiosError(error)) {
|
|
48
48
|
const axiosError = error as AxiosError;
|
|
49
49
|
if (axiosError.response) {
|
|
50
|
-
|
|
50
|
+
error(FETCHING_ERROR_MESSAGES.validatorsFetchFailed, axiosError.response.status);
|
|
51
51
|
} else if (axiosError.request) {
|
|
52
|
-
|
|
52
|
+
error(FETCHING_ERROR_MESSAGES.generalValidatorsFetchFailed, axiosError.request);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
throw
|
|
55
|
+
throw err;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -63,20 +63,20 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
63
63
|
const data: TvlStats = response.data;
|
|
64
64
|
|
|
65
65
|
if (withSnapshot) {
|
|
66
|
-
await writeFile(
|
|
66
|
+
await writeFile(`${this.snapshotsBasePath}/tvl.json`, JSON.stringify(data, null, 2));
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
return data;
|
|
70
|
-
} catch (
|
|
70
|
+
} catch (err) {
|
|
71
71
|
if (axios.isAxiosError(error)) {
|
|
72
72
|
const axiosError = error as AxiosError;
|
|
73
73
|
if (axiosError.response) {
|
|
74
|
-
|
|
74
|
+
error(FETCHING_ERROR_MESSAGES.tvlFetchFailed, axiosError.response.status);
|
|
75
75
|
} else if (axiosError.request) {
|
|
76
|
-
|
|
76
|
+
error(FETCHING_ERROR_MESSAGES.generalTvlFetchFailed, axiosError.request);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
throw
|
|
79
|
+
throw err;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -91,21 +91,21 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
91
91
|
}, {} as Bonds);
|
|
92
92
|
|
|
93
93
|
if (withSnapshot) {
|
|
94
|
-
await writeFile(
|
|
94
|
+
await writeFile(`${this.snapshotsBasePath}/bonds.json`, JSON.stringify(data, null, 2));
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
return bonds;
|
|
98
|
-
} catch (
|
|
99
|
-
if (axios.isAxiosError(
|
|
100
|
-
const axiosError =
|
|
98
|
+
} catch (err) {
|
|
99
|
+
if (axios.isAxiosError(err)) {
|
|
100
|
+
const axiosError = err as AxiosError;
|
|
101
101
|
if (axiosError.response) {
|
|
102
|
-
|
|
102
|
+
error(FETCHING_ERROR_MESSAGES.bondsFetchFailed, axiosError.response.status);
|
|
103
103
|
} else if (axiosError.request) {
|
|
104
|
-
|
|
104
|
+
error(FETCHING_ERROR_MESSAGES.generalBondsFetchFailed, axiosError.request);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
throw
|
|
108
|
+
throw err;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -118,7 +118,7 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
118
118
|
const csv: string = response.data;
|
|
119
119
|
|
|
120
120
|
if (withSnapshot) {
|
|
121
|
-
await writeFile(
|
|
121
|
+
await writeFile(`${this.snapshotsBasePath}/blacklist.csv`, csv);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const blacklistSet = new Set(
|
|
@@ -129,16 +129,16 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
129
129
|
);
|
|
130
130
|
|
|
131
131
|
return blacklistSet;
|
|
132
|
-
} catch (
|
|
133
|
-
if (axios.isAxiosError(
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
} else if (
|
|
137
|
-
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (axios.isAxiosError(err)) {
|
|
134
|
+
if (err.response) {
|
|
135
|
+
error(FETCHING_ERROR_MESSAGES.blacklistFetchFailed, err.response.status);
|
|
136
|
+
} else if (err.request) {
|
|
137
|
+
error(FETCHING_ERROR_MESSAGES.generalBlacklistFetchFailed, err.request);
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
throw
|
|
141
|
+
throw err;
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -148,7 +148,7 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
148
148
|
const data: veMNDESnapshotDto = response.data;
|
|
149
149
|
|
|
150
150
|
if (withSnapshot) {
|
|
151
|
-
await writeFile(
|
|
151
|
+
await writeFile(`${this.snapshotsBasePath}/vemnde-votes.json`, JSON.stringify(data, null, 2));
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
const result: Votes = {};
|
|
@@ -160,16 +160,16 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
return result;
|
|
163
|
-
} catch (
|
|
164
|
-
if (axios.isAxiosError(
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
} else if (
|
|
168
|
-
|
|
163
|
+
} catch (err) {
|
|
164
|
+
if (axios.isAxiosError(err)) {
|
|
165
|
+
if (err.response) {
|
|
166
|
+
error(FETCHING_ERROR_MESSAGES.vemndeFetchFailed, err.response.status);
|
|
167
|
+
} else if (err.request) {
|
|
168
|
+
error(FETCHING_ERROR_MESSAGES.generalVemndeFetchFailed, err.request);
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
throw
|
|
172
|
+
throw err;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
@@ -179,7 +179,7 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
179
179
|
const data: mSolSnapshotDto = response.data;
|
|
180
180
|
|
|
181
181
|
if (withSnapshot) {
|
|
182
|
-
await writeFile(
|
|
182
|
+
await writeFile(`${this.snapshotsBasePath}/msol-votes.json`, JSON.stringify(data, null, 2));
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
const result: Votes = {};
|
|
@@ -191,16 +191,16 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
return result;
|
|
194
|
-
} catch (
|
|
195
|
-
if (axios.isAxiosError(
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
} else if (
|
|
199
|
-
|
|
194
|
+
} catch (err) {
|
|
195
|
+
if (axios.isAxiosError(err)) {
|
|
196
|
+
if (err.response) {
|
|
197
|
+
error(FETCHING_ERROR_MESSAGES.msolFetchFailed, err.response.status);
|
|
198
|
+
} else if (err.request) {
|
|
199
|
+
error(FETCHING_ERROR_MESSAGES.generalMsolFetchFailed, err.request);
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
throw
|
|
203
|
+
throw err;
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
|
|
@@ -214,23 +214,23 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
214
214
|
const data = new RewardsResponseDto(jsonResponse);
|
|
215
215
|
|
|
216
216
|
if (withSnapshot) {
|
|
217
|
-
await writeFile(
|
|
217
|
+
await writeFile(`${this.snapshotsBasePath}/rewards.json`, JSON.stringify(jsonResponse, null, 2));
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
const mev = sum(data.rewards_mev.map(dto => dto.amount));
|
|
221
221
|
const inflation = sum(data.rewards_inflation_est.map(dto => dto.amount));
|
|
222
222
|
|
|
223
223
|
return { inflation, mev };
|
|
224
|
-
} catch (
|
|
225
|
-
if (axios.isAxiosError(
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
} else if (
|
|
229
|
-
|
|
224
|
+
} catch (err) {
|
|
225
|
+
if (axios.isAxiosError(err)) {
|
|
226
|
+
if (err.response) {
|
|
227
|
+
error(FETCHING_ERROR_MESSAGES.rewardsFetchFailed, err.response.status);
|
|
228
|
+
} else if (err.request) {
|
|
229
|
+
error(FETCHING_ERROR_MESSAGES.generalRewardsFetchFailed, err.request);
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
throw
|
|
233
|
+
throw err;
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
|
|
@@ -240,22 +240,22 @@ export class ApiDataProvider implements IDataProvider {
|
|
|
240
240
|
const jitoMevRecords: JitoValidatorsResponseDto = response.data;
|
|
241
241
|
|
|
242
242
|
if (withSnapshot) {
|
|
243
|
-
await writeFile(
|
|
243
|
+
await writeFile(`${this.snapshotsBasePath}/jito-mev-records.json`, JSON.stringify(jitoMevRecords, null, 2));
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
return Object.fromEntries(
|
|
247
247
|
jitoMevRecords.validators.map((validator) => [validator.vote_account, validator])
|
|
248
248
|
);
|
|
249
|
-
} catch (
|
|
250
|
-
if (axios.isAxiosError(
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
} else if (
|
|
254
|
-
|
|
249
|
+
} catch (err) {
|
|
250
|
+
if (axios.isAxiosError(err)) {
|
|
251
|
+
if (err.response) {
|
|
252
|
+
error(FETCHING_ERROR_MESSAGES.jitoMevFetchFailed, err.response.status);
|
|
253
|
+
} else if (err.request) {
|
|
254
|
+
error(FETCHING_ERROR_MESSAGES.generalJitoMEVFetchFailed, err.request);
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
throw
|
|
258
|
+
throw err;
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3
2
|
import { TvlStats } from '../dto/marinade.dto';
|
|
4
3
|
import { Bonds, BondsDto } from '../dto/bonds.dto';
|
|
@@ -10,6 +9,7 @@ import { readFile } from 'fs/promises';
|
|
|
10
9
|
import { FETCHING_ERROR_MESSAGES } from '../errors/fetching';
|
|
11
10
|
import { sum } from '../utils/maths';
|
|
12
11
|
import { Votes, mSolSnapshotDto } from '../dto/snapshots.dto';
|
|
12
|
+
import { error } from 'logger';
|
|
13
13
|
|
|
14
14
|
class Paths {
|
|
15
15
|
validatorsPath: string;
|
|
@@ -38,25 +38,21 @@ export class FileDataProvider implements IDataProvider {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
return validatorsData.validators;
|
|
41
|
-
} catch (
|
|
42
|
-
|
|
43
|
-
throw
|
|
41
|
+
} catch (err) {
|
|
42
|
+
error(FETCHING_ERROR_MESSAGES.generalValidatorsFetchFailed, err);
|
|
43
|
+
throw err;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
|
|
47
46
|
async fetchTvl (withSnapshot: boolean): Promise<TvlStats> {
|
|
48
47
|
const tvlFile = await readFile(this.paths.marinadeTvlPath, 'utf8');
|
|
49
48
|
const tvlData: TvlStats = JSON.parse(tvlFile);
|
|
50
|
-
|
|
51
49
|
return tvlData;
|
|
52
50
|
}
|
|
53
|
-
|
|
54
51
|
async fetchBlacklist (): Promise<Set<string>> {
|
|
55
52
|
const blacklistFile = await readFile(this.paths.blacklistPath, 'utf8');
|
|
56
53
|
const blacklistSet = new Set(blacklistFile.split('\n').map((line) => line.split(',')[0]).filter((value): value is string => !!value));
|
|
57
54
|
return blacklistSet;
|
|
58
55
|
}
|
|
59
|
-
|
|
60
56
|
async fetchBonds (): Promise<Bonds> {
|
|
61
57
|
const bondsFile = await readFile(this.paths.bondsPath, 'utf8');
|
|
62
58
|
const bondsData: BondsDto = JSON.parse(bondsFile);
|
|
@@ -68,7 +64,6 @@ export class FileDataProvider implements IDataProvider {
|
|
|
68
64
|
|
|
69
65
|
return bonds;
|
|
70
66
|
}
|
|
71
|
-
|
|
72
67
|
async fetchVeMndeVotes (): Promise<Record<string, number>> {
|
|
73
68
|
const vemndeVotesFile = await readFile(this.paths.vemndeVotesPath, 'utf8');
|
|
74
69
|
const vemndeVotesData = JSON.parse(vemndeVotesFile) as mSolSnapshotDto;
|
|
@@ -82,7 +77,6 @@ export class FileDataProvider implements IDataProvider {
|
|
|
82
77
|
|
|
83
78
|
return result;
|
|
84
79
|
}
|
|
85
|
-
|
|
86
80
|
async fetchMSolVotes (): Promise<Record<string, number>> {
|
|
87
81
|
const msolVotesFile = await readFile(this.paths.msolVotesPath, 'utf8');
|
|
88
82
|
const msolVotesData = JSON.parse(msolVotesFile) as mSolSnapshotDto;
|
|
@@ -96,7 +90,6 @@ export class FileDataProvider implements IDataProvider {
|
|
|
96
90
|
|
|
97
91
|
return result;
|
|
98
92
|
}
|
|
99
|
-
|
|
100
93
|
async fetchRewards (epochs: number): Promise<Rewards> {
|
|
101
94
|
const rewardsFile = await readFile(this.paths.rewardsPath, 'utf8');
|
|
102
95
|
const rewardsData = JSON.parse(rewardsFile) as { rewards_mev: [number, number][], rewards_inflation_est: [number, number][] };
|
|
@@ -108,7 +101,6 @@ export class FileDataProvider implements IDataProvider {
|
|
|
108
101
|
|
|
109
102
|
return { inflation, mev };
|
|
110
103
|
}
|
|
111
|
-
|
|
112
104
|
async fetchValidatorsJitoMEV (): Promise<Record<string, JitoValidatorDto>> {
|
|
113
105
|
try {
|
|
114
106
|
const jitoMevFile = await readFile(this.paths.jitoMevPath, 'utf8');
|
|
@@ -117,9 +109,9 @@ export class FileDataProvider implements IDataProvider {
|
|
|
117
109
|
return Object.fromEntries(
|
|
118
110
|
jitoMevData.validators.map((validator) => [validator.vote_account, validator])
|
|
119
111
|
);
|
|
120
|
-
} catch (
|
|
121
|
-
|
|
122
|
-
throw
|
|
112
|
+
} catch (err) {
|
|
113
|
+
error(FETCHING_ERROR_MESSAGES.generalJitoMEVFetchFailed, err);
|
|
114
|
+
throw err;
|
|
123
115
|
}
|
|
124
116
|
}
|
|
125
117
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
|
-
/* eslint-disable no-console */
|
|
3
2
|
import * as fs from 'fs';
|
|
4
3
|
import { Score } from '../dto/scoring.dto';
|
|
4
|
+
import { error } from 'logger';
|
|
5
5
|
|
|
6
|
-
export function writeScoresToCsv
|
|
6
|
+
export function writeScoresToCsv(filePath: string, data: Score[]): void {
|
|
7
7
|
if (data.length === 0) {
|
|
8
8
|
console.error('Data array is empty, no CSV will be written.');
|
|
9
9
|
return;
|
|
@@ -24,7 +24,7 @@ export function writeScoresToCsv (filePath: string, data: Score[]): void {
|
|
|
24
24
|
|
|
25
25
|
fs.writeFile(filePath, csvString, (err) => {
|
|
26
26
|
if (err) {
|
|
27
|
-
|
|
27
|
+
error('Error writing CSV file:', err);
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { BlockProductionResult, ClusterInfo } from '../dto/cluster.dto';
|
|
2
|
-
import { ValidatorDto } from '../dto/validators.dto';
|
|
3
|
-
import { mean, stdDev } from '../utils/maths';
|
|
4
|
-
import Decimal from 'decimal.js';
|
|
5
|
-
|
|
6
|
-
export class ClusterService {
|
|
7
|
-
computeBlockProduction (validators: ValidatorDto[], startEpoch: number, endEpoch: number): BlockProductionResult {
|
|
8
|
-
const blockProductions: number[] = [];
|
|
9
|
-
|
|
10
|
-
for (const validator of validators) {
|
|
11
|
-
let leaderSlots = 0;
|
|
12
|
-
let blocksProduced = 0;
|
|
13
|
-
|
|
14
|
-
for (let epoch = startEpoch; epoch <= endEpoch; epoch++) {
|
|
15
|
-
if (!validator.epochStats) {
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
const epochStat = validator.epochStats[epoch];
|
|
19
|
-
if (epochStat) {
|
|
20
|
-
leaderSlots += epochStat.leader_slots;
|
|
21
|
-
blocksProduced += epochStat.blocks_produced;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (leaderSlots) {
|
|
26
|
-
blockProductions.push(blocksProduced / leaderSlots);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
mean: mean(blockProductions),
|
|
32
|
-
stdDev: stdDev(blockProductions),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
computeTargetCreditsByEpoch (validators: ValidatorDto[]): Map<number, number> {
|
|
37
|
-
const sumOfWeightedCreditsPerEpoch = new Map<number, Decimal>();
|
|
38
|
-
const sumOfWeightsPerEpoch = new Map<number, Decimal>();
|
|
39
|
-
|
|
40
|
-
validators.forEach(validator => {
|
|
41
|
-
validator.epoch_stats.forEach(epochStat => {
|
|
42
|
-
if (epochStat.epoch_end_at) {
|
|
43
|
-
const activatedStakeDecimal = new Decimal(epochStat.activated_stake);
|
|
44
|
-
const stakeInLamports = activatedStakeDecimal.dividedBy(1e9);
|
|
45
|
-
|
|
46
|
-
const prevSumOfWeightedCredits = sumOfWeightedCreditsPerEpoch.get(epochStat.epoch) || new Decimal(0);
|
|
47
|
-
sumOfWeightedCreditsPerEpoch.set(epochStat.epoch, prevSumOfWeightedCredits.plus(new Decimal(epochStat.credits).times(stakeInLamports)));
|
|
48
|
-
|
|
49
|
-
const prevSumOfWeightsPerEpoch = sumOfWeightsPerEpoch.get(epochStat.epoch) || new Decimal(0);
|
|
50
|
-
sumOfWeightsPerEpoch.set(epochStat.epoch, prevSumOfWeightsPerEpoch.plus(stakeInLamports));
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const result = new Map<number, number>();
|
|
56
|
-
sumOfWeightsPerEpoch.forEach((sumOfWeights, epoch) => {
|
|
57
|
-
const weightedCredits = sumOfWeightedCreditsPerEpoch.get(epoch) || new Decimal(0);
|
|
58
|
-
result.set(epoch, weightedCredits.dividedBy(sumOfWeights).toDecimalPlaces(0, Decimal.ROUND_HALF_UP).toNumber());
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
computeConcentrations (validators: ValidatorDto[], epoch: number): { city: Map<string, number>, country: Map<string, number>, aso: Map<string, number> } {
|
|
65
|
-
let total = 0;
|
|
66
|
-
const city = new Map<string, number>();
|
|
67
|
-
const country = new Map<string, number>();
|
|
68
|
-
const aso = new Map<string, number>();
|
|
69
|
-
|
|
70
|
-
for (const validator of validators) {
|
|
71
|
-
const lastEpochStats = validator.epoch_stats[0];
|
|
72
|
-
if (lastEpochStats?.epoch === epoch) {
|
|
73
|
-
const cityKey = validator.dc_full_city ?? '???';
|
|
74
|
-
const countryKey = validator.dc_country ?? '???';
|
|
75
|
-
const asoKey = validator.dc_aso ?? '???';
|
|
76
|
-
|
|
77
|
-
const stake = Number(validator.activated_stake) / 1e9;
|
|
78
|
-
|
|
79
|
-
city.set(cityKey, (city.get(cityKey) ?? 0) + stake);
|
|
80
|
-
country.set(countryKey, (country.get(countryKey) ?? 0) + stake);
|
|
81
|
-
aso.set(asoKey, (aso.get(asoKey) ?? 0) + stake);
|
|
82
|
-
|
|
83
|
-
total += stake;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (total > 0) {
|
|
88
|
-
for (const map of [city, country, aso]) {
|
|
89
|
-
for (const [key, value] of map) {
|
|
90
|
-
map.set(key, value / total);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return { city, country, aso };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
computeClusterInfo (validators: ValidatorDto[], basicEligibilityEpochs: number, lastEpoch: number): ClusterInfo {
|
|
99
|
-
const { mean: meanBlockProduction, stdDev: stdDevBlockProduction } = this.computeBlockProduction(validators, lastEpoch - basicEligibilityEpochs, lastEpoch - 1);
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
targetCreditsByEpoch: this.computeTargetCreditsByEpoch(validators),
|
|
103
|
-
...this.computeConcentrations(validators, lastEpoch),
|
|
104
|
-
meanBlockProductionOverBasicEligibilityPeriod: meanBlockProduction,
|
|
105
|
-
stdDevBlockProductionOverBasicEligibilityPeriod: stdDevBlockProduction,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import * as dotenv from 'dotenv';
|
|
2
|
-
import { Injectable } from '@nestjs/common';
|
|
3
|
-
import { Logger } from '../../../../src/logger';
|
|
4
|
-
import { ScoringConfig } from '../dto/scoring.dto';
|
|
5
|
-
import { readFileSync } from 'fs';
|
|
6
|
-
import * as defaultScoringConfig from '../constants/marinade.json';
|
|
7
|
-
|
|
8
|
-
dotenv.config();
|
|
9
|
-
|
|
10
|
-
@Injectable()
|
|
11
|
-
export class ConfigService {
|
|
12
|
-
private readonly logger = new Logger();
|
|
13
|
-
readonly scoringConfig: ScoringConfig;
|
|
14
|
-
private getEnvVar (key: string): string {
|
|
15
|
-
const val = process.env[key];
|
|
16
|
-
if (!val) {
|
|
17
|
-
this.logger.error(`Missing environment variable: ${key}`);
|
|
18
|
-
throw new Error(`Missing environment variable: ${key}`);
|
|
19
|
-
}
|
|
20
|
-
return val;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
getScoringConfig (): ScoringConfig {
|
|
24
|
-
const scoringConfigPath = process.env['SCORING_CONFIG_PATH'];
|
|
25
|
-
|
|
26
|
-
if (!scoringConfigPath) {
|
|
27
|
-
return {
|
|
28
|
-
DS_PUBKEY: defaultScoringConfig.DS_PUBKEY,
|
|
29
|
-
REWARDS_PAST_EPOCHS: defaultScoringConfig.REWARDS_PAST_EPOCHS,
|
|
30
|
-
CAP_FROM_BOND: defaultScoringConfig.CAP_FROM_BOND,
|
|
31
|
-
formulaStakeBlockSize: defaultScoringConfig.formulaStakeBlockSize,
|
|
32
|
-
formulaStakeBlocksFromScore: defaultScoringConfig.formulaStakeBlocksFromScore,
|
|
33
|
-
formulaVoteCredits: defaultScoringConfig.formulaVoteCredits,
|
|
34
|
-
formulaBlockProduction: defaultScoringConfig.formulaBlockProduction,
|
|
35
|
-
formulaInflationCommission: defaultScoringConfig.formulaInflationCommission,
|
|
36
|
-
formulaMEVCommission: defaultScoringConfig.formulaMEVCommission,
|
|
37
|
-
formulaStakeConcentrationCountry: defaultScoringConfig.formulaStakeConcentrationCountry,
|
|
38
|
-
formulaStakeConcentrationCity: defaultScoringConfig.formulaStakeConcentrationCity,
|
|
39
|
-
formulaStakeConcentrationASO: defaultScoringConfig.formulaStakeConcentrationASO,
|
|
40
|
-
formulaStakeConcentrationNode: defaultScoringConfig.formulaStakeConcentrationNode,
|
|
41
|
-
weightTargetSumOfRewards: defaultScoringConfig.weightTargetSumOfRewards,
|
|
42
|
-
weightMevHeuristic: defaultScoringConfig.weightMevHeuristic,
|
|
43
|
-
weightVoteCredits: defaultScoringConfig.weightVoteCredits,
|
|
44
|
-
weightBlockProduction: defaultScoringConfig.weightBlockProduction,
|
|
45
|
-
weightInflationCommission: defaultScoringConfig.weightInflationCommission,
|
|
46
|
-
weightMEVCommission: defaultScoringConfig.weightMEVCommission,
|
|
47
|
-
weightStakeConcentrationCountry: defaultScoringConfig.weightStakeConcentrationCountry,
|
|
48
|
-
weightStakeConcentrationCity: defaultScoringConfig.weightStakeConcentrationCity,
|
|
49
|
-
weightStakeConcentrationASO: defaultScoringConfig.weightStakeConcentrationASO,
|
|
50
|
-
weightStakeConcentrationNode: defaultScoringConfig.weightStakeConcentrationNode,
|
|
51
|
-
basicEligibilityEpochs: defaultScoringConfig.basicEligibilityEpochs,
|
|
52
|
-
bonusEligibilityExtraEpochs: defaultScoringConfig.bonusEligibilityExtraEpochs,
|
|
53
|
-
maxCommission: defaultScoringConfig.maxCommission,
|
|
54
|
-
voteCreditsWarning: defaultScoringConfig.voteCreditsWarning,
|
|
55
|
-
voteCreditsLow: defaultScoringConfig.voteCreditsLow,
|
|
56
|
-
minExternalStake: defaultScoringConfig.minExternalStake,
|
|
57
|
-
minScore: defaultScoringConfig.minScore,
|
|
58
|
-
maxStakeShare: defaultScoringConfig.maxStakeShare,
|
|
59
|
-
maxWarnings: defaultScoringConfig.maxWarnings,
|
|
60
|
-
mSolControl: defaultScoringConfig.mSolControl,
|
|
61
|
-
veMndeControl: defaultScoringConfig.veMndeControl,
|
|
62
|
-
stakeBlocksFromBonus: defaultScoringConfig.stakeBlocksFromBonus,
|
|
63
|
-
concentrationParams: defaultScoringConfig.concentrationParams,
|
|
64
|
-
};
|
|
65
|
-
} else {
|
|
66
|
-
const scoringConfigData = readFileSync(scoringConfigPath, 'utf8');
|
|
67
|
-
return JSON.parse(scoringConfigData) as ScoringConfig;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
getDataProviderMode (): string {
|
|
72
|
-
return this.getEnvVar('DATA_PROVIDER_MODE');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
getMarinadeTvlURL (): string {
|
|
76
|
-
return this.getEnvVar('MARINADE_TVL_URL');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getValidatorsURL (): string {
|
|
80
|
-
return this.getEnvVar('VALIDATORS_URL');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
getBlacklistURL (): string {
|
|
84
|
-
return this.getEnvVar('BLACKLIST_URL');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
getBondsURL (): string {
|
|
88
|
-
return this.getEnvVar('BONDS_URL');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
getVemndeVotesURL (): string {
|
|
92
|
-
return this.getEnvVar('VEMNDE_VOTES_URL');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
getMsolVotesURL (): string {
|
|
96
|
-
return this.getEnvVar('MSOL_VOTES_URL');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
getRewardsURL (): string {
|
|
100
|
-
return this.getEnvVar('REWARDS_URL');
|
|
101
|
-
}
|
|
102
|
-
getJitoMevURL (): string {
|
|
103
|
-
return this.getEnvVar('JITO_MEV_URL');
|
|
104
|
-
}
|
|
105
|
-
getValidatorsPath (): string {
|
|
106
|
-
return this.getEnvVar('VALIDATORS_PATH');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
getBlacklistPath (): string {
|
|
110
|
-
return this.getEnvVar('BLACKLIST_PATH');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
getVemndeVotesPath (): string {
|
|
114
|
-
return this.getEnvVar('VEMNDE_VOTES_PATH');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
getMsolVotesPath (): string {
|
|
118
|
-
return this.getEnvVar('MSOL_VOTES_PATH');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
getRewardsPath (): string {
|
|
122
|
-
return this.getEnvVar('REWARDS_PATH');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
getJitoMevPath (): string {
|
|
126
|
-
return this.getEnvVar('JITO_MEV_PATH');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
getBondsPath (): string {
|
|
130
|
-
return this.getEnvVar('JITO_MEV_PATH');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
getMarinadeTvlPath (): string {
|
|
134
|
-
return this.getEnvVar('MARINADE_TVL_PATH');
|
|
135
|
-
}
|
|
136
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
2
|
-
import { EligibilityService } from './eligibility.service';
|
|
3
|
-
import { ValidatorsModule } from '../validators/validators.module';
|
|
4
|
-
import { ConfigModule } from '../config/config.module';
|
|
5
|
-
|
|
6
|
-
@Module({
|
|
7
|
-
imports: [ValidatorsModule, ConfigModule],
|
|
8
|
-
providers: [EligibilityService],
|
|
9
|
-
exports: [EligibilityService],
|
|
10
|
-
})
|
|
11
|
-
export class EligibilityModule {}
|