@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.
Files changed (37) hide show
  1. package/computing/cluster.ts +102 -0
  2. package/computing/eligibility.ts +163 -0
  3. package/computing/score.ts +164 -0
  4. package/computing/stake.ts +246 -0
  5. package/computing/validators.ts +133 -0
  6. package/{src/dto → dto}/bonds.dto.ts +0 -2
  7. package/{src/dto → dto}/cluster.dto.ts +0 -1
  8. package/{src/dto → dto}/eligibility.dto.ts +0 -5
  9. package/{src/dto → dto}/jito.dto.ts +1 -2
  10. package/{src/dto → dto}/marinade.dto.ts +1 -1
  11. package/{src/dto → dto}/rewards.dto.ts +1 -3
  12. package/{src/dto → dto}/scoring.dto.ts +1 -7
  13. package/{src/dto → dto}/snapshots.dto.ts +0 -3
  14. package/{src/dto → dto}/stakes.dto.ts +0 -4
  15. package/{src/dto → dto}/validators.dto.ts +1 -37
  16. package/package.json +17 -13
  17. package/{src/providers → providers}/api-data.provider.ts +58 -58
  18. package/{src/providers → providers}/file-data.provider.ts +7 -15
  19. package/{src/utils → utils}/csv.ts +3 -3
  20. package/src/cluster/cluster.module.ts +0 -8
  21. package/src/cluster/cluster.service.ts +0 -108
  22. package/src/config/config.module.ts +0 -8
  23. package/src/config/config.service.ts +0 -136
  24. package/src/eligibility/eligibility.module.ts +0 -11
  25. package/src/eligibility/eligibility.service.ts +0 -183
  26. package/src/scoring/scoring.module.ts +0 -11
  27. package/src/scoring/scoring.service.ts +0 -184
  28. package/src/stake/stake.module.ts +0 -10
  29. package/src/stake/stake.service.ts +0 -242
  30. package/src/validators/validators.module.ts +0 -48
  31. package/src/validators/validators.service.ts +0 -177
  32. /package/{src/constants → constants}/marinade.json +0 -0
  33. /package/{src/errors → errors}/fetching.ts +0 -0
  34. /package/{src/interfaces → interfaces}/data-provider.interface.ts +0 -0
  35. /package/{src/utils → utils}/maths.ts +0 -0
  36. /package/{src/utils → utils}/solana.ts +0 -0
  37. /package/{src/utils → utils}/zip.ts +0 -0
@@ -0,0 +1,133 @@
1
+ import { AggregatedValidator, AggregatedValidators, ValidatorDto } from '../dto/validators.dto';
2
+ import { JitoValidatorDto } from '../dto/jito.dto';
3
+ import { mean, sum } from '../utils/maths';
4
+ import { ClusterInfo } from '../dto/cluster.dto';
5
+
6
+ export function aggregateValidatorData (
7
+ validator: ValidatorDto,
8
+ mevRecord: JitoValidatorDto,
9
+ blacklist: Set<string>,
10
+ firstEpoch: number,
11
+ lastEpoch: number
12
+ ): AggregatedValidator {
13
+ const currentMarinadeStake = Number(validator.epochStats[lastEpoch]?.marinade_native_stake || 0) / 1e9 + Number(validator.epochStats[lastEpoch]?.marinade_stake || 0) / 1e9;
14
+ const voteAccount = validator.vote_account;
15
+ const name = validator.info_name || 'Unknown';
16
+ const epochs: number[] = [];
17
+ const commission: number[] = [];
18
+ const stake: number[] = [];
19
+ const externalStake: number[] = [];
20
+ const credits: number[] = [];
21
+ const blocksProduced: number[] = [];
22
+ const leaderSlots: number[] = [];
23
+ const dataAvailable: boolean[] = [];
24
+
25
+ const currentStake = Number(validator.epochStats[lastEpoch]?.activated_stake || 0) / 1e9;
26
+
27
+ const blacklisted = blacklist.has(voteAccount);
28
+
29
+ for (let epoch = lastEpoch; epoch >= firstEpoch; epoch--) {
30
+ const epochStat = validator.epochStats?.[epoch];
31
+
32
+ epochs.push(epoch);
33
+ commission.push(epochStat?.commission_max_observed || epochStat?.commission_advertised || 0);
34
+ stake.push(Number(epochStat?.activated_stake || 0) / 1e9);
35
+ externalStake.push(Number(epochStat?.activated_stake || 0) / 1e9 - Number(epochStat?.marinade_stake || 0) / 1e9 - Number(epochStat?.marinade_native_stake || 0) / 1e9);
36
+ credits.push(epochStat?.credits || 0);
37
+ blocksProduced.push(epochStat?.blocks_produced || 0);
38
+ leaderSlots.push(epochStat?.leader_slots || 0);
39
+ dataAvailable.push(!!epochStat);
40
+ }
41
+
42
+ const mevCommission = mevRecord?.running_jito ? mevRecord.mev_commission_bps / 100 : 100;
43
+ return {
44
+ voteAccount,
45
+ name,
46
+ epochs,
47
+ currentMarinadeStake,
48
+ currentStake,
49
+ commission,
50
+ stake,
51
+ externalStake,
52
+ credits,
53
+ blocksProduced,
54
+ leaderSlots,
55
+ dataAvailable,
56
+ mevCommission,
57
+ country: validator.dc_country || 'Unknown',
58
+ city: validator.dc_full_city || 'Unknown',
59
+ aso: validator.dc_aso || 'Unknown',
60
+ blacklisted,
61
+ version: validator.version
62
+ };
63
+ }
64
+ export async function aggregateValidatorsData (
65
+ validators: ValidatorDto[],
66
+ basicEligibilityEpochs: number,
67
+ bonusEligibilityExtraEpochs: number
68
+ ): Promise<AggregatedValidators> {
69
+ const lastEpoch = this.getMaxEpoch(validators);
70
+ const firstEpoch = lastEpoch - basicEligibilityEpochs - bonusEligibilityExtraEpochs;
71
+
72
+ const jitoMevRecords: Record<string, JitoValidatorDto> = await this.dataProvider.fetchValidatorsJitoMEV(true);
73
+ const blacklist: Set<string> = await this.dataProvider.fetchBlacklist(true);
74
+
75
+ const result: { [voteAccount: string]: AggregatedValidator } = {};
76
+
77
+ for (const validator of validators) {
78
+ const mevRecord = jitoMevRecords[validator.vote_account] ?? {
79
+ vote_account: validator.vote_account,
80
+ mev_commission_bps: 100,
81
+ running_jito: false,
82
+ active_stake: Number(validator.activated_stake)
83
+ } as JitoValidatorDto;
84
+
85
+ if (mevRecord) {
86
+ const aggregatedData = this.aggregateValidatorData(validator, mevRecord, blacklist, firstEpoch, lastEpoch);
87
+ result[validator.vote_account] = aggregatedData;
88
+ }
89
+ }
90
+
91
+ return result;
92
+ }
93
+ export function getMaxEpoch (validators: ValidatorDto[]): number {
94
+ return validators.reduce((maxEpoch, validator) => {
95
+ const validatorMaxEpoch = validator.epoch_stats.reduce((max, { epoch }) => {
96
+ return Math.max(epoch, max);
97
+ }, 0);
98
+ return Math.max(maxEpoch, validatorMaxEpoch);
99
+ }, 0);
100
+ }
101
+ export function selectExternalStakeMin (validator: AggregatedValidator, fullEpochs: number): number {
102
+ if (validator.externalStake.length === 0) {
103
+ throw new Error('Validator has no external stake data.');
104
+ }
105
+ return Math.min(...validator.externalStake.slice(0, fullEpochs + 1));
106
+ }
107
+ export function selectCreditsPctMean (validator: AggregatedValidator, clusterInfo: ClusterInfo, fullEpochs: number): number {
108
+ return mean(validator.epochs.slice(1, fullEpochs + 1).map((epoch, fullEpochIndex) =>
109
+ (validator.credits[fullEpochIndex + 1] ?? 0) / (clusterInfo.targetCreditsByEpoch.get(epoch) ?? 1)));
110
+ }
111
+ export function selectBlockProductionMean (validator: AggregatedValidator, fullEpochs: number): number {
112
+ const leaderSlots = sum(validator.leaderSlots.slice(1, fullEpochs + 1));
113
+ return leaderSlots === 0 ? 1 : sum(validator.blocksProduced.slice(1, fullEpochs + 1)) / leaderSlots;
114
+ }
115
+ export function selectCommissionInflationMax (validator: AggregatedValidator, epochs: number): number {
116
+ return Math.max(...validator.commission.slice(0, epochs));
117
+ }
118
+ export function selectCommissionMEV (validator: AggregatedValidator): number {
119
+ return validator.mevCommission;
120
+ }
121
+ export function selectCountryStakeConcentration (validator: AggregatedValidator, clusterInfo: ClusterInfo): number {
122
+ return clusterInfo.country.get(validator.country ?? '???') ?? 0;
123
+ }
124
+ export function selectCityStakeConcentration (validator: AggregatedValidator, clusterInfo: ClusterInfo): number {
125
+ return clusterInfo.city.get(validator.city ?? '???') ?? 0;
126
+ }
127
+ export function selectASOStakeConcentration (validator: AggregatedValidator, clusterInfo: ClusterInfo): number {
128
+ return clusterInfo.aso.get(validator.aso ?? '???') ?? 0;
129
+ }
130
+ export function selectNodeStake (validator: AggregatedValidator): number {
131
+ return validator.stake[0] ?? 0;
132
+ }
133
+
@@ -6,9 +6,7 @@ export type BondDto = {
6
6
  updated_at: string;
7
7
  epoch: number;
8
8
  }
9
-
10
9
  export type BondsDto = {
11
10
  bonds: BondDto[];
12
11
  }
13
-
14
12
  export type Bonds = Record<string, number>;
@@ -6,7 +6,6 @@ export type ClusterInfo = {
6
6
  meanBlockProductionOverBasicEligibilityPeriod: number,
7
7
  stdDevBlockProductionOverBasicEligibilityPeriod: number,
8
8
  }
9
-
10
9
  export type BlockProductionResult = {
11
10
  mean: number;
12
11
  stdDev: number;
@@ -9,7 +9,6 @@ export type EligibilityConfig = {
9
9
  maxStakeShare: number;
10
10
  maxWarnings: number;
11
11
  }
12
-
13
12
  export type ValidatorEligibility = {
14
13
  basicEligibility: boolean;
15
14
  bonusEligibility: boolean;
@@ -17,17 +16,14 @@ export type ValidatorEligibility = {
17
16
  capFromBond: number;
18
17
  capFromExternalStake: number;
19
18
  }
20
-
21
19
  export type EvaluationResult = {
22
20
  criticals: number;
23
21
  warnings: number;
24
22
  }
25
-
26
23
  export type Issue = {
27
24
  type: IssueType;
28
25
  message: string;
29
26
  }
30
-
31
27
  export const enum IssueType {
32
28
  NO_DATA = 'NO_DATA',
33
29
  NO_BOND = 'NO_BOND',
@@ -39,5 +35,4 @@ export const enum IssueType {
39
35
  VOTE_CREDITS_LOW = 'VOTE_CREDITS_LOW',
40
36
  VOTE_CREDITS_WARNING = 'VOTE_CREDITS_WARNING',
41
37
  }
42
-
43
38
  export type ValidatorsEligibilities = Record<string, ValidatorEligibility>
@@ -4,7 +4,6 @@ export class JitoValidatorDto {
4
4
  running_jito: boolean;
5
5
  active_stake: number;
6
6
  }
7
-
8
7
  export class JitoValidatorsResponseDto {
9
8
  validators: JitoValidatorDto[];
10
- }
9
+ }
@@ -15,4 +15,4 @@ export type TvlStats = {
15
15
  total_virtual_staked_usd: number;
16
16
  marinade_native_stake_sol: number;
17
17
  marinade_native_stake_usd: number;
18
- }
18
+ }
@@ -7,7 +7,6 @@ export class RewardPairDto {
7
7
  this.amount = pair[1];
8
8
  }
9
9
  }
10
-
11
10
  export class RewardsResponseDto {
12
11
  rewards_mev: RewardPairDto[];
13
12
  rewards_inflation_est: RewardPairDto[];
@@ -17,5 +16,4 @@ export class RewardsResponseDto {
17
16
  this.rewards_inflation_est = data.rewards_inflation_est.map(pair => new RewardPairDto(pair));
18
17
  }
19
18
  }
20
-
21
- export type Rewards = { inflation: number, mev: number }
19
+ export type Rewards = { inflation: number, mev: number }
@@ -9,7 +9,6 @@ export type ScoreDto = {
9
9
  scoreErrors: boolean[]
10
10
  tooltips: string[]
11
11
  }
12
-
13
12
  export type Score = {
14
13
  vote_account: string,
15
14
  score: string,
@@ -37,12 +36,10 @@ export type Score = {
37
36
  target_stake_algo: string,
38
37
  target_stake: string,
39
38
  }
40
-
41
39
  export type ScoreConfig = {
42
40
  epochs: number,
43
41
  concentrationParams: number[]
44
42
  }
45
-
46
43
  export type ScoringConfig = {
47
44
  DS_PUBKEY: string;
48
45
  REWARDS_PAST_EPOCHS: number;
@@ -94,16 +91,13 @@ export type Variables = {
94
91
  aso_stake_concentration_last: number;
95
92
  node_stake_last: number;
96
93
  };
97
-
98
94
  export type Scores = Record<string, ScoreDto>
99
95
  export type ScoreTooltipBuilder = (validator: AggregatedValidator, clusterInfo: ClusterInfo) => string
100
-
101
96
  export type UnstakeHint = {
102
97
  vote_account: string;
103
98
  marinade_stake: string;
104
99
  hints: string[];
105
100
  };
106
-
107
101
  export type UnstakeHints = {
108
102
  unstake_hints: UnstakeHint[];
109
- };
103
+ };
@@ -3,17 +3,14 @@ export class RecordDto {
3
3
  tokenOwner: string;
4
4
  validatorVoteAccount: string;
5
5
  }
6
-
7
6
  export class veMNDESnapshotDto {
8
7
  voteRecordsCreatedAt: string;
9
8
  records: RecordDto[];
10
9
  }
11
-
12
10
  export class mSolSnapshotDto {
13
11
  mSolSnapshotCreatedAt: string;
14
12
  voteRecordsCreatedAt: string;
15
13
  records: RecordDto[];
16
14
  }
17
-
18
15
  export type Votes = Record<string, number>
19
16
 
@@ -8,7 +8,6 @@ export type StakesConfig = {
8
8
  formulaStakeBlockSize: string
9
9
  formulaStakeBlocksFromScore: string
10
10
  }
11
-
12
11
  export type Stake = {
13
12
  algoStake: number
14
13
  mSolStake: number
@@ -19,7 +18,6 @@ export type Stake = {
19
18
  veMndeStakeFromOverflow: number
20
19
  totalStakeFromOverlfow: number
21
20
  }
22
-
23
21
  export type ProcessedVotesAndTVLs = {
24
22
  updatedMSolVotes: Votes;
25
23
  updatedVeMndeVotes: Votes;
@@ -27,9 +25,7 @@ export type ProcessedVotesAndTVLs = {
27
25
  veMndeTvl: number;
28
26
  totalTvl: number;
29
27
  };
30
-
31
28
  export type StakingVariables = {
32
29
  tvl: number;
33
30
  }
34
-
35
31
  export type Stakes = Record<string, Stake>
@@ -1,67 +1,36 @@
1
-
2
1
  export class WarningDto {
3
2
  warning: string;
4
3
  }
5
-
6
4
  export class EpochStatDto {
7
-
8
5
  epoch: number;
9
-
10
6
  epoch_start_at: string;
11
-
12
7
  epoch_end_at?: string;
13
-
14
8
  commission_max_observed?: number | null;
15
-
16
9
  commission_min_observed?: number | null;
17
-
18
10
  commission_advertised: number;
19
-
20
11
  commission_effective?: number | null;
21
-
22
12
  version: string;
23
-
24
13
  activated_stake: string;
25
-
26
14
  marinade_stake: string;
27
-
28
15
  foundation_stake: string;
29
-
30
16
  marinade_native_stake: string;
31
-
32
17
  self_stake: string;
33
-
34
18
  superminority: boolean;
35
-
36
19
  stake_to_become_superminority: string;
37
-
38
20
  credits: number;
39
-
40
21
  leader_slots: number;
41
-
42
22
  blocks_produced: number;
43
-
44
23
  skip_rate: number;
45
-
46
24
  uptime_pct?: number | null;
47
-
48
25
  uptime?: number | null;
49
-
50
26
  downtime?: number | null;
51
-
52
27
  apr?: number | null;
53
-
54
28
  apy?: number | null;
55
-
56
29
  score: number;
57
-
58
30
  rank_score: number;
59
-
60
31
  rank_activated_stake: number;
61
-
62
32
  rank_apy: number;
63
33
  }
64
-
65
34
  export class ValidatorDto {
66
35
  identity: string;
67
36
  vote_account: string;
@@ -108,19 +77,16 @@ export class ValidatorDto {
108
77
  avg_uptime_pct: number;
109
78
  avg_apy: number;
110
79
  }
111
-
112
80
  export class ValidatorAggregatedDto {
113
81
  epoch: number;
114
82
  epoch_start_date: string;
115
83
  avg_marinade_score?: number | null;
116
84
  avg_apy?: number | null;
117
85
  }
118
-
119
86
  export class ValidatorsResponseDto {
120
87
  validators: ValidatorDto[];
121
88
  validators_aggregated: ValidatorAggregatedDto[];
122
89
  }
123
-
124
90
  export type AggregatedValidator = {
125
91
  voteAccount: string;
126
92
  name: string;
@@ -141,6 +107,4 @@ export type AggregatedValidator = {
141
107
  blacklisted: boolean;
142
108
  version: string;
143
109
  };
144
-
145
- export type AggregatedValidators = Record<string, AggregatedValidator>
146
-
110
+ export type AggregatedValidators = Record<string, AggregatedValidator>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marinade.finance/scoring",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Delegation Strategy Scoring",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,16 +9,21 @@
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
12
-
13
12
  "files": [
14
- "src",
13
+ "computing",
14
+ "constants",
15
+ "dto",
16
+ "errors",
17
+ "interfaces",
18
+ "providers",
19
+ "utils",
15
20
  "generated",
16
21
  "README.md"
17
22
  ],
18
23
  "main": "index.js",
19
24
  "scripts": {
20
- "build": "nest build",
21
- "test": "echo \"Error: no test specified\" && exit 1"
25
+ "build": "tsc",
26
+ "test": "jest"
22
27
  },
23
28
  "keywords": [
24
29
  "solana",
@@ -29,16 +34,15 @@
29
34
  "author": "Marinade Finance",
30
35
  "license": "ISC",
31
36
  "dependencies": {
32
- "@nestjs/cli": "^10.3.2",
33
- "@nestjs/common": "^10.3.3",
34
- "axios": "^1.6.7",
35
- "class-transformer": "^0.5.1",
36
- "class-validator": "^0.14.1",
37
+ "axios": "^1.6.8",
37
38
  "decimal.js": "^10.4.3",
38
39
  "expr-eval": "^2.0.2",
39
- "nestjs": "^0.0.1"
40
+ "log4js": "^6.9.1"
40
41
  },
41
42
  "devDependencies": {
42
- "@types/node": "^20.11.19"
43
+ "@types/jest": "29.5.0",
44
+ "@types/node": "^20.12.7",
45
+ "jest": "29.5.0",
46
+ "ts-jest": "29.0.5"
43
47
  }
44
- }
48
+ }