@@ -4,15 +4,18 @@ import (
44 "context"
55 "database/sql"
66 "fmt"
7+ "math/big"
78 "time"
89
910 "github.com/doug-martin/goqu/v9"
1011 "github.com/gobitfly/beaconchain/pkg/api/enums"
12+ "github.com/gobitfly/beaconchain/pkg/api/services"
1113 t "github.com/gobitfly/beaconchain/pkg/api/types"
1214 "github.com/gobitfly/beaconchain/pkg/commons/cache"
1315 "github.com/gobitfly/beaconchain/pkg/commons/utils"
1416 "github.com/lib/pq"
1517 "github.com/pkg/errors"
18+ "github.com/shopspring/decimal"
1619)
1720
1821//////////////////// Helper functions (must be used by more than one VDB endpoint!)
@@ -130,3 +133,98 @@ func (d *DataAccessService) getTimeToNextWithdrawal(distance uint64) time.Time {
130133
131134 return timeToWithdrawal
132135}
136+
137+ type RpOperatorInfo struct {
138+ ValidatorIndex uint64 `db:"validatorindex"`
139+ NodeFee float64 `db:"node_fee"`
140+ NodeDepositBalance decimal.Decimal `db:"node_deposit_balance"`
141+ UserDepositBalance decimal.Decimal `db:"user_deposit_balance"`
142+ }
143+
144+ func (d * DataAccessService ) getValidatorDashboardRpOperatorInfo (ctx context.Context , dashboardId t.VDBId ) ([]RpOperatorInfo , error ) {
145+ var rpOperatorInfo []RpOperatorInfo
146+
147+ ds := goqu .Dialect ("postgres" ).
148+ Select (
149+ goqu .L ("v.validatorindex" ),
150+ goqu .L ("rplm.node_fee" ),
151+ goqu .L ("rplm.node_deposit_balance" ),
152+ goqu .L ("rplm.user_deposit_balance" )).
153+ From (goqu .L ("rocketpool_minipools AS rplm" )).
154+ LeftJoin (goqu .L ("validators AS v" ), goqu .On (goqu .L ("rplm.pubkey = v.pubkey" ))).
155+ Where (goqu .L ("node_deposit_balance IS NOT NULL" )).
156+ Where (goqu .L ("user_deposit_balance IS NOT NULL" ))
157+
158+ if len (dashboardId .Validators ) == 0 {
159+ ds = ds .
160+ LeftJoin (goqu .L ("users_val_dashboards_validators uvdv" ), goqu .On (goqu .L ("uvdv.validator_index = v.validatorindex" ))).
161+ Where (goqu .L ("uvdv.dashboard_id = ?" , dashboardId .Id ))
162+ } else {
163+ ds = ds .
164+ Where (goqu .L ("v.validatorindex = ANY(?)" , pq .Array (dashboardId .Validators )))
165+ }
166+
167+ query , args , err := ds .Prepared (true ).ToSQL ()
168+ if err != nil {
169+ return nil , fmt .Errorf ("error preparing query: %w" , err )
170+ }
171+
172+ err = d .alloyReader .SelectContext (ctx , & rpOperatorInfo , query , args ... )
173+ if err != nil {
174+ return nil , fmt .Errorf ("error retrieving rocketpool validators data: %w" , err )
175+ }
176+ return rpOperatorInfo , nil
177+ }
178+
179+ func (d * DataAccessService ) calculateValidatorDashboardBalance (ctx context.Context , rpOperatorInfo []RpOperatorInfo , validators []t.VDBValidator , validatorMapping * services.ValidatorMapping , protocolModes t.VDBProtocolModes ) (t.ValidatorBalances , error ) {
180+ balances := t.ValidatorBalances {}
181+
182+ rpValidators := make (map [uint64 ]RpOperatorInfo )
183+ for _ , res := range rpOperatorInfo {
184+ rpValidators [res .ValidatorIndex ] = res
185+ }
186+
187+ // Create a new sub-dashboard to get the total cl deposits for non-rocketpool validators
188+ var nonRpDashboardId t.VDBId
189+
190+ for _ , validator := range validators {
191+ metadata := validatorMapping .ValidatorMetadata [validator ]
192+ validatorBalance := utils .GWeiToWei (big .NewInt (int64 (metadata .Balance )))
193+ effectiveBalance := utils .GWeiToWei (big .NewInt (int64 (metadata .EffectiveBalance )))
194+
195+ if rpValidator , ok := rpValidators [validator ]; ok {
196+ if protocolModes .RocketPool {
197+ // Calculate the balance of the operator
198+ fullDeposit := rpValidator .UserDepositBalance .Add (rpValidator .NodeDepositBalance )
199+ operatorShare := rpValidator .NodeDepositBalance .Div (fullDeposit )
200+ invOperatorShare := decimal .NewFromInt (1 ).Sub (operatorShare )
201+
202+ base := decimal .Min (decimal .Max (decimal .Zero , validatorBalance .Sub (rpValidator .UserDepositBalance )), rpValidator .NodeDepositBalance )
203+ commission := decimal .Max (decimal .Zero , validatorBalance .Sub (fullDeposit ).Mul (invOperatorShare ).Mul (decimal .NewFromFloat (rpValidator .NodeFee )))
204+ reward := decimal .Max (decimal .Zero , validatorBalance .Sub (fullDeposit ).Mul (operatorShare ).Add (commission ))
205+
206+ operatorBalance := base .Add (reward )
207+
208+ balances .Total = balances .Total .Add (operatorBalance )
209+ } else {
210+ balances .Total = balances .Total .Add (validatorBalance )
211+ }
212+ balances .StakedEth = balances .StakedEth .Add (rpValidator .NodeDepositBalance )
213+ } else {
214+ balances .Total = balances .Total .Add (validatorBalance )
215+
216+ nonRpDashboardId .Validators = append (nonRpDashboardId .Validators , validator )
217+ }
218+ balances .Effective = balances .Effective .Add (effectiveBalance )
219+ }
220+
221+ // Get the total cl deposits for non-rocketpool validators
222+ if len (nonRpDashboardId .Validators ) > 0 {
223+ totalNonRpDeposits , err := d .GetValidatorDashboardTotalClDeposits (ctx , nonRpDashboardId )
224+ if err != nil {
225+ return balances , fmt .Errorf ("error retrieving total cl deposits for non-rocketpool validators: %w" , err )
226+ }
227+ balances .StakedEth = balances .StakedEth .Add (totalNonRpDeposits .TotalAmount )
228+ }
229+ return balances , nil
230+ }
0 commit comments