diff --git a/internal/config/config.go b/internal/config/config.go index a251c949..f9efde0b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -3,9 +3,10 @@ package config import ( "errors" "fmt" - "github.com/spf13/viper" "strconv" "strings" + + "github.com/spf13/viper" ) type EnvScope string @@ -29,6 +30,7 @@ const ( Fork_Nile ForkName = "nile" Fork_Amazon ForkName = "amazon" Fork_Panama ForkName = "panama" + Fork_Arno ForkName = "arno" ENV_PREFIX = "SIDECAR" ) @@ -252,18 +254,21 @@ func (c *Config) GetForkDates() (ForkMap, error) { Fork_Amazon: "1970-01-01", // Amazon hard fork was never on preprod as we backfilled Fork_Nile: "2024-08-14", // Last calculation end timestamp was 8-13: https://holesky.etherscan.io/tx/0xb5a6855e88c79312b7c0e1c9f59ae9890b97f157ea27e69e4f0fadada4712b64#eventlog Fork_Panama: "2024-10-01", + Fork_Arno: "2024-12-04", }, nil case Chain_Holesky: return ForkMap{ Fork_Amazon: "1970-01-01", // Amazon hard fork was never on testnet as we backfilled Fork_Nile: "2024-08-13", // Last calculation end timestamp was 8-12: https://holesky.etherscan.io/tx/0x5fc81b5ed2a78b017ef313c181d8627737a97fef87eee85acedbe39fc8708c56#eventlog Fork_Panama: "2024-10-01", + Fork_Arno: "2024-12-10", }, nil case Chain_Mainnet: return ForkMap{ Fork_Amazon: "2024-08-02", // Last calculation end timestamp was 8-01: https://etherscan.io/tx/0x2aff6f7b0132092c05c8f6f41a5e5eeeb208aa0d95ebcc9022d7823e343dd012#eventlog Fork_Nile: "2024-08-12", // Last calculation end timestamp was 8-11: https://etherscan.io/tx/0x922d29d93c02d189fc2332041f01a80e0007cd7a625a5663ef9d30082f7ef66f#eventlog Fork_Panama: "2024-10-01", + Fork_Arno: "2025-01-07", }, nil } return nil, errors.New("unsupported chain") diff --git a/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go index 15261d8f..34f2b084 100644 --- a/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go +++ b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go @@ -2,6 +2,7 @@ package _202411191550_operatorAVSSplits import ( "database/sql" + "github.com/Layr-Labs/sidecar/internal/config" "gorm.io/gorm" ) @@ -9,7 +10,7 @@ import ( type Migration struct { } -func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { +func (m *Migration) Up(db *sql.DB, grm *gorm.DB, cfg *config.Config) error { query := ` create table if not exists operator_avs_splits ( operator varchar not null, diff --git a/pkg/postgres/migrations/202411191708_operatorPISplits/up.go b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go index a03d4148..4bae75ce 100644 --- a/pkg/postgres/migrations/202411191708_operatorPISplits/up.go +++ b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go @@ -2,6 +2,7 @@ package _202411191708_operatorPISplits import ( "database/sql" + "github.com/Layr-Labs/sidecar/internal/config" "gorm.io/gorm" ) @@ -9,7 +10,7 @@ import ( type Migration struct { } -func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { +func (m *Migration) Up(db *sql.DB, grm *gorm.DB, cfg *config.Config) error { query := ` create table if not exists operator_pi_splits ( operator varchar not null, diff --git a/pkg/postgres/migrations/202411221331_operatorAVSSplitSnapshots/up.go b/pkg/postgres/migrations/202411221331_operatorAVSSplitSnapshots/up.go new file mode 100644 index 00000000..18c9172f --- /dev/null +++ b/pkg/postgres/migrations/202411221331_operatorAVSSplitSnapshots/up.go @@ -0,0 +1,32 @@ +package _202411221331_operatorAVSSplitSnapshots + +import ( + "database/sql" + "github.com/Layr-Labs/sidecar/internal/config" + + "gorm.io/gorm" +) + +type Migration struct { +} + +func (m *Migration) Up(db *sql.DB, grm *gorm.DB, cfg *config.Config) error { + queries := []string{ + `CREATE TABLE IF NOT EXISTS operator_avs_split_snapshots ( + operator varchar not null, + avs varchar not null, + split integer not null, + snapshot date not null + )`, + } + for _, query := range queries { + if _, err := db.Exec(query); err != nil { + return err + } + } + return nil +} + +func (m *Migration) GetName() string { + return "202411221331_operatorAVSSplitSnapshots" +} diff --git a/pkg/postgres/migrations/202411221331_operatorPISplitSnapshots/up.go b/pkg/postgres/migrations/202411221331_operatorPISplitSnapshots/up.go new file mode 100644 index 00000000..ea86997c --- /dev/null +++ b/pkg/postgres/migrations/202411221331_operatorPISplitSnapshots/up.go @@ -0,0 +1,31 @@ +package _202411221331_operatorPISplitSnapshots + +import ( + "database/sql" + "github.com/Layr-Labs/sidecar/internal/config" + + "gorm.io/gorm" +) + +type Migration struct { +} + +func (m *Migration) Up(db *sql.DB, grm *gorm.DB, cfg *config.Config) error { + queries := []string{ + `CREATE TABLE IF NOT EXISTS operator_pi_split_snapshots ( + operator varchar not null, + split integer not null, + snapshot date not null + )`, + } + for _, query := range queries { + if _, err := db.Exec(query); err != nil { + return err + } + } + return nil +} + +func (m *Migration) GetName() string { + return "202411221331_operatorPISplitSnapshots" +} diff --git a/pkg/postgres/migrations/migrator.go b/pkg/postgres/migrations/migrator.go index 66ce16a4..0986819f 100644 --- a/pkg/postgres/migrations/migrator.go +++ b/pkg/postgres/migrations/migrator.go @@ -37,6 +37,8 @@ import ( _202411191550_operatorAVSSplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191550_operatorAVSSplits" _202411191708_operatorPISplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191708_operatorPISplits" _202411191947_cleanupUnusedTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191947_cleanupUnusedTables" + _202411221331_operatorAVSSplitSnapshots "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411221331_operatorAVSSplitSnapshots" + _202411221331_operatorPISplitSnapshots "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411221331_operatorPISplitSnapshots" _202412021311_stakerOperatorTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412021311_stakerOperatorTables" _202412061553_addBlockNumberIndexes "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412061553_addBlockNumberIndexes" _202412061626_operatorRestakedStrategiesConstraint "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412061626_operatorRestakedStrategiesConstraint" @@ -119,6 +121,8 @@ func (m *Migrator) MigrateAll() error { &_202412021311_stakerOperatorTables.Migration{}, &_202412061553_addBlockNumberIndexes.Migration{}, &_202412061626_operatorRestakedStrategiesConstraint.Migration{}, + &_202411221331_operatorAVSSplitSnapshots.Migration{}, + &_202411221331_operatorPISplitSnapshots.Migration{}, } for _, migration := range migrations { diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index e9f1bf53..a4f510cc 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -2,6 +2,7 @@ package rewards import ( "database/sql" + "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" "go.uber.org/zap" @@ -108,26 +109,33 @@ staker_operator_total_tokens AS ( END as total_staker_operator_payout FROM staker_proportion ), --- Calculate the token breakdown for each (staker, operator) pair +-- Calculate the token breakdown for each (staker, operator) pair with dynamic split logic +-- If no split is found, default to 1000 (10%) token_breakdowns AS ( - SELECT *, + SELECT sott.*, CASE - WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - cast(total_staker_operator_payout * 0.10 AS DECIMAL(38,0)) - WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - (total_staker_operator_payout * 0.10)::text::decimal(38,0) + WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN + cast(sott.total_staker_operator_payout * 0.10 AS DECIMAL(38,0)) + WHEN sott.snapshot < @nileHardforkDate AND sott.reward_submission_date < @nileHardforkDate THEN + (sott.total_staker_operator_payout * 0.10)::text::decimal(38,0) + WHEN sott.snapshot < @arnoHardforkDate AND sott.reward_submission_date < @arnoHardforkDate THEN + floor(sott.total_staker_operator_payout * 0.10) ELSE - floor(total_staker_operator_payout * 0.10) + floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) END as operator_tokens, CASE - WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - total_staker_operator_payout - cast(total_staker_operator_payout * 0.10 as DECIMAL(38,0)) - WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - total_staker_operator_payout - ((total_staker_operator_payout * 0.10)::text::decimal(38,0)) + WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN + sott.total_staker_operator_payout - cast(sott.total_staker_operator_payout * 0.10 as DECIMAL(38,0)) + WHEN sott.snapshot < @nileHardforkDate AND sott.reward_submission_date < @nileHardforkDate THEN + sott.total_staker_operator_payout - ((sott.total_staker_operator_payout * 0.10)::text::decimal(38,0)) + WHEN sott.snapshot < @arnoHardforkDate AND sott.reward_submission_date < @arnoHardforkDate THEN + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * 0.10) ELSE - total_staker_operator_payout - floor(total_staker_operator_payout * 0.10) + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) END as staker_tokens - FROM staker_operator_total_tokens + FROM staker_operator_total_tokens sott + LEFT JOIN operator_avs_split_snapshots oas + ON sott.operator = oas.operator AND sott.avs = oas.avs AND sott.snapshot = oas.snapshot ) SELECT * from token_breakdowns ORDER BY reward_hash, snapshot, staker, operator @@ -142,6 +150,7 @@ func (rc *RewardsCalculator) GenerateGold2StakerRewardAmountsTable(snapshotDate zap.String("destTableName", destTableName), zap.String("amazonHardforkDate", forks[config.Fork_Amazon]), zap.String("nileHardforkDate", forks[config.Fork_Nile]), + zap.String("arnoHardforkDate", forks[config.Fork_Arno]), ) query, err := rewardsUtils.RenderQueryTemplate(_2_goldStakerRewardAmountsQuery, map[string]string{ @@ -156,6 +165,7 @@ func (rc *RewardsCalculator) GenerateGold2StakerRewardAmountsTable(snapshotDate res := rc.grm.Exec(query, sql.Named("amazonHardforkDate", forks[config.Fork_Amazon]), sql.Named("nileHardforkDate", forks[config.Fork_Nile]), + sql.Named("arnoHardforkDate", forks[config.Fork_Arno]), ) if res.Error != nil { rc.logger.Sugar().Errorw("Failed to create gold_staker_reward_amounts", "error", res.Error) diff --git a/pkg/rewards/5_goldRfaeStakers.go b/pkg/rewards/5_goldRfaeStakers.go index 9e7d87bc..9d1fd7aa 100644 --- a/pkg/rewards/5_goldRfaeStakers.go +++ b/pkg/rewards/5_goldRfaeStakers.go @@ -2,6 +2,7 @@ package rewards import ( "database/sql" + "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" "go.uber.org/zap" @@ -107,12 +108,25 @@ staker_operator_total_tokens AS ( FLOOR(staker_proportion * tokens_per_day_decimal) as total_staker_operator_payout FROM staker_proportion ), --- Calculate the token breakdown for each (staker, operator) pair +-- Calculate the token breakdown for each (staker, operator) pair with dynamic split logic +-- If no split is found, default to 1000 (10%) token_breakdowns AS ( - SELECT *, - floor(total_staker_operator_payout * 0.10) as operator_tokens, - total_staker_operator_payout - floor(total_staker_operator_payout * 0.10) as staker_tokens - FROM staker_operator_total_tokens + SELECT sott.*, + CASE + WHEN sott.snapshot < @arnoHardforkDate AND sott.reward_submission_date < @arnoHardforkDate THEN + floor(sott.total_staker_operator_payout * 0.10) + ELSE + floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) + END as operator_tokens, + CASE + WHEN sott.snapshot < @arnoHardforkDate AND sott.reward_submission_date < @arnoHardforkDate THEN + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * 0.10) + ELSE + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) + END as staker_tokens + FROM staker_operator_total_tokens sott + LEFT JOIN operator_pi_split_snapshots ops + ON sott.operator = ops.operator AND sott.snapshot = ops.snapshot ) SELECT * from token_breakdowns ORDER BY reward_hash, snapshot, staker, operator @@ -125,6 +139,7 @@ func (rc *RewardsCalculator) GenerateGold5RfaeStakersTable(snapshotDate string, rc.logger.Sugar().Infow("Generating rfae stakers table", zap.String("cutoffDate", snapshotDate), zap.String("destTableName", destTableName), + zap.String("arnoHardforkDate", forks[config.Fork_Arno]), ) query, err := rewardsUtils.RenderQueryTemplate(_5_goldRfaeStakersQuery, map[string]string{ @@ -139,6 +154,7 @@ func (rc *RewardsCalculator) GenerateGold5RfaeStakersTable(snapshotDate string, res := rc.grm.Exec(query, sql.Named("panamaForkDate", forks[config.Fork_Panama]), sql.Named("network", rc.globalConfig.Chain.String()), + sql.Named("arnoHardforkDate", forks[config.Fork_Arno]), ) if res.Error != nil { rc.logger.Sugar().Errorw("Failed to generate gold_rfae_stakers", "error", res.Error) diff --git a/pkg/rewards/operatorAvsSplitSnapshots.go b/pkg/rewards/operatorAvsSplitSnapshots.go new file mode 100644 index 00000000..0634fbf0 --- /dev/null +++ b/pkg/rewards/operatorAvsSplitSnapshots.go @@ -0,0 +1,92 @@ +package rewards + +const operatorAvsSplitSnapshotQuery = ` +WITH operator_avs_splits_with_block_info as ( + select + oas.operator, + oas.avs, + oas.activated_at::timestamp(6) as activated_at, + oas.new_operator_avs_split_bips as split, + oas.block_number, + oas.log_index, + b.block_time::timestamp(6) as block_time + from operator_avs_splits as oas + join blocks as b on (b.number = oas.block_number) + where activated_at < TIMESTAMP '{{.cutoffDate}}' +), +-- Rank the records for each combination of (operator, avs, activation date) by activation time, block time and log index +ranked_operator_avs_split_records as ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY operator, avs, cast(activated_at AS DATE) ORDER BY activated_at DESC, block_time DESC, log_index DESC) AS rn + FROM operator_avs_splits_with_block_info +), +-- Get the latest record for each day & round up to the snapshot day +snapshotted_records as ( + SELECT + operator, + avs, + split, + block_time, + date_trunc('day', activated_at) + INTERVAL '1' day AS snapshot_time + from ranked_operator_avs_split_records + where rn = 1 +), +-- Get the range for each operator, avs pairing +operator_avs_split_windows as ( + SELECT + operator, avs, split, snapshot_time as start_time, + CASE + -- If the range does not have the end, use the current timestamp truncated to 0 UTC + WHEN LEAD(snapshot_time) OVER (PARTITION BY operator, avs ORDER BY snapshot_time) is null THEN date_trunc('day', TIMESTAMP '{{.cutoffDate}}') + ELSE LEAD(snapshot_time) OVER (PARTITION BY operator, avs ORDER BY snapshot_time) + END AS end_time + FROM snapshotted_records +), +-- Clean up any records where start_time >= end_time +cleaned_records as ( + SELECT * FROM operator_avs_split_windows + WHERE start_time < end_time +), +-- Generate a snapshot for each day in the range +final_results as ( + SELECT + operator, + avs, + split, + d AS snapshot + FROM + cleaned_records + CROSS JOIN + generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS d +) +select * from final_results +` + +func (r *RewardsCalculator) GenerateAndInsertOperatorAvsSplitSnapshots(snapshotDate string) error { + tableName := "operator_avs_split_snapshots" + + query, err := renderQueryTemplate(operatorAvsSplitSnapshotQuery, map[string]string{ + "cutoffDate": snapshotDate, + }) + if err != nil { + r.logger.Sugar().Errorw("Failed to render query template", "error", err) + return err + } + + err = r.generateAndInsertFromQuery(tableName, query, nil) + if err != nil { + r.logger.Sugar().Errorw("Failed to generate operator_avs_split_snapshots", "error", err) + return err + } + return nil +} + +func (r *RewardsCalculator) ListOperatorAvsSplitSnapshots() ([]*OperatorAVSSplitSnapshots, error) { + var snapshots []*OperatorAVSSplitSnapshots + res := r.grm.Model(&OperatorAVSSplitSnapshots{}).Find(&snapshots) + if res.Error != nil { + r.logger.Sugar().Errorw("Failed to list operator avs split snapshots", "error", res.Error) + return nil, res.Error + } + return snapshots, nil +} diff --git a/pkg/rewards/operatorPISplitSnapshots.go b/pkg/rewards/operatorPISplitSnapshots.go new file mode 100644 index 00000000..c7e8c2bf --- /dev/null +++ b/pkg/rewards/operatorPISplitSnapshots.go @@ -0,0 +1,89 @@ +package rewards + +const operatorPISplitSnapshotQuery = ` +WITH operator_pi_splits_with_block_info as ( + select + ops.operator, + ops.activated_at::timestamp(6) as activated_at, + ops.new_operator_avs_split_bips as split, + ops.block_number, + ops.log_index, + b.block_time::timestamp(6) as block_time + from operator_pi_splits as ops + join blocks as b on (b.number = ops.block_number) + where activated_at < TIMESTAMP '{{.cutoffDate}}' +), +-- Rank the records for each combination of (operator, activation date) by activation time, block time and log index +ranked_operator_pi_split_records as ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY operator, cast(activated_at AS DATE) ORDER BY activated_at DESC, block_time DESC, log_index DESC) AS rn + FROM operator_pi_splits_with_block_info +), +-- Get the latest record for each day & round up to the snapshot day +snapshotted_records as ( + SELECT + operator, + split, + block_time, + date_trunc('day', activated_at) + INTERVAL '1' day AS snapshot_time + from ranked_operator_pi_split_records + where rn = 1 +), +-- Get the range for each operator +operator_pi_split_windows as ( + SELECT + operator, split, snapshot_time as start_time, + CASE + -- If the range does not have the end, use the current timestamp truncated to 0 UTC + WHEN LEAD(snapshot_time) OVER (PARTITION BY operator ORDER BY snapshot_time) is null THEN date_trunc('day', TIMESTAMP '{{.cutoffDate}}') + ELSE LEAD(snapshot_time) OVER (PARTITION BY operator ORDER BY snapshot_time) + END AS end_time + FROM snapshotted_records +), +-- Clean up any records where start_time >= end_time +cleaned_records as ( + SELECT * FROM operator_pi_split_windows + WHERE start_time < end_time +), +-- Generate a snapshot for each day in the range +final_results as ( + SELECT + operator, + split, + d AS snapshot + FROM + cleaned_records + CROSS JOIN + generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS d +) +select * from final_results +` + +func (r *RewardsCalculator) GenerateAndInsertOperatorPISplitSnapshots(snapshotDate string) error { + tableName := "operator_pi_split_snapshots" + + query, err := renderQueryTemplate(operatorPISplitSnapshotQuery, map[string]string{ + "cutoffDate": snapshotDate, + }) + if err != nil { + r.logger.Sugar().Errorw("Failed to render query template", "error", err) + return err + } + + err = r.generateAndInsertFromQuery(tableName, query, nil) + if err != nil { + r.logger.Sugar().Errorw("Failed to generate operator_pi_split_snapshots", "error", err) + return err + } + return nil +} + +func (r *RewardsCalculator) ListOperatorPISplitSnapshots() ([]*OperatorPISplitSnapshots, error) { + var snapshots []*OperatorPISplitSnapshots + res := r.grm.Model(&OperatorPISplitSnapshots{}).Find(&snapshots) + if res.Error != nil { + r.logger.Sugar().Errorw("Failed to list operator pi split snapshots", "error", res.Error) + return nil, res.Error + } + return snapshots, nil +} diff --git a/pkg/rewards/rewards.go b/pkg/rewards/rewards.go index d079bce5..a7b1e26c 100644 --- a/pkg/rewards/rewards.go +++ b/pkg/rewards/rewards.go @@ -4,21 +4,21 @@ import ( "database/sql" "errors" "fmt" + "time" + "github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/distribution" + "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators" "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" "github.com/Layr-Labs/sidecar/pkg/storage" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/wealdtech/go-merkletree/v2" + "go.uber.org/zap" + "gorm.io/gorm" "gorm.io/gorm/clause" "slices" "strings" "sync/atomic" - "time" - - "github.com/Layr-Labs/sidecar/internal/config" - "go.uber.org/zap" - "gorm.io/gorm" ) type RewardsCalculator struct { @@ -604,6 +604,18 @@ func (rc *RewardsCalculator) generateSnapshotData(snapshotDate string) error { } rc.logger.Sugar().Debugw("Generated staker delegation snapshots") + if err = rc.GenerateAndInsertOperatorAvsSplitSnapshots(snapshotDate); err != nil { + rc.logger.Sugar().Errorw("Failed to generate operator avs split snapshots", "error", err) + return err + } + rc.logger.Sugar().Debugw("Generated operator avs split snapshots") + + if err = rc.GenerateAndInsertOperatorPISplitSnapshots(snapshotDate); err != nil { + rc.logger.Sugar().Errorw("Failed to generate operator pi snapshots", "error", err) + return err + } + rc.logger.Sugar().Debugw("Generated operator pi snapshots") + return nil } diff --git a/pkg/rewards/tables.go b/pkg/rewards/tables.go index f52c5f94..519f70b4 100644 --- a/pkg/rewards/tables.go +++ b/pkg/rewards/tables.go @@ -76,3 +76,16 @@ type OperatorShares struct { BlockTime time.Time BlockDate string } + +type OperatorAVSSplitSnapshots struct { + Operator string + Avs string + Split uint64 + Snapshot time.Time +} + +type OperatorPISplitSnapshots struct { + Operator string + Split uint64 + Snapshot time.Time +}