Skip to content

Commit

Permalink
staker od rewards calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
0xrajath committed Dec 4, 2024
1 parent ec6e804 commit 49d6bc7
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 0 deletions.
136 changes: 136 additions & 0 deletions pkg/rewards/9_goldStakerODRewardAmounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package rewards

import (
"github.com/Layr-Labs/sidecar/pkg/rewardsUtils"
"go.uber.org/zap"
)

const _9_goldStakerODRewardAmountsQuery = `
create table {{.destTableName}} as
-- Get the rows where operators have registered for the AVS
WITH reward_snapshot_operators as (
SELECT
ap.reward_hash,
ap.snapshot as snapshot,
ap.token,
ap.tokens_per_day,
ap.tokens_per_day_decimal,
ap.avs as avs,
ap.operator as operator,
ap.strategy,
ap.multiplier,
ap.reward_submission_date
FROM {{.activeRewardsTable}} ap
JOIN operator_avs_registration_snapshots oar
ON ap.avs = oar.avs and ap.snapshot = oar.snapshot and ap.operator = oar.operator
),
-- Calculate the total staker split for each operator reward with dynamic split logic
-- If no split is found, default to 1000 (10%)
staker_splits AS (
SELECT rso.*,
rso.tokens_per_day_decimal - FLOOR(rso.tokens_per_day_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) as staker_split
FROM reward_snapshot_operators rso
LEFT JOIN operator_avs_split_snapshots oas
ON rso.operator = oas.operator AND rso.avs = oas.avs AND rso.snapshot = oas.snapshot
),
_operator_restaked_strategies AS (
SELECT
ss.*
FROM staker_splits ss
JOIN operator_avs_strategy_snapshots oas
ON
ss.operator = oas.operator AND
ss.avs = oas.avs AND
ss.strategy = oas.strategy AND
ss.snapshot = oas.snapshot
),
-- Get the stakers that were delegated to the operator for the snapshot
staker_delegated_operators AS (
SELECT
ors.*,
sds.staker
FROM _operator_restaked_strategies ors
JOIN staker_delegation_snapshots sds
ON
ors.operator = sds.operator AND
ors.snapshot = sds.snapshot
),
-- Get the shares for staker delegated to the operator
staker_avs_strategy_shares AS (
SELECT
sdo.*,
sss.shares
FROM staker_delegated_operators sdo
JOIN staker_share_snapshots sss
ON
sdo.staker = sss.staker AND
sdo.snapshot = sss.snapshot AND
sdo.strategy = sss.strategy
-- Parse out negative shares and zero multiplier so there is no division by zero case
WHERE sss.shares > 0 and sdo.multiplier != 0
),
-- Calculate the weight of a staker
staker_weights AS (
SELECT *,
SUM(multiplier * shares) OVER (PARTITION BY staker, reward_hash, snapshot) AS staker_weight
FROM staker_avs_strategy_shares
),
-- Get distinct stakers since their weights are already calculated
distinct_stakers AS (
SELECT *
FROM (
SELECT *,
-- We can use an arbitrary order here since the staker_weight is the same for each (staker, strategy, hash, snapshot)
-- We use strategy ASC for better debuggability
ROW_NUMBER() OVER (PARTITION BY reward_hash, snapshot, staker ORDER BY strategy ASC) as rn
FROM staker_weights
) t
WHERE rn = 1
ORDER BY reward_hash, snapshot, staker
),
-- Calculate sum of all staker weights for each reward and snapshot
staker_weight_sum AS (
SELECT *,
SUM(staker_weight) OVER (PARTITION BY reward_hash, snapshot) as total_weight
FROM distinct_stakers
),
-- Calculate staker proportion of tokens for each reward and snapshot
staker_proportion AS (
SELECT *,
FLOOR((staker_weight / total_weight) * 1000000000000000) / 1000000000000000 AS staker_proportion
FROM staker_weight_sum
),
-- Calculate the staker reward amount
staker_reward_amounts AS (
SELECT *,
FLOOR(staker_proportion * staker_split) as staker_tokens
FROM staker_proportion
)
SELECT * FROM staker_reward_amounts
`

func (rc *RewardsCalculator) GenerateGold9StakerODRewardAmountsTable(snapshotDate string) error {
allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate)
destTableName := allTableNames[rewardsUtils.Table_9_StakerODRewardAmounts]

rc.logger.Sugar().Infow("Generating Staker OD reward amounts",
zap.String("cutoffDate", snapshotDate),
zap.String("destTableName", destTableName),
)

query, err := rewardsUtils.RenderQueryTemplate(_9_goldStakerODRewardAmountsQuery, map[string]string{
"destTableName": destTableName,
"activeRewardsTable": allTableNames[rewardsUtils.Table_7_ActiveODRewards],
})
if err != nil {
rc.logger.Sugar().Errorw("Failed to render query template", "error", err)
return err
}

res := rc.grm.Exec(query)
if res.Error != nil {
rc.logger.Sugar().Errorw("Failed to create gold_staker_od_reward_amounts", "error", res.Error)
return res.Error
}
return nil
}
5 changes: 5 additions & 0 deletions pkg/rewards/rewards.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,11 @@ func (rc *RewardsCalculator) generateGoldTables(snapshotDate string) error {
return err
}

if err := rc.GenerateGold9StakerODRewardAmountsTable(snapshotDate); err != nil {
rc.logger.Sugar().Errorw("Failed to generate staker od reward amounts", "error", err)
return err
}

if err := rc.GenerateGold10AvsODRewardAmountsTable(snapshotDate); err != nil {
rc.logger.Sugar().Errorw("Failed to generate avs od reward amounts", "error", err)
return err
Expand Down

0 comments on commit 49d6bc7

Please sign in to comment.