diff --git a/internal/eigenState/avsOperators.go b/internal/eigenState/avsOperators/avsOperators.go similarity index 94% rename from internal/eigenState/avsOperators.go rename to internal/eigenState/avsOperators/avsOperators.go index 4245a3f3..876a1ae8 100644 --- a/internal/eigenState/avsOperators.go +++ b/internal/eigenState/avsOperators/avsOperators.go @@ -1,9 +1,10 @@ -package eigenState +package avsOperators import ( "database/sql" "fmt" "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/eigenState" "github.com/Layr-Labs/sidecar/internal/storage" "github.com/wealdtech/go-merkletree/v2" "github.com/wealdtech/go-merkletree/v2/keccak256" @@ -40,8 +41,8 @@ type AvsOperatorChange struct { // EigenState model for AVS operators that implements IEigenStateModel type AvsOperators struct { - BaseEigenState - StateTransitions StateTransitions[AvsOperatorChange] + eigenState.BaseEigenState + StateTransitions eigenState.StateTransitions[AvsOperatorChange] Db *gorm.DB Network config.Network Environment config.Environment @@ -51,7 +52,7 @@ type AvsOperators struct { // Create new instance of AvsOperators state model func NewAvsOperators( - esm *EigenStateManager, + esm *eigenState.EigenStateManager, grm *gorm.DB, Network config.Network, Environment config.Environment, @@ -59,7 +60,7 @@ func NewAvsOperators( globalConfig *config.Config, ) (*AvsOperators, error) { s := &AvsOperators{ - BaseEigenState: BaseEigenState{}, + BaseEigenState: eigenState.BaseEigenState{}, Db: grm, Network: Network, Environment: Environment, @@ -77,8 +78,8 @@ func NewAvsOperators( // // Returns the map and a reverse sorted list of block numbers that can be traversed when // processing a log to determine which state change to apply. -func (a *AvsOperators) GetStateTransitions() (StateTransitions[AvsOperatorChange], []uint64) { - stateChanges := make(StateTransitions[AvsOperatorChange]) +func (a *AvsOperators) GetStateTransitions() (eigenState.StateTransitions[AvsOperatorChange], []uint64) { + stateChanges := make(eigenState.StateTransitions[AvsOperatorChange]) // TODO(seanmcgary): make this not a closure so this function doesnt get big an messy... stateChanges[0] = func(log *storage.TransactionLog) (*AvsOperatorChange, error) { @@ -247,7 +248,7 @@ func (a *AvsOperators) WriteFinalState(blockNumber uint64) error { // 3. Create a merkle tree for each AVS, with the operator:block_number pairs as leaves // 4. Create a merkle tree for all AVS trees // 5. Return the root of the full tree -func (a *AvsOperators) GenerateStateRoot(blockNumber uint64) (StateRoot, error) { +func (a *AvsOperators) GenerateStateRoot(blockNumber uint64) (eigenState.StateRoot, error) { query := ` select avs, @@ -320,5 +321,5 @@ func (a *AvsOperators) GenerateStateRoot(blockNumber uint64) (StateRoot, error) if err != nil { return "", err } - return StateRoot(fullTree.Root()), nil + return eigenState.StateRoot(fullTree.Root()), nil } diff --git a/internal/eigenState/avsOperators_test.go b/internal/eigenState/avsOperators/avsOperators_test.go similarity index 95% rename from internal/eigenState/avsOperators_test.go rename to internal/eigenState/avsOperators/avsOperators_test.go index 1022419e..0de181d6 100644 --- a/internal/eigenState/avsOperators_test.go +++ b/internal/eigenState/avsOperators/avsOperators_test.go @@ -1,9 +1,10 @@ -package eigenState +package avsOperators import ( "database/sql" "fmt" "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/eigenState" "github.com/Layr-Labs/sidecar/internal/logger" "github.com/Layr-Labs/sidecar/internal/storage" "github.com/Layr-Labs/sidecar/internal/tests" @@ -18,7 +19,7 @@ func setup() ( *config.Config, *gorm.DB, *zap.Logger, - *EigenStateManager, + *eigenState.EigenStateManager, error, ) { cfg := tests.GetConfig() @@ -26,7 +27,7 @@ func setup() ( _, grm, err := tests.GetDatabaseConnection(cfg) - eigenState := NewEigenStateManager(l) + eigenState := eigenState.NewEigenStateManager(l) return cfg, grm, l, eigenState, err } diff --git a/internal/eigenState/operatorShares/operatorShares.go b/internal/eigenState/operatorShares/operatorShares.go new file mode 100644 index 00000000..b3e98c0c --- /dev/null +++ b/internal/eigenState/operatorShares/operatorShares.go @@ -0,0 +1,358 @@ +package operatorShares + +import ( + "database/sql" + "encoding/json" + "fmt" + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/eigenState" + "github.com/Layr-Labs/sidecar/internal/parser" + "github.com/Layr-Labs/sidecar/internal/storage" + "github.com/Layr-Labs/sidecar/internal/utils" + "github.com/wealdtech/go-merkletree/v2" + "github.com/wealdtech/go-merkletree/v2/keccak256" + orderedmap "github.com/wk8/go-ordered-map/v2" + "go.uber.org/zap" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "math/big" + "slices" + "sort" + "strings" + "time" +) + +// Changes table +type OperatorShareChange struct { + Id uint64 `gorm:"type:serial"` + Operator string + Strategy string + Shares string `gorm:"type:numeric"` + TransactionHash string + TransactionIndex uint64 + LogIndex uint64 + BlockNumber uint64 + CreatedAt time.Time +} + +// Block table +type OperatorShares struct { + Operator string + Strategy string + Shares string `gorm:"type:numeric"` + BlockNumber uint64 + CreatedAt time.Time +} + +// Implements IEigenStateModel +type OperatorSharesModel struct { + eigenState.BaseEigenState + StateTransitions eigenState.StateTransitions[OperatorShareChange] + Db *gorm.DB + Network config.Network + Environment config.Environment + logger *zap.Logger + globalConfig *config.Config +} + +func NewOperatorSharesModel( + esm *eigenState.EigenStateManager, + grm *gorm.DB, + Network config.Network, + Environment config.Environment, + logger *zap.Logger, + globalConfig *config.Config, +) (*OperatorSharesModel, error) { + model := &OperatorSharesModel{ + BaseEigenState: eigenState.BaseEigenState{}, + Db: grm, + Network: Network, + Environment: Environment, + logger: logger, + globalConfig: globalConfig, + } + + esm.RegisterState(model) + return model, nil +} + +func (osm *OperatorSharesModel) GetStateTransitions() (eigenState.StateTransitions[OperatorShareChange], []uint64) { + stateChanges := make(eigenState.StateTransitions[OperatorShareChange]) + + stateChanges[0] = func(log *storage.TransactionLog) (*OperatorShareChange, error) { + arguments := make([]parser.Argument, 0) + err := json.Unmarshal([]byte(log.Arguments), &arguments) + if err != nil { + osm.logger.Sugar().Errorw("Failed to unmarshal arguments", + zap.Error(err), + zap.String("transactionHash", log.TransactionHash), + zap.Uint64("transactionIndex", log.TransactionIndex), + ) + return nil, err + } + outputData := make(map[string]interface{}) + err = json.Unmarshal([]byte(log.OutputData), &outputData) + if err != nil { + osm.logger.Sugar().Errorw("Failed to unmarshal outputData", + zap.Error(err), + zap.String("transactionHash", log.TransactionHash), + zap.Uint64("transactionIndex", log.TransactionIndex), + ) + return nil, err + } + fmt.Printf("Outputdata: %+v\n", outputData) + shares := big.Int{} + sharesInt, _ := shares.SetString(outputData["shares"].(string), 10) + + if log.EventName == "OperatorSharesDecreased" { + sharesInt.Mul(sharesInt, big.NewInt(-1)) + } + + change := &OperatorShareChange{ + Operator: arguments[0].Value.(string), + Strategy: outputData["strategy"].(string), + Shares: sharesInt.String(), + TransactionHash: log.TransactionHash, + TransactionIndex: log.TransactionIndex, + LogIndex: log.LogIndex, + BlockNumber: log.BlockNumber, + } + fmt.Printf("Change: %+v\n", change) + return change, 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 (osm *OperatorSharesModel) getContractAddressesForEnvironment() map[string][]string { + contracts := osm.globalConfig.GetContractsMapForEnvAndNetwork() + return map[string][]string{ + contracts.DelegationManager: []string{ + "OperatorSharesIncreased", + "OperatorSharesDecreased", + }, + } +} + +func (osm *OperatorSharesModel) IsInterestingLog(log *storage.TransactionLog) bool { + addresses := osm.getContractAddressesForEnvironment() + logAddress := strings.ToLower(log.Address) + if eventNames, ok := addresses[logAddress]; ok { + if slices.Contains(eventNames, log.EventName) { + return true + } + } + return false +} + +func (osm *OperatorSharesModel) HandleStateChange(log *storage.TransactionLog) (interface{}, error) { + stateChanges, sortedBlockNumbers := osm.GetStateTransitions() + + for _, blockNumber := range sortedBlockNumbers { + if log.BlockNumber >= blockNumber { + osm.logger.Sugar().Debugw("Handling state change", zap.Uint64("blockNumber", blockNumber)) + + change, err := stateChanges[blockNumber](log) + if err != nil { + return nil, err + } + + if change != nil { + wroteChange, err := osm.writeStateChange(change) + if err != nil { + return wroteChange, err + } + return wroteChange, nil + } + } + } + return nil, nil +} + +func (osm *OperatorSharesModel) writeStateChange(change *OperatorShareChange) (interface{}, error) { + osm.logger.Sugar().Debugw("Writing state change", zap.Any("change", change)) + res := osm.Db.Model(&OperatorShareChange{}).Clauses(clause.Returning{}).Create(change) + if res.Error != nil { + osm.logger.Error("Failed to insert into avs_operator_changes", zap.Error(res.Error)) + return change, res.Error + } + return change, nil +} + +func (osm *OperatorSharesModel) WriteFinalState(blockNumber uint64) error { + query := ` + with new_sum as ( + select + operator, + strategy, + sum(shares) as shares + from + operator_share_changes + where + block_number = @currentBlock + group by 1, 2 + ), + previous_values as ( + select + operator, + strategy, + shares + from operator_shares + where block_number = @previousBlock + ), + unioned_values as ( + (select operator, strategy, shares from previous_values) + union + (select operator, strategy, shares from new_sum) + ), + final_values as ( + select + operator, + strategy, + sum(shares) as shares + from unioned_values + group by 1, 2 + ) + insert into operator_shares (operator, strategy, shares, block_number) + select operator, strategy, shares, @currentBlock as block_number from final_values + ` + + res := osm.Db.Exec(query, + sql.Named("currentBlock", blockNumber), + sql.Named("previousBlock", blockNumber-1), + ) + if res.Error != nil { + osm.logger.Sugar().Errorw("Failed to insert into operator_shares", zap.Error(res.Error)) + return res.Error + } + return nil +} + +func (osm *OperatorSharesModel) getDifferencesInStates(currentBlock uint64) ([]OperatorShares, error) { + query := ` + with new_states as ( + select + concat(operator, '_', strategy) as slot_id, + operator, + strategy, + shares + from operator_shares + where block_number = @currentBlock + ), + previous_states as ( + select + concat(operator, '_', strategy) as slot_id, + operator, + strategy, + shares + from operator_shares + where block_number = @previousBlock + ), + diffs as ( + select slot_id, operator, strategy, shares from new_states + except + select slot_id, operator, strategy, shares from previous_states + ) + select operator, strategy, shares from diffs + order by strategy asc, operator asc + ` + + diffs := make([]OperatorShares, 0) + res := osm.Db. + Raw(query, + sql.Named("currentBlock", currentBlock), + sql.Named("previousBlock", currentBlock-1), + ). + Scan(&diffs) + if res.Error != nil { + osm.logger.Sugar().Errorw("Failed to fetch operator_shares", zap.Error(res.Error)) + return nil, res.Error + } + return diffs, nil +} + +func (osm *OperatorSharesModel) GenerateStateRoot(blockNumber uint64) (eigenState.StateRoot, error) { + diffs, err := osm.getDifferencesInStates(blockNumber) + if err != nil { + return "", err + } + + fullTree, err := osm.merkelizeState(diffs) + if err != nil { + return "", err + } + return eigenState.StateRoot(utils.ConvertBytesToString(fullTree.Root())), nil +} + +func (osm *OperatorSharesModel) merkelizeState(diffs []OperatorShares) (*merkletree.MerkleTree, error) { + // Create a merkle tree with the structure: + // strategy: map[operators]: shares + om := orderedmap.New[string, *orderedmap.OrderedMap[string, string]]() + + for _, diff := range diffs { + existingStrategy, found := om.Get(diff.Strategy) + if !found { + existingStrategy = orderedmap.New[string, string]() + om.Set(diff.Strategy, existingStrategy) + + prev := om.GetPair(diff.Strategy).Prev() + if prev != nil && strings.Compare(prev.Key, diff.Strategy) >= 0 { + om.Delete(diff.Strategy) + return nil, fmt.Errorf("strategy not in order") + } + } + existingStrategy.Set(diff.Operator, diff.Shares) + + prev := existingStrategy.GetPair(diff.Operator).Prev() + if prev != nil && strings.Compare(prev.Key, diff.Operator) >= 0 { + existingStrategy.Delete(diff.Operator) + return nil, fmt.Errorf("operator not in order") + } + } + + leaves := make([][]byte, 0) + for strat := om.Oldest(); strat != nil; strat = strat.Next() { + + operatorLeaves := make([][]byte, 0) + for operator := strat.Value.Oldest(); operator != nil; operator = operator.Next() { + operatorAddr := operator.Key + shares := operator.Value + operatorLeaves = append(operatorLeaves, encodeOperatorSharesLeaf(operatorAddr, shares)) + } + + stratTree, err := merkletree.NewTree( + merkletree.WithData(operatorLeaves), + merkletree.WithHashType(keccak256.New()), + ) + if err != nil { + return nil, err + } + leaves = append(leaves, encodeStratTree(strat.Key, stratTree.Root())) + } + return merkletree.NewTree( + merkletree.WithData(leaves), + merkletree.WithHashType(keccak256.New()), + ) +} + +func encodeOperatorSharesLeaf(operator string, shares string) []byte { + operatorBytes := []byte(operator) + sharesBytes := []byte(shares) + + return append(operatorBytes, sharesBytes[:]...) +} + +func encodeStratTree(strategy string, operatorTreeRoot []byte) []byte { + strategyBytes := []byte(strategy) + return append(strategyBytes, operatorTreeRoot[:]...) +} diff --git a/internal/eigenState/operatorShares/operatorShares_test.go b/internal/eigenState/operatorShares/operatorShares_test.go new file mode 100644 index 00000000..c5894690 --- /dev/null +++ b/internal/eigenState/operatorShares/operatorShares_test.go @@ -0,0 +1,124 @@ +package operatorShares + +import ( + "database/sql" + "fmt" + "github.com/Layr-Labs/sidecar/internal/config" + "github.com/Layr-Labs/sidecar/internal/eigenState" + "github.com/Layr-Labs/sidecar/internal/logger" + "github.com/Layr-Labs/sidecar/internal/storage" + "github.com/Layr-Labs/sidecar/internal/tests" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "gorm.io/gorm" + "math/big" + "testing" + "time" +) + +func setup() ( + *config.Config, + *gorm.DB, + *zap.Logger, + *eigenState.EigenStateManager, + error, +) { + cfg := tests.GetConfig() + l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: cfg.Debug}) + + _, grm, err := tests.GetDatabaseConnection(cfg) + + eigenState := eigenState.NewEigenStateManager(l) + + return cfg, grm, l, eigenState, err +} + +func teardown(model *OperatorSharesModel) { + model.Db.Exec("truncate table operator_share_changes cascade") + model.Db.Exec("truncate table operator_shares cascade") +} + +func Test_OperatorSharesState(t *testing.T) { + cfg, grm, l, esm, err := setup() + + if err != nil { + t.Fatal(err) + } + + t.Run("Should create a new OperatorSharesState", func(t *testing.T) { + model, err := NewOperatorSharesModel(esm, grm, cfg.Network, cfg.Environment, l, cfg) + assert.Nil(t, err) + assert.NotNil(t, model) + }) + t.Run("Should register OperatorSharesState", func(t *testing.T) { + blockNumber := uint64(200) + log := storage.TransactionLog{ + TransactionHash: "some hash", + TransactionIndex: big.NewInt(100).Uint64(), + BlockNumber: blockNumber, + BlockSequenceId: big.NewInt(300).Uint64(), + Address: cfg.GetContractsMapForEnvAndNetwork().DelegationManager, + Arguments: `[{"Value": "0xdb9afbdcfeca94dfb25790c900c527969e78bd3c"}]`, + EventName: "OperatorSharesIncreased", + LogIndex: big.NewInt(400).Uint64(), + OutputData: `{"shares": "100", "strategy": "0x93c4b944d05dfe6df7645a86cd2206016c51564d"}`, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + DeletedAt: time.Time{}, + } + + model, err := NewOperatorSharesModel(esm, grm, cfg.Network, cfg.Environment, l, cfg) + + change, err := model.HandleStateChange(&log) + assert.Nil(t, err) + assert.NotNil(t, change) + + teardown(model) + }) + t.Run("Should register AvsOperatorState and generate the table for the block", func(t *testing.T) { + blockNumber := uint64(200) + log := storage.TransactionLog{ + TransactionHash: "some hash", + TransactionIndex: big.NewInt(100).Uint64(), + BlockNumber: blockNumber, + BlockSequenceId: big.NewInt(300).Uint64(), + Address: cfg.GetContractsMapForEnvAndNetwork().DelegationManager, + Arguments: `[{"Value": "0xdb9afbdcfeca94dfb25790c900c527969e78bd3c"}]`, + EventName: "OperatorSharesIncreased", + LogIndex: big.NewInt(400).Uint64(), + OutputData: `{"shares": "100", "strategy": "0x93c4b944d05dfe6df7645a86cd2206016c51564d"}`, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + DeletedAt: time.Time{}, + } + + model, err := NewOperatorSharesModel(esm, grm, cfg.Network, cfg.Environment, l, cfg) + assert.Nil(t, err) + + stateChange, err := model.HandleStateChange(&log) + assert.Nil(t, err) + assert.NotNil(t, stateChange) + + err = model.WriteFinalState(blockNumber) + assert.Nil(t, err) + + states := []OperatorShares{} + statesRes := model.Db. + Model(&OperatorShares{}). + Raw("select * from operator_shares where block_number = @blockNumber", sql.Named("blockNumber", blockNumber)). + Scan(&states) + + if statesRes.Error != nil { + t.Fatalf("Failed to fetch operator_shares: %v", statesRes.Error) + } + assert.Equal(t, 1, len(states)) + + assert.Equal(t, "100", states[0].Shares) + + stateRoot, err := model.GenerateStateRoot(blockNumber) + assert.Nil(t, err) + fmt.Printf("StateRoot: %s\n", stateRoot) + + teardown(model) + }) +} diff --git a/internal/postgres/migrations/202408200934_eigenlayerStateTables/up.go b/internal/postgres/migrations/202408200934_eigenlayerStateTables/up.go index f1e38d65..c31d66fc 100644 --- a/internal/postgres/migrations/202408200934_eigenlayerStateTables/up.go +++ b/internal/postgres/migrations/202408200934_eigenlayerStateTables/up.go @@ -37,19 +37,6 @@ func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { `create index if not exists idx_registered_avs_operators_avs_operator on registered_avs_operators (avs, operator)`, `create index if not exists idx_registered_avs_operators_block on registered_avs_operators (block_number)`, /* - `create table if not exists operator_share_changes ( - id serial primary key, - operator varchar, - strategy varchar, - shares numeric, - transaction_hash varchar, - log_index bigint, - block_number bigint, - created_at timestamp with time zone default current_timestamp - ) - `, - `create index if not exists idx_operator_share_changes_operator_strat on operator_share_changes (operator, strategy)`, - `create index if not exists idx_operator_share_changes_block on operator_share_changes (block_number)`, `create table if not exists staker_share_changes ( id serial primary key, staker varchar, @@ -116,16 +103,6 @@ func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { `, `create index if not exists idx_active_reward_for_all_submissions_avs on active_reward_for_all_submissions (avs)`, `create index if not exists idx_active_reward_for_all_submissions_block on active_reward_for_all_submissions (block_number)`, - `create table if not exists operator_shares ( - operator varchar, - strategy varchar, - shares numeric, - block_number bigint, - created_at timestamp with time zone default current_timestamp, - unique (operator, strategy, block_number) - )`, - `create index if not exists idx_operator_shares_operator_strategy on operator_shares (operator, strategy)`, - `create index if not exists idx_operator_shares_block on operator_shares (block_number)`, `create table if not exists staker_shares ( staker varchar, strategy varchar, diff --git a/internal/postgres/migrations/202409051720_operatorShareChanges/up.go b/internal/postgres/migrations/202409051720_operatorShareChanges/up.go new file mode 100644 index 00000000..b61ee8e0 --- /dev/null +++ b/internal/postgres/migrations/202409051720_operatorShareChanges/up.go @@ -0,0 +1,49 @@ +package _202409051720_operatorShareChanges + +import ( + "database/sql" + "gorm.io/gorm" +) + +type Migration struct { +} + +func (m *Migration) Up(db *sql.DB, grm *gorm.DB) error { + queries := []string{ + `create table if not exists operator_share_changes ( + id serial primary key, + operator varchar, + strategy varchar, + shares numeric, + transaction_hash varchar, + transaction_index bigint, + log_index bigint, + block_number bigint, + created_at timestamp with time zone default current_timestamp, + unique(operator, strategy, transaction_hash, log_index) + ) + `, + `create index if not exists idx_operator_share_changes_operator_strat on operator_share_changes (operator, strategy)`, + `create index if not exists idx_operator_share_changes_block on operator_share_changes (block_number)`, + `create table if not exists operator_shares ( + operator varchar, + strategy varchar, + shares numeric, + block_number bigint, + created_at timestamp with time zone default current_timestamp, + unique (operator, strategy, block_number) + )`, + `create index if not exists idx_operator_shares_operator_strategy on operator_shares (operator, strategy)`, + `create index if not exists idx_operator_shares_block on operator_shares (block_number)`, + } + for _, query := range queries { + if _, err := db.Exec(query); err != nil { + return err + } + } + return nil +} + +func (m *Migration) GetName() string { + return "202409051720_operatorShareChanges" +} diff --git a/internal/postgres/migrations/migrator.go b/internal/postgres/migrations/migrator.go index 8cf98e6b..1a939f94 100644 --- a/internal/postgres/migrations/migrator.go +++ b/internal/postgres/migrations/migrator.go @@ -26,6 +26,7 @@ import ( _202407111116_addAvsDirectoryAddress "github.com/Layr-Labs/sidecar/internal/postgres/migrations/202407111116_addAvsDirectoryAddress" _202407121407_updateProxyContractIndex "github.com/Layr-Labs/sidecar/internal/postgres/migrations/202407121407_updateProxyContractIndex" _202408200934_eigenlayerStateTables "github.com/Layr-Labs/sidecar/internal/postgres/migrations/202408200934_eigenlayerStateTables" + _202409051720_operatorShareChanges "github.com/Layr-Labs/sidecar/internal/postgres/migrations/202409051720_operatorShareChanges" "go.uber.org/zap" "gorm.io/gorm" "time" @@ -76,6 +77,7 @@ func (m *Migrator) MigrateAll() error { &_202407111116_addAvsDirectoryAddress.Migration{}, &_202407121407_updateProxyContractIndex.Migration{}, &_202408200934_eigenlayerStateTables.Migration{}, + &_202409051720_operatorShareChanges.Migration{}, } for _, migration := range migrations { diff --git a/internal/storage/tables.go b/internal/storage/tables.go index e17e210f..0f87e6aa 100644 --- a/internal/storage/tables.go +++ b/internal/storage/tables.go @@ -9,30 +9,6 @@ import ( // Append only tables of state // ---------------------------------------------------------------------------- -/* -create table if not exists operator_share_changes ( - - id serial primary key, - operator varchar, - strategy varchar, - shares numeric, - transaction_hash varchar, - log_index bigint, - block_number bigint - -); -*/ -type OperatorShareChanges struct { - Id uint64 `gorm:"type:serial"` - Operator string - Strategy string - Shares big.Int `gorm:"type:numeric"` - TransactionHash string - LogIndex uint64 - BlockNumber uint64 - CreatedAt time.Time -} - /* create table if not exists staker_share_changes ( @@ -164,26 +140,6 @@ type RewardForAllSubmissions struct { // Block-based "summary" tables // ---------------------------------------------------------------------------- -/* -create table if not exists operator_shares ( - - operator varchar, - strategy varchar, - shares numeric, - block_number bigint, - created_at timestamp with time zone - unique idx_uniq_operator_shares_block (operator, strategy, block_number) - -) -*/ -type OperatorShares struct { - Operator string - Strategy string - Shares big.Int `gorm:"type:numeric"` - BlockNumber uint64 - CreatedAt time.Time -} - /* create table if not exists staker_shares ( diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 6265ef0c..d3408e0e 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,6 +1,7 @@ package utils import ( + "encoding/hex" "fmt" "strings" ) @@ -13,3 +14,7 @@ var ( func AreAddressesEqual(a, b string) bool { return strings.ToLower(a) == strings.ToLower(b) } + +func ConvertBytesToString(b []byte) string { + return "0x" + hex.EncodeToString(b[:]) +} diff --git a/sql/schema.sql b/sql/schema.sql index b0e9febd..b5353100 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -188,6 +188,56 @@ CREATE SEQUENCE public.operator_restaked_strategies_id_seq ALTER SEQUENCE public.operator_restaked_strategies_id_seq OWNED BY public.operator_restaked_strategies.id; +-- +-- Name: operator_share_changes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.operator_share_changes ( + id integer NOT NULL, + operator character varying, + strategy character varying, + shares numeric, + transaction_hash character varying, + transaction_index bigint, + log_index bigint, + block_number bigint, + created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP +); + + +-- +-- Name: operator_share_changes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.operator_share_changes_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: operator_share_changes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.operator_share_changes_id_seq OWNED BY public.operator_share_changes.id; + + +-- +-- Name: operator_shares; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.operator_shares ( + operator character varying, + strategy character varying, + shares numeric, + block_number bigint, + created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP +); + + -- -- Name: proxy_contracts; Type: TABLE; Schema: public; Owner: - -- @@ -296,6 +346,13 @@ ALTER TABLE ONLY public.contracts ALTER COLUMN id SET DEFAULT nextval('public.co ALTER TABLE ONLY public.operator_restaked_strategies ALTER COLUMN id SET DEFAULT nextval('public.operator_restaked_strategies_id_seq'::regclass); +-- +-- Name: operator_share_changes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.operator_share_changes ALTER COLUMN id SET DEFAULT nextval('public.operator_share_changes_id_seq'::regclass); + + -- -- Name: avs_operator_changes avs_operator_changes_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -344,6 +401,22 @@ ALTER TABLE ONLY public.operator_restaked_strategies ADD CONSTRAINT operator_restaked_strategies_pkey PRIMARY KEY (id); +-- +-- Name: operator_share_changes operator_share_changes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.operator_share_changes + ADD CONSTRAINT operator_share_changes_pkey PRIMARY KEY (id); + + +-- +-- Name: operator_shares operator_shares_operator_strategy_block_number_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.operator_shares + ADD CONSTRAINT operator_shares_operator_strategy_block_number_key UNIQUE (operator, strategy, block_number); + + -- -- Name: registered_avs_operators registered_avs_operators_operator_avs_block_number_key; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -389,6 +462,34 @@ CREATE INDEX idx_avs_operator_changes_block ON public.avs_operator_changes USING CREATE INDEX idx_bytecode_hash ON public.contracts USING btree (bytecode_hash); +-- +-- Name: idx_operator_share_changes_block; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_operator_share_changes_block ON public.operator_share_changes USING btree (block_number); + + +-- +-- Name: idx_operator_share_changes_operator_strat; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_operator_share_changes_operator_strat ON public.operator_share_changes USING btree (operator, strategy); + + +-- +-- Name: idx_operator_shares_block; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_operator_shares_block ON public.operator_shares USING btree (block_number); + + +-- +-- Name: idx_operator_shares_operator_strategy; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_operator_shares_operator_strategy ON public.operator_shares USING btree (operator, strategy); + + -- -- Name: idx_proxy_contract_contract_address; Type: INDEX; Schema: public; Owner: - -- @@ -575,6 +676,7 @@ INSERT INTO public.migrations VALUES ('202407110946_addBlockTimeToRestakedStrate INSERT INTO public.migrations VALUES ('202407111116_addAvsDirectoryAddress', '2024-07-24 09:13:18.235218-05', NULL); INSERT INTO public.migrations VALUES ('202407121407_updateProxyContractIndex', '2024-07-24 09:13:18.240594-05', NULL); INSERT INTO public.migrations VALUES ('202408200934_eigenlayerStateTables', '2024-09-05 16:16:40.950631-05', NULL); +INSERT INTO public.migrations VALUES ('202409051720_operatorShareChanges', '2024-09-05 19:14:07.595987-05', NULL); --