Skip to content

Add market fee discount to protocol#3130

Merged
davidli1997 merged 25 commits intomainfrom
davidli/fee_discount_campaign
Oct 17, 2025
Merged

Add market fee discount to protocol#3130
davidli1997 merged 25 commits intomainfrom
davidli/fee_discount_campaign

Conversation

@davidli1997
Copy link
Contributor

@davidli1997 davidli1997 commented Oct 8, 2025

Changelist

This PR implements a Market Fee Discount feature that allows governance to configure flexible fee discount periods for specific markets/CLOB pairs. During an active discount period, fees can be reduced by any percentage (from 0% to 100% of normal fees) for designated markets during specified time periods.

Detailed tech spec

Key Changes:

  • Proto definitions for PerMarketFeeDiscountParams message with charge_ppm field for flexible discount percentages
  • State management functions to get/set/list market fee discounts by CLOB pair
  • Governance message handler (MsgSetMarketFeeDiscountParams) with validation for time ranges, CLOB pair existence, and charge_ppm limits
  • gRPC query handlers to retrieve market fee discount configurations
  • Fee calculation integration: GetPerpetualFeePpm() now checks for active fee discounts and applies proportional discount based on charge_ppm
  • CLOB module updates: Pass clobPairId to fee calculation function
  • TypeScript client codegen for indexer integration

Breaking Changes:

  • GetPerpetualFeePpm() signature updated to include clobPairId parameter (all callers updated in this PR)

Example Governance Proposal

{
  "messages": [{
    "@type": "/dydxprotocol.feetiers.MsgSetMarketFeeDiscountParamss",
    "authority": "dydx10d07y265gmmuvt4z0w9aw880jnsr700jnmapky",
    "params": [{
      "clob_pair_id": 1,
       "start_time": "2024-07-31T00:00:00Z",
       "end_time": "2024-08-07T00:00:00Z", 
      "charge_ppm": 0
    }]
  }],
  "title": "Free Trading Week for ETH-USD"
}

Test Plan

Unit Tests

  • ✅ Fee discount with charge_ppm = 0 returns zero fees (100% discount)
  • ✅ Fee discount with charge_ppm = 500000 returns 50% of normal fees
  • ✅ Fee discount with charge_ppm = 1000000 returns normal fees (no discount)
  • ✅ Expired discounts do not affect fees (normal tier fees apply)
  • ✅ Discounts are market-specific (only affect configured CLOB pairs)
  • ✅ Discount applies proportionally to both maker and taker fees
  • ✅ Discount applies to maker rebates (negative fees reduced proportionally)
  • ✅ Validation rejects charge_ppm > 1000000
  • ✅ Validation rejects invalid time ranges, non-existent CLOB pairs, excessive durations
  • ✅ Query handlers return correct data for single/all market fee discount lookups

Manual Testing on Staging

  1. Place Order WITHOUT Fee Discount
    Fill Result (BEFORE discount):
{
  "id": "6fba0e3e-bf39-54f2-9ac2-b316d25c1fe8",
  "side": "BUY",
  "liquidity": "TAKER",
  "type": "LIMIT",
  "market": "BTC-USD",
  "marketType": "PERPETUAL",
  "price": "107442",
  "size": "0.0001",
  "fee": "0.005373",
  "affiliateRevShare": "0",
  "createdAt": "2025-10-17T16:15:55.617Z",
  "createdAtHeight": "61353",
  "orderId": "4e9d5278-22f3-5902-bba1-d70853fd9c70",
  "clientMetadata": "0",
  "subaccountNumber": 0
}
  1. Submit and Pass Governance Proposal for BTC-USD
{
  "messages": [
    {
      "@type": "/dydxprotocol.feetiers.MsgSetMarketFeeDiscountParams",
      "authority": "dydx10d07y265gmmuvt4z0w9aw880jnsr700jnmapky",
      "params": [
        {
          "clob_pair_id": 0,
          "start_time": "2025-10-01T00:00:00Z",
          "end_time": "2025-10-31T00:00:00Z",
          "charge_ppm": 0
        }
      ]
    }
  ],
  "deposit": "20000000adv4tnt",
  "metadata": "",
  "title": "Test Fee Discount Campaign - 100% Off BTC-USD",
  "summary": "Testing 100% fee discount (free trading) for CLOB pair 0 (BTC-USD)",
  "expedited": true
}
  1. Verify Proposal Applied
$ ./build/dydxprotocold query feetiers get-market-fee-discount-params
params:
- charge_ppm: 0
  clob_pair_id: 0
  end_time_unix: "1767139200"
  start_time_unix: "1759363200"
  1. Place Order WITH Fee Discount
{
  "id": "86a13a26-8bb9-558c-aee3-966bedfc2b8d",
  "side": "BUY",
  "liquidity": "TAKER",
  "type": "LIMIT",
  "market": "BTC-USD",
  "marketType": "PERPETUAL",
  "price": "107382",
  "size": "0.0001",
  "fee": "0",
  "affiliateRevShare": "0",
  "createdAt": "2025-10-17T16:20:41.758Z",
  "createdAtHeight": "61613",
  "orderId": "4e9d5278-22f3-5902-bba1-d70853fd9c70",
  "clientMetadata": "0",
  "subaccountNumber": 0
}

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

    • Per-market fee discounts: new messages/tx to set discounts, gRPC/LCD queries to fetch one or all market discounts, and a CLI command to view them.
  • Improvements

    • Fee computation now applies per-market discounts to taker/maker fees.
  • Tests

    • Added unit and integration tests for validation, keeper logic, RPC/CLI queries, message handling, and fee computation.
  • Chores

    • CI workflows updated to trigger on the new branch.

@davidli1997 davidli1997 requested a review from a team as a code owner October 8, 2025 20:11
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

Walkthrough

Adds per-market fee discount support end-to-end: proto messages/RPCs, CLI and TypeScript codegen, keeper storage/validation, msg and gRPC handlers, tests, and propagates clobPairId into fee calculations; also regenerates several generated bundle exports and updates CI triggers.

Changes

Cohort / File(s) Summary
Bundle rebindings
indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts, indexer/packages/v4-protos/src/codegen/gogoproto/bundle.ts, indexer/packages/v4-protos/src/codegen/google/bundle.ts
Regenerated/rewired exported namespace spreads to different generated aliases, changing which imports are spread into public bundles.
Feetiers proto additions
proto/dydxprotocol/feetiers/per_market_fee_discount.proto, proto/dydxprotocol/feetiers/query.proto, proto/dydxprotocol/feetiers/tx.proto
New PerMarketFeeDiscountParams message; added Query RPCs PerMarketFeeDiscountParams / AllMarketFeeDiscountParams; added Msg SetMarketFeeDiscountParams and response.
Feetiers TypeScript codegen
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/per_market_fee_discount.ts, .../query.ts, .../query.lcd.ts, .../query.rpc.Query.ts, .../tx.ts, .../tx.rpc.msg.ts
Generated types/codecs and added LCD/RPC/tx client bindings for the new proto types and RPCs.
Keeper: storage & logic
protocol/x/feetiers/keeper/per_market_fee_discount.go, protocol/x/feetiers/keeper/keeper.go
New keeper methods for Get/Set/GetAll per-market discounts, GetDiscountedPpm logic, and GetPerpetualFeePpm updated to accept clobPairId and apply discounts.
Keeper: gRPC & Msg server
protocol/x/feetiers/keeper/grpc_query.go, protocol/x/feetiers/keeper/msg_server.go
gRPC query handlers and Msg server handler for SetMarketFeeDiscountParams added with validation and persistence.
Types, keys, errors & validation
protocol/x/feetiers/types/per_market_fee_discount.go, protocol/x/feetiers/types/errors.go, protocol/x/feetiers/types/keys.go, protocol/x/feetiers/types/types.go
Validation, constants (MaxFeeDiscountDuration, MaxChargePpm), new error vars, storage prefix, and FeeTiersKeeper interface extended (per-market methods and clobPairId in GetPerpetualFeePpm).
CLOB integration (call sites & interfaces)
protocol/x/clob/keeper/mev.go, protocol/x/clob/keeper/orders.go, protocol/x/clob/keeper/process_single_match.go, protocol/x/clob/types/expected_keepers.go
Updated call sites and expected keeper signatures to pass clobPairId into GetPerpetualFeePpm.
App wiring & registries
protocol/app/msgs/all_msgs.go, protocol/app/msgs/internal_msgs.go, protocol/app/msgs/internal_msgs_test.go, protocol/lib/ante/internal_msg.go
Registered new Msg type entries and internal mappings; updated tests/registries to include new feetiers message keys.
CLI and tests
protocol/x/feetiers/client/cli/query.go, protocol/x/feetiers/client/cli/query_test.go, protocol/x/feetiers/keeper/grpc_query_test.go, protocol/x/feetiers/keeper/msg_server_test.go, protocol/x/feetiers/keeper/keeper_test.go, protocol/x/feetiers/keeper/per_market_fee_discount_test.go, protocol/x/feetiers/types/per_market_fee_discount_test.go, protocol/x/vault/keeper/vault_test.go
Added CLI query command for market fee discount params; new and updated tests covering validation, keeper, msg server, gRPC queries, integration, and call-site signature updates.
CI workflow triggers
.github/workflows/*.yml
Added branch pattern davidli/fee_discount_campaign to multiple CI workflow push triggers.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI
  participant QClient as Query Client
  participant Keeper as FeeTiers Keeper
  Note over User,Keeper: Query per-market fee discount params
  User->>CLI: dydx feetiers market-fee-discount-params [clob_pair_id]
  CLI->>QClient: PerMarketFeeDiscountParams(clobPairId?) / AllMarketFeeDiscountParams()
  QClient->>Keeper: PerMarketFeeDiscountParams / AllMarketFeeDiscountParams
  alt clob_pair_id provided
    Keeper-->>QClient: params or NotFound
  else no arg
    Keeper-->>QClient: [params...]
  end
  QClient-->>CLI: response
  CLI-->>User: print JSON
Loading
sequenceDiagram
  autonumber
  actor Gov as Gov Authority
  participant TxClient as Msg Client
  participant MsgSrv as feetiers.Msg
  participant Keeper as FeeTiers Keeper
  Note over Gov,Keeper: Set per-market fee discount params
  Gov->>TxClient: MsgSetMarketFeeDiscountParams{authority, params[]}
  TxClient->>MsgSrv: SetMarketFeeDiscountParams(request)
  MsgSrv->>Keeper: Validate each params (time, charge)
  alt any invalid
    MsgSrv-->>Gov: error (ErrInvalidTimeRange / ErrInvalidChargePpm)
  else all valid
    MsgSrv->>Keeper: SetPerMarketFeeDiscountParams for each
    MsgSrv-->>Gov: MsgSetMarketFeeDiscountParamsResponse{}
  end
Loading
sequenceDiagram
  autonumber
  participant CLOB as CLOB Keeper
  participant Feetiers as FeeTiers Keeper
  Note over CLOB,Feetiers: Fee calculation with per-market discount
  CLOB->>Feetiers: GetPerpetualFeePpm(ctx, address, isTaker, overrideIdx, clobPairId)
  Feetiers->>Feetiers: baseFee = taker/maker tier
  Feetiers->>Feetiers: discountPpm = GetDiscountedPpm(clobPairId)
  Feetiers-->>CLOB: discountedFee = baseFee * discountPpm / MaxChargePpm
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • teddyding
  • anmolagrawal345
  • northstar456

Poem

"I hop through proto fields at night and patch each tiny gap,
Per-market discounts planted now — a carrot-coded map.
I bound the CLI and keeper paths, I test and then I nap,
Traders smile as fees adjust — hooray! 🥕🐇"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.13% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Add market fee discount to protocol" is directly and clearly related to the primary change documented in this changeset, which is the implementation of a market fee discount feature enabling governance-controlled, time-bounded fee discounts for specific CLOB pairs. The title is concise, readable, and avoids noise or vague terminology. A teammate scanning the repository history would immediately understand that this PR introduces fee discount functionality to the protocol.
Description Check ✅ Passed The pull request description is comprehensive and well-structured. It includes all required sections from the template: a detailed Changelist section describing the Market Fee Discount feature with key changes and breaking changes clearly noted, a Test Plan section with both unit tests (with specific test cases like fee discount with charge_ppm = 0 and validation scenarios) and manual testing evidence from staging, and the Author/Reviewer Checklist with all appropriate checkboxes provided. The description goes beyond minimum requirements by including a technical spec link, example governance proposal JSON, and actual order fill results demonstrating the feature working correctly before and after discount application.
✨ 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 davidli/fee_discount_campaign

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
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 (3)
protocol/x/feetiers/types/fee_discount_campaign.go (1)

8-8: Minor terminology inconsistency in comment.

The comment refers to "fee holiday" but the feature is called "fee discount campaign" throughout the codebase. Consider updating the comment for consistency.

-	// Maximum duration for a fee holiday (90 days in seconds)
+	// Maximum duration for a fee discount campaign (90 days in seconds)
proto/dydxprotocol/feetiers/fee_discount_campaign.proto (1)

1-22: Add trailing newline for consistency.

The proto definition is well-structured with clear documentation. However, the file is missing a trailing newline.

Add a newline at the end of the file:

   uint32 charge_ppm = 4;
 }
+
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.ts (1)

102-102: Consider type aliases for empty interfaces (codegen improvement).

Static analysis correctly flags that QueryAllFeeDiscountCampaignParamsRequest and its SDK type are empty interfaces equivalent to {}. While this is standard for empty protobuf messages, using type aliases would be more idiomatic TypeScript:

export type QueryAllFeeDiscountCampaignParamsRequest = {};
export type QueryAllFeeDiscountCampaignParamsRequestSDKType = {};

However, since this is generated code, the fix would need to be implemented in the code generator itself.

Also applies to: 108-108

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de62212 and c6e8f0a.

⛔ Files ignored due to path filters (4)
  • protocol/x/feetiers/types/fee_discount_campaign.pb.go is excluded by !**/*.pb.go
  • protocol/x/feetiers/types/query.pb.go is excluded by !**/*.pb.go
  • protocol/x/feetiers/types/query.pb.gw.go is excluded by !**/*.pb.gw.go
  • protocol/x/feetiers/types/tx.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (26)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts (3 hunks)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/fee_discount_campaign.ts (1 hunks)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.lcd.ts (3 hunks)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.rpc.Query.ts (5 hunks)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.ts (3 hunks)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.rpc.msg.ts (2 hunks)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.ts (3 hunks)
  • indexer/packages/v4-protos/src/codegen/gogoproto/bundle.ts (1 hunks)
  • indexer/packages/v4-protos/src/codegen/google/bundle.ts (1 hunks)
  • proto/dydxprotocol/feetiers/fee_discount_campaign.proto (1 hunks)
  • proto/dydxprotocol/feetiers/query.proto (3 hunks)
  • proto/dydxprotocol/feetiers/tx.proto (2 hunks)
  • protocol/app/msgs/all_msgs.go (1 hunks)
  • protocol/app/msgs/internal_msgs.go (1 hunks)
  • protocol/app/msgs/internal_msgs_test.go (1 hunks)
  • protocol/lib/ante/internal_msg.go (1 hunks)
  • protocol/x/feetiers/keeper/fee_discount_campaign.go (1 hunks)
  • protocol/x/feetiers/keeper/fee_discount_campaign_test.go (1 hunks)
  • protocol/x/feetiers/keeper/grpc_query.go (2 hunks)
  • protocol/x/feetiers/keeper/grpc_query_test.go (2 hunks)
  • protocol/x/feetiers/keeper/msg_server.go (1 hunks)
  • protocol/x/feetiers/keeper/msg_server_test.go (2 hunks)
  • protocol/x/feetiers/types/errors.go (1 hunks)
  • protocol/x/feetiers/types/fee_discount_campaign.go (1 hunks)
  • protocol/x/feetiers/types/keys.go (1 hunks)
  • protocol/x/feetiers/types/types.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
protocol/x/feetiers/keeper/grpc_query_test.go (2)
protocol/testutil/app/app.go (1)
  • NewTestAppBuilder (335-350)
protocol/app/app.go (1)
  • App (271-376)
protocol/x/feetiers/keeper/msg_server.go (3)
protocol/x/feetiers/keeper/keeper.go (1)
  • Keeper (18-26)
protocol/lib/context.go (1)
  • UnwrapSDKContext (31-55)
protocol/x/feetiers/types/keys.go (1)
  • ModuleName (6-6)
protocol/x/feetiers/keeper/fee_discount_campaign_test.go (4)
protocol/testutil/app/app.go (1)
  • NewTestAppBuilder (335-350)
protocol/app/app.go (1)
  • App (271-376)
protocol/x/feetiers/types/errors.go (1)
  • ErrFeeDiscountCampaignNotFound (43-47)
protocol/x/feetiers/types/fee_discount_campaign.go (1)
  • MaxChargePpm (12-12)
protocol/x/feetiers/keeper/grpc_query.go (3)
protocol/lib/context.go (1)
  • UnwrapSDKContext (31-55)
protocol/x/feetiers/types/keys.go (1)
  • ModuleName (6-6)
protocol/x/feetiers/types/errors.go (1)
  • ErrFeeDiscountCampaignNotFound (43-47)
protocol/x/feetiers/types/fee_discount_campaign.go (1)
protocol/x/feetiers/types/errors.go (2)
  • ErrInvalidTimeRange (38-42)
  • ErrInvalidChargePpm (48-52)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.lcd.ts (1)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.ts (6)
  • QueryFeeDiscountCampaignParamsRequest (62-68)
  • QueryFeeDiscountCampaignParamsRequest (311-348)
  • QueryFeeDiscountCampaignParamsResponseSDKType (94-96)
  • QueryAllFeeDiscountCampaignParamsRequest (102-102)
  • QueryAllFeeDiscountCampaignParamsRequest (399-427)
  • QueryAllFeeDiscountCampaignParamsResponseSDKType (122-124)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.rpc.msg.ts (1)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.ts (4)
  • MsgSetFeeDiscountCampaignParams (38-44)
  • MsgSetFeeDiscountCampaignParams (166-212)
  • MsgSetFeeDiscountCampaignParamsResponse (62-62)
  • MsgSetFeeDiscountCampaignParamsResponse (218-246)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.ts (1)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/fee_discount_campaign.ts (3)
  • FeeDiscountCampaignParams (8-24)
  • FeeDiscountCampaignParams (57-121)
  • FeeDiscountCampaignParamsSDKType (30-46)
protocol/x/feetiers/types/errors.go (1)
protocol/x/feetiers/types/keys.go (1)
  • ModuleName (6-6)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.ts (1)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/fee_discount_campaign.ts (3)
  • FeeDiscountCampaignParams (8-24)
  • FeeDiscountCampaignParams (57-121)
  • FeeDiscountCampaignParamsSDKType (30-46)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.rpc.Query.ts (1)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.ts (8)
  • QueryFeeDiscountCampaignParamsRequest (62-68)
  • QueryFeeDiscountCampaignParamsRequest (311-348)
  • QueryFeeDiscountCampaignParamsResponse (86-88)
  • QueryFeeDiscountCampaignParamsResponse (356-393)
  • QueryAllFeeDiscountCampaignParamsRequest (102-102)
  • QueryAllFeeDiscountCampaignParamsRequest (399-427)
  • QueryAllFeeDiscountCampaignParamsResponse (114-116)
  • QueryAllFeeDiscountCampaignParamsResponse (435-472)
protocol/x/feetiers/keeper/msg_server_test.go (1)
protocol/lib/module_addresses.go (1)
  • GovModuleAddress (10-10)
protocol/x/feetiers/keeper/fee_discount_campaign.go (4)
protocol/x/feetiers/types/keys.go (1)
  • FeeDiscountCampaignPrefix (18-18)
protocol/lib/keys.go (1)
  • Uint32ToKey (9-13)
protocol/x/feetiers/types/errors.go (1)
  • ErrFeeDiscountCampaignNotFound (43-47)
protocol/x/feetiers/types/fee_discount_campaign.go (1)
  • MaxChargePpm (12-12)
indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts (2)
indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/query.lcd.ts (1)
  • vault (31-34)
indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/query.rpc.Query.ts (2)
  • vault (53-57)
  • vault (108-110)
🪛 Biome (2.1.2)
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.ts

[error] 102-102: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


[error] 108-108: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.ts

[error] 62-62: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


[error] 68-68: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ 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). (38)
  • 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-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: (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-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: (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-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: (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: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: call-build-ecs-service-ender / (ender) Check docker image build
  • GitHub Check: call-build-ecs-service-socks / (socks) Check docker image build
  • GitHub Check: call-build-ecs-service-vulcan / (vulcan) Check docker image build
  • GitHub Check: call-build-ecs-service-roundtable / (roundtable) Check docker image build
  • GitHub Check: call-build-ecs-service-comlink / (comlink) Check docker image build
  • GitHub Check: check-build-auxo
  • GitHub Check: check-build-bazooka
  • GitHub Check: test / run_command
  • GitHub Check: container-tests
  • GitHub Check: build-and-push-testnet
  • GitHub Check: build
  • GitHub Check: benchmark
  • GitHub Check: lint
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: test-coverage-upload
  • GitHub Check: test-race
  • GitHub Check: liveness-test
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: golangci-lint
  • GitHub Check: run_command
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (38)
indexer/packages/v4-protos/src/codegen/google/bundle.ts (1)

1-16: LGTM! Mechanical codegen reorganization.

The import renumbering and bundle reorganization are standard protobuf codegen output. The addition of protobuf/any module and the renumbered spreads maintain the same public API surface.

indexer/packages/v4-protos/src/codegen/gogoproto/bundle.ts (1)

1-3: LGTM! Mechanical codegen reorganization.

The import renumbering is standard protobuf codegen output with no functional change.

protocol/lib/ante/internal_msg.go (1)

100-100: LGTM! Correct internal message registration.

The new MsgSetFeeDiscountCampaignParams type is properly added to the internal message type switch, consistent with existing patterns and correctly placed in the feetiers section.

protocol/x/feetiers/types/keys.go (1)

16-18: LGTM! Well-defined store key prefix.

The FeeDiscountCampaignPrefix constant is properly defined with clear documentation for storing fee discount campaign data.

protocol/app/msgs/internal_msgs_test.go (1)

115-116: LGTM! Test expectations properly updated.

The new feetiers message keys are correctly added to the Gov message expectations, maintaining alphabetical order and including both request and response types.

indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.ts (3)

2-2: LGTM! Correct import for new campaign params.

The import properly references the new FeeDiscountCampaignParams types from the fee_discount_campaign module.


33-68: LGTM! Standard protobuf message definitions.

The message interfaces follow the established patterns for protobuf codegen. The empty response interfaces (lines 62, 68) are standard for protobuf messages with no response fields—this is expected codegen output, not an issue.


159-246: LGTM! Correct encode/decode implementation.

The encode, decode, and fromPartial methods properly handle the params array field:

  • Line 172-174: Correctly iterates and encodes each FeeDiscountCampaignParams
  • Line 193: Properly decodes and pushes to params array
  • Line 208: Correctly maps partial objects to FeeDiscountCampaignParams instances
protocol/app/msgs/all_msgs.go (1)

221-222: LGTM! Message types properly registered.

The new feetiers message types are correctly added to the AllTypeMessages registry, consistent with existing patterns and properly placed in the feetiers section.

protocol/x/feetiers/keeper/grpc_query_test.go (4)

5-5: LGTM! Required import for time-based tests.

The time package import is necessary for setting block time in the fee discount campaign tests.


102-160: LGTM! Comprehensive test coverage for FeeDiscountCampaignParams query.

The test properly covers:

  • Success case with valid campaign retrieval
  • Nil request handling with proper error code
  • Not Found case for non-existent CLOB pair
  • Block time set before campaign creation (line 118) for time-based validation

162-246: LGTM! Excellent handling of unordered results.

The test demonstrates best practices:

  • Sets up multiple campaigns with varying discount levels (0%, 50%, 75%)
  • Uses map-based comparison (lines 229-242) to handle unordered query results
  • Verifies all campaign fields (ClobPairId, StartTimeUnix, EndTimeUnix, ChargePpm)
  • Includes nil request test case

248-263: LGTM! Proper empty state test coverage.

The test correctly verifies that querying with no campaigns returns an empty list rather than an error, which is the expected behavior for a "list all" query.

protocol/x/feetiers/keeper/msg_server.go (1)

45-82: LGTM! Well-structured governance handler.

The SetFeeDiscountCampaignParams handler follows the established pattern from UpdatePerpetualFeeParams and correctly:

  • Validates authority before processing
  • Unwraps SDK context with module name for logging
  • Validates each campaign against the current block time before persistence
  • Uses fail-fast pattern by returning on first validation or persistence error
  • Wraps errors with contextual information including the CLOB pair ID
protocol/x/feetiers/types/types.go (1)

18-25: LGTM! Clean interface extension.

The two new methods are well-defined and follow the existing interface conventions. The naming is clear and the signatures are consistent with similar methods in the keeper.

protocol/app/msgs/internal_msgs.go (1)

161-162: LGTM! Standard message registration.

The new feetiers messages are correctly registered following the established pattern for governance-authorized internal messages.

protocol/x/feetiers/keeper/msg_server_test.go (2)

85-225: LGTM! Comprehensive test coverage.

The test suite covers all critical scenarios:

  • Valid configurations (single/multiple campaigns)
  • Authority validation
  • Time range validations (end before current, start after end, excessive duration)
  • ChargePpm validation (exceeds maximum)
  • Empty params handling

The use of fixed base time (line 90-91) ensures deterministic behavior.


227-333: LGTM! Update and multi-campaign tests are thorough.

These tests properly verify:

  • Updates to existing campaigns overwrite correctly
  • Multiple campaigns can be set simultaneously
  • GetAllFeeDiscountCampaignParams returns all configured campaigns
protocol/x/feetiers/types/fee_discount_campaign.go (1)

15-39: LGTM! Validation logic is comprehensive and correct.

The validation properly checks:

  • Time range ordering (start < end)
  • Maximum duration constraint
  • ChargePpm bounds
  • Campaign hasn't already expired
protocol/x/feetiers/keeper/fee_discount_campaign_test.go (1)

12-277: LGTM! Excellent test coverage for fee discount campaign functionality.

This test suite is comprehensive and covers:

  • Basic CRUD operations (Get/Set/GetAll)
  • Error handling (ErrFeeDiscountCampaignNotFound)
  • Time-based discount eligibility across all edge cases (before start, at start, between, at end, after end)
  • Campaign updates and overwrites
  • Empty state handling (verifies non-nil empty slice)

The use of fixed base times throughout ensures deterministic, reproducible tests.

protocol/x/feetiers/types/errors.go (1)

38-52: LGTM! Error definitions are clear and properly registered.

The three new error codes (406-408) are:

  • Sequentially numbered without conflicts
  • Clearly named and descriptive
  • Properly registered using errorsmod.Register
protocol/x/feetiers/keeper/grpc_query.go (3)

5-5: LGTM! Correct import for error inspection.

The errors import is necessary for the errors.Is check on line 75.


59-84: LGTM! Well-implemented gRPC query handler.

The FeeDiscountCampaignParams handler correctly:

  • Validates nil request
  • Unwraps SDK context with module name
  • Maps ErrFeeDiscountCampaignNotFound to codes.NotFound for proper gRPC semantics
  • Returns codes.Internal for unexpected errors with context

86-104: LGTM! Clean implementation for querying all campaigns.

The AllFeeDiscountCampaignParams handler follows the established pattern and correctly handles the request-response flow.

indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.lcd.ts (1)

2-2: LGTM! Generated LCD client code follows established patterns.

The implementation correctly adds LCD query client methods for fee discount campaigns:

  • Import statement includes all necessary types
  • Constructor properly binds the new methods
  • feeDiscountCampaignParams uses path parameter interpolation for clobPairId
  • allFeeDiscountCampaignParams queries all campaigns without parameters

The code follows the same pattern as existing query methods (perpetualFeeParams, userFeeTier).

Also applies to: 14-15, 39-53

proto/dydxprotocol/feetiers/tx.proto (1)

9-9: LGTM! New RPC and message types follow Cosmos SDK patterns.

The additions are well-structured:

  • Import statement for the new proto definition
  • SetFeeDiscountCampaignParams RPC follows the same pattern as UpdatePerpetualFeeParams
  • MsgSetFeeDiscountCampaignParams correctly includes authority field with signer option
  • repeated FeeDiscountCampaignParams with gogoproto.nullable = false is appropriate for batch operations
  • Empty response message follows standard practice

Also applies to: 17-53

indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.ts (2)

2-2: LGTM! Generated query type definitions are correct.

The new query types are properly structured:

  • Import statement includes the new FeeDiscountCampaignParams types
  • Four new interfaces (Request/Response pairs with SDK types) follow protobuf conventions
  • Field types are correct (clobPairId: number, optional params, array of params)

Also applies to: 57-124


305-472: LGTM! Encode/decode implementations are correct.

The protobuf encode/decode/fromPartial implementations for all four query types follow standard patterns:

  • Proper handling of default values (0 for numbers, undefined for optional objects)
  • Correct protobuf wire types and tags
  • Array handling in QueryAllFeeDiscountCampaignParamsResponse
  • Delegation to FeeDiscountCampaignParams codec for nested messages
indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/tx.rpc.msg.ts (1)

3-3: LGTM! Generated RPC client implementation follows established patterns.

The new setFeeDiscountCampaignParams method is correctly implemented:

  • Import includes both request and response types
  • Interface method signature is properly typed with Promise return
  • Constructor binding ensures correct this context
  • Implementation follows standard pattern: encode → RPC request → decode response

Also applies to: 9-14, 22-22, 31-35

protocol/x/feetiers/keeper/fee_discount_campaign.go (4)

14-30: LGTM! GetFeeDiscountCampaignParams is correctly implemented.

The method properly:

  • Uses prefix store with FeeDiscountCampaignPrefix
  • Returns ErrFeeDiscountCampaignNotFound when campaign doesn't exist
  • Handles unmarshal errors appropriately
  • Returns the params on success

33-52: LGTM! SetFeeDiscountCampaignParams includes proper validation.

The method correctly:

  • Validates params before storage using params.Validate(ctx.BlockTime())
  • Uses prefix store with the correct key (lib.Uint32ToKey(params.ClobPairId))
  • Marshals params with the codec
  • Handles marshal errors

The validation ensures that invalid campaigns (expired, invalid time ranges, invalid charge_ppm) cannot be stored.


55-79: LGTM! GetAllFeeDiscountCampaignParams handles errors gracefully.

The method is well-implemented:

  • Properly closes iterator with defer iterator.Close()
  • Handles unmarshal errors by logging and continuing (robust against corrupted entries)
  • Decodes clobPairId from key for error logging using binary.BigEndian.Uint32 (matches lib.Uint32ToKey)
  • Returns empty slice when no campaigns exist

The error handling approach (log and skip) is appropriate for enumeration methods, preventing one corrupted entry from breaking the entire query.


84-108: LGTM! GetDiscountPpm implements correct active campaign logic.

The method correctly:

  • Retrieves campaign params for the CLOB pair
  • Treats ErrFeeDiscountCampaignNotFound as a normal case (no logging)
  • Logs unexpected errors for visibility
  • Checks if current time is within the campaign window using currentTime >= campaign.StartTimeUnix && currentTime < campaign.EndTimeUnix
    • Start time is inclusive
    • End time is exclusive
    • This is correct for time range semantics
  • Returns campaign.ChargePpm when active, MaxChargePpm otherwise

The fallback to MaxChargePpm (100% charge, no discount) is appropriate for both missing campaigns and inactive campaigns.

indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/fee_discount_campaign.ts (1)

1-121: LGTM! Generated FeeDiscountCampaignParams types are well-formed.

The generated code is correct:

  • Interface definitions properly use Long for 64-bit timestamp fields (startTimeUnix, endTimeUnix)
  • SDK type uses snake_case field names (clob_pair_id, start_time_unix, etc.)
  • Default values are appropriate: 0 for numbers, Long.ZERO for int64
  • Encode writes fields with correct protobuf tags (1-4) and wire types
  • Decode handles all field tags correctly with type casting for Long fields
  • fromPartial properly converts values to Long when present, using defaults otherwise
proto/dydxprotocol/feetiers/query.proto (2)

8-8: LGTM! Query RPC definitions follow Cosmos SDK patterns.

The additions are well-structured:

  • Import statement for fee_discount_campaign.proto
  • Two new RPC methods with proper documentation
  • HTTP GET endpoints correctly defined:
    • /dydxprotocol/feetiers/fee_discount_campaign_params/{clob_pair_id} for single campaign query
    • /dydxprotocol/feetiers/fee_discount_campaign_params for listing all campaigns
  • Follows the same pattern as existing PerpetualFeeParams and UserFeeTier RPCs

Also applies to: 25-39


64-83: LGTM! Message definitions are correctly structured.

The query message types are properly defined:

  • QueryFeeDiscountCampaignParamsRequest with clob_pair_id field
  • QueryFeeDiscountCampaignParamsResponse with non-nullable params field
  • QueryAllFeeDiscountCampaignParamsRequest is empty (standard for list-all queries)
  • QueryAllFeeDiscountCampaignParamsResponse with repeated non-nullable params

The use of gogoproto.nullable = false is consistent with other messages in the module and ensures fields are always present.

indexer/packages/v4-protos/src/codegen/dydxprotocol/feetiers/query.rpc.Query.ts (1)

4-4: LGTM! Clean RPC query client implementation.

The fee discount campaign query methods are correctly implemented following the established patterns:

  • Proper type imports and interface extensions
  • Constructor bindings for method context
  • Standard encode-request → RPC-call → decode-response flow
  • Correct delegation in the extension factory

The allFeeDiscountCampaignParams method appropriately defaults the request parameter to an empty object, consistent with similar query methods.

Also applies to: 13-21, 30-31, 46-56, 69-77

indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts (1)

53-61: LGTM! Consistent mechanical refactoring.

The bundle correctly incorporates the new fee discount campaign module:

  • New import _57 for fee_discount_campaign added at line 57
  • All subsequent imports systematically shifted by one index
  • The feetiers namespace properly includes the new module and updated indices for query/tx handlers

This is standard codegen output with consistent index management across all namespaces.

Also applies to: 276-284

@davidli1997 davidli1997 merged commit 33a4abb into main Oct 17, 2025
55 checks passed
@davidli1997 davidli1997 deleted the davidli/fee_discount_campaign branch October 17, 2025 16:59
@davidli1997
Copy link
Contributor Author

@Mergifyio backport release/protocol/v9.x

@mergify
Copy link
Contributor

mergify bot commented Oct 22, 2025

backport release/protocol/v9.x

✅ Backports have been created

Details

@davidli1997
Copy link
Contributor Author

@Mergifyio backport release/indexer/v9.x

@mergify
Copy link
Contributor

mergify bot commented Oct 22, 2025

backport release/indexer/v9.x

✅ Backports have been created

Details

@davidli1997
Copy link
Contributor Author

https://github.com/Mergifyio backport release/protocol/v9.x

@mergify
Copy link
Contributor

mergify bot commented Oct 23, 2025

backport release/protocol/v9.x

✅ Backports have been created

Details

davidli1997 added a commit that referenced this pull request Oct 23, 2025
davidli1997 added a commit that referenced this pull request Oct 23, 2025
davidli1997 added a commit that referenced this pull request Oct 23, 2025
@davidli1997
Copy link
Contributor Author

https://github.com/Mergifyio backport release/indexer/v9.x

@mergify
Copy link
Contributor

mergify bot commented Oct 23, 2025

backport release/indexer/v9.x

✅ Backports have been created

Details

Kefancao pushed a commit that referenced this pull request Oct 24, 2025
Kefancao pushed a commit that referenced this pull request Oct 24, 2025
@davidli1997
Copy link
Contributor Author

https://github.com/Mergifyio backport release/indexer/v9.4.x-2

@mergify
Copy link
Contributor

mergify bot commented Oct 24, 2025

backport release/indexer/v9.4.x-2

✅ Backports have been created

Details

mergify bot pushed a commit that referenced this pull request Oct 24, 2025
(cherry picked from commit 33a4abb)

# Conflicts:
#	protocol/x/feetiers/client/cli/query.go
#	protocol/x/feetiers/keeper/grpc_query.go
#	protocol/x/feetiers/keeper/grpc_query_test.go
#	protocol/x/feetiers/keeper/keeper.go
#	protocol/x/feetiers/keeper/keeper_test.go
davidli1997 added a commit that referenced this pull request Oct 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants

Comments