diff --git a/.gitignore b/.gitignore index c367e7a3..58930609 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ chart_releases /snapshots/**/*.sql /snapshots/**/*.csv +.env \ No newline at end of file diff --git a/go.mod b/go.mod index 932ecec8..161a6aea 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/wealdtech/go-merkletree/v2 v2.6.0 github.com/wk8/go-ordered-map/v2 v2.1.8 go.uber.org/zap v1.27.0 + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 google.golang.org/grpc v1.65.0 gorm.io/driver/postgres v1.5.9 gorm.io/gorm v1.25.10 diff --git a/go.sum b/go.sum index c09d6acd..0c8f4b20 100644 --- a/go.sum +++ b/go.sum @@ -670,6 +670,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/internal/config/config.go b/internal/config/config.go index a263bb5f..b3cef47e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -255,7 +255,7 @@ func (c *Config) GetForkDates() (ForkMap, error) { Fork_Amazon: "1970-01-01", // Amazon hard fork was never on preprod as we backfilled Fork_Nile: "2024-08-14", // Last calculation end timestamp was 8-13: https://holesky.etherscan.io/tx/0xb5a6855e88c79312b7c0e1c9f59ae9890b97f157ea27e69e4f0fadada4712b64#eventlog Fork_Panama: "2024-10-01", - Fork_Arno: "2024-12-12", + Fork_Arno: "2024-12-04", }, nil case Chain_Holesky: return ForkMap{ diff --git a/pkg/rewards/10_goldAvsODRewardAmounts.go b/pkg/rewards/10_goldAvsODRewardAmounts.go index 19b5f77e..77fd42dc 100644 --- a/pkg/rewards/10_goldAvsODRewardAmounts.go +++ b/pkg/rewards/10_goldAvsODRewardAmounts.go @@ -14,19 +14,16 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_day, - ap.tokens_per_day_decimal, + ap.tokens_per_registered_snapshot, + ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, ap.strategy, ap.multiplier, ap.reward_submission_date FROM {{.activeODRewardsTable}} ap - LEFT JOIN operator_avs_registration_snapshots oar - ON ap.avs = oar.avs - AND ap.snapshot = oar.snapshot - AND ap.operator = oar.operator - WHERE oar.avs IS NULL OR oar.operator IS NULL + WHERE + ap.num_registered_snapshots = 0 ), -- Step 2: Dedupe the operator tokens across strategies for each (operator, reward hash, snapshot) @@ -56,7 +53,7 @@ operator_token_sums AS ( token, avs, operator, - SUM(tokens_per_day_decimal) OVER (PARTITION BY reward_hash, snapshot) AS avs_tokens + SUM(tokens_per_registered_snapshot_decimal) OVER (PARTITION BY reward_hash, snapshot) AS avs_tokens FROM distinct_operators ) diff --git a/pkg/rewards/7_goldActiveODRewards.go b/pkg/rewards/7_goldActiveODRewards.go index 902c9438..bc5d3742 100644 --- a/pkg/rewards/7_goldActiveODRewards.go +++ b/pkg/rewards/7_goldActiveODRewards.go @@ -14,7 +14,6 @@ WITH active_rewards_modified AS ( SELECT *, - amount / (duration / 86400) AS tokens_per_day, CAST(@cutoffDate AS TIMESTAMP(6)) AS global_end_inclusive -- Inclusive means we DO USE this day as a snapshot FROM operator_directed_rewards WHERE end_timestamp >= TIMESTAMP '{{.rewardsStart}}' @@ -34,11 +33,12 @@ active_rewards_updated_end_timestamps AS ( */ start_timestamp AS reward_start_exclusive, LEAST(global_end_inclusive, end_timestamp) AS reward_end_inclusive, - tokens_per_day, + amount, token, multiplier, strategy, reward_hash, + duration, global_end_inclusive, block_date AS reward_submission_date FROM active_rewards_modified @@ -53,12 +53,13 @@ active_rewards_updated_start_timestamps AS ( ap.reward_end_inclusive, ap.token, -- We use floor to ensure we are always underesimating total tokens per day - FLOOR(ap.tokens_per_day) AS tokens_per_day_decimal, + FLOOR(ap.amount) AS amount_decimal, -- Round down to 15 sigfigs for double precision, ensuring know errouneous round up or down - ap.tokens_per_day * ((POW(10, 15) - 1) / POW(10, 15)) AS tokens_per_day, + ap.amount * ((POW(10, 15) - 1) / POW(10, 15)) AS amount, ap.multiplier, ap.strategy, ap.reward_hash, + ap.duration, ap.global_end_inclusive, ap.reward_submission_date FROM active_rewards_updated_end_timestamps ap @@ -69,10 +70,11 @@ active_rewards_updated_start_timestamps AS ( ap.operator, ap.reward_end_inclusive, ap.token, - ap.tokens_per_day, + ap.amount, ap.multiplier, ap.strategy, ap.reward_hash, + ap.duration, ap.global_end_inclusive, ap.reward_start_exclusive, ap.reward_submission_date @@ -100,22 +102,73 @@ exploded_active_range_rewards AS ( ) AS day ), --- Step 7: Prepare final active rewards -active_rewards_final AS ( +-- Step 7: Prepare cleaned active rewards +active_rewards_cleaned AS ( SELECT avs, operator, CAST(day AS DATE) AS snapshot, token, - tokens_per_day, - tokens_per_day_decimal, + amount, + amount_decimal, multiplier, strategy, + duration, reward_hash, reward_submission_date FROM exploded_active_range_rewards -- Remove snapshots on the start day WHERE day != reward_start_exclusive +), + +-- Step 8: Dedupe the active rewards by (avs, snapshot, operator, reward_hash) +active_rewards_reduced_deduped AS ( + SELECT DISTINCT avs, snapshot, operator, reward_hash + FROM active_rewards_cleaned +), + +-- Step 9: Divide by the number of snapshots that the operator was registered +op_avs_num_registered_snapshots AS ( + SELECT + ar.reward_hash, + ar.operator, + COUNT(*) AS num_registered_snapshots + FROM active_rewards_reduced_deduped ar + JOIN operator_avs_registration_snapshots oar + ON + ar.avs = oar.avs + AND ar.snapshot = oar.snapshot + AND ar.operator = oar.operator + GROUP BY ar.reward_hash, ar.operator +), + +-- Step 9: Divide amount to pay by the number of snapshots that the operator was registered +active_rewards_with_registered_snapshots AS ( + SELECT + arc.*, + COALESCE(nrs.num_registered_snapshots, 0) as num_registered_snapshots + FROM active_rewards_cleaned arc + LEFT JOIN op_avs_num_registered_snapshots nrs + ON + arc.reward_hash = nrs.reward_hash + AND arc.operator = nrs.operator +), + +-- Step 10: Divide amount to pay by the number of snapshots that the operator was registered +active_rewards_final AS ( + SELECT + ar.*, + CASE + -- If the operator was not registered for any snapshots, just get regular tokens per day to refund the AVS + WHEN ar.num_registered_snapshots = 0 THEN ar.amount_decimal / (duration / 86400) + ELSE ar.amount_decimal / ar.num_registered_snapshots + END AS tokens_per_registered_snapshot_decimal, + CASE + -- If the operator was not registered for any snapshots, just get regular tokens per day to refund the AVS + WHEN ar.num_registered_snapshots = 0 THEN ar.amount / (duration / 86400) + ELSE ar.amount / ar.num_registered_snapshots + END AS tokens_per_registered_snapshot + FROM active_rewards_with_registered_snapshots ar ) SELECT * FROM active_rewards_final diff --git a/pkg/rewards/8_goldOperatorODRewardAmounts.go b/pkg/rewards/8_goldOperatorODRewardAmounts.go index 5bb9718f..52d3684b 100644 --- a/pkg/rewards/8_goldOperatorODRewardAmounts.go +++ b/pkg/rewards/8_goldOperatorODRewardAmounts.go @@ -14,8 +14,8 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_day, - ap.tokens_per_day_decimal, + ap.tokens_per_registered_snapshot, + ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, ap.strategy, @@ -53,7 +53,7 @@ operator_splits AS ( SELECT dop.*, COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL) as split_pct, - FLOOR(dop.tokens_per_day_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) AS operator_tokens + FLOOR(dop.tokens_per_registered_snapshot_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) AS operator_tokens FROM distinct_operators dop LEFT JOIN operator_avs_split_snapshots oas ON dop.operator = oas.operator diff --git a/pkg/rewards/9_goldStakerODRewardAmounts.go b/pkg/rewards/9_goldStakerODRewardAmounts.go index 5956f39c..37d7ca2e 100644 --- a/pkg/rewards/9_goldStakerODRewardAmounts.go +++ b/pkg/rewards/9_goldStakerODRewardAmounts.go @@ -14,8 +14,8 @@ WITH reward_snapshot_operators AS ( ap.reward_hash, ap.snapshot AS snapshot, ap.token, - ap.tokens_per_day, - ap.tokens_per_day_decimal, + ap.tokens_per_registered_snapshot, + ap.tokens_per_registered_snapshot_decimal, ap.avs AS avs, ap.operator AS operator, ap.strategy, @@ -33,7 +33,7 @@ WITH reward_snapshot_operators AS ( staker_splits AS ( SELECT rso.*, - rso.tokens_per_day_decimal - FLOOR(rso.tokens_per_day_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) AS staker_split + rso.tokens_per_registered_snapshot_decimal - FLOOR(rso.tokens_per_registered_snapshot_decimal * COALESCE(oas.split, 1000) / CAST(10000 AS DECIMAL)) AS staker_split FROM reward_snapshot_operators rso LEFT JOIN operator_avs_split_snapshots oas ON rso.operator = oas.operator diff --git a/pkg/rewards/operatorDirectedRewards.go b/pkg/rewards/operatorDirectedRewards.go index 9cc7d896..de604802 100644 --- a/pkg/rewards/operatorDirectedRewards.go +++ b/pkg/rewards/operatorDirectedRewards.go @@ -22,7 +22,7 @@ const operatorDirectedRewardsQuery = ` TO_CHAR(b.block_time, 'YYYY-MM-DD') AS block_date FROM operator_directed_reward_submissions AS odrs JOIN blocks AS b ON(b.number = odrs.block_number) - WHERE b.block_time < TIMESTAMP '{{.cutoffDate}}' + WHERE b.block_time <= TIMESTAMP '{{.cutoffDate}}' ) select avs, diff --git a/pkg/rewards/rewardsV2_test.go b/pkg/rewards/rewardsV2_test.go index cd890901..28b5d8d1 100644 --- a/pkg/rewards/rewardsV2_test.go +++ b/pkg/rewards/rewardsV2_test.go @@ -2,6 +2,7 @@ package rewards import ( "fmt" + "strings" "testing" "time" @@ -210,6 +211,12 @@ func Test_RewardsV2(t *testing.T) { assert.Nil(t, err) t.Logf("Gold staging rows for snapshot %s: %d", snapshotDate, len(goldRows)) + for i, row := range goldRows { + if strings.EqualFold(row.RewardHash, strings.ToLower("0xB38AB57E8E858F197C07D0CDF61F34EB07C3D0FC58390417DDAD0BF528681909")) { + t.Logf("%d: %s %s %s %s %s", i, row.Earner, row.Snapshot.String(), row.RewardHash, row.Token, row.Amount) + } + // t.Logf("%d: %s %s %s %s %s", i, row.Earner, row.Snapshot.String(), row.RewardHash, row.Token, row.Amount) + } fmt.Printf("Total duration for rewards compute %s: %v\n", snapshotDate, time.Since(snapshotStartTime)) testStart = time.Now()