diff --git a/backend/pkg/api/data_access/mobile.go b/backend/pkg/api/data_access/mobile.go index 6d585b2caa..50a1a66959 100644 --- a/backend/pkg/api/data_access/mobile.go +++ b/backend/pkg/api/data_access/mobile.go @@ -173,7 +173,8 @@ func (d *DataAccessService) GetValidatorDashboardMobileWidget(ctx context.Contex if err != nil { return nil, fmt.Errorf("error retrieving validator dashboard overview data: %w", err) } - data.NetworkEfficiency = efficiency.TotalEfficiency[enums.AllTime].Float64 + networkEfficiency := efficiency.TotalEfficiency[enums.AllTime].Float64 + data.NetworkEfficiency = &networkEfficiency // Validator status eg.Go(func() error { @@ -251,23 +252,31 @@ func (d *DataAccessService) GetValidatorDashboardMobileWidget(ctx context.Contex share := queryResult.EffectiveRPLStake.Div(rpNetworkStats.EffectiveRPLStaked) periodsPerYear := decimal.NewFromFloat(365 / (rpNetworkStats.ClaimIntervalHours / 24)) - data.RplApr = rpNetworkStats.NodeOperatorRewards. + rplApr := rpNetworkStats.NodeOperatorRewards. Mul(share). Div(queryResult.RPLStake). Mul(periodsPerYear).InexactFloat64() + data.RplApr = &rplApr } return nil }) - retrieveApr := func(timeFrame enums.TimePeriod, apr *float64) { - eg.Go(func() error { - incomeInfo, err := d.getElClAPR(ctx, wrappedDashboardId, -1, timeFrame) - if err != nil { - return err - } - *apr = incomeInfo.Apr.El + incomeInfo.Apr.Cl - return nil - }) + retrieveApr := func(timeFrame enums.TimePeriod) (*float64, error) { + incomeInfo, err := d.getElClAPR(ctx, wrappedDashboardId, -1, timeFrame) + if err != nil { + return nil, err + } + if incomeInfo.Apr.El == nil && incomeInfo.Apr.Cl == nil { + return nil, nil + } + var totalApr float64 + if incomeInfo.Apr.El != nil { + totalApr += *incomeInfo.Apr.El + } + if incomeInfo.Apr.Cl != nil { + totalApr += *incomeInfo.Apr.Cl + } + return &totalApr, nil } retrieveRewards := func(timeFrame enums.TimePeriod, rewards *t.ClElValue[decimal.Decimal]) { @@ -281,27 +290,42 @@ func (d *DataAccessService) GetValidatorDashboardMobileWidget(ctx context.Contex }) } - retrieveEfficiency := func(table string, efficiency *float64) { - eg.Go(func() error { - ds := goqu.Dialect("postgres"). - From(goqu.L(fmt.Sprintf(`%s AS r`, table))). - With("validators", goqu.L("(SELECT dashboard_id, validator_index FROM users_val_dashboards_validators WHERE dashboard_id = ?)", dashboardId)). - Select( - goqu.L("COALESCE(SUM(efficiency_dividend::Int256) / NULLIF(SUM(efficiency_divisor::Int256), 0), 0)").As("efficiency"), - ). - InnerJoin(goqu.L("validators v"), goqu.On(goqu.L("r.validator_index = v.validator_index"))). - Where(goqu.L("r.validator_index IN (SELECT validator_index FROM validators)")) + retrieveEfficiency := func(table string) (*float64, error) { + ds := goqu.Dialect("postgres"). + From(goqu.L(fmt.Sprintf(`%s AS r`, table))). + With("validators", goqu.L("(SELECT dashboard_id, validator_index FROM users_val_dashboards_validators WHERE dashboard_id = ?)", dashboardId)). + Select( + goqu.L("SUM(efficiency_dividend::decimal)").As("efficiency_dividend"), + goqu.L("SUM(efficiency_divisor::decimal)").As("efficiency_divisor"), + ). + InnerJoin(goqu.L("validators v"), goqu.On(goqu.L("r.validator_index = v.validator_index"))). + Where(goqu.L("r.validator_index IN (SELECT validator_index FROM validators)")) + + type dbResult struct { + EfficiencyDividend decimal.Decimal `db:"efficiency_dividend"` + EfficiencyDivisor decimal.Decimal `db:"efficiency_divisor"` + } + dbRes, err := runQuery[dbResult](ctx, d.clickhouseReader, ds) - *efficiency, err = runQuery[float64](ctx, d.clickhouseReader, ds) + var efficiency *float64 + if !dbRes.EfficiencyDivisor.IsZero() { + eff := dbRes.EfficiencyDividend.Div(dbRes.EfficiencyDivisor).InexactFloat64() + efficiency = &eff + } - return err - }) + return efficiency, err } retrieveRewards(enums.Last24h, &data.Last24hIncome) retrieveRewards(enums.Last7d, &data.Last7dIncome) - retrieveApr(enums.Last30d, &data.Last30dApr) - retrieveEfficiency("validator_dashboard_data_rolling_30d", &data.Last30dEfficiency) + eg.Go(func() error { + data.Last30dApr, err = retrieveApr(enums.Last30d) + return err + }) + eg.Go(func() error { + data.Last30dEfficiency, err = retrieveEfficiency("validator_dashboard_data_rolling_30d") + return err + }) err = eg.Wait() diff --git a/backend/pkg/api/data_access/vdb_helpers.go b/backend/pkg/api/data_access/vdb_helpers.go index 5cbe593b79..33e4290f95 100644 --- a/backend/pkg/api/data_access/vdb_helpers.go +++ b/backend/pkg/api/data_access/vdb_helpers.go @@ -117,7 +117,7 @@ func (d *DataAccessService) getTotalRewardsColumns() string { type IncomeInfo struct { Rewards t.ClElValue[decimal.Decimal] - Apr t.ClElValue[float64] + Apr t.ClElValue[*float64] } func (d *DataAccessService) getElClAPR(ctx context.Context, dashboardId t.VDBId, groupId int64, timeFrame enums.TimePeriod) (rewardsApr IncomeInfo, err error) { @@ -208,12 +208,17 @@ func (d *DataAccessService) getElClAPR(ctx context.Context, dashboardId t.VDBId, } // precondition: invested amount and rewards are in the same currency -func calcAPR(rewards, cumulativeDivisor decimal.Decimal, duration time.Duration) float64 { +func calcAPR(rewards, cumulativeDivisor decimal.Decimal, duration time.Duration) *float64 { + if cumulativeDivisor.IsZero() { + return nil + } + var apr float64 if rewards.IsZero() || cumulativeDivisor.IsZero() || duration.Nanoseconds() == 0 { - return 0 + return &apr } annualizationFactor := decimal.NewFromInt(utils.Year.Nanoseconds()).Div(decimal.NewFromInt(duration.Nanoseconds())) - return rewards.Div(cumulativeDivisor).Mul(annualizationFactor).InexactFloat64() + apr = rewards.Div(cumulativeDivisor).Mul(annualizationFactor).InexactFloat64() + return &apr } // converts a cl amount to the main currency diff --git a/backend/pkg/api/data_access/vdb_management.go b/backend/pkg/api/data_access/vdb_management.go index 1ec93fa7c1..088b49d580 100644 --- a/backend/pkg/api/data_access/vdb_management.go +++ b/backend/pkg/api/data_access/vdb_management.go @@ -377,49 +377,86 @@ func (d *DataAccessService) GetValidatorDashboardOverview(ctx context.Context, d return nil }) - retrieveRewardsAndEfficiency := func(timeFrame enums.TimePeriod, rewards *t.ClElValue[decimal.Decimal], apr *t.ClElValue[float64], efficiency *float64) { - // Rewards + APR - eg.Go(func() error { - incomeInfo, err := d.getElClAPR(ctx, dashboardId, -1, timeFrame) - if err != nil { - return err - } - *rewards = incomeInfo.Rewards - *apr = incomeInfo.Apr - return nil - }) + retrieveRewards := func(timeFrame enums.TimePeriod) (rewards t.ClElValue[decimal.Decimal], apr t.ClElValue[*float64], err error) { + incomeInfo, err := d.getElClAPR(ctx, dashboardId, -1, timeFrame) + rewards = incomeInfo.Rewards + apr = incomeInfo.Apr + return + } - // Efficiency - eg.Go(func() error { - table, err := timeFrame.Table() - if err != nil { - return err - } - ds := goqu.Dialect("postgres"). - From(goqu.L(fmt.Sprintf(`%s AS r`, table))). - With("validators", goqu.L("(SELECT dashboard_id, validator_index FROM users_val_dashboards_validators WHERE dashboard_id = ?)", dashboardId.Id)). - Select( - goqu.L("COALESCE(SUM(efficiency_dividend::Int256) / NULLIF(SUM(efficiency_divisor::Int256), 0), 0)").As("efficiency"), - ) - - if len(dashboardId.Validators) == 0 { - ds = ds. - InnerJoin(goqu.L("validators v"), goqu.On(goqu.L("r.validator_index = v.validator_index"))). - Where(goqu.L("r.validator_index IN (SELECT validator_index FROM validators)")) - } else { - ds = ds. - Where(goqu.L("r.validator_index IN ?", dashboardId.Validators)) - } + retrieveEfficiency := func(timeFrame enums.TimePeriod) (efficiency *float64, err error) { + table, err := timeFrame.Table() + if err != nil { + return + } + ds := goqu.Dialect("postgres"). + From(goqu.L(fmt.Sprintf(`%s AS r`, table))). + With("validators", goqu.L("(SELECT dashboard_id, validator_index FROM users_val_dashboards_validators WHERE dashboard_id = ?)", dashboardId.Id)). + Select( + goqu.L("SUM(efficiency_dividend::decimal)").As("efficiency_dividend"), + goqu.L("SUM(efficiency_divisor::decimal)").As("efficiency_divisor"), + ) + + if len(dashboardId.Validators) == 0 { + ds = ds. + InnerJoin(goqu.L("validators v"), goqu.On(goqu.L("r.validator_index = v.validator_index"))). + Where(goqu.L("r.validator_index IN (SELECT validator_index FROM validators)")) + } else { + ds = ds. + Where(goqu.L("r.validator_index IN ?", dashboardId.Validators)) + } - *efficiency, err = runQuery[float64](ctx, d.clickhouseReader, ds) - return err - }) + type dbResult struct { + EfficiencyDividend decimal.Decimal `db:"efficiency_dividend"` + EfficiencyDivisor decimal.Decimal `db:"efficiency_divisor"` + } + dbRes, err := runQuery[dbResult](ctx, d.clickhouseReader, ds) + if !dbRes.EfficiencyDivisor.IsZero() { + eff := dbRes.EfficiencyDividend.Div(dbRes.EfficiencyDivisor).InexactFloat64() + efficiency = &eff + } + return } - retrieveRewardsAndEfficiency(enums.Last24h, &data.Rewards.Last24h, &data.Apr.Last24h, &data.Efficiency.Last24h) - retrieveRewardsAndEfficiency(enums.Last7d, &data.Rewards.Last7d, &data.Apr.Last7d, &data.Efficiency.Last7d) - retrieveRewardsAndEfficiency(enums.Last30d, &data.Rewards.Last30d, &data.Apr.Last30d, &data.Efficiency.Last30d) - retrieveRewardsAndEfficiency(enums.AllTime, &data.Rewards.AllTime, &data.Apr.AllTime, &data.Efficiency.AllTime) + // last 24h + eg.Go(func() error { + data.Rewards.Last24h, data.Apr.Last24h, err = retrieveRewards(enums.Last24h) + return err + }) + eg.Go(func() error { + data.Efficiency.Last24h, err = retrieveEfficiency(enums.Last24h) + return err + }) + + // last 7d + eg.Go(func() error { + data.Rewards.Last7d, data.Apr.Last7d, err = retrieveRewards(enums.Last7d) + return err + }) + eg.Go(func() error { + data.Efficiency.Last7d, err = retrieveEfficiency(enums.Last7d) + return err + }) + + // last 30d + eg.Go(func() error { + data.Rewards.Last30d, data.Apr.Last30d, err = retrieveRewards(enums.Last30d) + return err + }) + eg.Go(func() error { + data.Efficiency.Last30d, err = retrieveEfficiency(enums.Last30d) + return err + }) + + // all time + eg.Go(func() error { + data.Rewards.AllTime, data.Apr.AllTime, err = retrieveRewards(enums.AllTime) + return err + }) + eg.Go(func() error { + data.Efficiency.AllTime, err = retrieveEfficiency(enums.AllTime) + return err + }) err = eg.Wait() diff --git a/backend/pkg/api/data_access/vdb_summary.go b/backend/pkg/api/data_access/vdb_summary.go index 709fdcdb74..18c1ba089d 100644 --- a/backend/pkg/api/data_access/vdb_summary.go +++ b/backend/pkg/api/data_access/vdb_summary.go @@ -389,7 +389,10 @@ func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, da } case enums.VDBSummaryColumns.Efficiency: sortParam = func(resultEntry t.VDBSummaryTableRow) float64 { - return resultEntry.Efficiency + if resultEntry.Efficiency == nil { + return -1 + } + return *resultEntry.Efficiency } case enums.VDBSummaryColumns.Attestations: sortParam = func(resultEntry t.VDBSummaryTableRow) float64 { @@ -741,23 +744,21 @@ func (d *DataAccessService) GetValidatorDashboardGroupSummary(ctx context.Contex } if totalBlockChance > 0 { - ret.Luck.Proposal.Percent = (float64(totalBlocksScheduled)) / totalBlockChance + proposalLuck := (float64(totalBlocksScheduled)) / totalBlockChance + ret.Luck.Proposal.Percent = &proposalLuck // calculate the average time it takes for the set of validators to propose a single block on average ret.Luck.Proposal.AverageIntervalSeconds = uint64(time.Duration((luckHours / totalBlockChance) * float64(time.Hour)).Seconds()) ret.Luck.Proposal.ExpectedTimestamp = uint64(lastBlockTs.Unix()) + ret.Luck.Proposal.AverageIntervalSeconds - } else { - ret.Luck.Proposal.Percent = 0 } - if totalSyncExpected == 0 { - ret.Luck.Sync.Percent = 0 - } else { + if totalSyncExpected > 0 { totalSyncSlotDuties := float64(ret.SyncCommittee.StatusCount.Failed) + float64(ret.SyncCommittee.StatusCount.Success) slotDutiesPerSyncCommittee := float64(utils.SlotsPerSyncCommittee()) syncCommittees := math.Ceil(totalSyncSlotDuties / slotDutiesPerSyncCommittee) // gets the number of sync committees - ret.Luck.Sync.Percent = syncCommittees / totalSyncExpected + syncLuck := syncCommittees / totalSyncExpected + ret.Luck.Sync.Percent = &syncLuck // calculate the average time it takes for the set of validators to be elected into a sync committee on average ret.Luck.Sync.AverageIntervalSeconds = uint64(time.Duration((luckHours / totalSyncExpected) * float64(time.Hour)).Seconds()) @@ -816,24 +817,16 @@ func (d *DataAccessService) GetValidatorDashboardGroupSummary(ctx context.Contex return ret, nil } -func calcEfficiencyNulled(dividend, divisor decimal.Decimal) *float64 { +func calcEfficiency(dividend, divisor decimal.Decimal) *float64 { if divisor.IsZero() { return nil } - efficiency := calcEfficiency(dividend, divisor) - return &efficiency -} - -func calcEfficiency(dividend, divisor decimal.Decimal) float64 { - if divisor.IsZero() { - return 1 - } eff := dividend.Div(divisor).InexactFloat64() if eff > 1 { log.Error(nil, "efficiency is greater than 100%", 1, map[string]interface{}{"efficiency": eff}) eff = 1 } - return eff + return &eff } // for summary charts: series id is group id, no stack @@ -956,7 +949,7 @@ func (d *DataAccessService) GetValidatorDashboardSummaryChart(ctx context.Contex } if !dashboardId.AggregateGroups && requestedGroupsMap[row.GroupId] { - eff := calcEfficiencyNulled(row.EfficiencyDividend, row.EfficiencyDivisor) + eff := calcEfficiency(row.EfficiencyDividend, row.EfficiencyDivisor) if eff == nil { continue } @@ -998,7 +991,7 @@ func (d *DataAccessService) GetValidatorDashboardSummaryChart(ctx context.Contex totalLineGroupId = t.DefaultGroupId } for _, row := range totalEfficiencyMap { - data[row.Timestamp][totalLineGroupId] = calcEfficiencyNulled(row.EfficiencyDividend, row.EfficiencyDivisor) + data[row.Timestamp][totalLineGroupId] = calcEfficiency(row.EfficiencyDividend, row.EfficiencyDivisor) } groupMap[totalLineGroupId] = true } diff --git a/backend/pkg/api/handlers/auth.go b/backend/pkg/api/handlers/auth.go index 36f4d9799f..bf457e2cf7 100644 --- a/backend/pkg/api/handlers/auth.go +++ b/backend/pkg/api/handlers/auth.go @@ -196,7 +196,7 @@ func (h *HandlerService) GetUserIdByApiKey(r *http.Request) (uint64, error) { return userId, err } -// if this is used, user ID should've been stored in context (by GetUserIdStoreMiddleware) +// if this is used, user ID should've been stored in context (by StoreUserIdMiddleware) func GetUserIdByContext(ctx context.Context) (uint64, error) { userId, ok := ctx.Value(types.CtxUserIdKey).(uint64) if !ok { diff --git a/backend/pkg/api/types/common.go b/backend/pkg/api/types/common.go index 004cadeb55..3747c09bcd 100644 --- a/backend/pkg/api/types/common.go +++ b/backend/pkg/api/types/common.go @@ -39,9 +39,9 @@ type Address struct { } type LuckItem struct { - Percent float64 `json:"percent"` - ExpectedTimestamp uint64 `json:"expected_timestamp"` - AverageIntervalSeconds uint64 `json:"average_interval_seconds"` + Percent *float64 `json:"percent"` + ExpectedTimestamp uint64 `json:"expected_timestamp"` + AverageIntervalSeconds uint64 `json:"average_interval_seconds"` } type Luck struct { diff --git a/backend/pkg/api/types/mobile.go b/backend/pkg/api/types/mobile.go index 9c16b2fe2e..fa14366e29 100644 --- a/backend/pkg/api/types/mobile.go +++ b/backend/pkg/api/types/mobile.go @@ -13,11 +13,11 @@ type MobileWidgetData struct { ValidatorStateCounts ValidatorStateCounts `json:"validator_state_counts"` Last24hIncome ClElValue[decimal.Decimal] `json:"last_24h_income" faker:"eth"` Last7dIncome ClElValue[decimal.Decimal] `json:"last_7d_income" faker:"eth"` - Last30dApr float64 `json:"last_30d_apr"` - Last30dEfficiency float64 `json:"last_30d_efficiency"` - NetworkEfficiency float64 `json:"network_efficiency"` + Last30dApr *float64 `json:"last_30d_apr"` + Last30dEfficiency *float64 `json:"last_30d_efficiency"` + NetworkEfficiency *float64 `json:"network_efficiency"` RplPrice decimal.Decimal `json:"rpl_price" faker:"eth"` - RplApr float64 `json:"rpl_apr"` + RplApr *float64 `json:"rpl_apr"` ELCLPrice float64 `json:"el_cl_price"` } diff --git a/backend/pkg/api/types/validator_dashboard.go b/backend/pkg/api/types/validator_dashboard.go index 7b7824740d..a85b86ed55 100644 --- a/backend/pkg/api/types/validator_dashboard.go +++ b/backend/pkg/api/types/validator_dashboard.go @@ -25,9 +25,9 @@ type VDBOverviewData struct { Network uint64 `json:"network"` Groups []VDBOverviewGroup `json:"groups"` Validators ValidatorStateCounts `json:"validators"` - Efficiency PeriodicValues[float64] `json:"efficiency"` + Efficiency PeriodicValues[*float64] `json:"efficiency"` Rewards PeriodicValues[ClElValue[decimal.Decimal]] `json:"rewards"` - Apr PeriodicValues[ClElValue[float64]] `json:"apr"` + Apr PeriodicValues[ClElValue[*float64]] `json:"apr"` ChartHistorySeconds ChartHistorySeconds `json:"chart_history_seconds"` Balances ValidatorBalances `json:"balances"` IsAboveEbLimit bool `json:"is_above_effective_balance_limit"` // refers to owner; relevant for shared dashboards @@ -58,7 +58,7 @@ type VDBSummaryTableRow struct { GroupId int64 `json:"group_id" extensions:"x-order=1"` Status VDBSummaryStatus `json:"status"` Validators VDBSummaryValidators `json:"validators"` - Efficiency float64 `json:"efficiency"` + Efficiency *float64 `json:"efficiency"` AverageNetworkEfficiency float64 `json:"average_network_efficiency"` Attestations StatusCount `json:"attestations"` Proposals StatusCount `json:"proposals"` @@ -84,26 +84,26 @@ type VDBGroupSummaryMissedRewards struct { Sync decimal.Decimal `json:"sync"` } type VDBGroupSummaryData struct { - Efficiency float64 `json:"efficiency"` + Efficiency *float64 `json:"efficiency"` Balances ValidatorBalances `json:"balances"` Rewards ClElValue[decimal.Decimal] `json:"rewards"` AttestationsHead StatusCount `json:"attestations_head"` AttestationsSource StatusCount `json:"attestations_source"` AttestationsTarget StatusCount `json:"attestations_target"` - AttestationEfficiency float64 `json:"attestation_efficiency"` + AttestationEfficiency *float64 `json:"attestation_efficiency"` AttestationAvgInclDist float64 `json:"attestation_avg_incl_dist"` SyncCommittee VDBGroupSummaryColumnItem `json:"sync"` SyncCommitteeCount VDBGroupSummarySyncCount `json:"sync_count"` - SyncEfficiency float64 `json:"sync_efficiency"` + SyncEfficiency *float64 `json:"sync_efficiency"` Slashings VDBGroupSummaryColumnItem `json:"slashings"` // Failed slashings are count of validators in the group that were slashed ProposalValidators []uint64 `json:"proposal_validators"` // fill with up to 3 validator indexes ProposalValidatorCount uint64 `json:"proposal_validator_count"` // number of distinct validators - ProposalEfficiency float64 `json:"proposal_efficiency"` + ProposalEfficiency *float64 `json:"proposal_efficiency"` MissedRewards VDBGroupSummaryMissedRewards `json:"missed_rewards"` - Apr ClElValue[float64] `json:"apr"` + Apr ClElValue[*float64] `json:"apr"` Luck Luck `json:"luck"` diff --git a/frontend/types/api/common.ts b/frontend/types/api/common.ts index 6b13482d14..3b657944df 100644 --- a/frontend/types/api/common.ts +++ b/frontend/types/api/common.ts @@ -32,7 +32,7 @@ export interface Address { label?: string; } export interface LuckItem { - percent: number /* float64 */; + percent?: number /* float64 */; expected_timestamp: number /* uint64 */; average_interval_seconds: number /* uint64 */; } diff --git a/frontend/types/api/mobile.ts b/frontend/types/api/mobile.ts index e4837d1fdb..2bd2184b4c 100644 --- a/frontend/types/api/mobile.ts +++ b/frontend/types/api/mobile.ts @@ -14,11 +14,11 @@ export interface MobileWidgetData { validator_state_counts: ValidatorStateCounts; last_24h_income: ClElValue; last_7d_income: ClElValue; - last_30d_apr: number /* float64 */; - last_30d_efficiency: number /* float64 */; - network_efficiency: number /* float64 */; + last_30d_apr?: number /* float64 */; + last_30d_efficiency?: number /* float64 */; + network_efficiency?: number /* float64 */; rpl_price: string /* decimal.Decimal */; - rpl_apr: number /* float64 */; + rpl_apr?: number /* float64 */; el_cl_price: number /* float64 */; } export type InternalGetValidatorDashboardMobileWidgetResponse = ApiDataResponse; diff --git a/frontend/types/api/validator_dashboard.ts b/frontend/types/api/validator_dashboard.ts index 7e8eea3964..7dd061b8f8 100644 --- a/frontend/types/api/validator_dashboard.ts +++ b/frontend/types/api/validator_dashboard.ts @@ -21,9 +21,9 @@ export interface VDBOverviewData { network: number /* uint64 */; groups: VDBOverviewGroup[]; validators: ValidatorStateCounts; - efficiency: PeriodicValues; + efficiency: PeriodicValues; rewards: PeriodicValues>; - apr: PeriodicValues>; + apr: PeriodicValues>; chart_history_seconds: ChartHistorySeconds; balances: ValidatorBalances; is_above_effective_balance_limit: boolean; // refers to owner; relevant for shared dashboards @@ -47,7 +47,7 @@ export interface VDBSummaryTableRow { group_id: number /* int64 */; status: VDBSummaryStatus; validators: VDBSummaryValidators; - efficiency: number /* float64 */; + efficiency?: number /* float64 */; average_network_efficiency: number /* float64 */; attestations: StatusCount; proposals: StatusCount; @@ -70,23 +70,23 @@ export interface VDBGroupSummaryMissedRewards { sync: string /* decimal.Decimal */; } export interface VDBGroupSummaryData { - efficiency: number /* float64 */; + efficiency?: number /* float64 */; balances: ValidatorBalances; rewards: ClElValue; attestations_head: StatusCount; attestations_source: StatusCount; attestations_target: StatusCount; - attestation_efficiency: number /* float64 */; + attestation_efficiency?: number /* float64 */; attestation_avg_incl_dist: number /* float64 */; sync: VDBGroupSummaryColumnItem; sync_count: VDBGroupSummarySyncCount; - sync_efficiency: number /* float64 */; + sync_efficiency?: number /* float64 */; slashings: VDBGroupSummaryColumnItem; // Failed slashings are count of validators in the group that were slashed proposal_validators: number /* uint64 */[]; // fill with up to 3 validator indexes proposal_validator_count: number /* uint64 */; // number of distinct validators - proposal_efficiency: number /* float64 */; + proposal_efficiency?: number /* float64 */; missed_rewards: VDBGroupSummaryMissedRewards; - apr: ClElValue; + apr: ClElValue; luck: Luck; rocket_pool?: { minipools: number /* uint64 */;