Skip to content

9.5 upgrade & add underflow protection for referred volume#3261

Merged
jusbar23 merged 13 commits into
mainfrom
9.5_upgrade
Dec 2, 2025
Merged

9.5 upgrade & add underflow protection for referred volume#3261
jusbar23 merged 13 commits into
mainfrom
9.5_upgrade

Conversation

@jusbar23
Copy link
Copy Markdown
Contributor

@jusbar23 jusbar23 commented Nov 25, 2025

Changelist

  • Add 9.5 upgrade and underflow protection for ExpireOldStats

Test Plan

  • Unit tests

Author/Reviewer Checklist

  • If this PR has changes that result in a different app state given the same prior state and transaction list, manually add the state-breaking label.
  • If the PR has breaking postgres changes to the indexer add the indexer-postgres-breaking label.
  • If this PR isn't state-breaking but has changes that modify behavior in PrepareProposal or ProcessProposal, manually add the label proposal-breaking.
  • If this PR is one of many that implement a specific feature, manually label them all feature:[feature-name].
  • If you wish to for mergify-bot to automatically create a PR to backport your change to a release branch, manually add the label backport/[branch-name].
  • Manually add any of the following labels: refactor, chore, bug.

Summary by CodeRabbit

  • New Features

    • v9.5 upgrade: migrates 30-day referred volumes into epoch-level statistics and registers a v9.5 upgrade handler.
  • Bug Fixes

    • Expiration logic now guards against numeric underflow when trimming old stats.
  • Tests

    • Added unit and container-style tests for the v9.5 migration (empty and multi-user scenarios); removed legacy v9.4 container test.
  • Chores

    • Bumped test/version files to v9.5.

✏️ Tip: You can customize this high-level summary in your review settings.

@jusbar23 jusbar23 requested a review from a team as a code owner November 25, 2025 17:00
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 25, 2025

Walkthrough

Adds a v9.5 upgrade that migrates users' 30-day referred volume into epoch-level stats, wires a v9.5 upgrade handler, adds underflow protection and an address-enumeration helper to the stats keeper, updates testing version files, adds unit and container tests for the migration, and removes the v9.4 container test.

Changes

Cohort / File(s) Summary
v9.5 upgrade package
protocol/app/upgrades/v9.5/constants.go, protocol/app/upgrades/v9.5/upgrade.go
Adds UpgradeName = "v9.5" and Upgrade variable; implements Migrate30dReferredVolumeToEpochStats and CreateUpgradeHandler to migrate per-user 30-day referred-volume into epoch stats and run migrations.
Stats keeper core
protocol/x/stats/keeper/keeper.go
Adds GetAllAddressesWithReferredVolume(ctx) and clamps affiliate-related uint64 fields to zero in ExpireOldStats to prevent underflow.
Stats keeper tests
protocol/x/stats/keeper/keeper_test.go
Adds tests TestExpireOldStats_UnderflowProtection* covering underflow guards, boundary, and multi-user scenarios.
v9.5 container & state tests
protocol/app/upgrades/v9.5/upgrade_container_test.go
Adds tests TestMigrate30dReferredVolumeToEpochStats, TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats, and state-upgrade scaffolding to validate migration end-to-end.
Removed v9.4 container test
protocol/app/upgrades/v9.4/upgrade_container_test.go
Deletes the v9.4 container upgrade test and its helper functions.
App upgrade wiring
protocol/app/upgrades.go
Replaces v_9_4 wiring with v_9_5: registers v_9_5.Upgrade, checks v_9_5.UpgradeName, and calls v_9_5.CreateUpgradeHandler with updated parameters (configurator, StatsKeeper, EpochsKeeper).
Version artifacts
protocol/testing/version/VERSION_CURRENT, protocol/testing/version/VERSION_FULL_NAME_PREUPGRADE, protocol/testing/version/VERSION_PREUPGRADE
Bumps protocol testing version strings (v9.4 → v9.5 and related preupgrade identifiers).

Sequence Diagram(s)

sequenceDiagram
    participant Operator as Upgrade Trigger
    participant Handler as CreateUpgradeHandler
    participant Migration as Migrate30dReferredVolumeToEpochStats
    participant Stats as StatsKeeper
    participant Epochs as EpochsKeeper
    participant MM as ModuleManager

    Operator->>Handler: start upgrade
    Handler->>Migration: run migration(ctx, statsKeeper, epochsKeeper)
    Migration->>Stats: GetAllAddressesWithReferredVolume()
    Stats-->>Migration: addresses[]

    rect rgba(150,200,255,0.14)
        note right of Migration: For each address with 30d referred volume
        Migration->>Epochs: GetCurrentEpoch()
        Epochs-->>Migration: epoch
        Migration->>Epochs: GetEpochStats(addr, epoch)
        Epochs-->>Migration: epochStats (or nil)
        Migration->>Migration: merge 30d referred volume -> epochStats
        Migration->>Epochs: SetEpochStats(updated)
        Migration->>Migration: log per-address migration
    end

    Handler->>MM: mm.RunMigrations(configurator)
    MM-->>Handler: done
    Handler-->>Operator: upgrade complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to migration idempotency, address deduplication, and logging verbosity.
  • Verify ExpireOldStats clamping covers all affiliate fields and maintains expected invariants.
  • Review new container tests for deterministic setup and potential flakiness.

Possibly related PRs

Suggested labels

feature:x/affiliates

Suggested reviewers

  • shrenujb
  • teddyding

Poem

🐰 A hop, a ledger shuffle — volumes take their place,
Thirty days tucked into each epoch's quiet space.
Counters guarded, no slips, no underflow fright,
Tests hop forward, making sure the numbers are right.
Hooray — the chain advances, neat and bright.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive The description includes required sections (Changelist, Test Plan, Author/Reviewer Checklist) but lacks sufficient detail about the migration logic and scope of changes. Expand Changelist to describe the epoch stats migration and keeper method additions; clarify scope and type of state changes for proper labeling.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the two main changes: adding v9.5 upgrade functionality and implementing underflow protection for referred volume handling.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 9.5_upgrade

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
protocol/x/stats/keeper/keeper_test.go (1)

1070-1075: Redundant assertions after confirming value is 0.

Lines 1065-1068 already assert the values equal 0. The subsequent require.Less assertions on lines 1072-1075 will always pass since 0 < 1000.

Consider removing these redundant checks or restructuring the test to use only one assertion style.

protocol/app/upgrades/v9.5/upgrade.go (1)

54-58: Unnecessary nil check: GetUserStats never returns nil.

Looking at keeper.go lines 149-160, GetUserStats always returns a pointer to a struct (either unmarshaled data or an empty &types.UserStats{}), never nil.

The nil check on line 56 will never be true and can be removed:

 globalUserStats := statsKeeper.GetUserStats(ctx, address)
-if globalUserStats == nil {
-    continue
-}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e58cce2 and 7cbeb01.

📒 Files selected for processing (5)
  • protocol/app/upgrades/v9.5/constants.go (1 hunks)
  • protocol/app/upgrades/v9.5/upgrade.go (1 hunks)
  • protocol/app/upgrades/v9.5/upgrade_test.go (1 hunks)
  • protocol/x/stats/keeper/keeper.go (2 hunks)
  • protocol/x/stats/keeper/keeper_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2597
File: indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql:16-20
Timestamp: 2024-11-22T18:12:04.606Z
Learning: Avoid suggesting changes to deprecated functions such as `dydx_update_perpetual_v1_handler` in `indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql` if they are unchanged in the PR.
📚 Learning: 2024-11-15T16:00:11.304Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:852-865
Timestamp: 2024-11-15T16:00:11.304Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/stats/keeper/keeper.go
  • protocol/x/stats/keeper/keeper_test.go
📚 Learning: 2024-11-15T15:59:28.095Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:833-850
Timestamp: 2024-11-15T15:59:28.095Z
Learning: The function `GetInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/stats/keeper/keeper.go
📚 Learning: 2024-11-15T16:17:29.092Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/clob/types/expected_keepers.go:86-90
Timestamp: 2024-11-15T16:17:29.092Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/clob/types/expected_keepers.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/stats/keeper/keeper.go
  • protocol/x/stats/keeper/keeper_test.go
🧬 Code graph analysis (3)
protocol/x/stats/keeper/keeper.go (2)
protocol/x/stats/types/keys.go (1)
  • UserStatsKeyPrefix (21-21)
protocol/x/stats/types/stats.pb.go (3)
  • UserStats (459-471)
  • UserStats (475-475)
  • UserStats (476-478)
protocol/app/upgrades/v9.5/upgrade.go (4)
protocol/x/stats/keeper/keeper.go (1)
  • Keeper (23-30)
protocol/mocks/Configurator.go (1)
  • Configurator (15-17)
protocol/lib/context.go (1)
  • UnwrapSDKContext (31-55)
protocol/app/upgrades/v9.5/constants.go (1)
  • UpgradeName (9-9)
protocol/x/stats/keeper/keeper_test.go (1)
protocol/x/stats/types/stats.pb.go (15)
  • EpochStats (304-309)
  • EpochStats (313-313)
  • EpochStats (314-316)
  • EpochStats_UserWithStats (359-362)
  • EpochStats_UserWithStats (366-366)
  • EpochStats_UserWithStats (367-369)
  • UserStats (459-471)
  • UserStats (475-475)
  • UserStats (476-478)
  • GlobalStats (412-415)
  • GlobalStats (419-419)
  • GlobalStats (420-422)
  • StatsMetadata (257-261)
  • StatsMetadata (265-265)
  • StatsMetadata (266-268)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: golangci-lint
  • GitHub Check: build
  • GitHub Check: container-tests
  • GitHub Check: build-and-push-testnet
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: benchmark
  • GitHub Check: test-coverage-upload
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: liveness-test
  • GitHub Check: test-race
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Summary
🔇 Additional comments (9)
protocol/x/stats/keeper/keeper_test.go (3)

1061-1063: Note: TakerNotional and MakerNotional still lack underflow protection.

The comment acknowledges that TakerNotional and MakerNotional can wrap around for uint64. Looking at keeper.go lines 332-334, these fields are subtracted without clamping:

stats.TakerNotional -= removedStats.Stats.TakerNotional
stats.MakerNotional -= removedStats.Stats.MakerNotional
stats.Affiliate_30DRevenueGeneratedQuantums -= removedStats.Stats.Affiliate_30DRevenueGeneratedQuantums

If the same data inconsistency scenario occurs for these fields, they would underflow. Consider whether consistent clamping should be applied to all uint64 stats fields.


1078-1128: LGTM! Edge case test is well-structured.

The test correctly verifies exact boundary behavior where user stats equal epoch stats, ensuring the result is exactly 0 after subtraction.


1130-1221: LGTM! Multi-user test provides comprehensive coverage.

The test effectively covers three scenarios in one: normal subtraction (Alice), underflow clamping (Bob), and exact boundary (Carl). This ensures the underflow protection works correctly regardless of other users' data.

protocol/x/stats/keeper/keeper.go (1)

454-473: LGTM! New helper method is well-implemented.

The method correctly:

  • Uses prefix store with the proper key prefix
  • Properly closes the iterator with defer
  • Filters for non-zero referred volume only
  • Returns addresses as strings (matching the key type)

The full iteration is acceptable for a one-time migration operation.

protocol/app/upgrades/v9.5/constants.go (1)

1-15: LGTM! Standard upgrade constants structure.

The upgrade constants follow the established pattern with the correct version naming and empty StoreUpgrades since this upgrade doesn't modify store structure.

protocol/app/upgrades/v9.5/upgrade_test.go (2)

13-124: LGTM! Comprehensive migration test.

The test effectively covers:

  • Migration preserves existing TakerNotional/MakerNotional values (Alice, Bob)
  • Users with referred volume but no trading activity are added (Carl)
  • Users without referred volume are excluded (Dave)
  • Verifies pre-migration state has zero referred volume in epoch stats

126-169: LGTM! Empty epoch stats edge case covered.

Good test coverage for the scenario where no epoch stats exist initially, ensuring the migration creates new epoch stats correctly.

protocol/app/upgrades/v9.5/upgrade.go (2)

73-76: Potential issue: Addition instead of assignment could double-count referred volume.

Line 75 adds to existing Affiliate_30DReferredVolumeQuoteQuantums:

epochUserStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums += referredVolume

However, looking at the test setup on lines 28-47 of upgrade_test.go, the initial epoch stats have no referred volume (comment: "No referred volume set yet"). If epoch stats were to already contain referred volume from normal trading activity during the epoch, this addition would result in double-counting.

Consider whether this should be an assignment instead:

-epochUserStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums += referredVolume
+epochUserStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums = referredVolume

Or clarify in the PR objectives if additive behavior is intentional.


108-125: LGTM! Upgrade handler follows established patterns.

The handler correctly:

  • Unwraps the SDK context with the module name
  • Logs upgrade start/completion
  • Runs the migration before standard module migrations
  • Returns the result of RunMigrations

Comment thread protocol/x/stats/keeper/keeper.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
protocol/x/stats/keeper/keeper.go (2)

334-352: Affiliate underflow guards are correct; consider optional minor polish.

The comparison-before-subtract pattern on the three affiliate-related fields correctly prevents uint64 underflow, including the equality case. If you expect these clamps to ever trigger due to state inconsistencies, you might optionally add a debug log in the else branches or extract a tiny safeSubUint64 helper to de-duplicate the pattern; not required, current implementation is fine as-is.


460-478: GetAllAddressesWithReferredVolume is correct; keep scalability in mind for very large state.

Iterating the UserStats prefix store and filtering on Affiliate_30DReferredVolumeQuoteQuantums > 0 is a clear, correct way to surface addresses for the migration, and returning raw address strings matches the existing GetUserStats/SetUserStats API. If this ever needs to run over millions of users, consider a streaming/chunked migration that processes entries during iteration instead of accumulating all addresses into a single slice, to cap peak memory usage.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6324145 and f91a7e7.

📒 Files selected for processing (3)
  • protocol/app/upgrades/v9.4/upgrade_container_test.go (0 hunks)
  • protocol/scripts/genesis/sample_pregenesis.json (1 hunks)
  • protocol/x/stats/keeper/keeper.go (2 hunks)
💤 Files with no reviewable changes (1)
  • protocol/app/upgrades/v9.4/upgrade_container_test.go
✅ Files skipped from review due to trivial changes (1)
  • protocol/scripts/genesis/sample_pregenesis.json
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2597
File: indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql:16-20
Timestamp: 2024-11-22T18:12:04.606Z
Learning: Avoid suggesting changes to deprecated functions such as `dydx_update_perpetual_v1_handler` in `indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql` if they are unchanged in the PR.
📚 Learning: 2024-11-15T16:00:11.304Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:852-865
Timestamp: 2024-11-15T16:00:11.304Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/stats/keeper/keeper.go
📚 Learning: 2024-11-15T15:59:28.095Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:833-850
Timestamp: 2024-11-15T15:59:28.095Z
Learning: The function `GetInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/stats/keeper/keeper.go
📚 Learning: 2024-11-15T16:17:29.092Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/clob/types/expected_keepers.go:86-90
Timestamp: 2024-11-15T16:17:29.092Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/clob/types/expected_keepers.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/stats/keeper/keeper.go
🧬 Code graph analysis (1)
protocol/x/stats/keeper/keeper.go (2)
protocol/x/stats/types/keys.go (1)
  • UserStatsKeyPrefix (21-21)
protocol/x/stats/types/stats.pb.go (3)
  • UserStats (459-471)
  • UserStats (475-475)
  • UserStats (476-478)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: container-tests
  • GitHub Check: build
  • GitHub Check: golangci-lint
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: benchmark
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: test-coverage-upload
  • GitHub Check: test-race
  • GitHub Check: liveness-test
  • GitHub Check: build-and-push-testnet
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Summary

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (1)

81-124: Consider verifying that global user stats remain unchanged after migration.

The test thoroughly verifies the epoch stats after migration, but it doesn't check whether the global user stats (set via SetUserStats) remain unchanged. If the migration is intended to copy data rather than move it, adding assertions to verify that global stats are preserved would strengthen the test coverage.

Example verification you could add after line 124:

+	// Verify global user stats remain unchanged after migration
+	for _, user := range testUsers {
+		globalStats := statsKeeper.GetUserStats(ctx, user.address)
+		require.Equal(t, user.referredVolume, globalStats.Affiliate_30DReferredVolumeQuoteQuantums,
+			"Global referred volume should remain unchanged for user %s", user.address)
+	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f91a7e7 and 83ab1e1.

📒 Files selected for processing (1)
  • protocol/app/upgrades/v9.5/upgrade_container_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2597
File: indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql:16-20
Timestamp: 2024-11-22T18:12:04.606Z
Learning: Avoid suggesting changes to deprecated functions such as `dydx_update_perpetual_v1_handler` in `indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql` if they are unchanged in the PR.
🧬 Code graph analysis (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (3)
protocol/testutil/app/app.go (2)
  • NewTestAppBuilder (335-350)
  • AdvanceToBlockOptions (125-152)
protocol/app/app.go (1)
  • App (271-376)
protocol/testutil/constants/addresses.go (4)
  • AliceAccAddress (10-10)
  • BobAccAddress (11-11)
  • CarlAccAddress (12-12)
  • DaveAccAddress (13-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (26)
  • GitHub Check: test-coverage-upload
  • GitHub Check: liveness-test
  • GitHub Check: test-race
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: benchmark
  • GitHub Check: golangci-lint
  • GitHub Check: container-tests
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: build-and-push-testnet
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (4)
protocol/app/upgrades/v9.5/upgrade_container_test.go (4)

1-11: LGTM! Clean imports and package structure.

The package declaration and imports are appropriate for the test file.


13-68: Test setup is comprehensive and well-organized.

The test setup covers multiple scenarios with clear test data structure. The inclusion of Dave with zero referred volume is a good edge case to verify that users without referred volume are not migrated.


126-169: LGTM! Excellent edge case coverage.

The empty epoch stats test effectively validates the migration behavior when no epoch stats exist initially. The assertions confirm that users with referred volume are properly added with correct values and zero trading notional.


79-79: The function migrate30dReferredVolumeToEpochStats exists and is properly accessible.

The function is defined in protocol/app/upgrades/v9.5/upgrade.go at line 19 with the exact signature used in the test: func migrate30dReferredVolumeToEpochStats(ctx sdk.Context, statsKeeper statskeeper.Keeper, epochsKeeper epochskeeper.Keeper). Since both the test file and the function definition are in the same package, the function is fully accessible with no visibility issues.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (1)

126-153: Leverage TestStateUpgrade hooks to assert referred-volume migration end-to-end

TestStateUpgrade successfully wires up a pre‑upgrade testnet and runs the v9.5 software upgrade, but preUpgradeSetups, preUpgradeChecks, and postUpgradeChecks are currently no‑ops. That means this test only verifies that the chain upgrades without error, not that 30‑day referred volume is actually migrated as expected in a real node.

Non‑blocking for this PR, but it would be valuable to:

  • In preUpgradeSetups, create users with 30d referred volume in global stats (via txs or appropriate module messages).
  • In preUpgradeChecks, assert the pre‑upgrade shape (e.g., global stats populated, epoch stats empty).
  • In postUpgradeChecks, query epoch stats and verify that the referred volume has been migrated according to the same expectations as the unit tests.

That will give you an end‑to‑end, “real node” safeguard around the v9.5 migration in addition to the in‑process keeper tests.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83ab1e1 and dce8636.

📒 Files selected for processing (2)
  • protocol/app/upgrades/v9.5/upgrade.go (1 hunks)
  • protocol/app/upgrades/v9.5/upgrade_container_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • protocol/app/upgrades/v9.5/upgrade.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2597
File: indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql:16-20
Timestamp: 2024-11-22T18:12:04.606Z
Learning: Avoid suggesting changes to deprecated functions such as `dydx_update_perpetual_v1_handler` in `indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql` if they are unchanged in the PR.
🧬 Code graph analysis (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (5)
protocol/testutil/app/app.go (1)
  • NewTestAppBuilder (335-350)
protocol/testutil/constants/addresses.go (4)
  • AliceAccAddress (10-10)
  • BobAccAddress (11-11)
  • CarlAccAddress (12-12)
  • DaveAccAddress (13-13)
protocol/testing/containertest/testnet.go (1)
  • NewTestnetWithPreupgradeGenesis (79-86)
protocol/testing/containertest/testnet_utils.go (1)
  • UpgradeTestnet (24-66)
protocol/testing/containertest/node.go (1)
  • Node (35-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (26)
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: liveness-test
  • GitHub Check: build
  • GitHub Check: build-and-push-testnet
  • GitHub Check: test-coverage-upload
  • GitHub Check: golangci-lint
  • GitHub Check: benchmark
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: container-tests
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Summary

Comment on lines +16 to +88
func TestMigrate30dReferredVolumeToEpochStats(t *testing.T) {
tApp := testapp.NewTestAppBuilder(t).Build()
tApp.InitChain()

statsKeeper := tApp.App.StatsKeeper
epochsKeeper := tApp.App.EpochsKeeper

// Advance to the next epoch so we have a current epoch with some activity
ctx := tApp.AdvanceToBlock(10, testapp.AdvanceToBlockOptions{})

// Get current epoch directly from keeper instead of using containertest.Query
statsEpochInfo := epochsKeeper.MustGetStatsEpochInfo(ctx)
currentEpoch := statsEpochInfo.CurrentEpoch

// Create some initial epoch stats (simulating existing trading activity)
initialEpochStats := &statstypes.EpochStats{
Stats: []*statstypes.EpochStats_UserWithStats{
{
User: constants.AliceAccAddress.String(),
Stats: &statstypes.UserStats{
TakerNotional: 50,
MakerNotional: 75,
// No referred volume set yet
},
},
{
User: constants.BobAccAddress.String(),
Stats: &statstypes.UserStats{
TakerNotional: 100,
MakerNotional: 150,
// No referred volume set yet
},
},
},
}
statsKeeper.SetEpochStats(ctx, currentEpoch, initialEpochStats)

// Set up global user stats with referred volume (this represents the 30d cumulative volume)
testUsers := []struct {
address string
referredVolume uint64
}{
{constants.AliceAccAddress.String(), 1_000_000_000}, // 1k volume
{constants.BobAccAddress.String(), 5_000_000_000}, // 5k volume
{constants.CarlAccAddress.String(), 10_000_000_000}, // 10k volume
{constants.DaveAccAddress.String(), 0}, // no referred volume
}

for _, user := range testUsers {
userStats := &statstypes.UserStats{
TakerNotional: 100,
MakerNotional: 200,
Affiliate_30DReferredVolumeQuoteQuantums: user.referredVolume,
}
statsKeeper.SetUserStats(ctx, user.address, userStats)
}

// Verify initial state - epoch stats should not have referred volume
preUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.NotNil(t, preUpgradeEpochStats)
for _, userStats := range preUpgradeEpochStats.Stats {
require.Equal(t, uint64(0), userStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums,
"Referred volume should be 0 before migration for user %s", userStats.User)
}

// Run the migration function directly
v_9_5.Migrate30dReferredVolumeToEpochStats(ctx, statsKeeper, epochsKeeper)

// Verify migration results
postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.NotNil(t, postUpgradeEpochStats)

// Create a map for easier verification
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Complete TestMigrate30dReferredVolumeToEpochStats: missing closing brace and core assertions

This test currently never closes the function (no terminating }) and stops right after checking that postUpgradeEpochStats is non‑nil. It doesn’t actually assert anything about which users ended up in epoch stats or what their referred volumes are, so it won’t guard the migration logic.

Consider:

  • Adding a map from user address → stats to validate the migration for each of testUsers.
  • Verifying that users with non‑zero referred volume have their epoch stats populated with the expected Affiliate_30DReferredVolumeQuoteQuantums.
  • Verifying that users with zero referred volume are handled as intended (either absent from epoch stats or present with zero, depending on your migration contract).
  • Closing the function with a }.

For example, something along these lines would both fix the syntax and exercise the migration:

  // Verify migration results
  postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
  require.NotNil(t, postUpgradeEpochStats)
 
- // Create a map for easier verification
-
+ // Create a map for easier verification
+ statsByUser := make(map[string]*statstypes.UserStats)
+ for _, u := range postUpgradeEpochStats.Stats {
+   statsByUser[u.User] = u.Stats
+ }
+
+ for _, user := range testUsers {
+   stats, ok := statsByUser[user.address]
+
+   if user.referredVolume == 0 {
+     // Adjust this expectation if zero-volume users should still appear in epoch stats.
+     require.False(t, ok, "expected no epoch stats entry for user %s", user.address)
+     continue
+   }
+
+   require.True(t, ok, "expected epoch stats entry for user %s", user.address)
+   require.Equal(
+     t,
+     user.referredVolume,
+     stats.Affiliate_30DReferredVolumeQuoteQuantums,
+     "unexpected referred volume for user %s",
+     user.address,
+   )
+ }
+}

Adjust the zero‑volume behavior (Dave) to match how Migrate30dReferredVolumeToEpochStats is intended to behave, but you’ll at least have a concrete check that the per‑user migration is correct.

Comment on lines +90 to +125
func TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats(t *testing.T) {
tApp := testapp.NewTestAppBuilder(t).Build()
tApp.InitChain()

statsKeeper := tApp.App.StatsKeeper
epochsKeeper := tApp.App.EpochsKeeper

// Advance to next epoch
ctx := tApp.AdvanceToBlock(10, testapp.AdvanceToBlockOptions{})

// Get current epoch directly from keeper instead of using containertest.Query
statsEpochInfo := epochsKeeper.MustGetStatsEpochInfo(ctx)
currentEpoch := statsEpochInfo.CurrentEpoch

// Setup: Create user stats with referred volume but no epoch stats
userStats := &statstypes.UserStats{
TakerNotional: 100,
MakerNotional: 200,
Affiliate_30DReferredVolumeQuoteQuantums: 1_000_000_000,
}
statsKeeper.SetUserStats(ctx, constants.AliceAccAddress.String(), userStats)

// Verify no epoch stats exist initially
preUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.Nil(t, preUpgradeEpochStats)

// Run the migration function directly
v_9_5.Migrate30dReferredVolumeToEpochStats(ctx, statsKeeper, epochsKeeper)

// Verify Alice was added to epoch stats even though she wasn't trading
postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.NotNil(t, postUpgradeEpochStats)
require.Len(t, postUpgradeEpochStats.Stats, 1,
"Alice should be added to epoch stats because she has referred volume")

aliceStats := postUpgradeEpochStats.Stats[0]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Finish TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats: use aliceStats and close the function

This test also lacks a terminating } and declares aliceStats without ever asserting on it, which will fail to compile and leaves the main behavior (epoch stats created for non‑trading referrer) untested.

You likely want to:

  • Assert that the sole stats entry is for Alice.
  • Assert that Alice’s referred volume in epoch stats matches her global UserStats.
  • Optionally assert your chosen behavior for taker/maker notionals when a user only has referred volume but no prior epoch stats.

For example:

  // Verify Alice was added to epoch stats even though she wasn't trading
  postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
  require.NotNil(t, postUpgradeEpochStats)
  require.Len(t, postUpgradeEpochStats.Stats, 1,
    "Alice should be added to epoch stats because she has referred volume")
 
- aliceStats := postUpgradeEpochStats.Stats[0]
+ aliceStats := postUpgradeEpochStats.Stats[0]
+ require.Equal(t, constants.AliceAccAddress.String(), aliceStats.User)
+ require.Equal(
+   t,
+   uint64(1_000_000_000),
+   aliceStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums,
+   "expected referred volume to be migrated into epoch stats for Alice",
+ )
+
+ // If the migration is supposed to keep epoch notionals at zero for non-trading users,
+ // add these checks (otherwise adjust to the intended behavior):
+ // require.Equal(t, uint64(0), aliceStats.Stats.TakerNotional)
+ // require.Equal(t, uint64(0), aliceStats.Stats.MakerNotional)
+}

This will both fix the compilation issue and make the “no prior epoch stats” scenario a meaningful regression test for the migration.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats(t *testing.T) {
tApp := testapp.NewTestAppBuilder(t).Build()
tApp.InitChain()
statsKeeper := tApp.App.StatsKeeper
epochsKeeper := tApp.App.EpochsKeeper
// Advance to next epoch
ctx := tApp.AdvanceToBlock(10, testapp.AdvanceToBlockOptions{})
// Get current epoch directly from keeper instead of using containertest.Query
statsEpochInfo := epochsKeeper.MustGetStatsEpochInfo(ctx)
currentEpoch := statsEpochInfo.CurrentEpoch
// Setup: Create user stats with referred volume but no epoch stats
userStats := &statstypes.UserStats{
TakerNotional: 100,
MakerNotional: 200,
Affiliate_30DReferredVolumeQuoteQuantums: 1_000_000_000,
}
statsKeeper.SetUserStats(ctx, constants.AliceAccAddress.String(), userStats)
// Verify no epoch stats exist initially
preUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.Nil(t, preUpgradeEpochStats)
// Run the migration function directly
v_9_5.Migrate30dReferredVolumeToEpochStats(ctx, statsKeeper, epochsKeeper)
// Verify Alice was added to epoch stats even though she wasn't trading
postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.NotNil(t, postUpgradeEpochStats)
require.Len(t, postUpgradeEpochStats.Stats, 1,
"Alice should be added to epoch stats because she has referred volume")
aliceStats := postUpgradeEpochStats.Stats[0]
func TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats(t *testing.T) {
tApp := testapp.NewTestAppBuilder(t).Build()
tApp.InitChain()
statsKeeper := tApp.App.StatsKeeper
epochsKeeper := tApp.App.EpochsKeeper
// Advance to next epoch
ctx := tApp.AdvanceToBlock(10, testapp.AdvanceToBlockOptions{})
// Get current epoch directly from keeper instead of using containertest.Query
statsEpochInfo := epochsKeeper.MustGetStatsEpochInfo(ctx)
currentEpoch := statsEpochInfo.CurrentEpoch
// Setup: Create user stats with referred volume but no epoch stats
userStats := &statstypes.UserStats{
TakerNotional: 100,
MakerNotional: 200,
Affiliate_30DReferredVolumeQuoteQuantums: 1_000_000_000,
}
statsKeeper.SetUserStats(ctx, constants.AliceAccAddress.String(), userStats)
// Verify no epoch stats exist initially
preUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.Nil(t, preUpgradeEpochStats)
// Run the migration function directly
v_9_5.Migrate30dReferredVolumeToEpochStats(ctx, statsKeeper, epochsKeeper)
// Verify Alice was added to epoch stats even though she wasn't trading
postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch)
require.NotNil(t, postUpgradeEpochStats)
require.Len(t, postUpgradeEpochStats.Stats, 1,
"Alice should be added to epoch stats because she has referred volume")
aliceStats := postUpgradeEpochStats.Stats[0]
require.Equal(t, constants.AliceAccAddress.String(), aliceStats.User)
require.Equal(
t,
uint64(1_000_000_000),
aliceStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums,
"expected referred volume to be migrated into epoch stats for Alice",
)
// If the migration is supposed to keep epoch notionals at zero for non-trading users,
// add these checks (otherwise adjust to the intended behavior):
// require.Equal(t, uint64(0), aliceStats.Stats.TakerNotional)
// require.Equal(t, uint64(0), aliceStats.Stats.MakerNotional)
}
🤖 Prompt for AI Agents
In protocol/app/upgrades/v9.5/upgrade_container_test.go around lines 90–125, the
test TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats is missing the
closing brace and never asserts on the declared aliceStats; fix it by closing
the function and adding assertions that the single epoch stat entry belongs to
Alice (compare account/address), that Alice’s referred-volume field in the epoch
stat equals the 1_000_000_000 value set on her global UserStats, and assert
expected taker/maker notionals (e.g., zero) to fully validate the migration
behavior for a non‑trading referrer. Ensure you use
constants.AliceAccAddress.String() for the identity check and then close the
function with a trailing }.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (1)

164-165: Critical: Missing closing brace and assertions for TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats.

The function at lines 129-164 is incomplete. Line 164 declares aliceStats but never uses it, and there's no closing } before TestStateUpgrade begins on line 165. This will not compile.

 	aliceStats := postUpgradeEpochStats.Stats[0]
+	require.Equal(t, constants.AliceAccAddress.String(), aliceStats.User)
+	require.Equal(t, uint64(1_000_000_000), aliceStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums,
+		"Alice's referred volume should be migrated")
+	require.Equal(t, uint64(0), aliceStats.Stats.TakerNotional,
+		"Alice's taker notional should be 0 (epoch stats were empty)")
+	require.Equal(t, uint64(0), aliceStats.Stats.MakerNotional,
+		"Alice's maker notional should be 0 (epoch stats were empty)")
+}
+
 func TestStateUpgrade(t *testing.T) {
🧹 Nitpick comments (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (1)

183-192: Empty helper functions leave container test without assertions.

The helper functions have comments indicating intent but no implementation. For a meaningful container test of the v9.5 upgrade, these should:

  • preUpgradeSetups: Set up user stats with referred volume via gRPC/transactions
  • preUpgradeChecks: Verify pre-upgrade state (epoch stats without referred volume)
  • postUpgradeChecks: Query epoch stats and verify the migration populated referred volume correctly

Would you like me to help implement these helper functions based on the migration logic in Migrate30dReferredVolumeToEpochStats?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dce8636 and bc85147.

📒 Files selected for processing (1)
  • protocol/app/upgrades/v9.5/upgrade_container_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2597
File: indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql:16-20
Timestamp: 2024-11-22T18:12:04.606Z
Learning: Avoid suggesting changes to deprecated functions such as `dydx_update_perpetual_v1_handler` in `indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql` if they are unchanged in the PR.
🧬 Code graph analysis (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (4)
protocol/testutil/constants/addresses.go (4)
  • AliceAccAddress (10-10)
  • BobAccAddress (11-11)
  • CarlAccAddress (12-12)
  • DaveAccAddress (13-13)
protocol/app/upgrades/v9.5/upgrade.go (1)
  • Migrate30dReferredVolumeToEpochStats (19-100)
protocol/testing/containertest/testnet_utils.go (1)
  • UpgradeTestnet (24-66)
protocol/testing/containertest/node.go (1)
  • Node (35-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: build
  • GitHub Check: golangci-lint
  • GitHub Check: liveness-test
  • GitHub Check: test-coverage-upload
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: benchmark
  • GitHub Check: container-tests
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: build-and-push-testnet
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (2)
protocol/app/upgrades/v9.5/upgrade_container_test.go (2)

16-127: LGTM!

This test is comprehensive and well-structured. It properly validates:

  • Migration of referred volume for existing epoch stats users (Alice, Bob)
  • Preservation of existing taker/maker notional values
  • Addition of users with referred volume but no prior epoch stats (Carl)
  • Exclusion of users with zero referred volume (Dave)

165-181: Container test scaffolding is in place but lacks assertions.

The TestStateUpgrade function correctly orchestrates the upgrade flow using containertest.UpgradeTestnet, but the helper functions (preUpgradeSetups, preUpgradeChecks, postUpgradeChecks) are empty. This means the test will pass but won't verify any upgrade behavior.

Is this intentional as a placeholder for future implementation, or should assertions be added for the referred volume migration?

import (
"testing"

"github.com/gogo/protobuf/proto"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused import proto.

The proto package is imported but never used in this file, which will cause a compilation error.

-	"github.com/gogo/protobuf/proto"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"github.com/gogo/protobuf/proto"
🤖 Prompt for AI Agents
In protocol/app/upgrades/v9.5/upgrade_container_test.go around line 6, the
import "github.com/gogo/protobuf/proto" is unused and causes a compilation
error; remove that import from the file's import block (or if it was intended
for side-effects, replace with the appropriate usage or a blank identifier
import), then run go vet/build to confirm the unused import is gone.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
protocol/app/upgrades/v9.5/upgrade_container_test.go (2)

15-125: Well‑scoped coverage of migration behavior; consider a couple of small refinements

This test does a good job of exercising the main migration paths: existing epoch stats (Alice/Bob), a non‑trading user with referred volume (Carl), and a user with zero referred volume (Dave). It also correctly asserts preservation of taker/maker notionals for users that already have epoch stats, and zero notionals for newly created epoch stats.

Two small, optional refinements you might consider:

  • Clarify the inline comments for the referredVolume values in testUsers (Lines 57–60): the literals (e.g., 1_000_000_000) don’t really correspond to “1k volume” / “5k volume” in human units, so the comments could just say “sample volume” or reflect the actual scale.
  • If you care about the post‑migration state of global UserStats, you could add an assertion (or a follow‑up test) that documents whether Affiliate_30DReferredVolumeQuoteQuantums is expected to remain unchanged or be cleared after migration; right now the test is intentionally silent on that contract.

These are non‑blocking; the current test meaningfully guards the migration logic.


173-200: TestStateUpgrade currently only checks upgrade success; consider fleshing out pre/post checks

Right now TestStateUpgrade plus preUpgradeSetups / preUpgradeChecks / postUpgradeChecks effectively assert only that the v9.5 upgrade proposal can be submitted, executed, and that the node stays healthy. That’s still useful as a smoke test, but it doesn’t validate the referred‑volume migration in a full containerized network.

If you want this to be a stronger regression test for v9.5 later, consider:

  • In preUpgradeSetups, seeding on‑chain state so some users have non‑zero 30d referred volume (and some don’t).
  • In preUpgradeChecks, asserting pre‑upgrade epoch stats don’t yet contain that referred volume.
  • In postUpgradeChecks, querying epoch stats via RPC and asserting that referred volume has been migrated as in the in‑process tests (including behavior for non‑trading users and zero‑volume users).

This can be deferred; the current skeleton is acceptable as a basic upgrade flow test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc85147 and 19286e4.

📒 Files selected for processing (1)
  • protocol/app/upgrades/v9.5/upgrade_container_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2597
File: indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql:16-20
Timestamp: 2024-11-22T18:12:04.606Z
Learning: Avoid suggesting changes to deprecated functions such as `dydx_update_perpetual_v1_handler` in `indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql` if they are unchanged in the PR.
📚 Learning: 2025-05-24T00:45:00.519Z
Learnt from: anmolagrawal345
Repo: dydxprotocol/v4-chain PR: 2837
File: proto/dydxprotocol/clob/order.proto:4-4
Timestamp: 2025-05-24T00:45:00.519Z
Learning: In the dydxprotocol/v4-chain repository, cosmos_proto dependencies are managed through buf.yaml configuration rather than being physically present in the repository. The import "cosmos_proto/cosmos.proto" is a standard pattern used across 28+ proto files for cosmos address annotations like (cosmos_proto.scalar) = "cosmos.AddressString".

Applied to files:

  • protocol/app/upgrades/v9.5/upgrade_container_test.go
🧬 Code graph analysis (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (3)
protocol/testutil/constants/addresses.go (4)
  • AliceAccAddress (10-10)
  • BobAccAddress (11-11)
  • CarlAccAddress (12-12)
  • DaveAccAddress (13-13)
protocol/app/upgrades/v9.5/upgrade.go (1)
  • Migrate30dReferredVolumeToEpochStats (19-100)
protocol/testing/containertest/testnet_utils.go (1)
  • UpgradeTestnet (24-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: build
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: test-coverage-upload
  • GitHub Check: test-race
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: liveness-test
  • GitHub Check: golangci-lint
  • GitHub Check: benchmark
  • GitHub Check: container-tests
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: build-and-push-testnet
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (1)
protocol/app/upgrades/v9.5/upgrade_container_test.go (1)

128-171: Empty‑epoch scenario test looks solid and matches migration contract

This test cleanly captures the “no existing epoch stats” case: it ensures that a user with only global referred volume (Alice) gets an epoch‑level entry created with the migrated referred volume and zero taker/maker notionals. That matches the described behavior of Migrate30dReferredVolumeToEpochStats for non‑trading referrers.

No issues from my side here.

Comment thread protocol/app/upgrades/v9.5/upgrade.go
Comment thread protocol/app/upgrades/v9.5/upgrade.go Outdated
Comment thread protocol/app/upgrades/v9.5/upgrade.go Outdated
Comment thread protocol/app/upgrades/v9.5/upgrade.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
protocol/app/upgrades/v9.5/upgrade.go (3)

60-76: Defensive guard for potentially nil Stats on existing epoch entries

When epochUserStats already exists (Line 62), the code assumes epochUserStats.Stats is non‑nil before incrementing Affiliate_30DReferredVolumeQuoteQuantums (Line 75). If any historical data were written with a nil Stats pointer, this would panic at upgrade time.

Even if current invariants guarantee Stats != nil, a small defensive guard keeps this migration robust against legacy/partial data:

-    } else {
-        // User already in epoch stats, add the referred volume
-        epochUserStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums += referredVolume
-    }
+    } else {
+        // User already in epoch stats, add the referred volume
+        if epochUserStats.Stats == nil {
+            epochUserStats.Stats = &statstypes.UserStats{}
+        }
+        epochUserStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums += referredVolume
+    }

80-86: Per‑address Info logs may be excessively verbose on large mainnet sets

Logging an Info line for every migrated address (including the running count and volume) could generate a very large number of log lines during upgrade on mainnet, which may bloat node logs and slow I/O.

Consider either:

  • lowering this to Debug (if available in your logger setup), or
  • sampling (e.g., log every Nth address), or
  • aggregating into a small number of summary logs.

116-125: “Successfully completed … Upgrade” log emitted before RunMigrations result

The handler logs "Successfully completed %s Upgrade" (Lines 123–123) before calling mm.RunMigrations (Line 125). If RunMigrations returns an error, logs will still say the upgrade completed successfully, which is misleading for operators.

Consider restructuring to log success only after migrations complete, and optionally log on error:

-    // Migrate 30d referred volume to epoch stats
-    Migrate30dReferredVolumeToEpochStats(sdkCtx, statsKeeper, epochsKeeper)
-
-    sdkCtx.Logger().Info(fmt.Sprintf("Successfully completed %s Upgrade", UpgradeName))
-
-    return mm.RunMigrations(ctx, configurator, vm)
+    // Migrate 30d referred volume to epoch stats
+    Migrate30dReferredVolumeToEpochStats(sdkCtx, statsKeeper, epochsKeeper)
+
+    newVM, err := mm.RunMigrations(ctx, configurator, vm)
+    if err != nil {
+        sdkCtx.Logger().Error(fmt.Sprintf("Error during %s Upgrade: %v", UpgradeName, err))
+        return nil, err
+    }
+
+    sdkCtx.Logger().Info(fmt.Sprintf("Successfully completed %s Upgrade", UpgradeName))
+    return newVM, nil
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb3861e and f5e243b.

📒 Files selected for processing (1)
  • protocol/app/upgrades/v9.5/upgrade.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2597
File: indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql:16-20
Timestamp: 2024-11-22T18:12:04.606Z
Learning: Avoid suggesting changes to deprecated functions such as `dydx_update_perpetual_v1_handler` in `indexer/services/ender/src/scripts/handlers/dydx_update_perpetual_v1_handler.sql` if they are unchanged in the PR.
🧬 Code graph analysis (1)
protocol/app/upgrades/v9.5/upgrade.go (4)
protocol/x/stats/keeper/keeper.go (1)
  • Keeper (23-30)
protocol/mocks/Configurator.go (1)
  • Configurator (15-17)
protocol/lib/context.go (1)
  • UnwrapSDKContext (31-55)
protocol/app/upgrades/v9.5/constants.go (1)
  • UpgradeName (9-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: build-and-push-testnet
  • GitHub Check: benchmark
  • GitHub Check: golangci-lint
  • GitHub Check: test-coverage-upload
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: liveness-test
  • GitHub Check: build
  • GitHub Check: test-race
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: container-tests
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (1)
protocol/app/upgrades/v9.5/upgrade.go (1)

89-98: Deterministic rebuild from userStatsMap looks good

Sorting keys before rebuilding epochStats.Stats (Lines 89–98) ensures deterministic ordering and avoids map‑iteration nondeterminism in state hashes. This is the right pattern for Cosmos SDK state migrations.

@jusbar23
Copy link
Copy Markdown
Contributor Author

jusbar23 commented Dec 2, 2025

@mergify backport release/protocol/v9.5.x

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Dec 2, 2025

backport release/protocol/v9.5.x

✅ Backports have been created

Details

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

3 participants