From 4b1871991ea7bbf836fa893fa465239e2350a157 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Wed, 13 Nov 2024 11:41:16 -0600 Subject: [PATCH 01/55] Release: Rewards v2 From 6de8d617207b85ef52a1b5135c20f7c351b83d7e Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 12 Nov 2024 18:31:57 -0500 Subject: [PATCH 02/55] Initial changes --- pkg/eigenState/eigenState.go | 23 +- .../operatorDirectedRewardSubmissions.go | 400 ++++++++++++++++++ 2 files changed, 414 insertions(+), 9 deletions(-) create mode 100644 pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go diff --git a/pkg/eigenState/eigenState.go b/pkg/eigenState/eigenState.go index b95f4ed0..07ab2f9f 100644 --- a/pkg/eigenState/eigenState.go +++ b/pkg/eigenState/eigenState.go @@ -1,15 +1,16 @@ package eigenState import ( - "github.com/Layr-Labs/sidecar/internal/config" - "github.com/Layr-Labs/sidecar/pkg/eigenState/avsOperators" - "github.com/Layr-Labs/sidecar/pkg/eigenState/disabledDistributionRoots" - "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorShares" - "github.com/Layr-Labs/sidecar/pkg/eigenState/rewardSubmissions" - "github.com/Layr-Labs/sidecar/pkg/eigenState/stakerDelegations" - "github.com/Layr-Labs/sidecar/pkg/eigenState/stakerShares" - "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" - "github.com/Layr-Labs/sidecar/pkg/eigenState/submittedDistributionRoots" + "github.com/Layr-Labs/go-sidecar/internal/config" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/avsOperators" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/disabledDistributionRoots" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/operatorDirectedRewardSubmissions" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/operatorShares" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/rewardSubmissions" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stakerDelegations" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stakerShares" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stateManager" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/submittedDistributionRoots" "go.uber.org/zap" "gorm.io/gorm" ) @@ -48,5 +49,9 @@ func LoadEigenStateModels( l.Sugar().Errorw("Failed to create DisabledDistributionRootsModel", zap.Error(err)) return err } + if _, err := operatorDirectedRewardSubmissions.NewOperatorDirectedRewardSubmissionsModel(sm, grm, l, cfg); err != nil { + l.Sugar().Errorw("Failed to create OperatorDirectedRewardSubmissionsModel", zap.Error(err)) + return err + } return nil } diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go new file mode 100644 index 00000000..9df3216f --- /dev/null +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -0,0 +1,400 @@ +package operatorDirectedRewardSubmissions + +import ( + "database/sql" + "encoding/json" + "fmt" + "slices" + "sort" + "strings" + "time" + + "github.com/Layr-Labs/go-sidecar/pkg/storage" + "github.com/Layr-Labs/go-sidecar/pkg/types/numbers" + "github.com/Layr-Labs/go-sidecar/pkg/utils" + + "github.com/Layr-Labs/go-sidecar/internal/config" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/base" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stateManager" + "github.com/Layr-Labs/go-sidecar/pkg/eigenState/types" + "go.uber.org/zap" + "golang.org/x/xerrors" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type OperatorDirectedRewardSubmission struct { + Avs string + RewardHash string + Token string + Operator string + OperatorIndex uint64 + Amount string + Strategy string + StrategyIndex uint64 + Multiplier string + StartTimestamp *time.Time + EndTimestamp *time.Time + Duration uint64 + BlockNumber uint64 +} + +type RewardSubmissionDiff struct { + OperatorDirectedRewardSubmission *OperatorDirectedRewardSubmission + IsNew bool + IsNoLongerActive bool +} + +type OperatorDirectedRewardSubmissions struct { + Submissions []*OperatorDirectedRewardSubmission +} + +func NewSlotID(rewardHash string, strategy string, operator string) types.SlotID { + return types.SlotID(fmt.Sprintf("%s_%s_%s", rewardHash, strategy, operator)) +} + +type OperatorDirectedRewardSubmissionsModel struct { + base.BaseEigenState + StateTransitions types.StateTransitions[OperatorDirectedRewardSubmission] + DB *gorm.DB + Network config.Network + Environment config.Environment + logger *zap.Logger + globalConfig *config.Config + + // Accumulates state changes for SlotIds, grouped by block number + stateAccumulator map[uint64]map[types.SlotID]*OperatorDirectedRewardSubmission +} + +func NewOperatorDirectedRewardSubmissionsModel( + esm *stateManager.EigenStateManager, + grm *gorm.DB, + logger *zap.Logger, + globalConfig *config.Config, +) (*OperatorDirectedRewardSubmissionsModel, error) { + model := &OperatorDirectedRewardSubmissionsModel{ + BaseEigenState: base.BaseEigenState{ + Logger: logger, + }, + DB: grm, + logger: logger, + globalConfig: globalConfig, + stateAccumulator: make(map[uint64]map[types.SlotID]*OperatorDirectedRewardSubmission), + } + + esm.RegisterState(model, 7) + return model, nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) GetModelName() string { + return "OperatorDirectedRewardSubmissionsModel" +} + +type genericRewardPaymentData struct { + Token string + Amount json.Number + StartTimestamp uint64 + Duration uint64 + StrategiesAndMultipliers []struct { + Strategy string + Multiplier json.Number + } `json:"strategiesAndMultipliers"` +} + +type rewardSubmissionOutputData struct { + RewardsSubmission *genericRewardPaymentData `json:"rewardsSubmission"` + RangePayment *genericRewardPaymentData `json:"rangePayment"` +} + +func parseRewardSubmissionOutputData(outputDataStr string) (*rewardSubmissionOutputData, error) { + outputData := &rewardSubmissionOutputData{} + decoder := json.NewDecoder(strings.NewReader(outputDataStr)) + decoder.UseNumber() + + err := decoder.Decode(&outputData) + if err != nil { + return nil, err + } + + return outputData, err +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreatedEvent(log *storage.TransactionLog) (*OperatorDirectedRewardSubmissions, error) { + arguments, err := odrs.ParseLogArguments(log) + if err != nil { + return nil, err + } + + outputData, err := parseRewardSubmissionOutputData(log.OutputData) + if err != nil { + return nil, err + } + + var actualOuputData *genericRewardPaymentData + if log.EventName == "RangePaymentCreated" || log.EventName == "RangePaymentForAllCreated" { + actualOuputData = outputData.RangePayment + } else { + actualOuputData = outputData.RewardsSubmission + } + + rewardSubmissions := make([]*OperatorDirectedRewardSubmission, 0) + + for _, strategyAndMultiplier := range actualOuputData.StrategiesAndMultipliers { + startTimestamp := time.Unix(int64(actualOuputData.StartTimestamp), 0) + endTimestamp := startTimestamp.Add(time.Duration(actualOuputData.Duration) * time.Second) + + amountBig, success := numbers.NewBig257().SetString(actualOuputData.Amount.String(), 10) + if !success { + return nil, xerrors.Errorf("Failed to parse amount to Big257: %s", actualOuputData.Amount.String()) + } + + multiplierBig, success := numbers.NewBig257().SetString(strategyAndMultiplier.Multiplier.String(), 10) + if !success { + return nil, xerrors.Errorf("Failed to parse multiplier to Big257: %s", actualOuputData.Amount.String()) + } + + var rewardType string + if log.EventName == "RewardsSubmissionForAllCreated" || log.EventName == "RangePaymentForAllCreated" { + rewardType = "all_stakers" + } else if log.EventName == "RangePaymentCreated" || log.EventName == "AVSRewardsSubmissionCreated" { + rewardType = "avs" + } else if log.EventName == "RewardsSubmissionForAllEarnersCreated" { + rewardType = "all_earners" + } else { + return nil, xerrors.Errorf("Unknown event name: %s", log.EventName) + } + + rewardSubmission := &OperatorDirectedRewardSubmission{ + Avs: strings.ToLower(arguments[0].Value.(string)), + RewardHash: strings.ToLower(arguments[2].Value.(string)), + Token: strings.ToLower(actualOuputData.Token), + Amount: amountBig.String(), + Strategy: strategyAndMultiplier.Strategy, + Multiplier: multiplierBig.String(), + StartTimestamp: &startTimestamp, + EndTimestamp: &endTimestamp, + Duration: actualOuputData.Duration, + BlockNumber: log.BlockNumber, + RewardType: rewardType, + } + rewardSubmissions = append(rewardSubmissions, rewardSubmission) + } + + return &OperatorDirectedRewardSubmissions{Submissions: rewardSubmissions}, nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types.StateTransitions[OperatorDirectedRewardSubmissions], []uint64) { + stateChanges := make(types.StateTransitions[OperatorDirectedRewardSubmissions]) + + stateChanges[0] = func(log *storage.TransactionLog) (*OperatorDirectedRewardSubmissions, error) { + rewardSubmissions, err := odrs.handleRewardSubmissionCreatedEvent(log) + if err != nil { + return nil, err + } + + for _, rewardSubmission := range rewardSubmissions.Submissions { + slotId := NewSlotID(rewardSubmission.RewardHash, rewardSubmission.Strategy) + + _, ok := odrs.stateAccumulator[log.BlockNumber][slotId] + if ok { + err := xerrors.Errorf("Duplicate distribution root submitted for slot %s at block %d", slotId, log.BlockNumber) + odrs.logger.Sugar().Errorw("Duplicate distribution root submitted", zap.Error(err)) + return nil, err + } + + odrs.stateAccumulator[log.BlockNumber][slotId] = rewardSubmission + } + + return rewardSubmissions, nil + } + + // Create an ordered list of block numbers + blockNumbers := make([]uint64, 0) + for blockNumber := range stateChanges { + blockNumbers = append(blockNumbers, blockNumber) + } + sort.Slice(blockNumbers, func(i, j int) bool { + return blockNumbers[i] < blockNumbers[j] + }) + slices.Reverse(blockNumbers) + + return stateChanges, blockNumbers +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) getContractAddressesForEnvironment() map[string][]string { + contracts := odrs.globalConfig.GetContractsMapForChain() + return map[string][]string{ + contracts.RewardsCoordinator: { + "OperatorDirectedAVSRewardsSubmissionCreated", + }, + } +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) IsInterestingLog(log *storage.TransactionLog) bool { + addresses := odrs.getContractAddressesForEnvironment() + return odrs.BaseEigenState.IsInterestingLog(addresses, log) +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) SetupStateForBlock(blockNumber uint64) error { + odrs.stateAccumulator[blockNumber] = make(map[types.SlotID]*OperatorDirectedRewardSubmission) + return nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) CleanupProcessedStateForBlock(blockNumber uint64) error { + delete(odrs.stateAccumulator, blockNumber) + return nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) HandleStateChange(log *storage.TransactionLog) (interface{}, error) { + stateChanges, sortedBlockNumbers := odrs.GetStateTransitions() + + for _, blockNumber := range sortedBlockNumbers { + if log.BlockNumber >= blockNumber { + odrs.logger.Sugar().Debugw("Handling state change", zap.Uint64("blockNumber", log.BlockNumber)) + + change, err := stateChanges[blockNumber](log) + if err != nil { + return nil, err + } + if change == nil { + return nil, nil + } + return change, nil + } + } + return nil, nil +} + +// prepareState prepares the state for commit by adding the new state to the existing state. +func (odrs *OperatorDirectedRewardSubmissionsModel) prepareState(blockNumber uint64) ([]*RewardSubmissionDiff, []*RewardSubmissionDiff, error) { + accumulatedState, ok := odrs.stateAccumulator[blockNumber] + if !ok { + err := xerrors.Errorf("No accumulated state found for block %d", blockNumber) + odrs.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, nil, err + } + + currentBlock := &storage.Block{} + err := odrs.DB.Where("number = ?", blockNumber).First(currentBlock).Error + if err != nil { + odrs.logger.Sugar().Errorw("Failed to fetch block", zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, nil, err + } + + inserts := make([]*RewardSubmissionDiff, 0) + for _, change := range accumulatedState { + if change == nil { + continue + } + + inserts = append(inserts, &RewardSubmissionDiff{ + OperatorDirectedRewardSubmission: change, + IsNew: true, + }) + } + + // find all the records that are no longer active + noLongerActiveSubmissions := make([]*OperatorDirectedRewardSubmission, 0) + query := ` + select + * + from reward_submissions + where + block_number = @previousBlock + and end_timestamp <= @blockTime + ` + res := odrs.DB. + Model(&OperatorDirectedRewardSubmission{}). + Raw(query, + sql.Named("previousBlock", blockNumber-1), + sql.Named("blockTime", currentBlock.BlockTime), + ). + Find(&noLongerActiveSubmissions) + + if res.Error != nil { + odrs.logger.Sugar().Errorw("Failed to fetch no longer active submissions", zap.Error(res.Error)) + return nil, nil, res.Error + } + + deletes := make([]*RewardSubmissionDiff, 0) + for _, submission := range noLongerActiveSubmissions { + deletes = append(deletes, &RewardSubmissionDiff{ + OperatorDirectedRewardSubmission: submission, + IsNoLongerActive: true, + }) + } + return inserts, deletes, nil +} + +// CommitFinalState commits the final state for the given block number. +func (odrs *OperatorDirectedRewardSubmissionsModel) CommitFinalState(blockNumber uint64) error { + recordsToInsert, _, err := odrs.prepareState(blockNumber) + if err != nil { + return err + } + + if len(recordsToInsert) > 0 { + for _, record := range recordsToInsert { + res := odrs.DB.Model(&OperatorDirectedRewardSubmission{}).Clauses(clause.Returning{}).Create(&record.OperatorDirectedRewardSubmission) + if res.Error != nil { + odrs.logger.Sugar().Errorw("Failed to insert records", zap.Error(res.Error)) + return res.Error + } + } + } + return nil +} + +// GenerateStateRoot generates the state root for the given block number using the results of the state changes. +func (odrs *OperatorDirectedRewardSubmissionsModel) GenerateStateRoot(blockNumber uint64) (types.StateRoot, error) { + inserts, deletes, err := odrs.prepareState(blockNumber) + if err != nil { + return "", err + } + + combinedResults := make([]*RewardSubmissionDiff, 0) + combinedResults = append(combinedResults, inserts...) + combinedResults = append(combinedResults, deletes...) + + inputs := odrs.sortValuesForMerkleTree(combinedResults) + + if len(inputs) == 0 { + return "", nil + } + + fullTree, err := odrs.MerkleizeState(blockNumber, inputs) + if err != nil { + odrs.logger.Sugar().Errorw("Failed to create merkle tree", + zap.Error(err), + zap.Uint64("blockNumber", blockNumber), + zap.Any("inputs", inputs), + ) + return "", err + } + return types.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(submissions []*RewardSubmissionDiff) []*base.MerkleTreeInput { + inputs := make([]*base.MerkleTreeInput, 0) + for _, submission := range submissions { + slotID := NewSlotID(submission.OperatorDirectedRewardSubmission.RewardHash, submission.OperatorDirectedRewardSubmission.Strategy, submission.OperatorDirectedRewardSubmission.Operator) + value := "added" + if submission.IsNoLongerActive { + value = "removed" + } + inputs = append(inputs, &base.MerkleTreeInput{ + SlotID: slotID, + Value: []byte(value), + }) + } + + slices.SortFunc(inputs, func(i, j *base.MerkleTreeInput) int { + return strings.Compare(string(i.SlotID), string(j.SlotID)) + }) + + return inputs +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { + return odrs.BaseEigenState.DeleteState("operator_directed_reward_submissions", startBlockNumber, endBlockNumber, odrs.DB) +} From 04c09c11e160f682462730ee24156f5ae147101e Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 12 Nov 2024 19:18:19 -0500 Subject: [PATCH 03/55] small fixes --- .../operatorDirectedRewardSubmissions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index 9df3216f..d136e00b 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -193,7 +193,7 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types } for _, rewardSubmission := range rewardSubmissions.Submissions { - slotId := NewSlotID(rewardSubmission.RewardHash, rewardSubmission.Strategy) + slotId := NewSlotID(rewardSubmission.RewardHash, rewardSubmission.Strategy, rewardSubmission.Operator) _, ok := odrs.stateAccumulator[log.BlockNumber][slotId] if ok { From 9c1c251bbde2e2ee54d670d81b6254c3b79314c6 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 15 Nov 2024 15:14:36 -0500 Subject: [PATCH 04/55] cleanup --- pkg/eigenState/eigenState.go | 20 +- .../operatorDirectedRewardSubmissions.go | 192 +++++++----------- 2 files changed, 79 insertions(+), 133 deletions(-) diff --git a/pkg/eigenState/eigenState.go b/pkg/eigenState/eigenState.go index 07ab2f9f..db339945 100644 --- a/pkg/eigenState/eigenState.go +++ b/pkg/eigenState/eigenState.go @@ -1,16 +1,16 @@ package eigenState import ( - "github.com/Layr-Labs/go-sidecar/internal/config" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/avsOperators" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/disabledDistributionRoots" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/operatorDirectedRewardSubmissions" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/operatorShares" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/rewardSubmissions" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stakerDelegations" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stakerShares" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stateManager" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/submittedDistributionRoots" + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/pkg/eigenState/avsOperators" + "github.com/Layr-Labs/sidecar/pkg/eigenState/disabledDistributionRoots" + "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorDirectedRewardSubmissions" + "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorShares" + "github.com/Layr-Labs/sidecar/pkg/eigenState/rewardSubmissions" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stakerDelegations" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stakerShares" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" + "github.com/Layr-Labs/sidecar/pkg/eigenState/submittedDistributionRoots" "go.uber.org/zap" "gorm.io/gorm" ) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index d136e00b..4f222dd8 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -1,7 +1,6 @@ package operatorDirectedRewardSubmissions import ( - "database/sql" "encoding/json" "fmt" "slices" @@ -9,14 +8,14 @@ import ( "strings" "time" - "github.com/Layr-Labs/go-sidecar/pkg/storage" - "github.com/Layr-Labs/go-sidecar/pkg/types/numbers" - "github.com/Layr-Labs/go-sidecar/pkg/utils" + "github.com/Layr-Labs/sidecar/pkg/storage" + "github.com/Layr-Labs/sidecar/pkg/types/numbers" + "github.com/Layr-Labs/sidecar/pkg/utils" - "github.com/Layr-Labs/go-sidecar/internal/config" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/base" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/stateManager" - "github.com/Layr-Labs/go-sidecar/pkg/eigenState/types" + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/pkg/eigenState/base" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" + "github.com/Layr-Labs/sidecar/pkg/eigenState/types" "go.uber.org/zap" "golang.org/x/xerrors" "gorm.io/gorm" @@ -24,38 +23,29 @@ import ( ) type OperatorDirectedRewardSubmission struct { - Avs string - RewardHash string - Token string - Operator string - OperatorIndex uint64 - Amount string - Strategy string - StrategyIndex uint64 - Multiplier string - StartTimestamp *time.Time - EndTimestamp *time.Time - Duration uint64 - BlockNumber uint64 + Avs string + RewardHash string + Token string + Amount string + Strategy string + StrategyIndex uint64 + Multiplier string + StartTimestamp *time.Time + EndTimestamp *time.Time + Duration uint64 + BlockNumber uint64 + RewardType string // avs, all_stakers, all_earners + TransactionHash string + LogIndex uint64 } -type RewardSubmissionDiff struct { - OperatorDirectedRewardSubmission *OperatorDirectedRewardSubmission - IsNew bool - IsNoLongerActive bool -} - -type OperatorDirectedRewardSubmissions struct { - Submissions []*OperatorDirectedRewardSubmission -} - -func NewSlotID(rewardHash string, strategy string, operator string) types.SlotID { - return types.SlotID(fmt.Sprintf("%s_%s_%s", rewardHash, strategy, operator)) +func NewSlotID(transactionHash string, logIndex uint64, rewardHash string, strategyIndex uint64) types.SlotID { + return base.NewSlotIDWithSuffix(transactionHash, logIndex, fmt.Sprintf("%s_%d", rewardHash, strategyIndex)) } type OperatorDirectedRewardSubmissionsModel struct { base.BaseEigenState - StateTransitions types.StateTransitions[OperatorDirectedRewardSubmission] + StateTransitions types.StateTransitions[[]*OperatorDirectedRewardSubmission] DB *gorm.DB Network config.Network Environment config.Environment @@ -82,7 +72,7 @@ func NewOperatorDirectedRewardSubmissionsModel( stateAccumulator: make(map[uint64]map[types.SlotID]*OperatorDirectedRewardSubmission), } - esm.RegisterState(model, 7) + esm.RegisterState(model, 5) return model, nil } @@ -101,13 +91,13 @@ type genericRewardPaymentData struct { } `json:"strategiesAndMultipliers"` } -type rewardSubmissionOutputData struct { +type operatorDirectedRewardSubmissionOutputData struct { RewardsSubmission *genericRewardPaymentData `json:"rewardsSubmission"` RangePayment *genericRewardPaymentData `json:"rangePayment"` } -func parseRewardSubmissionOutputData(outputDataStr string) (*rewardSubmissionOutputData, error) { - outputData := &rewardSubmissionOutputData{} +func parseRewardSubmissionOutputData(outputDataStr string) (*operatorDirectedRewardSubmissionOutputData, error) { + outputData := &operatorDirectedRewardSubmissionOutputData{} decoder := json.NewDecoder(strings.NewReader(outputDataStr)) decoder.UseNumber() @@ -119,7 +109,7 @@ func parseRewardSubmissionOutputData(outputDataStr string) (*rewardSubmissionOut return outputData, err } -func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreatedEvent(log *storage.TransactionLog) (*OperatorDirectedRewardSubmissions, error) { +func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreatedEvent(log *storage.TransactionLog) ([]*OperatorDirectedRewardSubmission, error) { arguments, err := odrs.ParseLogArguments(log) if err != nil { return nil, err @@ -139,7 +129,7 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreate rewardSubmissions := make([]*OperatorDirectedRewardSubmission, 0) - for _, strategyAndMultiplier := range actualOuputData.StrategiesAndMultipliers { + for i, strategyAndMultiplier := range actualOuputData.StrategiesAndMultipliers { startTimestamp := time.Unix(int64(actualOuputData.StartTimestamp), 0) endTimestamp := startTimestamp.Add(time.Duration(actualOuputData.Duration) * time.Second) @@ -165,38 +155,42 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreate } rewardSubmission := &OperatorDirectedRewardSubmission{ - Avs: strings.ToLower(arguments[0].Value.(string)), - RewardHash: strings.ToLower(arguments[2].Value.(string)), - Token: strings.ToLower(actualOuputData.Token), - Amount: amountBig.String(), - Strategy: strategyAndMultiplier.Strategy, - Multiplier: multiplierBig.String(), - StartTimestamp: &startTimestamp, - EndTimestamp: &endTimestamp, - Duration: actualOuputData.Duration, - BlockNumber: log.BlockNumber, - RewardType: rewardType, + Avs: strings.ToLower(arguments[0].Value.(string)), + RewardHash: strings.ToLower(arguments[2].Value.(string)), + Token: strings.ToLower(actualOuputData.Token), + Amount: amountBig.String(), + Strategy: strategyAndMultiplier.Strategy, + Multiplier: multiplierBig.String(), + StartTimestamp: &startTimestamp, + EndTimestamp: &endTimestamp, + Duration: actualOuputData.Duration, + BlockNumber: log.BlockNumber, + RewardType: rewardType, + TransactionHash: log.TransactionHash, + LogIndex: log.LogIndex, + StrategyIndex: uint64(i), } rewardSubmissions = append(rewardSubmissions, rewardSubmission) } - return &OperatorDirectedRewardSubmissions{Submissions: rewardSubmissions}, nil + return rewardSubmissions, nil } -func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types.StateTransitions[OperatorDirectedRewardSubmissions], []uint64) { - stateChanges := make(types.StateTransitions[OperatorDirectedRewardSubmissions]) +func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types.StateTransitions[[]*OperatorDirectedRewardSubmission], []uint64) { + stateChanges := make(types.StateTransitions[[]*OperatorDirectedRewardSubmission]) - stateChanges[0] = func(log *storage.TransactionLog) (*OperatorDirectedRewardSubmissions, error) { + stateChanges[0] = func(log *storage.TransactionLog) ([]*OperatorDirectedRewardSubmission, error) { rewardSubmissions, err := odrs.handleRewardSubmissionCreatedEvent(log) if err != nil { return nil, err } - for _, rewardSubmission := range rewardSubmissions.Submissions { - slotId := NewSlotID(rewardSubmission.RewardHash, rewardSubmission.Strategy, rewardSubmission.Operator) + for _, rewardSubmission := range rewardSubmissions { + slotId := NewSlotID(rewardSubmission.TransactionHash, rewardSubmission.LogIndex, rewardSubmission.RewardHash, rewardSubmission.StrategyIndex) _, ok := odrs.stateAccumulator[log.BlockNumber][slotId] if ok { + fmt.Printf("Submissions: %+v\n", odrs.stateAccumulator[log.BlockNumber]) err := xerrors.Errorf("Duplicate distribution root submitted for slot %s at block %d", slotId, log.BlockNumber) odrs.logger.Sugar().Errorw("Duplicate distribution root submitted", zap.Error(err)) return nil, err @@ -225,7 +219,11 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) getContractAddressesForEnvir contracts := odrs.globalConfig.GetContractsMapForChain() return map[string][]string{ contracts.RewardsCoordinator: { - "OperatorDirectedAVSRewardsSubmissionCreated", + "RangePaymentForAllCreated", + "RewardsSubmissionForAllCreated", + "RangePaymentCreated", + "AVSRewardsSubmissionCreated", + "RewardsSubmissionForAllEarnersCreated", }, } } @@ -266,76 +264,31 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) HandleStateChange(log *stora } // prepareState prepares the state for commit by adding the new state to the existing state. -func (odrs *OperatorDirectedRewardSubmissionsModel) prepareState(blockNumber uint64) ([]*RewardSubmissionDiff, []*RewardSubmissionDiff, error) { +func (odrs *OperatorDirectedRewardSubmissionsModel) prepareState(blockNumber uint64) ([]*OperatorDirectedRewardSubmission, error) { accumulatedState, ok := odrs.stateAccumulator[blockNumber] if !ok { err := xerrors.Errorf("No accumulated state found for block %d", blockNumber) odrs.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) - return nil, nil, err - } - - currentBlock := &storage.Block{} - err := odrs.DB.Where("number = ?", blockNumber).First(currentBlock).Error - if err != nil { - odrs.logger.Sugar().Errorw("Failed to fetch block", zap.Error(err), zap.Uint64("blockNumber", blockNumber)) - return nil, nil, err - } - - inserts := make([]*RewardSubmissionDiff, 0) - for _, change := range accumulatedState { - if change == nil { - continue - } - - inserts = append(inserts, &RewardSubmissionDiff{ - OperatorDirectedRewardSubmission: change, - IsNew: true, - }) - } - - // find all the records that are no longer active - noLongerActiveSubmissions := make([]*OperatorDirectedRewardSubmission, 0) - query := ` - select - * - from reward_submissions - where - block_number = @previousBlock - and end_timestamp <= @blockTime - ` - res := odrs.DB. - Model(&OperatorDirectedRewardSubmission{}). - Raw(query, - sql.Named("previousBlock", blockNumber-1), - sql.Named("blockTime", currentBlock.BlockTime), - ). - Find(&noLongerActiveSubmissions) - - if res.Error != nil { - odrs.logger.Sugar().Errorw("Failed to fetch no longer active submissions", zap.Error(res.Error)) - return nil, nil, res.Error + return nil, err } - deletes := make([]*RewardSubmissionDiff, 0) - for _, submission := range noLongerActiveSubmissions { - deletes = append(deletes, &RewardSubmissionDiff{ - OperatorDirectedRewardSubmission: submission, - IsNoLongerActive: true, - }) + recordsToInsert := make([]*OperatorDirectedRewardSubmission, 0) + for _, submission := range accumulatedState { + recordsToInsert = append(recordsToInsert, submission) } - return inserts, deletes, nil + return recordsToInsert, nil } // CommitFinalState commits the final state for the given block number. func (odrs *OperatorDirectedRewardSubmissionsModel) CommitFinalState(blockNumber uint64) error { - recordsToInsert, _, err := odrs.prepareState(blockNumber) + recordsToInsert, err := odrs.prepareState(blockNumber) if err != nil { return err } if len(recordsToInsert) > 0 { for _, record := range recordsToInsert { - res := odrs.DB.Model(&OperatorDirectedRewardSubmission{}).Clauses(clause.Returning{}).Create(&record.OperatorDirectedRewardSubmission) + res := odrs.DB.Model(&OperatorDirectedRewardSubmission{}).Clauses(clause.Returning{}).Create(&record) if res.Error != nil { odrs.logger.Sugar().Errorw("Failed to insert records", zap.Error(res.Error)) return res.Error @@ -347,16 +300,12 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) CommitFinalState(blockNumber // GenerateStateRoot generates the state root for the given block number using the results of the state changes. func (odrs *OperatorDirectedRewardSubmissionsModel) GenerateStateRoot(blockNumber uint64) (types.StateRoot, error) { - inserts, deletes, err := odrs.prepareState(blockNumber) + inserts, err := odrs.prepareState(blockNumber) if err != nil { return "", err } - combinedResults := make([]*RewardSubmissionDiff, 0) - combinedResults = append(combinedResults, inserts...) - combinedResults = append(combinedResults, deletes...) - - inputs := odrs.sortValuesForMerkleTree(combinedResults) + inputs := odrs.sortValuesForMerkleTree(inserts) if len(inputs) == 0 { return "", nil @@ -374,14 +323,11 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) GenerateStateRoot(blockNumbe return types.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil } -func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(submissions []*RewardSubmissionDiff) []*base.MerkleTreeInput { +func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(submissions []*OperatorDirectedRewardSubmission) []*base.MerkleTreeInput { inputs := make([]*base.MerkleTreeInput, 0) for _, submission := range submissions { - slotID := NewSlotID(submission.OperatorDirectedRewardSubmission.RewardHash, submission.OperatorDirectedRewardSubmission.Strategy, submission.OperatorDirectedRewardSubmission.Operator) + slotID := NewSlotID(submission.TransactionHash, submission.LogIndex, submission.RewardHash, submission.StrategyIndex) value := "added" - if submission.IsNoLongerActive { - value = "removed" - } inputs = append(inputs, &base.MerkleTreeInput{ SlotID: slotID, Value: []byte(value), @@ -396,5 +342,5 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(subm } func (odrs *OperatorDirectedRewardSubmissionsModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { - return odrs.BaseEigenState.DeleteState("operator_directed_reward_submissions", startBlockNumber, endBlockNumber, odrs.DB) + return odrs.BaseEigenState.DeleteState("reward_submissions", startBlockNumber, endBlockNumber, odrs.DB) } From 3e673b64ff16f044ce549acdd1db60e8a391130f Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 15 Nov 2024 16:07:42 -0500 Subject: [PATCH 05/55] refactoring --- .../operatorDirectedRewardSubmissions.go | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index 4f222dd8..3ba0337f 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -26,6 +26,8 @@ type OperatorDirectedRewardSubmission struct { Avs string RewardHash string Token string + Operator string + OperatorIndex uint64 Amount string Strategy string StrategyIndex uint64 @@ -34,13 +36,12 @@ type OperatorDirectedRewardSubmission struct { EndTimestamp *time.Time Duration uint64 BlockNumber uint64 - RewardType string // avs, all_stakers, all_earners TransactionHash string LogIndex uint64 } -func NewSlotID(transactionHash string, logIndex uint64, rewardHash string, strategyIndex uint64) types.SlotID { - return base.NewSlotIDWithSuffix(transactionHash, logIndex, fmt.Sprintf("%s_%d", rewardHash, strategyIndex)) +func NewSlotID(transactionHash string, logIndex uint64, rewardHash string, strategyIndex uint64, operatorIndex uint64) types.SlotID { + return base.NewSlotIDWithSuffix(transactionHash, logIndex, fmt.Sprintf("%s_%d_%d", rewardHash, strategyIndex, operatorIndex)) } type OperatorDirectedRewardSubmissionsModel struct { @@ -72,7 +73,7 @@ func NewOperatorDirectedRewardSubmissionsModel( stateAccumulator: make(map[uint64]map[types.SlotID]*OperatorDirectedRewardSubmission), } - esm.RegisterState(model, 5) + esm.RegisterState(model, 7) return model, nil } @@ -165,7 +166,6 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreate EndTimestamp: &endTimestamp, Duration: actualOuputData.Duration, BlockNumber: log.BlockNumber, - RewardType: rewardType, TransactionHash: log.TransactionHash, LogIndex: log.LogIndex, StrategyIndex: uint64(i), @@ -186,7 +186,7 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types } for _, rewardSubmission := range rewardSubmissions { - slotId := NewSlotID(rewardSubmission.TransactionHash, rewardSubmission.LogIndex, rewardSubmission.RewardHash, rewardSubmission.StrategyIndex) + slotId := NewSlotID(rewardSubmission.TransactionHash, rewardSubmission.LogIndex, rewardSubmission.RewardHash, rewardSubmission.StrategyIndex, rewardSubmission.OperatorIndex) _, ok := odrs.stateAccumulator[log.BlockNumber][slotId] if ok { @@ -219,11 +219,7 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) getContractAddressesForEnvir contracts := odrs.globalConfig.GetContractsMapForChain() return map[string][]string{ contracts.RewardsCoordinator: { - "RangePaymentForAllCreated", - "RewardsSubmissionForAllCreated", - "RangePaymentCreated", - "AVSRewardsSubmissionCreated", - "RewardsSubmissionForAllEarnersCreated", + "OperatorDirectedAVSRewardsSubmissionCreated", }, } } @@ -326,7 +322,7 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) GenerateStateRoot(blockNumbe func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(submissions []*OperatorDirectedRewardSubmission) []*base.MerkleTreeInput { inputs := make([]*base.MerkleTreeInput, 0) for _, submission := range submissions { - slotID := NewSlotID(submission.TransactionHash, submission.LogIndex, submission.RewardHash, submission.StrategyIndex) + slotID := NewSlotID(submission.TransactionHash, submission.LogIndex, submission.RewardHash, submission.StrategyIndex, submission.OperatorIndex) value := "added" inputs = append(inputs, &base.MerkleTreeInput{ SlotID: slotID, @@ -342,5 +338,5 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(subm } func (odrs *OperatorDirectedRewardSubmissionsModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { - return odrs.BaseEigenState.DeleteState("reward_submissions", startBlockNumber, endBlockNumber, odrs.DB) + return odrs.BaseEigenState.DeleteState("operator_directed_reward_submissions", startBlockNumber, endBlockNumber, odrs.DB) } From d2afbf41af149a5d33faf7fe64f371d53cf1a558 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 15 Nov 2024 16:08:06 -0500 Subject: [PATCH 06/55] more refactoring --- .../operatorDirectedRewardSubmissions.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index 3ba0337f..aa57c5e0 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -144,17 +144,6 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreate return nil, xerrors.Errorf("Failed to parse multiplier to Big257: %s", actualOuputData.Amount.String()) } - var rewardType string - if log.EventName == "RewardsSubmissionForAllCreated" || log.EventName == "RangePaymentForAllCreated" { - rewardType = "all_stakers" - } else if log.EventName == "RangePaymentCreated" || log.EventName == "AVSRewardsSubmissionCreated" { - rewardType = "avs" - } else if log.EventName == "RewardsSubmissionForAllEarnersCreated" { - rewardType = "all_earners" - } else { - return nil, xerrors.Errorf("Unknown event name: %s", log.EventName) - } - rewardSubmission := &OperatorDirectedRewardSubmission{ Avs: strings.ToLower(arguments[0].Value.(string)), RewardHash: strings.ToLower(arguments[2].Value.(string)), From e27744e9f0cb0b774e3a0451c63d5ee838c1ee5a Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 15 Nov 2024 17:26:27 -0500 Subject: [PATCH 07/55] rename --- .../operatorDirectedRewardSubmissions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index aa57c5e0..5162a3e0 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -110,7 +110,7 @@ func parseRewardSubmissionOutputData(outputDataStr string) (*operatorDirectedRew return outputData, err } -func (odrs *OperatorDirectedRewardSubmissionsModel) handleRewardSubmissionCreatedEvent(log *storage.TransactionLog) ([]*OperatorDirectedRewardSubmission, error) { +func (odrs *OperatorDirectedRewardSubmissionsModel) handleOperatorDirectedRewardSubmissionCreatedEvent(log *storage.TransactionLog) ([]*OperatorDirectedRewardSubmission, error) { arguments, err := odrs.ParseLogArguments(log) if err != nil { return nil, err @@ -169,7 +169,7 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types stateChanges := make(types.StateTransitions[[]*OperatorDirectedRewardSubmission]) stateChanges[0] = func(log *storage.TransactionLog) ([]*OperatorDirectedRewardSubmission, error) { - rewardSubmissions, err := odrs.handleRewardSubmissionCreatedEvent(log) + rewardSubmissions, err := odrs.handleOperatorDirectedRewardSubmissionCreatedEvent(log) if err != nil { return nil, err } From 649dff481771349928b7320515be4d7cc5bfcfbf Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 15 Nov 2024 18:38:42 -0500 Subject: [PATCH 08/55] operator directed rewards submission model --- .../operatorDirectedRewardSubmissions.go | 83 ++++++++++--------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index 5162a3e0..cd8eaf40 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -81,20 +81,24 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) GetModelName() string { return "OperatorDirectedRewardSubmissionsModel" } -type genericRewardPaymentData struct { - Token string - Amount json.Number - StartTimestamp uint64 - Duration uint64 +type operatorDirectedRewardData struct { StrategiesAndMultipliers []struct { - Strategy string - Multiplier json.Number + Strategy string `json:"strategy"` + Multiplier json.Number `json:"multiplier"` } `json:"strategiesAndMultipliers"` + Token string `json:"token"` + OperatorRewards []struct { + Operator string `json:"operator"` + Amount json.Number `json:"amount"` + } `json:"operatorRewards"` + StartTimestamp uint64 `json:"startTimestamp"` + Duration uint64 `json:"duration"` + Description string `json:"description"` } type operatorDirectedRewardSubmissionOutputData struct { - RewardsSubmission *genericRewardPaymentData `json:"rewardsSubmission"` - RangePayment *genericRewardPaymentData `json:"rangePayment"` + SubmissionNonce json.Number `json:"submissionNonce"` + OperatorDirectedRewardsSubmission *operatorDirectedRewardData `json:"operatorDirectedRewardsSubmission"` } func parseRewardSubmissionOutputData(outputDataStr string) (*operatorDirectedRewardSubmissionOutputData, error) { @@ -120,46 +124,45 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) handleOperatorDirectedReward if err != nil { return nil, err } - - var actualOuputData *genericRewardPaymentData - if log.EventName == "RangePaymentCreated" || log.EventName == "RangePaymentForAllCreated" { - actualOuputData = outputData.RangePayment - } else { - actualOuputData = outputData.RewardsSubmission - } + outputRewardData := outputData.OperatorDirectedRewardsSubmission rewardSubmissions := make([]*OperatorDirectedRewardSubmission, 0) - for i, strategyAndMultiplier := range actualOuputData.StrategiesAndMultipliers { - startTimestamp := time.Unix(int64(actualOuputData.StartTimestamp), 0) - endTimestamp := startTimestamp.Add(time.Duration(actualOuputData.Duration) * time.Second) - - amountBig, success := numbers.NewBig257().SetString(actualOuputData.Amount.String(), 10) - if !success { - return nil, xerrors.Errorf("Failed to parse amount to Big257: %s", actualOuputData.Amount.String()) - } + for i, strategyAndMultiplier := range outputRewardData.StrategiesAndMultipliers { + startTimestamp := time.Unix(int64(outputRewardData.StartTimestamp), 0) + endTimestamp := startTimestamp.Add(time.Duration(outputRewardData.Duration) * time.Second) multiplierBig, success := numbers.NewBig257().SetString(strategyAndMultiplier.Multiplier.String(), 10) if !success { - return nil, xerrors.Errorf("Failed to parse multiplier to Big257: %s", actualOuputData.Amount.String()) + return nil, xerrors.Errorf("Failed to parse multiplier to Big257: %s", strategyAndMultiplier.Multiplier.String()) } - rewardSubmission := &OperatorDirectedRewardSubmission{ - Avs: strings.ToLower(arguments[0].Value.(string)), - RewardHash: strings.ToLower(arguments[2].Value.(string)), - Token: strings.ToLower(actualOuputData.Token), - Amount: amountBig.String(), - Strategy: strategyAndMultiplier.Strategy, - Multiplier: multiplierBig.String(), - StartTimestamp: &startTimestamp, - EndTimestamp: &endTimestamp, - Duration: actualOuputData.Duration, - BlockNumber: log.BlockNumber, - TransactionHash: log.TransactionHash, - LogIndex: log.LogIndex, - StrategyIndex: uint64(i), + for j, operatorReward := range outputRewardData.OperatorRewards { + amountBig, success := numbers.NewBig257().SetString(operatorReward.Amount.String(), 10) + if !success { + return nil, xerrors.Errorf("Failed to parse amount to Big257: %s", operatorReward.Amount.String()) + } + + rewardSubmission := &OperatorDirectedRewardSubmission{ + Avs: strings.ToLower(arguments[1].Value.(string)), + RewardHash: strings.ToLower(arguments[2].Value.(string)), + Token: strings.ToLower(outputRewardData.Token), + Operator: strings.ToLower(operatorReward.Operator), + OperatorIndex: uint64(j), + Amount: amountBig.String(), + Strategy: strings.ToLower(strategyAndMultiplier.Strategy), + StrategyIndex: uint64(i), + Multiplier: multiplierBig.String(), + StartTimestamp: &startTimestamp, + EndTimestamp: &endTimestamp, + Duration: outputRewardData.Duration, + BlockNumber: log.BlockNumber, + TransactionHash: log.TransactionHash, + LogIndex: log.LogIndex, + } + + rewardSubmissions = append(rewardSubmissions, rewardSubmission) } - rewardSubmissions = append(rewardSubmissions, rewardSubmission) } return rewardSubmissions, nil From 297a807679ad07ec10141435ce25ce22acf885e0 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 15 Nov 2024 19:45:29 -0500 Subject: [PATCH 09/55] migration --- .../up.go | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go diff --git a/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go new file mode 100644 index 00000000..1aea82de --- /dev/null +++ b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go @@ -0,0 +1,41 @@ +package _202411151931_operatorDirectedRewardSubmissions + +import ( + "database/sql" + + "gorm.io/gorm" +) + +type Migration struct { +} + +func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { + query := ` + create table if not exists operator_directed_reward_submissions ( + avs varchar not null, + reward_hash varchar not null, + token varchar not null, + operator varchar not null, + operator_index integer not null, + amount numeric not null, + strategy varchar not null, + strategy_index integer not null, + multiplier numeric(78) not null, + start_timestamp timestamp(6) not null, + end_timestamp timestamp(6) not null, + duration bigint not null, + block_number bigint not null, + transaction_hash varchar not null, + log_index bigint not null, + unique(transaction_hash, log_index, block_number, reward_hash, strategy_index, operator_index) + ); + ` + if err := grm.Exec(query).Error; err != nil { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202411151931_operatorDirectedRewardSubmissions" +} From 5531125bb6c22f67a64a8252284e8095608138b6 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 15 Nov 2024 19:51:57 -0500 Subject: [PATCH 10/55] migrator --- .../operatorDirectedRewardSubmissions.go | 18 ++++++++---------- .../up.go | 3 ++- pkg/postgres/migrations/migrator.go | 2 ++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index cd8eaf40..055f649e 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -8,14 +8,12 @@ import ( "strings" "time" - "github.com/Layr-Labs/sidecar/pkg/storage" - "github.com/Layr-Labs/sidecar/pkg/types/numbers" - "github.com/Layr-Labs/sidecar/pkg/utils" - "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/eigenState/base" "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" "github.com/Layr-Labs/sidecar/pkg/eigenState/types" + "github.com/Layr-Labs/sidecar/pkg/storage" + "github.com/Layr-Labs/sidecar/pkg/types/numbers" "go.uber.org/zap" "golang.org/x/xerrors" "gorm.io/gorm" @@ -287,28 +285,28 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) CommitFinalState(blockNumber } // GenerateStateRoot generates the state root for the given block number using the results of the state changes. -func (odrs *OperatorDirectedRewardSubmissionsModel) GenerateStateRoot(blockNumber uint64) (types.StateRoot, error) { +func (odrs *OperatorDirectedRewardSubmissionsModel) GenerateStateRoot(blockNumber uint64) ([]byte, error) { inserts, err := odrs.prepareState(blockNumber) if err != nil { - return "", err + return nil, err } inputs := odrs.sortValuesForMerkleTree(inserts) if len(inputs) == 0 { - return "", nil + return nil, nil } - fullTree, err := odrs.MerkleizeState(blockNumber, inputs) + fullTree, err := odrs.MerkleizeEigenState(blockNumber, inputs) if err != nil { odrs.logger.Sugar().Errorw("Failed to create merkle tree", zap.Error(err), zap.Uint64("blockNumber", blockNumber), zap.Any("inputs", inputs), ) - return "", err + return nil, err } - return types.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil + return fullTree.Root(), nil } func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(submissions []*OperatorDirectedRewardSubmission) []*base.MerkleTreeInput { diff --git a/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go index 1aea82de..499ce10c 100644 --- a/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go +++ b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go @@ -2,6 +2,7 @@ package _202411151931_operatorDirectedRewardSubmissions 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_directed_reward_submissions ( avs varchar not null, diff --git a/pkg/postgres/migrations/migrator.go b/pkg/postgres/migrations/migrator.go index 1bc7cca3..802fbe64 100644 --- a/pkg/postgres/migrations/migrator.go +++ b/pkg/postgres/migrations/migrator.go @@ -33,6 +33,7 @@ import ( _202411120947_disabledDistributionRoots "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411120947_disabledDistributionRoots" _202411130953_addHashColumns "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411130953_addHashColumns" _202411131200_eigenStateModelConstraints "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411131200_eigenStateModelConstraints" + _202411151931_operatorDirectedRewardSubmissions "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions" _202411191947_cleanupUnusedTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191947_cleanupUnusedTables" _202412021311_stakerOperatorTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412021311_stakerOperatorTables" _202412061553_addBlockNumberIndexes "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412061553_addBlockNumberIndexes" @@ -109,6 +110,7 @@ func (m *Migrator) MigrateAll() error { &_202411120947_disabledDistributionRoots.Migration{}, &_202411130953_addHashColumns.Migration{}, &_202411131200_eigenStateModelConstraints.Migration{}, + &_202411151931_operatorDirectedRewardSubmissions.Migration{}, &_202411191947_cleanupUnusedTables.Migration{}, &_202412021311_stakerOperatorTables.Migration{}, &_202412061553_addBlockNumberIndexes.Migration{}, From bab35c26d8e7f3982e6ea60c84205a9d72aa3175 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 19 Nov 2024 12:18:53 -0500 Subject: [PATCH 11/55] test: operator directed rewards unit test --- .../operatorDirectedRewardSubmissions_test.go | 465 ++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go new file mode 100644 index 00000000..8f47ead8 --- /dev/null +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go @@ -0,0 +1,465 @@ +package operatorDirectedRewardSubmissions + +import ( + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/Layr-Labs/sidecar/pkg/postgres" + "github.com/Layr-Labs/sidecar/pkg/storage" + + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/logger" + "github.com/Layr-Labs/sidecar/internal/tests" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "gorm.io/gorm" +) + +func setup() ( + string, + *gorm.DB, + *zap.Logger, + *config.Config, + error, +) { + cfg := config.NewConfig() + cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() + + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true}) + + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, l) + if err != nil { + return dbname, nil, nil, nil, err + } + + return dbname, grm, l, cfg, nil +} + +func teardown(model *OperatorDirectedRewardSubmissionsModel) { + queries := []string{ + `truncate table operator_directed_reward_submissions`, + `truncate table blocks cascade`, + } + for _, query := range queries { + res := model.DB.Exec(query) + if res.Error != nil { + fmt.Printf("Failed to run query: %v\n", res.Error) + } + } +} + +func createBlock(model *OperatorDirectedRewardSubmissionsModel, blockNumber uint64) error { + block := &storage.Block{ + Number: blockNumber, + Hash: "some hash", + BlockTime: time.Now().Add(time.Hour * time.Duration(blockNumber)), + } + res := model.DB.Model(&storage.Block{}).Create(block) + if res.Error != nil { + return res.Error + } + return nil +} + +func Test_OperatorDirectedRewardSubmissions(t *testing.T) { + _, grm, l, cfg, err := setup() + + if err != nil { + t.Fatal(err) + } + + t.Run("Test each event type", func(t *testing.T) { + esm := stateManager.NewEigenStateManager(l, grm) + + model, err := NewOperatorDirectedRewardSubmissionsModel(esm, grm, l, cfg) + + submissionCounter := 0 + + t.Run("Handle an operator directed reward submission", func(t *testing.T) { + blockNumber := uint64(102) + + if err := createBlock(model, blockNumber); err != nil { + t.Fatal(err) + } + + log := &storage.TransactionLog{ + TransactionHash: "some hash", + TransactionIndex: big.NewInt(100).Uint64(), + BlockNumber: blockNumber, + Address: cfg.GetContractsMapForChain().RewardsCoordinator, + Arguments: `[{"Name": "caller", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "avs", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "operatorDirectedRewardsSubmissionHash", "Type": "bytes32", "Value": "0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": false}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,(address,uint256)[],uint32,uint32,string)", "Value": null, "Indexed": false}]`, + EventName: "OperatorDirectedAVSRewardsSubmissionCreated", + LogIndex: big.NewInt(12).Uint64(), + OutputData: `{"submissionNonce": 0, "operatorDirectedRewardsSubmission": {"token": "0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24", "operatorRewards": [{"operator": "0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "amount": 20000000000000000000000}], "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}]}}`, + } + + err = model.SetupStateForBlock(blockNumber) + assert.Nil(t, err) + + isInteresting := model.IsInterestingLog(log) + assert.True(t, isInteresting) + + change, err := model.HandleStateChange(log) + assert.Nil(t, err) + assert.NotNil(t, change) + + strategiesAndMultipliers := []struct { + Strategy string + Multiplier string + }{ + {"0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "1000000000000000000"}, + } + + operatorRewards := []struct { + Operator string + Amount string + }{ + {"0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "20000000000000000000000"}, + } + + typedChange := change.([]*OperatorDirectedRewardSubmission) + assert.Equal(t, len(strategiesAndMultipliers)*len(operatorRewards), len(typedChange)) + + for i, submission := range typedChange { + assert.Equal(t, strings.ToLower("0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101"), strings.ToLower(submission.Avs)) + assert.Equal(t, strings.ToLower("0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24"), strings.ToLower(submission.Token)) + assert.Equal(t, strings.ToLower("0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9"), strings.ToLower(submission.RewardHash)) + assert.Equal(t, uint64(2419200), submission.Duration) + assert.Equal(t, int64(1725494400), submission.StartTimestamp.Unix()) + assert.Equal(t, int64(2419200+1725494400), submission.EndTimestamp.Unix()) + + assert.Equal(t, strings.ToLower(strategiesAndMultipliers[i].Strategy), strings.ToLower(submission.Strategy)) + assert.Equal(t, strategiesAndMultipliers[i].Multiplier, submission.Multiplier) + + assert.Equal(t, strings.ToLower(operatorRewards[i].Operator), strings.ToLower(submission.Operator)) + assert.Equal(t, operatorRewards[i].Amount, submission.Amount) + } + + err = model.CommitFinalState(blockNumber) + assert.Nil(t, err) + + rewards := make([]*OperatorDirectedRewardSubmission, 0) + query := `select * from operator_directed_reward_submissions where block_number = ?` + res := model.DB.Raw(query, blockNumber).Scan(&rewards) + assert.Nil(t, res.Error) + assert.Equal(t, len(strategiesAndMultipliers)*len(operatorRewards), len(rewards)) + + submissionCounter += len(strategiesAndMultipliers) * len(operatorRewards) + + stateRoot, err := model.GenerateStateRoot(blockNumber) + assert.Nil(t, err) + assert.NotNil(t, stateRoot) + assert.True(t, len(stateRoot) > 0) + + t.Cleanup(func() { + teardown(model) + }) + }) + + t.Cleanup(func() { + teardown(model) + }) + }) + + // t.Run("multi-block test", func(t *testing.T) { + // esm := stateManager.NewEigenStateManager(l, grm) + + // model, err := NewOperatorDirectedRewardSubmissionsModel(esm, grm, l, cfg) + // assert.Nil(t, err) + + // blockNumber := uint64(100) + // // create first block + // if err := createBlock(model, blockNumber); err != nil { + // t.Fatal(err) + // } + + // // First RangePaymentCreated + // log := &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000000"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x58959fBe6661daEA647E20dF7c6d2c7F0d2215fB"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, + // EventName: "RangePaymentCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rangePayment": {"token": "0x94373a4919b3240d86ea41593d5eba789fef3848", "amount": 50000000000000000000, "duration": 2419200, "startTimestamp": 1712188800, "strategiesAndMultipliers": [{"strategy": "0x3c28437e610fb099cc3d6de4d9c707dfacd308ae", "multiplier": 1000000000000000000}, {"strategy": "0x3cb1fd19cfb178c1098f2fc1e11090a0642b2314", "multiplier": 2000000000000000000}, {"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 3000000000000000000}, {"strategy": "0x6dc6ce589f852f96ac86cb160ab0b15b9f56dedd", "multiplier": 4500000000000000000}, {"strategy": "0x87f6c7d24b109919eb38295e3f8298425e6331d9", "multiplier": 500000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 8000000000000000000}, {"strategy": "0xdccf401fd121d8c542e96bc1d0078884422afad2", "multiplier": 5000000000000000000}]}}`, + // } + + // err = model.SetupStateForBlock(blockNumber) + // assert.Nil(t, err) + + // isInteresting := model.IsInterestingLog(log) + // assert.True(t, isInteresting) + + // change, err := model.HandleStateChange(log) + // assert.Nil(t, err) + // assert.NotNil(t, change) + // typedChange := change.([]*OperatorDirectedRewardSubmission) + + // err = model.CommitFinalState(blockNumber) + // assert.Nil(t, err) + + // query := `select count(*) from reward_submissions where block_number = ?` + // var count int + // res := model.DB.Raw(query, blockNumber).Scan(&count) + + // assert.Nil(t, res.Error) + // assert.Equal(t, len(typedChange), count) + + // stateRoot, err := model.GenerateStateRoot(blockNumber) + // assert.Nil(t, err) + // assert.NotNil(t, stateRoot) + // assert.True(t, len(stateRoot) > 0) + + // // ----- + + // blockNumber = uint64(101) + // // create block + // if err := createBlock(model, blockNumber); err != nil { + // t.Fatal(err) + // } + + // // Second log: RangePaymentForAllCreated + // log = &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000001"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x69193C881C4BfA9015F1E9B2631e31238BedB93e"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, + // EventName: "RangePaymentForAllCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rangePayment": {"token": "0x3f1c547b21f65e10480de3ad8e19faac46c95034", "amount": 11000000000000000000, "duration": 2419200, "startTimestamp": 1713398400, "strategiesAndMultipliers": [{"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 1000000000000000000}, {"strategy": "0x7fa77c321bf66e42eabc9b10129304f7f90c5585", "multiplier": 2000000000000000000}, {"strategy": "0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0", "multiplier": 3000000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 4500000000000000000}]}}`, + // } + + // err = model.SetupStateForBlock(blockNumber) + // assert.Nil(t, err) + + // isInteresting = model.IsInterestingLog(log) + // assert.True(t, isInteresting) + + // change, err = model.HandleStateChange(log) + // assert.Nil(t, err) + // assert.NotNil(t, change) + // typedChange = change.([]*OperatorDirectedRewardSubmission) + + // err = model.CommitFinalState(blockNumber) + // assert.Nil(t, err) + + // stateRoot, err = model.GenerateStateRoot(blockNumber) + // assert.Nil(t, err) + // assert.NotNil(t, stateRoot) + // assert.True(t, len(stateRoot) > 0) + + // query = `select count(*) from reward_submissions where block_number = ?` + // res = model.DB.Raw(query, blockNumber).Scan(&count) + + // assert.Nil(t, res.Error) + // assert.Equal(t, len(typedChange), count) + + // // ----- + + // blockNumber = uint64(102) + // // create block + // if err := createBlock(model, blockNumber); err != nil { + // t.Fatal(err) + // } + + // log = &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, + // EventName: "AVSRewardsSubmissionCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rewardsSubmission": {"token": "0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24", "amount": 10000000000000000000000, "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}]}}`, + // } + + // err = model.SetupStateForBlock(blockNumber) + // assert.Nil(t, err) + + // isInteresting = model.IsInterestingLog(log) + // assert.True(t, isInteresting) + + // change, err = model.HandleStateChange(log) + // assert.Nil(t, err) + // assert.NotNil(t, change) + // typedChange = change.([]*OperatorDirectedRewardSubmission) + + // err = model.CommitFinalState(blockNumber) + // assert.Nil(t, err) + + // stateRoot, err = model.GenerateStateRoot(blockNumber) + // assert.Nil(t, err) + // assert.NotNil(t, stateRoot) + // assert.True(t, len(stateRoot) > 0) + + // query = `select count(*) from reward_submissions where block_number = ?` + // res = model.DB.Raw(query, blockNumber).Scan(&count) + + // assert.Nil(t, res.Error) + // assert.Equal(t, len(typedChange), count) + + // // ----- + + // blockNumber = uint64(103) + // // create block + // if err := createBlock(model, blockNumber); err != nil { + // t.Fatal(err) + // } + + // log = &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x66ae7d7c4d492e4e012b95977f14715b74498bc5", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 3, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0x99ebccb0f68eedbf3dff04c7773d6ff94fc439e0eebdd80918b3785ae8099f96", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, + // EventName: "RewardsSubmissionForAllCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rewardsSubmission": {"token": "0x554c393923c753d146aa34608523ad7946b61662", "amount": 10000000000000000000, "duration": 1814400, "startTimestamp": 1717632000, "strategiesAndMultipliers": [{"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 1000000000000000000}, {"strategy": "0xdccf401fd121d8c542e96bc1d0078884422afad2", "multiplier": 2000000000000000000}]}}`, + // } + + // err = model.SetupStateForBlock(blockNumber) + // assert.Nil(t, err) + + // isInteresting = model.IsInterestingLog(log) + // assert.True(t, isInteresting) + + // change, err = model.HandleStateChange(log) + // assert.Nil(t, err) + // assert.NotNil(t, change) + // typedChange = change.([]*OperatorDirectedRewardSubmission) + + // err = model.CommitFinalState(blockNumber) + // assert.Nil(t, err) + + // stateRoot, err = model.GenerateStateRoot(blockNumber) + // assert.Nil(t, err) + // assert.NotNil(t, stateRoot) + // assert.True(t, len(stateRoot) > 0) + + // query = `select count(*) from reward_submissions where block_number = ?` + // res = model.DB.Raw(query, blockNumber).Scan(&count) + + // assert.Nil(t, res.Error) + // assert.Equal(t, len(typedChange), count) + + // t.Cleanup(func() { + // teardown(model) + // }) + // }) + + // t.Run("single block, multiple events", func(t *testing.T) { + // esm := stateManager.NewEigenStateManager(l, grm) + + // model, err := NewOperatorDirectedRewardSubmissionsModel(esm, grm, l, cfg) + // assert.Nil(t, err) + + // submissionCounter := 0 + + // blockNumber := uint64(100) + // // create first block + // if err := createBlock(model, blockNumber); err != nil { + // t.Fatal(err) + // } + + // err = model.SetupStateForBlock(blockNumber) + // assert.Nil(t, err) + + // handleLog := func(log *storage.TransactionLog) { + // isInteresting := model.IsInterestingLog(log) + // assert.True(t, isInteresting) + + // change, err := model.HandleStateChange(log) + // assert.Nil(t, err) + // assert.NotNil(t, change) + // typedChange := change.([]*OperatorDirectedRewardSubmission) + + // submissionCounter += len(typedChange) + // } + + // // First RangePaymentCreated + // rangePaymentCreatedLog := &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000000"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x58959fBe6661daEA647E20dF7c6d2c7F0d2215fB"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, + // EventName: "RangePaymentCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rangePayment": {"token": "0x94373a4919b3240d86ea41593d5eba789fef3848", "amount": 50000000000000000000, "duration": 2419200, "startTimestamp": 1712188800, "strategiesAndMultipliers": [{"strategy": "0x3c28437e610fb099cc3d6de4d9c707dfacd308ae", "multiplier": 1000000000000000000}, {"strategy": "0x3cb1fd19cfb178c1098f2fc1e11090a0642b2314", "multiplier": 2000000000000000000}, {"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 3000000000000000000}, {"strategy": "0x6dc6ce589f852f96ac86cb160ab0b15b9f56dedd", "multiplier": 4500000000000000000}, {"strategy": "0x87f6c7d24b109919eb38295e3f8298425e6331d9", "multiplier": 500000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 8000000000000000000}, {"strategy": "0xdccf401fd121d8c542e96bc1d0078884422afad2", "multiplier": 5000000000000000000}]}}`, + // } + // handleLog(rangePaymentCreatedLog) + + // rangePaymentForAllLog := &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000001"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x69193C881C4BfA9015F1E9B2631e31238BedB93e"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, + // EventName: "RangePaymentForAllCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rangePayment": {"token": "0x3f1c547b21f65e10480de3ad8e19faac46c95034", "amount": 11000000000000000000, "duration": 2419200, "startTimestamp": 1713398400, "strategiesAndMultipliers": [{"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 1000000000000000000}, {"strategy": "0x7fa77c321bf66e42eabc9b10129304f7f90c5585", "multiplier": 2000000000000000000}, {"strategy": "0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0", "multiplier": 3000000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 4500000000000000000}]}}`, + // } + // handleLog(rangePaymentForAllLog) + + // rewardSubmissionCreatedLog := &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, + // EventName: "AVSRewardsSubmissionCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rewardsSubmission": {"token": "0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24", "amount": 10000000000000000000000, "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}]}}`, + // } + // handleLog(rewardSubmissionCreatedLog) + + // rewardsForAllLog := &storage.TransactionLog{ + // TransactionHash: "some hash", + // TransactionIndex: big.NewInt(100).Uint64(), + // BlockNumber: blockNumber, + // Address: cfg.GetContractsMapForChain().RewardsCoordinator, + // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x002b273d4459b5636f971cc7be6443e95517d394", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0xcb5e9dfd219cc5500e88a349d8f072b77241475b3266a0f2c6cf29b1e09d3211", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, + // EventName: "RewardsSubmissionForAllCreated", + // LogIndex: big.NewInt(12).Uint64(), + // OutputData: `{"rewardsSubmission": {"token": "0xdeeeee2b48c121e6728ed95c860e296177849932", "amount": 1000000000000000000000000000, "duration": 5443200, "startTimestamp": 1717027200, "strategiesAndMultipliers": [{"strategy": "0x05037a81bd7b4c9e0f7b430f1f2a22c31a2fd943", "multiplier": 1000000000000000000}, {"strategy": "0x31b6f59e1627cefc9fa174ad03859fc337666af7", "multiplier": 1000000000000000000}, {"strategy": "0x3a8fbdf9e77dfc25d09741f51d3e181b25d0c4e0", "multiplier": 1000000000000000000}, {"strategy": "0x46281e3b7fdcacdba44cadf069a94a588fd4c6ef", "multiplier": 1000000000000000000}, {"strategy": "0x70eb4d3c164a6b4a5f908d4fbb5a9caffb66bab6", "multiplier": 1000000000000000000}, {"strategy": "0x7673a47463f80c6a3553db9e54c8cdcd5313d0ac", "multiplier": 1000000000000000000}, {"strategy": "0x7d704507b76571a51d9cae8addabbfd0ba0e63d3", "multiplier": 1000000000000000000}, {"strategy": "0x80528d6e9a2babfc766965e0e26d5ab08d9cfaf9", "multiplier": 1000000000000000000}, {"strategy": "0x9281ff96637710cd9a5cacce9c6fad8c9f54631c", "multiplier": 1000000000000000000}, {"strategy": "0xaccc5a86732be85b5012e8614af237801636f8e5", "multiplier": 1000000000000000000}, {"strategy": "0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0", "multiplier": 1000000000000000000}]}}`, + // } + // handleLog(rewardsForAllLog) + + // // check that we're starting with 0 rows + // query := `select count(*) from reward_submissions` + // var count int + // res := model.DB.Raw(query).Scan(&count) + // assert.Nil(t, res.Error) + // assert.Equal(t, 0, count) + + // // Commit the final state + // err = model.CommitFinalState(blockNumber) + // assert.Nil(t, err) + + // // Generate the stateroot + // stateRoot, err := model.GenerateStateRoot(blockNumber) + // assert.Nil(t, err) + // assert.NotNil(t, stateRoot) + // assert.True(t, len(stateRoot) > 0) + + // // Verify we have the right number of rows + // query = `select count(*) from reward_submissions where block_number = ?` + // res = model.DB.Raw(query, blockNumber).Scan(&count) + // assert.Nil(t, res.Error) + // assert.Equal(t, submissionCounter, count) + + // t.Cleanup(func() { + // teardown(model) + // }) + // }) + + t.Cleanup(func() { + // postgres.TeardownTestDatabase(dbName, cfg, grm, l) + }) +} From b7b2823de3b9bf06c667ae36ed69dfba0f4ac294 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 19 Nov 2024 12:35:48 -0500 Subject: [PATCH 12/55] updated test --- .../operatorDirectedRewardSubmissions_test.go | 310 +----------------- 1 file changed, 9 insertions(+), 301 deletions(-) diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go index 8f47ead8..8021e6b5 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go @@ -94,7 +94,7 @@ func Test_OperatorDirectedRewardSubmissions(t *testing.T) { Arguments: `[{"Name": "caller", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "avs", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "operatorDirectedRewardsSubmissionHash", "Type": "bytes32", "Value": "0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": false}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,(address,uint256)[],uint32,uint32,string)", "Value": null, "Indexed": false}]`, EventName: "OperatorDirectedAVSRewardsSubmissionCreated", LogIndex: big.NewInt(12).Uint64(), - OutputData: `{"submissionNonce": 0, "operatorDirectedRewardsSubmission": {"token": "0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24", "operatorRewards": [{"operator": "0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "amount": 20000000000000000000000}], "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}]}}`, + OutputData: `{"submissionNonce": 0, "operatorDirectedRewardsSubmission": {"token": "0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24", "operatorRewards": [{"operator": "0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "amount": 30000000000000000000000}, {"operator": "0xF50Cba7a66b5E615587157e43286DaA7aF94009e", "amount": 40000000000000000000000}], "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}, {"strategy": "0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc", "multiplier": 2000000000000000000}]}}`, } err = model.SetupStateForBlock(blockNumber) @@ -112,19 +112,21 @@ func Test_OperatorDirectedRewardSubmissions(t *testing.T) { Multiplier string }{ {"0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "1000000000000000000"}, + {"0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc", "2000000000000000000"}, } operatorRewards := []struct { Operator string Amount string }{ - {"0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "20000000000000000000000"}, + {"0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "30000000000000000000000"}, + {"0xF50Cba7a66b5E615587157e43286DaA7aF94009e", "40000000000000000000000"}, } typedChange := change.([]*OperatorDirectedRewardSubmission) assert.Equal(t, len(strategiesAndMultipliers)*len(operatorRewards), len(typedChange)) - for i, submission := range typedChange { + for _, submission := range typedChange { assert.Equal(t, strings.ToLower("0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101"), strings.ToLower(submission.Avs)) assert.Equal(t, strings.ToLower("0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24"), strings.ToLower(submission.Token)) assert.Equal(t, strings.ToLower("0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9"), strings.ToLower(submission.RewardHash)) @@ -132,11 +134,11 @@ func Test_OperatorDirectedRewardSubmissions(t *testing.T) { assert.Equal(t, int64(1725494400), submission.StartTimestamp.Unix()) assert.Equal(t, int64(2419200+1725494400), submission.EndTimestamp.Unix()) - assert.Equal(t, strings.ToLower(strategiesAndMultipliers[i].Strategy), strings.ToLower(submission.Strategy)) - assert.Equal(t, strategiesAndMultipliers[i].Multiplier, submission.Multiplier) + assert.Equal(t, strings.ToLower(strategiesAndMultipliers[submission.StrategyIndex].Strategy), strings.ToLower(submission.Strategy)) + assert.Equal(t, strategiesAndMultipliers[submission.StrategyIndex].Multiplier, submission.Multiplier) - assert.Equal(t, strings.ToLower(operatorRewards[i].Operator), strings.ToLower(submission.Operator)) - assert.Equal(t, operatorRewards[i].Amount, submission.Amount) + assert.Equal(t, strings.ToLower(operatorRewards[submission.OperatorIndex].Operator), strings.ToLower(submission.Operator)) + assert.Equal(t, operatorRewards[submission.OperatorIndex].Amount, submission.Amount) } err = model.CommitFinalState(blockNumber) @@ -165,300 +167,6 @@ func Test_OperatorDirectedRewardSubmissions(t *testing.T) { }) }) - // t.Run("multi-block test", func(t *testing.T) { - // esm := stateManager.NewEigenStateManager(l, grm) - - // model, err := NewOperatorDirectedRewardSubmissionsModel(esm, grm, l, cfg) - // assert.Nil(t, err) - - // blockNumber := uint64(100) - // // create first block - // if err := createBlock(model, blockNumber); err != nil { - // t.Fatal(err) - // } - - // // First RangePaymentCreated - // log := &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000000"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x58959fBe6661daEA647E20dF7c6d2c7F0d2215fB"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, - // EventName: "RangePaymentCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rangePayment": {"token": "0x94373a4919b3240d86ea41593d5eba789fef3848", "amount": 50000000000000000000, "duration": 2419200, "startTimestamp": 1712188800, "strategiesAndMultipliers": [{"strategy": "0x3c28437e610fb099cc3d6de4d9c707dfacd308ae", "multiplier": 1000000000000000000}, {"strategy": "0x3cb1fd19cfb178c1098f2fc1e11090a0642b2314", "multiplier": 2000000000000000000}, {"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 3000000000000000000}, {"strategy": "0x6dc6ce589f852f96ac86cb160ab0b15b9f56dedd", "multiplier": 4500000000000000000}, {"strategy": "0x87f6c7d24b109919eb38295e3f8298425e6331d9", "multiplier": 500000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 8000000000000000000}, {"strategy": "0xdccf401fd121d8c542e96bc1d0078884422afad2", "multiplier": 5000000000000000000}]}}`, - // } - - // err = model.SetupStateForBlock(blockNumber) - // assert.Nil(t, err) - - // isInteresting := model.IsInterestingLog(log) - // assert.True(t, isInteresting) - - // change, err := model.HandleStateChange(log) - // assert.Nil(t, err) - // assert.NotNil(t, change) - // typedChange := change.([]*OperatorDirectedRewardSubmission) - - // err = model.CommitFinalState(blockNumber) - // assert.Nil(t, err) - - // query := `select count(*) from reward_submissions where block_number = ?` - // var count int - // res := model.DB.Raw(query, blockNumber).Scan(&count) - - // assert.Nil(t, res.Error) - // assert.Equal(t, len(typedChange), count) - - // stateRoot, err := model.GenerateStateRoot(blockNumber) - // assert.Nil(t, err) - // assert.NotNil(t, stateRoot) - // assert.True(t, len(stateRoot) > 0) - - // // ----- - - // blockNumber = uint64(101) - // // create block - // if err := createBlock(model, blockNumber); err != nil { - // t.Fatal(err) - // } - - // // Second log: RangePaymentForAllCreated - // log = &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000001"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x69193C881C4BfA9015F1E9B2631e31238BedB93e"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, - // EventName: "RangePaymentForAllCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rangePayment": {"token": "0x3f1c547b21f65e10480de3ad8e19faac46c95034", "amount": 11000000000000000000, "duration": 2419200, "startTimestamp": 1713398400, "strategiesAndMultipliers": [{"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 1000000000000000000}, {"strategy": "0x7fa77c321bf66e42eabc9b10129304f7f90c5585", "multiplier": 2000000000000000000}, {"strategy": "0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0", "multiplier": 3000000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 4500000000000000000}]}}`, - // } - - // err = model.SetupStateForBlock(blockNumber) - // assert.Nil(t, err) - - // isInteresting = model.IsInterestingLog(log) - // assert.True(t, isInteresting) - - // change, err = model.HandleStateChange(log) - // assert.Nil(t, err) - // assert.NotNil(t, change) - // typedChange = change.([]*OperatorDirectedRewardSubmission) - - // err = model.CommitFinalState(blockNumber) - // assert.Nil(t, err) - - // stateRoot, err = model.GenerateStateRoot(blockNumber) - // assert.Nil(t, err) - // assert.NotNil(t, stateRoot) - // assert.True(t, len(stateRoot) > 0) - - // query = `select count(*) from reward_submissions where block_number = ?` - // res = model.DB.Raw(query, blockNumber).Scan(&count) - - // assert.Nil(t, res.Error) - // assert.Equal(t, len(typedChange), count) - - // // ----- - - // blockNumber = uint64(102) - // // create block - // if err := createBlock(model, blockNumber); err != nil { - // t.Fatal(err) - // } - - // log = &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, - // EventName: "AVSRewardsSubmissionCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rewardsSubmission": {"token": "0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24", "amount": 10000000000000000000000, "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}]}}`, - // } - - // err = model.SetupStateForBlock(blockNumber) - // assert.Nil(t, err) - - // isInteresting = model.IsInterestingLog(log) - // assert.True(t, isInteresting) - - // change, err = model.HandleStateChange(log) - // assert.Nil(t, err) - // assert.NotNil(t, change) - // typedChange = change.([]*OperatorDirectedRewardSubmission) - - // err = model.CommitFinalState(blockNumber) - // assert.Nil(t, err) - - // stateRoot, err = model.GenerateStateRoot(blockNumber) - // assert.Nil(t, err) - // assert.NotNil(t, stateRoot) - // assert.True(t, len(stateRoot) > 0) - - // query = `select count(*) from reward_submissions where block_number = ?` - // res = model.DB.Raw(query, blockNumber).Scan(&count) - - // assert.Nil(t, res.Error) - // assert.Equal(t, len(typedChange), count) - - // // ----- - - // blockNumber = uint64(103) - // // create block - // if err := createBlock(model, blockNumber); err != nil { - // t.Fatal(err) - // } - - // log = &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x66ae7d7c4d492e4e012b95977f14715b74498bc5", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 3, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0x99ebccb0f68eedbf3dff04c7773d6ff94fc439e0eebdd80918b3785ae8099f96", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, - // EventName: "RewardsSubmissionForAllCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rewardsSubmission": {"token": "0x554c393923c753d146aa34608523ad7946b61662", "amount": 10000000000000000000, "duration": 1814400, "startTimestamp": 1717632000, "strategiesAndMultipliers": [{"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 1000000000000000000}, {"strategy": "0xdccf401fd121d8c542e96bc1d0078884422afad2", "multiplier": 2000000000000000000}]}}`, - // } - - // err = model.SetupStateForBlock(blockNumber) - // assert.Nil(t, err) - - // isInteresting = model.IsInterestingLog(log) - // assert.True(t, isInteresting) - - // change, err = model.HandleStateChange(log) - // assert.Nil(t, err) - // assert.NotNil(t, change) - // typedChange = change.([]*OperatorDirectedRewardSubmission) - - // err = model.CommitFinalState(blockNumber) - // assert.Nil(t, err) - - // stateRoot, err = model.GenerateStateRoot(blockNumber) - // assert.Nil(t, err) - // assert.NotNil(t, stateRoot) - // assert.True(t, len(stateRoot) > 0) - - // query = `select count(*) from reward_submissions where block_number = ?` - // res = model.DB.Raw(query, blockNumber).Scan(&count) - - // assert.Nil(t, res.Error) - // assert.Equal(t, len(typedChange), count) - - // t.Cleanup(func() { - // teardown(model) - // }) - // }) - - // t.Run("single block, multiple events", func(t *testing.T) { - // esm := stateManager.NewEigenStateManager(l, grm) - - // model, err := NewOperatorDirectedRewardSubmissionsModel(esm, grm, l, cfg) - // assert.Nil(t, err) - - // submissionCounter := 0 - - // blockNumber := uint64(100) - // // create first block - // if err := createBlock(model, blockNumber); err != nil { - // t.Fatal(err) - // } - - // err = model.SetupStateForBlock(blockNumber) - // assert.Nil(t, err) - - // handleLog := func(log *storage.TransactionLog) { - // isInteresting := model.IsInterestingLog(log) - // assert.True(t, isInteresting) - - // change, err := model.HandleStateChange(log) - // assert.Nil(t, err) - // assert.NotNil(t, change) - // typedChange := change.([]*OperatorDirectedRewardSubmission) - - // submissionCounter += len(typedChange) - // } - - // // First RangePaymentCreated - // rangePaymentCreatedLog := &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000000"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x58959fBe6661daEA647E20dF7c6d2c7F0d2215fB"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, - // EventName: "RangePaymentCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rangePayment": {"token": "0x94373a4919b3240d86ea41593d5eba789fef3848", "amount": 50000000000000000000, "duration": 2419200, "startTimestamp": 1712188800, "strategiesAndMultipliers": [{"strategy": "0x3c28437e610fb099cc3d6de4d9c707dfacd308ae", "multiplier": 1000000000000000000}, {"strategy": "0x3cb1fd19cfb178c1098f2fc1e11090a0642b2314", "multiplier": 2000000000000000000}, {"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 3000000000000000000}, {"strategy": "0x6dc6ce589f852f96ac86cb160ab0b15b9f56dedd", "multiplier": 4500000000000000000}, {"strategy": "0x87f6c7d24b109919eb38295e3f8298425e6331d9", "multiplier": 500000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 8000000000000000000}, {"strategy": "0xdccf401fd121d8c542e96bc1d0078884422afad2", "multiplier": 5000000000000000000}]}}`, - // } - // handleLog(rangePaymentCreatedLog) - - // rangePaymentForAllLog := &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x00526A07855f743964F05CccAeCcf7a9E34847fF"}, {"Name": "paymentNonce", "Type": "uint256", "Value": "0x0000000000000000000000000000000000000001"}, {"Name": "rangePaymentHash", "Type": "bytes32", "Value": "0x69193C881C4BfA9015F1E9B2631e31238BedB93e"}, {"Name": "rangePayment", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": ""}]`, - // EventName: "RangePaymentForAllCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rangePayment": {"token": "0x3f1c547b21f65e10480de3ad8e19faac46c95034", "amount": 11000000000000000000, "duration": 2419200, "startTimestamp": 1713398400, "strategiesAndMultipliers": [{"strategy": "0x5c8b55722f421556a2aafb7a3ea63d4c3e514312", "multiplier": 1000000000000000000}, {"strategy": "0x7fa77c321bf66e42eabc9b10129304f7f90c5585", "multiplier": 2000000000000000000}, {"strategy": "0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0", "multiplier": 3000000000000000000}, {"strategy": "0xd523267698c81a372191136e477fdebfa33d9fb4", "multiplier": 4500000000000000000}]}}`, - // } - // handleLog(rangePaymentForAllLog) - - // rewardSubmissionCreatedLog := &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "avs", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0x7402669fb2c8a0cfe8108acb8a0070257c77ec6906ecb07d97c38e8a5ddc66a9", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, - // EventName: "AVSRewardsSubmissionCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rewardsSubmission": {"token": "0x0ddd9dc88e638aef6a8e42d0c98aaa6a48a98d24", "amount": 10000000000000000000000, "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}]}}`, - // } - // handleLog(rewardSubmissionCreatedLog) - - // rewardsForAllLog := &storage.TransactionLog{ - // TransactionHash: "some hash", - // TransactionIndex: big.NewInt(100).Uint64(), - // BlockNumber: blockNumber, - // Address: cfg.GetContractsMapForChain().RewardsCoordinator, - // Arguments: `[{"Name": "submitter", "Type": "address", "Value": "0x002b273d4459b5636f971cc7be6443e95517d394", "Indexed": true}, {"Name": "submissionNonce", "Type": "uint256", "Value": 0, "Indexed": true}, {"Name": "rewardsSubmissionHash", "Type": "bytes32", "Value": "0xcb5e9dfd219cc5500e88a349d8f072b77241475b3266a0f2c6cf29b1e09d3211", "Indexed": true}, {"Name": "rewardsSubmission", "Type": "((address,uint96)[],address,uint256,uint32,uint32)", "Value": null, "Indexed": false}]`, - // EventName: "RewardsSubmissionForAllCreated", - // LogIndex: big.NewInt(12).Uint64(), - // OutputData: `{"rewardsSubmission": {"token": "0xdeeeee2b48c121e6728ed95c860e296177849932", "amount": 1000000000000000000000000000, "duration": 5443200, "startTimestamp": 1717027200, "strategiesAndMultipliers": [{"strategy": "0x05037a81bd7b4c9e0f7b430f1f2a22c31a2fd943", "multiplier": 1000000000000000000}, {"strategy": "0x31b6f59e1627cefc9fa174ad03859fc337666af7", "multiplier": 1000000000000000000}, {"strategy": "0x3a8fbdf9e77dfc25d09741f51d3e181b25d0c4e0", "multiplier": 1000000000000000000}, {"strategy": "0x46281e3b7fdcacdba44cadf069a94a588fd4c6ef", "multiplier": 1000000000000000000}, {"strategy": "0x70eb4d3c164a6b4a5f908d4fbb5a9caffb66bab6", "multiplier": 1000000000000000000}, {"strategy": "0x7673a47463f80c6a3553db9e54c8cdcd5313d0ac", "multiplier": 1000000000000000000}, {"strategy": "0x7d704507b76571a51d9cae8addabbfd0ba0e63d3", "multiplier": 1000000000000000000}, {"strategy": "0x80528d6e9a2babfc766965e0e26d5ab08d9cfaf9", "multiplier": 1000000000000000000}, {"strategy": "0x9281ff96637710cd9a5cacce9c6fad8c9f54631c", "multiplier": 1000000000000000000}, {"strategy": "0xaccc5a86732be85b5012e8614af237801636f8e5", "multiplier": 1000000000000000000}, {"strategy": "0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0", "multiplier": 1000000000000000000}]}}`, - // } - // handleLog(rewardsForAllLog) - - // // check that we're starting with 0 rows - // query := `select count(*) from reward_submissions` - // var count int - // res := model.DB.Raw(query).Scan(&count) - // assert.Nil(t, res.Error) - // assert.Equal(t, 0, count) - - // // Commit the final state - // err = model.CommitFinalState(blockNumber) - // assert.Nil(t, err) - - // // Generate the stateroot - // stateRoot, err := model.GenerateStateRoot(blockNumber) - // assert.Nil(t, err) - // assert.NotNil(t, stateRoot) - // assert.True(t, len(stateRoot) > 0) - - // // Verify we have the right number of rows - // query = `select count(*) from reward_submissions where block_number = ?` - // res = model.DB.Raw(query, blockNumber).Scan(&count) - // assert.Nil(t, res.Error) - // assert.Equal(t, submissionCounter, count) - - // t.Cleanup(func() { - // teardown(model) - // }) - // }) - t.Cleanup(func() { // postgres.TeardownTestDatabase(dbName, cfg, grm, l) }) From 7e30d1b4303f74b148c7c732bf2090f25a3234fc Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 19 Nov 2024 15:43:59 -0500 Subject: [PATCH 13/55] operator avs split eigen model --- pkg/eigenState/eigenState.go | 5 + .../operatorAVSSplits/operatorAVSSplits.go | 279 ++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go diff --git a/pkg/eigenState/eigenState.go b/pkg/eigenState/eigenState.go index db339945..860617a3 100644 --- a/pkg/eigenState/eigenState.go +++ b/pkg/eigenState/eigenState.go @@ -4,6 +4,7 @@ import ( "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/eigenState/avsOperators" "github.com/Layr-Labs/sidecar/pkg/eigenState/disabledDistributionRoots" + "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorAVSSplits" "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorDirectedRewardSubmissions" "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorShares" "github.com/Layr-Labs/sidecar/pkg/eigenState/rewardSubmissions" @@ -53,5 +54,9 @@ func LoadEigenStateModels( l.Sugar().Errorw("Failed to create OperatorDirectedRewardSubmissionsModel", zap.Error(err)) return err } + if _, err := operatorAVSSplits.NewOperatorAVSSplitModel(sm, grm, l, cfg); err != nil { + l.Sugar().Errorw("Failed to create OperatorAVSSplitModel", zap.Error(err)) + return err + } return nil } diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go new file mode 100644 index 00000000..8115d2fb --- /dev/null +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go @@ -0,0 +1,279 @@ +package operatorAVSSplits + +import ( + "encoding/json" + "fmt" + "slices" + "sort" + "strings" + "time" + + "github.com/Layr-Labs/sidecar/pkg/storage" + "github.com/Layr-Labs/sidecar/pkg/utils" + + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/pkg/eigenState/base" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" + "github.com/Layr-Labs/sidecar/pkg/eigenState/types" + "go.uber.org/zap" + "golang.org/x/xerrors" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type OperatorAVSSplit struct { + Operator string + Avs string + ActivatedAt *time.Time + OldOperatorAVSSplitBips uint64 + NewOperatorAVSSplitBips uint64 + BlockNumber uint64 + TransactionHash string + LogIndex uint64 +} + +type OperatorAVSSplitModel struct { + base.BaseEigenState + StateTransitions types.StateTransitions[[]*OperatorAVSSplit] + DB *gorm.DB + Network config.Network + Environment config.Environment + logger *zap.Logger + globalConfig *config.Config + + // Accumulates state changes for SlotIds, grouped by block number + stateAccumulator map[uint64]map[types.SlotID]*OperatorAVSSplit +} + +func NewOperatorAVSSplitModel( + esm *stateManager.EigenStateManager, + grm *gorm.DB, + logger *zap.Logger, + globalConfig *config.Config, +) (*OperatorAVSSplitModel, error) { + model := &OperatorAVSSplitModel{ + BaseEigenState: base.BaseEigenState{ + Logger: logger, + }, + DB: grm, + logger: logger, + globalConfig: globalConfig, + stateAccumulator: make(map[uint64]map[types.SlotID]*OperatorAVSSplit), + } + + esm.RegisterState(model, 8) + return model, nil +} + +func (oas *OperatorAVSSplitModel) GetModelName() string { + return "OperatorAVSSplitModel" +} + +type operatorAVSSplitOutputData struct { + ActivatedAt uint64 `json:"activatedAt"` + OldOperatorAVSSplitBips uint64 `json:"oldOperatorAVSSplitBips"` + NewOperatorAVSSplitBips uint64 `json:"newOperatorAVSSplitBips"` +} + +func parseOperatorAVSSplitOutputData(outputDataStr string) (*operatorAVSSplitOutputData, error) { + outputData := &operatorAVSSplitOutputData{} + decoder := json.NewDecoder(strings.NewReader(outputDataStr)) + decoder.UseNumber() + + err := decoder.Decode(&outputData) + if err != nil { + return nil, err + } + + return outputData, err +} + +func (oas *OperatorAVSSplitModel) handleOperatorAVSSplitBipsSetEvent(log *storage.TransactionLog) (*OperatorAVSSplit, error) { + arguments, err := oas.ParseLogArguments(log) + if err != nil { + return nil, err + } + + outputData, err := parseOperatorAVSSplitOutputData(log.OutputData) + if err != nil { + return nil, err + } + + activatedAt := time.Unix(int64(outputData.ActivatedAt), 0) + + split := &OperatorAVSSplit{ + Operator: strings.ToLower(arguments[1].Value.(string)), + Avs: strings.ToLower(arguments[2].Value.(string)), + ActivatedAt: &activatedAt, + OldOperatorAVSSplitBips: outputData.OldOperatorAVSSplitBips, + NewOperatorAVSSplitBips: outputData.NewOperatorAVSSplitBips, + BlockNumber: log.BlockNumber, + TransactionHash: log.TransactionHash, + LogIndex: log.LogIndex, + } + + return split, nil +} + +func (oas *OperatorAVSSplitModel) GetStateTransitions() (types.StateTransitions[*OperatorAVSSplit], []uint64) { + stateChanges := make(types.StateTransitions[*OperatorAVSSplit]) + + stateChanges[0] = func(log *storage.TransactionLog) (*OperatorAVSSplit, error) { + operatorAVSSplit, err := oas.handleOperatorAVSSplitBipsSetEvent(log) + if err != nil { + return nil, err + } + + slotId := base.NewSlotID(operatorAVSSplit.TransactionHash, operatorAVSSplit.LogIndex) + + _, ok := oas.stateAccumulator[log.BlockNumber][slotId] + if ok { + fmt.Printf("Submissions: %+v\n", oas.stateAccumulator[log.BlockNumber]) + err := xerrors.Errorf("Duplicate distribution root submitted for slot %s at block %d", slotId, log.BlockNumber) + oas.logger.Sugar().Errorw("Duplicate distribution root submitted", zap.Error(err)) + return nil, err + } + + oas.stateAccumulator[log.BlockNumber][slotId] = operatorAVSSplit + + return operatorAVSSplit, nil + } + + // Create an ordered list of block numbers + blockNumbers := make([]uint64, 0) + for blockNumber := range stateChanges { + blockNumbers = append(blockNumbers, blockNumber) + } + sort.Slice(blockNumbers, func(i, j int) bool { + return blockNumbers[i] < blockNumbers[j] + }) + slices.Reverse(blockNumbers) + + return stateChanges, blockNumbers +} + +func (oas *OperatorAVSSplitModel) getContractAddressesForEnvironment() map[string][]string { + contracts := oas.globalConfig.GetContractsMapForChain() + return map[string][]string{ + contracts.RewardsCoordinator: { + "OperatorAVSSplitBipsSet", + }, + } +} + +func (oas *OperatorAVSSplitModel) IsInterestingLog(log *storage.TransactionLog) bool { + addresses := oas.getContractAddressesForEnvironment() + return oas.BaseEigenState.IsInterestingLog(addresses, log) +} + +func (oas *OperatorAVSSplitModel) SetupStateForBlock(blockNumber uint64) error { + oas.stateAccumulator[blockNumber] = make(map[types.SlotID]*OperatorAVSSplit) + return nil +} + +func (oas *OperatorAVSSplitModel) CleanupProcessedStateForBlock(blockNumber uint64) error { + delete(oas.stateAccumulator, blockNumber) + return nil +} + +func (oas *OperatorAVSSplitModel) HandleStateChange(log *storage.TransactionLog) (interface{}, error) { + stateChanges, sortedBlockNumbers := oas.GetStateTransitions() + + for _, blockNumber := range sortedBlockNumbers { + if log.BlockNumber >= blockNumber { + oas.logger.Sugar().Debugw("Handling state change", zap.Uint64("blockNumber", log.BlockNumber)) + + change, err := stateChanges[blockNumber](log) + if err != nil { + return nil, err + } + if change == nil { + return nil, nil + } + return change, nil + } + } + return nil, nil +} + +// prepareState prepares the state for commit by adding the new state to the existing state. +func (oas *OperatorAVSSplitModel) prepareState(blockNumber uint64) ([]*OperatorAVSSplit, error) { + accumulatedState, ok := oas.stateAccumulator[blockNumber] + if !ok { + err := xerrors.Errorf("No accumulated state found for block %d", blockNumber) + oas.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + + recordsToInsert := make([]*OperatorAVSSplit, 0) + for _, split := range accumulatedState { + recordsToInsert = append(recordsToInsert, split) + } + return recordsToInsert, nil +} + +// CommitFinalState commits the final state for the given block number. +func (oas *OperatorAVSSplitModel) CommitFinalState(blockNumber uint64) error { + recordsToInsert, err := oas.prepareState(blockNumber) + if err != nil { + return err + } + + if len(recordsToInsert) > 0 { + for _, record := range recordsToInsert { + res := oas.DB.Model(&OperatorAVSSplit{}).Clauses(clause.Returning{}).Create(&record) + if res.Error != nil { + oas.logger.Sugar().Errorw("Failed to insert records", zap.Error(res.Error)) + return res.Error + } + } + } + return nil +} + +// GenerateStateRoot generates the state root for the given block number using the results of the state changes. +func (oas *OperatorAVSSplitModel) GenerateStateRoot(blockNumber uint64) (types.StateRoot, error) { + inserts, err := oas.prepareState(blockNumber) + if err != nil { + return "", err + } + + inputs := oas.sortValuesForMerkleTree(inserts) + + if len(inputs) == 0 { + return "", nil + } + + fullTree, err := oas.MerkleizeState(blockNumber, inputs) + if err != nil { + oas.logger.Sugar().Errorw("Failed to create merkle tree", + zap.Error(err), + zap.Uint64("blockNumber", blockNumber), + zap.Any("inputs", inputs), + ) + return "", err + } + return types.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil +} + +func (oas *OperatorAVSSplitModel) sortValuesForMerkleTree(splits []*OperatorAVSSplit) []*base.MerkleTreeInput { + inputs := make([]*base.MerkleTreeInput, 0) + for _, split := range splits { + slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) + value := "added" + inputs = append(inputs, &base.MerkleTreeInput{ + SlotID: slotID, + Value: []byte(value), + }) + } + + slices.SortFunc(inputs, func(i, j *base.MerkleTreeInput) int { + return strings.Compare(string(i.SlotID), string(j.SlotID)) + }) + + return inputs +} + +func (oas *OperatorAVSSplitModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { + return oas.BaseEigenState.DeleteState("operator_avs_splits", startBlockNumber, endBlockNumber, oas.DB) +} From 1f61266062213bbb43d74d233d2f80cf1bf9f7de Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 19 Nov 2024 15:57:48 -0500 Subject: [PATCH 14/55] migration --- .../202411191550_operatorAVSSplits/up.go | 34 +++++++++++++++++++ pkg/postgres/migrations/migrator.go | 2 ++ 2 files changed, 36 insertions(+) create mode 100644 pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go diff --git a/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go new file mode 100644 index 00000000..850dd89d --- /dev/null +++ b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go @@ -0,0 +1,34 @@ +package _202411191550_operatorAVSSplits + +import ( + "database/sql" + + "gorm.io/gorm" +) + +type Migration struct { +} + +func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { + query := ` + create table if not exists operator_avs_splits ( + operator varchar not null, + avs varchar not null, + activated_at timestamp(6) not null, + old_operator_avs_split_bips integer not null, + new_operator_avs_split_bips integer not null, + block_number bigint not null, + transaction_hash varchar not null, + log_index bigint not null, + unique(transaction_hash, log_index, block_number) + ); + ` + if err := grm.Exec(query).Error; err != nil { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202411191550_operatorAVSSplits" +} diff --git a/pkg/postgres/migrations/migrator.go b/pkg/postgres/migrations/migrator.go index 802fbe64..8a35e814 100644 --- a/pkg/postgres/migrations/migrator.go +++ b/pkg/postgres/migrations/migrator.go @@ -34,6 +34,7 @@ import ( _202411130953_addHashColumns "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411130953_addHashColumns" _202411131200_eigenStateModelConstraints "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411131200_eigenStateModelConstraints" _202411151931_operatorDirectedRewardSubmissions "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions" + _202411191550_operatorAVSSplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191550_operatorAVSSplits" _202411191947_cleanupUnusedTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191947_cleanupUnusedTables" _202412021311_stakerOperatorTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412021311_stakerOperatorTables" _202412061553_addBlockNumberIndexes "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412061553_addBlockNumberIndexes" @@ -111,6 +112,7 @@ func (m *Migrator) MigrateAll() error { &_202411130953_addHashColumns.Migration{}, &_202411131200_eigenStateModelConstraints.Migration{}, &_202411151931_operatorDirectedRewardSubmissions.Migration{}, + &_202411191550_operatorAVSSplits.Migration{}, &_202411191947_cleanupUnusedTables.Migration{}, &_202412021311_stakerOperatorTables.Migration{}, &_202412061553_addBlockNumberIndexes.Migration{}, From 0361d9618ab8c293ce2460e335c6ad934d69df3d Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 19 Nov 2024 16:57:37 -0500 Subject: [PATCH 15/55] unit tests --- .../operatorAVSSplits_test.go | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go new file mode 100644 index 00000000..ae0d0d77 --- /dev/null +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go @@ -0,0 +1,143 @@ +package operatorAVSSplits + +import ( + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/Layr-Labs/sidecar/pkg/postgres" + "github.com/Layr-Labs/sidecar/pkg/storage" + + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/logger" + "github.com/Layr-Labs/sidecar/internal/tests" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "gorm.io/gorm" +) + +func setup() ( + string, + *gorm.DB, + *zap.Logger, + *config.Config, + error, +) { + cfg := config.NewConfig() + cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() + + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true}) + + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, l) + if err != nil { + return dbname, nil, nil, nil, err + } + + return dbname, grm, l, cfg, nil +} + +func teardown(model *OperatorAVSSplitModel) { + queries := []string{ + `truncate table operator_avs_splits`, + `truncate table blocks cascade`, + } + for _, query := range queries { + res := model.DB.Exec(query) + if res.Error != nil { + fmt.Printf("Failed to run query: %v\n", res.Error) + } + } +} + +func createBlock(model *OperatorAVSSplitModel, blockNumber uint64) error { + block := &storage.Block{ + Number: blockNumber, + Hash: "some hash", + BlockTime: time.Now().Add(time.Hour * time.Duration(blockNumber)), + } + res := model.DB.Model(&storage.Block{}).Create(block) + if res.Error != nil { + return res.Error + } + return nil +} + +func Test_OperatorAVSSplit(t *testing.T) { + _, grm, l, cfg, err := setup() + + if err != nil { + t.Fatal(err) + } + + t.Run("Test each event type", func(t *testing.T) { + esm := stateManager.NewEigenStateManager(l, grm) + + model, err := NewOperatorAVSSplitModel(esm, grm, l, cfg) + + t.Run("Handle an operator avs split", func(t *testing.T) { + blockNumber := uint64(102) + + if err := createBlock(model, blockNumber); err != nil { + t.Fatal(err) + } + + log := &storage.TransactionLog{ + TransactionHash: "some hash", + TransactionIndex: big.NewInt(100).Uint64(), + BlockNumber: blockNumber, + Address: cfg.GetContractsMapForChain().RewardsCoordinator, + Arguments: `[{"Name": "caller", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "operator", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "avs", "Type": "address", "Value": "0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "Indexed": true}, {"Name": "activatedAt", "Type": "uint32", "Value": 1725494400, "Indexed": false}, {"Name": "oldOperatorAVSSplitBips", "Type": "uint16", "Value": 1000, "Indexed": false}, {"Name": "newOperatorAVSSplitBips", "Type": "uint16", "Value": 2000, "Indexed": false}]`, + EventName: "OperatorAVSSplitBipsSet", + LogIndex: big.NewInt(12).Uint64(), + OutputData: `{"activatedAt": 1725494400, "oldOperatorAVSSplitBips": 1000, "newOperatorAVSSplitBips": 2000}`, + } + + err = model.SetupStateForBlock(blockNumber) + assert.Nil(t, err) + + isInteresting := model.IsInterestingLog(log) + assert.True(t, isInteresting) + + change, err := model.HandleStateChange(log) + assert.Nil(t, err) + assert.NotNil(t, change) + + split := change.(*OperatorAVSSplit) + + assert.Equal(t, strings.ToLower("0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101"), strings.ToLower(split.Operator)) + assert.Equal(t, strings.ToLower("0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1"), strings.ToLower(split.Avs)) + assert.Equal(t, int64(1725494400), split.ActivatedAt.Unix()) + assert.Equal(t, uint64(1000), split.OldOperatorAVSSplitBips) + assert.Equal(t, uint64(2000), split.NewOperatorAVSSplitBips) + + err = model.CommitFinalState(blockNumber) + assert.Nil(t, err) + + splits := make([]*OperatorAVSSplit, 0) + query := `select * from operator_avs_splits where block_number = ?` + res := model.DB.Raw(query, blockNumber).Scan(&splits) + assert.Nil(t, res.Error) + assert.Equal(t, 1, len(splits)) + + stateRoot, err := model.GenerateStateRoot(blockNumber) + assert.Nil(t, err) + assert.NotNil(t, stateRoot) + assert.True(t, len(stateRoot) > 0) + + t.Cleanup(func() { + teardown(model) + }) + }) + + t.Cleanup(func() { + teardown(model) + }) + }) + + t.Cleanup(func() { + // postgres.TeardownTestDatabase(dbName, cfg, grm, l) + }) +} From a17dde2d9ee8dd321ccfe14b09d108b6e6b7beef Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 19 Nov 2024 17:17:57 -0500 Subject: [PATCH 16/55] migration --- .../202411191708_operatorPISplits/up.go | 33 +++++++++++++++++++ .../up.go | 32 ++++++++++++++++++ pkg/postgres/migrations/migrator.go | 4 +++ 3 files changed, 69 insertions(+) create mode 100644 pkg/postgres/migrations/202411191708_operatorPISplits/up.go create mode 100644 pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go diff --git a/pkg/postgres/migrations/202411191708_operatorPISplits/up.go b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go new file mode 100644 index 00000000..cbeb04bf --- /dev/null +++ b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go @@ -0,0 +1,33 @@ +package _202411191708_operatorPISplits + +import ( + "database/sql" + + "gorm.io/gorm" +) + +type Migration struct { +} + +func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { + query := ` + create table if not exists operator_pi_splits ( + operator varchar not null, + activated_at timestamp(6) not null, + old_operator_avs_split_bips integer not null, + new_operator_avs_split_bips integer not null, + block_number bigint not null, + transaction_hash varchar not null, + log_index bigint not null, + unique(transaction_hash, log_index, block_number) + ); + ` + if err := grm.Exec(query).Error; err != nil { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202411191708_operatorPISplits" +} diff --git a/pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go b/pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go new file mode 100644 index 00000000..ccc92339 --- /dev/null +++ b/pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go @@ -0,0 +1,32 @@ +package _202411191710_blockNumberFkConstraint + +import ( + "database/sql" + "fmt" + + "gorm.io/gorm" +) + +type Migration struct { +} + +func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { + queries := []string{ + `alter table operator_directed_reward_submissions add constraint operator_directed_reward_submissions_block_number_fkey foreign key (block_number) references blocks (number) on delete cascade`, + `alter table operator_avs_splits add constraint operator_avs_splits_block_number_fkey foreign key (block_number) references blocks (number) on delete cascade`, + `alter table operator_pi_splits add constraint operator_pi_splits_block_number_fkey foreign key (block_number) references blocks (number) on delete cascade`, + } + + for _, query := range queries { + _, err := db.Exec(query) + if err != nil { + fmt.Printf("Failed to run migration query: %s - %+v\n", query, err) + return err + } + } + return nil +} + +func (m *Migration) GetName() string { + return "202411191710_blockNumberFkConstraint" +} diff --git a/pkg/postgres/migrations/migrator.go b/pkg/postgres/migrations/migrator.go index 8a35e814..18cb7709 100644 --- a/pkg/postgres/migrations/migrator.go +++ b/pkg/postgres/migrations/migrator.go @@ -35,6 +35,8 @@ import ( _202411131200_eigenStateModelConstraints "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411131200_eigenStateModelConstraints" _202411151931_operatorDirectedRewardSubmissions "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions" _202411191550_operatorAVSSplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191550_operatorAVSSplits" + _202411191708_operatorPISplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191708_operatorPISplits" + _202411191710_blockNumberFkConstraint "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191710_blockNumberFkConstraint" _202411191947_cleanupUnusedTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191947_cleanupUnusedTables" _202412021311_stakerOperatorTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412021311_stakerOperatorTables" _202412061553_addBlockNumberIndexes "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412061553_addBlockNumberIndexes" @@ -113,6 +115,8 @@ func (m *Migrator) MigrateAll() error { &_202411131200_eigenStateModelConstraints.Migration{}, &_202411151931_operatorDirectedRewardSubmissions.Migration{}, &_202411191550_operatorAVSSplits.Migration{}, + &_202411191708_operatorPISplits.Migration{}, + &_202411191710_blockNumberFkConstraint.Migration{}, &_202411191947_cleanupUnusedTables.Migration{}, &_202412021311_stakerOperatorTables.Migration{}, &_202412061553_addBlockNumberIndexes.Migration{}, From 9386bbdb17bed145c837a8ae115d01d26cb00eba Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 19 Nov 2024 17:46:16 -0500 Subject: [PATCH 17/55] operator pi split state model --- pkg/eigenState/eigenState.go | 5 + .../operatorPISplits/operatorPISplits.go | 277 ++++++++++++++++++ .../operatorPISplits/operatorPISplits_test.go | 142 +++++++++ 3 files changed, 424 insertions(+) create mode 100644 pkg/eigenState/operatorPISplits/operatorPISplits.go create mode 100644 pkg/eigenState/operatorPISplits/operatorPISplits_test.go diff --git a/pkg/eigenState/eigenState.go b/pkg/eigenState/eigenState.go index 860617a3..ecdac8c9 100644 --- a/pkg/eigenState/eigenState.go +++ b/pkg/eigenState/eigenState.go @@ -6,6 +6,7 @@ import ( "github.com/Layr-Labs/sidecar/pkg/eigenState/disabledDistributionRoots" "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorAVSSplits" "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorDirectedRewardSubmissions" + "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorPISplits" "github.com/Layr-Labs/sidecar/pkg/eigenState/operatorShares" "github.com/Layr-Labs/sidecar/pkg/eigenState/rewardSubmissions" "github.com/Layr-Labs/sidecar/pkg/eigenState/stakerDelegations" @@ -58,5 +59,9 @@ func LoadEigenStateModels( l.Sugar().Errorw("Failed to create OperatorAVSSplitModel", zap.Error(err)) return err } + if _, err := operatorPISplits.NewOperatorPISplitModel(sm, grm, l, cfg); err != nil { + l.Sugar().Errorw("Failed to create OperatorPISplitModel", zap.Error(err)) + return err + } return nil } diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits.go b/pkg/eigenState/operatorPISplits/operatorPISplits.go new file mode 100644 index 00000000..adb78a0b --- /dev/null +++ b/pkg/eigenState/operatorPISplits/operatorPISplits.go @@ -0,0 +1,277 @@ +package operatorPISplits + +import ( + "encoding/json" + "fmt" + "slices" + "sort" + "strings" + "time" + + "github.com/Layr-Labs/sidecar/pkg/storage" + "github.com/Layr-Labs/sidecar/pkg/utils" + + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/pkg/eigenState/base" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" + "github.com/Layr-Labs/sidecar/pkg/eigenState/types" + "go.uber.org/zap" + "golang.org/x/xerrors" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type OperatorPISplit struct { + Operator string + ActivatedAt *time.Time + OldOperatorAVSSplitBips uint64 + NewOperatorAVSSplitBips uint64 + BlockNumber uint64 + TransactionHash string + LogIndex uint64 +} + +type OperatorPISplitModel struct { + base.BaseEigenState + StateTransitions types.StateTransitions[[]*OperatorPISplit] + DB *gorm.DB + Network config.Network + Environment config.Environment + logger *zap.Logger + globalConfig *config.Config + + // Accumulates state changes for SlotIds, grouped by block number + stateAccumulator map[uint64]map[types.SlotID]*OperatorPISplit +} + +func NewOperatorPISplitModel( + esm *stateManager.EigenStateManager, + grm *gorm.DB, + logger *zap.Logger, + globalConfig *config.Config, +) (*OperatorPISplitModel, error) { + model := &OperatorPISplitModel{ + BaseEigenState: base.BaseEigenState{ + Logger: logger, + }, + DB: grm, + logger: logger, + globalConfig: globalConfig, + stateAccumulator: make(map[uint64]map[types.SlotID]*OperatorPISplit), + } + + esm.RegisterState(model, 9) + return model, nil +} + +func (ops *OperatorPISplitModel) GetModelName() string { + return "OperatorPISplitModel" +} + +type operatorPISplitOutputData struct { + ActivatedAt uint64 `json:"activatedAt"` + OldOperatorAVSSplitBips uint64 `json:"oldOperatorAVSSplitBips"` + NewOperatorAVSSplitBips uint64 `json:"newOperatorAVSSplitBips"` +} + +func parseOperatorPISplitOutputData(outputDataStr string) (*operatorPISplitOutputData, error) { + outputData := &operatorPISplitOutputData{} + decoder := json.NewDecoder(strings.NewReader(outputDataStr)) + decoder.UseNumber() + + err := decoder.Decode(&outputData) + if err != nil { + return nil, err + } + + return outputData, err +} + +func (ops *OperatorPISplitModel) handleOperatorPISplitBipsSetEvent(log *storage.TransactionLog) (*OperatorPISplit, error) { + arguments, err := ops.ParseLogArguments(log) + if err != nil { + return nil, err + } + + outputData, err := parseOperatorPISplitOutputData(log.OutputData) + if err != nil { + return nil, err + } + + activatedAt := time.Unix(int64(outputData.ActivatedAt), 0) + + split := &OperatorPISplit{ + Operator: strings.ToLower(arguments[1].Value.(string)), + ActivatedAt: &activatedAt, + OldOperatorAVSSplitBips: outputData.OldOperatorAVSSplitBips, + NewOperatorAVSSplitBips: outputData.NewOperatorAVSSplitBips, + BlockNumber: log.BlockNumber, + TransactionHash: log.TransactionHash, + LogIndex: log.LogIndex, + } + + return split, nil +} + +func (ops *OperatorPISplitModel) GetStateTransitions() (types.StateTransitions[*OperatorPISplit], []uint64) { + stateChanges := make(types.StateTransitions[*OperatorPISplit]) + + stateChanges[0] = func(log *storage.TransactionLog) (*OperatorPISplit, error) { + operatorPISplit, err := ops.handleOperatorPISplitBipsSetEvent(log) + if err != nil { + return nil, err + } + + slotId := base.NewSlotID(operatorPISplit.TransactionHash, operatorPISplit.LogIndex) + + _, ok := ops.stateAccumulator[log.BlockNumber][slotId] + if ok { + fmt.Printf("Submissions: %+v\n", ops.stateAccumulator[log.BlockNumber]) + err := xerrors.Errorf("Duplicate distribution root submitted for slot %s at block %d", slotId, log.BlockNumber) + ops.logger.Sugar().Errorw("Duplicate distribution root submitted", zap.Error(err)) + return nil, err + } + + ops.stateAccumulator[log.BlockNumber][slotId] = operatorPISplit + + return operatorPISplit, nil + } + + // Create an ordered list of block numbers + blockNumbers := make([]uint64, 0) + for blockNumber := range stateChanges { + blockNumbers = append(blockNumbers, blockNumber) + } + sort.Slice(blockNumbers, func(i, j int) bool { + return blockNumbers[i] < blockNumbers[j] + }) + slices.Reverse(blockNumbers) + + return stateChanges, blockNumbers +} + +func (ops *OperatorPISplitModel) getContractAddressesForEnvironment() map[string][]string { + contracts := ops.globalConfig.GetContractsMapForChain() + return map[string][]string{ + contracts.RewardsCoordinator: { + "OperatorPISplitBipsSet", + }, + } +} + +func (ops *OperatorPISplitModel) IsInterestingLog(log *storage.TransactionLog) bool { + addresses := ops.getContractAddressesForEnvironment() + return ops.BaseEigenState.IsInterestingLog(addresses, log) +} + +func (ops *OperatorPISplitModel) SetupStateForBlock(blockNumber uint64) error { + ops.stateAccumulator[blockNumber] = make(map[types.SlotID]*OperatorPISplit) + return nil +} + +func (ops *OperatorPISplitModel) CleanupProcessedStateForBlock(blockNumber uint64) error { + delete(ops.stateAccumulator, blockNumber) + return nil +} + +func (ops *OperatorPISplitModel) HandleStateChange(log *storage.TransactionLog) (interface{}, error) { + stateChanges, sortedBlockNumbers := ops.GetStateTransitions() + + for _, blockNumber := range sortedBlockNumbers { + if log.BlockNumber >= blockNumber { + ops.logger.Sugar().Debugw("Handling state change", zap.Uint64("blockNumber", log.BlockNumber)) + + change, err := stateChanges[blockNumber](log) + if err != nil { + return nil, err + } + if change == nil { + return nil, nil + } + return change, nil + } + } + return nil, nil +} + +// prepareState prepares the state for commit by adding the new state to the existing state. +func (ops *OperatorPISplitModel) prepareState(blockNumber uint64) ([]*OperatorPISplit, error) { + accumulatedState, ok := ops.stateAccumulator[blockNumber] + if !ok { + err := xerrors.Errorf("No accumulated state found for block %d", blockNumber) + ops.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + + recordsToInsert := make([]*OperatorPISplit, 0) + for _, split := range accumulatedState { + recordsToInsert = append(recordsToInsert, split) + } + return recordsToInsert, nil +} + +// CommitFinalState commits the final state for the given block number. +func (ops *OperatorPISplitModel) CommitFinalState(blockNumber uint64) error { + recordsToInsert, err := ops.prepareState(blockNumber) + if err != nil { + return err + } + + if len(recordsToInsert) > 0 { + for _, record := range recordsToInsert { + res := ops.DB.Model(&OperatorPISplit{}).Clauses(clause.Returning{}).Create(&record) + if res.Error != nil { + ops.logger.Sugar().Errorw("Failed to insert records", zap.Error(res.Error)) + return res.Error + } + } + } + return nil +} + +// GenerateStateRoot generates the state root for the given block number using the results of the state changes. +func (ops *OperatorPISplitModel) GenerateStateRoot(blockNumber uint64) (types.StateRoot, error) { + inserts, err := ops.prepareState(blockNumber) + if err != nil { + return "", err + } + + inputs := ops.sortValuesForMerkleTree(inserts) + + if len(inputs) == 0 { + return "", nil + } + + fullTree, err := ops.MerkleizeState(blockNumber, inputs) + if err != nil { + ops.logger.Sugar().Errorw("Failed to create merkle tree", + zap.Error(err), + zap.Uint64("blockNumber", blockNumber), + zap.Any("inputs", inputs), + ) + return "", err + } + return types.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil +} + +func (ops *OperatorPISplitModel) sortValuesForMerkleTree(splits []*OperatorPISplit) []*base.MerkleTreeInput { + inputs := make([]*base.MerkleTreeInput, 0) + for _, split := range splits { + slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) + value := "added" + inputs = append(inputs, &base.MerkleTreeInput{ + SlotID: slotID, + Value: []byte(value), + }) + } + + slices.SortFunc(inputs, func(i, j *base.MerkleTreeInput) int { + return strings.Compare(string(i.SlotID), string(j.SlotID)) + }) + + return inputs +} + +func (ops *OperatorPISplitModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { + return ops.BaseEigenState.DeleteState("operator_pi_splits", startBlockNumber, endBlockNumber, ops.DB) +} diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits_test.go b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go new file mode 100644 index 00000000..d9e6b339 --- /dev/null +++ b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go @@ -0,0 +1,142 @@ +package operatorPISplits + +import ( + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/Layr-Labs/sidecar/pkg/postgres" + "github.com/Layr-Labs/sidecar/pkg/storage" + + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/logger" + "github.com/Layr-Labs/sidecar/internal/tests" + "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "gorm.io/gorm" +) + +func setup() ( + string, + *gorm.DB, + *zap.Logger, + *config.Config, + error, +) { + cfg := config.NewConfig() + cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() + + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true}) + + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, l) + if err != nil { + return dbname, nil, nil, nil, err + } + + return dbname, grm, l, cfg, nil +} + +func teardown(model *OperatorPISplitModel) { + queries := []string{ + `truncate table operator_pi_splits`, + `truncate table blocks cascade`, + } + for _, query := range queries { + res := model.DB.Exec(query) + if res.Error != nil { + fmt.Printf("Failed to run query: %v\n", res.Error) + } + } +} + +func createBlock(model *OperatorPISplitModel, blockNumber uint64) error { + block := &storage.Block{ + Number: blockNumber, + Hash: "some hash", + BlockTime: time.Now().Add(time.Hour * time.Duration(blockNumber)), + } + res := model.DB.Model(&storage.Block{}).Create(block) + if res.Error != nil { + return res.Error + } + return nil +} + +func Test_OperatorPISplit(t *testing.T) { + _, grm, l, cfg, err := setup() + + if err != nil { + t.Fatal(err) + } + + t.Run("Test each event type", func(t *testing.T) { + esm := stateManager.NewEigenStateManager(l, grm) + + model, err := NewOperatorPISplitModel(esm, grm, l, cfg) + + t.Run("Handle an operator pi split", func(t *testing.T) { + blockNumber := uint64(102) + + if err := createBlock(model, blockNumber); err != nil { + t.Fatal(err) + } + + log := &storage.TransactionLog{ + TransactionHash: "some hash", + TransactionIndex: big.NewInt(100).Uint64(), + BlockNumber: blockNumber, + Address: cfg.GetContractsMapForChain().RewardsCoordinator, + Arguments: `[{"Name": "caller", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "operator", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "activatedAt", "Type": "uint32", "Value": 1725494400, "Indexed": false}, {"Name": "oldOperatorAVSSplitBips", "Type": "uint16", "Value": 1000, "Indexed": false}, {"Name": "newOperatorAVSSplitBips", "Type": "uint16", "Value": 2000, "Indexed": false}]`, + EventName: "OperatorPISplitBipsSet", + LogIndex: big.NewInt(12).Uint64(), + OutputData: `{"activatedAt": 1725494400, "oldOperatorAVSSplitBips": 1000, "newOperatorAVSSplitBips": 2000}`, + } + + err = model.SetupStateForBlock(blockNumber) + assert.Nil(t, err) + + isInteresting := model.IsInterestingLog(log) + assert.True(t, isInteresting) + + change, err := model.HandleStateChange(log) + assert.Nil(t, err) + assert.NotNil(t, change) + + split := change.(*OperatorPISplit) + + assert.Equal(t, strings.ToLower("0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101"), strings.ToLower(split.Operator)) + assert.Equal(t, int64(1725494400), split.ActivatedAt.Unix()) + assert.Equal(t, uint64(1000), split.OldOperatorAVSSplitBips) + assert.Equal(t, uint64(2000), split.NewOperatorAVSSplitBips) + + err = model.CommitFinalState(blockNumber) + assert.Nil(t, err) + + splits := make([]*OperatorPISplit, 0) + query := `select * from operator_pi_splits where block_number = ?` + res := model.DB.Raw(query, blockNumber).Scan(&splits) + assert.Nil(t, res.Error) + assert.Equal(t, 1, len(splits)) + + stateRoot, err := model.GenerateStateRoot(blockNumber) + assert.Nil(t, err) + assert.NotNil(t, stateRoot) + assert.True(t, len(stateRoot) > 0) + + t.Cleanup(func() { + teardown(model) + }) + }) + + t.Cleanup(func() { + teardown(model) + }) + }) + + t.Cleanup(func() { + // postgres.TeardownTestDatabase(dbName, cfg, grm, l) + }) +} From 2dd2f0671ef037528a52a90f8e6100d991c0a9da Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Wed, 20 Nov 2024 11:04:51 -0500 Subject: [PATCH 18/55] removed unnecessary printf --- pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go | 6 ++---- .../operatorDirectedRewardSubmissions.go | 5 ++--- pkg/eigenState/operatorPISplits/operatorPISplits.go | 6 ++---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go index 8115d2fb..91cf9c44 100644 --- a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go @@ -2,7 +2,6 @@ package operatorAVSSplits import ( "encoding/json" - "fmt" "slices" "sort" "strings" @@ -128,9 +127,8 @@ func (oas *OperatorAVSSplitModel) GetStateTransitions() (types.StateTransitions[ _, ok := oas.stateAccumulator[log.BlockNumber][slotId] if ok { - fmt.Printf("Submissions: %+v\n", oas.stateAccumulator[log.BlockNumber]) - err := xerrors.Errorf("Duplicate distribution root submitted for slot %s at block %d", slotId, log.BlockNumber) - oas.logger.Sugar().Errorw("Duplicate distribution root submitted", zap.Error(err)) + err := xerrors.Errorf("Duplicate operator AVS split submitted for slot %s at block %d", slotId, log.BlockNumber) + oas.logger.Sugar().Errorw("Duplicate operator AVS split submitted", zap.Error(err)) return nil, err } diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index 055f649e..59b2238b 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -180,9 +180,8 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types _, ok := odrs.stateAccumulator[log.BlockNumber][slotId] if ok { - fmt.Printf("Submissions: %+v\n", odrs.stateAccumulator[log.BlockNumber]) - err := xerrors.Errorf("Duplicate distribution root submitted for slot %s at block %d", slotId, log.BlockNumber) - odrs.logger.Sugar().Errorw("Duplicate distribution root submitted", zap.Error(err)) + err := xerrors.Errorf("Duplicate operator directed reward submission submitted for slot %s at block %d", slotId, log.BlockNumber) + odrs.logger.Sugar().Errorw("Duplicate operator directed reward submission submitted", zap.Error(err)) return nil, err } diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits.go b/pkg/eigenState/operatorPISplits/operatorPISplits.go index adb78a0b..b862e067 100644 --- a/pkg/eigenState/operatorPISplits/operatorPISplits.go +++ b/pkg/eigenState/operatorPISplits/operatorPISplits.go @@ -2,7 +2,6 @@ package operatorPISplits import ( "encoding/json" - "fmt" "slices" "sort" "strings" @@ -126,9 +125,8 @@ func (ops *OperatorPISplitModel) GetStateTransitions() (types.StateTransitions[* _, ok := ops.stateAccumulator[log.BlockNumber][slotId] if ok { - fmt.Printf("Submissions: %+v\n", ops.stateAccumulator[log.BlockNumber]) - err := xerrors.Errorf("Duplicate distribution root submitted for slot %s at block %d", slotId, log.BlockNumber) - ops.logger.Sugar().Errorw("Duplicate distribution root submitted", zap.Error(err)) + err := xerrors.Errorf("Duplicate operator PI split submitted for slot %s at block %d", slotId, log.BlockNumber) + ops.logger.Sugar().Errorw("Duplicate operator PI split submitted", zap.Error(err)) return nil, err } From fe0556162e46b6a38fe3c9efea4f2efe098607d9 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Wed, 20 Nov 2024 11:10:38 -0500 Subject: [PATCH 19/55] test database teardown --- pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go | 4 ++-- .../operatorDirectedRewardSubmissions_test.go | 4 ++-- pkg/eigenState/operatorPISplits/operatorPISplits_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go index ae0d0d77..f7dd2fe5 100644 --- a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go @@ -66,7 +66,7 @@ func createBlock(model *OperatorAVSSplitModel, blockNumber uint64) error { } func Test_OperatorAVSSplit(t *testing.T) { - _, grm, l, cfg, err := setup() + dbName, grm, l, cfg, err := setup() if err != nil { t.Fatal(err) @@ -138,6 +138,6 @@ func Test_OperatorAVSSplit(t *testing.T) { }) t.Cleanup(func() { - // postgres.TeardownTestDatabase(dbName, cfg, grm, l) + postgres.TeardownTestDatabase(dbName, cfg, grm, l) }) } diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go index 8021e6b5..6081e2a4 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go @@ -66,7 +66,7 @@ func createBlock(model *OperatorDirectedRewardSubmissionsModel, blockNumber uint } func Test_OperatorDirectedRewardSubmissions(t *testing.T) { - _, grm, l, cfg, err := setup() + dbName, grm, l, cfg, err := setup() if err != nil { t.Fatal(err) @@ -168,6 +168,6 @@ func Test_OperatorDirectedRewardSubmissions(t *testing.T) { }) t.Cleanup(func() { - // postgres.TeardownTestDatabase(dbName, cfg, grm, l) + postgres.TeardownTestDatabase(dbName, cfg, grm, l) }) } diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits_test.go b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go index d9e6b339..97672bb9 100644 --- a/pkg/eigenState/operatorPISplits/operatorPISplits_test.go +++ b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go @@ -66,7 +66,7 @@ func createBlock(model *OperatorPISplitModel, blockNumber uint64) error { } func Test_OperatorPISplit(t *testing.T) { - _, grm, l, cfg, err := setup() + dbName, grm, l, cfg, err := setup() if err != nil { t.Fatal(err) @@ -137,6 +137,6 @@ func Test_OperatorPISplit(t *testing.T) { }) t.Cleanup(func() { - // postgres.TeardownTestDatabase(dbName, cfg, grm, l) + postgres.TeardownTestDatabase(dbName, cfg, grm, l) }) } From 202c2f44678dc1cf216accbb42712e8d8db45217 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Wed, 20 Nov 2024 11:45:40 -0500 Subject: [PATCH 20/55] updated merkle leaf value --- pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go | 3 ++- .../operatorDirectedRewardSubmissions.go | 2 +- pkg/eigenState/operatorPISplits/operatorPISplits.go | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go index 91cf9c44..ddaa802a 100644 --- a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go @@ -2,6 +2,7 @@ package operatorAVSSplits import ( "encoding/json" + "fmt" "slices" "sort" "strings" @@ -258,7 +259,7 @@ func (oas *OperatorAVSSplitModel) sortValuesForMerkleTree(splits []*OperatorAVSS inputs := make([]*base.MerkleTreeInput, 0) for _, split := range splits { slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) - value := "added" + value := fmt.Sprintf("%s_%s_%d_%d_%d", split.Operator, split.Avs, split.ActivatedAt.Unix(), split.OldOperatorAVSSplitBips, split.NewOperatorAVSSplitBips) inputs = append(inputs, &base.MerkleTreeInput{ SlotID: slotID, Value: []byte(value), diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go index 59b2238b..2d2df124 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -312,7 +312,7 @@ func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(subm inputs := make([]*base.MerkleTreeInput, 0) for _, submission := range submissions { slotID := NewSlotID(submission.TransactionHash, submission.LogIndex, submission.RewardHash, submission.StrategyIndex, submission.OperatorIndex) - value := "added" + value := fmt.Sprintf("%s_%s_%s_%s_%s", submission.RewardHash, submission.Strategy, submission.Multiplier, submission.Operator, submission.Amount) inputs = append(inputs, &base.MerkleTreeInput{ SlotID: slotID, Value: []byte(value), diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits.go b/pkg/eigenState/operatorPISplits/operatorPISplits.go index b862e067..baea18be 100644 --- a/pkg/eigenState/operatorPISplits/operatorPISplits.go +++ b/pkg/eigenState/operatorPISplits/operatorPISplits.go @@ -2,6 +2,7 @@ package operatorPISplits import ( "encoding/json" + "fmt" "slices" "sort" "strings" @@ -256,7 +257,7 @@ func (ops *OperatorPISplitModel) sortValuesForMerkleTree(splits []*OperatorPISpl inputs := make([]*base.MerkleTreeInput, 0) for _, split := range splits { slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) - value := "added" + value := fmt.Sprintf("%s_%d_%d_%d", split.Operator, split.ActivatedAt.Unix(), split.OldOperatorAVSSplitBips, split.NewOperatorAVSSplitBips) inputs = append(inputs, &base.MerkleTreeInput{ SlotID: slotID, Value: []byte(value), From d80f9dd9b76db58f1cd3eceb7c92851b5780449b Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Wed, 20 Nov 2024 12:17:03 -0500 Subject: [PATCH 21/55] cleanup migrations --- .../up.go | 3 +- .../202411191550_operatorAVSSplits/up.go | 3 +- .../202411191708_operatorPISplits/up.go | 3 +- .../up.go | 32 ------------------- pkg/postgres/migrations/migrator.go | 2 -- 5 files changed, 6 insertions(+), 37 deletions(-) delete mode 100644 pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go diff --git a/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go index 499ce10c..72eeb23f 100644 --- a/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go +++ b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go @@ -28,7 +28,8 @@ func (m *Migration) Up(db *sql.DB, grm *gorm.DB, cfg *config.Config) error { block_number bigint not null, transaction_hash varchar not null, log_index bigint not null, - unique(transaction_hash, log_index, block_number, reward_hash, strategy_index, operator_index) + unique(transaction_hash, log_index, block_number, reward_hash, strategy_index, operator_index), + CONSTRAINT operator_directed_reward_submissions_block_number_fkey FOREIGN KEY (block_number) REFERENCES blocks(number) ON DELETE CASCADE ); ` if err := grm.Exec(query).Error; err != nil { diff --git a/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go index 850dd89d..15261d8f 100644 --- a/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go +++ b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go @@ -20,7 +20,8 @@ func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { block_number bigint not null, transaction_hash varchar not null, log_index bigint not null, - unique(transaction_hash, log_index, block_number) + unique(transaction_hash, log_index, block_number), + CONSTRAINT operator_avs_splits_block_number_fkey FOREIGN KEY (block_number) REFERENCES blocks(number) ON DELETE CASCADE ); ` if err := grm.Exec(query).Error; err != nil { diff --git a/pkg/postgres/migrations/202411191708_operatorPISplits/up.go b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go index cbeb04bf..a03d4148 100644 --- a/pkg/postgres/migrations/202411191708_operatorPISplits/up.go +++ b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go @@ -19,7 +19,8 @@ func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { block_number bigint not null, transaction_hash varchar not null, log_index bigint not null, - unique(transaction_hash, log_index, block_number) + unique(transaction_hash, log_index, block_number), + CONSTRAINT operator_pi_splits_block_number_fkey FOREIGN KEY (block_number) REFERENCES blocks(number) ON DELETE CASCADE ); ` if err := grm.Exec(query).Error; err != nil { diff --git a/pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go b/pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go deleted file mode 100644 index ccc92339..00000000 --- a/pkg/postgres/migrations/202411191710_blockNumberFkConstraint/up.go +++ /dev/null @@ -1,32 +0,0 @@ -package _202411191710_blockNumberFkConstraint - -import ( - "database/sql" - "fmt" - - "gorm.io/gorm" -) - -type Migration struct { -} - -func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { - queries := []string{ - `alter table operator_directed_reward_submissions add constraint operator_directed_reward_submissions_block_number_fkey foreign key (block_number) references blocks (number) on delete cascade`, - `alter table operator_avs_splits add constraint operator_avs_splits_block_number_fkey foreign key (block_number) references blocks (number) on delete cascade`, - `alter table operator_pi_splits add constraint operator_pi_splits_block_number_fkey foreign key (block_number) references blocks (number) on delete cascade`, - } - - for _, query := range queries { - _, err := db.Exec(query) - if err != nil { - fmt.Printf("Failed to run migration query: %s - %+v\n", query, err) - return err - } - } - return nil -} - -func (m *Migration) GetName() string { - return "202411191710_blockNumberFkConstraint" -} diff --git a/pkg/postgres/migrations/migrator.go b/pkg/postgres/migrations/migrator.go index 18cb7709..66ce16a4 100644 --- a/pkg/postgres/migrations/migrator.go +++ b/pkg/postgres/migrations/migrator.go @@ -36,7 +36,6 @@ import ( _202411151931_operatorDirectedRewardSubmissions "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions" _202411191550_operatorAVSSplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191550_operatorAVSSplits" _202411191708_operatorPISplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191708_operatorPISplits" - _202411191710_blockNumberFkConstraint "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191710_blockNumberFkConstraint" _202411191947_cleanupUnusedTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202411191947_cleanupUnusedTables" _202412021311_stakerOperatorTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412021311_stakerOperatorTables" _202412061553_addBlockNumberIndexes "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412061553_addBlockNumberIndexes" @@ -116,7 +115,6 @@ func (m *Migrator) MigrateAll() error { &_202411151931_operatorDirectedRewardSubmissions.Migration{}, &_202411191550_operatorAVSSplits.Migration{}, &_202411191708_operatorPISplits.Migration{}, - &_202411191710_blockNumberFkConstraint.Migration{}, &_202411191947_cleanupUnusedTables.Migration{}, &_202412021311_stakerOperatorTables.Migration{}, &_202412061553_addBlockNumberIndexes.Migration{}, From 36ae34a2f1a8d15a02f13f9397713386524d43aa Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Wed, 13 Nov 2024 11:41:16 -0600 Subject: [PATCH 22/55] Release: Rewards v2 From f2a570e2407abe3cf1c9af723c65d0b4bd137efa Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Wed, 13 Nov 2024 11:41:16 -0600 Subject: [PATCH 23/55] Release: Rewards v2 From 59c35d634c0e8685203652f9e2d89876a44133c1 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 22 Nov 2024 13:42:55 -0500 Subject: [PATCH 24/55] migration --- .../202411191550_operatorAVSSplits/up.go | 3 +- .../202411191708_operatorPISplits/up.go | 3 +- .../up.go | 32 +++++++++++++++++++ .../up.go | 31 ++++++++++++++++++ pkg/postgres/migrations/migrator.go | 4 +++ 5 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 pkg/postgres/migrations/202411221331_operatorAVSSplitSnapshots/up.go create mode 100644 pkg/postgres/migrations/202411221331_operatorPISplitSnapshots/up.go 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 { From f39e888c2433c34b7ad7beb2326edfbf27a5937b Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 22 Nov 2024 16:23:13 -0500 Subject: [PATCH 25/55] operatorAvsSplitSnapshots --- pkg/rewards/operatorAvsSplitSnapshots.go | 92 ++++++++++++++++++++++++ pkg/rewards/tables.go | 13 ++++ 2 files changed, 105 insertions(+) create mode 100644 pkg/rewards/operatorAvsSplitSnapshots.go diff --git a/pkg/rewards/operatorAvsSplitSnapshots.go b/pkg/rewards/operatorAvsSplitSnapshots.go new file mode 100644 index 00000000..b9482323 --- /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 + left 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/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 +} From e9983e7ce6cae2b888a093b89f3aac6c5ac473ed Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 22 Nov 2024 16:55:03 -0500 Subject: [PATCH 26/55] operator pi snapshots --- pkg/rewards/operatorPISplitSnapshots.go | 89 +++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 pkg/rewards/operatorPISplitSnapshots.go diff --git a/pkg/rewards/operatorPISplitSnapshots.go b/pkg/rewards/operatorPISplitSnapshots.go new file mode 100644 index 00000000..5d8561ca --- /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 + left 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 +} From 16722d7ed03eab65cd0862663892ad8903d49231 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 22 Nov 2024 17:04:00 -0500 Subject: [PATCH 27/55] updated snapshots --- pkg/rewards/rewards.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) 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 } From 829df2578cf34d0eed8ccce89ecf276622982057 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 22 Nov 2024 18:20:21 -0500 Subject: [PATCH 28/55] operator avs split in rewards calculation --- pkg/rewards/2_goldStakerRewardAmounts.go | 39 ++++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index e9f1bf53..5ab0a39a 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,29 +109,41 @@ staker_operator_total_tokens AS ( END as total_staker_operator_payout FROM staker_proportion ), --- Calculate the token breakdown for each (staker, operator) pair +-- Include the operator_avs_split_snapshots table +operator_avs_splits_cte AS ( + SELECT + operator, + avs, + snapshot, + split + FROM operator_avs_split_snapshots +), +-- 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 sot.*, CASE WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - cast(total_staker_operator_payout * 0.10 AS DECIMAL(38,0)) + CAST(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38, 0)) WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - (total_staker_operator_payout * 0.10)::text::decimal(38,0) + (total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::TEXT::DECIMAL(38, 0) ELSE - floor(total_staker_operator_payout * 0.10) - END as operator_tokens, + FLOOR(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + 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)) + total_staker_operator_payout - CAST(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 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)) + total_staker_operator_payout - (total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::TEXT::DECIMAL(38, 0) ELSE - total_staker_operator_payout - floor(total_staker_operator_payout * 0.10) - END as staker_tokens - FROM staker_operator_total_tokens + total_staker_operator_payout - FLOOR(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + END AS staker_tokens + FROM staker_operator_total_tokens sot + LEFT JOIN operator_avs_splits_cte oas + ON sot.operator = oas.operator AND sot.avs = oas.avs AND sot.snapshot = oas.snapshot ) -SELECT * from token_breakdowns -ORDER BY reward_hash, snapshot, staker, operator +SELECT * FROM token_breakdowns +ORDER BY reward_hash, snapshot, staker, operator; ` func (rc *RewardsCalculator) GenerateGold2StakerRewardAmountsTable(snapshotDate string, forks config.ForkMap) error { From 5bfc9cd470787ee1b7d764221f437e85703ca0aa Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 22 Nov 2024 18:44:25 -0500 Subject: [PATCH 29/55] rfae split calculation --- pkg/rewards/5_goldRfaeStakers.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pkg/rewards/5_goldRfaeStakers.go b/pkg/rewards/5_goldRfaeStakers.go index 9e7d87bc..8551f831 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,23 @@ 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 +-- Include the operator_pi_split_snapshots table +operator_pi_splits_cte AS ( + SELECT + operator, + snapshot, + split + FROM operator_pi_split_snapshots +), +-- 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 sot.*, + floor(total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as operator_tokens, + total_staker_operator_payout - floor(total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as staker_tokens + FROM staker_operator_total_tokens sot + LEFT JOIN operator_pi_splits_cte ops + ON sot.operator = ops.operator AND sot.snapshot = ops.snapshot ) SELECT * from token_breakdowns ORDER BY reward_hash, snapshot, staker, operator From d24118860fe40d7690f066029448e000437339d1 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Fri, 22 Nov 2024 18:49:24 -0500 Subject: [PATCH 30/55] fixes --- pkg/rewards/2_goldStakerRewardAmounts.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index 5ab0a39a..eca613bd 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -124,26 +124,26 @@ token_breakdowns AS ( SELECT sot.*, CASE WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - CAST(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38, 0)) + cast(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38,0)) WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - (total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::TEXT::DECIMAL(38, 0) + (total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::text::decimal(38,0) ELSE - FLOOR(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) - END AS operator_tokens, + floor(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + END as operator_tokens, CASE WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - total_staker_operator_payout - CAST(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38, 0)) + total_staker_operator_payout - cast(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38, 0)) WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - total_staker_operator_payout - (total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::TEXT::DECIMAL(38, 0) + total_staker_operator_payout - ((total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::text::decimal(38, 0)) ELSE - total_staker_operator_payout - FLOOR(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) - END AS staker_tokens + total_staker_operator_payout - floor(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + END as staker_tokens FROM staker_operator_total_tokens sot LEFT JOIN operator_avs_splits_cte oas ON sot.operator = oas.operator AND sot.avs = oas.avs AND sot.snapshot = oas.snapshot ) -SELECT * FROM token_breakdowns -ORDER BY reward_hash, snapshot, staker, operator; +SELECT * from token_breakdowns +ORDER BY reward_hash, snapshot, staker, operator ` func (rc *RewardsCalculator) GenerateGold2StakerRewardAmountsTable(snapshotDate string, forks config.ForkMap) error { From f6d20094994c9d5d9c2faf2eba6a66b78c5fd890 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Sat, 23 Nov 2024 17:36:12 -0500 Subject: [PATCH 31/55] fixed snapshot constraint issue --- pkg/rewards/2_goldStakerRewardAmounts.go | 26 ++++++++++++------------ pkg/rewards/5_goldRfaeStakers.go | 10 ++++----- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index eca613bd..3b97aaeb 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -121,26 +121,26 @@ operator_avs_splits_cte AS ( -- 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 sot.*, + SELECT sott.*, CASE - WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - cast(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38,0)) - WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - (total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::text::decimal(38,0) + WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN + cast(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38,0)) + WHEN sott.snapshot < @nileHardforkDate AND sott.reward_submission_date < @nileHardforkDate THEN + (sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::text::decimal(38,0) ELSE - floor(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) END as operator_tokens, CASE - WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - total_staker_operator_payout - cast(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38, 0)) - WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - total_staker_operator_payout - ((total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0)::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 * COALESCE(oas.split, 1000) / 10000.0 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 * COALESCE(oas.split, 1000) / 10000.0)::text::decimal(38,0)) ELSE - total_staker_operator_payout - floor(total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) END as staker_tokens - FROM staker_operator_total_tokens sot + FROM staker_operator_total_tokens sott LEFT JOIN operator_avs_splits_cte oas - ON sot.operator = oas.operator AND sot.avs = oas.avs AND sot.snapshot = oas.snapshot + 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 diff --git a/pkg/rewards/5_goldRfaeStakers.go b/pkg/rewards/5_goldRfaeStakers.go index 8551f831..fd846a68 100644 --- a/pkg/rewards/5_goldRfaeStakers.go +++ b/pkg/rewards/5_goldRfaeStakers.go @@ -119,12 +119,12 @@ operator_pi_splits_cte AS ( -- 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 sot.*, - floor(total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as operator_tokens, - total_staker_operator_payout - floor(total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as staker_tokens - FROM staker_operator_total_tokens sot + SELECT sott.*, + floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as operator_tokens, + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as staker_tokens + FROM staker_operator_total_tokens sott LEFT JOIN operator_pi_splits_cte ops - ON sot.operator = ops.operator AND sot.snapshot = ops.snapshot + ON sott.operator = ops.operator AND sott.snapshot = ops.snapshot ) SELECT * from token_breakdowns ORDER BY reward_hash, snapshot, staker, operator From 15700f7d00541d0f58494e98b4ea3a5c22a779fd Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Sat, 23 Nov 2024 21:02:40 -0500 Subject: [PATCH 32/55] remove unnecessary cte --- pkg/rewards/2_goldStakerRewardAmounts.go | 11 +---------- pkg/rewards/5_goldRfaeStakers.go | 10 +--------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index 3b97aaeb..199dfe8f 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -109,15 +109,6 @@ staker_operator_total_tokens AS ( END as total_staker_operator_payout FROM staker_proportion ), --- Include the operator_avs_split_snapshots table -operator_avs_splits_cte AS ( - SELECT - operator, - avs, - snapshot, - split - FROM operator_avs_split_snapshots -), -- 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 ( @@ -139,7 +130,7 @@ token_breakdowns AS ( sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) END as staker_tokens FROM staker_operator_total_tokens sott - LEFT JOIN operator_avs_splits_cte oas + 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 diff --git a/pkg/rewards/5_goldRfaeStakers.go b/pkg/rewards/5_goldRfaeStakers.go index fd846a68..3d59ff47 100644 --- a/pkg/rewards/5_goldRfaeStakers.go +++ b/pkg/rewards/5_goldRfaeStakers.go @@ -108,14 +108,6 @@ staker_operator_total_tokens AS ( FLOOR(staker_proportion * tokens_per_day_decimal) as total_staker_operator_payout FROM staker_proportion ), --- Include the operator_pi_split_snapshots table -operator_pi_splits_cte AS ( - SELECT - operator, - snapshot, - split - FROM operator_pi_split_snapshots -), -- 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 ( @@ -123,7 +115,7 @@ token_breakdowns AS ( floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as operator_tokens, sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as staker_tokens FROM staker_operator_total_tokens sott - LEFT JOIN operator_pi_splits_cte ops + LEFT JOIN operator_pi_split_snapshots ops ON sott.operator = ops.operator AND sott.snapshot = ops.snapshot ) SELECT * from token_breakdowns From a35050b56f81b91a136e4455d770c3678c5b4be8 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Sun, 1 Dec 2024 02:05:45 -1000 Subject: [PATCH 33/55] fixed left join --- pkg/rewards/operatorAvsSplitSnapshots.go | 2 +- pkg/rewards/operatorPISplitSnapshots.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/rewards/operatorAvsSplitSnapshots.go b/pkg/rewards/operatorAvsSplitSnapshots.go index b9482323..0634fbf0 100644 --- a/pkg/rewards/operatorAvsSplitSnapshots.go +++ b/pkg/rewards/operatorAvsSplitSnapshots.go @@ -11,7 +11,7 @@ WITH operator_avs_splits_with_block_info as ( oas.log_index, b.block_time::timestamp(6) as block_time from operator_avs_splits as oas - left join blocks as b on (b.number = oas.block_number) + 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 diff --git a/pkg/rewards/operatorPISplitSnapshots.go b/pkg/rewards/operatorPISplitSnapshots.go index 5d8561ca..c7e8c2bf 100644 --- a/pkg/rewards/operatorPISplitSnapshots.go +++ b/pkg/rewards/operatorPISplitSnapshots.go @@ -10,7 +10,7 @@ WITH operator_pi_splits_with_block_info as ( ops.log_index, b.block_time::timestamp(6) as block_time from operator_pi_splits as ops - left join blocks as b on (b.number = ops.block_number) + 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 From e95ff8aa16fd5a84ffcf387a798f51a6fcaa2907 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Sun, 1 Dec 2024 16:18:38 -0500 Subject: [PATCH 34/55] hardforks untouched --- pkg/rewards/2_goldStakerRewardAmounts.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index 199dfe8f..84cee6bb 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -115,17 +115,17 @@ token_breakdowns AS ( SELECT sott.*, CASE WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN - cast(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38,0)) + 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 * COALESCE(oas.split, 1000) / 10000.0)::text::decimal(38,0) + (sott.total_staker_operator_payout * 0.10)::text::decimal(38,0) ELSE floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) END as operator_tokens, CASE WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN - sott.total_staker_operator_payout - cast(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0 AS DECIMAL(38,0)) + 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 * COALESCE(oas.split, 1000) / 10000.0)::text::decimal(38,0)) + sott.total_staker_operator_payout - ((sott.total_staker_operator_payout * 0.10)::text::decimal(38,0)) ELSE sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) END as staker_tokens From 10cb11eaf3efb46511422d5e05cd5df1e1cf9762 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Sun, 1 Dec 2024 17:30:54 -0500 Subject: [PATCH 35/55] casting --- pkg/rewards/2_goldStakerRewardAmounts.go | 4 ++-- pkg/rewards/5_goldRfaeStakers.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index 84cee6bb..de2c94e0 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -119,7 +119,7 @@ token_breakdowns AS ( WHEN sott.snapshot < @nileHardforkDate AND sott.reward_submission_date < @nileHardforkDate THEN (sott.total_staker_operator_payout * 0.10)::text::decimal(38,0) ELSE - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) END as operator_tokens, CASE WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN @@ -127,7 +127,7 @@ token_breakdowns AS ( 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)) ELSE - sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / 10000.0) + 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 sott LEFT JOIN operator_avs_split_snapshots oas diff --git a/pkg/rewards/5_goldRfaeStakers.go b/pkg/rewards/5_goldRfaeStakers.go index 3d59ff47..830b262a 100644 --- a/pkg/rewards/5_goldRfaeStakers.go +++ b/pkg/rewards/5_goldRfaeStakers.go @@ -112,8 +112,8 @@ staker_operator_total_tokens AS ( -- If no split is found, default to 1000 (10%) token_breakdowns AS ( SELECT sott.*, - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as operator_tokens, - sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / 10000.0) as staker_tokens + floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) as operator_tokens, + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) 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 From 17aec5008ee25500ff2f61bfdb27f152cc6bd084 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Mon, 2 Dec 2024 11:18:56 -0500 Subject: [PATCH 36/55] arno hardfork --- internal/config/config.go | 7 ++++++- pkg/rewards/2_goldStakerRewardAmounts.go | 6 ++++++ pkg/rewards/5_goldRfaeStakers.go | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) 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/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index de2c94e0..a4f510cc 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -118,6 +118,8 @@ token_breakdowns AS ( 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(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) END as operator_tokens, @@ -126,6 +128,8 @@ token_breakdowns AS ( 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 sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) END as staker_tokens @@ -146,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{ @@ -160,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 830b262a..9d1fd7aa 100644 --- a/pkg/rewards/5_goldRfaeStakers.go +++ b/pkg/rewards/5_goldRfaeStakers.go @@ -112,8 +112,18 @@ staker_operator_total_tokens AS ( -- If no split is found, default to 1000 (10%) token_breakdowns AS ( SELECT sott.*, - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) as operator_tokens, - sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) as staker_tokens + 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 @@ -129,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{ @@ -143,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) From 442b8fa58beed74d6a2ee81641cb8e97b3656ffe Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Tue, 3 Dec 2024 21:34:19 -0600 Subject: [PATCH 37/55] fix reference to rewardsUtils.RenderQueryTemplate --- pkg/rewards/operatorAvsSplitSnapshots.go | 4 +++- pkg/rewards/operatorPISplitSnapshots.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/rewards/operatorAvsSplitSnapshots.go b/pkg/rewards/operatorAvsSplitSnapshots.go index 0634fbf0..fea5c916 100644 --- a/pkg/rewards/operatorAvsSplitSnapshots.go +++ b/pkg/rewards/operatorAvsSplitSnapshots.go @@ -1,5 +1,7 @@ package rewards +import "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + const operatorAvsSplitSnapshotQuery = ` WITH operator_avs_splits_with_block_info as ( select @@ -65,7 +67,7 @@ select * from final_results func (r *RewardsCalculator) GenerateAndInsertOperatorAvsSplitSnapshots(snapshotDate string) error { tableName := "operator_avs_split_snapshots" - query, err := renderQueryTemplate(operatorAvsSplitSnapshotQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorAvsSplitSnapshotQuery, map[string]string{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorPISplitSnapshots.go b/pkg/rewards/operatorPISplitSnapshots.go index c7e8c2bf..710b5225 100644 --- a/pkg/rewards/operatorPISplitSnapshots.go +++ b/pkg/rewards/operatorPISplitSnapshots.go @@ -1,5 +1,7 @@ package rewards +import "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + const operatorPISplitSnapshotQuery = ` WITH operator_pi_splits_with_block_info as ( select @@ -62,7 +64,7 @@ select * from final_results func (r *RewardsCalculator) GenerateAndInsertOperatorPISplitSnapshots(snapshotDate string) error { tableName := "operator_pi_split_snapshots" - query, err := renderQueryTemplate(operatorPISplitSnapshotQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorPISplitSnapshotQuery, map[string]string{ "cutoffDate": snapshotDate, }) if err != nil { From fb3a31c361bdb97dd2d29e6cf1563bb979cad323 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 5 Dec 2024 12:23:03 -0600 Subject: [PATCH 38/55] fix: backport stateroot generation fixes --- .../operatorAVSSplits/operatorAVSSplits.go | 16 +++++++--------- .../operatorAVSSplits/operatorAVSSplits_test.go | 2 +- .../operatorDirectedRewardSubmissions_test.go | 2 +- .../operatorPISplits/operatorPISplits.go | 16 +++++++--------- .../operatorPISplits/operatorPISplits_test.go | 2 +- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go index ddaa802a..cb1c2f30 100644 --- a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go @@ -8,13 +8,11 @@ import ( "strings" "time" - "github.com/Layr-Labs/sidecar/pkg/storage" - "github.com/Layr-Labs/sidecar/pkg/utils" - "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/eigenState/base" "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" "github.com/Layr-Labs/sidecar/pkg/eigenState/types" + "github.com/Layr-Labs/sidecar/pkg/storage" "go.uber.org/zap" "golang.org/x/xerrors" "gorm.io/gorm" @@ -231,28 +229,28 @@ func (oas *OperatorAVSSplitModel) CommitFinalState(blockNumber uint64) error { } // GenerateStateRoot generates the state root for the given block number using the results of the state changes. -func (oas *OperatorAVSSplitModel) GenerateStateRoot(blockNumber uint64) (types.StateRoot, error) { +func (oas *OperatorAVSSplitModel) GenerateStateRoot(blockNumber uint64) ([]byte, error) { inserts, err := oas.prepareState(blockNumber) if err != nil { - return "", err + return nil, err } inputs := oas.sortValuesForMerkleTree(inserts) if len(inputs) == 0 { - return "", nil + return nil, nil } - fullTree, err := oas.MerkleizeState(blockNumber, inputs) + fullTree, err := oas.MerkleizeEigenState(blockNumber, inputs) if err != nil { oas.logger.Sugar().Errorw("Failed to create merkle tree", zap.Error(err), zap.Uint64("blockNumber", blockNumber), zap.Any("inputs", inputs), ) - return "", err + return nil, err } - return types.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil + return fullTree.Root(), nil } func (oas *OperatorAVSSplitModel) sortValuesForMerkleTree(splits []*OperatorAVSSplit) []*base.MerkleTreeInput { diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go index f7dd2fe5..9e60db9d 100644 --- a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go @@ -31,7 +31,7 @@ func setup() ( l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true}) - dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, l) + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l) if err != nil { return dbname, nil, nil, nil, err } diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go index 6081e2a4..03521787 100644 --- a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go @@ -31,7 +31,7 @@ func setup() ( l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true}) - dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, l) + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l) if err != nil { return dbname, nil, nil, nil, err } diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits.go b/pkg/eigenState/operatorPISplits/operatorPISplits.go index baea18be..da147662 100644 --- a/pkg/eigenState/operatorPISplits/operatorPISplits.go +++ b/pkg/eigenState/operatorPISplits/operatorPISplits.go @@ -8,13 +8,11 @@ import ( "strings" "time" - "github.com/Layr-Labs/sidecar/pkg/storage" - "github.com/Layr-Labs/sidecar/pkg/utils" - "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/eigenState/base" "github.com/Layr-Labs/sidecar/pkg/eigenState/stateManager" "github.com/Layr-Labs/sidecar/pkg/eigenState/types" + "github.com/Layr-Labs/sidecar/pkg/storage" "go.uber.org/zap" "golang.org/x/xerrors" "gorm.io/gorm" @@ -229,28 +227,28 @@ func (ops *OperatorPISplitModel) CommitFinalState(blockNumber uint64) error { } // GenerateStateRoot generates the state root for the given block number using the results of the state changes. -func (ops *OperatorPISplitModel) GenerateStateRoot(blockNumber uint64) (types.StateRoot, error) { +func (ops *OperatorPISplitModel) GenerateStateRoot(blockNumber uint64) ([]byte, error) { inserts, err := ops.prepareState(blockNumber) if err != nil { - return "", err + return nil, err } inputs := ops.sortValuesForMerkleTree(inserts) if len(inputs) == 0 { - return "", nil + return nil, nil } - fullTree, err := ops.MerkleizeState(blockNumber, inputs) + fullTree, err := ops.MerkleizeEigenState(blockNumber, inputs) if err != nil { ops.logger.Sugar().Errorw("Failed to create merkle tree", zap.Error(err), zap.Uint64("blockNumber", blockNumber), zap.Any("inputs", inputs), ) - return "", err + return nil, err } - return types.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil + return fullTree.Root(), nil } func (ops *OperatorPISplitModel) sortValuesForMerkleTree(splits []*OperatorPISplit) []*base.MerkleTreeInput { diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits_test.go b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go index 97672bb9..acd1de4a 100644 --- a/pkg/eigenState/operatorPISplits/operatorPISplits_test.go +++ b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go @@ -31,7 +31,7 @@ func setup() ( l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true}) - dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, l) + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l) if err != nil { return dbname, nil, nil, nil, err } From 9e3c068799f57920e193efb44c60e104f1231a61 Mon Sep 17 00:00:00 2001 From: 0xrajath Date: Tue, 3 Dec 2024 19:17:59 -0500 Subject: [PATCH 39/55] feat: operator-directed rewards calculation --- .gitignore | 5 +- .testdataVersion | 2 +- internal/config/config.go | 2 +- .../tests/testdata/avsOperators/README.md | 22 +- .../tests/testdata/combinedRewards/README.md | 20 ++ .../tests/testdata/fetchExpectedResults.sh | 14 +- .../README.md | 28 +++ ...prodRewardsV2_generateExpectedResults.sql} | 10 +- .../operatorAvsSplitSnapshots/README.md | 18 ++ .../README.md | 49 ++++ .../operatorPISplitSnapshots/README.md | 17 ++ .../operatorRestakedStrategies/README.md | 15 ++ ...prodRewardsV2_generateExpectedResults.sql} | 8 +- .../testdata/operatorShareSnapshots/README.md | 9 + ...eprodRewardsV2_generateExpectedResults.sql | 5 + .../tests/testdata/operatorShares/README.md | 24 ++ ...eprodRewardsV2_generateExpectedResults.sql | 22 ++ .../stakerDelegationSnapshots/README.md | 16 ++ ...eprodRewardsV2_generateExpectedResults.sql | 47 ++++ .../testdata/stakerShareSnapshots/README.md | 17 ++ ...eprodRewardsV2_generateExpectedResults.sql | 6 + .../tests/testdata/stakerShares/README.md | 40 ++++ ...eprodRewardsV2_generateExpectedResults.sql | 37 +++ internal/tests/utils.go | 23 ++ pkg/contractStore/coreContracts/preprod.json | 10 + .../operatorPISplits/operatorPISplits.go | 36 +-- .../operatorPISplits/operatorPISplits_test.go | 12 +- .../up.go | 29 +++ pkg/postgres/migrations/migrator.go | 5 +- pkg/rewards/10_goldAvsODRewardAmounts.go | 91 ++++++++ .../{7_goldStaging.go => 11_goldStaging.go} | 62 ++++- .../{8_goldFinal.go => 12_goldFinal.go} | 11 +- pkg/rewards/7_goldActiveODRewards.go | 159 +++++++++++++ pkg/rewards/8_goldOperatorODRewardAmounts.go | 92 ++++++++ pkg/rewards/9_goldStakerODRewardAmounts.go | 141 ++++++++++++ pkg/rewards/operatorAvsSplitSnapshots.go | 47 ++-- pkg/rewards/operatorAvsSplitSnapshots_test.go | 129 +++++++++++ pkg/rewards/operatorDirectedRewards.go | 73 ++++++ pkg/rewards/operatorDirectedRewards_test.go | 118 ++++++++++ pkg/rewards/operatorPISplitSnapshots.go | 53 +++-- pkg/rewards/operatorPISplitSnapshots_test.go | 129 +++++++++++ pkg/rewards/operatorShares_test.go | 2 +- pkg/rewards/rewards.go | 37 ++- pkg/rewards/rewardsV2_test.go | 212 ++++++++++++++++++ pkg/rewards/rewards_test.go | 99 +++++++- pkg/rewards/stakerShareSnapshots_test.go | 2 +- pkg/rewards/stakerShares_test.go | 2 +- pkg/rewards/tables.go | 18 ++ pkg/rewardsUtils/rewardsUtils.go | 43 ++-- scripts/updateTestData.sh | 3 + 50 files changed, 1948 insertions(+), 123 deletions(-) rename internal/tests/testdata/operatorAvsRegistrationSnapshots/{generateExpectedResults.sql => preprodRewardsV2_generateExpectedResults.sql} (89%) create mode 100644 internal/tests/testdata/operatorAvsSplitSnapshots/README.md create mode 100644 internal/tests/testdata/operatorDirectedRewardSubmissions/README.md create mode 100644 internal/tests/testdata/operatorPISplitSnapshots/README.md rename internal/tests/testdata/operatorRestakedStrategies/{generateExpectedResults.sql => preprodRewardsV2_generateExpectedResults.sql} (94%) create mode 100644 internal/tests/testdata/operatorShareSnapshots/preprodRewardsV2_generateExpectedResults.sql create mode 100644 internal/tests/testdata/operatorShares/preprodRewardsV2_generateExpectedResults.sql create mode 100644 internal/tests/testdata/stakerDelegationSnapshots/preprodRewardsV2_generateExpectedResults.sql create mode 100644 internal/tests/testdata/stakerShareSnapshots/preprodRewardsV2_generateExpectedResults.sql create mode 100644 internal/tests/testdata/stakerShares/preprodRewardsV2_generateExpectedResults.sql create mode 100644 pkg/postgres/migrations/202412091100_fixOperatorPiSplitsFields/up.go create mode 100644 pkg/rewards/10_goldAvsODRewardAmounts.go rename pkg/rewards/{7_goldStaging.go => 11_goldStaging.go} (60%) rename pkg/rewards/{8_goldFinal.go => 12_goldFinal.go} (80%) create mode 100644 pkg/rewards/7_goldActiveODRewards.go create mode 100644 pkg/rewards/8_goldOperatorODRewardAmounts.go create mode 100644 pkg/rewards/9_goldStakerODRewardAmounts.go create mode 100644 pkg/rewards/operatorAvsSplitSnapshots_test.go create mode 100644 pkg/rewards/operatorDirectedRewards.go create mode 100644 pkg/rewards/operatorDirectedRewards_test.go create mode 100644 pkg/rewards/operatorPISplitSnapshots_test.go create mode 100644 pkg/rewards/rewardsV2_test.go diff --git a/.gitignore b/.gitignore index 1d3760f9..c367e7a3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /*.sw* *.terraform *.terraform* +*.sql +*.dump /bin /scripts/runLocal sidecar.db* @@ -30,5 +32,4 @@ node_modules chart_releases /snapshots/**/*.sql /snapshots/**/*.csv -*.sql -*.dump + diff --git a/.testdataVersion b/.testdataVersion index 06514b7d..193e406f 100644 --- a/.testdataVersion +++ b/.testdataVersion @@ -1 +1 @@ -e94b8e364dfddd0743746f089f89a3d570751f03 \ No newline at end of file +d67ef5d895bdc0ccd6a006a1683ac3e58f820ad0 \ No newline at end of file diff --git a/internal/config/config.go b/internal/config/config.go index f9efde0b..a6763ab1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -261,7 +261,7 @@ func (c *Config) GetForkDates() (ForkMap, error) { 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", + Fork_Arno: "2024-12-12", }, nil case Chain_Mainnet: return ForkMap{ diff --git a/internal/tests/testdata/avsOperators/README.md b/internal/tests/testdata/avsOperators/README.md index 1c17c83b..83865459 100644 --- a/internal/tests/testdata/avsOperators/README.md +++ b/internal/tests/testdata/avsOperators/README.md @@ -16,4 +16,24 @@ where address = '0x135dda560e946695d6f155dacafc6f1f25c1f5af' and event_name = 'OperatorAVSRegistrationStatusUpdated' and block_number < 20613003 -`` +``` + +## preprod rewardsv2 + + +```sql +select + transaction_hash, + transaction_index, + block_number, + address, + arguments, + event_name, + log_index, + output_data +from transaction_logs +where + address = '0x141d6995556135d4997b2ff72eb443be300353bc' + and event_name = 'OperatorAVSRegistrationStatusUpdated' + and block_number < 2909490 +``` diff --git a/internal/tests/testdata/combinedRewards/README.md b/internal/tests/testdata/combinedRewards/README.md index aeeebba8..a06edf0f 100644 --- a/internal/tests/testdata/combinedRewards/README.md +++ b/internal/tests/testdata/combinedRewards/README.md @@ -69,3 +69,23 @@ select from dbt_mainnet_ethereum_rewards.rewards_combined where block_time < '2024-08-20' ``` + +## preprod rewardsv2 + +```sql +select + avs, + reward_hash, + token, + amount as amount, + strategy, + strategy_index, + multiplier as multiplier, + start_timestamp::timestamp(6) as start_timestamp, + end_timestamp::timestamp(6) as end_timestamp, + reward_type, + duration, + block_number as block_number +from dbt_preprod_holesky_rewards.rewards_combined +where block_time < '2024-12-10' +``` diff --git a/internal/tests/testdata/fetchExpectedResults.sh b/internal/tests/testdata/fetchExpectedResults.sh index b27adea7..6a677347 100755 --- a/internal/tests/testdata/fetchExpectedResults.sh +++ b/internal/tests/testdata/fetchExpectedResults.sh @@ -5,6 +5,8 @@ NETWORK=$1 sqlFileName="generateExpectedResults.sql" outputFile="expectedResults.csv" +postgresPort=5432 + if [[ -z $NETWORK ]]; then echo "Usage: $0 " exit 1 @@ -12,13 +14,19 @@ fi if [[ $NETWORK == "mainnet-reduced" ]]; then sqlFileName="mainnetReduced_${sqlFileName}" + postgresPort=5434 fi if [[ $NETWORK == "testnet-reduced" ]]; then sqlFileName="testnetReduced_${sqlFileName}" fi -for d in operatorShares; do +if [[ $NETWORK == "preprod-rewardsV2" ]]; then + sqlFileName="preprodRewardsV2_${sqlFileName}" + postgresPort=5435 +fi + +for d in stakerShareSnapshots; do echo "Processing directory: $d" if [[ $d == "7_goldStaging" ]]; then files=$(ls "./${d}" | grep "_generateExpectedResults_") @@ -30,12 +38,12 @@ for d in operatorShares; do sqlFileWithPath="${d}/$f" outputFileWithPath="${d}/expectedResults_${snapshotDate}.csv" echo "Generating expected results for ${sqlFileWithPath} to ${outputFileWithPath}" - psql --host localhost --port 5434 --user blocklake --dbname blocklake --password < $sqlFileWithPath > $outputFileWithPath + psql --host localhost --port $postgresPort --user blocklake --dbname blocklake --password < $sqlFileWithPath > $outputFileWithPath done else echo "Generating expected results for $d" sqlFileWithPath="${d}/${sqlFileName}" outputFileWithPath="${d}/${outputFile}" - psql --host localhost --port 5434 --user blocklake --dbname blocklake --password < $sqlFileWithPath > $outputFileWithPath + psql --host localhost --port $postgresPort --user blocklake --dbname blocklake --password < $sqlFileWithPath > $outputFileWithPath fi done diff --git a/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md b/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md index 956797ca..30a4f29d 100644 --- a/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md +++ b/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md @@ -81,6 +81,34 @@ select from filtered ``` +preprod rewardsv2 + +```sql +with filtered as ( + SELECT + lower(t.arguments #>> '{0,Value}') as operator, + lower(t.arguments #>> '{1,Value}') as avs, + case when (t.output_data ->> 'status')::integer = 1 then true else false end as status, + t.transaction_hash, + t.log_index, + b.block_time::timestamp(6), + to_char(b.block_time, 'YYYY-MM-DD') AS block_date, + t.block_number +FROM transaction_logs t + LEFT JOIN blocks b ON t.block_sequence_id = b.id +WHERE t.address = '0x141d6995556135d4997b2ff72eb443be300353bc' + AND t.event_name = 'OperatorAVSRegistrationStatusUpdated' + AND date_trunc('day', b.block_time) < TIMESTAMP '2024-12-10' + ) +select + operator, + avs, + status as registered, + log_index, + block_number +from filtered +``` + ## Expected results _See `generateExpectedResults.sql`_ diff --git a/internal/tests/testdata/operatorAvsRegistrationSnapshots/generateExpectedResults.sql b/internal/tests/testdata/operatorAvsRegistrationSnapshots/preprodRewardsV2_generateExpectedResults.sql similarity index 89% rename from internal/tests/testdata/operatorAvsRegistrationSnapshots/generateExpectedResults.sql rename to internal/tests/testdata/operatorAvsRegistrationSnapshots/preprodRewardsV2_generateExpectedResults.sql index 3a338ea1..33ded9bd 100644 --- a/internal/tests/testdata/operatorAvsRegistrationSnapshots/generateExpectedResults.sql +++ b/internal/tests/testdata/operatorAvsRegistrationSnapshots/preprodRewardsV2_generateExpectedResults.sql @@ -1,7 +1,7 @@ COPY ( with filtered as ( - select * from dbt_testnet_holesky_rewards.operator_avs_status - where block_time < '2024-09-17' + select * from dbt_preprod_holesky_rewards.operator_avs_status + where block_time < '2024-12-11' ), marked_statuses AS ( SELECT @@ -34,7 +34,7 @@ marked_statuses AS ( operator, avs, block_time AS start_time, - COALESCE(next_block_time, TIMESTAMP '2024-09-01') AS end_time, + COALESCE(next_block_time, TIMESTAMP '2024-12-11') AS end_time, registered FROM removed_same_day_deregistrations WHERE registered = TRUE @@ -59,9 +59,9 @@ marked_statuses AS ( SELECT operator, avs, - day AS snapshot + to_char(d, 'YYYY-MM-DD') AS snapshot FROM cleaned_records - CROSS JOIN generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS day + CROSS JOIN generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS d ) select * from final_results ) TO STDOUT WITH DELIMITER ',' CSV HEADER; diff --git a/internal/tests/testdata/operatorAvsSplitSnapshots/README.md b/internal/tests/testdata/operatorAvsSplitSnapshots/README.md new file mode 100644 index 00000000..013b0b4e --- /dev/null +++ b/internal/tests/testdata/operatorAvsSplitSnapshots/README.md @@ -0,0 +1,18 @@ +## preprod rewardsv2 + +```sql +select + lower(arguments #>> '{1, Value}') as operator, + lower(arguments #>> '{2, Value}') as avs, + to_timestamp((output_data ->> 'activatedAt')::integer)::timestamp(6) as activated_at, + output_data ->> 'oldOperatorAVSSplitBips' as old_operator_avs_split_bips, + output_data ->> 'newOperatorAVSSplitBips' as new_operator_avs_split_bips, + block_number, + transaction_hash, + log_index +from transaction_logs +where + address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' + and event_name = 'OperatorAVSSplitBipsSet' +order by block_number desc +``` diff --git a/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md b/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md new file mode 100644 index 00000000..7d549696 --- /dev/null +++ b/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md @@ -0,0 +1,49 @@ +## preprod rewards-v2 + +```sql +WITH strategies AS ( + SELECT + tl.*, + output_data->'operatorDirectedRewardsSubmission'->>'token' as token, + output_data->'operatorDirectedRewardsSubmission'->>'duration' as duration, + output_data->'operatorDirectedRewardsSubmission'->>'startTimestamp' as start_timestamp, + strategy_data, + strategy_idx - 1 as strategy_idx -- Subtract 1 for 0-based indexing + FROM transaction_logs as tl, + jsonb_array_elements(output_data->'operatorDirectedRewardsSubmission'->'strategiesAndMultipliers') + WITH ORDINALITY AS t(strategy_data, strategy_idx) + where + address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' + and event_name = 'OperatorDirectedAVSRewardsSubmissionCreated' +), +operators AS ( + SELECT + operator_data, + output_data->'operatorDirectedRewardsSubmission' as rewards_submission, + operator_idx - 1 as operator_idx -- Subtract 1 to make it 0-based indexing + FROM transaction_logs, + jsonb_array_elements(output_data->'operatorDirectedRewardsSubmission'->'operatorRewards') + WITH ORDINALITY AS t(operator_data, operator_idx) + where + address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' + and event_name = 'OperatorDirectedAVSRewardsSubmissionCreated' +) +SELECT + lower(arguments #>> '{1, Value}') as avs, + lower(arguments #>> '{2, Value}') as reward_hash, + strategies.token, + operator_data->>'operator' as operator, + operator_idx as operator_index, + operator_data->>'amount' as amount, + strategy_data->>'strategy' as strategy, + strategy_idx as strategy_index, + strategy_data->>'multiplier' as multiplier, + (to_timestamp((rewards_submission->>'startTimestamp')::int))::timestamp(6) as start_timestamp, + (rewards_submission->>'duration')::int as duration, + to_timestamp((rewards_submission->>'startTimestamp')::int + (rewards_submission->>'duration')::int)::timestamp(6) as end_timestamp, + block_number, + transaction_hash, + log_index +FROM strategies +CROSS JOIN operators; +``` diff --git a/internal/tests/testdata/operatorPISplitSnapshots/README.md b/internal/tests/testdata/operatorPISplitSnapshots/README.md new file mode 100644 index 00000000..c50fa147 --- /dev/null +++ b/internal/tests/testdata/operatorPISplitSnapshots/README.md @@ -0,0 +1,17 @@ + +## preprod rewardsV2 +```sql +select + lower(arguments #>> '{1, Value}') as operator, + to_timestamp((output_data ->> 'activatedAt')::integer)::timestamp(6) as activated_at, + output_data ->> 'oldOperatorPISplitBips' as old_operator_pi_split_bips, + output_data ->> 'newOperatorPISplitBips' as new_operator_pi_split_bips, + block_number, + transaction_hash, + log_index +from transaction_logs +where + address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' + and event_name = 'OperatorPISplitBipsSet' +order by block_number desc +``` diff --git a/internal/tests/testdata/operatorRestakedStrategies/README.md b/internal/tests/testdata/operatorRestakedStrategies/README.md index 0722c631..796fc641 100644 --- a/internal/tests/testdata/operatorRestakedStrategies/README.md +++ b/internal/tests/testdata/operatorRestakedStrategies/README.md @@ -42,6 +42,21 @@ where avs_directory_address = '0x135dda560e946695d6f155dacafc6f1f25c1f5af' and block_time < '2024-08-20' ``` +preprod rewardsV2 + +```sql +select + block_number, + operator, + avs, + strategy, + block_time::timestamp(6), + avs_directory_address +from operator_restaked_strategies +where avs_directory_address = '0x141d6995556135d4997b2ff72eb443be300353bc' +and block_time < '2024-12-10' +``` + ## Expected results _See `generateExpectedResults.sql`_ diff --git a/internal/tests/testdata/operatorRestakedStrategies/generateExpectedResults.sql b/internal/tests/testdata/operatorRestakedStrategies/preprodRewardsV2_generateExpectedResults.sql similarity index 94% rename from internal/tests/testdata/operatorRestakedStrategies/generateExpectedResults.sql rename to internal/tests/testdata/operatorRestakedStrategies/preprodRewardsV2_generateExpectedResults.sql index 5236cc2d..52eceb25 100644 --- a/internal/tests/testdata/operatorRestakedStrategies/generateExpectedResults.sql +++ b/internal/tests/testdata/operatorRestakedStrategies/preprodRewardsV2_generateExpectedResults.sql @@ -10,8 +10,8 @@ copy (with ranked_records AS ( ORDER BY block_time DESC ) AS rn FROM public.operator_restaked_strategies - WHERE avs_directory_address = lower('0x055733000064333caddbc92763c58bf0192ffebf') - and block_time < '2024-09-17' + WHERE avs_directory_address = lower('0x141d6995556135d4997b2ff72eb443be300353bc') + and block_time < '2024-12-11' ), latest_records AS ( SELECT @@ -102,11 +102,11 @@ SELECT operator, avs, strategy, - cast(day AS DATE) AS snapshot + to_char(d, 'YYYY-MM-DD') AS snapshot FROM cleaned_records CROSS JOIN - generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS day + generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS d ) select * from final_results ) to STDOUT DELIMITER ',' CSV HEADER; diff --git a/internal/tests/testdata/operatorShareSnapshots/README.md b/internal/tests/testdata/operatorShareSnapshots/README.md index 2b55bd90..71439e7f 100644 --- a/internal/tests/testdata/operatorShareSnapshots/README.md +++ b/internal/tests/testdata/operatorShareSnapshots/README.md @@ -32,6 +32,15 @@ from dbt_mainnet_ethereum_rewards.operator_shares where block_time < '2024-08-20' ``` +preprod-rewardsV2 + +```sql +select + * +from dbt_preprod_holesky_rewards.operator_shares +where block_time < '2024-12-10' +``` + ## Expected results _See `generateExpectedResults.sql`_ diff --git a/internal/tests/testdata/operatorShareSnapshots/preprodRewardsV2_generateExpectedResults.sql b/internal/tests/testdata/operatorShareSnapshots/preprodRewardsV2_generateExpectedResults.sql new file mode 100644 index 00000000..1f9fa0db --- /dev/null +++ b/internal/tests/testdata/operatorShareSnapshots/preprodRewardsV2_generateExpectedResults.sql @@ -0,0 +1,5 @@ +COPY ( +select * +FROM dbt_preprod_holesky_rewards.operator_share_snapshots +where snapshot < '2024-12-10' + ) TO STDOUT WITH DELIMITER ',' CSV HEADER diff --git a/internal/tests/testdata/operatorShares/README.md b/internal/tests/testdata/operatorShares/README.md index f37f4643..e3d05074 100644 --- a/internal/tests/testdata/operatorShares/README.md +++ b/internal/tests/testdata/operatorShares/README.md @@ -70,5 +70,29 @@ FROM ( FROM dbt_mainnet_ethereum_rewards.operator_share_decreases where block_date < '2024-08-20' ) combined_shares +``` + +### preprod-rewardsv2 +```sql +SELECT + operator, + strategy, + shares, + transaction_hash, + log_index, + block_time, + block_date, + block_number +FROM ( + SELECT operator, strategy, shares, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_testnet_holesky_rewards.operator_share_increases + where block_date < '2024-12-10' + + UNION ALL + + SELECT operator, strategy, shares * -1 AS shares, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_testnet_holesky_rewards.operator_share_decreases + where block_date < '2024-12-10' + ) combined_shares ``` diff --git a/internal/tests/testdata/operatorShares/preprodRewardsV2_generateExpectedResults.sql b/internal/tests/testdata/operatorShares/preprodRewardsV2_generateExpectedResults.sql new file mode 100644 index 00000000..42c38ff3 --- /dev/null +++ b/internal/tests/testdata/operatorShares/preprodRewardsV2_generateExpectedResults.sql @@ -0,0 +1,22 @@ +COPY ( +SELECT + operator, + strategy, + SUM(shares) OVER (PARTITION BY operator, strategy ORDER BY block_time, log_index) AS shares, + transaction_hash, + log_index, + block_time, + block_date, + block_number +FROM ( + SELECT operator, strategy, shares, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.operator_share_increases + where block_date < '2024-12-11' + + UNION ALL + + SELECT operator, strategy, shares * -1 AS shares, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.operator_share_decreases + where block_date < '2024-12-11' + ) combined_shares + ) TO STDOUT WITH DELIMITER ',' CSV HEADER diff --git a/internal/tests/testdata/stakerDelegationSnapshots/README.md b/internal/tests/testdata/stakerDelegationSnapshots/README.md index 2ea4a454..92c095f6 100644 --- a/internal/tests/testdata/stakerDelegationSnapshots/README.md +++ b/internal/tests/testdata/stakerDelegationSnapshots/README.md @@ -48,6 +48,22 @@ FROM ( where block_time < '2024-08-20' ``` +preprod-rewardsV2 +```sql +SELECT + staker, + operator, + log_index, + block_number, + case when src = 'undelegations' THEN false ELSE true END AS delegated +FROM ( + SELECT *, 'undelegations' AS src FROM dbt_preprod_holesky_rewards.staker_undelegations + UNION ALL + SELECT *, 'delegations' AS src FROM dbt_preprod_holesky_rewards.staker_delegations + ) as delegations_combined +where block_time < '2024-12-10' +``` + ```bash psql --host localhost --port 5435 --user blocklake --dbname blocklake --password < internal/tests/testdata/stakerDelegationSnapshots/generateExpectedResults.sql > internal/tests/testdata/stakerDelegationSnapshots/expectedResults.csv diff --git a/internal/tests/testdata/stakerDelegationSnapshots/preprodRewardsV2_generateExpectedResults.sql b/internal/tests/testdata/stakerDelegationSnapshots/preprodRewardsV2_generateExpectedResults.sql new file mode 100644 index 00000000..1537e4b6 --- /dev/null +++ b/internal/tests/testdata/stakerDelegationSnapshots/preprodRewardsV2_generateExpectedResults.sql @@ -0,0 +1,47 @@ +COPY ( + with delegated_stakers as ( + select + * + from dbt_preprod_holesky_rewards.staker_delegation_status + where block_time < '2024-12-11' +), +ranked_delegations as ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY staker, cast(block_time AS DATE) ORDER BY block_time DESC, log_index DESC) AS rn + FROM delegated_stakers +), + snapshotted_records as ( + SELECT + staker, + operator, + block_time, + date_trunc('day', block_time) + INTERVAL '1' day AS snapshot_time + from ranked_delegations + where rn = 1 + ), + staker_delegation_windows as ( + SELECT + staker, operator, snapshot_time as start_time, + CASE + -- If the range does not have the end, use the cutoff date truncated to 0 UTC + WHEN LEAD(snapshot_time) OVER (PARTITION BY staker ORDER BY snapshot_time) is null THEN date_trunc('day', TIMESTAMP '2024-12-11') + ELSE LEAD(snapshot_time) OVER (PARTITION BY staker ORDER BY snapshot_time) + END AS end_time + FROM snapshotted_records + ), +cleaned_records as ( + SELECT * FROM staker_delegation_windows + WHERE start_time < end_time +), +final_results as ( + SELECT + staker, + operator, + to_char(d, 'YYYY-MM-DD') 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 +) TO STDOUT WITH DELIMITER ',' CSV HEADER; diff --git a/internal/tests/testdata/stakerShareSnapshots/README.md b/internal/tests/testdata/stakerShareSnapshots/README.md index 8ed7914b..5904126c 100644 --- a/internal/tests/testdata/stakerShareSnapshots/README.md +++ b/internal/tests/testdata/stakerShareSnapshots/README.md @@ -48,6 +48,23 @@ where block_time < '2024-08-20' ``` +preprod rewardsV2 + +```sql +select + staker, + strategy, + shares, + strategy_index, + transaction_hash, + log_index, + block_time, + block_date, + block_number +from dbt_preprod_holesky_rewards.staker_shares +where block_time < '2024-12-10' +``` + ## Expected results _See `generateExpectedResults.sql`_ diff --git a/internal/tests/testdata/stakerShareSnapshots/preprodRewardsV2_generateExpectedResults.sql b/internal/tests/testdata/stakerShareSnapshots/preprodRewardsV2_generateExpectedResults.sql new file mode 100644 index 00000000..3101b1d4 --- /dev/null +++ b/internal/tests/testdata/stakerShareSnapshots/preprodRewardsV2_generateExpectedResults.sql @@ -0,0 +1,6 @@ +COPY ( +select + * +from dbt_preprod_holesky_rewards.staker_share_snapshots +where snapshot < '2024-12-11' + ) TO STDOUT WITH DELIMITER ',' CSV HEADER diff --git a/internal/tests/testdata/stakerShares/README.md b/internal/tests/testdata/stakerShares/README.md index d6588736..cb87fa63 100644 --- a/internal/tests/testdata/stakerShares/README.md +++ b/internal/tests/testdata/stakerShares/README.md @@ -116,3 +116,43 @@ FROM ( ) combined_staker_shares ``` + +preprod-rewardsV2 + +```sql +SELECT + staker, + strategy, + shares, + transaction_hash, + log_index, + strategy_index, + block_time, + block_date, + block_number +FROM ( + SELECT staker, strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_mainnet_ethereum_rewards.staker_deposits + where block_date < '2024-08-20' + + UNION ALL + + -- Subtract m1 & m2 withdrawals + SELECT staker, strategy, shares * -1, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_mainnet_ethereum_rewards.m1_staker_withdrawals + where block_date < '2024-08-20' + + UNION ALL + + SELECT staker, strategy, shares * -1, strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_mainnet_ethereum_rewards.m2_staker_withdrawals + where block_date < '2024-08-20' + + UNION all + + -- Shares in eigenpod are positive or negative, so no need to multiply by -1 + SELECT staker, '0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0' as strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_mainnet_ethereum_rewards.eigenpod_shares + where block_date < '2024-08-20' +) combined_staker_shares +``` diff --git a/internal/tests/testdata/stakerShares/preprodRewardsV2_generateExpectedResults.sql b/internal/tests/testdata/stakerShares/preprodRewardsV2_generateExpectedResults.sql new file mode 100644 index 00000000..d24cdfc2 --- /dev/null +++ b/internal/tests/testdata/stakerShares/preprodRewardsV2_generateExpectedResults.sql @@ -0,0 +1,37 @@ +COPY ( +SELECT + staker, + strategy, + shares, + transaction_hash, + log_index, + SUM(shares) OVER (PARTITION BY staker, strategy ORDER BY block_time, log_index) AS shares, + block_time, + block_date, + block_number +FROM ( + SELECT staker, strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.staker_deposits + where block_date < '2024-12-11' + + UNION ALL + + -- Subtract m1 & m2 withdrawals + SELECT staker, strategy, shares * -1, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.m1_staker_withdrawals + where block_date < '2024-12-11' + + UNION ALL + + SELECT staker, strategy, shares * -1, strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.m2_staker_withdrawals + where block_date < '2024-12-11' + + UNION all + + -- Shares in eigenpod are positive or negative, so no need to multiply by -1 + SELECT staker, '0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0' as strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.eigenpod_shares + where block_date < '2024-12-11' + ) combined_staker_shares + ) TO STDOUT WITH DELIMITER ',' CSV HEADER diff --git a/internal/tests/utils.go b/internal/tests/utils.go index 057e97a5..b4d0ebb6 100644 --- a/internal/tests/utils.go +++ b/internal/tests/utils.go @@ -99,6 +99,11 @@ func GetAllBlocksSqlFile(projectBase string) (string, error) { return getSqlFile(path) } +func GetRewardsV2Blocks(projectBase string) (string, error) { + path := getTestdataPathFromProjectRoot(projectBase, "/rewardsV2Blocks.sql") + return getSqlFile(path) +} + func GetOperatorAvsRegistrationsSqlFile(projectBase string) (string, error) { path := getTestdataPathFromProjectRoot(projectBase, "/operatorAvsRegistrationSnapshots/operatorAvsRegistrations.sql") return getSqlFile(path) @@ -265,3 +270,21 @@ func GetStakerDelegationsTransactionLogsSqlFile(projectBase string) (string, err func LargeTestsEnabled() bool { return os.Getenv("TEST_REWARDS") == "true" || os.Getenv("TEST_LARGE") == "true" } + +// ---------------------------------------------------------------------------- +// Rewards V2 +// ---------------------------------------------------------------------------- +func GetOperatorAvsSplitsSqlFile(projectBase string) (string, error) { + path := getTestdataPathFromProjectRoot(projectBase, "/operatorAvsSplitSnapshots/operatorAvsSplits.sql") + return getSqlFile(path) +} + +func GetOperatorPISplitsSqlFile(projectBase string) (string, error) { + path := getTestdataPathFromProjectRoot(projectBase, "/operatorPISplitSnapshots/operatorPISplits.sql") + return getSqlFile(path) +} + +func GetOperatorDirectedRewardsSqlFile(projectBase string) (string, error) { + path := getTestdataPathFromProjectRoot(projectBase, "/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.sql") + return getSqlFile(path) +} diff --git a/pkg/contractStore/coreContracts/preprod.json b/pkg/contractStore/coreContracts/preprod.json index 26e30fd0..b804fd98 100644 --- a/pkg/contractStore/coreContracts/preprod.json +++ b/pkg/contractStore/coreContracts/preprod.json @@ -89,6 +89,11 @@ "contract_address": "0x1a26b23a004c512350d7dd89056655a80b850199", "contract_abi": "[{\"inputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"_delegation\",\"type\":\"address\"},{\"internalType\":\"contract IEigenPodManager\",\"name\":\"_eigenPodManager\",\"type\":\"address\"},{\"internalType\":\"contract ISlasher\",\"name\":\"_slasher\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"pauserRegistry\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"PauserRegistrySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyAddedToDepositWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyRemovedFromDepositWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"StrategyWhitelisterChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"value\",\"type\":\"bool\"}],\"name\":\"UpdatedThirdPartyTransfersForbidden\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEPOSIT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"addShares\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategy[]\",\"name\":\"strategiesToWhitelist\",\"type\":\"address[]\"},{\"internalType\":\"bool[]\",\"name\":\"thirdPartyTransfersForbiddenValues\",\"type\":\"bool[]\"}],\"name\":\"addStrategiesToDepositWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contract IStrategy[]\",\"name\":\"strategies\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"shares\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"nonce\",\"type\":\"uint96\"}],\"internalType\":\"struct IStrategyManager.DeprecatedStruct_WithdrawerAndNonce\",\"name\":\"withdrawerAndNonce\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"withdrawalStartBlock\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"delegatedAddress\",\"type\":\"address\"}],\"internalType\":\"struct IStrategyManager.DeprecatedStruct_QueuedWithdrawal\",\"name\":\"queuedWithdrawal\",\"type\":\"tuple\"}],\"name\":\"calculateWithdrawalRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"delegation\",\"outputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"depositIntoStrategy\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"depositIntoStrategyWithSignature\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"domainSeparator\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"eigenPodManager\",\"outputs\":[{\"internalType\":\"contract IEigenPodManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"}],\"name\":\"getDeposits\",\"outputs\":[{\"internalType\":\"contract IStrategy[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialStrategyWhitelister\",\"type\":\"address\"},{\"internalType\":\"contract IPauserRegistry\",\"name\":\"_pauserRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"initialPausedStatus\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contract IStrategy[]\",\"name\":\"strategies\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"shares\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"nonce\",\"type\":\"uint96\"}],\"internalType\":\"struct IStrategyManager.DeprecatedStruct_WithdrawerAndNonce\",\"name\":\"withdrawerAndNonce\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"withdrawalStartBlock\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"delegatedAddress\",\"type\":\"address\"}],\"internalType\":\"struct IStrategyManager.DeprecatedStruct_QueuedWithdrawal\",\"name\":\"queuedWithdrawal\",\"type\":\"tuple\"}],\"name\":\"migrateQueuedWithdrawal\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauserRegistry\",\"outputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"removeShares\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategy[]\",\"name\":\"strategiesToRemoveFromWhitelist\",\"type\":\"address[]\"}],\"name\":\"removeStrategiesFromDepositWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"setPauserRegistry\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newStrategyWhitelister\",\"type\":\"address\"}],\"name\":\"setStrategyWhitelister\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"value\",\"type\":\"bool\"}],\"name\":\"setThirdPartyTransfersForbidden\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"slasher\",\"outputs\":[{\"internalType\":\"contract ISlasher\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"stakerStrategyList\",\"outputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"}],\"name\":\"stakerStrategyListLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"stakerStrategyShares\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"strategyIsWhitelistedForDeposit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategyWhitelister\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"thirdPartyTransfersForbidden\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"withdrawSharesAsTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"withdrawalRootPending\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", "bytecode_hash": "0b5a653cdc78e427edf32a89325e3d45e47e4c1ce4ffd151a6788f743a747a08" + }, + { + "contract_address": "0x096694a4c8c2c13a005a200309d64995c08ed065", + "contract_abi": "[{\"inputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"_delegationManager\",\"type\":\"address\"},{\"internalType\":\"contract IStrategyManager\",\"name\":\"_strategyManager\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_CALCULATION_INTERVAL_SECONDS\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_REWARDS_DURATION\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_RETROACTIVE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_FUTURE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"__GENESIS_REWARDS_TIMESTAMP\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"AVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"oldActivationDelay\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"newActivationDelay\",\"type\":\"uint32\"}],\"name\":\"ActivationDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldClaimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"ClaimerForSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldDefaultOperatorSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newDefaultOperatorSplitBips\",\"type\":\"uint16\"}],\"name\":\"DefaultOperatorSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"DistributionRootDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"}],\"name\":\"DistributionRootSubmitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorAVSSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorAVSSplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorAVSSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"operatorDirectedRewardsSubmissionHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission\",\"name\":\"operatorDirectedRewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"OperatorDirectedAVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorPISplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorPISplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorPISplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"pauserRegistry\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"PauserRegistrySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"claimedAmount\",\"type\":\"uint256\"}],\"name\":\"RewardsClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rewardsForAllSubmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"oldValue\",\"type\":\"bool\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"newValue\",\"type\":\"bool\"}],\"name\":\"RewardsForAllSubmitterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenHopper\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllEarnersCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldRewardsUpdater\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newRewardsUpdater\",\"type\":\"address\"}],\"name\":\"RewardsUpdaterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"CALCULATION_INTERVAL_SECONDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GENESIS_REWARDS_TIMESTAMP\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_FUTURE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_RETROACTIVE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REWARDS_DURATION\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"activationDelay\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"beaconChainETHStrategy\",\"outputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateEarnerLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateTokenLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"}],\"name\":\"checkClaim\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"claimerFor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission[]\",\"name\":\"operatorDirectedRewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createOperatorDirectedAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllEarners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"cumulativeClaimed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currRewardsCalculationEndTimestamp\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"defaultOperatorSplitBips\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"delegationManager\",\"outputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"disableRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"domainSeparator\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentClaimableDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getDistributionRootAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDistributionRootsLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"}],\"name\":\"getOperatorAVSSplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"getOperatorPISplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"rootHash\",\"type\":\"bytes32\"}],\"name\":\"getRootIndexFromHash\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"},{\"internalType\":\"contract IPauserRegistry\",\"name\":\"_pauserRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"initialPausedStatus\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_defaultSplitBips\",\"type\":\"uint16\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isOperatorDirectedAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isRewardsForAllSubmitter\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllEarnersHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauserRegistry\",\"outputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaim\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim[]\",\"name\":\"claims\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaims\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardsUpdater\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"}],\"name\":\"setActivationDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"setClaimerFor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setDefaultOperatorSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorAVSSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorPISplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"setPauserRegistry\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_submitter\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_newValue\",\"type\":\"bool\"}],\"name\":\"setRewardsForAllSubmitter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"}],\"name\":\"setRewardsUpdater\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategyManager\",\"outputs\":[{\"internalType\":\"contract IStrategyManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"submissionNonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"}],\"name\":\"submitRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "bytecode_hash": "960dde4fe771e141992669bc5db4819a55cc5b4641ca509c7dac787e5e095237" } ], "proxy_contracts": [ @@ -171,6 +176,11 @@ "contract_address": "0xb22ef643e1e067c994019a4c19e403253c05c2b0", "proxy_contract_address": "0x7523b42b081c30fa245aa4039c645e36746fc400", "block_number": 2282920 + }, + { + "contract_address": "0xb22ef643e1e067c994019a4c19e403253c05c2b0", + "proxy_contract_address": "0x096694a4c8c2c13a005a200309d64995c08ed065", + "block_number": 2871534 } ] } diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits.go b/pkg/eigenState/operatorPISplits/operatorPISplits.go index da147662..624d0982 100644 --- a/pkg/eigenState/operatorPISplits/operatorPISplits.go +++ b/pkg/eigenState/operatorPISplits/operatorPISplits.go @@ -20,13 +20,13 @@ import ( ) type OperatorPISplit struct { - Operator string - ActivatedAt *time.Time - OldOperatorAVSSplitBips uint64 - NewOperatorAVSSplitBips uint64 - BlockNumber uint64 - TransactionHash string - LogIndex uint64 + Operator string + ActivatedAt *time.Time + OldOperatorPISplitBips uint64 + NewOperatorPISplitBips uint64 + BlockNumber uint64 + TransactionHash string + LogIndex uint64 } type OperatorPISplitModel struct { @@ -67,9 +67,9 @@ func (ops *OperatorPISplitModel) GetModelName() string { } type operatorPISplitOutputData struct { - ActivatedAt uint64 `json:"activatedAt"` - OldOperatorAVSSplitBips uint64 `json:"oldOperatorAVSSplitBips"` - NewOperatorAVSSplitBips uint64 `json:"newOperatorAVSSplitBips"` + ActivatedAt uint64 `json:"activatedAt"` + OldOperatorPISplitBips uint64 `json:"oldOperatorPISplitBips"` + NewOperatorPISplitBips uint64 `json:"newOperatorPISplitBips"` } func parseOperatorPISplitOutputData(outputDataStr string) (*operatorPISplitOutputData, error) { @@ -99,13 +99,13 @@ func (ops *OperatorPISplitModel) handleOperatorPISplitBipsSetEvent(log *storage. activatedAt := time.Unix(int64(outputData.ActivatedAt), 0) split := &OperatorPISplit{ - Operator: strings.ToLower(arguments[1].Value.(string)), - ActivatedAt: &activatedAt, - OldOperatorAVSSplitBips: outputData.OldOperatorAVSSplitBips, - NewOperatorAVSSplitBips: outputData.NewOperatorAVSSplitBips, - BlockNumber: log.BlockNumber, - TransactionHash: log.TransactionHash, - LogIndex: log.LogIndex, + Operator: strings.ToLower(arguments[1].Value.(string)), + ActivatedAt: &activatedAt, + OldOperatorPISplitBips: outputData.OldOperatorPISplitBips, + NewOperatorPISplitBips: outputData.NewOperatorPISplitBips, + BlockNumber: log.BlockNumber, + TransactionHash: log.TransactionHash, + LogIndex: log.LogIndex, } return split, nil @@ -255,7 +255,7 @@ func (ops *OperatorPISplitModel) sortValuesForMerkleTree(splits []*OperatorPISpl inputs := make([]*base.MerkleTreeInput, 0) for _, split := range splits { slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) - value := fmt.Sprintf("%s_%d_%d_%d", split.Operator, split.ActivatedAt.Unix(), split.OldOperatorAVSSplitBips, split.NewOperatorAVSSplitBips) + value := fmt.Sprintf("%s_%d_%d_%d", split.Operator, split.ActivatedAt.Unix(), split.OldOperatorPISplitBips, split.NewOperatorPISplitBips) inputs = append(inputs, &base.MerkleTreeInput{ SlotID: slotID, Value: []byte(value), diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits_test.go b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go index acd1de4a..9bc12b07 100644 --- a/pkg/eigenState/operatorPISplits/operatorPISplits_test.go +++ b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go @@ -89,10 +89,10 @@ func Test_OperatorPISplit(t *testing.T) { TransactionIndex: big.NewInt(100).Uint64(), BlockNumber: blockNumber, Address: cfg.GetContractsMapForChain().RewardsCoordinator, - Arguments: `[{"Name": "caller", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "operator", "Type": "address", "Value": "0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101", "Indexed": true}, {"Name": "activatedAt", "Type": "uint32", "Value": 1725494400, "Indexed": false}, {"Name": "oldOperatorAVSSplitBips", "Type": "uint16", "Value": 1000, "Indexed": false}, {"Name": "newOperatorAVSSplitBips", "Type": "uint16", "Value": 2000, "Indexed": false}]`, + Arguments: `[{"Name": "caller", "Type": "address", "Value": "0xcf4f3453828f09f5b526101b81d0199d2de39ec5", "Indexed": true}, {"Name": "operator", "Type": "address", "Value": "0xcf4f3453828f09f5b526101b81d0199d2de39ec5", "Indexed": true}, {"Name": "activatedAt", "Type": "uint32", "Value": null, "Indexed": false}, {"Name": "oldOperatorPISplitBips", "Type": "uint16", "Value": null, "Indexed": false}, {"Name": "newOperatorPISplitBips", "Type": "uint16", "Value": null, "Indexed": false}]`, EventName: "OperatorPISplitBipsSet", LogIndex: big.NewInt(12).Uint64(), - OutputData: `{"activatedAt": 1725494400, "oldOperatorAVSSplitBips": 1000, "newOperatorAVSSplitBips": 2000}`, + OutputData: `{"activatedAt": 1733341104, "newOperatorPISplitBips": 6545, "oldOperatorPISplitBips": 1000}`, } err = model.SetupStateForBlock(blockNumber) @@ -107,10 +107,10 @@ func Test_OperatorPISplit(t *testing.T) { split := change.(*OperatorPISplit) - assert.Equal(t, strings.ToLower("0xd36b6e5eee8311d7bffb2f3bb33301a1ab7de101"), strings.ToLower(split.Operator)) - assert.Equal(t, int64(1725494400), split.ActivatedAt.Unix()) - assert.Equal(t, uint64(1000), split.OldOperatorAVSSplitBips) - assert.Equal(t, uint64(2000), split.NewOperatorAVSSplitBips) + assert.Equal(t, strings.ToLower("0xcf4f3453828f09f5b526101b81d0199d2de39ec5"), strings.ToLower(split.Operator)) + assert.Equal(t, int64(1733341104), split.ActivatedAt.Unix()) + assert.Equal(t, uint64(6545), split.NewOperatorPISplitBips) + assert.Equal(t, uint64(1000), split.OldOperatorPISplitBips) err = model.CommitFinalState(blockNumber) assert.Nil(t, err) diff --git a/pkg/postgres/migrations/202412091100_fixOperatorPiSplitsFields/up.go b/pkg/postgres/migrations/202412091100_fixOperatorPiSplitsFields/up.go new file mode 100644 index 00000000..4275d606 --- /dev/null +++ b/pkg/postgres/migrations/202412091100_fixOperatorPiSplitsFields/up.go @@ -0,0 +1,29 @@ +package _202412091100_fixOperatorPiSplitsFields + +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{ + `alter table operator_pi_splits rename column old_operator_avs_split_bips to old_operator_pi_split_bips`, + `alter table operator_pi_splits rename column new_operator_avs_split_bips to new_operator_pi_split_bips`, + } + + for _, query := range queries { + res := grm.Exec(query) + if res.Error != nil { + return res.Error + } + } + return nil +} + +func (m *Migration) GetName() string { + return "202412091100_fixOperatorPiSplitsFields" +} diff --git a/pkg/postgres/migrations/migrator.go b/pkg/postgres/migrations/migrator.go index 0986819f..dca991b9 100644 --- a/pkg/postgres/migrations/migrator.go +++ b/pkg/postgres/migrations/migrator.go @@ -42,6 +42,7 @@ import ( _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" + _202412091100_fixOperatorPiSplitsFields "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412091100_fixOperatorPiSplitsFields" "go.uber.org/zap" "gorm.io/gorm" "time" @@ -123,6 +124,7 @@ func (m *Migrator) MigrateAll() error { &_202412061626_operatorRestakedStrategiesConstraint.Migration{}, &_202411221331_operatorAVSSplitSnapshots.Migration{}, &_202411221331_operatorPISplitSnapshots.Migration{}, + &_202412091100_fixOperatorPiSplitsFields.Migration{}, } for _, migration := range migrations { @@ -142,7 +144,7 @@ func (m *Migrator) Migrate(migration Migration) error { result := m.GDb.Find(&migrationRecord, "name = ?", name).Limit(1) if result.Error == nil && result.RowsAffected == 0 { - m.Logger.Sugar().Infof("Running migration '%s'", name) + m.Logger.Sugar().Debugf("Running migration '%s'", name) // run migration err := migration.Up(m.Db, m.GDb, m.globalConfig) if err != nil { @@ -166,6 +168,7 @@ func (m *Migrator) Migrate(migration Migration) error { m.Logger.Sugar().Infof("Migration %s already run", name) return nil } + m.Logger.Sugar().Infof("Migration %s applied", name) return nil } diff --git a/pkg/rewards/10_goldAvsODRewardAmounts.go b/pkg/rewards/10_goldAvsODRewardAmounts.go new file mode 100644 index 00000000..bd5e7180 --- /dev/null +++ b/pkg/rewards/10_goldAvsODRewardAmounts.go @@ -0,0 +1,91 @@ +package rewards + +import ( + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + "go.uber.org/zap" +) + +const _10_goldAvsODRewardAmountsQuery = ` +CREATE TABLE {{.destTableName}} AS + +-- Step 1: Get the rows where operators have not registered for the AVS or if the AVS does not exist +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 {{.activeODRewardsTable}} ap + LEFT JOIN operator_avs_registration_snapshots oar + ON ap.avs = oar.avs + AND ap.snapshot = oar.snapshot + AND ap.operator = oar.operator + WHERE oar.avs IS NULL OR oar.operator IS NULL +), + +-- Step 2: Dedupe the operator tokens across strategies for each (operator, reward hash, snapshot) +-- Since the above result is a flattened operator-directed reward submission across strategies +distinct_operators AS ( + SELECT * + FROM ( + SELECT + *, + -- We can use an arbitrary order here since the avs_tokens is the same for each (operator, strategy, hash, snapshot) + -- We use strategy ASC for better debuggability + ROW_NUMBER() OVER ( + PARTITION BY reward_hash, snapshot, operator + ORDER BY strategy ASC + ) AS rn + FROM reward_snapshot_operators + ) t + WHERE rn = 1 +), + +-- Step 3: Sum the operator tokens for each (reward hash, snapshot) +-- Since we want to refund the sum of those operator amounts to the AVS in that reward submission for that snapshot +operator_token_sums AS ( + SELECT + reward_hash, + snapshot, + token, + avs, + operator, + SUM(tokens_per_day_decimal) OVER (PARTITION BY reward_hash, snapshot) AS avs_tokens + FROM distinct_operators +) + +-- Step 4: Output the final table +SELECT * FROM operator_token_sums +` + +func (rc *RewardsCalculator) GenerateGold10AvsODRewardAmountsTable(snapshotDate string) error { + allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) + destTableName := allTableNames[rewardsUtils.Table_10_AvsODRewardAmounts] + + rc.logger.Sugar().Infow("Generating Avs OD reward amounts", + zap.String("cutoffDate", snapshotDate), + zap.String("destTableName", destTableName), + ) + + query, err := rewardsUtils.RenderQueryTemplate(_10_goldAvsODRewardAmountsQuery, map[string]string{ + "destTableName": destTableName, + "activeODRewardsTable": 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_avs_od_reward_amounts", "error", res.Error) + return res.Error + } + return nil +} diff --git a/pkg/rewards/7_goldStaging.go b/pkg/rewards/11_goldStaging.go similarity index 60% rename from pkg/rewards/7_goldStaging.go rename to pkg/rewards/11_goldStaging.go index ca6ca04b..677078ae 100644 --- a/pkg/rewards/7_goldStaging.go +++ b/pkg/rewards/11_goldStaging.go @@ -2,11 +2,12 @@ package rewards import ( "database/sql" + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" "go.uber.org/zap" ) -const _7_goldStagingQuery = ` +const _11_goldStagingQuery = ` create table {{.destTableName}} as WITH staker_rewards AS ( -- We can select DISTINCT here because the staker's tokens are the same for each strategy in the reward hash @@ -55,6 +56,36 @@ rewards_for_all_earners_operators AS ( operator_tokens as amount FROM {{.rfaeOperatorTable}} ), +operator_od_rewards AS ( + SELECT DISTINCT + -- We can select DISTINCT here because the operator's tokens are the same for each strategy in the reward hash + operator as earner, + snapshot, + reward_hash, + token, + operator_tokens as amount + FROM {{.operatorODRewardAmountsTable}} +), +staker_od_rewards AS ( + SELECT DISTINCT + -- We can select DISTINCT here because the staker's tokens are the same for each strategy in the reward hash + staker as earner, + snapshot, + reward_hash, + token, + staker_tokens as amount + FROM {{.stakerODRewardAmountsTable}} +), +avs_od_rewards AS ( + SELECT DISTINCT + -- We can select DISTINCT here because the avs's tokens are the same for each strategy in the reward hash + avs as earner, + snapshot, + reward_hash, + token, + avs_tokens as amount + FROM {{.avsODRewardAmountsTable}} +), combined_rewards AS ( SELECT * FROM operator_rewards UNION ALL @@ -65,6 +96,12 @@ combined_rewards AS ( SELECT * FROM rewards_for_all_earners_stakers UNION ALL SELECT * FROM rewards_for_all_earners_operators + UNION ALL + SELECT * FROM operator_od_rewards + UNION ALL + SELECT * FROM staker_od_rewards + UNION ALL + SELECT * FROM avs_od_rewards ), -- Dedupe earners, primarily operators who are also their own staker. deduped_earners AS ( @@ -85,22 +122,25 @@ SELECT * FROM deduped_earners ` -func (rc *RewardsCalculator) GenerateGold7StagingTable(snapshotDate string) error { +func (rc *RewardsCalculator) GenerateGold11StagingTable(snapshotDate string) error { allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) - destTableName := allTableNames[rewardsUtils.Table_7_GoldStaging] + destTableName := allTableNames[rewardsUtils.Table_11_GoldStaging] rc.logger.Sugar().Infow("Generating gold staging", zap.String("cutoffDate", snapshotDate), zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_7_goldStagingQuery, map[string]string{ - "destTableName": destTableName, - "stakerRewardAmountsTable": allTableNames[rewardsUtils.Table_2_StakerRewardAmounts], - "operatorRewardAmountsTable": allTableNames[rewardsUtils.Table_3_OperatorRewardAmounts], - "rewardsForAllTable": allTableNames[rewardsUtils.Table_4_RewardsForAll], - "rfaeStakerTable": allTableNames[rewardsUtils.Table_5_RfaeStakers], - "rfaeOperatorTable": allTableNames[rewardsUtils.Table_6_RfaeOperators], + query, err := rewardsUtils.RenderQueryTemplate(_11_goldStagingQuery, map[string]string{ + "destTableName": destTableName, + "stakerRewardAmountsTable": allTableNames[rewardsUtils.Table_2_StakerRewardAmounts], + "operatorRewardAmountsTable": allTableNames[rewardsUtils.Table_3_OperatorRewardAmounts], + "rewardsForAllTable": allTableNames[rewardsUtils.Table_4_RewardsForAll], + "rfaeStakerTable": allTableNames[rewardsUtils.Table_5_RfaeStakers], + "rfaeOperatorTable": allTableNames[rewardsUtils.Table_6_RfaeOperators], + "operatorODRewardAmountsTable": allTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts], + "stakerODRewardAmountsTable": allTableNames[rewardsUtils.Table_9_StakerODRewardAmounts], + "avsODRewardAmountsTable": allTableNames[rewardsUtils.Table_10_AvsODRewardAmounts], }) if err != nil { rc.logger.Sugar().Errorw("Failed to render query template", "error", err) @@ -135,7 +175,7 @@ func (rc *RewardsCalculator) ListGoldStagingRowsForSnapshot(snapshotDate string) token, amount FROM {{.goldStagingTable}} WHERE DATE(snapshot) < @cutoffDate`, map[string]string{ - "goldStagingTable": allTableNames[rewardsUtils.Table_7_GoldStaging], + "goldStagingTable": allTableNames[rewardsUtils.Table_11_GoldStaging], }) if err != nil { rc.logger.Sugar().Errorw("Failed to render query template", "error", err) diff --git a/pkg/rewards/8_goldFinal.go b/pkg/rewards/12_goldFinal.go similarity index 80% rename from pkg/rewards/8_goldFinal.go rename to pkg/rewards/12_goldFinal.go index 48b243d2..c65e7096 100644 --- a/pkg/rewards/8_goldFinal.go +++ b/pkg/rewards/12_goldFinal.go @@ -1,12 +1,13 @@ package rewards import ( + "time" + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" "go.uber.org/zap" - "time" ) -const _8_goldFinalQuery = ` +const _12_goldFinalQuery = ` insert into gold_table SELECT earner, @@ -25,15 +26,15 @@ type GoldRow struct { Amount string } -func (rc *RewardsCalculator) GenerateGold8FinalTable(snapshotDate string) error { +func (rc *RewardsCalculator) GenerateGold12FinalTable(snapshotDate string) error { allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) rc.logger.Sugar().Infow("Generating gold final table", zap.String("cutoffDate", snapshotDate), ) - query, err := rewardsUtils.RenderQueryTemplate(_8_goldFinalQuery, map[string]string{ - "goldStagingTable": allTableNames[rewardsUtils.Table_7_GoldStaging], + query, err := rewardsUtils.RenderQueryTemplate(_12_goldFinalQuery, map[string]string{ + "goldStagingTable": allTableNames[rewardsUtils.Table_11_GoldStaging], }) if err != nil { rc.logger.Sugar().Errorw("Failed to render query template", "error", err) diff --git a/pkg/rewards/7_goldActiveODRewards.go b/pkg/rewards/7_goldActiveODRewards.go new file mode 100644 index 00000000..ee84cc1d --- /dev/null +++ b/pkg/rewards/7_goldActiveODRewards.go @@ -0,0 +1,159 @@ +package rewards + +import ( + "database/sql" + + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + "go.uber.org/zap" +) + +var _7_goldActiveODRewardsQuery = ` +CREATE TABLE {{.destTableName}} AS +WITH +-- Step 2: Modify active rewards and compute tokens per day +active_rewards_modified AS ( + SELECT + *, + amount / (duration / 86400) AS tokens_per_day, + CAST(@cutoffDate AS TIMESTAMP(6)) AS global_end_inclusive -- Inclusive means we DO USE this day as a snapshot + FROM operator_directed_rewards + WHERE end_timestamp >= TIMESTAMP '{{.rewardsStart}}' + AND start_timestamp <= TIMESTAMP '{{.cutoffDate}}' + AND block_time <= TIMESTAMP '{{.cutoffDate}}' -- Always ensure we're not using future data. Should never happen since we're never backfilling, but here for safety and consistency. +), + +-- Step 3: Cut each reward's start and end windows to handle the global range +active_rewards_updated_end_timestamps AS ( + SELECT + avs, + operator, + /** + * Cut the start and end windows to handle + * A. Retroactive rewards that came recently whose start date is less than start_timestamp + * B. Don't make any rewards past end_timestamp for this run + */ + start_timestamp AS reward_start_exclusive, + LEAST(global_end_inclusive, end_timestamp) AS reward_end_inclusive, + tokens_per_day, + token, + multiplier, + strategy, + reward_hash, + global_end_inclusive, + block_date AS reward_submission_date + FROM active_rewards_modified +), + +-- Step 4: For each reward hash, find the latest snapshot +active_rewards_updated_start_timestamps AS ( + SELECT + ap.avs, + ap.operator, + COALESCE(MAX(g.snapshot), ap.reward_start_exclusive) AS reward_start_exclusive, + ap.reward_end_inclusive, + ap.token, + -- We use floor to ensure we are always underesimating total tokens per day + FLOOR(ap.tokens_per_day) AS tokens_per_day_decimal, + -- Round down to 15 sigfigs for double precision, ensuring know errouneous round up or down + ap.tokens_per_day * ((POW(10, 15) - 1) / POW(10, 15)) AS tokens_per_day, + ap.multiplier, + ap.strategy, + ap.reward_hash, + ap.global_end_inclusive, + ap.reward_submission_date + FROM active_rewards_updated_end_timestamps ap + LEFT JOIN gold_table g + ON g.reward_hash = ap.reward_hash + GROUP BY + ap.avs, + ap.operator, + ap.reward_end_inclusive, + ap.token, + ap.tokens_per_day, + ap.multiplier, + ap.strategy, + ap.reward_hash, + ap.global_end_inclusive, + ap.reward_start_exclusive, + ap.reward_submission_date +), + +-- Step 5: Filter out invalid reward ranges +active_reward_ranges AS ( + /** Take out (reward_start_exclusive, reward_end_inclusive) windows where + * 1. reward_start_exclusive >= reward_end_inclusive: The reward period is done or we will handle on a subsequent run + */ + SELECT * + FROM active_rewards_updated_start_timestamps + WHERE reward_start_exclusive < reward_end_inclusive +), + +-- Step 6: Explode out the ranges for a day per inclusive date +exploded_active_range_rewards AS ( + SELECT + * + FROM active_reward_ranges + CROSS JOIN generate_series( + DATE(reward_start_exclusive), + DATE(reward_end_inclusive), + INTERVAL '1' DAY + ) AS day +), + +-- Step 7: Prepare final active rewards +active_rewards_final AS ( + SELECT + avs, + operator, + CAST(day AS DATE) AS snapshot, + token, + tokens_per_day, + tokens_per_day_decimal, + multiplier, + strategy, + reward_hash, + reward_submission_date + FROM exploded_active_range_rewards + -- Remove snapshots on the start day + WHERE day != reward_start_exclusive +) + +SELECT * FROM active_rewards_final +` + +// Generate7ActiveODRewards generates active operator-directed rewards for the gold_7_active_od_rewards table +// +// @param snapshotDate: The upper bound of when to calculate rewards to +// @param startDate: The lower bound of when to calculate rewards from. If we're running rewards for the first time, +// this will be "1970-01-01". If this is a subsequent run, this will be the last snapshot date. +func (r *RewardsCalculator) Generate7ActiveODRewards(snapshotDate string) error { + allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) + destTableName := allTableNames[rewardsUtils.Table_7_ActiveODRewards] + + rewardsStart := "1970-01-01 00:00:00" // This will always start as this date and get's updated later in the query + + r.logger.Sugar().Infow("Generating active rewards", + zap.String("rewardsStart", rewardsStart), + zap.String("cutoffDate", snapshotDate), + zap.String("destTableName", destTableName), + ) + + query, err := rewardsUtils.RenderQueryTemplate(_7_goldActiveODRewardsQuery, map[string]string{ + "destTableName": destTableName, + "rewardsStart": rewardsStart, + "cutoffDate": snapshotDate, + }) + if err != nil { + r.logger.Sugar().Errorw("Failed to render query template", "error", err) + return err + } + + res := r.grm.Exec(query, + sql.Named("cutoffDate", snapshotDate), + ) + if res.Error != nil { + r.logger.Sugar().Errorw("Failed to generate active od rewards", "error", res.Error) + return res.Error + } + return nil +} diff --git a/pkg/rewards/8_goldOperatorODRewardAmounts.go b/pkg/rewards/8_goldOperatorODRewardAmounts.go new file mode 100644 index 00000000..8ae90773 --- /dev/null +++ b/pkg/rewards/8_goldOperatorODRewardAmounts.go @@ -0,0 +1,92 @@ +package rewards + +import ( + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + "go.uber.org/zap" +) + +const _8_goldOperatorODRewardAmountsQuery = ` +CREATE TABLE {{.destTableName}} AS + +-- Step 1: 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 {{.activeODRewardsTable}} ap + JOIN operator_avs_registration_snapshots oar + ON ap.avs = oar.avs + AND ap.snapshot = oar.snapshot + AND ap.operator = oar.operator +), + +-- Step 2: Dedupe the operator tokens across strategies for each (operator, reward hash, snapshot) +-- Since the above result is a flattened operator-directed reward submission across strategies. +distinct_operators AS ( + SELECT * + FROM ( + SELECT + *, + -- We can use an arbitrary order here since the avs_tokens is the same for each (operator, strategy, hash, snapshot) + -- We use strategy ASC for better debuggability + ROW_NUMBER() OVER ( + PARTITION BY reward_hash, snapshot, operator + ORDER BY strategy ASC + ) AS rn + FROM reward_snapshot_operators + ) t + -- Keep only the first row for each (operator, reward hash, snapshot) + WHERE rn = 1 +), + +-- Step 3: Calculate the tokens for each operator with dynamic split logic +-- If no split is found, default to 1000 (10%) +operator_splits AS ( + SELECT + dop.*, + COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL) as split_pct, + FLOOR(dop.tokens_per_day_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) AS operator_tokens + FROM distinct_operators dop + LEFT JOIN operator_avs_split_snapshots oas + ON dop.operator = oas.operator + AND dop.avs = oas.avs + AND dop.snapshot = oas.snapshot +) + +-- Step 4: Output the final table with operator splits +SELECT * FROM operator_splits +` + +func (rc *RewardsCalculator) GenerateGold8OperatorODRewardAmountsTable(snapshotDate string) error { + allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) + destTableName := allTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts] + + rc.logger.Sugar().Infow("Generating Operator OD reward amounts", + zap.String("cutoffDate", snapshotDate), + zap.String("destTableName", destTableName), + ) + + query, err := rewardsUtils.RenderQueryTemplate(_8_goldOperatorODRewardAmountsQuery, map[string]string{ + "destTableName": destTableName, + "activeODRewardsTable": 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_operator_od_reward_amounts", "error", res.Error) + return res.Error + } + return nil +} diff --git a/pkg/rewards/9_goldStakerODRewardAmounts.go b/pkg/rewards/9_goldStakerODRewardAmounts.go new file mode 100644 index 00000000..5b66a75f --- /dev/null +++ b/pkg/rewards/9_goldStakerODRewardAmounts.go @@ -0,0 +1,141 @@ +package rewards + +import ( + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + "go.uber.org/zap" +) + +const _9_goldStakerODRewardAmountsQuery = ` +CREATE TABLE {{.destTableName}} AS + +-- Step 1: 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 {{.activeODRewardsTable}} 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 +), +-- Get the stakers that were delegated to the operator for the snapshot +staker_delegated_operators AS ( + SELECT + ors.*, + sds.staker + FROM staker_splits ors + JOIN staker_delegation_snapshots sds + ON ors.operator = sds.operator + AND ors.snapshot = sds.snapshot +), + +-- Get the shares for stakers 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 + -- Filter out negative shares and zero multiplier to avoid division by zero + WHERE sss.shares > 0 AND sdo.multiplier != 0 +), + +-- Calculate the weight of each 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 the sum of all staker weights for each reward and snapshot +staker_weight_sum AS ( + SELECT + *, + SUM(staker_weight) OVER (PARTITION BY reward_hash, operator, 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 amounts +staker_reward_amounts AS ( + SELECT + *, + FLOOR(staker_proportion * staker_split) AS staker_tokens + FROM staker_proportion +) +-- Output the final table +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, + "activeODRewardsTable": 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 +} diff --git a/pkg/rewards/operatorAvsSplitSnapshots.go b/pkg/rewards/operatorAvsSplitSnapshots.go index fea5c916..5456af2b 100644 --- a/pkg/rewards/operatorAvsSplitSnapshots.go +++ b/pkg/rewards/operatorAvsSplitSnapshots.go @@ -18,20 +18,37 @@ WITH operator_avs_splits_with_block_info as ( ), -- 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 + SELECT + *, + -- round activated up to the nearest day + date_trunc('day', activated_at) + INTERVAL '1' day AS rounded_activated_at, + ROW_NUMBER() OVER (PARTITION BY operator, avs ORDER BY block_time asc, log_index asc) 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 +decorated_operator_avs_splits as ( + select + rops.*, + -- if there is a row, we have found another split that overlaps the current split + -- meaning the current split should be discarded + case when rops2.block_time is not null then false else true end as active + from ranked_operator_avs_split_records as rops + left join ranked_operator_avs_split_records as rops2 on ( + rops.operator = rops2.operator + and rops.avs = rops2.avs + -- rn is orderd by block and log_index, so this should encapsulate rops2 occurring afer rops + and rops.rn > rops2.rn + -- only find the next split that overlaps with the current one + and rops2.rounded_activated_at <= rops.rounded_activated_at + ) +), +-- filter in only splits flagged as active +active_operator_splits as ( + select + *, + rounded_activated_at as snapshot_time, + ROW_NUMBER() over (partition by operator, avs order by rounded_activated_at asc) as rn + from decorated_operator_avs_splits + where active = true ), -- Get the range for each operator, avs pairing operator_avs_split_windows as ( @@ -40,9 +57,11 @@ operator_avs_split_windows as ( 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) + + -- need to subtract 1 day from the end time since generate_series will be inclusive below. + ELSE LEAD(snapshot_time) OVER (PARTITION BY operator ORDER BY snapshot_time) - interval '1 day' END AS end_time - FROM snapshotted_records + FROM active_operator_splits ), -- Clean up any records where start_time >= end_time cleaned_records as ( diff --git a/pkg/rewards/operatorAvsSplitSnapshots_test.go b/pkg/rewards/operatorAvsSplitSnapshots_test.go new file mode 100644 index 00000000..8b65e954 --- /dev/null +++ b/pkg/rewards/operatorAvsSplitSnapshots_test.go @@ -0,0 +1,129 @@ +package rewards + +import ( + "fmt" + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/logger" + "github.com/Layr-Labs/sidecar/internal/tests" + "github.com/Layr-Labs/sidecar/pkg/postgres" + "github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "gorm.io/gorm" + "testing" +) + +func setupOperatorAvsSplitWindows() ( + string, + *config.Config, + *gorm.DB, + *zap.Logger, + error, +) { + testContext := getRewardsTestContext() + cfg := tests.GetConfig() + switch testContext { + case "testnet": + cfg.Chain = config.Chain_Holesky + case "testnet-reduced": + cfg.Chain = config.Chain_Holesky + case "mainnet-reduced": + cfg.Chain = config.Chain_Mainnet + default: + return "", nil, nil, nil, fmt.Errorf("Unknown test context") + } + + cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() + + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: cfg.Debug}) + + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l) + if err != nil { + return dbname, nil, nil, nil, err + } + + return dbname, cfg, grm, l, nil +} + +func teardownOperatorAvsSplitWindows(dbname string, cfg *config.Config, db *gorm.DB, l *zap.Logger) { + rawDb, _ := db.DB() + _ = rawDb.Close() + + pgConfig := postgres.PostgresConfigFromDbConfig(&cfg.DatabaseConfig) + + if err := postgres.DeleteTestDatabase(pgConfig, dbname); err != nil { + l.Sugar().Errorw("Failed to delete test database", "error", err) + } +} + +func hydrateOperatorAvsSplits(grm *gorm.DB, l *zap.Logger) error { + projectRoot := getProjectRootPath() + contents, err := tests.GetOperatorAvsSplitsSqlFile(projectRoot) + + if err != nil { + return err + } + + res := grm.Exec(contents) + if res.Error != nil { + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) + return res.Error + } + return nil +} + +func Test_OperatorAvsSplitSnapshots(t *testing.T) { + if !rewardsTestsEnabled() { + t.Skipf("Skipping %s", t.Name()) + return + } + + // projectRoot := getProjectRootPath() + dbFileName, cfg, grm, l, err := setupOperatorAvsSplitWindows() + if err != nil { + t.Fatal(err) + } + // testContext := getRewardsTestContext() + + snapshotDate := "2024-12-09" + + t.Run("Should hydrate dependency tables", func(t *testing.T) { + t.Log("Hydrating blocks") + + _, err := hydrateRewardsV2Blocks(grm, l) + assert.Nil(t, err) + + t.Log("Hydrating restaked strategies") + err = hydrateOperatorAvsSplits(grm, l) + if err != nil { + t.Fatal(err) + } + + query := `select count(*) from operator_avs_splits` + var count int + res := grm.Raw(query).Scan(&count) + + assert.Nil(t, res.Error) + + assert.Equal(t, 48, count) + }) + + t.Run("Should calculate correct operatorAvsSplit windows", func(t *testing.T) { + sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg) + rewards, _ := NewRewardsCalculator(cfg, grm, nil, sog, l) + + t.Log("Generating snapshots") + err := rewards.GenerateAndInsertOperatorAvsSplitSnapshots(snapshotDate) + assert.Nil(t, err) + + windows, err := rewards.ListOperatorAvsSplitSnapshots() + assert.Nil(t, err) + + t.Logf("Found %d windows", len(windows)) + + assert.Equal(t, 192, len(windows)) + }) + t.Cleanup(func() { + teardownOperatorAvsSplitWindows(dbFileName, cfg, grm, l) + }) +} diff --git a/pkg/rewards/operatorDirectedRewards.go b/pkg/rewards/operatorDirectedRewards.go new file mode 100644 index 00000000..d0fa074c --- /dev/null +++ b/pkg/rewards/operatorDirectedRewards.go @@ -0,0 +1,73 @@ +package rewards + +import "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + +const operatorDirectedRewardsQuery = ` + with _operator_directed_rewards as ( + SELECT + odrs.avs, + odrs.reward_hash, + odrs.token, + odrs.operator, + odrs.operator_index, + odrs.amount, + odrs.strategy, + odrs.strategy_index, + odrs.multiplier, + odrs.start_timestamp::TIMESTAMP(6), + odrs.end_timestamp::TIMESTAMP(6), + odrs.duration, + odrs.block_number, + b.block_time::TIMESTAMP(6), + TO_CHAR(b.block_time, 'YYYY-MM-DD') AS block_date + FROM operator_directed_reward_submissions AS odrs + JOIN blocks AS b ON(b.number = odrs.block_number) + WHERE b.block_time < TIMESTAMP '{{.cutoffDate}}' + ) + select + avs, + reward_hash, + token, + operator, + operator_index, + amount, + strategy, + strategy_index, + multiplier, + start_timestamp::TIMESTAMP(6), + end_timestamp::TIMESTAMP(6), + duration, + block_number, + block_time, + block_date + from _operator_directed_rewards +` + +func (r *RewardsCalculator) GenerateAndInsertOperatorDirectedRewards(snapshotDate string) error { + tableName := "operator_directed_rewards" + + query, err := rewardsUtils.RenderQueryTemplate(operatorDirectedRewardsQuery, map[string]string{ + "cutoffDate": snapshotDate, + }) + if err != nil { + r.logger.Sugar().Errorw("Failed to render rewards combined query", "error", err) + return err + } + + err = r.generateAndInsertFromQuery(tableName, query, nil) + if err != nil { + r.logger.Sugar().Errorw("Failed to generate combined rewards", "error", err) + return err + } + return nil +} + +func (rc *RewardsCalculator) ListOperatorDirectedRewards() ([]*OperatorDirectedRewards, error) { + var operatorDirectedRewards []*OperatorDirectedRewards + res := rc.grm.Model(&OperatorDirectedRewards{}).Find(&operatorDirectedRewards) + if res.Error != nil { + rc.logger.Sugar().Errorw("Failed to list combined rewards", "error", res.Error) + return nil, res.Error + } + return operatorDirectedRewards, nil +} diff --git a/pkg/rewards/operatorDirectedRewards_test.go b/pkg/rewards/operatorDirectedRewards_test.go new file mode 100644 index 00000000..ffe62753 --- /dev/null +++ b/pkg/rewards/operatorDirectedRewards_test.go @@ -0,0 +1,118 @@ +package rewards + +import ( + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/logger" + "github.com/Layr-Labs/sidecar/internal/tests" + "github.com/Layr-Labs/sidecar/pkg/postgres" + "github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "gorm.io/gorm" + "testing" +) + +func setupOperatorDirectedRewards() ( + string, + *config.Config, + *gorm.DB, + *zap.Logger, + error, +) { + cfg := tests.GetConfig() + cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() + + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: cfg.Debug}) + + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l) + if err != nil { + return dbname, nil, nil, nil, err + } + + return dbname, cfg, grm, l, nil +} + +func teardownOperatorDirectedRewards(dbname string, cfg *config.Config, db *gorm.DB, l *zap.Logger) { + rawDb, _ := db.DB() + _ = rawDb.Close() + + pgConfig := postgres.PostgresConfigFromDbConfig(&cfg.DatabaseConfig) + + if err := postgres.DeleteTestDatabase(pgConfig, dbname); err != nil { + l.Sugar().Errorw("Failed to delete test database", "error", err) + } +} + +func hydrateOperatorDirectedRewardSubmissionsTable(grm *gorm.DB, l *zap.Logger) error { + projectRoot := getProjectRootPath() + contents, err := tests.GetOperatorDirectedRewardsSqlFile(projectRoot) + + if err != nil { + return err + } + + res := grm.Exec(contents) + if res.Error != nil { + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) + return res.Error + } + return nil +} + +func Test_OperatorDirectedRewards(t *testing.T) { + if !rewardsTestsEnabled() { + t.Skipf("Skipping %s", t.Name()) + return + } + + dbFileName, cfg, grm, l, err := setupOperatorDirectedRewards() + + if err != nil { + t.Fatal(err) + } + + snapshotDate := "2024-12-09" + + t.Run("Should hydrate blocks and operator_directed_reward_submissions tables", func(t *testing.T) { + totalBlockCount, err := hydrateRewardsV2Blocks(grm, l) + if err != nil { + t.Fatal(err) + } + + query := "select count(*) from blocks" + var count int + res := grm.Raw(query).Scan(&count) + assert.Nil(t, res.Error) + assert.Equal(t, totalBlockCount, count) + + err = hydrateOperatorDirectedRewardSubmissionsTable(grm, l) + if err != nil { + t.Fatal(err) + } + + query = "select count(*) from operator_directed_reward_submissions" + res = grm.Raw(query).Scan(&count) + assert.Nil(t, res.Error) + assert.Equal(t, 1120, count) + }) + t.Run("Should generate the proper operatorDirectedRewards", func(t *testing.T) { + sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg) + rewards, _ := NewRewardsCalculator(cfg, grm, nil, sog, l) + + err = rewards.GenerateAndInsertOperatorDirectedRewards(snapshotDate) + assert.Nil(t, err) + + operatorDirectedRewards, err := rewards.ListOperatorDirectedRewards() + assert.Nil(t, err) + + assert.NotNil(t, operatorDirectedRewards) + + t.Logf("Generated %d operatorDirectedRewards", len(operatorDirectedRewards)) + + assert.Equal(t, 1120, len(operatorDirectedRewards)) + + }) + t.Cleanup(func() { + teardownOperatorDirectedRewards(dbFileName, cfg, grm, l) + }) +} diff --git a/pkg/rewards/operatorPISplitSnapshots.go b/pkg/rewards/operatorPISplitSnapshots.go index 710b5225..ef212e47 100644 --- a/pkg/rewards/operatorPISplitSnapshots.go +++ b/pkg/rewards/operatorPISplitSnapshots.go @@ -7,7 +7,7 @@ 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.new_operator_pi_split_bips as split, ops.block_number, ops.log_index, b.block_time::timestamp(6) as block_time @@ -15,32 +15,53 @@ WITH operator_pi_splits_with_block_info as ( 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 +-- Rank the records for each operator by block time asc, log index asc (in the order they happened) 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 + SELECT + *, + -- round activated up to the nearest day + date_trunc('day', activated_at) + INTERVAL '1' day AS rounded_activated_at, + ROW_NUMBER() OVER (PARTITION BY operator ORDER BY block_time asc, log_index asc) 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 +decorated_operator_splits as ( + select + rops.*, + -- if there is a row, we have found another split that overlaps the current split + -- meaning the current split should be discarded + case when rops2.block_time is not null then false else true end as active + from ranked_operator_pi_split_records as rops + left join ranked_operator_pi_split_records as rops2 on ( + rops.operator = rops2.operator + -- rn is orderd by block and log_index, so this should encapsulate rops2 occurring afer rops + and rops.rn > rops2.rn + -- only find the next split that overlaps with the current one + and rops2.rounded_activated_at <= rops.rounded_activated_at + ) +), +-- filter in only splits flagged as active +active_operator_splits as ( + select + *, + rounded_activated_at as snapshot_time, + ROW_NUMBER() over (partition by operator order by rounded_activated_at asc) as rn + from decorated_operator_splits + where active = true ), -- Get the range for each operator operator_pi_split_windows as ( SELECT - operator, split, snapshot_time as start_time, + 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) + + -- need to subtract 1 day from the end time since generate_series will be inclusive below. + ELSE LEAD(snapshot_time) OVER (PARTITION BY operator ORDER BY snapshot_time) - interval '1 day' END AS end_time - FROM snapshotted_records + FROM active_operator_splits ), -- Clean up any records where start_time >= end_time cleaned_records as ( diff --git a/pkg/rewards/operatorPISplitSnapshots_test.go b/pkg/rewards/operatorPISplitSnapshots_test.go new file mode 100644 index 00000000..c4f04d53 --- /dev/null +++ b/pkg/rewards/operatorPISplitSnapshots_test.go @@ -0,0 +1,129 @@ +package rewards + +import ( + "fmt" + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/logger" + "github.com/Layr-Labs/sidecar/internal/tests" + "github.com/Layr-Labs/sidecar/pkg/postgres" + "github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "gorm.io/gorm" + "testing" +) + +func setupOperatorPISplitWindows() ( + string, + *config.Config, + *gorm.DB, + *zap.Logger, + error, +) { + testContext := getRewardsTestContext() + cfg := tests.GetConfig() + switch testContext { + case "testnet": + cfg.Chain = config.Chain_Holesky + case "testnet-reduced": + cfg.Chain = config.Chain_Holesky + case "mainnet-reduced": + cfg.Chain = config.Chain_Mainnet + default: + return "", nil, nil, nil, fmt.Errorf("Unknown test context") + } + + cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() + + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: cfg.Debug}) + + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l) + if err != nil { + return dbname, nil, nil, nil, err + } + + return dbname, cfg, grm, l, nil +} + +func teardownOperatorPISplitWindows(dbname string, cfg *config.Config, db *gorm.DB, l *zap.Logger) { + rawDb, _ := db.DB() + _ = rawDb.Close() + + pgConfig := postgres.PostgresConfigFromDbConfig(&cfg.DatabaseConfig) + + if err := postgres.DeleteTestDatabase(pgConfig, dbname); err != nil { + l.Sugar().Errorw("Failed to delete test database", "error", err) + } +} + +func hydrateOperatorPISplits(grm *gorm.DB, l *zap.Logger) error { + projectRoot := getProjectRootPath() + contents, err := tests.GetOperatorPISplitsSqlFile(projectRoot) + + if err != nil { + return err + } + + res := grm.Exec(contents) + if res.Error != nil { + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) + return res.Error + } + return nil +} + +func Test_OperatorPISplitSnapshots(t *testing.T) { + if !rewardsTestsEnabled() { + t.Skipf("Skipping %s", t.Name()) + return + } + + // projectRoot := getProjectRootPath() + dbFileName, cfg, grm, l, err := setupOperatorPISplitWindows() + if err != nil { + t.Fatal(err) + } + // testContext := getRewardsTestContext() + + snapshotDate := "2024-12-09" + + t.Run("Should hydrate dependency tables", func(t *testing.T) { + t.Log("Hydrating blocks") + + _, err := hydrateRewardsV2Blocks(grm, l) + assert.Nil(t, err) + + t.Log("Hydrating restaked strategies") + err = hydrateOperatorPISplits(grm, l) + if err != nil { + t.Fatal(err) + } + + query := `select count(*) from operator_pi_splits` + var count int + res := grm.Raw(query).Scan(&count) + + assert.Nil(t, res.Error) + + assert.Equal(t, 48, count) + }) + + t.Run("Should calculate correct operatorPISplit windows", func(t *testing.T) { + sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg) + rewards, _ := NewRewardsCalculator(cfg, grm, nil, sog, l) + + t.Log("Generating snapshots") + err := rewards.GenerateAndInsertOperatorPISplitSnapshots(snapshotDate) + assert.Nil(t, err) + + windows, err := rewards.ListOperatorPISplitSnapshots() + assert.Nil(t, err) + + t.Logf("Found %d windows", len(windows)) + + assert.Equal(t, 192, len(windows)) + }) + t.Cleanup(func() { + teardownOperatorPISplitWindows(dbFileName, cfg, grm, l) + }) +} diff --git a/pkg/rewards/operatorShares_test.go b/pkg/rewards/operatorShares_test.go index af42050c..20930efb 100644 --- a/pkg/rewards/operatorShares_test.go +++ b/pkg/rewards/operatorShares_test.go @@ -55,7 +55,7 @@ func hydrateOperatorShareDeltas(grm *gorm.DB, l *zap.Logger) error { res := grm.Exec(contents) if res.Error != nil { - l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error), zap.String("query", contents)) + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) return res.Error } return nil diff --git a/pkg/rewards/rewards.go b/pkg/rewards/rewards.go index a7b1e26c..7c072e4e 100644 --- a/pkg/rewards/rewards.go +++ b/pkg/rewards/rewards.go @@ -6,6 +6,8 @@ import ( "fmt" "time" + "sync/atomic" + "github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/distribution" "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators" @@ -18,7 +20,6 @@ import ( "gorm.io/gorm/clause" "slices" "strings" - "sync/atomic" ) type RewardsCalculator struct { @@ -215,7 +216,7 @@ func (rc *RewardsCalculator) MerkelizeRewardsForSnapshot(snapshotDate string) (* } func (rc *RewardsCalculator) GetMaxSnapshotDateForCutoffDate(cutoffDate string) (string, error) { - goldStagingTableName := rewardsUtils.GetGoldTableNames(cutoffDate)[rewardsUtils.Table_7_GoldStaging] + goldStagingTableName := rewardsUtils.GetGoldTableNames(cutoffDate)[rewardsUtils.Table_11_GoldStaging] var maxSnapshotStr string query := fmt.Sprintf(`select to_char(max(snapshot), 'YYYY-MM-DD') as snapshot from %s`, goldStagingTableName) @@ -604,6 +605,14 @@ func (rc *RewardsCalculator) generateSnapshotData(snapshotDate string) error { } rc.logger.Sugar().Debugw("Generated staker delegation snapshots") + // ------------------------------------------------------------------------ + // Rewards V2 snapshots + // ------------------------------------------------------------------------ + if err = rc.GenerateAndInsertOperatorDirectedRewards(snapshotDate); err != nil { + rc.logger.Sugar().Errorw("Failed to generate operator directed rewards", "error", err) + return err + } + rc.logger.Sugar().Debugw("Generated operator directed rewards") if err = rc.GenerateAndInsertOperatorAvsSplitSnapshots(snapshotDate); err != nil { rc.logger.Sugar().Errorw("Failed to generate operator avs split snapshots", "error", err) return err @@ -654,12 +663,32 @@ func (rc *RewardsCalculator) generateGoldTables(snapshotDate string) error { return err } - if err := rc.GenerateGold7StagingTable(snapshotDate); err != nil { + if err := rc.Generate7ActiveODRewards(snapshotDate); err != nil { + rc.logger.Sugar().Errorw("Failed to generate active od rewards", "error", err) + return err + } + + if err := rc.GenerateGold8OperatorODRewardAmountsTable(snapshotDate); err != nil { + rc.logger.Sugar().Errorw("Failed to generate operator od reward amounts", "error", err) + 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 + } + + if err := rc.GenerateGold11StagingTable(snapshotDate); err != nil { rc.logger.Sugar().Errorw("Failed to generate gold staging", "error", err) return err } - if err := rc.GenerateGold8FinalTable(snapshotDate); err != nil { + if err := rc.GenerateGold12FinalTable(snapshotDate); err != nil { rc.logger.Sugar().Errorw("Failed to generate final table", "error", err) return err } diff --git a/pkg/rewards/rewardsV2_test.go b/pkg/rewards/rewardsV2_test.go new file mode 100644 index 00000000..49d9c6ee --- /dev/null +++ b/pkg/rewards/rewardsV2_test.go @@ -0,0 +1,212 @@ +package rewards + +import ( + "fmt" + "testing" + "time" + + "github.com/Layr-Labs/sidecar/pkg/rewards/stakerOperators" + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + "github.com/stretchr/testify/assert" +) + +func Test_RewardsV2(t *testing.T) { + if !rewardsTestsEnabled() { + t.Skipf("Skipping %s", t.Name()) + return + } + + dbFileName, cfg, grm, l, err := setupRewardsV2() + fmt.Printf("Using db file: %+v\n", dbFileName) + + if err != nil { + t.Fatal(err) + } + + sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg) + + t.Run("Should initialize the rewards calculator", func(t *testing.T) { + rc, err := NewRewardsCalculator(cfg, grm, nil, sog, l) + assert.Nil(t, err) + if err != nil { + t.Fatal(err) + } + assert.NotNil(t, rc) + + fmt.Printf("DB Path: %+v\n", dbFileName) + + testStart := time.Now() + + // Setup all tables and source data + _, err = hydrateRewardsV2Blocks(grm, l) + assert.Nil(t, err) + + err = hydrateOperatorAvsStateChangesTable(grm, l) + assert.Nil(t, err) + + err = hydrateOperatorAvsRestakedStrategies(grm, l) + assert.Nil(t, err) + + err = hydrateOperatorShareDeltas(grm, l) + assert.Nil(t, err) + + err = hydrateStakerDelegations(grm, l) + assert.Nil(t, err) + + err = hydrateStakerShareDeltas(grm, l) + assert.Nil(t, err) + + err = hydrateRewardSubmissionsTable(grm, l) + assert.Nil(t, err) + + // RewardsV2 tables + err = hydrateOperatorAvsSplits(grm, l) + assert.Nil(t, err) + + err = hydrateOperatorPISplits(grm, l) + assert.Nil(t, err) + + err = hydrateOperatorDirectedRewardSubmissionsTable(grm, l) + assert.Nil(t, err) + + t.Log("Hydrated tables") + + snapshotDates := []string{ + "2024-12-11", + } + + fmt.Printf("Hydration duration: %v\n", time.Since(testStart)) + testStart = time.Now() + + for _, snapshotDate := range snapshotDates { + t.Log("-----------------------------\n") + + snapshotStartTime := time.Now() + + t.Logf("Generating rewards - snapshotDate: %s", snapshotDate) + // Generate snapshots + err = rc.generateSnapshotData(snapshotDate) + assert.Nil(t, err) + + goldTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) + + fmt.Printf("Snapshot duration: %v\n", time.Since(testStart)) + testStart = time.Now() + + t.Log("Generated and inserted snapshots") + forks, err := cfg.GetForkDates() + assert.Nil(t, err) + + fmt.Printf("Running gold_1_active_rewards\n") + err = rc.Generate1ActiveRewards(snapshotDate) + assert.Nil(t, err) + rows, err := getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_1_ActiveRewards]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_1_active_rewards: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_2_staker_reward_amounts %+v\n", time.Now()) + err = rc.GenerateGold2StakerRewardAmountsTable(snapshotDate, forks) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_2_StakerRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_2_staker_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_3_operator_reward_amounts\n") + err = rc.GenerateGold3OperatorRewardAmountsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_3_OperatorRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_3_operator_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_4_rewards_for_all\n") + err = rc.GenerateGold4RewardsForAllTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_4_RewardsForAll]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_4_rewards_for_all: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_5_rfae_stakers\n") + err = rc.GenerateGold5RfaeStakersTable(snapshotDate, forks) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_5_RfaeStakers]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_5_rfae_stakers: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_6_rfae_operators\n") + err = rc.GenerateGold6RfaeOperatorsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_6_RfaeOperators]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_6_rfae_operators: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + // ------------------------------------------------------------------------ + // Rewards V2 + // ------------------------------------------------------------------------ + fmt.Printf("Running gold_7_active_od_rewards\n") + err = rc.Generate7ActiveODRewards(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_7_ActiveODRewards]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_7_active_od_rewards: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_8_operator_od_reward_amounts\n") + err = rc.GenerateGold8OperatorODRewardAmountsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_8_operator_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_9_staker_od_reward_amounts\n") + err = rc.GenerateGold9StakerODRewardAmountsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_9_StakerODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_9_staker_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_10_avs_od_reward_amounts\n") + err = rc.GenerateGold10AvsODRewardAmountsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_10_AvsODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_10_avs_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_11_staging\n") + err = rc.GenerateGold11StagingTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_11_GoldStaging]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_11_staging: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_12_final_table\n") + err = rc.GenerateGold12FinalTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, "gold_table") + assert.Nil(t, err) + fmt.Printf("\tRows in gold_table: %v - [time: %v]\n", rows, time.Since(testStart)) + + goldRows, err := rc.ListGoldRows() + assert.Nil(t, err) + + t.Logf("Gold staging rows for snapshot %s: %d", snapshotDate, len(goldRows)) + + fmt.Printf("Total duration for rewards compute %s: %v\n", snapshotDate, time.Since(snapshotStartTime)) + testStart = time.Now() + } + + fmt.Printf("Done!\n\n") + t.Cleanup(func() { + // teardownRewards(dbFileName, cfg, grm, l) + }) + }) +} diff --git a/pkg/rewards/rewards_test.go b/pkg/rewards/rewards_test.go index 723db0a3..ddee53e5 100644 --- a/pkg/rewards/rewards_test.go +++ b/pkg/rewards/rewards_test.go @@ -3,6 +3,12 @@ package rewards import ( "errors" "fmt" + "os" + "path/filepath" + "strings" + "testing" + "time" + "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/internal/logger" "github.com/Layr-Labs/sidecar/internal/tests" @@ -14,11 +20,6 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/zap" "gorm.io/gorm" - "os" - "path/filepath" - "strings" - "testing" - "time" ) // const TOTAL_BLOCK_COUNT = 1229187 @@ -57,6 +58,8 @@ func getSnapshotDate() (string, error) { return "2024-07-25", nil case "mainnet-reduced": return "2024-08-12", nil + case "preprod-rewardsV2": + return "2024-12-09", nil } return "", fmt.Errorf("Unknown context: %s", context) } @@ -79,6 +82,24 @@ func hydrateAllBlocksTable(grm *gorm.DB, l *zap.Logger) (int, error) { return count, nil } +func hydrateRewardsV2Blocks(grm *gorm.DB, l *zap.Logger) (int, error) { + projectRoot := getProjectRootPath() + contents, err := tests.GetRewardsV2Blocks(projectRoot) + + if err != nil { + return 0, err + } + + count := len(strings.Split(strings.Trim(contents, "\n"), "\n")) - 1 + + res := grm.Exec(contents) + if res.Error != nil { + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) + return count, res.Error + } + return count, nil +} + func getRowCountForTable(grm *gorm.DB, tableName string) (int, error) { query := fmt.Sprintf("select count(*) as cnt from %s", tableName) var count int @@ -114,6 +135,30 @@ func setupRewards() ( return dbname, cfg, grm, l, nil } +func setupRewardsV2() ( + string, + *config.Config, + *gorm.DB, + *zap.Logger, + error, +) { + cfg := tests.GetConfig() + cfg.Rewards.GenerateStakerOperatorsTable = true + cfg.Rewards.ValidateRewardsRoot = true + cfg.Chain = config.Chain_Preprod + + cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() + + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: cfg.Debug}) + + dbname, _, grm, err := postgres.GetTestPostgresDatabase(cfg.DatabaseConfig, cfg, l) + if err != nil { + return dbname, nil, nil, nil, err + } + + return dbname, cfg, grm, l, nil +} + func Test_Rewards(t *testing.T) { if !rewardsTestsEnabled() { t.Skipf("Skipping %s", t.Name()) @@ -249,16 +294,48 @@ func Test_Rewards(t *testing.T) { fmt.Printf("\tRows in gold_6_rfae_operators: %v - [time: %v]\n", rows, time.Since(testStart)) testStart = time.Now() - fmt.Printf("Running gold_7_staging\n") - err = rc.GenerateGold7StagingTable(snapshotDate) + fmt.Printf("Running gold_7_active_od_rewards\n") + err = rc.Generate7ActiveODRewards(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_7_ActiveODRewards]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_7_active_od_rewards: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_8_operator_od_reward_amounts\n") + err = rc.GenerateGold8OperatorODRewardAmountsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_8_operator_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_9_staker_od_reward_amounts\n") + err = rc.GenerateGold9StakerODRewardAmountsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_9_StakerODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_9_staker_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_10_avs_od_reward_amounts\n") + err = rc.GenerateGold10AvsODRewardAmountsTable(snapshotDate) + assert.Nil(t, err) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_10_AvsODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_10_avs_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + testStart = time.Now() + + fmt.Printf("Running gold_11_staging\n") + err = rc.GenerateGold11StagingTable(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_7_GoldStaging]) + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_11_GoldStaging]) assert.Nil(t, err) - fmt.Printf("\tRows in gold_7_staging: %v - [time: %v]\n", rows, time.Since(testStart)) + fmt.Printf("\tRows in gold_11_staging: %v - [time: %v]\n", rows, time.Since(testStart)) testStart = time.Now() - fmt.Printf("Running gold_8_final_table\n") - err = rc.GenerateGold8FinalTable(snapshotDate) + fmt.Printf("Running gold_12_final_table\n") + err = rc.GenerateGold12FinalTable(snapshotDate) assert.Nil(t, err) rows, err = getRowCountForTable(grm, "gold_table") assert.Nil(t, err) diff --git a/pkg/rewards/stakerShareSnapshots_test.go b/pkg/rewards/stakerShareSnapshots_test.go index abd5dbe8..31526dd4 100644 --- a/pkg/rewards/stakerShareSnapshots_test.go +++ b/pkg/rewards/stakerShareSnapshots_test.go @@ -67,7 +67,7 @@ func hydrateStakerShares(grm *gorm.DB, l *zap.Logger) error { res := grm.Exec(contents) if res.Error != nil { - l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error), zap.String("query", contents)) + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) return res.Error } return nil diff --git a/pkg/rewards/stakerShares_test.go b/pkg/rewards/stakerShares_test.go index d536ef9c..dfd090b6 100644 --- a/pkg/rewards/stakerShares_test.go +++ b/pkg/rewards/stakerShares_test.go @@ -55,7 +55,7 @@ func hydrateStakerShareDeltas(grm *gorm.DB, l *zap.Logger) error { res := grm.Exec(contents) if res.Error != nil { - l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error), zap.String("query", contents)) + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) return res.Error } return nil diff --git a/pkg/rewards/tables.go b/pkg/rewards/tables.go index 519f70b4..624ce4f9 100644 --- a/pkg/rewards/tables.go +++ b/pkg/rewards/tables.go @@ -89,3 +89,21 @@ type OperatorPISplitSnapshots struct { Split uint64 Snapshot time.Time } + +type OperatorDirectedRewards struct { + Avs string + RewardHash string + Token string + Operator string + OperatorIndex uint64 + Amount string + Strategy string + StrategyIndex uint64 + Multiplier string + StartTimestamp *time.Time + EndTimestamp *time.Time + Duration uint64 + BlockNumber uint64 + TransactionHash string + LogIndex uint64 +} diff --git a/pkg/rewardsUtils/rewardsUtils.go b/pkg/rewardsUtils/rewardsUtils.go index fcfa28c6..10ab3b6e 100644 --- a/pkg/rewardsUtils/rewardsUtils.go +++ b/pkg/rewardsUtils/rewardsUtils.go @@ -3,36 +3,45 @@ package rewardsUtils import ( "bytes" "fmt" + "text/template" + "github.com/Layr-Labs/sidecar/pkg/postgres/helpers" "github.com/Layr-Labs/sidecar/pkg/utils" "go.uber.org/zap" "gorm.io/gorm" - "text/template" ) var ( - Table_1_ActiveRewards = "gold_1_active_rewards" - Table_2_StakerRewardAmounts = "gold_2_staker_reward_amounts" - Table_3_OperatorRewardAmounts = "gold_3_operator_reward_amounts" - Table_4_RewardsForAll = "gold_4_rewards_for_all" - Table_5_RfaeStakers = "gold_5_rfae_stakers" - Table_6_RfaeOperators = "gold_6_rfae_operators" - Table_7_GoldStaging = "gold_7_staging" - Table_8_GoldTable = "gold_table" + Table_1_ActiveRewards = "gold_1_active_rewards" + Table_2_StakerRewardAmounts = "gold_2_staker_reward_amounts" + Table_3_OperatorRewardAmounts = "gold_3_operator_reward_amounts" + Table_4_RewardsForAll = "gold_4_rewards_for_all" + Table_5_RfaeStakers = "gold_5_rfae_stakers" + Table_6_RfaeOperators = "gold_6_rfae_operators" + Table_7_ActiveODRewards = "gold_7_active_od_rewards" + Table_8_OperatorODRewardAmounts = "gold_8_operator_od_reward_amounts" + Table_9_StakerODRewardAmounts = "gold_9_staker_od_reward_amounts" + Table_10_AvsODRewardAmounts = "gold_10_avs_od_reward_amounts" + Table_11_GoldStaging = "gold_11_staging" + Table_12_GoldTable = "gold_table" Sot_6_StakerOperatorStaging = "sot_6_staker_operator_staging" Sot_7_StakerOperatorTable = "staker_operator" ) var goldTableBaseNames = map[string]string{ - Table_1_ActiveRewards: Table_1_ActiveRewards, - Table_2_StakerRewardAmounts: Table_2_StakerRewardAmounts, - Table_3_OperatorRewardAmounts: Table_3_OperatorRewardAmounts, - Table_4_RewardsForAll: Table_4_RewardsForAll, - Table_5_RfaeStakers: Table_5_RfaeStakers, - Table_6_RfaeOperators: Table_6_RfaeOperators, - Table_7_GoldStaging: Table_7_GoldStaging, - Table_8_GoldTable: Table_8_GoldTable, + Table_1_ActiveRewards: Table_1_ActiveRewards, + Table_2_StakerRewardAmounts: Table_2_StakerRewardAmounts, + Table_3_OperatorRewardAmounts: Table_3_OperatorRewardAmounts, + Table_4_RewardsForAll: Table_4_RewardsForAll, + Table_5_RfaeStakers: Table_5_RfaeStakers, + Table_6_RfaeOperators: Table_6_RfaeOperators, + Table_7_ActiveODRewards: Table_7_ActiveODRewards, + Table_8_OperatorODRewardAmounts: Table_8_OperatorODRewardAmounts, + Table_9_StakerODRewardAmounts: Table_9_StakerODRewardAmounts, + Table_10_AvsODRewardAmounts: Table_10_AvsODRewardAmounts, + Table_11_GoldStaging: Table_11_GoldStaging, + Table_12_GoldTable: Table_12_GoldTable, Sot_6_StakerOperatorStaging: Sot_6_StakerOperatorStaging, } diff --git a/scripts/updateTestData.sh b/scripts/updateTestData.sh index f026f7f7..2e2b30ef 100755 --- a/scripts/updateTestData.sh +++ b/scripts/updateTestData.sh @@ -41,6 +41,9 @@ fi if [[ $NETWORK == "testnet-reduced" ]]; then bucketPath="${bucketPath}testnet-reduced/" fi +if [[ $NETWORK == "preprod-rewardsv2" ]]; then + bucketPath="${bucketPath}preprod-rewardsv2/" +fi aws s3 cp "${newVersion}.tar" $bucketPath From 3fa4f24fcaf11976103b3b4cce6264e8fa661493 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Wed, 11 Dec 2024 15:51:05 -0600 Subject: [PATCH 40/55] Add check to see if rewards-v2 is enabled update snapshot date in test --- internal/config/config.go | 40 ++++++++++++++++++- pkg/pipeline/pipeline.go | 1 + pkg/rewards/10_goldAvsODRewardAmounts.go | 12 +++++- pkg/rewards/11_goldStaging.go | 21 ++++++++-- pkg/rewards/12_goldFinal.go | 2 +- pkg/rewards/1_goldActiveRewards.go | 2 +- pkg/rewards/2_goldStakerRewardAmounts.go | 2 +- pkg/rewards/3_goldOperatorRewardAmounts.go | 2 +- pkg/rewards/4_goldRewardsForAll.go | 2 +- pkg/rewards/5_goldRfaeStakers.go | 2 +- pkg/rewards/6_goldRfaeOperators.go | 2 +- pkg/rewards/7_goldActiveODRewards.go | 12 +++++- pkg/rewards/8_goldOperatorODRewardAmounts.go | 11 ++++- pkg/rewards/9_goldStakerODRewardAmounts.go | 12 +++++- pkg/rewards/combinedRewards.go | 2 +- .../operatorAvsRegistrationSnaphots.go | 2 +- pkg/rewards/operatorAvsSplitSnapshots.go | 2 +- pkg/rewards/operatorAvsStrategySnapshots.go | 2 +- pkg/rewards/operatorDirectedRewards.go | 2 +- pkg/rewards/operatorPISplitSnapshots.go | 2 +- pkg/rewards/operatorShareSnapshots.go | 2 +- pkg/rewards/operatorShares.go | 2 +- pkg/rewards/rewards.go | 2 +- pkg/rewards/rewardsV2_test.go | 37 +++++++++++------ pkg/rewards/stakerDelegationSnapshots.go | 2 +- .../1_stakerStrategyPayouts.go | 2 +- .../2_operatorStrategyRewards.go | 2 +- .../3_rewardsForAllStrategyPayouts.go | 2 +- .../4_rfaeStakerStrategyPayouts.go | 2 +- .../5_rfaeOperatorStrategyPayout.go | 2 +- .../6_stakerOperatorStaging.go | 2 +- .../stakerOperators/7_stakerOperator.go | 2 +- pkg/rewards/stakerShareSnapshots.go | 2 +- pkg/rewards/stakerShares.go | 2 +- pkg/rewardsUtils/rewardsUtils.go | 2 +- 35 files changed, 150 insertions(+), 50 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index a6763ab1..a263bb5f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" "strings" + "time" "github.com/spf13/viper" ) @@ -254,14 +255,14 @@ 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", + Fork_Arno: "2024-12-12", }, 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-12", + Fork_Arno: "2024-12-13", }, nil case Chain_Mainnet: return ForkMap{ @@ -296,6 +297,41 @@ func (c *Config) GetOperatorRestakedStrategiesStartBlock() uint64 { return 0 } +func (c *Config) ShouldSkipRewardsGeneration(blockNumber uint64) bool { + switch c.Chain { + case Chain_Preprod: + // During this period we deployed the rewards-v2 contracts before updating the sidecar. + // This results in missed events which have to be filled by some means. To fill them in, + // we needed to manually delete delete blocks >= 2871534 and re-index. The trouble here + // is that re-indexing introduces new state which was not present at the original process time. + if blockNumber >= 2871534 && blockNumber <= 2909856 { + return true + } + case Chain_Holesky: + // Skip rewards generation for holesky + case Chain_Mainnet: + // Skip rewards generation for mainnet + } + return false +} + +func (c *Config) IsRewardsV2EnabledForCutoffDate(cutoffDate string) (bool, error) { + forks, err := c.GetForkDates() + if err != nil { + return false, err + } + cutoffDateTime, err := time.Parse(time.DateOnly, cutoffDate) + if err != nil { + return false, errors.Join(fmt.Errorf("failed to parse cutoff date %s", cutoffDate), err) + } + arnoForkDateTime, err := time.Parse(time.DateOnly, forks[Fork_Arno]) + if err != nil { + return false, errors.Join(fmt.Errorf("failed to parse Arno fork date %s", forks[Fork_Arno]), err) + } + + return cutoffDateTime.Compare(arnoForkDateTime) >= 0, nil +} + // CanIgnoreIncorrectRewardsRoot returns true if the rewards root can be ignored for the given block number // // Due to inconsistencies in the rewards root calculation on testnet, we know that some roots diff --git a/pkg/pipeline/pipeline.go b/pkg/pipeline/pipeline.go index 957cdcaf..15d504d2 100644 --- a/pkg/pipeline/pipeline.go +++ b/pkg/pipeline/pipeline.go @@ -186,6 +186,7 @@ func (p *Pipeline) RunForFetchedBlock(ctx context.Context, block *fetcher.Fetche distributionRoots, err := p.stateManager.GetSubmittedDistributionRoots(blockNumber) if err == nil && distributionRoots != nil { for _, rs := range distributionRoots { + rewardStartTime := time.Now() // first check to see if the root was disabled. If it was, it's possible we introduced changes that diff --git a/pkg/rewards/10_goldAvsODRewardAmounts.go b/pkg/rewards/10_goldAvsODRewardAmounts.go index bd5e7180..19b5f77e 100644 --- a/pkg/rewards/10_goldAvsODRewardAmounts.go +++ b/pkg/rewards/10_goldAvsODRewardAmounts.go @@ -65,6 +65,16 @@ SELECT * FROM operator_token_sums ` func (rc *RewardsCalculator) GenerateGold10AvsODRewardAmountsTable(snapshotDate string) error { + rewardsV2Enabled, err := rc.globalConfig.IsRewardsV2EnabledForCutoffDate(snapshotDate) + if err != nil { + rc.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + if !rewardsV2Enabled { + rc.logger.Sugar().Infow("Rewards v2 is not enabled for this cutoff date, skipping GenerateGold10AvsODRewardAmountsTable") + return nil + } + allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) destTableName := allTableNames[rewardsUtils.Table_10_AvsODRewardAmounts] @@ -73,7 +83,7 @@ func (rc *RewardsCalculator) GenerateGold10AvsODRewardAmountsTable(snapshotDate zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_10_goldAvsODRewardAmountsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_10_goldAvsODRewardAmountsQuery, map[string]interface{}{ "destTableName": destTableName, "activeODRewardsTable": allTableNames[rewardsUtils.Table_7_ActiveODRewards], }) diff --git a/pkg/rewards/11_goldStaging.go b/pkg/rewards/11_goldStaging.go index 677078ae..2ad37727 100644 --- a/pkg/rewards/11_goldStaging.go +++ b/pkg/rewards/11_goldStaging.go @@ -56,6 +56,7 @@ rewards_for_all_earners_operators AS ( operator_tokens as amount FROM {{.rfaeOperatorTable}} ), +{{ if .enableRewardsV2 }} operator_od_rewards AS ( SELECT DISTINCT -- We can select DISTINCT here because the operator's tokens are the same for each strategy in the reward hash @@ -86,6 +87,7 @@ avs_od_rewards AS ( avs_tokens as amount FROM {{.avsODRewardAmountsTable}} ), +{{ end }} combined_rewards AS ( SELECT * FROM operator_rewards UNION ALL @@ -96,12 +98,14 @@ combined_rewards AS ( SELECT * FROM rewards_for_all_earners_stakers UNION ALL SELECT * FROM rewards_for_all_earners_operators +{{ if .enableRewardsV2 }} UNION ALL SELECT * FROM operator_od_rewards UNION ALL SELECT * FROM staker_od_rewards UNION ALL SELECT * FROM avs_od_rewards +{{ end }} ), -- Dedupe earners, primarily operators who are also their own staker. deduped_earners AS ( @@ -131,7 +135,14 @@ func (rc *RewardsCalculator) GenerateGold11StagingTable(snapshotDate string) err zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_11_goldStagingQuery, map[string]string{ + isRewardsV2Enabled, err := rc.globalConfig.IsRewardsV2EnabledForCutoffDate(snapshotDate) + if err != nil { + rc.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + rc.logger.Sugar().Infow("Is RewardsV2 enabled?", "enabled", isRewardsV2Enabled) + + query, err := rewardsUtils.RenderQueryTemplate(_11_goldStagingQuery, map[string]interface{}{ "destTableName": destTableName, "stakerRewardAmountsTable": allTableNames[rewardsUtils.Table_2_StakerRewardAmounts], "operatorRewardAmountsTable": allTableNames[rewardsUtils.Table_3_OperatorRewardAmounts], @@ -141,6 +152,7 @@ func (rc *RewardsCalculator) GenerateGold11StagingTable(snapshotDate string) err "operatorODRewardAmountsTable": allTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts], "stakerODRewardAmountsTable": allTableNames[rewardsUtils.Table_9_StakerODRewardAmounts], "avsODRewardAmountsTable": allTableNames[rewardsUtils.Table_10_AvsODRewardAmounts], + "enableRewardsV2": isRewardsV2Enabled, }) if err != nil { rc.logger.Sugar().Errorw("Failed to render query template", "error", err) @@ -167,21 +179,22 @@ func (rc *RewardsCalculator) ListGoldStagingRowsForSnapshot(snapshotDate string) allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) results := make([]*GoldStagingRow, 0) - query, err := rewardsUtils.RenderQueryTemplate(` + query := ` SELECT earner, snapshot::text as snapshot, reward_hash, token, amount - FROM {{.goldStagingTable}} WHERE DATE(snapshot) < @cutoffDate`, map[string]string{ + FROM {{.goldStagingTable}} WHERE DATE(snapshot) < @cutoffDate` + query, err := rewardsUtils.RenderQueryTemplate(query, map[string]interface{}{ "goldStagingTable": allTableNames[rewardsUtils.Table_11_GoldStaging], }) if err != nil { rc.logger.Sugar().Errorw("Failed to render query template", "error", err) return nil, err } - res := rc.grm.Raw(query, + res := rc.grm.Debug().Raw(query, sql.Named("cutoffDate", snapshotDate), ).Scan(&results) if res.Error != nil { diff --git a/pkg/rewards/12_goldFinal.go b/pkg/rewards/12_goldFinal.go index c65e7096..f993b7b2 100644 --- a/pkg/rewards/12_goldFinal.go +++ b/pkg/rewards/12_goldFinal.go @@ -33,7 +33,7 @@ func (rc *RewardsCalculator) GenerateGold12FinalTable(snapshotDate string) error zap.String("cutoffDate", snapshotDate), ) - query, err := rewardsUtils.RenderQueryTemplate(_12_goldFinalQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_12_goldFinalQuery, map[string]interface{}{ "goldStagingTable": allTableNames[rewardsUtils.Table_11_GoldStaging], }) if err != nil { diff --git a/pkg/rewards/1_goldActiveRewards.go b/pkg/rewards/1_goldActiveRewards.go index 9c9ad3c2..d5a7377a 100644 --- a/pkg/rewards/1_goldActiveRewards.go +++ b/pkg/rewards/1_goldActiveRewards.go @@ -112,7 +112,7 @@ func (r *RewardsCalculator) Generate1ActiveRewards(snapshotDate string) error { zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_1_goldActiveRewardsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_1_goldActiveRewardsQuery, map[string]interface{}{ "destTableName": destTableName, "rewardsStart": rewardsStart, "cutoffDate": snapshotDate, diff --git a/pkg/rewards/2_goldStakerRewardAmounts.go b/pkg/rewards/2_goldStakerRewardAmounts.go index a4f510cc..52cbcb76 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -153,7 +153,7 @@ func (rc *RewardsCalculator) GenerateGold2StakerRewardAmountsTable(snapshotDate zap.String("arnoHardforkDate", forks[config.Fork_Arno]), ) - query, err := rewardsUtils.RenderQueryTemplate(_2_goldStakerRewardAmountsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_2_goldStakerRewardAmountsQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], }) diff --git a/pkg/rewards/3_goldOperatorRewardAmounts.go b/pkg/rewards/3_goldOperatorRewardAmounts.go index 787ec446..13e76ace 100644 --- a/pkg/rewards/3_goldOperatorRewardAmounts.go +++ b/pkg/rewards/3_goldOperatorRewardAmounts.go @@ -45,7 +45,7 @@ func (rc *RewardsCalculator) GenerateGold3OperatorRewardAmountsTable(snapshotDat zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_3_goldOperatorRewardAmountsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_3_goldOperatorRewardAmountsQuery, map[string]interface{}{ "destTableName": destTableName, "stakerRewardAmountsTable": allTableNames[rewardsUtils.Table_2_StakerRewardAmounts], }) diff --git a/pkg/rewards/4_goldRewardsForAll.go b/pkg/rewards/4_goldRewardsForAll.go index a9464c76..46038352 100644 --- a/pkg/rewards/4_goldRewardsForAll.go +++ b/pkg/rewards/4_goldRewardsForAll.go @@ -76,7 +76,7 @@ func (rc *RewardsCalculator) GenerateGold4RewardsForAllTable(snapshotDate string zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_4_goldRewardsForAllQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_4_goldRewardsForAllQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], }) diff --git a/pkg/rewards/5_goldRfaeStakers.go b/pkg/rewards/5_goldRfaeStakers.go index 9d1fd7aa..af605f5f 100644 --- a/pkg/rewards/5_goldRfaeStakers.go +++ b/pkg/rewards/5_goldRfaeStakers.go @@ -142,7 +142,7 @@ func (rc *RewardsCalculator) GenerateGold5RfaeStakersTable(snapshotDate string, zap.String("arnoHardforkDate", forks[config.Fork_Arno]), ) - query, err := rewardsUtils.RenderQueryTemplate(_5_goldRfaeStakersQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_5_goldRfaeStakersQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], }) diff --git a/pkg/rewards/6_goldRfaeOperators.go b/pkg/rewards/6_goldRfaeOperators.go index c17d80fe..2def909d 100644 --- a/pkg/rewards/6_goldRfaeOperators.go +++ b/pkg/rewards/6_goldRfaeOperators.go @@ -45,7 +45,7 @@ func (rc *RewardsCalculator) GenerateGold6RfaeOperatorsTable(snapshotDate string zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_6_goldRfaeOperatorsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_6_goldRfaeOperatorsQuery, map[string]interface{}{ "destTableName": destTableName, "rfaeStakersTable": allTableNames[rewardsUtils.Table_5_RfaeStakers], }) diff --git a/pkg/rewards/7_goldActiveODRewards.go b/pkg/rewards/7_goldActiveODRewards.go index ee84cc1d..902c9438 100644 --- a/pkg/rewards/7_goldActiveODRewards.go +++ b/pkg/rewards/7_goldActiveODRewards.go @@ -127,6 +127,16 @@ SELECT * FROM active_rewards_final // @param startDate: The lower bound of when to calculate rewards from. If we're running rewards for the first time, // this will be "1970-01-01". If this is a subsequent run, this will be the last snapshot date. func (r *RewardsCalculator) Generate7ActiveODRewards(snapshotDate string) error { + rewardsV2Enabled, err := r.globalConfig.IsRewardsV2EnabledForCutoffDate(snapshotDate) + if err != nil { + r.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + if !rewardsV2Enabled { + r.logger.Sugar().Infow("Rewards v2 is not enabled for this cutoff date, skipping Generate7ActiveODRewards") + return nil + } + allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) destTableName := allTableNames[rewardsUtils.Table_7_ActiveODRewards] @@ -138,7 +148,7 @@ func (r *RewardsCalculator) Generate7ActiveODRewards(snapshotDate string) error zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_7_goldActiveODRewardsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_7_goldActiveODRewardsQuery, map[string]interface{}{ "destTableName": destTableName, "rewardsStart": rewardsStart, "cutoffDate": snapshotDate, diff --git a/pkg/rewards/8_goldOperatorODRewardAmounts.go b/pkg/rewards/8_goldOperatorODRewardAmounts.go index 8ae90773..5bb9718f 100644 --- a/pkg/rewards/8_goldOperatorODRewardAmounts.go +++ b/pkg/rewards/8_goldOperatorODRewardAmounts.go @@ -66,6 +66,15 @@ SELECT * FROM operator_splits ` func (rc *RewardsCalculator) GenerateGold8OperatorODRewardAmountsTable(snapshotDate string) error { + rewardsV2Enabled, err := rc.globalConfig.IsRewardsV2EnabledForCutoffDate(snapshotDate) + if err != nil { + rc.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + if !rewardsV2Enabled { + rc.logger.Sugar().Infow("Rewards v2 is not enabled for this cutoff date, skipping GenerateGold8OperatorODRewardAmountsTable") + return nil + } allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) destTableName := allTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts] @@ -74,7 +83,7 @@ func (rc *RewardsCalculator) GenerateGold8OperatorODRewardAmountsTable(snapshotD zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_8_goldOperatorODRewardAmountsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_8_goldOperatorODRewardAmountsQuery, map[string]interface{}{ "destTableName": destTableName, "activeODRewardsTable": allTableNames[rewardsUtils.Table_7_ActiveODRewards], }) diff --git a/pkg/rewards/9_goldStakerODRewardAmounts.go b/pkg/rewards/9_goldStakerODRewardAmounts.go index 5b66a75f..5956f39c 100644 --- a/pkg/rewards/9_goldStakerODRewardAmounts.go +++ b/pkg/rewards/9_goldStakerODRewardAmounts.go @@ -115,6 +115,16 @@ SELECT * FROM staker_reward_amounts ` func (rc *RewardsCalculator) GenerateGold9StakerODRewardAmountsTable(snapshotDate string) error { + rewardsV2Enabled, err := rc.globalConfig.IsRewardsV2EnabledForCutoffDate(snapshotDate) + if err != nil { + rc.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + if !rewardsV2Enabled { + rc.logger.Sugar().Infow("Rewards v2 is not enabled for this cutoff date, skipping GenerateGold9StakerODRewardAmountsTable") + return nil + } + allTableNames := rewardsUtils.GetGoldTableNames(snapshotDate) destTableName := allTableNames[rewardsUtils.Table_9_StakerODRewardAmounts] @@ -123,7 +133,7 @@ func (rc *RewardsCalculator) GenerateGold9StakerODRewardAmountsTable(snapshotDat zap.String("destTableName", destTableName), ) - query, err := rewardsUtils.RenderQueryTemplate(_9_goldStakerODRewardAmountsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_9_goldStakerODRewardAmountsQuery, map[string]interface{}{ "destTableName": destTableName, "activeODRewardsTable": allTableNames[rewardsUtils.Table_7_ActiveODRewards], }) diff --git a/pkg/rewards/combinedRewards.go b/pkg/rewards/combinedRewards.go index 290eba6d..c4d90cf0 100644 --- a/pkg/rewards/combinedRewards.go +++ b/pkg/rewards/combinedRewards.go @@ -45,7 +45,7 @@ const rewardsCombinedQuery = ` func (r *RewardsCalculator) GenerateAndInsertCombinedRewards(snapshotDate string) error { tableName := "combined_rewards" - query, err := rewardsUtils.RenderQueryTemplate(rewardsCombinedQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(rewardsCombinedQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorAvsRegistrationSnaphots.go b/pkg/rewards/operatorAvsRegistrationSnaphots.go index 9ea856e3..fc16a4d7 100644 --- a/pkg/rewards/operatorAvsRegistrationSnaphots.go +++ b/pkg/rewards/operatorAvsRegistrationSnaphots.go @@ -105,7 +105,7 @@ CROSS JOIN generate_series(DATE(start_time), DATE(end_time) - interval '1' day, func (r *RewardsCalculator) GenerateAndInsertOperatorAvsRegistrationSnapshots(snapshotDate string) error { tableName := "operator_avs_registration_snapshots" - query, err := rewardsUtils.RenderQueryTemplate(operatorAvsRegistrationSnapshotsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorAvsRegistrationSnapshotsQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorAvsSplitSnapshots.go b/pkg/rewards/operatorAvsSplitSnapshots.go index 5456af2b..30a593d8 100644 --- a/pkg/rewards/operatorAvsSplitSnapshots.go +++ b/pkg/rewards/operatorAvsSplitSnapshots.go @@ -86,7 +86,7 @@ select * from final_results func (r *RewardsCalculator) GenerateAndInsertOperatorAvsSplitSnapshots(snapshotDate string) error { tableName := "operator_avs_split_snapshots" - query, err := rewardsUtils.RenderQueryTemplate(operatorAvsSplitSnapshotQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorAvsSplitSnapshotQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorAvsStrategySnapshots.go b/pkg/rewards/operatorAvsStrategySnapshots.go index 7e4f2174..3a15d93f 100644 --- a/pkg/rewards/operatorAvsStrategySnapshots.go +++ b/pkg/rewards/operatorAvsStrategySnapshots.go @@ -144,7 +144,7 @@ func (r *RewardsCalculator) GenerateAndInsertOperatorAvsStrategySnapshots(snapsh tableName := "operator_avs_strategy_snapshots" contractAddresses := r.globalConfig.GetContractsMapForChain() - query, err := rewardsUtils.RenderQueryTemplate(operatorAvsStrategyWindowsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorAvsStrategyWindowsQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorDirectedRewards.go b/pkg/rewards/operatorDirectedRewards.go index d0fa074c..9cc7d896 100644 --- a/pkg/rewards/operatorDirectedRewards.go +++ b/pkg/rewards/operatorDirectedRewards.go @@ -46,7 +46,7 @@ const operatorDirectedRewardsQuery = ` func (r *RewardsCalculator) GenerateAndInsertOperatorDirectedRewards(snapshotDate string) error { tableName := "operator_directed_rewards" - query, err := rewardsUtils.RenderQueryTemplate(operatorDirectedRewardsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorDirectedRewardsQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorPISplitSnapshots.go b/pkg/rewards/operatorPISplitSnapshots.go index ef212e47..2f9a5ed7 100644 --- a/pkg/rewards/operatorPISplitSnapshots.go +++ b/pkg/rewards/operatorPISplitSnapshots.go @@ -85,7 +85,7 @@ select * from final_results func (r *RewardsCalculator) GenerateAndInsertOperatorPISplitSnapshots(snapshotDate string) error { tableName := "operator_pi_split_snapshots" - query, err := rewardsUtils.RenderQueryTemplate(operatorPISplitSnapshotQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorPISplitSnapshotQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorShareSnapshots.go b/pkg/rewards/operatorShareSnapshots.go index f83e003e..35aa78d8 100644 --- a/pkg/rewards/operatorShareSnapshots.go +++ b/pkg/rewards/operatorShareSnapshots.go @@ -50,7 +50,7 @@ FROM func (r *RewardsCalculator) GenerateAndInsertOperatorShareSnapshots(snapshotDate string) error { tableName := "operator_share_snapshots" - query, err := rewardsUtils.RenderQueryTemplate(operatorShareSnapshotsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorShareSnapshotsQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/operatorShares.go b/pkg/rewards/operatorShares.go index 88c42517..3af9775a 100644 --- a/pkg/rewards/operatorShares.go +++ b/pkg/rewards/operatorShares.go @@ -19,7 +19,7 @@ const operatorSharesQuery = ` func (r *RewardsCalculator) GenerateAndInsertOperatorShares(snapshotDate string) error { tableName := "operator_shares" - query, err := rewardsUtils.RenderQueryTemplate(operatorSharesQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(operatorSharesQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/rewards.go b/pkg/rewards/rewards.go index 7c072e4e..c8d714c8 100644 --- a/pkg/rewards/rewards.go +++ b/pkg/rewards/rewards.go @@ -493,7 +493,7 @@ func (rc *RewardsCalculator) fetchRewardsForSnapshot(snapshotDate string) ([]*Re where snapshot <= date '{{.snapshotDate}}' group by 1, 2 order by snapshot desc - `, map[string]string{"snapshotDate": snapshotDate}) + `, map[string]interface{}{"snapshotDate": snapshotDate}) if err != nil { return nil, err diff --git a/pkg/rewards/rewardsV2_test.go b/pkg/rewards/rewardsV2_test.go index 49d9c6ee..cd890901 100644 --- a/pkg/rewards/rewardsV2_test.go +++ b/pkg/rewards/rewardsV2_test.go @@ -72,7 +72,7 @@ func Test_RewardsV2(t *testing.T) { t.Log("Hydrated tables") snapshotDates := []string{ - "2024-12-11", + "2024-12-12", } fmt.Printf("Hydration duration: %v\n", time.Since(testStart)) @@ -148,36 +148,47 @@ func Test_RewardsV2(t *testing.T) { // ------------------------------------------------------------------------ // Rewards V2 // ------------------------------------------------------------------------ + rewardsV2Enabled, err := cfg.IsRewardsV2EnabledForCutoffDate(snapshotDate) + assert.Nil(t, err) + fmt.Printf("Running gold_7_active_od_rewards\n") err = rc.Generate7ActiveODRewards(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_7_ActiveODRewards]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_7_active_od_rewards: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_7_ActiveODRewards]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_7_active_od_rewards: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_8_operator_od_reward_amounts\n") err = rc.GenerateGold8OperatorODRewardAmountsTable(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_8_operator_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_8_operator_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_9_staker_od_reward_amounts\n") err = rc.GenerateGold9StakerODRewardAmountsTable(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_9_StakerODRewardAmounts]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_9_staker_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_9_StakerODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_9_staker_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_10_avs_od_reward_amounts\n") err = rc.GenerateGold10AvsODRewardAmountsTable(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_10_AvsODRewardAmounts]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_10_avs_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_10_AvsODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_10_avs_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_11_staging\n") diff --git a/pkg/rewards/stakerDelegationSnapshots.go b/pkg/rewards/stakerDelegationSnapshots.go index 58edc9ad..117d2018 100644 --- a/pkg/rewards/stakerDelegationSnapshots.go +++ b/pkg/rewards/stakerDelegationSnapshots.go @@ -62,7 +62,7 @@ select * from final_results func (r *RewardsCalculator) GenerateAndInsertStakerDelegationSnapshots(snapshotDate string) error { tableName := "staker_delegation_snapshots" - query, err := rewardsUtils.RenderQueryTemplate(stakerDelegationSnapshotsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(stakerDelegationSnapshotsQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go b/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go index 88bda541..6ad5dbcc 100644 --- a/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go +++ b/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go @@ -128,7 +128,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert1StakerStrategyPayouts(cut tableName := "sot_1_staker_strategy_payouts" allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) - query, err := rewardsUtils.RenderQueryTemplate(_1_stakerStrategyPayoutsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_1_stakerStrategyPayoutsQuery, map[string]interface{}{ "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], "stakerRewardAmountsTable": allTableNames[rewardsUtils.Table_2_StakerRewardAmounts], }) diff --git a/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go b/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go index 0a0824b6..d9fa1d15 100644 --- a/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go +++ b/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go @@ -112,7 +112,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert2OperatorStrategyRewards(c tableName := "sot_2_operator_strategy_rewards" allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) - query, err := rewardsUtils.RenderQueryTemplate(_2_operatorStrategyRewardsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_2_operatorStrategyRewardsQuery, map[string]interface{}{ "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], "operatorRewardAmountsTable": allTableNames[rewardsUtils.Table_3_OperatorRewardAmounts], }) diff --git a/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go b/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go index 4c95ad7b..082dd1eb 100644 --- a/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go +++ b/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go @@ -102,7 +102,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert3RewardsForAllStrategyPayo tableName := "sot_3_rewards_for_all_strategy_payout" allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) - query, err := rewardsUtils.RenderQueryTemplate(_3_rewardsForAllStrategyPayoutsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_3_rewardsForAllStrategyPayoutsQuery, map[string]interface{}{ "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], "rewardsForAllTable": allTableNames[rewardsUtils.Table_4_RewardsForAll], }) diff --git a/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go b/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go index dfb1f71b..4968c0a1 100644 --- a/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go +++ b/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go @@ -134,7 +134,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert4RfaeStakerStrategyPayout( tableName := "sot_4_rfae_staker_strategy_payout" allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) - query, err := rewardsUtils.RenderQueryTemplate(_4_rfaeStakerStrategyPayoutsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_4_rfaeStakerStrategyPayoutsQuery, map[string]interface{}{ "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], "rfaeStakerTable": allTableNames[rewardsUtils.Table_5_RfaeStakers], }) diff --git a/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go b/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go index 7a532572..a0d35ce3 100644 --- a/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go +++ b/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go @@ -123,7 +123,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert5RfaeOperatorStrategyPayou tableName := "sot_5_rfae_operator_strategy_payout" allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) - query, err := rewardsUtils.RenderQueryTemplate(_5_rfaeOperatorStrategyPayoutsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_5_rfaeOperatorStrategyPayoutsQuery, map[string]interface{}{ "activeRewardsTable": allTableNames[rewardsUtils.Table_1_ActiveRewards], "rfaeOperatorTable": allTableNames[rewardsUtils.Table_6_RfaeOperators], }) diff --git a/pkg/rewards/stakerOperators/6_stakerOperatorStaging.go b/pkg/rewards/stakerOperators/6_stakerOperatorStaging.go index 151c6021..514f98eb 100644 --- a/pkg/rewards/stakerOperators/6_stakerOperatorStaging.go +++ b/pkg/rewards/stakerOperators/6_stakerOperatorStaging.go @@ -113,7 +113,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert6StakerOperatorStaging(cut zap.String("cutoffDate", cutoffDate), ) - query, err := rewardsUtils.RenderQueryTemplate(_6_stakerOperatorsStaging, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_6_stakerOperatorsStaging, map[string]interface{}{ "destTableName": destTableName, }) if err != nil { diff --git a/pkg/rewards/stakerOperators/7_stakerOperator.go b/pkg/rewards/stakerOperators/7_stakerOperator.go index 8d54d54d..2f5bb9f6 100644 --- a/pkg/rewards/stakerOperators/7_stakerOperator.go +++ b/pkg/rewards/stakerOperators/7_stakerOperator.go @@ -62,7 +62,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert7StakerOperator(cutoffDate zap.String("cutoffDate", cutoffDate), ) - query, err := rewardsUtils.RenderQueryTemplate(_7_stakerOperator, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_7_stakerOperator, map[string]interface{}{ "destTableName": destTableName, "stakerOperatorStaging": allTableNames[rewardsUtils.Sot_6_StakerOperatorStaging], }) diff --git a/pkg/rewards/stakerShareSnapshots.go b/pkg/rewards/stakerShareSnapshots.go index f2a2640c..1fdace1b 100644 --- a/pkg/rewards/stakerShareSnapshots.go +++ b/pkg/rewards/stakerShareSnapshots.go @@ -50,7 +50,7 @@ CROSS JOIN func (r *RewardsCalculator) GenerateAndInsertStakerShareSnapshots(snapshotDate string) error { tableName := "staker_share_snapshots" - query, err := rewardsUtils.RenderQueryTemplate(stakerShareSnapshotsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(stakerShareSnapshotsQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewards/stakerShares.go b/pkg/rewards/stakerShares.go index 66fda671..a8b16171 100644 --- a/pkg/rewards/stakerShares.go +++ b/pkg/rewards/stakerShares.go @@ -21,7 +21,7 @@ const stakerSharesQuery = ` func (r *RewardsCalculator) GenerateAndInsertStakerShares(snapshotDate string) error { tableName := "staker_shares" - query, err := rewardsUtils.RenderQueryTemplate(stakerSharesQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(stakerSharesQuery, map[string]interface{}{ "cutoffDate": snapshotDate, }) if err != nil { diff --git a/pkg/rewardsUtils/rewardsUtils.go b/pkg/rewardsUtils/rewardsUtils.go index 10ab3b6e..5d5ac980 100644 --- a/pkg/rewardsUtils/rewardsUtils.go +++ b/pkg/rewardsUtils/rewardsUtils.go @@ -54,7 +54,7 @@ func GetGoldTableNames(snapshotDate string) map[string]string { return tableNames } -func RenderQueryTemplate(query string, variables map[string]string) (string, error) { +func RenderQueryTemplate(query string, variables map[string]interface{}) (string, error) { queryTmpl := template.Must(template.New("").Parse(query)) var dest bytes.Buffer From 82f96e243d13005242f4114e22f25d3c94584b6c Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Wed, 11 Dec 2024 16:15:01 -0600 Subject: [PATCH 41/55] Updated testdata version to 1490a0e18dd51ffbb59209ca9cf6513268af21f5 --- .testdataVersion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.testdataVersion b/.testdataVersion index 193e406f..62872b9f 100644 --- a/.testdataVersion +++ b/.testdataVersion @@ -1 +1 @@ -d67ef5d895bdc0ccd6a006a1683ac3e58f820ad0 \ No newline at end of file +1490a0e18dd51ffbb59209ca9cf6513268af21f5 \ No newline at end of file From 9a16a6d457fb643f6f4a870110d9fdd3d36385c4 Mon Sep 17 00:00:00 2001 From: gpsanant Date: Wed, 11 Dec 2024 20:49:51 -0800 Subject: [PATCH 42/55] feat: rewards-v2 pay by snapshots --- .gitignore | 1 + go.mod | 1 + go.sum | 2 + internal/config/config.go | 2 +- pkg/rewards/10_goldAvsODRewardAmounts.go | 13 ++-- pkg/rewards/7_goldActiveODRewards.go | 71 +++++++++++++++++--- pkg/rewards/8_goldOperatorODRewardAmounts.go | 6 +- pkg/rewards/9_goldStakerODRewardAmounts.go | 6 +- pkg/rewards/operatorDirectedRewards.go | 2 +- pkg/rewards/rewardsV2_test.go | 7 ++ 10 files changed, 86 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index c367e7a3..58930609 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ chart_releases /snapshots/**/*.sql /snapshots/**/*.csv +.env \ No newline at end of file diff --git a/go.mod b/go.mod index 932ecec8..161a6aea 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/wealdtech/go-merkletree/v2 v2.6.0 github.com/wk8/go-ordered-map/v2 v2.1.8 go.uber.org/zap v1.27.0 + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 google.golang.org/grpc v1.65.0 gorm.io/driver/postgres v1.5.9 gorm.io/gorm v1.25.10 diff --git a/go.sum b/go.sum index c09d6acd..0c8f4b20 100644 --- a/go.sum +++ b/go.sum @@ -670,6 +670,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/internal/config/config.go b/internal/config/config.go index a263bb5f..b3cef47e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -255,7 +255,7 @@ 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-12", + Fork_Arno: "2024-12-04", }, nil case Chain_Holesky: return ForkMap{ diff --git a/pkg/rewards/10_goldAvsODRewardAmounts.go b/pkg/rewards/10_goldAvsODRewardAmounts.go index 19b5f77e..77fd42dc 100644 --- a/pkg/rewards/10_goldAvsODRewardAmounts.go +++ b/pkg/rewards/10_goldAvsODRewardAmounts.go @@ -14,19 +14,16 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_day, - ap.tokens_per_day_decimal, + ap.tokens_per_registered_snapshot, + ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, ap.strategy, ap.multiplier, ap.reward_submission_date FROM {{.activeODRewardsTable}} ap - LEFT JOIN operator_avs_registration_snapshots oar - ON ap.avs = oar.avs - AND ap.snapshot = oar.snapshot - AND ap.operator = oar.operator - WHERE oar.avs IS NULL OR oar.operator IS NULL + WHERE + ap.num_registered_snapshots = 0 ), -- Step 2: Dedupe the operator tokens across strategies for each (operator, reward hash, snapshot) @@ -56,7 +53,7 @@ operator_token_sums AS ( token, avs, operator, - SUM(tokens_per_day_decimal) OVER (PARTITION BY reward_hash, snapshot) AS avs_tokens + SUM(tokens_per_registered_snapshot_decimal) OVER (PARTITION BY reward_hash, snapshot) AS avs_tokens FROM distinct_operators ) diff --git a/pkg/rewards/7_goldActiveODRewards.go b/pkg/rewards/7_goldActiveODRewards.go index 902c9438..bc5d3742 100644 --- a/pkg/rewards/7_goldActiveODRewards.go +++ b/pkg/rewards/7_goldActiveODRewards.go @@ -14,7 +14,6 @@ WITH active_rewards_modified AS ( SELECT *, - amount / (duration / 86400) AS tokens_per_day, CAST(@cutoffDate AS TIMESTAMP(6)) AS global_end_inclusive -- Inclusive means we DO USE this day as a snapshot FROM operator_directed_rewards WHERE end_timestamp >= TIMESTAMP '{{.rewardsStart}}' @@ -34,11 +33,12 @@ active_rewards_updated_end_timestamps AS ( */ start_timestamp AS reward_start_exclusive, LEAST(global_end_inclusive, end_timestamp) AS reward_end_inclusive, - tokens_per_day, + amount, token, multiplier, strategy, reward_hash, + duration, global_end_inclusive, block_date AS reward_submission_date FROM active_rewards_modified @@ -53,12 +53,13 @@ active_rewards_updated_start_timestamps AS ( ap.reward_end_inclusive, ap.token, -- We use floor to ensure we are always underesimating total tokens per day - FLOOR(ap.tokens_per_day) AS tokens_per_day_decimal, + FLOOR(ap.amount) AS amount_decimal, -- Round down to 15 sigfigs for double precision, ensuring know errouneous round up or down - ap.tokens_per_day * ((POW(10, 15) - 1) / POW(10, 15)) AS tokens_per_day, + ap.amount * ((POW(10, 15) - 1) / POW(10, 15)) AS amount, ap.multiplier, ap.strategy, ap.reward_hash, + ap.duration, ap.global_end_inclusive, ap.reward_submission_date FROM active_rewards_updated_end_timestamps ap @@ -69,10 +70,11 @@ active_rewards_updated_start_timestamps AS ( ap.operator, ap.reward_end_inclusive, ap.token, - ap.tokens_per_day, + ap.amount, ap.multiplier, ap.strategy, ap.reward_hash, + ap.duration, ap.global_end_inclusive, ap.reward_start_exclusive, ap.reward_submission_date @@ -100,22 +102,73 @@ exploded_active_range_rewards AS ( ) AS day ), --- Step 7: Prepare final active rewards -active_rewards_final AS ( +-- Step 7: Prepare cleaned active rewards +active_rewards_cleaned AS ( SELECT avs, operator, CAST(day AS DATE) AS snapshot, token, - tokens_per_day, - tokens_per_day_decimal, + amount, + amount_decimal, multiplier, strategy, + duration, reward_hash, reward_submission_date FROM exploded_active_range_rewards -- Remove snapshots on the start day WHERE day != reward_start_exclusive +), + +-- Step 8: Dedupe the active rewards by (avs, snapshot, operator, reward_hash) +active_rewards_reduced_deduped AS ( + SELECT DISTINCT avs, snapshot, operator, reward_hash + FROM active_rewards_cleaned +), + +-- Step 9: Divide by the number of snapshots that the operator was registered +op_avs_num_registered_snapshots AS ( + SELECT + ar.reward_hash, + ar.operator, + COUNT(*) AS num_registered_snapshots + FROM active_rewards_reduced_deduped ar + JOIN operator_avs_registration_snapshots oar + ON + ar.avs = oar.avs + AND ar.snapshot = oar.snapshot + AND ar.operator = oar.operator + GROUP BY ar.reward_hash, ar.operator +), + +-- Step 9: Divide amount to pay by the number of snapshots that the operator was registered +active_rewards_with_registered_snapshots AS ( + SELECT + arc.*, + COALESCE(nrs.num_registered_snapshots, 0) as num_registered_snapshots + FROM active_rewards_cleaned arc + LEFT JOIN op_avs_num_registered_snapshots nrs + ON + arc.reward_hash = nrs.reward_hash + AND arc.operator = nrs.operator +), + +-- Step 10: Divide amount to pay by the number of snapshots that the operator was registered +active_rewards_final AS ( + SELECT + ar.*, + CASE + -- If the operator was not registered for any snapshots, just get regular tokens per day to refund the AVS + WHEN ar.num_registered_snapshots = 0 THEN ar.amount_decimal / (duration / 86400) + ELSE ar.amount_decimal / ar.num_registered_snapshots + END AS tokens_per_registered_snapshot_decimal, + CASE + -- If the operator was not registered for any snapshots, just get regular tokens per day to refund the AVS + WHEN ar.num_registered_snapshots = 0 THEN ar.amount / (duration / 86400) + ELSE ar.amount / ar.num_registered_snapshots + END AS tokens_per_registered_snapshot + FROM active_rewards_with_registered_snapshots ar ) SELECT * FROM active_rewards_final diff --git a/pkg/rewards/8_goldOperatorODRewardAmounts.go b/pkg/rewards/8_goldOperatorODRewardAmounts.go index 5bb9718f..52d3684b 100644 --- a/pkg/rewards/8_goldOperatorODRewardAmounts.go +++ b/pkg/rewards/8_goldOperatorODRewardAmounts.go @@ -14,8 +14,8 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_day, - ap.tokens_per_day_decimal, + ap.tokens_per_registered_snapshot, + ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, ap.strategy, @@ -53,7 +53,7 @@ operator_splits AS ( SELECT dop.*, COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL) as split_pct, - FLOOR(dop.tokens_per_day_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) AS operator_tokens + FLOOR(dop.tokens_per_registered_snapshot_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) AS operator_tokens FROM distinct_operators dop LEFT JOIN operator_avs_split_snapshots oas ON dop.operator = oas.operator diff --git a/pkg/rewards/9_goldStakerODRewardAmounts.go b/pkg/rewards/9_goldStakerODRewardAmounts.go index 5956f39c..37d7ca2e 100644 --- a/pkg/rewards/9_goldStakerODRewardAmounts.go +++ b/pkg/rewards/9_goldStakerODRewardAmounts.go @@ -14,8 +14,8 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_day, - ap.tokens_per_day_decimal, + ap.tokens_per_registered_snapshot, + ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, ap.strategy, @@ -33,7 +33,7 @@ WITH reward_snapshot_operators AS ( 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 + rso.tokens_per_registered_snapshot_decimal - FLOOR(rso.tokens_per_registered_snapshot_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 diff --git a/pkg/rewards/operatorDirectedRewards.go b/pkg/rewards/operatorDirectedRewards.go index 9cc7d896..de604802 100644 --- a/pkg/rewards/operatorDirectedRewards.go +++ b/pkg/rewards/operatorDirectedRewards.go @@ -22,7 +22,7 @@ const operatorDirectedRewardsQuery = ` TO_CHAR(b.block_time, 'YYYY-MM-DD') AS block_date FROM operator_directed_reward_submissions AS odrs JOIN blocks AS b ON(b.number = odrs.block_number) - WHERE b.block_time < TIMESTAMP '{{.cutoffDate}}' + WHERE b.block_time <= TIMESTAMP '{{.cutoffDate}}' ) select avs, diff --git a/pkg/rewards/rewardsV2_test.go b/pkg/rewards/rewardsV2_test.go index cd890901..28b5d8d1 100644 --- a/pkg/rewards/rewardsV2_test.go +++ b/pkg/rewards/rewardsV2_test.go @@ -2,6 +2,7 @@ package rewards import ( "fmt" + "strings" "testing" "time" @@ -210,6 +211,12 @@ func Test_RewardsV2(t *testing.T) { assert.Nil(t, err) t.Logf("Gold staging rows for snapshot %s: %d", snapshotDate, len(goldRows)) + for i, row := range goldRows { + if strings.EqualFold(row.RewardHash, strings.ToLower("0xB38AB57E8E858F197C07D0CDF61F34EB07C3D0FC58390417DDAD0BF528681909")) { + t.Logf("%d: %s %s %s %s %s", i, row.Earner, row.Snapshot.String(), row.RewardHash, row.Token, row.Amount) + } + // t.Logf("%d: %s %s %s %s %s", i, row.Earner, row.Snapshot.String(), row.RewardHash, row.Token, row.Amount) + } fmt.Printf("Total duration for rewards compute %s: %v\n", snapshotDate, time.Since(snapshotStartTime)) testStart = time.Now() From 9240e4f71e7c9e1d0db9eb2057451dadaab0c9a0 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:51:26 -0500 Subject: [PATCH 43/55] chore: use decimal everywhere --- pkg/rewards/10_goldAvsODRewardAmounts.go | 1 - pkg/rewards/7_goldActiveODRewards.go | 18 +++++------------- pkg/rewards/9_goldStakerODRewardAmounts.go | 1 - 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/pkg/rewards/10_goldAvsODRewardAmounts.go b/pkg/rewards/10_goldAvsODRewardAmounts.go index 77fd42dc..2fe6d76f 100644 --- a/pkg/rewards/10_goldAvsODRewardAmounts.go +++ b/pkg/rewards/10_goldAvsODRewardAmounts.go @@ -14,7 +14,6 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_registered_snapshot, ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, diff --git a/pkg/rewards/7_goldActiveODRewards.go b/pkg/rewards/7_goldActiveODRewards.go index bc5d3742..e47582a8 100644 --- a/pkg/rewards/7_goldActiveODRewards.go +++ b/pkg/rewards/7_goldActiveODRewards.go @@ -52,10 +52,8 @@ active_rewards_updated_start_timestamps AS ( COALESCE(MAX(g.snapshot), ap.reward_start_exclusive) AS reward_start_exclusive, ap.reward_end_inclusive, ap.token, - -- We use floor to ensure we are always underesimating total tokens per day + -- We use floor to ensure we are always underestimating total tokens per day FLOOR(ap.amount) AS amount_decimal, - -- Round down to 15 sigfigs for double precision, ensuring know errouneous round up or down - ap.amount * ((POW(10, 15) - 1) / POW(10, 15)) AS amount, ap.multiplier, ap.strategy, ap.reward_hash, @@ -70,7 +68,7 @@ active_rewards_updated_start_timestamps AS ( ap.operator, ap.reward_end_inclusive, ap.token, - ap.amount, + ap.amount, ap.multiplier, ap.strategy, ap.reward_hash, @@ -109,7 +107,6 @@ active_rewards_cleaned AS ( operator, CAST(day AS DATE) AS snapshot, token, - amount, amount_decimal, multiplier, strategy, @@ -142,7 +139,7 @@ op_avs_num_registered_snapshots AS ( GROUP BY ar.reward_hash, ar.operator ), --- Step 9: Divide amount to pay by the number of snapshots that the operator was registered +-- Step 10: Divide amount to pay by the number of snapshots that the operator was registered active_rewards_with_registered_snapshots AS ( SELECT arc.*, @@ -154,7 +151,7 @@ active_rewards_with_registered_snapshots AS ( AND arc.operator = nrs.operator ), --- Step 10: Divide amount to pay by the number of snapshots that the operator was registered +-- Step 11: Divide amount to pay by the number of snapshots that the operator was registered active_rewards_final AS ( SELECT ar.*, @@ -162,12 +159,7 @@ active_rewards_final AS ( -- If the operator was not registered for any snapshots, just get regular tokens per day to refund the AVS WHEN ar.num_registered_snapshots = 0 THEN ar.amount_decimal / (duration / 86400) ELSE ar.amount_decimal / ar.num_registered_snapshots - END AS tokens_per_registered_snapshot_decimal, - CASE - -- If the operator was not registered for any snapshots, just get regular tokens per day to refund the AVS - WHEN ar.num_registered_snapshots = 0 THEN ar.amount / (duration / 86400) - ELSE ar.amount / ar.num_registered_snapshots - END AS tokens_per_registered_snapshot + END AS tokens_per_registered_snapshot_decimal FROM active_rewards_with_registered_snapshots ar ) diff --git a/pkg/rewards/9_goldStakerODRewardAmounts.go b/pkg/rewards/9_goldStakerODRewardAmounts.go index 37d7ca2e..f2f4f94e 100644 --- a/pkg/rewards/9_goldStakerODRewardAmounts.go +++ b/pkg/rewards/9_goldStakerODRewardAmounts.go @@ -14,7 +14,6 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_registered_snapshot, ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, From 8234797b7514fc8ef6d396c7a86796dad9500448 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 11:08:33 -0600 Subject: [PATCH 44/55] Update preprod test data update query --- .../tests/testdata/combinedRewards/README.md | 2 +- .../README.md | 2 +- .../README.md | 44 +++++++++++-------- .../operatorRestakedStrategies/README.md | 2 +- .../testdata/operatorShareSnapshots/README.md | 2 +- .../tests/testdata/operatorShares/README.md | 4 +- .../stakerDelegationSnapshots/README.md | 2 +- .../testdata/stakerShareSnapshots/README.md | 2 +- .../tests/testdata/stakerShares/README.md | 36 +++++++-------- pkg/rewards/rewardsV2_test.go | 2 +- 10 files changed, 53 insertions(+), 45 deletions(-) diff --git a/internal/tests/testdata/combinedRewards/README.md b/internal/tests/testdata/combinedRewards/README.md index a06edf0f..e02385b1 100644 --- a/internal/tests/testdata/combinedRewards/README.md +++ b/internal/tests/testdata/combinedRewards/README.md @@ -87,5 +87,5 @@ select duration, block_number as block_number from dbt_preprod_holesky_rewards.rewards_combined -where block_time < '2024-12-10' +where block_time < '2024-12-13' ``` diff --git a/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md b/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md index 30a4f29d..ed331621 100644 --- a/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md +++ b/internal/tests/testdata/operatorAvsRegistrationSnapshots/README.md @@ -98,7 +98,7 @@ FROM transaction_logs t LEFT JOIN blocks b ON t.block_sequence_id = b.id WHERE t.address = '0x141d6995556135d4997b2ff72eb443be300353bc' AND t.event_name = 'OperatorAVSRegistrationStatusUpdated' - AND date_trunc('day', b.block_time) < TIMESTAMP '2024-12-10' + AND date_trunc('day', b.block_time) < TIMESTAMP '2024-12-13' ) select operator, diff --git a/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md b/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md index 7d549696..1bd489dd 100644 --- a/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md +++ b/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md @@ -2,32 +2,36 @@ ```sql WITH strategies AS ( - SELECT - tl.*, - output_data->'operatorDirectedRewardsSubmission'->>'token' as token, + SELECT + tl.*, + lower(arguments #>> '{2, Value}') as reward_hash, + output_data->'operatorDirectedRewardsSubmission'->>'token' as token, output_data->'operatorDirectedRewardsSubmission'->>'duration' as duration, output_data->'operatorDirectedRewardsSubmission'->>'startTimestamp' as start_timestamp, strategy_data, strategy_idx - 1 as strategy_idx -- Subtract 1 for 0-based indexing - FROM transaction_logs as tl, - jsonb_array_elements(output_data->'operatorDirectedRewardsSubmission'->'strategiesAndMultipliers') - WITH ORDINALITY AS t(strategy_data, strategy_idx) - where +FROM transaction_logs as tl, + jsonb_array_elements(output_data->'operatorDirectedRewardsSubmission'->'strategiesAndMultipliers') +WITH ORDINALITY AS t(strategy_data, strategy_idx) +where address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' - and event_name = 'OperatorDirectedAVSRewardsSubmissionCreated' -), -operators AS ( - SELECT + and event_name = 'OperatorDirectedAVSRewardsSubmissionCreated' + ), + operators AS ( +SELECT + lower(arguments #>> '{2, Value}') as reward_hash, operator_data, + operator_data->>'operator' as operator, output_data->'operatorDirectedRewardsSubmission' as rewards_submission, operator_idx - 1 as operator_idx -- Subtract 1 to make it 0-based indexing - FROM transaction_logs, - jsonb_array_elements(output_data->'operatorDirectedRewardsSubmission'->'operatorRewards') - WITH ORDINALITY AS t(operator_data, operator_idx) - where +FROM transaction_logs, + jsonb_array_elements(output_data->'operatorDirectedRewardsSubmission'->'operatorRewards') +WITH ORDINALITY AS t(operator_data, operator_idx) +where address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' - and event_name = 'OperatorDirectedAVSRewardsSubmissionCreated' -) + and event_name = 'OperatorDirectedAVSRewardsSubmissionCreated' + ), + joined_data as ( SELECT lower(arguments #>> '{1, Value}') as avs, lower(arguments #>> '{2, Value}') as reward_hash, @@ -45,5 +49,9 @@ SELECT transaction_hash, log_index FROM strategies -CROSS JOIN operators; + inner join operators on( + strategies.reward_hash = operators.reward_hash + ) + ) +select * from joined_data ``` diff --git a/internal/tests/testdata/operatorRestakedStrategies/README.md b/internal/tests/testdata/operatorRestakedStrategies/README.md index 796fc641..24d9d73c 100644 --- a/internal/tests/testdata/operatorRestakedStrategies/README.md +++ b/internal/tests/testdata/operatorRestakedStrategies/README.md @@ -54,7 +54,7 @@ select avs_directory_address from operator_restaked_strategies where avs_directory_address = '0x141d6995556135d4997b2ff72eb443be300353bc' -and block_time < '2024-12-10' +and block_time < '2024-12-13' ``` ## Expected results diff --git a/internal/tests/testdata/operatorShareSnapshots/README.md b/internal/tests/testdata/operatorShareSnapshots/README.md index 71439e7f..3b6165b8 100644 --- a/internal/tests/testdata/operatorShareSnapshots/README.md +++ b/internal/tests/testdata/operatorShareSnapshots/README.md @@ -38,7 +38,7 @@ preprod-rewardsV2 select * from dbt_preprod_holesky_rewards.operator_shares -where block_time < '2024-12-10' +where block_time < '2024-12-13' ``` ## Expected results diff --git a/internal/tests/testdata/operatorShares/README.md b/internal/tests/testdata/operatorShares/README.md index e3d05074..b8c5cfc8 100644 --- a/internal/tests/testdata/operatorShares/README.md +++ b/internal/tests/testdata/operatorShares/README.md @@ -87,12 +87,12 @@ SELECT FROM ( SELECT operator, strategy, shares, transaction_hash, log_index, block_time, block_date, block_number FROM dbt_testnet_holesky_rewards.operator_share_increases - where block_date < '2024-12-10' + where block_date < '2024-12-13' UNION ALL SELECT operator, strategy, shares * -1 AS shares, transaction_hash, log_index, block_time, block_date, block_number FROM dbt_testnet_holesky_rewards.operator_share_decreases - where block_date < '2024-12-10' + where block_date < '2024-12-13' ) combined_shares ``` diff --git a/internal/tests/testdata/stakerDelegationSnapshots/README.md b/internal/tests/testdata/stakerDelegationSnapshots/README.md index 92c095f6..96a59a5b 100644 --- a/internal/tests/testdata/stakerDelegationSnapshots/README.md +++ b/internal/tests/testdata/stakerDelegationSnapshots/README.md @@ -61,7 +61,7 @@ FROM ( UNION ALL SELECT *, 'delegations' AS src FROM dbt_preprod_holesky_rewards.staker_delegations ) as delegations_combined -where block_time < '2024-12-10' +where block_time < '2024-12-13' ``` diff --git a/internal/tests/testdata/stakerShareSnapshots/README.md b/internal/tests/testdata/stakerShareSnapshots/README.md index 5904126c..a805cee2 100644 --- a/internal/tests/testdata/stakerShareSnapshots/README.md +++ b/internal/tests/testdata/stakerShareSnapshots/README.md @@ -62,7 +62,7 @@ select block_date, block_number from dbt_preprod_holesky_rewards.staker_shares -where block_time < '2024-12-10' +where block_time < '2024-12-13' ``` ## Expected results diff --git a/internal/tests/testdata/stakerShares/README.md b/internal/tests/testdata/stakerShares/README.md index cb87fa63..468f48d2 100644 --- a/internal/tests/testdata/stakerShares/README.md +++ b/internal/tests/testdata/stakerShares/README.md @@ -131,28 +131,28 @@ SELECT block_date, block_number FROM ( - SELECT staker, strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number - FROM dbt_mainnet_ethereum_rewards.staker_deposits - where block_date < '2024-08-20' + SELECT staker, strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.staker_deposits + where block_date < '2024-12-13' - UNION ALL + UNION ALL - -- Subtract m1 & m2 withdrawals - SELECT staker, strategy, shares * -1, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number - FROM dbt_mainnet_ethereum_rewards.m1_staker_withdrawals - where block_date < '2024-08-20' + -- Subtract m1 & m2 withdrawals + SELECT staker, strategy, shares * -1, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.m1_staker_withdrawals + where block_date < '2024-12-13' - UNION ALL + UNION ALL - SELECT staker, strategy, shares * -1, strategy_index, transaction_hash, log_index, block_time, block_date, block_number - FROM dbt_mainnet_ethereum_rewards.m2_staker_withdrawals - where block_date < '2024-08-20' + SELECT staker, strategy, shares * -1, strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.m2_staker_withdrawals + where block_date < '2024-12-13' - UNION all + UNION all - -- Shares in eigenpod are positive or negative, so no need to multiply by -1 - SELECT staker, '0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0' as strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number - FROM dbt_mainnet_ethereum_rewards.eigenpod_shares - where block_date < '2024-08-20' -) combined_staker_shares + -- Shares in eigenpod are positive or negative, so no need to multiply by -1 + SELECT staker, '0xbeac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0' as strategy, shares, 0 as strategy_index, transaction_hash, log_index, block_time, block_date, block_number + FROM dbt_preprod_holesky_rewards.eigenpod_shares + where block_date < '2024-12-13' + ) combined_staker_shares ``` diff --git a/pkg/rewards/rewardsV2_test.go b/pkg/rewards/rewardsV2_test.go index 28b5d8d1..e1316013 100644 --- a/pkg/rewards/rewardsV2_test.go +++ b/pkg/rewards/rewardsV2_test.go @@ -73,7 +73,7 @@ func Test_RewardsV2(t *testing.T) { t.Log("Hydrated tables") snapshotDates := []string{ - "2024-12-12", + "2024-12-14", } fmt.Printf("Hydration duration: %v\n", time.Since(testStart)) From d61d5070415fc22f5a4782852b3e0528eb823fed Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 11:38:03 -0600 Subject: [PATCH 45/55] Updated testdata version to a2dce496e18ec5536eeb8247e442deae289c679d --- .testdataVersion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.testdataVersion b/.testdataVersion index 62872b9f..032314c4 100644 --- a/.testdataVersion +++ b/.testdataVersion @@ -1 +1 @@ -1490a0e18dd51ffbb59209ca9cf6513268af21f5 \ No newline at end of file +a2dce496e18ec5536eeb8247e442deae289c679d \ No newline at end of file From 0e4888cff05b4486ffb7321f72b67088adfd251d Mon Sep 17 00:00:00 2001 From: gpsanant Date: Thu, 12 Dec 2024 09:59:52 -0800 Subject: [PATCH 46/55] fix: assorted --- pkg/postgres/postgres.go | 3 ++- pkg/rewards/8_goldOperatorODRewardAmounts.go | 1 - pkg/rewards/rewardsV2_test.go | 3 ++- scripts/downloadTestData.sh | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go index 44df42d4..7cf0832c 100644 --- a/pkg/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -3,6 +3,8 @@ package postgres import ( "database/sql" "fmt" + "regexp" + "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/internal/tests" "github.com/Layr-Labs/sidecar/pkg/postgres/migrations" @@ -11,7 +13,6 @@ import ( "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" - "regexp" ) type PostgresConfig struct { diff --git a/pkg/rewards/8_goldOperatorODRewardAmounts.go b/pkg/rewards/8_goldOperatorODRewardAmounts.go index 52d3684b..f3ec3c7c 100644 --- a/pkg/rewards/8_goldOperatorODRewardAmounts.go +++ b/pkg/rewards/8_goldOperatorODRewardAmounts.go @@ -14,7 +14,6 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_registered_snapshot, ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, diff --git a/pkg/rewards/rewardsV2_test.go b/pkg/rewards/rewardsV2_test.go index e1316013..599c990a 100644 --- a/pkg/rewards/rewardsV2_test.go +++ b/pkg/rewards/rewardsV2_test.go @@ -212,7 +212,8 @@ func Test_RewardsV2(t *testing.T) { t.Logf("Gold staging rows for snapshot %s: %d", snapshotDate, len(goldRows)) for i, row := range goldRows { - if strings.EqualFold(row.RewardHash, strings.ToLower("0xB38AB57E8E858F197C07D0CDF61F34EB07C3D0FC58390417DDAD0BF528681909")) { + if strings.EqualFold(row.RewardHash, strings.ToLower("0xB38AB57E8E858F197C07D0CDF61F34EB07C3D0FC58390417DDAD0BF528681909")) && + strings.EqualFold(row.Earner, strings.ToLower("0xaFF71569D30ED876987088a62E0EA881EBc761E6")) { t.Logf("%d: %s %s %s %s %s", i, row.Earner, row.Snapshot.String(), row.RewardHash, row.Token, row.Amount) } // t.Logf("%d: %s %s %s %s %s", i, row.Earner, row.Snapshot.String(), row.RewardHash, row.Token, row.Amount) diff --git a/scripts/downloadTestData.sh b/scripts/downloadTestData.sh index 2b82e230..0951bfbb 100755 --- a/scripts/downloadTestData.sh +++ b/scripts/downloadTestData.sh @@ -16,4 +16,5 @@ if [[ -z $version ]]; then exit 1 fi echo "Downloading testdata version $dataUrl" + curl -L $dataUrl | tar xvz -C ./ From 0fc147871d865fae819fc1a287fd859e3eca2218 Mon Sep 17 00:00:00 2001 From: gpsanant Date: Thu, 12 Dec 2024 11:06:06 -0800 Subject: [PATCH 47/55] fix: cutoff --- pkg/rewards/operatorDirectedRewards.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/rewards/operatorDirectedRewards.go b/pkg/rewards/operatorDirectedRewards.go index de604802..9cc7d896 100644 --- a/pkg/rewards/operatorDirectedRewards.go +++ b/pkg/rewards/operatorDirectedRewards.go @@ -22,7 +22,7 @@ const operatorDirectedRewardsQuery = ` TO_CHAR(b.block_time, 'YYYY-MM-DD') AS block_date FROM operator_directed_reward_submissions AS odrs JOIN blocks AS b ON(b.number = odrs.block_number) - WHERE b.block_time <= TIMESTAMP '{{.cutoffDate}}' + WHERE b.block_time < TIMESTAMP '{{.cutoffDate}}' ) select avs, From 33c67f19b7aac0809af093dad5aac00fe18b9e8b Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 15:06:14 -0600 Subject: [PATCH 48/55] Updated testdata version to da8f00be49d1447f22934629d9d6819e3d763af9 --- .testdataVersion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.testdataVersion b/.testdataVersion index 032314c4..31040d96 100644 --- a/.testdataVersion +++ b/.testdataVersion @@ -1 +1 @@ -a2dce496e18ec5536eeb8247e442deae289c679d \ No newline at end of file +da8f00be49d1447f22934629d9d6819e3d763af9 \ No newline at end of file From c4a318ed28fb86367ba666f6b3bf0c9e851af3b4 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 15:11:58 -0600 Subject: [PATCH 49/55] feat: update rewardsv2 contract ABIs --- pkg/contractStore/coreContracts/preprod.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/contractStore/coreContracts/preprod.json b/pkg/contractStore/coreContracts/preprod.json index b804fd98..57fca286 100644 --- a/pkg/contractStore/coreContracts/preprod.json +++ b/pkg/contractStore/coreContracts/preprod.json @@ -94,6 +94,14 @@ "contract_address": "0x096694a4c8c2c13a005a200309d64995c08ed065", "contract_abi": "[{\"inputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"_delegationManager\",\"type\":\"address\"},{\"internalType\":\"contract IStrategyManager\",\"name\":\"_strategyManager\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_CALCULATION_INTERVAL_SECONDS\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_REWARDS_DURATION\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_RETROACTIVE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_FUTURE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"__GENESIS_REWARDS_TIMESTAMP\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"AVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"oldActivationDelay\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"newActivationDelay\",\"type\":\"uint32\"}],\"name\":\"ActivationDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldClaimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"ClaimerForSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldDefaultOperatorSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newDefaultOperatorSplitBips\",\"type\":\"uint16\"}],\"name\":\"DefaultOperatorSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"DistributionRootDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"}],\"name\":\"DistributionRootSubmitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorAVSSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorAVSSplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorAVSSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"operatorDirectedRewardsSubmissionHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission\",\"name\":\"operatorDirectedRewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"OperatorDirectedAVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorPISplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorPISplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorPISplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"pauserRegistry\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"PauserRegistrySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"claimedAmount\",\"type\":\"uint256\"}],\"name\":\"RewardsClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rewardsForAllSubmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"oldValue\",\"type\":\"bool\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"newValue\",\"type\":\"bool\"}],\"name\":\"RewardsForAllSubmitterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenHopper\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllEarnersCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldRewardsUpdater\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newRewardsUpdater\",\"type\":\"address\"}],\"name\":\"RewardsUpdaterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"CALCULATION_INTERVAL_SECONDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GENESIS_REWARDS_TIMESTAMP\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_FUTURE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_RETROACTIVE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REWARDS_DURATION\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"activationDelay\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"beaconChainETHStrategy\",\"outputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateEarnerLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateTokenLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"}],\"name\":\"checkClaim\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"claimerFor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission[]\",\"name\":\"operatorDirectedRewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createOperatorDirectedAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllEarners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"cumulativeClaimed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currRewardsCalculationEndTimestamp\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"defaultOperatorSplitBips\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"delegationManager\",\"outputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"disableRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"domainSeparator\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentClaimableDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getDistributionRootAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDistributionRootsLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"}],\"name\":\"getOperatorAVSSplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"getOperatorPISplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"rootHash\",\"type\":\"bytes32\"}],\"name\":\"getRootIndexFromHash\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"},{\"internalType\":\"contract IPauserRegistry\",\"name\":\"_pauserRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"initialPausedStatus\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_defaultSplitBips\",\"type\":\"uint16\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isOperatorDirectedAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isRewardsForAllSubmitter\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllEarnersHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauserRegistry\",\"outputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaim\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim[]\",\"name\":\"claims\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaims\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardsUpdater\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"}],\"name\":\"setActivationDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"setClaimerFor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setDefaultOperatorSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorAVSSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorPISplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"setPauserRegistry\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_submitter\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_newValue\",\"type\":\"bool\"}],\"name\":\"setRewardsForAllSubmitter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"}],\"name\":\"setRewardsUpdater\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategyManager\",\"outputs\":[{\"internalType\":\"contract IStrategyManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"submissionNonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"}],\"name\":\"submitRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", "bytecode_hash": "960dde4fe771e141992669bc5db4819a55cc5b4641ca509c7dac787e5e095237" + }, { + "contract_address": "0xe1200acdec6aef63005bba5f0f48cd719cc37040", + "contract_abi": "[{\"inputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"_delegationManager\",\"type\":\"address\"},{\"internalType\":\"contract IStrategyManager\",\"name\":\"_strategyManager\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_CALCULATION_INTERVAL_SECONDS\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_REWARDS_DURATION\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_RETROACTIVE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_FUTURE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"__GENESIS_REWARDS_TIMESTAMP\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"AVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"oldActivationDelay\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"newActivationDelay\",\"type\":\"uint32\"}],\"name\":\"ActivationDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldClaimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"ClaimerForSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldDefaultOperatorSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newDefaultOperatorSplitBips\",\"type\":\"uint16\"}],\"name\":\"DefaultOperatorSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"DistributionRootDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"}],\"name\":\"DistributionRootSubmitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorAVSSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorAVSSplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorAVSSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"operatorDirectedRewardsSubmissionHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission\",\"name\":\"operatorDirectedRewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"OperatorDirectedAVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorPISplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorPISplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorPISplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"pauserRegistry\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"PauserRegistrySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"claimedAmount\",\"type\":\"uint256\"}],\"name\":\"RewardsClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rewardsForAllSubmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"oldValue\",\"type\":\"bool\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"newValue\",\"type\":\"bool\"}],\"name\":\"RewardsForAllSubmitterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenHopper\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllEarnersCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldRewardsUpdater\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newRewardsUpdater\",\"type\":\"address\"}],\"name\":\"RewardsUpdaterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"CALCULATION_INTERVAL_SECONDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GENESIS_REWARDS_TIMESTAMP\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_FUTURE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_RETROACTIVE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REWARDS_DURATION\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"activationDelay\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"beaconChainETHStrategy\",\"outputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateEarnerLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateTokenLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"}],\"name\":\"checkClaim\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"claimerFor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission[]\",\"name\":\"operatorDirectedRewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createOperatorDirectedAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllEarners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"cumulativeClaimed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currRewardsCalculationEndTimestamp\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"defaultOperatorSplitBips\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"delegationManager\",\"outputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"disableRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"domainSeparator\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentClaimableDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getDistributionRootAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDistributionRootsLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"}],\"name\":\"getOperatorAVSSplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"getOperatorPISplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"rootHash\",\"type\":\"bytes32\"}],\"name\":\"getRootIndexFromHash\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"},{\"internalType\":\"contract IPauserRegistry\",\"name\":\"_pauserRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"initialPausedStatus\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_defaultSplitBips\",\"type\":\"uint16\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isOperatorDirectedAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isRewardsForAllSubmitter\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllEarnersHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauserRegistry\",\"outputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaim\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim[]\",\"name\":\"claims\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaims\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardsUpdater\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"}],\"name\":\"setActivationDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"setClaimerFor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setDefaultOperatorSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorAVSSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorPISplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"setPauserRegistry\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_submitter\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_newValue\",\"type\":\"bool\"}],\"name\":\"setRewardsForAllSubmitter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"}],\"name\":\"setRewardsUpdater\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategyManager\",\"outputs\":[{\"internalType\":\"contract IStrategyManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"submissionNonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"}],\"name\":\"submitRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "bytecode_hash": "776118dac9d55b009dd3ec36e2595d1bd24ec5dba8bca55d8fbb444efb2ed469" + }, { + "contract_address": "0xdd6cf6cf3b60219c0e3627d595a44e09098c436e", + "contract_abi": "[{\"inputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"_delegationManager\",\"type\":\"address\"},{\"internalType\":\"contract IStrategyManager\",\"name\":\"_strategyManager\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_CALCULATION_INTERVAL_SECONDS\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_REWARDS_DURATION\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_RETROACTIVE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"_MAX_FUTURE_LENGTH\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"__GENESIS_REWARDS_TIMESTAMP\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"AVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"oldActivationDelay\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"newActivationDelay\",\"type\":\"uint32\"}],\"name\":\"ActivationDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldClaimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"ClaimerForSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldDefaultOperatorSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newDefaultOperatorSplitBips\",\"type\":\"uint16\"}],\"name\":\"DefaultOperatorSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"DistributionRootDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"}],\"name\":\"DistributionRootSubmitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorAVSSplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorAVSSplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorAVSSplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"operatorDirectedRewardsSubmissionHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission\",\"name\":\"operatorDirectedRewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"OperatorDirectedAVSRewardsSubmissionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOperatorPISplitBips\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOperatorPISplitBips\",\"type\":\"uint16\"}],\"name\":\"OperatorPISplitBipsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"pauserRegistry\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"PauserRegistrySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"claimedAmount\",\"type\":\"uint256\"}],\"name\":\"RewardsClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rewardsForAllSubmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"oldValue\",\"type\":\"bool\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"newValue\",\"type\":\"bool\"}],\"name\":\"RewardsForAllSubmitterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenHopper\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"submissionNonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"name\":\"rewardsSubmission\",\"type\":\"tuple\"}],\"name\":\"RewardsSubmissionForAllEarnersCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldRewardsUpdater\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newRewardsUpdater\",\"type\":\"address\"}],\"name\":\"RewardsUpdaterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"CALCULATION_INTERVAL_SECONDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GENESIS_REWARDS_TIMESTAMP\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_FUTURE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_RETROACTIVE_LENGTH\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REWARDS_DURATION\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"activationDelay\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"beaconChainETHStrategy\",\"outputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateEarnerLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"}],\"name\":\"calculateTokenLeafHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"}],\"name\":\"checkClaim\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"claimerFor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"name\":\"operatorRewards\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission[]\",\"name\":\"operatorDirectedRewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createOperatorDirectedAVSRewardsSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllEarners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"contract IStrategy\",\"name\":\"strategy\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"multiplier\",\"type\":\"uint96\"}],\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"startTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"duration\",\"type\":\"uint32\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\"}],\"name\":\"createRewardsForAllSubmission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"cumulativeClaimed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currRewardsCalculationEndTimestamp\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"defaultOperatorSplitBips\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"delegationManager\",\"outputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"}],\"name\":\"disableRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"domainSeparator\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentClaimableDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentDistributionRoot\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getDistributionRootAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"activatedAt\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"disabled\",\"type\":\"bool\"}],\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDistributionRootsLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"}],\"name\":\"getOperatorAVSSplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"getOperatorPISplit\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"rootHash\",\"type\":\"bytes32\"}],\"name\":\"getRootIndexFromHash\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"},{\"internalType\":\"contract IPauserRegistry\",\"name\":\"_pauserRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"initialPausedStatus\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"_defaultSplitBips\",\"type\":\"uint16\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isOperatorDirectedAVSRewardsSubmissionHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isRewardsForAllSubmitter\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllEarnersHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isRewardsSubmissionForAllHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauserRegistry\",\"outputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"name\":\"claim\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaim\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"rootIndex\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"earnerIndex\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"earnerTreeProof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"earner\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"name\":\"earnerLeaf\",\"type\":\"tuple\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenIndices\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cumulativeEarnings\",\"type\":\"uint256\"}],\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"name\":\"tokenLeaves\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim[]\",\"name\":\"claims\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"processClaims\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardsUpdater\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"_activationDelay\",\"type\":\"uint32\"}],\"name\":\"setActivationDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"setClaimerFor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setDefaultOperatorSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorAVSSplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"split\",\"type\":\"uint16\"}],\"name\":\"setOperatorPISplit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IPauserRegistry\",\"name\":\"newPauserRegistry\",\"type\":\"address\"}],\"name\":\"setPauserRegistry\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_submitter\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_newValue\",\"type\":\"bool\"}],\"name\":\"setRewardsForAllSubmitter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_rewardsUpdater\",\"type\":\"address\"}],\"name\":\"setRewardsUpdater\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategyManager\",\"outputs\":[{\"internalType\":\"contract IStrategyManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"submissionNonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\"}],\"name\":\"submitRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPausedStatus\",\"type\":\"uint256\"}],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "bytecode_hash": "c3537671a1111d3bba88e2de3f25707ccedebca0cfac7ba82f805bfeb582ad8d" } ], "proxy_contracts": [ @@ -181,6 +189,16 @@ "contract_address": "0xb22ef643e1e067c994019a4c19e403253c05c2b0", "proxy_contract_address": "0x096694a4c8c2c13a005a200309d64995c08ed065", "block_number": 2871534 + }, + { + "contract_address": "0xb22ef643e1e067c994019a4c19e403253c05c2b0", + "proxy_contract_address": "0xe1200acdec6aef63005bba5f0f48cd719cc37040", + "block_number": 2910657 + }, + { + "contract_address": "0xb22ef643e1e067c994019a4c19e403253c05c2b0", + "proxy_contract_address": "0xdd6cf6cf3b60219c0e3627d595a44e09098c436e", + "block_number": 2919262 } ] } From 3b2173d0ce3a5b3f473ca97829d2c5d5ae49e4b7 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 15:29:16 -0600 Subject: [PATCH 50/55] fix: set arno preprod fork back to 12/12 --- internal/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/config/config.go b/internal/config/config.go index b3cef47e..a263bb5f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -255,7 +255,7 @@ 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", + Fork_Arno: "2024-12-12", }, nil case Chain_Holesky: return ForkMap{ From a83d4a8d177b537cb05c28c5075f5ee8e7ea46a3 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 15:35:42 -0600 Subject: [PATCH 51/55] Set arno preprod date to 12/11 --- internal/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/config/config.go b/internal/config/config.go index a263bb5f..e2659dc2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -255,7 +255,7 @@ 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-12", + Fork_Arno: "2024-12-11", }, nil case Chain_Holesky: return ForkMap{ From dbb9da670cca38fc7dd2a3f406ee748a7e34fa9e Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 15:42:53 -0600 Subject: [PATCH 52/55] Ignore rewards-v2 testing ranges --- internal/config/config.go | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index e2659dc2..3df40f18 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -297,24 +297,6 @@ func (c *Config) GetOperatorRestakedStrategiesStartBlock() uint64 { return 0 } -func (c *Config) ShouldSkipRewardsGeneration(blockNumber uint64) bool { - switch c.Chain { - case Chain_Preprod: - // During this period we deployed the rewards-v2 contracts before updating the sidecar. - // This results in missed events which have to be filled by some means. To fill them in, - // we needed to manually delete delete blocks >= 2871534 and re-index. The trouble here - // is that re-indexing introduces new state which was not present at the original process time. - if blockNumber >= 2871534 && blockNumber <= 2909856 { - return true - } - case Chain_Holesky: - // Skip rewards generation for holesky - case Chain_Mainnet: - // Skip rewards generation for mainnet - } - return false -} - func (c *Config) IsRewardsV2EnabledForCutoffDate(cutoffDate string) (bool, error) { forks, err := c.GetForkDates() if err != nil { @@ -347,6 +329,11 @@ func (c *Config) CanIgnoreIncorrectRewardsRoot(blockNumber uint64) bool { if blockNumber == 2812052 { return true } + + // ignore rewards-v2 deployment/testing range + if blockNumber > 2877938 && blockNumber <= 2909856 { + return true + } case Chain_Holesky: // roughly 2024-08-01 if blockNumber < 2046020 { From 28e0aa8a8631311e4ced16f5f6ca873ee5455577 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 15:48:59 -0600 Subject: [PATCH 53/55] Add missing = to >= --- internal/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/config/config.go b/internal/config/config.go index 3df40f18..8c3d0ed1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -331,7 +331,7 @@ func (c *Config) CanIgnoreIncorrectRewardsRoot(blockNumber uint64) bool { } // ignore rewards-v2 deployment/testing range - if blockNumber > 2877938 && blockNumber <= 2909856 { + if blockNumber >= 2877938 && blockNumber <= 2909856 { return true } case Chain_Holesky: From 3a0de368eccde2f2ca195597a02640d4cc84ae1e Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Thu, 12 Dec 2024 21:55:36 -0600 Subject: [PATCH 54/55] fix: add missing `floor()` sql function --- pkg/rewards/7_goldActiveODRewards.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/rewards/7_goldActiveODRewards.go b/pkg/rewards/7_goldActiveODRewards.go index e47582a8..a52dc019 100644 --- a/pkg/rewards/7_goldActiveODRewards.go +++ b/pkg/rewards/7_goldActiveODRewards.go @@ -157,8 +157,8 @@ active_rewards_final AS ( ar.*, CASE -- If the operator was not registered for any snapshots, just get regular tokens per day to refund the AVS - WHEN ar.num_registered_snapshots = 0 THEN ar.amount_decimal / (duration / 86400) - ELSE ar.amount_decimal / ar.num_registered_snapshots + WHEN ar.num_registered_snapshots = 0 THEN floor(ar.amount_decimal / (duration / 86400)) + ELSE floor(ar.amount_decimal / ar.num_registered_snapshots) END AS tokens_per_registered_snapshot_decimal FROM active_rewards_with_registered_snapshots ar ) From 14b2d9ddfc2382728e5a9d97c1fc9614709ca7e1 Mon Sep 17 00:00:00 2001 From: Sean McGary Date: Fri, 13 Dec 2024 08:39:45 -0600 Subject: [PATCH 55/55] fix: tweak expected reults query and add some test logging --- .../generateExpectedResults.sql | 67 +++++++++++ .../generateExpectedResults.sql | 112 ++++++++++++++++++ pkg/rewards/rewards_test.go | 35 ++++-- 3 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 internal/tests/testdata/operatorAvsRegistrationSnapshots/generateExpectedResults.sql create mode 100644 internal/tests/testdata/operatorRestakedStrategies/generateExpectedResults.sql diff --git a/internal/tests/testdata/operatorAvsRegistrationSnapshots/generateExpectedResults.sql b/internal/tests/testdata/operatorAvsRegistrationSnapshots/generateExpectedResults.sql new file mode 100644 index 00000000..3a338ea1 --- /dev/null +++ b/internal/tests/testdata/operatorAvsRegistrationSnapshots/generateExpectedResults.sql @@ -0,0 +1,67 @@ +COPY ( + with filtered as ( + select * from dbt_testnet_holesky_rewards.operator_avs_status + where block_time < '2024-09-17' +), +marked_statuses AS ( + SELECT + operator, + avs, + registered, + block_time, + block_date, + LEAD(block_time) OVER (PARTITION BY operator, avs ORDER BY block_time ASC, log_index ASC) AS next_block_time, + LEAD(registered) OVER (PARTITION BY operator, avs ORDER BY block_time ASC, log_index ASC) AS next_registration_status, + LEAD(block_date) OVER (PARTITION BY operator, avs ORDER BY block_time ASC, log_index ASC) AS next_block_date, + LAG(registered) OVER (PARTITION BY operator, avs ORDER BY block_time ASC, log_index ASC) AS prev_registered, + LAG(block_date) OVER (PARTITION BY operator, avs ORDER BY block_time ASC, log_index ASC) AS prev_block_date + FROM filtered +), + removed_same_day_deregistrations AS ( + SELECT * from marked_statuses + WHERE NOT ( + (registered = TRUE AND + COALESCE(next_registration_status = FALSE, false) AND + COALESCE(block_date = next_block_date, false)) OR + (registered = FALSE AND + COALESCE(prev_registered = TRUE, false) and + COALESCE(block_date = prev_block_date, false) + ) + ) + ), + registration_periods AS ( + SELECT + operator, + avs, + block_time AS start_time, + COALESCE(next_block_time, TIMESTAMP '2024-09-01') AS end_time, + registered + FROM removed_same_day_deregistrations + WHERE registered = TRUE + ), + registration_windows_extra as ( + SELECT + operator, + avs, + date_trunc('day', start_time) + interval '1' day as start_time, + date_trunc('day', end_time) as end_time + FROM registration_periods + ), + operator_avs_registration_windows as ( + SELECT * from registration_windows_extra + WHERE start_time != end_time + ), + cleaned_records AS ( + SELECT * FROM operator_avs_registration_windows + WHERE start_time < end_time + ), + final_results as ( + SELECT + operator, + avs, + day AS snapshot + FROM cleaned_records + CROSS JOIN generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS day + ) + select * from final_results +) TO STDOUT WITH DELIMITER ',' CSV HEADER; diff --git a/internal/tests/testdata/operatorRestakedStrategies/generateExpectedResults.sql b/internal/tests/testdata/operatorRestakedStrategies/generateExpectedResults.sql new file mode 100644 index 00000000..5236cc2d --- /dev/null +++ b/internal/tests/testdata/operatorRestakedStrategies/generateExpectedResults.sql @@ -0,0 +1,112 @@ +copy (with ranked_records AS ( + SELECT + lower(operator) as operator, + lower(avs) as avs, + lower(strategy) as strategy, + block_time, + date_trunc('day', CAST(block_time as timestamp(6))) + interval '1' day as start_time, + ROW_NUMBER() OVER ( + PARTITION BY operator, avs, strategy, date_trunc('day', CAST(block_time as timestamp(6))) + interval '1' day + ORDER BY block_time DESC + ) AS rn + FROM public.operator_restaked_strategies + WHERE avs_directory_address = lower('0x055733000064333caddbc92763c58bf0192ffebf') + and block_time < '2024-09-17' +), + latest_records AS ( + SELECT + operator, + avs, + strategy, + start_time, + block_time + FROM ranked_records + WHERE rn = 1 + ), + grouped_records AS ( + SELECT + operator, + avs, + strategy, + start_time, + LEAD(start_time) OVER ( + PARTITION BY operator, avs, strategy + ORDER BY start_time ASC + ) AS next_start_time + FROM latest_records + ), + parsed_ranges AS ( + SELECT + operator, + avs, + strategy, + start_time, + CASE + WHEN next_start_time IS NULL OR next_start_time > start_time + INTERVAL '1' DAY THEN start_time + ELSE next_start_time + END AS end_time + FROM grouped_records + ), + active_windows as ( + SELECT * + FROM parsed_ranges + WHERE start_time != end_time + ), + gaps_and_islands AS ( + SELECT + operator, + avs, + strategy, + start_time, + end_time, + LAG(end_time) OVER(PARTITION BY operator, avs, strategy ORDER BY start_time) as prev_end_time + FROM active_windows + ), + island_detection AS ( + SELECT operator, avs, strategy, start_time, end_time, prev_end_time, + CASE + WHEN prev_end_time = start_time THEN 0 + ELSE 1 + END as new_island + FROM gaps_and_islands + ), + island_groups AS ( + SELECT + operator, + avs, + strategy, + start_time, + end_time, + SUM(new_island) OVER ( + PARTITION BY operator, avs, strategy ORDER BY start_time + ) AS island_id + FROM island_detection + ), + operator_avs_strategy_windows AS ( + SELECT + operator, + avs, + strategy, + MIN(start_time) AS start_time, + MAX(end_time) AS end_time + FROM island_groups + GROUP BY operator, avs, strategy, island_id + ORDER BY operator, avs, strategy, start_time + ), + cleaned_records AS ( + SELECT * FROM operator_avs_strategy_windows + WHERE start_time < end_time + ), + final_results as ( +SELECT + operator, + avs, + strategy, + cast(day AS DATE) AS snapshot +FROM + cleaned_records + CROSS JOIN + generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS day +) +select * from final_results +) to STDOUT DELIMITER ',' CSV HEADER; diff --git a/pkg/rewards/rewards_test.go b/pkg/rewards/rewards_test.go index ddee53e5..efc5bc54 100644 --- a/pkg/rewards/rewards_test.go +++ b/pkg/rewards/rewards_test.go @@ -294,36 +294,47 @@ func Test_Rewards(t *testing.T) { fmt.Printf("\tRows in gold_6_rfae_operators: %v - [time: %v]\n", rows, time.Since(testStart)) testStart = time.Now() + rewardsV2Enabled, err := cfg.IsRewardsV2EnabledForCutoffDate(snapshotDate) + assert.Nil(t, err) + fmt.Printf("Running gold_7_active_od_rewards\n") err = rc.Generate7ActiveODRewards(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_7_ActiveODRewards]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_7_active_od_rewards: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_7_ActiveODRewards]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_7_active_od_rewards: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_8_operator_od_reward_amounts\n") err = rc.GenerateGold8OperatorODRewardAmountsTable(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_8_operator_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_8_OperatorODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_8_operator_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_9_staker_od_reward_amounts\n") err = rc.GenerateGold9StakerODRewardAmountsTable(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_9_StakerODRewardAmounts]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_9_staker_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_9_StakerODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_9_staker_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_10_avs_od_reward_amounts\n") err = rc.GenerateGold10AvsODRewardAmountsTable(snapshotDate) assert.Nil(t, err) - rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_10_AvsODRewardAmounts]) - assert.Nil(t, err) - fmt.Printf("\tRows in gold_10_avs_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + if rewardsV2Enabled { + rows, err = getRowCountForTable(grm, goldTableNames[rewardsUtils.Table_10_AvsODRewardAmounts]) + assert.Nil(t, err) + fmt.Printf("\tRows in gold_10_avs_od_reward_amounts: %v - [time: %v]\n", rows, time.Since(testStart)) + } testStart = time.Now() fmt.Printf("Running gold_11_staging\n")