Skip to content

Create new instance for fee calculation#3051

Merged
jusbar23 merged 12 commits intomainfrom
revshares_logging
Aug 22, 2025
Merged

Create new instance for fee calculation#3051
jusbar23 merged 12 commits intomainfrom
revshares_logging

Conversation

@jusbar23
Copy link
Contributor

@jusbar23 jusbar23 commented Aug 21, 2025

Changelist

  • Fix issue with fee calculation for ORRS

Test Plan

  • Test cases

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

  • Bug Fixes

    • Prevented unintended mutation of net fees during revenue-share calculations by using mutation‑safe copies and adjusting subtraction logic; added a post‑aggregation validation that logs and aborts if total shared fees exceed net fees.
  • Tests

    • Added comprehensive tests covering taker/maker order‑router rev shares, unconditional rev shares, maker rebates, and affiliate‑whitelist interactions to validate aggregation, allocation, and edge cases.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 21, 2025

Walkthrough

Avoid in-place mutation of netFees by using a copied big.Int for subsequent subtractions, apply order-router and affiliate deductions to that copy, add post-aggregation validation that errors when total shared fees exceed net fees, and expand tests covering taker/maker/order-router/unconditional/affiliate scenarios.

Changes

Cohort / File(s) Summary
Revshare keeper logic
protocol/x/revshare/keeper/revshare.go
Replace in-place netFees subtractions with netFeesSubRevenueShare := new(big.Int).Set(netFees) and apply order-router/affiliate deductions to the copy; add post-aggregation validation that logs details and returns ErrTotalFeesSharedExceedsNetFees when total shared fees > net fees.
Revshare tests expansion
protocol/x/revshare/keeper/revshare_test.go
Add multiple test cases exercising taker and maker order-router rev shares, unconditional rev shares, maker-fee rebates, affiliate-whitelist interactions, and combined distributions/validations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant K as Revshare Keeper
  participant AFF as Affiliate calc
  participant OR as Order-router config
  participant LOG as Logger
  participant C as Caller

  rect rgba(200,230,255,0.18)
    note over K: GetAllRevShares start — use copy of netFees
    K->>K: netFeesSub := new(big.Int).Set(netFees)
  end

  alt Affiliate present
    K->>AFF: compute affiliateFeesShared
    AFF-->>K: affiliateFeesShared
    K->>K: netFeesSub -= affiliateFeesShared
  else No affiliate
    note right of K: no affiliate subtraction
  end

  rect rgba(220,255,220,0.12)
    K->>OR: compute order-router rev shares (taker/maker)
    OR-->>K: routerShares
    K->>K: netFeesSub -= routerShares
    K->>K: aggregate unconditional & maker shares
  end

  K->>K: totalFeesShared := sum(all shares)

  alt totalFeesShared > netFees
    K->>LOG: error(totalFeesShared, netFees, RevShares)
    K-->>C: ErrTotalFeesSharedExceedsNetFees
  else OK
    K-->>C: RevSharesForFill
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

revshare, feature:order_router_rev_share, feature:revshare_refactor

Suggested reviewers

  • teddyding
  • shrenujb
  • anmolagrawal345

Poem

I copy nettle-net fees, hop with care,
Routers, makers, affiliates — all share a fare.
I tally each ppm, hold totals tight,
If shares exceed, I sound the light.
Hooray — math safe, carrots in sight! 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch revshares_logging

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
protocol/x/revshare/keeper/revshare.go (3)

343-346: Prevent negative taker ORRS when maker rebate > taker fee.

takerFeesSide = min(taker, taker+maker) can become negative when the maker rebate exceeds the taker fee, yielding a negative QuoteQuantums for the taker’s ORRS. That inflates the “remainder” (subtracting a negative), misallocates unconditional/MM shares, and can mask the final invariant check.

Clamp the base at zero and keep BigMulPpm argument order consistent with other sites.

- // This is taker ppm * min(taker, taker - maker_rebate)
- takerFeesSide := lib.BigMin(takerFees, new(big.Int).Add(takerFees, makerFees))
- takerRevShare := lib.BigMulPpm(lib.BigU(takerOrderRouterRevSharePpm), takerFeesSide, false)
+ // taker ppm * max(0, min(taker, taker - maker_rebate))
+ takerFeesSide := lib.BigMax(
+   lib.BigI(0),
+   lib.BigMin(takerFees, new(big.Int).Add(takerFees, makerFees)),
+ )
+ takerRevShare := lib.BigMulPpm(takerFeesSide, lib.BigU(takerOrderRouterRevSharePpm), false)

I recommend adding/expanding tests where maker rebate > taker fee to verify taker ORRS is zero, not negative. I can draft them if helpful.


178-181: Unsigned cast of possibly negative fees corrupts safety check.

Casting int32 fees to uint64 before addition converts negatives into huge positives, breaking ValidateRevShareSafety for maker rebates (negative maker fee). Use signed big.Int addition.

- bigNetFee := new(big.Int).SetUint64(
-   // Casting is safe since both variables are int32.
-   uint64(lowestTakerFeePpm) + uint64(lowestMakerFeePpm),
- )
+ bigNetFee := new(big.Int).Add(
+   lib.BigI(lowestTakerFeePpm),
+   lib.BigI(lowestMakerFeePpm),
+ )

141-149: Ensure safe handling of uninitialized UnconditionalRevShareConfig

GetUnconditionalRevShareConfigParams currently calls MustUnmarshal on the bytes returned from the KV store without checking for a nil slice. Because this module does not perform any explicit initialization of UnconditionalRevShareConfig in its InitGenesis, default genesis, or upgrade handlers—and only tests invoke SetUnconditionalRevShareConfigParams—there exists a real risk of a panic when the key is unset on chain startup or after an upgrade.

Please apply the following mandatory guard:

• File: protocol/x/revshare/keeper/revshare.go
• Lines: 141–149

 func (k Keeper) GetUnconditionalRevShareConfigParams(ctx sdk.Context) (types.UnconditionalRevShareConfig, error) {
   store := ctx.KVStore(k.storeKey)
   unconditionalRevShareConfigBytes := store.Get(
     []byte(types.UnconditionalRevShareConfigKey),
   )
   var unconditionalRevShareConfig types.UnconditionalRevShareConfig
-  k.cdc.MustUnmarshal(unconditionalRevShareConfigBytes, &unconditionalRevShareConfig)
+  if unconditionalRevShareConfigBytes == nil {
+    // Return empty config if not yet set to avoid panics on MustUnmarshal
+    return unconditionalRevShareConfig, nil
+  }
+  k.cdc.MustUnmarshal(unconditionalRevShareConfigBytes, &unconditionalRevShareConfig)
   return unconditionalRevShareConfig, nil
 }

Applying this change will ensure the keeper safely returns a zero-value config instead of panicking when the store key is absent.

♻️ Duplicate comments (1)
protocol/x/revshare/keeper/revshare.go (1)

233-234: Subtract affiliate fees from the net-fee copy (matches earlier suggestion).

This resolves the prior nit about subtracting into the local copy rather than mutating netFees.

🧹 Nitpick comments (4)
protocol/x/revshare/keeper/revshare.go (4)

236-238: Misleading error when ORRS consume the remainder — split the check or use a neutral error.

The current error type ErrAffiliateFeesSharedGreaterThanOrEqualToNetFees fires even in the ORRS-only path (when there’s no affiliate share) if ORRS fully consume the remainder. That’s confusing for ops/debugging.

Consider branching by path (affiliate vs ORRS) or using a neutral error for “non-positive remainder.”

Apply one of the following minimal diffs:

Option A (branch-specific checks):

- if netFeesSubRevenueShare.Sign() <= 0 {
-   return types.RevSharesForFill{}, types.ErrAffiliateFeesSharedGreaterThanOrEqualToNetFees
- }
+ if len(affiliateRevShares) == 0 {
+   if netFeesSubRevenueShare.Sign() < 0 {
+     return types.RevSharesForFill{}, types.ErrTotalFeesSharedExceedsNetFees
+   }
+ } else {
+   if netFeesSubRevenueShare.Sign() <= 0 {
+     return types.RevSharesForFill{}, types.ErrAffiliateFeesSharedGreaterThanOrEqualToNetFees
+   }
+ }

Option B (neutral check; allow exact-zero remainder to proceed with zero unconditional/MM):

- if netFeesSubRevenueShare.Sign() <= 0 {
-   return types.RevSharesForFill{}, types.ErrAffiliateFeesSharedGreaterThanOrEqualToNetFees
- }
+ if netFeesSubRevenueShare.Sign() < 0 {
+   return types.RevSharesForFill{}, types.ErrTotalFeesSharedExceedsNetFees
+ }

273-279: Improve logging: structured keys, more context.

Use structured keys without spaces/colons and include identifiers (marketId, taker/maker) to aid incident triage.

- k.Logger(ctx).Error(
-   "Total fees exceed net fees. Total fees: ", totalFeesShared,
-   "Net fees: ", netFees,
-   "Revshares generated: ", revShares)
+ k.Logger(ctx).Error(
+   "total fees exceed net fees",
+   "total_fees_shared", totalFeesShared.String(),
+   "net_fees",           netFees.String(),
+   "market_id",          fill.MarketId,
+   "taker_addr",         fill.TakerAddr,
+   "maker_addr",         fill.MakerAddr,
+   "revshares_generated", revShares,
+ )

240-245: Naming consistency: parameters still say “SubAffiliateFeesShared” but now also reflect ORRS.

Now that ORRS also reduce the base, the parameter names are misleading. Renaming improves readability without behavior changes.

- func (k Keeper) getUnconditionalRevShares(
+ func (k Keeper) getUnconditionalRevShares(
   ctx sdk.Context,
-  netFeesSubAffiliateFeesShared *big.Int,
+  netProtocolRevenueBase *big.Int,
 ) ([]types.RevShare, error) {
@@
-   feeShared := lib.BigMulPpm(netFeesSubAffiliateFeesShared, lib.BigU(revShare.SharePpm), false)
+   feeShared := lib.BigMulPpm(netProtocolRevenueBase, lib.BigU(revShare.SharePpm), false)
- func (k Keeper) getMarketMapperRevShare(
+ func (k Keeper) getMarketMapperRevShare(
   ctx sdk.Context,
   marketId uint32,
-  netFeesSubAffiliateFeesShared *big.Int,
+  netProtocolRevenueBase *big.Int,
 ) ([]types.RevShare, error) {
@@
- marketMapperRevshareAmount := lib.BigMulPpm(netFeesSubAffiliateFeesShared, lib.BigU(revenueSharePpm), false)
+ marketMapperRevshareAmount := lib.BigMulPpm(netProtocolRevenueBase, lib.BigU(revenueSharePpm), false)
- unconditionalRevShares, err := k.getUnconditionalRevShares(ctx, netFeesSubRevenueShare)
+ unconditionalRevShares, err := k.getUnconditionalRevShares(ctx, netFeesSubRevenueShare)
@@
- marketMapperRevShares, err := k.getMarketMapperRevShare(ctx, fill.MarketId, netFeesSubRevenueShare)
+ marketMapperRevShares, err := k.getMarketMapperRevShare(ctx, fill.MarketId, netFeesSubRevenueShare)

Also applies to: 385-406, 408-432


332-353: Optional: unify BigMulPpm argument order for readability.

Elsewhere you consistently pass (amount, ppm). The taker branch currently passes (ppm, amount). While mathematically equivalent, consistent ordering reduces cognitive load.

The change in the “negative taker ORRS” fix above already standardizes it; no further action needed if you adopt that diff.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9589892 and d04559f.

📒 Files selected for processing (1)
  • protocol/x/revshare/keeper/revshare.go (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-15T16:00:11.304Z
Learnt from: hwray
PR: dydxprotocol/v4-chain#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/revshare/keeper/revshare.go
📚 Learning: 2024-11-15T15:59:28.095Z
Learnt from: hwray
PR: dydxprotocol/v4-chain#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/revshare/keeper/revshare.go
⏰ 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). (3)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (1)
protocol/x/revshare/keeper/revshare.go (1)

223-223: Use a fresh big.Int to avoid aliasing/mutation — correct fix.

Cloning netFees into netFeesSubRevenueShare prevents in-place mutation by subsequent Sub calls. This was the right move to keep the original netFees intact for later validation.

@jusbar23 jusbar23 merged commit 143470d into main Aug 22, 2025
38 checks passed
@jusbar23 jusbar23 deleted the revshares_logging branch August 22, 2025 14:31
@jusbar23
Copy link
Contributor Author

@Mergifyio backport release/indexer/v9.x

@jusbar23
Copy link
Contributor Author

@Mergifyio backport release/protocol/v9.x

@mergify
Copy link
Contributor

mergify bot commented Aug 22, 2025

backport release/indexer/v9.x

✅ Backports have been created

Details

@mergify
Copy link
Contributor

mergify bot commented Aug 22, 2025

backport release/protocol/v9.x

✅ Backports have been created

Details

mergify bot pushed a commit that referenced this pull request Aug 22, 2025
mergify bot pushed a commit that referenced this pull request Aug 22, 2025
jusbar23 added a commit that referenced this pull request Aug 22, 2025
jusbar23 added a commit that referenced this pull request Aug 22, 2025
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.

2 participants

Comments