diff --git a/.gitignore b/.gitignore index 1d3760f9..58930609 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /*.sw* *.terraform *.terraform* +*.sql +*.dump /bin /scripts/runLocal sidecar.db* @@ -30,5 +32,5 @@ node_modules chart_releases /snapshots/**/*.sql /snapshots/**/*.csv -*.sql -*.dump + +.env \ No newline at end of file diff --git a/.testdataVersion b/.testdataVersion index 72b6e54f..423fd962 100644 --- a/.testdataVersion +++ b/.testdataVersion @@ -1 +1 @@ -d67ef5d895bdc0ccd6a006a1683ac3e58f820ad0 +da8f00be49d1447f22934629d9d6819e3d763af9 diff --git a/VERSION b/VERSION index 795460fc..3448eb6e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.1.0 +v2.0.0-rc.1 diff --git a/audits/Rewards v2 - OpenBlock - Dec 2024.pdf b/audits/Rewards v2 - OpenBlock - Dec 2024.pdf new file mode 100644 index 00000000..a68af3b5 Binary files /dev/null and b/audits/Rewards v2 - OpenBlock - Dec 2024.pdf differ diff --git a/audits/Rewards v2 - SigmaPrime - Dec 2024.pdf b/audits/Rewards v2 - SigmaPrime - Dec 2024.pdf new file mode 100644 index 00000000..65d4c5d5 Binary files /dev/null and b/audits/Rewards v2 - SigmaPrime - Dec 2024.pdf differ diff --git a/audits/Sidecar - SigmaPrime - Nov 2024.pdf b/audits/Sidecar - SigmaPrime - Nov 2024.pdf new file mode 100644 index 00000000..9aff63d6 Binary files /dev/null and b/audits/Sidecar - SigmaPrime - Nov 2024.pdf differ diff --git a/internal/config/config.go b/internal/config/config.go index d1d0627d..dc1e9b42 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -3,8 +3,10 @@ package config import ( "errors" "fmt" + "slices" "strconv" "strings" + "time" "github.com/spf13/viper" ) @@ -27,13 +29,18 @@ const ( Chain_Holesky Chain = "holesky" Chain_Preprod Chain = "preprod" - Fork_Nile ForkName = "nile" - Fork_Amazon ForkName = "amazon" - Fork_Panama ForkName = "panama" - ENV_PREFIX = "SIDECAR" ) +// Rewards forks named after rivers +const ( + RewardsFork_Nile ForkName = "nile" + RewardsFork_Amazon ForkName = "amazon" + RewardsFork_Panama ForkName = "panama" + RewardsFork_Arno ForkName = "arno" + RewardsFork_Trinity ForkName = "trinity" +) + func normalizeFlagName(name string) string { return strings.ReplaceAll(name, "-", "_") } @@ -200,6 +207,10 @@ type ContractAddresses struct { AvsDirectory string } +func (c *Config) ChainIsOneOf(chains ...Chain) bool { + return slices.Contains(chains, c.Chain) +} + func (c *Config) GetContractsMapForChain() *ContractAddresses { if c.Chain == Chain_Preprod { return &ContractAddresses{ @@ -261,28 +272,62 @@ func (c *Config) GetGenesisBlockNumber() uint64 { type ForkMap map[ForkName]string -func (c *Config) GetForkDates() (ForkMap, error) { +func (c *Config) GetRewardsSqlForkDates() (ForkMap, error) { switch c.Chain { case Chain_Preprod: return ForkMap{ - 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", + RewardsFork_Amazon: "1970-01-01", // Amazon hard fork was never on preprod as we backfilled + RewardsFork_Nile: "2024-08-14", // Last calculation end timestamp was 8-13: https://holesky.etherscan.io/tx/0xb5a6855e88c79312b7c0e1c9f59ae9890b97f157ea27e69e4f0fadada4712b64#eventlog + RewardsFork_Panama: "2024-10-01", + RewardsFork_Arno: "2024-12-11", + RewardsFork_Trinity: "2025-01-09", }, 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", + RewardsFork_Amazon: "1970-01-01", // Amazon hard fork was never on testnet as we backfilled + RewardsFork_Nile: "2024-08-13", // Last calculation end timestamp was 8-12: https://holesky.etherscan.io/tx/0x5fc81b5ed2a78b017ef313c181d8627737a97fef87eee85acedbe39fc8708c56#eventlog + RewardsFork_Panama: "2024-10-01", + RewardsFork_Arno: "2024-12-13", + RewardsFork_Trinity: "2025-01-09", }, 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", + RewardsFork_Amazon: "2024-08-02", // Last calculation end timestamp was 8-01: https://etherscan.io/tx/0x2aff6f7b0132092c05c8f6f41a5e5eeeb208aa0d95ebcc9022d7823e343dd012#eventlog + RewardsFork_Nile: "2024-08-12", // Last calculation end timestamp was 8-11: https://etherscan.io/tx/0x922d29d93c02d189fc2332041f01a80e0007cd7a625a5663ef9d30082f7ef66f#eventlog + RewardsFork_Panama: "2024-10-01", + RewardsFork_Arno: "2025-01-21", + RewardsFork_Trinity: "2025-01-21", + }, nil + } + return nil, errors.New("unsupported chain") +} + +type ModelForkMap map[ForkName]uint64 + +// Model forks, named after US capitols +const ( + // ModelFork_Austin changes the formatting for merkel leaves in: ODRewardSubmissions, OperatorAVSSplits, and + // OperatorPISplits based on feedback from the rewards-v2 audit + ModelFork_Austin ForkName = "austin" +) + +func (c *Config) GetModelForks() (ModelForkMap, error) { + switch c.Chain { + case Chain_Preprod: + return ModelForkMap{ + ModelFork_Austin: 3113600, + }, nil + case Chain_Holesky: + return ModelForkMap{ + ModelFork_Austin: 3113600, + }, nil + case Chain_Mainnet: + return ModelForkMap{ + ModelFork_Austin: 0, // doesnt apply to mainnet }, nil } return nil, errors.New("unsupported chain") + } func (c *Config) GetEigenLayerGenesisBlockHeight() (uint64, error) { @@ -307,6 +352,23 @@ func (c *Config) GetOperatorRestakedStrategiesStartBlock() uint64 { return 0 } +func (c *Config) IsRewardsV2EnabledForCutoffDate(cutoffDate string) (bool, error) { + forks, err := c.GetRewardsSqlForkDates() + 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[RewardsFork_Arno]) + if err != nil { + return false, errors.Join(fmt.Errorf("failed to parse Arno fork date %s", forks[RewardsFork_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 @@ -322,6 +384,11 @@ func (c *Config) CanIgnoreIncorrectRewardsRoot(blockNumber uint64) bool { if blockNumber == 2812052 { return true } + + // ignore rewards-v2 and slashing deployment/testing range + if blockNumber >= 2877938 && blockNumber <= 2965149 { + return true + } case Chain_Holesky: // roughly 2024-08-01 if blockNumber < 2046020 { 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..e02385b1 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-13' +``` 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..ed331621 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-13' + ) +select + operator, + avs, + status as registered, + log_index, + block_number +from filtered +``` + ## Expected results _See `generateExpectedResults.sql`_ diff --git a/internal/tests/testdata/operatorAvsRegistrationSnapshots/preprodRewardsV2_generateExpectedResults.sql b/internal/tests/testdata/operatorAvsRegistrationSnapshots/preprodRewardsV2_generateExpectedResults.sql new file mode 100644 index 00000000..33ded9bd --- /dev/null +++ b/internal/tests/testdata/operatorAvsRegistrationSnapshots/preprodRewardsV2_generateExpectedResults.sql @@ -0,0 +1,67 @@ +COPY ( + with filtered as ( + select * from dbt_preprod_holesky_rewards.operator_avs_status + where block_time < '2024-12-11' +), +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-12-11') 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, + 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/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..1bd489dd --- /dev/null +++ b/internal/tests/testdata/operatorDirectedRewardSubmissions/README.md @@ -0,0 +1,57 @@ +## preprod rewards-v2 + +```sql +WITH strategies AS ( + 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 + address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' + 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 + address = '0xb22ef643e1e067c994019a4c19e403253c05c2b0' + and event_name = 'OperatorDirectedAVSRewardsSubmissionCreated' + ), + joined_data as ( +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 + inner join operators on( + strategies.reward_hash = operators.reward_hash + ) + ) +select * from joined_data +``` 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..24d9d73c 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-13' +``` + ## Expected results _See `generateExpectedResults.sql`_ diff --git a/internal/tests/testdata/operatorRestakedStrategies/preprodRewardsV2_generateExpectedResults.sql b/internal/tests/testdata/operatorRestakedStrategies/preprodRewardsV2_generateExpectedResults.sql new file mode 100644 index 00000000..52eceb25 --- /dev/null +++ b/internal/tests/testdata/operatorRestakedStrategies/preprodRewardsV2_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('0x141d6995556135d4997b2ff72eb443be300353bc') + and block_time < '2024-12-11' +), + 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, + 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 DELIMITER ',' CSV HEADER; diff --git a/internal/tests/testdata/operatorShareSnapshots/README.md b/internal/tests/testdata/operatorShareSnapshots/README.md index 2b55bd90..3b6165b8 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-13' +``` + ## 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..b8c5cfc8 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-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-13' + ) 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..96a59a5b 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-13' +``` + ```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..a805cee2 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-13' +``` + ## 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..468f48d2 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_preprod_holesky_rewards.staker_deposits + where block_date < '2024-12-13' + + 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-13' + + 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-13' + + 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-13' + ) 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/mainnet.json b/pkg/contractStore/coreContracts/mainnet.json index 6026c45f..1f366ee9 100644 --- a/pkg/contractStore/coreContracts/mainnet.json +++ b/pkg/contractStore/coreContracts/mainnet.json @@ -74,6 +74,10 @@ "contract_address": "0x731a0ad160e407393ff662231add6dd145ad3fea", "contract_abi": "[{\"inputs\":[{\"internalType\":\"contract IETHPOSDeposit\",\"name\":\"_ethPOS\",\"type\":\"address\"},{\"internalType\":\"contract IBeacon\",\"name\":\"_eigenPodBeacon\",\"type\":\"address\"},{\"internalType\":\"contract IStrategyManager\",\"name\":\"_strategyManager\",\"type\":\"address\"},{\"internalType\":\"contract ISlasher\",\"name\":\"_slasher\",\"type\":\"address\"},{\"internalType\":\"contract IDelegationManager\",\"name\":\"_delegationManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"BeaconChainETHDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"nonce\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"delegatedAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"name\":\"BeaconChainETHWithdrawalCompleted\",\"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\":\"podOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"newTotalShares\",\"type\":\"int256\"}],\"name\":\"NewTotalShares\",\"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\":true,\"internalType\":\"address\",\"name\":\"eigenPod\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"}],\"name\":\"PodDeployed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"sharesDelta\",\"type\":\"int256\"}],\"name\":\"PodSharesUpdated\",\"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\":[{\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"addShares\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"beaconChainETHStrategy\",\"outputs\":[{\"internalType\":\"contract IStrategy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createPod\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"delegationManager\",\"outputs\":[{\"internalType\":\"contract IDelegationManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"eigenPodBeacon\",\"outputs\":[{\"internalType\":\"contract IBeacon\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ethPOS\",\"outputs\":[{\"internalType\":\"contract IETHPOSDeposit\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"}],\"name\":\"getPod\",\"outputs\":[{\"internalType\":\"contract IEigenPod\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"}],\"name\":\"hasPod\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"},{\"internalType\":\"contract IPauserRegistry\",\"name\":\"_pauserRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_initPausedStatus\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"numPods\",\"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\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"ownerToPod\",\"outputs\":[{\"internalType\":\"contract IEigenPod\",\"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\":\"\",\"type\":\"address\"}],\"name\":\"podOwnerShares\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"sharesDelta\",\"type\":\"int256\"}],\"name\":\"recordBeaconChainETHBalanceUpdate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"podOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"removeShares\",\"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\":[],\"name\":\"slasher\",\"outputs\":[{\"internalType\":\"contract ISlasher\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"depositDataRoot\",\"type\":\"bytes32\"}],\"name\":\"stake\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategyManager\",\"outputs\":[{\"internalType\":\"contract IStrategyManager\",\"name\":\"\",\"type\":\"address\"}],\"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\":\"podOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"withdrawSharesAsTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", "bytecode_hash": "8e37325a8c09555be3e2ec19453cc67ee202f6847cc5348b263896126d7268a8" + }, { + "contract_address": "0x29a954e9e7f12936db89b183ecdf879fbbb99f14", + "contract_abi": "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_delegationManager\",\"type\":\"address\",\"internalType\":\"contract IDelegationManager\"},{\"name\":\"_strategyManager\",\"type\":\"address\",\"internalType\":\"contract IStrategyManager\"},{\"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\",\"internalType\":\"uint32\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"CALCULATION_INTERVAL_SECONDS\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"GENESIS_REWARDS_TIMESTAMP\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MAX_FUTURE_LENGTH\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MAX_RETROACTIVE_LENGTH\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MAX_REWARDS_DURATION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"activationDelay\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"beaconChainETHStrategy\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"calculateEarnerLeafHash\",\"inputs\":[{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"components\":[{\"name\":\"earner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"calculateTokenLeafHash\",\"inputs\":[{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf\",\"components\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"cumulativeEarnings\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"checkClaim\",\"inputs\":[{\"name\":\"claim\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"components\":[{\"name\":\"rootIndex\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"earnerIndex\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"earnerTreeProof\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"earnerLeaf\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"components\":[{\"name\":\"earner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"tokenIndices\",\"type\":\"uint32[]\",\"internalType\":\"uint32[]\"},{\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"tokenLeaves\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"components\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"cumulativeEarnings\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"claimerFor\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"createAVSRewardsSubmission\",\"inputs\":[{\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"createOperatorDirectedAVSRewardsSubmission\",\"inputs\":[{\"name\":\"avs\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"operatorDirectedRewardsSubmissions\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission[]\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"operatorRewards\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"components\":[{\"name\":\"operator\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"description\",\"type\":\"string\",\"internalType\":\"string\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"createRewardsForAllEarners\",\"inputs\":[{\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"createRewardsForAllSubmission\",\"inputs\":[{\"name\":\"rewardsSubmissions\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission[]\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"cumulativeClaimed\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contract IERC20\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"currRewardsCalculationEndTimestamp\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"defaultOperatorSplitBips\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"delegationManager\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contract IDelegationManager\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"disableRoot\",\"inputs\":[{\"name\":\"rootIndex\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"domainSeparator\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentClaimableDistributionRoot\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"components\":[{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"activatedAt\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"disabled\",\"type\":\"bool\",\"internalType\":\"bool\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentDistributionRoot\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"components\":[{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"activatedAt\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"disabled\",\"type\":\"bool\",\"internalType\":\"bool\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getDistributionRootAtIndex\",\"inputs\":[{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.DistributionRoot\",\"components\":[{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"activatedAt\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"disabled\",\"type\":\"bool\",\"internalType\":\"bool\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getDistributionRootsLength\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getOperatorAVSSplit\",\"inputs\":[{\"name\":\"operator\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"avs\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getOperatorPISplit\",\"inputs\":[{\"name\":\"operator\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRootIndexFromHash\",\"inputs\":[{\"name\":\"rootHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"initialOwner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_pauserRegistry\",\"type\":\"address\",\"internalType\":\"contract IPauserRegistry\"},{\"name\":\"initialPausedStatus\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_rewardsUpdater\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_activationDelay\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"_defaultSplitBips\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isAVSRewardsSubmissionHash\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isOperatorDirectedAVSRewardsSubmissionHash\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isRewardsForAllSubmitter\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isRewardsSubmissionForAllEarnersHash\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isRewardsSubmissionForAllHash\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pause\",\"inputs\":[{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"pauseAll\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[{\"name\":\"index\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pauserRegistry\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contract IPauserRegistry\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"processClaim\",\"inputs\":[{\"name\":\"claim\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim\",\"components\":[{\"name\":\"rootIndex\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"earnerIndex\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"earnerTreeProof\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"earnerLeaf\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"components\":[{\"name\":\"earner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"tokenIndices\",\"type\":\"uint32[]\",\"internalType\":\"uint32[]\"},{\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"tokenLeaves\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"components\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"cumulativeEarnings\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]},{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"processClaims\",\"inputs\":[{\"name\":\"claims\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.RewardsMerkleClaim[]\",\"components\":[{\"name\":\"rootIndex\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"earnerIndex\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"earnerTreeProof\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"earnerLeaf\",\"type\":\"tuple\",\"internalType\":\"struct IRewardsCoordinator.EarnerTreeMerkleLeaf\",\"components\":[{\"name\":\"earner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"earnerTokenRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"tokenIndices\",\"type\":\"uint32[]\",\"internalType\":\"uint32[]\"},{\"name\":\"tokenTreeProofs\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"tokenLeaves\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.TokenTreeMerkleLeaf[]\",\"components\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"cumulativeEarnings\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]},{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"rewardsUpdater\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setActivationDelay\",\"inputs\":[{\"name\":\"_activationDelay\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setClaimerFor\",\"inputs\":[{\"name\":\"claimer\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setDefaultOperatorSplit\",\"inputs\":[{\"name\":\"split\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setOperatorAVSSplit\",\"inputs\":[{\"name\":\"operator\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"avs\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"split\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setOperatorPISplit\",\"inputs\":[{\"name\":\"operator\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"split\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setPauserRegistry\",\"inputs\":[{\"name\":\"newPauserRegistry\",\"type\":\"address\",\"internalType\":\"contract IPauserRegistry\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setRewardsForAllSubmitter\",\"inputs\":[{\"name\":\"_submitter\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_newValue\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setRewardsUpdater\",\"inputs\":[{\"name\":\"_rewardsUpdater\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"strategyManager\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contract IStrategyManager\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"submissionNonce\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"submitRoot\",\"inputs\":[{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unpause\",\"inputs\":[{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"AVSRewardsSubmissionCreated\",\"inputs\":[{\"name\":\"avs\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"submissionNonce\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"rewardsSubmission\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ActivationDelaySet\",\"inputs\":[{\"name\":\"oldActivationDelay\",\"type\":\"uint32\",\"indexed\":false,\"internalType\":\"uint32\"},{\"name\":\"newActivationDelay\",\"type\":\"uint32\",\"indexed\":false,\"internalType\":\"uint32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ClaimerForSet\",\"inputs\":[{\"name\":\"earner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"oldClaimer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"claimer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DefaultOperatorSplitBipsSet\",\"inputs\":[{\"name\":\"oldDefaultOperatorSplitBips\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"},{\"name\":\"newDefaultOperatorSplitBips\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DistributionRootDisabled\",\"inputs\":[{\"name\":\"rootIndex\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"uint32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DistributionRootSubmitted\",\"inputs\":[{\"name\":\"rootIndex\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"uint32\"},{\"name\":\"root\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"rewardsCalculationEndTimestamp\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"uint32\"},{\"name\":\"activatedAt\",\"type\":\"uint32\",\"indexed\":false,\"internalType\":\"uint32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OperatorAVSSplitBipsSet\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"operator\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"avs\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"activatedAt\",\"type\":\"uint32\",\"indexed\":false,\"internalType\":\"uint32\"},{\"name\":\"oldOperatorAVSSplitBips\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"},{\"name\":\"newOperatorAVSSplitBips\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OperatorDirectedAVSRewardsSubmissionCreated\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"avs\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"operatorDirectedRewardsSubmissionHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"submissionNonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"operatorDirectedRewardsSubmission\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.OperatorDirectedRewardsSubmission\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"operatorRewards\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.OperatorReward[]\",\"components\":[{\"name\":\"operator\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"description\",\"type\":\"string\",\"internalType\":\"string\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OperatorPISplitBipsSet\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"operator\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"activatedAt\",\"type\":\"uint32\",\"indexed\":false,\"internalType\":\"uint32\"},{\"name\":\"oldOperatorPISplitBips\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"},{\"name\":\"newOperatorPISplitBips\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Paused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PauserRegistrySet\",\"inputs\":[{\"name\":\"pauserRegistry\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"contract IPauserRegistry\"},{\"name\":\"newPauserRegistry\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"contract IPauserRegistry\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RewardsClaimed\",\"inputs\":[{\"name\":\"root\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"earner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"claimer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"token\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"contract IERC20\"},{\"name\":\"claimedAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RewardsForAllSubmitterSet\",\"inputs\":[{\"name\":\"rewardsForAllSubmitter\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"oldValue\",\"type\":\"bool\",\"indexed\":true,\"internalType\":\"bool\"},{\"name\":\"newValue\",\"type\":\"bool\",\"indexed\":true,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RewardsSubmissionForAllCreated\",\"inputs\":[{\"name\":\"submitter\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"submissionNonce\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"rewardsSubmission\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RewardsSubmissionForAllEarnersCreated\",\"inputs\":[{\"name\":\"tokenHopper\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"submissionNonce\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"rewardsSubmissionHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"rewardsSubmission\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"struct IRewardsCoordinator.RewardsSubmission\",\"components\":[{\"name\":\"strategiesAndMultipliers\",\"type\":\"tuple[]\",\"internalType\":\"struct IRewardsCoordinator.StrategyAndMultiplier[]\",\"components\":[{\"name\":\"strategy\",\"type\":\"address\",\"internalType\":\"contract IStrategy\"},{\"name\":\"multiplier\",\"type\":\"uint96\",\"internalType\":\"uint96\"}]},{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"contract IERC20\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"startTimestamp\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"duration\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RewardsUpdaterSet\",\"inputs\":[{\"name\":\"oldRewardsUpdater\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newRewardsUpdater\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unpaused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false}]", + "bytecode_hash": "a58190e3c0842b13e8bfc55ccd4e76ac29ded1c363b6048194ec98921fbd3bf5" } ], "proxy_contracts": [ @@ -151,6 +155,10 @@ "contract_address": "0x91e677b07f7af907ec9a428aafa9fc14a0d3a338", "proxy_contract_address": "0x731a0ad160e407393ff662231add6dd145ad3fea", "block_number": 20571838 + }, { + "contract_address": "0x7750d328b314effa365a0402ccfd489b80b0adda", + "proxy_contract_address": "0x29a954e9e7f12936db89b183ecdf879fbbb99f14", + "block_number": 21595369 } ] } diff --git a/pkg/contractStore/coreContracts/preprod.json b/pkg/contractStore/coreContracts/preprod.json index 26e30fd0..57fca286 100644 --- a/pkg/contractStore/coreContracts/preprod.json +++ b/pkg/contractStore/coreContracts/preprod.json @@ -89,6 +89,19 @@ "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" + }, { + "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": [ @@ -171,6 +184,21 @@ "contract_address": "0xb22ef643e1e067c994019a4c19e403253c05c2b0", "proxy_contract_address": "0x7523b42b081c30fa245aa4039c645e36746fc400", "block_number": 2282920 + }, + { + "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 } ] } diff --git a/pkg/contractStore/coreContracts/testnet.json b/pkg/contractStore/coreContracts/testnet.json index 6d351a8d..a9405753 100644 --- a/pkg/contractStore/coreContracts/testnet.json +++ b/pkg/contractStore/coreContracts/testnet.json @@ -69,6 +69,11 @@ "contract_address": "0xacc1fb458a1317e886db376fc8141540537e68fe", "contract_abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"changeAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"implementation_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", "bytecode_hash": "7280883619d75d103a74a626d5b70cdaf83f15e6d3d275e454bad1a575015d52" + }, + { + "contract_address": "0x9174c082a4bec1f5a756a0a658bba470bc82550c", + "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": "a416506ea21e40eaec31dc77ce070a0af90562bfe7b8adc6da29b569c2416748" } ], "proxy_contracts" : [ @@ -116,6 +121,11 @@ "contract_address": "0xacc1fb458a1317e886db376fc8141540537e68fe", "proxy_contract_address": "0x1131d88143a35011e35263b43ec66cdd27025584", "block_number": 2323822 + }, + { + "contract_address": "0xacc1fb458a1317e886db376fc8141540537e68fe", + "proxy_contract_address": "0x9174c082a4bec1f5a756a0a658bba470bc82550c", + "block_number": 2930139 } ] } diff --git a/pkg/eigenState/defaultOperatorSplits/defaultOperatorSplits.go b/pkg/eigenState/defaultOperatorSplits/defaultOperatorSplits.go new file mode 100644 index 00000000..3879fc31 --- /dev/null +++ b/pkg/eigenState/defaultOperatorSplits/defaultOperatorSplits.go @@ -0,0 +1,275 @@ +package defaultOperatorSplits + +import ( + "encoding/json" + "fmt" + "slices" + "sort" + "strings" + + "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" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type DefaultOperatorSplit struct { + OldDefaultOperatorSplitBips uint64 + NewDefaultOperatorSplitBips uint64 + BlockNumber uint64 + TransactionHash string + LogIndex uint64 +} + +type DefaultOperatorSplitModel struct { + base.BaseEigenState + StateTransitions types.StateTransitions[[]*DefaultOperatorSplit] + 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]*DefaultOperatorSplit + committedState map[uint64][]*DefaultOperatorSplit +} + +func NewDefaultOperatorSplitModel( + esm *stateManager.EigenStateManager, + grm *gorm.DB, + logger *zap.Logger, + globalConfig *config.Config, +) (*DefaultOperatorSplitModel, error) { + model := &DefaultOperatorSplitModel{ + BaseEigenState: base.BaseEigenState{ + Logger: logger, + }, + DB: grm, + logger: logger, + globalConfig: globalConfig, + stateAccumulator: make(map[uint64]map[types.SlotID]*DefaultOperatorSplit), + committedState: make(map[uint64][]*DefaultOperatorSplit), + } + + esm.RegisterState(model, 10) + return model, nil +} + +func (dos *DefaultOperatorSplitModel) GetModelName() string { + return "DefaultOperatorSplitModel" +} + +type defaultOperatorSplitOutputData struct { + OldDefaultOperatorSplitBips uint64 `json:"oldDefaultOperatorSplitBips"` + NewDefaultOperatorSplitBips uint64 `json:"newDefaultOperatorSplitBips"` +} + +func parseDefaultOperatorSplitOutputData(outputDataStr string) (*defaultOperatorSplitOutputData, error) { + outputData := &defaultOperatorSplitOutputData{} + decoder := json.NewDecoder(strings.NewReader(outputDataStr)) + decoder.UseNumber() + + err := decoder.Decode(&outputData) + if err != nil { + return nil, err + } + + return outputData, err +} + +func (dos *DefaultOperatorSplitModel) handleDefaultOperatorSplitBipsSetEvent(log *storage.TransactionLog) (*DefaultOperatorSplit, error) { + outputData, err := parseDefaultOperatorSplitOutputData(log.OutputData) + if err != nil { + return nil, err + } + + split := &DefaultOperatorSplit{ + OldDefaultOperatorSplitBips: outputData.OldDefaultOperatorSplitBips, + NewDefaultOperatorSplitBips: outputData.NewDefaultOperatorSplitBips, + BlockNumber: log.BlockNumber, + TransactionHash: log.TransactionHash, + LogIndex: log.LogIndex, + } + + return split, nil +} + +func (dos *DefaultOperatorSplitModel) GetStateTransitions() (types.StateTransitions[*DefaultOperatorSplit], []uint64) { + stateChanges := make(types.StateTransitions[*DefaultOperatorSplit]) + + stateChanges[0] = func(log *storage.TransactionLog) (*DefaultOperatorSplit, error) { + defaultOperatorSplit, err := dos.handleDefaultOperatorSplitBipsSetEvent(log) + if err != nil { + return nil, err + } + + slotId := base.NewSlotID(defaultOperatorSplit.TransactionHash, defaultOperatorSplit.LogIndex) + + _, ok := dos.stateAccumulator[log.BlockNumber][slotId] + if ok { + err := fmt.Errorf("Duplicate default operator split submitted for slot %s at block %d", slotId, log.BlockNumber) + dos.logger.Sugar().Errorw("Duplicate default operator split submitted", zap.Error(err)) + return nil, err + } + + dos.stateAccumulator[log.BlockNumber][slotId] = defaultOperatorSplit + + return defaultOperatorSplit, 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 (dos *DefaultOperatorSplitModel) getContractAddressesForEnvironment() map[string][]string { + contracts := dos.globalConfig.GetContractsMapForChain() + return map[string][]string{ + contracts.RewardsCoordinator: { + "DefaultOperatorSplitBipsSet", + }, + } +} + +func (dos *DefaultOperatorSplitModel) IsInterestingLog(log *storage.TransactionLog) bool { + addresses := dos.getContractAddressesForEnvironment() + return dos.BaseEigenState.IsInterestingLog(addresses, log) +} + +func (dos *DefaultOperatorSplitModel) SetupStateForBlock(blockNumber uint64) error { + dos.stateAccumulator[blockNumber] = make(map[types.SlotID]*DefaultOperatorSplit) + dos.committedState[blockNumber] = make([]*DefaultOperatorSplit, 0) + return nil +} + +func (dos *DefaultOperatorSplitModel) CleanupProcessedStateForBlock(blockNumber uint64) error { + delete(dos.stateAccumulator, blockNumber) + delete(dos.committedState, blockNumber) + return nil +} + +func (dos *DefaultOperatorSplitModel) HandleStateChange(log *storage.TransactionLog) (interface{}, error) { + stateChanges, sortedBlockNumbers := dos.GetStateTransitions() + + for _, blockNumber := range sortedBlockNumbers { + if log.BlockNumber >= blockNumber { + dos.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 (dos *DefaultOperatorSplitModel) prepareState(blockNumber uint64) ([]*DefaultOperatorSplit, error) { + accumulatedState, ok := dos.stateAccumulator[blockNumber] + if !ok { + err := fmt.Errorf("No accumulated state found for block %d", blockNumber) + dos.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + + recordsToInsert := make([]*DefaultOperatorSplit, 0) + for _, split := range accumulatedState { + recordsToInsert = append(recordsToInsert, split) + } + return recordsToInsert, nil +} + +// CommitFinalState commits the final state for the given block number. +func (dos *DefaultOperatorSplitModel) CommitFinalState(blockNumber uint64) error { + recordsToInsert, err := dos.prepareState(blockNumber) + if err != nil { + return err + } + + if len(recordsToInsert) > 0 { + for _, record := range recordsToInsert { + res := dos.DB.Model(&DefaultOperatorSplit{}).Clauses(clause.Returning{}).Create(&record) + if res.Error != nil { + dos.logger.Sugar().Errorw("Failed to insert records", zap.Error(res.Error)) + return res.Error + } + } + } + dos.committedState[blockNumber] = recordsToInsert + return nil +} + +// GenerateStateRoot generates the state root for the given block number using the results of the state changes. +func (dos *DefaultOperatorSplitModel) GenerateStateRoot(blockNumber uint64) ([]byte, error) { + inserts, err := dos.prepareState(blockNumber) + if err != nil { + return nil, err + } + + inputs := dos.sortValuesForMerkleTree(inserts) + + if len(inputs) == 0 { + return nil, nil + } + + fullTree, err := dos.MerkleizeEigenState(blockNumber, inputs) + if err != nil { + dos.logger.Sugar().Errorw("Failed to create merkle tree", + zap.Error(err), + zap.Uint64("blockNumber", blockNumber), + zap.Any("inputs", inputs), + ) + return nil, err + } + return fullTree.Root(), nil +} + +func (dos *DefaultOperatorSplitModel) GetCommittedState(blockNumber uint64) ([]interface{}, error) { + records, ok := dos.committedState[blockNumber] + if !ok { + err := fmt.Errorf("No committed state found for block %d", blockNumber) + dos.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + return base.CastCommittedStateToInterface(records), nil +} + +func (dos *DefaultOperatorSplitModel) sortValuesForMerkleTree(splits []*DefaultOperatorSplit) []*base.MerkleTreeInput { + inputs := make([]*base.MerkleTreeInput, 0) + for _, split := range splits { + slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) + value := fmt.Sprintf("%016x_%016x", split.OldDefaultOperatorSplitBips, split.NewDefaultOperatorSplitBips) + 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 (dos *DefaultOperatorSplitModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { + return dos.BaseEigenState.DeleteState("default_operator_splits", startBlockNumber, endBlockNumber, dos.DB) +} diff --git a/pkg/eigenState/defaultOperatorSplits/defaultOperatorSplits_test.go b/pkg/eigenState/defaultOperatorSplits/defaultOperatorSplits_test.go new file mode 100644 index 00000000..b0eb3d2e --- /dev/null +++ b/pkg/eigenState/defaultOperatorSplits/defaultOperatorSplits_test.go @@ -0,0 +1,141 @@ +package defaultOperatorSplits + +import ( + "fmt" + "math/big" + "os" + "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.Debug = os.Getenv(config.Debug) == "true" + 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, grm, l, cfg, nil +} + +func teardown(model *DefaultOperatorSplitModel) { + queries := []string{ + `truncate table default_operator_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 *DefaultOperatorSplitModel, 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_DefaultOperatorSplit(t *testing.T) { + dbName, 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 := NewDefaultOperatorSplitModel(esm, grm, l, cfg) + + t.Run("Handle a default operator 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: `[]`, + EventName: "DefaultOperatorSplitBipsSet", + LogIndex: big.NewInt(12).Uint64(), + OutputData: `{"oldDefaultOperatorSplitBips": 1000, "newDefaultOperatorSplitBips": 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.(*DefaultOperatorSplit) + + assert.Equal(t, uint64(1000), split.OldDefaultOperatorSplitBips) + assert.Equal(t, uint64(2000), split.NewDefaultOperatorSplitBips) + + err = model.CommitFinalState(blockNumber) + assert.Nil(t, err) + + splits := make([]*DefaultOperatorSplit, 0) + query := `select * from default_operator_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) + }) +} diff --git a/pkg/eigenState/eigenState.go b/pkg/eigenState/eigenState.go index b95f4ed0..0d441810 100644 --- a/pkg/eigenState/eigenState.go +++ b/pkg/eigenState/eigenState.go @@ -3,7 +3,11 @@ 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/defaultOperatorSplits" "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" @@ -48,5 +52,21 @@ 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 + } + if _, err := operatorAVSSplits.NewOperatorAVSSplitModel(sm, grm, l, cfg); err != nil { + 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 + } + if _, err := defaultOperatorSplits.NewDefaultOperatorSplitModel(sm, grm, l, cfg); err != nil { + l.Sugar().Errorw("Failed to create DefaultOperatorSplitModel", 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..8bdd1059 --- /dev/null +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits.go @@ -0,0 +1,325 @@ +package operatorAVSSplits + +import ( + "encoding/json" + "fmt" + "slices" + "sort" + "strings" + "time" + + "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" + "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 + committedState map[uint64][]*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), + committedState: make(map[uint64][]*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 { + err := fmt.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 + } + + 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) + oas.committedState[blockNumber] = make([]*OperatorAVSSplit, 0) + return nil +} + +func (oas *OperatorAVSSplitModel) CleanupProcessedStateForBlock(blockNumber uint64) error { + delete(oas.stateAccumulator, blockNumber) + delete(oas.committedState, 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 := fmt.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 + } + } + } + oas.committedState[blockNumber] = recordsToInsert + 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) ([]byte, error) { + inserts, err := oas.prepareState(blockNumber) + if err != nil { + return nil, err + } + + inputs, err := oas.sortValuesForMerkleTree(inserts) + if err != nil { + return nil, err + } + + if len(inputs) == 0 { + return nil, nil + } + + 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 nil, err + } + return fullTree.Root(), nil +} + +func (oas *OperatorAVSSplitModel) GetCommittedState(blockNumber uint64) ([]interface{}, error) { + records, ok := oas.committedState[blockNumber] + if !ok { + err := fmt.Errorf("No committed state found for block %d", blockNumber) + oas.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + return base.CastCommittedStateToInterface(records), nil +} + +func (oas *OperatorAVSSplitModel) formatMerkleLeafValue( + blockNumber uint64, + operator string, + avs string, + activatedAt *time.Time, + oldOperatorAVSSplitBips uint64, + newOperatorAVSSplitBips uint64, +) (string, error) { + modelForks, err := oas.globalConfig.GetModelForks() + if err != nil { + return "", err + } + if oas.globalConfig.ChainIsOneOf(config.Chain_Holesky, config.Chain_Preprod) && blockNumber < modelForks[config.ModelFork_Austin] { + // This format was used on preprod and testnet for rewards-v2 before launching to mainnet + return fmt.Sprintf("%s_%s_%d_%d_%d", operator, avs, activatedAt.Unix(), oldOperatorAVSSplitBips, newOperatorAVSSplitBips), nil + } + + return fmt.Sprintf("%s_%s_%016x_%016x_%016x", operator, avs, activatedAt.Unix(), oldOperatorAVSSplitBips, newOperatorAVSSplitBips), nil +} + +func (oas *OperatorAVSSplitModel) sortValuesForMerkleTree(splits []*OperatorAVSSplit) ([]*base.MerkleTreeInput, error) { + inputs := make([]*base.MerkleTreeInput, 0) + for _, split := range splits { + slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) + value, err := oas.formatMerkleLeafValue(split.BlockNumber, split.Operator, split.Avs, split.ActivatedAt, split.OldOperatorAVSSplitBips, split.NewOperatorAVSSplitBips) + if err != nil { + oas.logger.Sugar().Errorw("Failed to format merkle leaf value", + zap.Error(err), + zap.Uint64("blockNumber", split.BlockNumber), + zap.String("operator", split.Operator), + zap.String("avs", split.Avs), + zap.Time("activatedAt", *split.ActivatedAt), + zap.Uint64("oldOperatorAVSSplitBips", split.OldOperatorAVSSplitBips), + zap.Uint64("newOperatorAVSSplitBips", split.NewOperatorAVSSplitBips), + ) + return nil, err + } + 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, nil +} + +func (oas *OperatorAVSSplitModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { + return oas.BaseEigenState.DeleteState("operator_avs_splits", startBlockNumber, endBlockNumber, oas.DB) +} diff --git a/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go new file mode 100644 index 00000000..93d4ef51 --- /dev/null +++ b/pkg/eigenState/operatorAVSSplits/operatorAVSSplits_test.go @@ -0,0 +1,145 @@ +package operatorAVSSplits + +import ( + "fmt" + "math/big" + "os" + "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.Debug = os.Getenv(config.Debug) == "true" + 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, 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) { + dbName, 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) + }) +} diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go new file mode 100644 index 00000000..3b3bb023 --- /dev/null +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions.go @@ -0,0 +1,437 @@ +package operatorDirectedRewardSubmissions + +import ( + "encoding/json" + "fmt" + "math/big" + "slices" + "sort" + "strings" + "time" + + "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" + "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 + Description string + BlockNumber uint64 + TransactionHash string + LogIndex uint64 +} + +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 + committedState map[uint64][]*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), + committedState: make(map[uint64][]*OperatorDirectedRewardSubmission), + } + + esm.RegisterState(model, 7) + return model, nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) GetModelName() string { + return "OperatorDirectedRewardSubmissionsModel" +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) NewSlotID( + blockNumber uint64, + transactionHash string, + logIndex uint64, + rewardHash string, + strategyIndex uint64, + operatorIndex uint64, +) (types.SlotID, error) { + forks, err := odrs.globalConfig.GetModelForks() + if err != nil { + return "", err + } + if odrs.globalConfig.ChainIsOneOf(config.Chain_Holesky, config.Chain_Preprod) && blockNumber < forks[config.ModelFork_Austin] { + // This format was used on preprod and testnet for rewards-v2 before launching to mainnet + return base.NewSlotIDWithSuffix(transactionHash, logIndex, fmt.Sprintf("%s_%d_%d", rewardHash, strategyIndex, operatorIndex)), nil + } + return base.NewSlotIDWithSuffix(transactionHash, logIndex, fmt.Sprintf("%s_%016x_%016x", rewardHash, strategyIndex, operatorIndex)), nil +} + +type operatorDirectedRewardData struct { + StrategiesAndMultipliers []struct { + 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 { + SubmissionNonce json.Number `json:"submissionNonce"` + OperatorDirectedRewardsSubmission *operatorDirectedRewardData `json:"operatorDirectedRewardsSubmission"` +} + +func parseRewardSubmissionOutputData(outputDataStr string) (*operatorDirectedRewardSubmissionOutputData, error) { + outputData := &operatorDirectedRewardSubmissionOutputData{} + decoder := json.NewDecoder(strings.NewReader(outputDataStr)) + decoder.UseNumber() + + err := decoder.Decode(&outputData) + if err != nil { + return nil, err + } + + return outputData, err +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) handleOperatorDirectedRewardSubmissionCreatedEvent(log *storage.TransactionLog) ([]*OperatorDirectedRewardSubmission, error) { + arguments, err := odrs.ParseLogArguments(log) + if err != nil { + return nil, err + } + + outputData, err := parseRewardSubmissionOutputData(log.OutputData) + if err != nil { + return nil, err + } + outputRewardData := outputData.OperatorDirectedRewardsSubmission + + rewardSubmissions := make([]*OperatorDirectedRewardSubmission, 0) + + 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, fmt.Errorf("Failed to parse multiplier to Big257: %s", strategyAndMultiplier.Multiplier.String()) + } + + for j, operatorReward := range outputRewardData.OperatorRewards { + amountBig, success := numbers.NewBig257().SetString(operatorReward.Amount.String(), 10) + if !success { + return nil, fmt.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, + Description: outputRewardData.Description, + BlockNumber: log.BlockNumber, + TransactionHash: log.TransactionHash, + LogIndex: log.LogIndex, + } + + rewardSubmissions = append(rewardSubmissions, rewardSubmission) + } + } + + return rewardSubmissions, nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) GetStateTransitions() (types.StateTransitions[[]*OperatorDirectedRewardSubmission], []uint64) { + stateChanges := make(types.StateTransitions[[]*OperatorDirectedRewardSubmission]) + + stateChanges[0] = func(log *storage.TransactionLog) ([]*OperatorDirectedRewardSubmission, error) { + rewardSubmissions, err := odrs.handleOperatorDirectedRewardSubmissionCreatedEvent(log) + if err != nil { + return nil, err + } + + for _, rewardSubmission := range rewardSubmissions { + slotId, err := odrs.NewSlotID(log.BlockNumber, rewardSubmission.TransactionHash, rewardSubmission.LogIndex, rewardSubmission.RewardHash, rewardSubmission.StrategyIndex, rewardSubmission.OperatorIndex) + if err != nil { + odrs.logger.Sugar().Errorw("Failed to create slot ID", + zap.Uint64("blockNumber", log.BlockNumber), + zap.String("transactionHash", log.TransactionHash), + zap.Uint64("logIndex", log.LogIndex), + zap.String("rewardHash", rewardSubmission.RewardHash), + zap.Uint64("strategyIndex", rewardSubmission.StrategyIndex), + zap.Uint64("operatorIndex", rewardSubmission.OperatorIndex), + zap.Error(err), + ) + return nil, err + } + + _, ok := odrs.stateAccumulator[log.BlockNumber][slotId] + if ok { + err := fmt.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 + } + + 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) + odrs.committedState[blockNumber] = make([]*OperatorDirectedRewardSubmission, 0) + return nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) CleanupProcessedStateForBlock(blockNumber uint64) error { + delete(odrs.stateAccumulator, blockNumber) + delete(odrs.committedState, 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) ([]*OperatorDirectedRewardSubmission, error) { + accumulatedState, ok := odrs.stateAccumulator[blockNumber] + if !ok { + err := fmt.Errorf("No accumulated state found for block %d", blockNumber) + odrs.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + + recordsToInsert := make([]*OperatorDirectedRewardSubmission, 0) + for _, submission := range accumulatedState { + recordsToInsert = append(recordsToInsert, submission) + } + 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) + if err != nil { + return err + } + + if len(recordsToInsert) > 0 { + for _, record := range recordsToInsert { + 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 + } + } + } + odrs.committedState[blockNumber] = recordsToInsert + 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) ([]byte, error) { + inserts, err := odrs.prepareState(blockNumber) + if err != nil { + return nil, err + } + + inputs, err := odrs.sortValuesForMerkleTree(inserts) + if err != nil { + return nil, err + } + + if len(inputs) == 0 { + return nil, nil + } + + 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 nil, err + } + return fullTree.Root(), nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) GetCommittedState(blockNumber uint64) ([]interface{}, error) { + records, ok := odrs.committedState[blockNumber] + if !ok { + err := fmt.Errorf("No committed state found for block %d", blockNumber) + odrs.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + return base.CastCommittedStateToInterface(records), nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) formatMerkleLeafValue( + blockNumber uint64, + rewardHash string, + strategy string, + multiplier string, + operator string, + amount string, +) (string, error) { + modelForks, err := odrs.globalConfig.GetModelForks() + if err != nil { + return "", err + } + + if odrs.globalConfig.ChainIsOneOf(config.Chain_Holesky, config.Chain_Preprod) && blockNumber < modelForks[config.ModelFork_Austin] { + // This format was used on preprod and testnet for rewards-v2 before launching to mainnet + return fmt.Sprintf("%s_%s_%s_%s_%s", rewardHash, strategy, multiplier, operator, amount), nil + } + // Following was fixed as part of the rewards-v2 audit feedback before launching on mainnet. + // + // Multiplier is a uint96 in the contracts, which translates to 24 hex characters + // Amount is a uint256 in the contracts, which translates to 64 hex characters + multiplierBig, success := new(big.Int).SetString(multiplier, 10) + if !success { + return "", fmt.Errorf("failed to parse multiplier to BigInt: %s", multiplier) + } + + amountBig, success := new(big.Int).SetString(amount, 10) + if !success { + return "", fmt.Errorf("failed to parse amount to BigInt: %s", amount) + } + + return fmt.Sprintf("%s_%s_%024x_%s_%064x", rewardHash, strategy, multiplierBig, operator, amountBig), nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) sortValuesForMerkleTree(submissions []*OperatorDirectedRewardSubmission) ([]*base.MerkleTreeInput, error) { + inputs := make([]*base.MerkleTreeInput, 0) + for _, submission := range submissions { + slotID, err := odrs.NewSlotID(submission.BlockNumber, submission.TransactionHash, submission.LogIndex, submission.RewardHash, submission.StrategyIndex, submission.OperatorIndex) + if err != nil { + odrs.logger.Sugar().Errorw("Failed to create slot ID", + zap.Uint64("blockNumber", submission.BlockNumber), + zap.String("transactionHash", submission.TransactionHash), + zap.Uint64("logIndex", submission.LogIndex), + zap.String("rewardHash", submission.RewardHash), + zap.Uint64("strategyIndex", submission.StrategyIndex), + zap.Uint64("operatorIndex", submission.OperatorIndex), + zap.Error(err), + ) + return nil, err + } + + value, err := odrs.formatMerkleLeafValue(submission.BlockNumber, submission.RewardHash, submission.Strategy, submission.Multiplier, submission.Operator, submission.Amount) + if err != nil { + odrs.Logger.Sugar().Errorw("Failed to format merkle leaf value", + zap.Error(err), + zap.Uint64("blockNumber", submission.BlockNumber), + zap.String("rewardHash", submission.RewardHash), + zap.String("strategy", submission.Strategy), + zap.String("multiplier", submission.Multiplier), + zap.String("operator", submission.Operator), + zap.String("amount", submission.Amount), + ) + return nil, err + } + 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, nil +} + +func (odrs *OperatorDirectedRewardSubmissionsModel) DeleteState(startBlockNumber uint64, endBlockNumber uint64) error { + return odrs.BaseEigenState.DeleteState("operator_directed_reward_submissions", startBlockNumber, endBlockNumber, odrs.DB) +} diff --git a/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go new file mode 100644 index 00000000..43e777ad --- /dev/null +++ b/pkg/eigenState/operatorDirectedRewardSubmissions/operatorDirectedRewardSubmissions_test.go @@ -0,0 +1,177 @@ +package operatorDirectedRewardSubmissions + +import ( + "fmt" + "math/big" + "os" + "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.Debug = os.Getenv(config.Debug) == "true" + 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, 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) { + dbName, 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": 30000000000000000000000}, {"operator": "0xF50Cba7a66b5E615587157e43286DaA7aF94009e", "amount": 40000000000000000000000}], "duration": 2419200, "startTimestamp": 1725494400, "strategiesAndMultipliers": [{"strategy": "0x5074dfd18e9498d9e006fb8d4f3fecdc9af90a2c", "multiplier": 1000000000000000000}, {"strategy": "0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc", "multiplier": 2000000000000000000}], "description": "test reward submission"}}`, + } + + 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"}, + {"0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc", "2000000000000000000"}, + } + + operatorRewards := []struct { + Operator string + Amount string + }{ + {"0x9401E5E6564DB35C0f86573a9828DF69Fc778aF1", "30000000000000000000000"}, + {"0xF50Cba7a66b5E615587157e43286DaA7aF94009e", "40000000000000000000000"}, + } + + typedChange := change.([]*OperatorDirectedRewardSubmission) + assert.Equal(t, len(strategiesAndMultipliers)*len(operatorRewards), len(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)) + 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[submission.StrategyIndex].Strategy), strings.ToLower(submission.Strategy)) + assert.Equal(t, strategiesAndMultipliers[submission.StrategyIndex].Multiplier, submission.Multiplier) + + assert.Equal(t, strings.ToLower(operatorRewards[submission.OperatorIndex].Operator), strings.ToLower(submission.Operator)) + assert.Equal(t, operatorRewards[submission.OperatorIndex].Amount, submission.Amount) + + assert.Equal(t, "test reward submission", submission.Description) + } + + 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.Cleanup(func() { + postgres.TeardownTestDatabase(dbName, cfg, grm, l) + }) +} diff --git a/pkg/eigenState/operatorPISplits/operatorPISplits.go b/pkg/eigenState/operatorPISplits/operatorPISplits.go new file mode 100644 index 00000000..aa3911f6 --- /dev/null +++ b/pkg/eigenState/operatorPISplits/operatorPISplits.go @@ -0,0 +1,321 @@ +package operatorPISplits + +import ( + "encoding/json" + "fmt" + "slices" + "sort" + "strings" + "time" + + "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" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type OperatorPISplit struct { + Operator string + ActivatedAt *time.Time + OldOperatorPISplitBips uint64 + NewOperatorPISplitBips 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 + committedState map[uint64][]*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), + committedState: make(map[uint64][]*OperatorPISplit), + } + + esm.RegisterState(model, 9) + return model, nil +} + +func (ops *OperatorPISplitModel) GetModelName() string { + return "OperatorPISplitModel" +} + +type operatorPISplitOutputData struct { + ActivatedAt uint64 `json:"activatedAt"` + OldOperatorPISplitBips uint64 `json:"oldOperatorPISplitBips"` + NewOperatorPISplitBips uint64 `json:"newOperatorPISplitBips"` +} + +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, + OldOperatorPISplitBips: outputData.OldOperatorPISplitBips, + NewOperatorPISplitBips: outputData.NewOperatorPISplitBips, + 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 { + err := fmt.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 + } + + 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) + ops.committedState[blockNumber] = make([]*OperatorPISplit, 0) + return nil +} + +func (ops *OperatorPISplitModel) CleanupProcessedStateForBlock(blockNumber uint64) error { + delete(ops.stateAccumulator, blockNumber) + delete(ops.committedState, 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 := fmt.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 + } + } + } + ops.committedState[blockNumber] = recordsToInsert + 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) ([]byte, error) { + inserts, err := ops.prepareState(blockNumber) + if err != nil { + return nil, err + } + + inputs, err := ops.sortValuesForMerkleTree(inserts) + if err != nil { + return nil, err + } + + if len(inputs) == 0 { + return nil, nil + } + + 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 nil, err + } + return fullTree.Root(), nil +} + +func (ops *OperatorPISplitModel) GetCommittedState(blockNumber uint64) ([]interface{}, error) { + records, ok := ops.committedState[blockNumber] + if !ok { + err := fmt.Errorf("No committed state found for block %d", blockNumber) + ops.logger.Sugar().Errorw(err.Error(), zap.Error(err), zap.Uint64("blockNumber", blockNumber)) + return nil, err + } + return base.CastCommittedStateToInterface(records), nil +} + +func (ops *OperatorPISplitModel) formatMerkleLeafValue( + blockNumber uint64, + operator string, + activatedAt *time.Time, + oldOperatorPISplitBips uint64, + newOperatorPISplitBips uint64, +) (string, error) { + modelForks, err := ops.globalConfig.GetModelForks() + if err != nil { + return "", err + } + if ops.globalConfig.ChainIsOneOf(config.Chain_Holesky, config.Chain_Preprod) && blockNumber < modelForks[config.ModelFork_Austin] { + // This format was used on preprod and testnet for rewards-v2 before launching to mainnet + return fmt.Sprintf("%s_%d_%d_%d", operator, activatedAt.Unix(), oldOperatorPISplitBips, newOperatorPISplitBips), nil + } + + return fmt.Sprintf("%s_%016x_%016x_%016x", operator, activatedAt.Unix(), oldOperatorPISplitBips, newOperatorPISplitBips), nil +} + +func (ops *OperatorPISplitModel) sortValuesForMerkleTree(splits []*OperatorPISplit) ([]*base.MerkleTreeInput, error) { + inputs := make([]*base.MerkleTreeInput, 0) + for _, split := range splits { + slotID := base.NewSlotID(split.TransactionHash, split.LogIndex) + value, err := ops.formatMerkleLeafValue(split.BlockNumber, split.Operator, split.ActivatedAt, split.OldOperatorPISplitBips, split.NewOperatorPISplitBips) + if err != nil { + ops.logger.Sugar().Errorw("Failed to format merkle leaf value", + zap.Error(err), + zap.Uint64("blockNumber", split.BlockNumber), + zap.String("operator", split.Operator), + zap.Time("activatedAt", *split.ActivatedAt), + zap.Uint64("oldOperatorPISplitBips", split.OldOperatorPISplitBips), + zap.Uint64("newOperatorPISplitBips", split.NewOperatorPISplitBips), + ) + return nil, err + } + 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, nil +} + +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..4822bf43 --- /dev/null +++ b/pkg/eigenState/operatorPISplits/operatorPISplits_test.go @@ -0,0 +1,144 @@ +package operatorPISplits + +import ( + "fmt" + "math/big" + "os" + "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.Debug = os.Getenv(config.Debug) == "true" + 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, 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) { + dbName, 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": "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": 1733341104, "newOperatorPISplitBips": 6545, "oldOperatorPISplitBips": 1000}`, + } + + 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("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) + + 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) + }) +} diff --git a/pkg/pipeline/pipeline.go b/pkg/pipeline/pipeline.go index 4f223444..f15d04dd 100644 --- a/pkg/pipeline/pipeline.go +++ b/pkg/pipeline/pipeline.go @@ -219,6 +219,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/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go new file mode 100644 index 00000000..72eeb23f --- /dev/null +++ b/pkg/postgres/migrations/202411151931_operatorDirectedRewardSubmissions/up.go @@ -0,0 +1,43 @@ +package _202411151931_operatorDirectedRewardSubmissions + +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 { + 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), + 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 { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202411151931_operatorDirectedRewardSubmissions" +} diff --git a/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go new file mode 100644 index 00000000..34f2b084 --- /dev/null +++ b/pkg/postgres/migrations/202411191550_operatorAVSSplits/up.go @@ -0,0 +1,36 @@ +package _202411191550_operatorAVSSplits + +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 { + 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), + 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 { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202411191550_operatorAVSSplits" +} diff --git a/pkg/postgres/migrations/202411191708_operatorPISplits/up.go b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go new file mode 100644 index 00000000..4bae75ce --- /dev/null +++ b/pkg/postgres/migrations/202411191708_operatorPISplits/up.go @@ -0,0 +1,35 @@ +package _202411191708_operatorPISplits + +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 { + 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), + 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 { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202411191708_operatorPISplits" +} 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/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/202501061029_addDescription/up.go b/pkg/postgres/migrations/202501061029_addDescription/up.go new file mode 100644 index 00000000..006c11e9 --- /dev/null +++ b/pkg/postgres/migrations/202501061029_addDescription/up.go @@ -0,0 +1,25 @@ +package _202501061029_addDescription + +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 { + query := ` + ALTER TABLE operator_directed_reward_submissions ADD COLUMN description character varying; + ` + if err := grm.Exec(query).Error; err != nil { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202501061029_addDescription" +} diff --git a/pkg/postgres/migrations/202501061422_defaultOperatorSplits/up.go b/pkg/postgres/migrations/202501061422_defaultOperatorSplits/up.go new file mode 100644 index 00000000..d24f903b --- /dev/null +++ b/pkg/postgres/migrations/202501061422_defaultOperatorSplits/up.go @@ -0,0 +1,33 @@ +package _202501061422_defaultOperatorSplits + +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 { + query := ` + create table if not exists default_operator_splits ( + old_default_operator_split_bips integer not null, + new_default_operator_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), + CONSTRAINT default_operator_splits_block_number_fkey FOREIGN KEY (block_number) REFERENCES blocks(number) ON DELETE CASCADE + ); + ` + if err := grm.Exec(query).Error; err != nil { + return err + } + return nil +} + +func (m *Migration) GetName() string { + return "202501061422_defaultOperatorSplits" +} diff --git a/pkg/postgres/migrations/202501071401_defaultOperatorSplitSnapshots/up.go b/pkg/postgres/migrations/202501071401_defaultOperatorSplitSnapshots/up.go new file mode 100644 index 00000000..e9c9ab74 --- /dev/null +++ b/pkg/postgres/migrations/202501071401_defaultOperatorSplitSnapshots/up.go @@ -0,0 +1,30 @@ +package _202501071401_defaultOperatorSplitSnapshots + +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 default_operator_split_snapshots ( + 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 "202501071401_defaultOperatorSplitSnapshots" +} diff --git a/pkg/postgres/migrations/migrator.go b/pkg/postgres/migrations/migrator.go index 5c92558c..8cca0c9a 100644 --- a/pkg/postgres/migrations/migrator.go +++ b/pkg/postgres/migrations/migrator.go @@ -3,6 +3,8 @@ package migrations import ( "database/sql" "fmt" + "time" + "github.com/Layr-Labs/sidecar/internal/config" _202409061249_bootstrapDb "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202409061249_bootstrapDb" _202409061250_eigenlayerStateTables "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202409061250_eigenlayerStateTables" @@ -33,14 +35,22 @@ 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" + _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" + _202412091100_fixOperatorPiSplitsFields "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202412091100_fixOperatorPiSplitsFields" + _202501061029_addDescription "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202501061029_addDescription" + _202501061422_defaultOperatorSplits "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202501061422_defaultOperatorSplits" + _202501071401_defaultOperatorSplitSnapshots "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202501071401_defaultOperatorSplitSnapshots" _202501151039_rewardsClaimed "github.com/Layr-Labs/sidecar/pkg/postgres/migrations/202501151039_rewardsClaimed" "go.uber.org/zap" "gorm.io/gorm" - "time" ) type Migration interface { @@ -110,11 +120,20 @@ func (m *Migrator) MigrateAll() error { &_202411120947_disabledDistributionRoots.Migration{}, &_202411130953_addHashColumns.Migration{}, &_202411131200_eigenStateModelConstraints.Migration{}, + &_202411151931_operatorDirectedRewardSubmissions.Migration{}, + &_202411191550_operatorAVSSplits.Migration{}, + &_202411191708_operatorPISplits.Migration{}, &_202411191947_cleanupUnusedTables.Migration{}, &_202412021311_stakerOperatorTables.Migration{}, &_202412061553_addBlockNumberIndexes.Migration{}, &_202412061626_operatorRestakedStrategiesConstraint.Migration{}, &_202501151039_rewardsClaimed.Migration{}, + &_202411221331_operatorAVSSplitSnapshots.Migration{}, + &_202411221331_operatorPISplitSnapshots.Migration{}, + &_202412091100_fixOperatorPiSplitsFields.Migration{}, + &_202501061029_addDescription.Migration{}, + &_202501061422_defaultOperatorSplits.Migration{}, + &_202501071401_defaultOperatorSplitSnapshots.Migration{}, } for _, migration := range migrations { @@ -158,6 +177,7 @@ func (m *Migrator) Migrate(migration Migration) error { m.Logger.Sugar().Debugf("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..2fe6d76f --- /dev/null +++ b/pkg/rewards/10_goldAvsODRewardAmounts.go @@ -0,0 +1,97 @@ +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_registered_snapshot_decimal, + ap.avs AS avs, + ap.operator AS operator, + ap.strategy, + ap.multiplier, + ap.reward_submission_date + FROM {{.activeODRewardsTable}} ap + WHERE + ap.num_registered_snapshots = 0 +), + +-- 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_registered_snapshot_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 { + 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] + + 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]interface{}{ + "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 53% rename from pkg/rewards/7_goldStaging.go rename to pkg/rewards/11_goldStaging.go index ca6ca04b..4c92afdf 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,38 @@ 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 + 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}} +), +{{ end }} combined_rewards AS ( SELECT * FROM operator_rewards UNION ALL @@ -65,6 +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 ( @@ -85,22 +126,33 @@ 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], + 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], + "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], + "enableRewardsV2": isRewardsV2Enabled, }) if err != nil { rc.logger.Sugar().Errorw("Failed to render query template", "error", err) @@ -127,15 +179,16 @@ 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{ - "goldStagingTable": allTableNames[rewardsUtils.Table_7_GoldStaging], + 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) diff --git a/pkg/rewards/8_goldFinal.go b/pkg/rewards/12_goldFinal.go similarity index 79% rename from pkg/rewards/8_goldFinal.go rename to pkg/rewards/12_goldFinal.go index 48b243d2..f993b7b2 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]interface{}{ + "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/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 e9f1bf53..e1cb0804 100644 --- a/pkg/rewards/2_goldStakerRewardAmounts.go +++ b/pkg/rewards/2_goldStakerRewardAmounts.go @@ -2,6 +2,7 @@ package rewards import ( "database/sql" + "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" "go.uber.org/zap" @@ -108,26 +109,38 @@ staker_operator_total_tokens AS ( END as total_staker_operator_payout FROM staker_proportion ), --- Calculate the token breakdown for each (staker, operator) pair +-- Calculate the token breakdown for each (staker, operator) pair with dynamic split logic +-- If no split is found, default to 1000 (10%) token_breakdowns AS ( - SELECT *, + SELECT sott.*, CASE - WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - cast(total_staker_operator_payout * 0.10 AS DECIMAL(38,0)) - WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - (total_staker_operator_payout * 0.10)::text::decimal(38,0) + WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN + cast(sott.total_staker_operator_payout * 0.10 AS DECIMAL(38,0)) + WHEN sott.snapshot < @nileHardforkDate AND sott.reward_submission_date < @nileHardforkDate THEN + (sott.total_staker_operator_payout * 0.10)::text::decimal(38,0) + WHEN sott.snapshot < @arnoHardforkDate AND sott.reward_submission_date < @arnoHardforkDate THEN + floor(sott.total_staker_operator_payout * 0.10) + WHEN sott.snapshot < @trinityHardforkDate AND sott.reward_submission_date < @trinityHardforkDate THEN + floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) ELSE - floor(total_staker_operator_payout * 0.10) + floor(sott.total_staker_operator_payout * COALESCE(oas.split, dos.split, 1000) / CAST(10000 AS DECIMAL)) END as operator_tokens, CASE - WHEN snapshot < @amazonHardforkDate AND reward_submission_date < @amazonHardforkDate THEN - total_staker_operator_payout - cast(total_staker_operator_payout * 0.10 as DECIMAL(38,0)) - WHEN snapshot < @nileHardforkDate AND reward_submission_date < @nileHardforkDate THEN - total_staker_operator_payout - ((total_staker_operator_payout * 0.10)::text::decimal(38,0)) + WHEN sott.snapshot < @amazonHardforkDate AND sott.reward_submission_date < @amazonHardforkDate THEN + sott.total_staker_operator_payout - cast(sott.total_staker_operator_payout * 0.10 as DECIMAL(38,0)) + WHEN sott.snapshot < @nileHardforkDate AND sott.reward_submission_date < @nileHardforkDate THEN + sott.total_staker_operator_payout - ((sott.total_staker_operator_payout * 0.10)::text::decimal(38,0)) + WHEN sott.snapshot < @arnoHardforkDate AND sott.reward_submission_date < @arnoHardforkDate THEN + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * 0.10) + WHEN sott.snapshot < @trinityHardforkDate AND sott.reward_submission_date < @trinityHardforkDate THEN + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) ELSE - total_staker_operator_payout - floor(total_staker_operator_payout * 0.10) + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(oas.split, dos.split, 1000) / CAST(10000 AS DECIMAL)) END as staker_tokens - FROM staker_operator_total_tokens + FROM staker_operator_total_tokens sott + LEFT JOIN operator_avs_split_snapshots oas + ON sott.operator = oas.operator AND sott.avs = oas.avs AND sott.snapshot = oas.snapshot + LEFT JOIN default_operator_split_snapshots dos ON (sott.snapshot = dos.snapshot) ) SELECT * from token_breakdowns ORDER BY reward_hash, snapshot, staker, operator @@ -140,11 +153,13 @@ func (rc *RewardsCalculator) GenerateGold2StakerRewardAmountsTable(snapshotDate rc.logger.Sugar().Infow("Generating staker reward amounts", zap.String("cutoffDate", snapshotDate), zap.String("destTableName", destTableName), - zap.String("amazonHardforkDate", forks[config.Fork_Amazon]), - zap.String("nileHardforkDate", forks[config.Fork_Nile]), + zap.String("amazonHardforkDate", forks[config.RewardsFork_Amazon]), + zap.String("nileHardforkDate", forks[config.RewardsFork_Nile]), + zap.String("arnoHardforkDate", forks[config.RewardsFork_Arno]), + zap.String("trinityHardforkDate", forks[config.RewardsFork_Trinity]), ) - 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], }) @@ -154,8 +169,10 @@ 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("amazonHardforkDate", forks[config.RewardsFork_Amazon]), + sql.Named("nileHardforkDate", forks[config.RewardsFork_Nile]), + sql.Named("arnoHardforkDate", forks[config.RewardsFork_Arno]), + sql.Named("trinityHardforkDate", forks[config.RewardsFork_Trinity]), ) if res.Error != nil { rc.logger.Sugar().Errorw("Failed to create gold_staker_reward_amounts", "error", res.Error) 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 9e7d87bc..c72bf8a8 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,30 @@ staker_operator_total_tokens AS ( FLOOR(staker_proportion * tokens_per_day_decimal) as total_staker_operator_payout FROM staker_proportion ), --- Calculate the token breakdown for each (staker, operator) pair +-- Calculate the token breakdown for each (staker, operator) pair with dynamic split logic +-- If no split is found, default to 1000 (10%) token_breakdowns AS ( - SELECT *, - floor(total_staker_operator_payout * 0.10) as operator_tokens, - total_staker_operator_payout - floor(total_staker_operator_payout * 0.10) as staker_tokens - FROM staker_operator_total_tokens + SELECT sott.*, + CASE + WHEN sott.snapshot < @arnoHardforkDate AND sott.reward_submission_date < @arnoHardforkDate THEN + floor(sott.total_staker_operator_payout * 0.10) + WHEN sott.snapshot < @trinityHardforkDate AND sott.reward_submission_date < @trinityHardforkDate THEN + floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) + ELSE + floor(sott.total_staker_operator_payout * COALESCE(ops.split, dos.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) + WHEN sott.snapshot < @trinityHardforkDate AND sott.reward_submission_date < @trinityHardforkDate THEN + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, 1000) / CAST(10000 AS DECIMAL)) + ELSE + sott.total_staker_operator_payout - floor(sott.total_staker_operator_payout * COALESCE(ops.split, dos.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 + LEFT JOIN default_operator_split_snapshots dos ON (sott.snapshot = dos.snapshot) ) SELECT * from token_breakdowns ORDER BY reward_hash, snapshot, staker, operator @@ -125,9 +144,11 @@ 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.RewardsFork_Arno]), + zap.String("trinityHardforkDate", forks[config.RewardsFork_Trinity]), ) - 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], }) @@ -137,8 +158,10 @@ func (rc *RewardsCalculator) GenerateGold5RfaeStakersTable(snapshotDate string, } res := rc.grm.Exec(query, - sql.Named("panamaForkDate", forks[config.Fork_Panama]), + sql.Named("panamaForkDate", forks[config.RewardsFork_Panama]), sql.Named("network", rc.globalConfig.Chain.String()), + sql.Named("arnoHardforkDate", forks[config.RewardsFork_Arno]), + sql.Named("trinityHardforkDate", forks[config.RewardsFork_Trinity]), ) if res.Error != nil { rc.logger.Sugar().Errorw("Failed to generate gold_rfae_stakers", "error", res.Error) 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 new file mode 100644 index 00000000..a52dc019 --- /dev/null +++ b/pkg/rewards/7_goldActiveODRewards.go @@ -0,0 +1,214 @@ +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 + *, + 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, + amount, + token, + multiplier, + strategy, + reward_hash, + duration, + 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 underestimating total tokens per day + FLOOR(ap.amount) AS amount_decimal, + ap.multiplier, + ap.strategy, + ap.reward_hash, + ap.duration, + 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.amount, + ap.multiplier, + ap.strategy, + ap.reward_hash, + ap.duration, + 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 cleaned active rewards +active_rewards_cleaned AS ( + SELECT + avs, + operator, + CAST(day AS DATE) AS snapshot, + token, + 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 10: 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 11: 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 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 +) + +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 { + 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] + + 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]interface{}{ + "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..0c6919a2 --- /dev/null +++ b/pkg/rewards/8_goldOperatorODRewardAmounts.go @@ -0,0 +1,115 @@ +package rewards + +import ( + "database/sql" + + "github.com/Layr-Labs/sidecar/internal/config" + "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_registered_snapshot_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.*, + CASE + WHEN dop.snapshot < @trinityHardforkDate AND dop.reward_submission_date < @trinityHardforkDate THEN + COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL) + ELSE + COALESCE(oas.split, dos.split, 1000) / CAST(10000 AS DECIMAL) + END AS split_pct, + CASE + WHEN dop.snapshot < @trinityHardforkDate AND dop.reward_submission_date < @trinityHardforkDate THEN + FLOOR(dop.tokens_per_registered_snapshot_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) + ELSE + FLOOR(dop.tokens_per_registered_snapshot_decimal * COALESCE(oas.split, dos.split, 1000) / CAST(10000 AS DECIMAL)) + END 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 + LEFT JOIN default_operator_split_snapshots dos ON (dop.snapshot = dos.snapshot) +) + +-- Step 4: Output the final table with operator splits +SELECT * FROM operator_splits +` + +func (rc *RewardsCalculator) GenerateGold8OperatorODRewardAmountsTable(snapshotDate string, forks config.ForkMap) 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] + + rc.logger.Sugar().Infow("Generating Operator OD reward amounts", + zap.String("cutoffDate", snapshotDate), + zap.String("destTableName", destTableName), + zap.String("trinityHardforkDate", forks[config.RewardsFork_Trinity]), + ) + + query, err := rewardsUtils.RenderQueryTemplate(_8_goldOperatorODRewardAmountsQuery, map[string]interface{}{ + "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, sql.Named("trinityHardforkDate", forks[config.RewardsFork_Trinity])) + 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..a5fc2ab4 --- /dev/null +++ b/pkg/rewards/9_goldStakerODRewardAmounts.go @@ -0,0 +1,160 @@ +package rewards + +import ( + "database/sql" + + "github.com/Layr-Labs/sidecar/internal/config" + "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_registered_snapshot_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.*, + CASE + WHEN rso.snapshot < @trinityHardforkDate AND rso.reward_submission_date < @trinityHardforkDate THEN + rso.tokens_per_registered_snapshot_decimal - FLOOR(rso.tokens_per_registered_snapshot_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) + ELSE + rso.tokens_per_registered_snapshot_decimal - FLOOR(rso.tokens_per_registered_snapshot_decimal * COALESCE(oas.split, dos.split, 1000) / CAST(10000 AS DECIMAL)) + END 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 + LEFT JOIN default_operator_split_snapshots dos ON (rso.snapshot = dos.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, forks config.ForkMap) 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] + + rc.logger.Sugar().Infow("Generating Staker OD reward amounts", + zap.String("cutoffDate", snapshotDate), + zap.String("destTableName", destTableName), + zap.String("trinityHardforkDate", forks[config.RewardsFork_Trinity]), + ) + + query, err := rewardsUtils.RenderQueryTemplate(_9_goldStakerODRewardAmountsQuery, map[string]interface{}{ + "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, sql.Named("trinityHardforkDate", forks[config.RewardsFork_Trinity])) + 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/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/defaultOperatorSplitSnapshots.go b/pkg/rewards/defaultOperatorSplitSnapshots.go new file mode 100644 index 00000000..05b533c7 --- /dev/null +++ b/pkg/rewards/defaultOperatorSplitSnapshots.go @@ -0,0 +1,89 @@ +package rewards + +import "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + +const defaultOperatorSplitSnapshotQuery = ` +WITH default_operator_splits_with_block_info as ( + select + dos.new_default_operator_split_bips as split, + dos.block_number, + dos.log_index, + b.block_time::timestamp(6) as block_time + from default_operator_splits as dos + join blocks as b on (b.number = dos.block_number) + where b.block_time < TIMESTAMP '{{.cutoffDate}}' +), +-- Rank the records for each combination of (block date) by block time and log index +ranked_default_operator_split_records as ( + SELECT + *, + ROW_NUMBER() OVER (PARTITION BY cast(block_time AS DATE) ORDER BY block_time DESC, log_index DESC) AS rn + FROM default_operator_splits_with_block_info +), +-- Get the latest record for each day & round up to the snapshot day +snapshotted_records as ( + SELECT + split, + block_time, + date_trunc('day', block_time) + INTERVAL '1' day AS snapshot_time + from ranked_default_operator_split_records + where rn = 1 +), +-- Get the range for each operator, avs pairing +default_operator_split_windows as ( + SELECT + 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 (ORDER BY snapshot_time) is null THEN date_trunc('day', TIMESTAMP '{{.cutoffDate}}') + ELSE LEAD(snapshot_time) OVER (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 default_operator_split_windows + WHERE start_time < end_time +), +-- Generate a snapshot for each day in the range +final_results as ( + SELECT + 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) GenerateAndInsertDefaultOperatorSplitSnapshots(snapshotDate string) error { + tableName := "default_operator_split_snapshots" + + query, err := rewardsUtils.RenderQueryTemplate(defaultOperatorSplitSnapshotQuery, map[string]interface{}{ + "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 default_operator_split_snapshots", "error", err) + return err + } + return nil +} + +func (r *RewardsCalculator) ListDefaultOperatorSplitSnapshots() ([]*DefaultOperatorSplitSnapshots, error) { + var snapshots []*DefaultOperatorSplitSnapshots + res := r.grm.Model(&DefaultOperatorSplitSnapshots{}).Find(&snapshots) + if res.Error != nil { + r.logger.Sugar().Errorw("Failed to list default operator split snapshots", "error", res.Error) + return nil, res.Error + } + return snapshots, nil +} diff --git a/pkg/rewards/defaultOperatorSplitSnapshots_test.go b/pkg/rewards/defaultOperatorSplitSnapshots_test.go new file mode 100644 index 00000000..c8dc7508 --- /dev/null +++ b/pkg/rewards/defaultOperatorSplitSnapshots_test.go @@ -0,0 +1,179 @@ +package rewards + +import ( + "fmt" + "testing" + + "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" +) + +func setupDefaultOperatorSplitWindows() ( + 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 teardownDefaultOperatorSplitWindows(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 hydrateDefaultOperatorSplits(grm *gorm.DB, l *zap.Logger) error { + query := ` + INSERT INTO default_operator_splits (old_default_operator_split_bips, new_default_operator_split_bips, block_number, transaction_hash, log_index) + VALUES (1000, 500, '1477020', '0xccc83cdfa365bacff5e4099b9931bccaec1c0b0cf37cd324c92c27b5cb5387d1', 545) + ` + + res := grm.Exec(query) + if res.Error != nil { + l.Sugar().Errorw("Failed to execute sql", "error", zap.Error(res.Error)) + return res.Error + } + return nil +} + +func Test_DefaultOperatorSplitSnapshots(t *testing.T) { + if !rewardsTestsEnabled() { + t.Skipf("Skipping %s", t.Name()) + return + } + + // projectRoot := getProjectRootPath() + dbFileName, cfg, grm, l, err := setupDefaultOperatorSplitWindows() + 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 default operator splits") + err = hydrateDefaultOperatorSplits(grm, l) + if err != nil { + t.Fatal(err) + } + + query := `select count(*) from default_operator_splits` + var count int + res := grm.Raw(query).Scan(&count) + + assert.Nil(t, res.Error) + + assert.Equal(t, 1, count) + }) + + t.Run("Should calculate correct default operator split windows", func(t *testing.T) { + sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg) + rewards, _ := NewRewardsCalculator(cfg, grm, nil, sog, l) + + t.Log("Generating snapshots") + err := rewards.GenerateAndInsertDefaultOperatorSplitSnapshots(snapshotDate) + assert.Nil(t, err) + + windows, err := rewards.ListDefaultOperatorSplitSnapshots() + assert.Nil(t, err) + + t.Logf("Found %d windows", len(windows)) + + assert.Equal(t, 218, len(windows)) + }) + t.Cleanup(func() { + teardownDefaultOperatorSplitWindows(dbFileName, cfg, grm, l) + }) +} + +func Test_NoDefaultOperatorSplitSnapshots(t *testing.T) { + if !rewardsTestsEnabled() { + t.Skipf("Skipping %s", t.Name()) + return + } + + // projectRoot := getProjectRootPath() + dbFileName, cfg, grm, l, err := setupDefaultOperatorSplitWindows() + 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) + + // No hydration of default operator splits + query := `select count(*) from default_operator_splits` + var count int + res := grm.Raw(query).Scan(&count) + + assert.Nil(t, res.Error) + + assert.Equal(t, 0, count) + }) + + t.Run("Should calculate correct default operator split windows", func(t *testing.T) { + sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg) + rewards, _ := NewRewardsCalculator(cfg, grm, nil, sog, l) + + t.Log("Generating snapshots") + err := rewards.GenerateAndInsertDefaultOperatorSplitSnapshots(snapshotDate) + assert.Nil(t, err) + + windows, err := rewards.ListDefaultOperatorSplitSnapshots() + assert.Nil(t, err) + + t.Logf("Found %d windows", len(windows)) + + assert.Equal(t, 0, len(windows)) + }) + t.Cleanup(func() { + teardownDefaultOperatorSplitWindows(dbFileName, cfg, grm, l) + }) +} diff --git a/pkg/rewards/operatorAvsRegistrationSnaphots.go b/pkg/rewards/operatorAvsRegistrationSnapshots.go similarity index 99% rename from pkg/rewards/operatorAvsRegistrationSnaphots.go rename to pkg/rewards/operatorAvsRegistrationSnapshots.go index 9ea856e3..fc16a4d7 100644 --- a/pkg/rewards/operatorAvsRegistrationSnaphots.go +++ b/pkg/rewards/operatorAvsRegistrationSnapshots.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/operatorAvsRegistrationSnaphots_test.go b/pkg/rewards/operatorAvsRegistrationSnapshots_test.go similarity index 100% rename from pkg/rewards/operatorAvsRegistrationSnaphots_test.go rename to pkg/rewards/operatorAvsRegistrationSnapshots_test.go diff --git a/pkg/rewards/operatorAvsSplitSnapshots.go b/pkg/rewards/operatorAvsSplitSnapshots.go new file mode 100644 index 00000000..a8df9e2d --- /dev/null +++ b/pkg/rewards/operatorAvsSplitSnapshots.go @@ -0,0 +1,95 @@ +package rewards + +import "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + +const operatorAvsSplitSnapshotQuery = ` +WITH operator_avs_splits_with_block_info as ( + select + oas.operator, + oas.avs, + oas.activated_at::timestamp(6) as activated_at, + oas.new_operator_avs_split_bips as split, + oas.block_number, + oas.log_index, + b.block_time::timestamp(6) as block_time + from operator_avs_splits as oas + join blocks as b on (b.number = oas.block_number) + where activated_at < TIMESTAMP '{{.cutoffDate}}' +), +-- Rank the records for each combination of (operator, avs, activation date) by activation time, block time and log index +ranked_operator_avs_split_records as ( + SELECT + *, + ROW_NUMBER() OVER (PARTITION BY operator, avs, cast(activated_at AS DATE) ORDER BY activated_at DESC, block_time DESC, log_index DESC) AS rn + FROM operator_avs_splits_with_block_info +), +-- Get the latest record for each day & round up to the snapshot day +snapshotted_records as ( + SELECT + operator, + avs, + split, + block_time, + date_trunc('day', activated_at) + INTERVAL '1' day AS snapshot_time + from ranked_operator_avs_split_records + where rn = 1 +), +-- Get the range for each operator, avs pairing +operator_avs_split_windows as ( + SELECT + operator, avs, split, snapshot_time as start_time, + CASE + -- If the range does not have the end, use the current timestamp truncated to 0 UTC + WHEN LEAD(snapshot_time) OVER (PARTITION BY operator, avs ORDER BY snapshot_time) is null THEN date_trunc('day', TIMESTAMP '{{.cutoffDate}}') + ELSE LEAD(snapshot_time) OVER (PARTITION BY operator, avs ORDER BY snapshot_time) + END AS end_time + FROM snapshotted_records +), +-- Clean up any records where start_time >= end_time +cleaned_records as ( + SELECT * FROM operator_avs_split_windows + WHERE start_time < end_time +), +-- Generate a snapshot for each day in the range +final_results as ( + SELECT + operator, + avs, + split, + d AS snapshot + FROM + cleaned_records + CROSS JOIN + generate_series(DATE(start_time), DATE(end_time) - interval '1' day, interval '1' day) AS d +) +select * from final_results +` + +func (r *RewardsCalculator) GenerateAndInsertOperatorAvsSplitSnapshots(snapshotDate string) error { + tableName := "operator_avs_split_snapshots" + + query, err := rewardsUtils.RenderQueryTemplate(operatorAvsSplitSnapshotQuery, map[string]interface{}{ + "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/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/operatorAvsStrategySnapshots.go b/pkg/rewards/operatorAvsStrategySnapshots.go index 92eb2725..428cfecb 100644 --- a/pkg/rewards/operatorAvsStrategySnapshots.go +++ b/pkg/rewards/operatorAvsStrategySnapshots.go @@ -147,7 +147,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 new file mode 100644 index 00000000..9cc7d896 --- /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]interface{}{ + "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 new file mode 100644 index 00000000..1ad86e2d --- /dev/null +++ b/pkg/rewards/operatorPISplitSnapshots.go @@ -0,0 +1,94 @@ +package rewards + +import "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + +const operatorPISplitSnapshotQuery = ` +WITH operator_pi_splits_with_block_info as ( + select + ops.operator, + ops.activated_at::timestamp(6) as activated_at, + ops.new_operator_pi_split_bips as split, + ops.block_number, + ops.log_index, + b.block_time::timestamp(6) as block_time + from operator_pi_splits as ops + join blocks as b on (b.number = ops.block_number) + where activated_at < TIMESTAMP '{{.cutoffDate}}' +), +-- Rank the records for each 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 + 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 := rewardsUtils.RenderQueryTemplate(operatorPISplitSnapshotQuery, map[string]interface{}{ + "cutoffDate": snapshotDate, + }) + if err != nil { + r.logger.Sugar().Errorw("Failed to render query template", "error", err) + return err + } + + err = r.generateAndInsertFromQuery(tableName, query, nil) + if err != nil { + r.logger.Sugar().Errorw("Failed to generate operator_pi_split_snapshots", "error", err) + return err + } + return nil +} + +func (r *RewardsCalculator) ListOperatorPISplitSnapshots() ([]*OperatorPISplitSnapshots, error) { + var snapshots []*OperatorPISplitSnapshots + res := r.grm.Model(&OperatorPISplitSnapshots{}).Find(&snapshots) + if res.Error != nil { + r.logger.Sugar().Errorw("Failed to list operator pi split snapshots", "error", res.Error) + return nil, res.Error + } + return snapshots, nil +} diff --git a/pkg/rewards/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/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/operatorShares_test.go b/pkg/rewards/operatorShares_test.go index c5c10252..03b74ce8 100644 --- a/pkg/rewards/operatorShares_test.go +++ b/pkg/rewards/operatorShares_test.go @@ -56,7 +56,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 87a0e195..9caf183b 100644 --- a/pkg/rewards/rewards.go +++ b/pkg/rewards/rewards.go @@ -4,23 +4,25 @@ import ( "database/sql" "errors" "fmt" + "time" + + "sync/atomic" + + "slices" + "strings" + "github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/distribution" + "github.com/Layr-Labs/sidecar/internal/config" "github.com/Layr-Labs/sidecar/pkg/eigenState/types" "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" - "gorm.io/gorm/clause" - "slices" - "strconv" - "strings" - "sync/atomic" - "time" - - "github.com/Layr-Labs/sidecar/internal/config" "go.uber.org/zap" "gorm.io/gorm" + "gorm.io/gorm/clause" + "strconv" ) type RewardsCalculator struct { @@ -222,7 +224,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) @@ -236,7 +238,7 @@ func (rc *RewardsCalculator) GetMaxSnapshotDateForCutoffDate(cutoffDate string) func (rc *RewardsCalculator) BackfillAllStakerOperators() error { var generatedSnapshots []storage.GeneratedRewardsSnapshots - query := `select * from generated_rewards_snapshots order by snapshot_date asc` + query := `select * from generated_rewards_snapshots where status = 'complete' order by snapshot_date asc` res := rc.grm.Raw(query).Scan(&generatedSnapshots) if res.Error != nil { rc.logger.Sugar().Errorw("Failed to get generated snapshots", "error", res.Error) @@ -278,7 +280,7 @@ func (rc *RewardsCalculator) BackfillAllStakerOperators() error { func (rc *RewardsCalculator) GenerateStakerOperatorsTableForPastSnapshot(cutoffDate string) error { // find the first snapshot that is >= to the provided cutoff date var generatedSnapshot storage.GeneratedRewardsSnapshots - query := `select * from generated_rewards_snapshots where snapshot_date >= ? order by snapshot_date asc limit 1` + query := `select * from generated_rewards_snapshots where snapshot_date >= ? and status = 'complete' order by snapshot_date asc limit 1` res := rc.grm.Raw(query, cutoffDate).Scan(&generatedSnapshot) if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) { rc.logger.Sugar().Errorw("Failed to get generated snapshot", "error", res.Error) @@ -499,12 +501,12 @@ 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 } - res := rc.grm.Debug().Raw(query).Scan(&goldRows) + res := rc.grm.Raw(query).Scan(&goldRows) if res.Error != nil { return nil, res.Error } @@ -611,11 +613,37 @@ 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 + } + 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") + + if err = rc.GenerateAndInsertDefaultOperatorSplitSnapshots(snapshotDate); err != nil { + rc.logger.Sugar().Errorw("Failed to generate default operator split snapshots", "error", err) + return err + } + rc.logger.Sugar().Debugw("Generated default operator split snapshots") + return nil } func (rc *RewardsCalculator) generateGoldTables(snapshotDate string) error { - forks, err := rc.globalConfig.GetForkDates() + forks, err := rc.globalConfig.GetRewardsSqlForkDates() if err != nil { return err } @@ -649,12 +677,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, forks); err != nil { + rc.logger.Sugar().Errorw("Failed to generate operator od reward amounts", "error", err) + return err + } + + if err := rc.GenerateGold9StakerODRewardAmountsTable(snapshotDate, forks); 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 } @@ -692,7 +740,7 @@ func (rc *RewardsCalculator) FindClaimableDistributionRoot(rootIndex int64) (*ty order by sdr.root_index desc limit 1 ` - renderedQuery, err := rewardsUtils.RenderQueryTemplate(query, map[string]string{ + renderedQuery, err := rewardsUtils.RenderQueryTemplate(query, map[string]interface{}{ "rootIndex": strconv.Itoa(int(rootIndex)), }) if err != nil { @@ -727,7 +775,7 @@ func (rc *RewardsCalculator) GetGeneratedRewardsForSnapshotDate(snapshotDate str and grs.snapshot_date::timestamp(6) >= '{{.snapshotDate}}'::timestamp(6) order by grs.snapshot_date asc limit 1 - `, map[string]string{"snapshotDate": snapshotDate}) + `, map[string]interface{}{"snapshotDate": snapshotDate}) if err != nil { rc.logger.Sugar().Errorw("Failed to render query template", "error", err) diff --git a/pkg/rewards/rewardsV2_test.go b/pkg/rewards/rewardsV2_test.go new file mode 100644 index 00000000..a96f2b50 --- /dev/null +++ b/pkg/rewards/rewardsV2_test.go @@ -0,0 +1,235 @@ +package rewards + +import ( + "fmt" + "strings" + "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-14", + } + + 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.GetRewardsSqlForkDates() + 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 + // ------------------------------------------------------------------------ + 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) + 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, forks) + assert.Nil(t, err) + 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, forks) + assert.Nil(t, err) + 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) + 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") + 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)) + for i, row := range goldRows { + 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) + } + + t.Logf("Generating staker operators table") + err = rc.sog.GenerateStakerOperatorsTable(snapshotDate) + assert.Nil(t, err) + + 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 44d09838..8a3e4040 100644 --- a/pkg/rewards/rewards_test.go +++ b/pkg/rewards/rewards_test.go @@ -58,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) } @@ -80,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 @@ -115,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()) @@ -199,7 +243,7 @@ func Test_Rewards(t *testing.T) { testStart = time.Now() t.Log("Generated and inserted snapshots") - forks, err := cfg.GetForkDates() + forks, err := cfg.GetRewardsSqlForkDates() assert.Nil(t, err) fmt.Printf("Running gold_1_active_rewards\n") @@ -250,16 +294,59 @@ 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) + 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) + 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, forks) + assert.Nil(t, err) + 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, forks) + assert.Nil(t, err) + 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) + 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") + 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/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/7_stakerOperator.go b/pkg/rewards/stakerOperators/10_stakerOperator.go similarity index 74% rename from pkg/rewards/stakerOperators/7_stakerOperator.go rename to pkg/rewards/stakerOperators/10_stakerOperator.go index 27c98c67..a1ae45eb 100644 --- a/pkg/rewards/stakerOperators/7_stakerOperator.go +++ b/pkg/rewards/stakerOperators/10_stakerOperator.go @@ -50,30 +50,30 @@ type StakerOperator struct { Snapshot time.Time } -func (sog *StakerOperatorsGenerator) GenerateAndInsert7StakerOperator(cutoffDate string) error { - sog.logger.Sugar().Infow("Generating and inserting 7_stakerOperator", +func (sog *StakerOperatorsGenerator) GenerateAndInsert10StakerOperator(cutoffDate string) error { + sog.logger.Sugar().Infow("Generating and inserting 10_stakerOperator", zap.String("cutoffDate", cutoffDate), ) allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) - destTableName := rewardsUtils.Sot_7_StakerOperatorTable + destTableName := rewardsUtils.Sot_10_StakerOperatorTable - sog.logger.Sugar().Infow("Generating 7_stakerOperator", + sog.logger.Sugar().Infow("Generating 10_stakerOperator", zap.String("destTableName", destTableName), 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], + "stakerOperatorStaging": allTableNames[rewardsUtils.Sot_9_StakerOperatorStaging], }) if err != nil { - sog.logger.Sugar().Errorw("Failed to render 7_stakerOperator query", "error", err) + sog.logger.Sugar().Errorw("Failed to render 10_stakerOperator query", "error", err) return err } res := sog.db.Exec(query) if res.Error != nil { - sog.logger.Sugar().Errorw("Failed to generate 7_stakerOperator", + sog.logger.Sugar().Errorw("Failed to generate 10_stakerOperator", zap.String("cutoffDate", cutoffDate), zap.Error(res.Error), ) diff --git a/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go b/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go index 055a68f3..dd14d897 100644 --- a/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go +++ b/pkg/rewards/stakerOperators/1_stakerStrategyPayouts.go @@ -146,7 +146,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert1StakerStrategyPayouts(cut return err } - query, err := rewardsUtils.RenderQueryTemplate(_1_stakerStrategyPayoutsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_1_stakerStrategyPayoutsQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": rewardsTables[rewardsUtils.Table_1_ActiveRewards], "stakerRewardAmountsTable": rewardsTables[rewardsUtils.Table_2_StakerRewardAmounts], @@ -157,8 +157,8 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert1StakerStrategyPayouts(cut } res := sog.db.Exec(query, - sql.Named("amazonHardforkDate", forks[config.Fork_Amazon]), - sql.Named("nileHardforkDate", forks[config.Fork_Nile]), + sql.Named("amazonHardforkDate", forks[config.RewardsFork_Amazon]), + sql.Named("nileHardforkDate", forks[config.RewardsFork_Nile]), ) if res.Error != nil { diff --git a/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go b/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go index c4b0a4be..61a7e034 100644 --- a/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go +++ b/pkg/rewards/stakerOperators/2_operatorStrategyRewards.go @@ -120,7 +120,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert2OperatorStrategyRewards(c return err } - query, err := rewardsUtils.RenderQueryTemplate(_2_operatorStrategyRewardsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_2_operatorStrategyRewardsQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": rewardsTables[rewardsUtils.Table_1_ActiveRewards], "operatorRewardAmountsTable": rewardsTables[rewardsUtils.Table_3_OperatorRewardAmounts], diff --git a/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go b/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go index c4a33b2a..56759787 100644 --- a/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go +++ b/pkg/rewards/stakerOperators/3_rewardsForAllStrategyPayouts.go @@ -89,7 +89,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert3RewardsForAllStrategyPayo return err } - query, err := rewardsUtils.RenderQueryTemplate(_3_rewardsForAllStrategyPayoutsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_3_rewardsForAllStrategyPayoutsQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": rewardsTables[rewardsUtils.Table_1_ActiveRewards], }) diff --git a/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go b/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go index 01cd7ac2..fbf74ec9 100644 --- a/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go +++ b/pkg/rewards/stakerOperators/4_rfaeStakerStrategyPayouts.go @@ -132,7 +132,7 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert4RfaeStakerStrategyPayout( return err } - query, err := rewardsUtils.RenderQueryTemplate(_4_rfaeStakerStrategyPayoutsQuery, map[string]string{ + query, err := rewardsUtils.RenderQueryTemplate(_4_rfaeStakerStrategyPayoutsQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": rewardsTables[rewardsUtils.Table_1_ActiveRewards], "rfaeStakersTable": rewardsTables[rewardsUtils.Table_5_RfaeStakers], diff --git a/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go b/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go index 6b61cd49..a290bbcc 100644 --- a/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go +++ b/pkg/rewards/stakerOperators/5_rfaeOperatorStrategyPayout.go @@ -116,8 +116,8 @@ func (sog *StakerOperatorsGenerator) GenerateAndInsert5RfaeOperatorStrategyPayou sog.logger.Sugar().Errorw("Failed to find staker operator table names", "error", err) return err } - - query, err := rewardsUtils.RenderQueryTemplate(_5_rfaeOperatorStrategyPayoutsQuery, map[string]string{ + + query, err := rewardsUtils.RenderQueryTemplate(_5_rfaeOperatorStrategyPayoutsQuery, map[string]interface{}{ "destTableName": destTableName, "activeRewardsTable": rewardsTables[rewardsUtils.Table_1_ActiveRewards], "rfaeOperatorsTable": rewardsTables[rewardsUtils.Table_6_RfaeOperators], diff --git a/pkg/rewards/stakerOperators/6_operatorODStrategyPayouts.go b/pkg/rewards/stakerOperators/6_operatorODStrategyPayouts.go new file mode 100644 index 00000000..22283c96 --- /dev/null +++ b/pkg/rewards/stakerOperators/6_operatorODStrategyPayouts.go @@ -0,0 +1,88 @@ +package stakerOperators + +import ( + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + "time" +) + +// _6_operatorODStrategyPayoutQuery is the query that generates the operator OD strategy payouts. +// +// In OperatorDirectedRewards (OD), the operator is paid a reward set by the AVS irrespective of the strategies +// that are delegated to the operator. Because we cant break out by strategy, we can only take the amount that +// the operator is paid of ALL strategies for the given snapshot since it doesnt matter if there are 1 or N strategies +// delegated to the operator; the amount remains the same. +const _6_operatorODStrategyPayoutQuery = ` +create table {{.destTableName}} as +select + od.operator, + od.reward_hash, + od.snapshot, + od.token, + od.avs, + od.strategy, + od.multiplier, + od.reward_submission_date, + od.split_pct, + od.operator_tokens +from {{.operatorODRewardAmountsTable}} as od +` + +type OperatorODStrategyPayout struct { + RewardHash string + Snapshot time.Time + Token string + Avs string + Strategy string + Multiplier string + RewardSubmissionDate time.Time + SplitPct string + OperatorTokens string +} + +func (sog *StakerOperatorsGenerator) GenerateAndInsert6OperatorODStrategyPayouts(cutoffDate string) error { + rewardsV2Enabled, err := sog.globalConfig.IsRewardsV2EnabledForCutoffDate(cutoffDate) + if err != nil { + sog.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + if !rewardsV2Enabled { + sog.logger.Sugar().Infow("Skipping 6_operatorODStrategyPayouts generation as rewards v2 is not enabled") + return nil + } + allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) + destTableName := allTableNames[rewardsUtils.Sot_6_OperatorODStrategyPayouts] + + sog.logger.Sugar().Infow("Generating and inserting 6_operatorODStrategyPayouts", + "cutoffDate", cutoffDate, + ) + + if err := rewardsUtils.DropTableIfExists(sog.db, destTableName, sog.logger); err != nil { + sog.logger.Sugar().Errorw("Failed to drop table", "error", err) + return err + } + + rewardsTables, err := sog.FindRewardsTableNamesForSearchPattersn(map[string]string{ + rewardsUtils.Table_8_OperatorODRewardAmounts: rewardsUtils.GoldTableNameSearchPattern[rewardsUtils.Table_8_OperatorODRewardAmounts], + }, cutoffDate) + if err != nil { + sog.logger.Sugar().Errorw("Failed to find staker operator table names", "error", err) + return err + } + + query, err := rewardsUtils.RenderQueryTemplate(_6_operatorODStrategyPayoutQuery, map[string]interface{}{ + "destTableName": destTableName, + "operatorODRewardAmountsTable": rewardsTables[rewardsUtils.Table_8_OperatorODRewardAmounts], + }) + if err != nil { + sog.logger.Sugar().Errorw("Failed to render 6_operatorODStrategyPayouts query", "error", err) + return err + } + + res := sog.db.Exec(query) + + if res.Error != nil { + sog.logger.Sugar().Errorw("Failed to generate 6_operatorODStrategyPayouts", "error", res.Error) + return err + } + return nil +} diff --git a/pkg/rewards/stakerOperators/7_stakerODStrategyPayouts.go b/pkg/rewards/stakerOperators/7_stakerODStrategyPayouts.go new file mode 100644 index 00000000..881067fb --- /dev/null +++ b/pkg/rewards/stakerOperators/7_stakerODStrategyPayouts.go @@ -0,0 +1,89 @@ +package stakerOperators + +import ( + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" + "time" +) + +// _7_stakerODStrategyPayoutQuery is a constant value that represents a query. +// +// Unlike rewards v1 where all staker amounts are pre-summed across operator/strategies, +// in rewards v2 they are already represented at the staker/operator/strategy/reward_hash level +// so adding them to the staker-operator table is a simple select from what we have already. +const _7_stakerODStrategyPayoutQuery = ` +create table {{.destTableName}} as +select + staker, + operator, + avs, + token, + strategy, + multiplier, + shares, + staker_tokens, + reward_hash, + snapshot +from {{.stakerODRewardAmountsTable}} +` + +type StakerODStrategyPayout struct { + Staker string + Operator string + Avs string + Token string + Strategy string + Multiplier string + Shares string + StakerTokens string + RewardHash string + Snapshot time.Time +} + +func (sog *StakerOperatorsGenerator) GenerateAndInsert7StakerODStrategyPayouts(cutoffDate string) error { + rewardsV2Enabled, err := sog.globalConfig.IsRewardsV2EnabledForCutoffDate(cutoffDate) + if err != nil { + sog.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + if !rewardsV2Enabled { + sog.logger.Sugar().Infow("Skipping 7_stakerODStrategyPayouts generation as rewards v2 is not enabled") + return nil + } + + allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) + destTableName := allTableNames[rewardsUtils.Sot_7_StakerODStrategyPayouts] + + sog.logger.Sugar().Infow("Generating and inserting 7_stakerODStrategyPayouts", + "cutoffDate", cutoffDate, + ) + + if err := rewardsUtils.DropTableIfExists(sog.db, destTableName, sog.logger); err != nil { + sog.logger.Sugar().Errorw("Failed to drop table", "error", err) + return err + } + + rewardsTables, err := sog.FindRewardsTableNamesForSearchPattersn(map[string]string{ + rewardsUtils.Table_9_StakerODRewardAmounts: rewardsUtils.GoldTableNameSearchPattern[rewardsUtils.Table_9_StakerODRewardAmounts], + }, cutoffDate) + if err != nil { + sog.logger.Sugar().Errorw("Failed to find staker operator table names", "error", err) + return err + } + + query, err := rewardsUtils.RenderQueryTemplate(_7_stakerODStrategyPayoutQuery, map[string]interface{}{ + "destTableName": destTableName, + "stakerODRewardAmountsTable": rewardsTables[rewardsUtils.Table_9_StakerODRewardAmounts], + }) + if err != nil { + sog.logger.Sugar().Errorw("Failed to render 7_stakerODStrategyPayouts query", "error", err) + return err + } + + res := sog.db.Exec(query) + + if res.Error != nil { + sog.logger.Sugar().Errorw("Failed to generate 7_stakerODStrategyPayouts", "error", res.Error) + return err + } + return nil +} diff --git a/pkg/rewards/stakerOperators/8_avsODStrategyPayouts.go b/pkg/rewards/stakerOperators/8_avsODStrategyPayouts.go new file mode 100644 index 00000000..51b55dd8 --- /dev/null +++ b/pkg/rewards/stakerOperators/8_avsODStrategyPayouts.go @@ -0,0 +1,82 @@ +package stakerOperators + +import ( + "github.com/Layr-Labs/sidecar/pkg/rewardsUtils" +) + +// _8_avsODStrategyPayoutQuery is the query that generates the 8_avsODStrategyPayouts table +// +// AVS operator directed rewards are not actually rewards, but refunds for the case when the operator +// defined in the rewards-v2 submission wasnt delegated at the time of the snapshot. Since operator rewards +// in rewards-v2 are not based on strategy and are a lump sum between the AVS and operator, the refund +// is also the same; a lump sum BACK to the AVS that was originally intended for the operator. As such, +// there is no strategy, shares or multiplier fields to represent. +const _8_avsODStrategyPayoutQuery = ` +create table {{.destTableName}} as +select + reward_hash, + snapshot, + token, + avs, + operator, + avs_tokens +from {{.avsODRewardAmountsTable}} +` + +type AvsODStrategyPayout struct { + RewardHash string + Snapshot string + Token string + Avs string + Operator string + AvsTokens string +} + +func (sog *StakerOperatorsGenerator) GenerateAndInsert8AvsODStrategyPayouts(cutoffDate string) error { + rewardsV2Enabled, err := sog.globalConfig.IsRewardsV2EnabledForCutoffDate(cutoffDate) + if err != nil { + sog.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } + if !rewardsV2Enabled { + sog.logger.Sugar().Infow("Skipping 8_avsODStrategyPayouts generation as rewards v2 is not enabled") + return nil + } + + allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) + destTableName := allTableNames[rewardsUtils.Sot_8_AvsODStrategyPayouts] + + sog.logger.Sugar().Infow("Generating and inserting 8_avsODStrategyPayouts", + "cutoffDate", cutoffDate, + ) + + if err := rewardsUtils.DropTableIfExists(sog.db, destTableName, sog.logger); err != nil { + sog.logger.Sugar().Errorw("Failed to drop table", "error", err) + return err + } + + rewardsTables, err := sog.FindRewardsTableNamesForSearchPattersn(map[string]string{ + rewardsUtils.Table_10_AvsODRewardAmounts: rewardsUtils.GoldTableNameSearchPattern[rewardsUtils.Table_10_AvsODRewardAmounts], + }, cutoffDate) + if err != nil { + sog.logger.Sugar().Errorw("Failed to find staker operator table names", "error", err) + return err + } + + query, err := rewardsUtils.RenderQueryTemplate(_8_avsODStrategyPayoutQuery, map[string]interface{}{ + "destTableName": destTableName, + "avsODRewardAmountsTable": rewardsTables[rewardsUtils.Table_10_AvsODRewardAmounts], + }) + if err != nil { + sog.logger.Sugar().Errorw("Failed to render 8_avsODStrategyPayouts query", "error", err) + return err + } + + res := sog.db.Exec(query) + + if res.Error != nil { + sog.logger.Sugar().Errorw("Failed to generate 8_avsODStrategyPayouts", "error", res.Error) + return err + } + return nil +} diff --git a/pkg/rewards/stakerOperators/6_stakerOperatorStaging.go b/pkg/rewards/stakerOperators/9_stakerOperatorStaging.go similarity index 63% rename from pkg/rewards/stakerOperators/6_stakerOperatorStaging.go rename to pkg/rewards/stakerOperators/9_stakerOperatorStaging.go index 4c7e0bab..dd7ebc7e 100644 --- a/pkg/rewards/stakerOperators/6_stakerOperatorStaging.go +++ b/pkg/rewards/stakerOperators/9_stakerOperatorStaging.go @@ -85,6 +85,55 @@ SELECT reward_hash, snapshot FROM {{.sot5RfaeOperatorStrategyPayout}} +{{ if .rewardsV2Enabled }} +UNION ALL + +SELECT + operator as earner, + operator as operator, + 'operator_od_reward' as reward_type, + avs, + token, + '0x0000000000000000000000000000000000000000' as strategy, + '0' as multiplier, + '0' as shares, + operator_tokens as amount, + reward_hash, + snapshot +from {{.sot6OperatorODStrategyPayouts}} + +UNION ALL + +SELECT + staker as earner, + operator, + 'staker_od_reward' as reward_type, + avs, + token, + strategy, + multiplier, + shares, + staker_tokens as amount, + reward_hash, + snapshot +from {{.sot7StakerODStrategyPayouts}} + +UNION ALL + +SELECT + avs as earner, + operator, + 'avs_od_reward' as reward_type, + avs, + token, + '0x0000000000000000000000000000000000000000' as strategy, + '0' as multiplier, + '0' as shares, + avs_tokens as amount, + reward_hash, + snapshot +from {{.sot8AvsODStrategyPayouts}} +{{ end }} ` type StakerOperatorStaging struct { @@ -101,33 +150,39 @@ type StakerOperatorStaging struct { Snapshot time.Time } -func (sog *StakerOperatorsGenerator) GenerateAndInsert6StakerOperatorStaging(cutoffDate string) error { - allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) - destTableName := allTableNames[rewardsUtils.Sot_6_StakerOperatorStaging] +func (sog *StakerOperatorsGenerator) GenerateAndInsert9StakerOperatorStaging(cutoffDate string) error { + rewardsV2Enabled, err := sog.globalConfig.IsRewardsV2EnabledForCutoffDate(cutoffDate) + if err != nil { + sog.logger.Sugar().Errorw("Failed to check if rewards v2 is enabled", "error", err) + return err + } - sog.logger.Sugar().Infow("Generating and inserting 6_stakerOperatorsStaging", - zap.String("cutoffDate", cutoffDate), - ) + allTableNames := rewardsUtils.GetGoldTableNames(cutoffDate) + destTableName := allTableNames[rewardsUtils.Sot_9_StakerOperatorStaging] if err := rewardsUtils.DropTableIfExists(sog.db, destTableName, sog.logger); err != nil { sog.logger.Sugar().Errorw("Failed to drop table", "error", err) return err } - sog.logger.Sugar().Infow("Generating 6_stakerOperatorsStaging", - zap.String("destTableName", destTableName), + sog.logger.Sugar().Infow("Generating and inserting 9_stakerOperatorsStaging", zap.String("cutoffDate", cutoffDate), ) - query, err := rewardsUtils.RenderQueryTemplate(_6_stakerOperatorsStaging, map[string]string{ + + query, err := rewardsUtils.RenderQueryTemplate(_6_stakerOperatorsStaging, map[string]interface{}{ "destTableName": destTableName, + "rewardsV2Enabled": rewardsV2Enabled, "sot1StakerStrategyPayouts": allTableNames[rewardsUtils.Sot_1_StakerStrategyPayouts], "sot2OperatorStrategyPayouts": allTableNames[rewardsUtils.Sot_2_OperatorStrategyPayouts], "sot3RewardsForAllStrategyPayouts": allTableNames[rewardsUtils.Sot_3_RewardsForAllStrategyPayout], "sot4RfaeStakerStrategyPayout": allTableNames[rewardsUtils.Sot_4_RfaeStakers], "sot5RfaeOperatorStrategyPayout": allTableNames[rewardsUtils.Sot_5_RfaeOperators], + "sot6OperatorODStrategyPayouts": allTableNames[rewardsUtils.Sot_6_OperatorODStrategyPayouts], + "sot7StakerODStrategyPayouts": allTableNames[rewardsUtils.Sot_7_StakerODStrategyPayouts], + "sot8AvsODStrategyPayouts": allTableNames[rewardsUtils.Sot_8_AvsODStrategyPayouts], }) if err != nil { - sog.logger.Sugar().Errorw("Failed to render 6_stakerOperatorsStaging query", "error", err) + sog.logger.Sugar().Errorw("Failed to render 9_stakerOperatorsStaging query", "error", err) return err } diff --git a/pkg/rewards/stakerOperators/stakerOperator.go b/pkg/rewards/stakerOperators/stakerOperator.go index 112c7997..8a672210 100644 --- a/pkg/rewards/stakerOperators/stakerOperator.go +++ b/pkg/rewards/stakerOperators/stakerOperator.go @@ -26,7 +26,7 @@ func NewStakerOperatorGenerator( } func (sog *StakerOperatorsGenerator) GenerateStakerOperatorsTable(cutoffDate string) error { - forks, err := sog.globalConfig.GetForkDates() + forks, err := sog.globalConfig.GetRewardsSqlForkDates() if err != nil { return err } @@ -79,7 +79,7 @@ func (sog *StakerOperatorsGenerator) GenerateStakerOperatorsTable(cutoffDate str return err } - if err := sog.GenerateAndInsert6StakerOperatorStaging(cutoffDate); err != nil { + if err := sog.GenerateAndInsert6OperatorODStrategyPayouts(cutoffDate); err != nil { sog.logger.Sugar().Errorw("Failed to generate and insert 6 staker strategy rewards", zap.String("cutoffDate", cutoffDate), zap.Error(err), @@ -87,7 +87,31 @@ func (sog *StakerOperatorsGenerator) GenerateStakerOperatorsTable(cutoffDate str return err } - if err := sog.GenerateAndInsert7StakerOperator(cutoffDate); err != nil { + if err := sog.GenerateAndInsert7StakerODStrategyPayouts(cutoffDate); err != nil { + sog.logger.Sugar().Errorw("Failed to generate and insert 7 staker strategy rewards", + zap.String("cutoffDate", cutoffDate), + zap.Error(err), + ) + return err + } + + if err := sog.GenerateAndInsert8AvsODStrategyPayouts(cutoffDate); err != nil { + sog.logger.Sugar().Errorw("Failed to generate and insert 8 staker strategy rewards", + zap.String("cutoffDate", cutoffDate), + zap.Error(err), + ) + return err + } + + if err := sog.GenerateAndInsert9StakerOperatorStaging(cutoffDate); err != nil { + sog.logger.Sugar().Errorw("Failed to generate and insert 6 staker strategy rewards", + zap.String("cutoffDate", cutoffDate), + zap.Error(err), + ) + return err + } + + if err := sog.GenerateAndInsert10StakerOperator(cutoffDate); err != nil { sog.logger.Sugar().Errorw("Failed to generate and insert 7 staker strategy rewards", zap.String("cutoffDate", cutoffDate), zap.Error(err), 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/stakerShareSnapshots_test.go b/pkg/rewards/stakerShareSnapshots_test.go index 84683365..3f87042b 100644 --- a/pkg/rewards/stakerShareSnapshots_test.go +++ b/pkg/rewards/stakerShareSnapshots_test.go @@ -68,7 +68,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.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/rewards/stakerShares_test.go b/pkg/rewards/stakerShares_test.go index 08939b8e..a6749b91 100644 --- a/pkg/rewards/stakerShares_test.go +++ b/pkg/rewards/stakerShares_test.go @@ -56,7 +56,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 f52c5f94..f2148633 100644 --- a/pkg/rewards/tables.go +++ b/pkg/rewards/tables.go @@ -76,3 +76,39 @@ type OperatorShares struct { BlockTime time.Time BlockDate string } + +type DefaultOperatorSplitSnapshots struct { + Split uint64 + Snapshot time.Time +} + +type OperatorAVSSplitSnapshots struct { + Operator string + Avs string + Split uint64 + Snapshot time.Time +} + +type OperatorPISplitSnapshots struct { + Operator string + 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 337ba719..930c5b24 100644 --- a/pkg/rewardsUtils/rewardsUtils.go +++ b/pkg/rewardsUtils/rewardsUtils.go @@ -4,65 +4,87 @@ import ( "bytes" "database/sql" "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_1_StakerStrategyPayouts = "sot_1_staker_strategy_payouts" Sot_2_OperatorStrategyPayouts = "sot_2_operator_strategy_payouts" Sot_3_RewardsForAllStrategyPayout = "sot_3_rewards_for_all_strategy_payout" Sot_4_RfaeStakers = "sot_4_rfae_stakers" Sot_5_RfaeOperators = "sot_5_rfae_operators" - Sot_6_StakerOperatorStaging = "sot_6_staker_operator_staging" - Sot_7_StakerOperatorTable = "staker_operator" + Sot_6_OperatorODStrategyPayouts = "sot_6_operator_od_strategy_payouts" + Sot_7_StakerODStrategyPayouts = "sot_7_staker_od_strategy_payouts" + Sot_8_AvsODStrategyPayouts = "sot_8_avs_od_strategy_payouts" + Sot_9_StakerOperatorStaging = "sot_9_staker_operator_staging" + Sot_10_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_1_StakerStrategyPayouts: Sot_1_StakerStrategyPayouts, Sot_2_OperatorStrategyPayouts: Sot_2_OperatorStrategyPayouts, Sot_3_RewardsForAllStrategyPayout: Sot_3_RewardsForAllStrategyPayout, Sot_4_RfaeStakers: Sot_4_RfaeStakers, Sot_5_RfaeOperators: Sot_5_RfaeOperators, - Sot_6_StakerOperatorStaging: Sot_6_StakerOperatorStaging, + Sot_6_OperatorODStrategyPayouts: Sot_6_OperatorODStrategyPayouts, + Sot_7_StakerODStrategyPayouts: Sot_7_StakerODStrategyPayouts, + Sot_8_AvsODStrategyPayouts: Sot_8_AvsODStrategyPayouts, + Sot_9_StakerOperatorStaging: Sot_9_StakerOperatorStaging, } var GoldTableNameSearchPattern = map[string]string{ - Table_1_ActiveRewards: "gold_%_active_rewards", - Table_2_StakerRewardAmounts: "gold_%_staker_reward_amounts", - Table_3_OperatorRewardAmounts: "gold_%_operator_reward_amounts", - Table_4_RewardsForAll: "gold_%_rewards_for_all", - Table_5_RfaeStakers: "gold_%_rfae_stakers", - Table_6_RfaeOperators: "gold_%_rfae_operators", - Table_7_GoldStaging: "gold_%_staging", + Table_1_ActiveRewards: "gold_%_active_rewards", + Table_2_StakerRewardAmounts: "gold_%_staker_reward_amounts", + Table_3_OperatorRewardAmounts: "gold_%_operator_reward_amounts", + Table_4_RewardsForAll: "gold_%_rewards_for_all", + Table_5_RfaeStakers: "gold_%_rfae_stakers", + Table_6_RfaeOperators: "gold_%_rfae_operators", + Table_7_ActiveODRewards: "gold_%_active_od_rewards", + Table_8_OperatorODRewardAmounts: "gold_%_operator_od_reward_amounts", + Table_9_StakerODRewardAmounts: "gold_%_staker_od_reward_amounts", + Table_10_AvsODRewardAmounts: "gold_%_avs_od_reward_amounts", + Table_11_GoldStaging: "gold_%_staging", Sot_1_StakerStrategyPayouts: "sot_%_staker_strategy_payouts", Sot_2_OperatorStrategyPayouts: "sot_%_operator_strategy_payouts", Sot_3_RewardsForAllStrategyPayout: "sot_%_rewards_for_all_strategy_payout", Sot_4_RfaeStakers: "sot_%_rfae_stakers", Sot_5_RfaeOperators: "sot_%_rfae_operators", - Sot_6_StakerOperatorStaging: "sot_%_staker_operator_staging", + Sot_6_OperatorODStrategyPayouts: "sot_%_operator_od_strategy_payouts", + Sot_7_StakerODStrategyPayouts: "sot_%_staker_od_strategy_payouts", + Sot_8_AvsODStrategyPayouts: "sot_%_avs_od_strategy_payouts", + Sot_9_StakerOperatorStaging: "sot_%_staker_operator_staging", } func GetGoldTableNames(snapshotDate string) map[string]string { @@ -73,7 +95,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 @@ -145,7 +167,7 @@ func findTableByLikeName(likeName string, grm *gorm.DB, schemaName string) (stri limit 1 ` var tname string - res := grm.Debug().Raw(query, + res := grm.Raw(query, sql.Named("schemaName", schemaName), sql.Named("pattern", likeName)). Scan(&tname) diff --git a/pkg/snapshot/snapshot_test.go b/pkg/snapshot/snapshot_test.go index 75da18aa..96098105 100644 --- a/pkg/snapshot/snapshot_test.go +++ b/pkg/snapshot/snapshot_test.go @@ -137,10 +137,10 @@ func TestSetupRestore(t *testing.T) { func setup() (*config.Config, *zap.Logger, error) { cfg := config.NewConfig() cfg.Chain = config.Chain_Mainnet - cfg.Debug = false + cfg.Debug = os.Getenv(config.Debug) == "true" cfg.DatabaseConfig = *tests.GetDbConfigFromEnv() - l, err := logger.NewLogger(&logger.LoggerConfig{Debug: true}) + l, err := logger.NewLogger(&logger.LoggerConfig{Debug: cfg.Debug}) if err != nil { return nil, nil, err } 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 ./ 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