Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions protocol/app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package app
import (
"fmt"

v_9_3 "github.com/dydxprotocol/v4-chain/protocol/app/upgrades/v9.3"
v_9_4 "github.com/dydxprotocol/v4-chain/protocol/app/upgrades/v9.4"

upgradetypes "cosmossdk.io/x/upgrade/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -14,21 +14,22 @@ var (
// `Upgrades` defines the upgrade handlers and store loaders for the application.
// New upgrades should be added to this slice after they are implemented.
Upgrades = []upgrades.Upgrade{
v_9_3.Upgrade,
v_9_4.Upgrade,
}
Forks = []upgrades.Fork{}
)

// setupUpgradeHandlers registers the upgrade handlers to perform custom upgrade
// logic and state migrations for software upgrades.
func (app *App) setupUpgradeHandlers() {
if app.UpgradeKeeper.HasHandler(v_9_3.UpgradeName) {
panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_9_3.UpgradeName))
if app.UpgradeKeeper.HasHandler(v_9_4.UpgradeName) {
panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_9_4.UpgradeName))
}
app.UpgradeKeeper.SetUpgradeHandler(
v_9_3.UpgradeName,
v_9_3.CreateUpgradeHandler(
v_9_4.UpgradeName,
v_9_4.CreateUpgradeHandler(
app.ModuleManager,
app.AffiliatesKeeper,
app.configurator,
),
)
Expand Down
37 changes: 0 additions & 37 deletions protocol/app/upgrades/v9.3/upgrade_container_test.go

This file was deleted.

82 changes: 82 additions & 0 deletions protocol/app/upgrades/v9.4/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package v_9_4

import (
store "cosmossdk.io/store/types"
"github.com/dydxprotocol/v4-chain/protocol/app/upgrades"
affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types"
)

const (
UpgradeName = "v9.4"
)

var (
Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
StoreUpgrades: store.StoreUpgrades{},
}

PreviousAffilliateTiers = affiliatetypes.AffiliateTiers{
Tiers: []affiliatetypes.AffiliateTiers_Tier{
{
ReqReferredVolumeQuoteQuantums: 0,
ReqStakedWholeCoins: 0,
TakerFeeSharePpm: 50_000,
},
{
ReqReferredVolumeQuoteQuantums: 1_000_000_000_000, // 1M volume
ReqStakedWholeCoins: 200,
TakerFeeSharePpm: 100_000,
},
{
ReqReferredVolumeQuoteQuantums: 5_000_000_000_000, // 5M volume
ReqStakedWholeCoins: 1_000,
TakerFeeSharePpm: 125_000,
},
{
ReqReferredVolumeQuoteQuantums: 25_000_000_000_000, // 25M volume
ReqStakedWholeCoins: 5_000,
TakerFeeSharePpm: 150_000,
},
},
}

PreviousAffiliateParameters = affiliatetypes.AffiliateParameters{
Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums: 1_000_000_000_000, // 10M volume
RefereeMinimumFeeTierIdx: 1,
Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 1_000_000_000, // 10k volume
}
Comment on lines +45 to +48
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify units and indices in AffiliateParameters.

  • Comment-to-value mapping for “10M volume”/“10k volume” looks off vs the magnitudes used elsewhere (e.g., genesis and asset exponents).
  • RefereeMinimumFeeTierIdx = 2: confirm this index is valid against the configured fee tiers (0/1-based?) to avoid OOB lookups.

Run this to locate definitions and usages:

Also applies to: 67-71


🏁 Script executed:

#!/bin/bash
# Inspect params fields and typical ranges/usages.
rg -n -C3 'AffiliateParameters|RefereeMinimumFeeTierIdx|Maximum_30D(AffiliateRevenue|AttributableVolume)' --type go --type proto

# Cross-check fee tier indices.
rg -n -C3 'MsgUpdatePerpetualFeeParams|feetiers.*tiers|FeeTierIdx' --type go --type proto

Length of output: 59


I need to correct the shell script—rg doesn't recognize "proto" as a valid file type. Let me search more broadly to find the AffiliateParameters definition, fee tier configuration, and verify the indices and magnitudes.


🏁 Script executed:

#!/bin/bash
# Search without --type proto, broader search for Go files
rg -n -C3 'AffiliateParameters|RefereeMinimumFeeTierIdx|Maximum_30D' --type go

# Search for fee tier definitions and indices
rg -n -C3 'FeeTier|feetiers' --type go | head -100

# Also check proto files without specifying type
fd -e proto -x rg -l 'AffiliateParameters|RefereeMinimumFeeTierIdx'

Length of output: 129859


I found critical issues. Let me search for fee tier configuration to verify the index validity.


🏁 Script executed:

#!/bin/bash
# Find fee tier configuration and array definitions
rg -n -A20 'PerpetualFeeParams|Tiers.*\[' --type go | grep -A20 'Tiers'

# Look for fee tier bounds checking or index validation
rg -n 'RefereeMinimumFeeTierIdx.*index|len.*tier|bounds|OOB' --type go

# Check affiliates.proto for field definitions
cat proto/dydxprotocol/affiliates/affiliates.proto 2>/dev/null | grep -A5 -B5 'referee_minimum'

Length of output: 262144


Fix unit comments and fee‑tier index in AffiliateParameters (upgrade constants)

  • protocol/app/upgrades/v9.4/constants.go:35–38 (PreviousAffiliateParameters) — numeric/comment mismatch: 1_000_000_000_000 is commented "10M" but equals 1,000,000 USDC (1M) if quote quantums = 1e6; 1_000_000_000 is commented "10k" but equals 1,000 USDC (1k). Update comments or scale literals to match intended values.
  • RefereeMinimumFeeTierIdx inconsistency: PreviousAffiliateParameters sets 1 while DefaultAffiliateParameters and affiliatetypes.DefaultAffiliateParameters use 2 and tests call index 2 to select tier 3 (fee tiers are 0‑based). Confirm intended starting tier and make PreviousAffiliateParameters match (likely set to 2) or document deliberate divergence.
  • protocol/app/upgrades/v9.4/constants.go:67–71 (DefaultAffiliateParameters) vs protocol/x/affiliates/types/constants.go:34–38 — defaults/magnitudes differ (e.g., attributable volume 10_000_000_000 vs 100_000_000_000_000). Align the upgrade defaults with the module defaults or justify differences.
🤖 Prompt for AI Agents
In protocol/app/upgrades/v9.4/constants.go around lines 35–38, update
PreviousAffiliateParameters so numeric literals and inline comments match
intended magnitudes (either adjust the numeric literals to represent the
commented USDC volumes given quote quantum=1e6, or update comments to reflect
the current literal values); set RefereeMinimumFeeTierIdx to match the rest of
the codebase (change from 1 to 2) so tests and DefaultAffiliateParameters select
the same fee tier; and reconcile DefaultAffiliateParameters at lines ~67–71 with
protocol/x/affiliates/types/constants.go by aligning attributable/affiliate
volume magnitudes or add a clear comment documenting why the upgrade defaults
intentionally differ.

Comment on lines +44 to +48
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Align numeric literals with comments and units (quote quantums).

“10M volume” vs 1_000_000_000_000 and “10k volume” vs 1_000_000_000 are inconsistent if 1 unit = 1e6 quote quantums.

Pick one:

  • Adjust numbers to match comments:
- Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums:   1_000_000_000_000, // 10M volume
+ Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums:  10_000_000_000_000, // 10M
- Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 1_000_000_000, // 10k volume
+ Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 10_000_000_000, // 10k
  • Or adjust comments to reflect current values (e.g., “1M”, “1k”).

Also, confirm RefereeMinimumFeeTierIdx = 1 is intentional given Default uses 2.


🏁 Script executed:

#!/bin/bash
# Inspect fee tier indices and ensure 0-based indexing and bounds.
rg -n 'RefereeMinimumFeeTierIdx' -C3
rg -nP '(Fee|Trading).*Tier(s)?\b.*\[' -C2 --type go

Length of output: 17977


Align numeric literals with comments or vice versa in PreviousAffiliateParameters.

The values 1_000_000_000_000 and 1_000_000_000 do not match their comments if quote quantums are scaled (1e6 base units). The new defaults correctly use 10_000_000_000_000 and 10_000_000_000 for "10M" and "10k" respectively. Either scale the previous values up by 10× or adjust comments to "1M" and "1k".

RefereeMinimumFeeTierIdx = 1 in the previous state vs 2 in the new default appears intentional as part of the upgrade migration.

🤖 Prompt for AI Agents
In protocol/app/upgrades/v9.4/constants.go around lines 44 to 48, the numeric
literals in PreviousAffiliateParameters (1_000_000_000_000 and 1_000_000_000) do
not match their "10M" and "10k" comments given quote quantums scale; update
those two values to 10_000_000_000_000 and 10_000_000_000 respectively so the
literals match the comments, and leave RefereeMinimumFeeTierIdx as 1 (no
change).


PreviousAffiliateWhitelist = affiliatetypes.AffiliateWhitelist{
Tiers: []affiliatetypes.AffiliateWhitelist_Tier{
{
Addresses: []string{
"dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4", // Carl
"dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs", // Dave
},
TakerFeeSharePpm: 200_000,
},
},
}

DefaultAffiliateTiers = affiliatetypes.AffiliateTiers{
Tiers: []affiliatetypes.AffiliateTiers_Tier{
{
ReqReferredVolumeQuoteQuantums: 0,
ReqStakedWholeCoins: 0,
TakerFeeSharePpm: 400_000,
},
{
ReqReferredVolumeQuoteQuantums: 1_000_000_000_000, // 1M volume
ReqStakedWholeCoins: 200,
TakerFeeSharePpm: 400_000,
},
},
}

DefaultAffiliateParameters = affiliatetypes.AffiliateParameters{
Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums: 10_000_000_000_000, // 10M volume
RefereeMinimumFeeTierIdx: 2,
Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 10_000_000_000, // 10k volume
}
)
74 changes: 74 additions & 0 deletions protocol/app/upgrades/v9.4/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package v_9_4

import (
"context"
"fmt"

upgradetypes "cosmossdk.io/x/upgrade/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/dydxprotocol/v4-chain/protocol/lib"

affiliatekeeper "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/keeper"
affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types"
)

func setDefaultAffiliateTiersForSlidingAffiliates(ctx sdk.Context, affiliateKeeper affiliatekeeper.Keeper) {
err := affiliateKeeper.UpdateAffiliateTiers(ctx, DefaultAffiliateTiers)

if err != nil {
panic(fmt.Sprintf("failed to set default affiliate tiers: %s", err))
}
}

func setDefaultAffiliateParameters(ctx sdk.Context, affiliateKeeper affiliatekeeper.Keeper) {
err := affiliateKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{
AffiliateParameters: DefaultAffiliateParameters,
})

if err != nil {
panic(fmt.Sprintf("failed to set default affiliate parameters: %s", err))
}
}
Comment on lines +24 to +32
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

RefereeMinimumFeeTierIdx likely out of range (will fail upgrade).

Per constants.go, DefaultAffiliateTiers defines 2 tiers (idx 0–1) while DefaultAffiliateParameters sets RefereeMinimumFeeTierIdx = 2. This mismatches and will cause UpdateAffiliateParameters to error, triggering a panic here. Add a guard and fix constants to align (preferred: set idx to 1 or add a third tier). Based on relevant_code_snippets.

Apply this guard locally to fail fast with a clear message:

 func setDefaultAffiliateParameters(ctx sdk.Context, affiliateKeeper affiliatekeeper.Keeper) {
-	err := affiliateKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{
+	// Sanity check: ensure referee tier index is within bounds of DefaultAffiliateTiers.
+	if int(DefaultAffiliateParameters.RefereeMinimumFeeTierIdx) >= len(DefaultAffiliateTiers.Tiers) {
+		panic(fmt.Sprintf(
+			"invalid RefereeMinimumFeeTierIdx=%d for %d tiers",
+			DefaultAffiliateParameters.RefereeMinimumFeeTierIdx, len(DefaultAffiliateTiers.Tiers),
+		))
+	}
+	err := affiliateKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{
 		AffiliateParameters: DefaultAffiliateParameters,
 	})
📝 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 setDefaultAffiliateParameters(ctx sdk.Context, affiliateKeeper affiliatekeeper.Keeper) {
err := affiliateKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{
AffiliateParameters: DefaultAffiliateParameters,
})
if err != nil {
panic(fmt.Sprintf("failed to set default affiliate parameters: %s", err))
}
}
func setDefaultAffiliateParameters(ctx sdk.Context, affiliateKeeper affiliatekeeper.Keeper) {
// Sanity check: ensure referee tier index is within bounds of DefaultAffiliateTiers.
if int(DefaultAffiliateParameters.RefereeMinimumFeeTierIdx) >= len(DefaultAffiliateTiers.Tiers) {
panic(fmt.Sprintf(
"invalid RefereeMinimumFeeTierIdx=%d for %d tiers",
DefaultAffiliateParameters.RefereeMinimumFeeTierIdx, len(DefaultAffiliateTiers.Tiers),
))
}
err := affiliateKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{
AffiliateParameters: DefaultAffiliateParameters,
})
if err != nil {
panic(fmt.Sprintf("failed to set default affiliate parameters: %s", err))
}
}
🤖 Prompt for AI Agents
In protocol/app/upgrades/v9.4/upgrade.go around lines 24 to 32, the code blindly
applies DefaultAffiliateParameters which sets RefereeMinimumFeeTierIdx = 2 while
DefaultAffiliateTiers only defines two tiers (indices 0–1), causing
UpdateAffiliateParameters to fail and panic; fix this by making the constants
consistent (preferred: change RefereeMinimumFeeTierIdx to 1 or add a third tier
to DefaultAffiliateTiers) and add a pre-check here that validates
RefereeMinimumFeeTierIdx is within the bounds of len(DefaultAffiliateTiers) and
returns/panics with a clear message if out of range before calling
affiliateKeeper.UpdateAffiliateParameters.


func migrateAffiliateOverrides(ctx sdk.Context, affiliateKeeper affiliatekeeper.Keeper) {
// Get all whitelist
whitelist, err := affiliateKeeper.GetAffiliateWhitelist(ctx)
if err != nil {
panic(fmt.Sprintf("failed to get affiliate whitelist: %s", err))
}
// Create overrides for all whitelist addresses
overrides := affiliatetypes.AffiliateOverrides{}
var overridesList []string
for _, addr := range whitelist.Tiers {
overridesList = append(overridesList, addr.Addresses...)
}
overrides.Addresses = overridesList
// Update affiliate overrides
err = affiliateKeeper.SetAffiliateOverrides(ctx, overrides)
if err != nil {
panic(fmt.Sprintf("failed to set affiliate overrides: %s", err))
}
}

func CreateUpgradeHandler(
mm *module.Manager,
affiliateKeeper affiliatekeeper.Keeper,
configurator module.Configurator,
) upgradetypes.UpgradeHandler {
return func(ctx context.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
sdkCtx := lib.UnwrapSDKContext(ctx, "app/upgrades")
sdkCtx.Logger().Info(fmt.Sprintf("Running %s Upgrade...", UpgradeName))

// Set default affiliate tiers and parameters.
setDefaultAffiliateTiersForSlidingAffiliates(sdkCtx, affiliateKeeper)

// Set default affiliate parameters.
setDefaultAffiliateParameters(sdkCtx, affiliateKeeper)

// Migrate affiliate overrides.
migrateAffiliateOverrides(sdkCtx, affiliateKeeper)

return mm.RunMigrations(ctx, configurator, vm)
}
}
97 changes: 97 additions & 0 deletions protocol/app/upgrades/v9.4/upgrade_container_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//go:build all || container_test

package v_9_4_test

import (
"testing"

"github.com/cosmos/gogoproto/proto"
v_9_4 "github.com/dydxprotocol/v4-chain/protocol/app/upgrades/v9.4"
"github.com/dydxprotocol/v4-chain/protocol/testing/containertest"
"github.com/dydxprotocol/v4-chain/protocol/testutil/constants"
affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types"
"github.com/stretchr/testify/require"
)

func TestStateUpgrade(t *testing.T) {
testnet, err := containertest.NewTestnetWithPreupgradeGenesis()
require.NoError(t, err, "failed to create testnet - is docker daemon running?")
err = testnet.Start()
require.NoError(t, err)
defer testnet.MustCleanUp()
node := testnet.Nodes["alice"]
nodeAddress := constants.AliceAccAddress.String()

preUpgradeSetups(node, t)
preUpgradeChecks(node, t)

err = containertest.UpgradeTestnet(nodeAddress, t, node, v_9_4.UpgradeName)
require.NoError(t, err)

postUpgradeChecks(node, t)
}

func preUpgradeSetups(node *containertest.Node, t *testing.T) {}

func preUpgradeChecks(node *containertest.Node, t *testing.T) {
// Verify affiliate tiers are set to default values
tiersResp := &affiliatetypes.AllAffiliateTiersResponse{}
resp, err := containertest.Query(
node,
affiliatetypes.NewQueryClient,
affiliatetypes.QueryClient.AllAffiliateTiers,
&affiliatetypes.AllAffiliateTiersRequest{},
)
require.NoError(t, err)
err = proto.UnmarshalText(resp.String(), tiersResp)
require.NoError(t, err)
require.Equal(t, v_9_4.PreviousAffilliateTiers, tiersResp.Tiers)
}

func postUpgradeChecks(node *containertest.Node, t *testing.T) {
// Verify affiliate tiers are set to default values
tiersResp := &affiliatetypes.AllAffiliateTiersResponse{}
resp, err := containertest.Query(
node,
affiliatetypes.NewQueryClient,
affiliatetypes.QueryClient.AllAffiliateTiers,
&affiliatetypes.AllAffiliateTiersRequest{},
)
require.NoError(t, err)
err = proto.UnmarshalText(resp.String(), tiersResp)
require.NoError(t, err)
require.Equal(t, v_9_4.DefaultAffiliateTiers, tiersResp.Tiers)

// Verify affiliate parameters are set to default values
paramsResp := &affiliatetypes.AffiliateParametersResponse{}
resp, err = containertest.Query(
node,
affiliatetypes.NewQueryClient,
affiliatetypes.QueryClient.AffiliateParameters,
&affiliatetypes.AffiliateParametersRequest{},
)
require.NoError(t, err)
err = proto.UnmarshalText(resp.String(), paramsResp)
require.NoError(t, err)
require.Equal(t, v_9_4.DefaultAffiliateParameters, paramsResp.Parameters)

// Verify affiliate overrides were migrated from whitelist
overridesResp := &affiliatetypes.AffiliateOverridesResponse{}
resp, err = containertest.Query(
node,
affiliatetypes.NewQueryClient,
affiliatetypes.QueryClient.AffiliateOverrides,
&affiliatetypes.AffiliateOverridesRequest{},
)
require.NoError(t, err)
err = proto.UnmarshalText(resp.String(), overridesResp)
require.NoError(t, err)
// Overrides should contain addresses from the pre-upgrade whitelist
expectedOverrides := affiliatetypes.AffiliateOverrides{
Addresses: []string{
"dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4", // Carl
"dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs", // Dave
},
}
require.Equal(t, expectedOverrides, overridesResp.Overrides)
}
2 changes: 1 addition & 1 deletion protocol/scripts/genesis/sample_pregenesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -4102,7 +4102,7 @@
]
}
},
"app_version": "9.0.0-64-g531bff2de",
"app_version": "9.0.0-49-g33a4abb2f",
"chain_id": "dydx-sample-1",
"consensus": {
"params": {
Expand Down
2 changes: 1 addition & 1 deletion protocol/testing/version/VERSION_CURRENT
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v9.3
v9.4
2 changes: 1 addition & 1 deletion protocol/testing/version/VERSION_FULL_NAME_PREUPGRADE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v9.2.1
v9.3
2 changes: 1 addition & 1 deletion protocol/testing/version/VERSION_PREUPGRADE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v9.2
v9.3
Loading