Skip to content

feat(perps-controller): report the winning fee source on fee quotes - #10650

Merged
tanguyenvn merged 5 commits into
mainfrom
feat/perps-fee-source-on-fee-quote
Oct 2, 2026
Merged

tanguyenvn merged 5 commits into
mainfrom
feat/perps-fee-source-on-fee-quote

Conversation

@tanguyenvn

@tanguyenvn tanguyenvn commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Explanation

PerpsController.calculateFees already decides which fee source wins for a quote (default, rewards or subscription) inside RewardsIntegrationService.resolveFee, and uses that winner to re-price the MetaMask builder fee. The winner itself was dropped before the quote reached clients, so a client wanting to say why a fee is reduced (for example, a member badge for a subscription waiver versus a VIP badge for a rewards discount) had to guess from the rates.

This PR adds an optional feeSource to FeeCalculationResult, set in applyFeeResolution from the same resolution the rates were priced with.

Why applyFeeResolution. It is the only place that knows whether the resolution actually made it into the quote. Its early-return paths (no resolution, no MetaMask rate, or a placement that carries no builder fee, such as TWAP or Lighter) hand back the provider's quote untouched, so feeSource is absent there instead of naming a discount the quote does not contain. The label and the rates come from one resolution object and cannot disagree.

Why feeSource rather than source. FeeCalculationResult mixes protocol and MetaMask components, so a bare source is ambiguous. TradingService already logs this value as feeSource.

Reliability notes (documented on the field):

  • subscription always means a real reduction: the resolver only picks it when it is strictly cheaper after venue quantization.
  • rewards wins ties with the default (<=), so it can carry a 0% discount. Consumers should check metamaskFeeRate before presenting it as a reduction. The value is kept identical to PerpsFeeResolution.source rather than normalized, so the preview, the submit-path logs and attribution all use the same vocabulary.
  • The value describes the preview only. The submit path resolves the fee again, so an order is attributed to whichever source wins at submission time, which matches how the quoted rates already behave.

References

N/A

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
Additive optional field on fee quote types with no change to fee math; consumers that ignore the new property behave as before.

Overview
Fee previews from calculateFees can now expose which fee source (default, rewards, or subscription) the quoted MetaMask builder fee was priced from, so UIs can label discounts without inferring them from rates alone.

An optional feeSource on FeeCalculationResult is set in applyFeeResolution from the same unified resolution used to re-price metamaskFeeRate and totals. When the quote is not re-priced (no resolution, no MetaMask rate, or placements with no builder fee), feeSource stays absent. JSDoc and the changelog note that rewards can win with a 0% discount—clients should check metamaskFeeRate before showing a reduction—and that the field describes the preview only (submit resolves fees again).

Tests assert feeSource for subscription, rewards, and default winning paths.

Reviewed by Cursor Bugbot for commit 59ca4b0. Bugbot is set up for automated code reviews on this repo. Configure here.

@tanguyenvn
tanguyenvn requested review from a team as code owners October 1, 2026 09:36
@tanguyenvn
tanguyenvn deployed to default-branch October 1, 2026 09:37 — with GitHub Actions Active
@tanguyenvn

Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-previews

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@11.0.0-preview-378f5fcf7
@metamask-previews/accounts-controller@40.0.0-preview-378f5fcf7
@metamask-previews/address-book-controller@8.0.0-preview-378f5fcf7
@metamask-previews/advanced-chart-core@1.0.0-preview-378f5fcf7
@metamask-previews/ai-controllers@2.0.0-preview-378f5fcf7
@metamask-previews/analytics-controller@3.2.0-preview-378f5fcf7
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-378f5fcf7
@metamask-previews/announcement-controller@9.0.0-preview-378f5fcf7
@metamask-previews/app-metadata-controller@3.0.0-preview-378f5fcf7
@metamask-previews/approval-controller@10.0.0-preview-378f5fcf7
@metamask-previews/assets-controller@17.0.0-preview-378f5fcf7
@metamask-previews/assets-controllers@112.0.4-preview-378f5fcf7
@metamask-previews/authenticated-user-storage@4.1.0-preview-378f5fcf7
@metamask-previews/base-controller@10.0.0-preview-378f5fcf7
@metamask-previews/base-data-service@2.1.0-preview-378f5fcf7
@metamask-previews/bitcoin-regtest-up@2.0.0-preview-378f5fcf7
@metamask-previews/bridge-controller@81.4.0-preview-378f5fcf7
@metamask-previews/bridge-status-controller@76.3.3-preview-378f5fcf7
@metamask-previews/build-utils@4.0.0-preview-378f5fcf7
@metamask-previews/chain-agnostic-permission@2.0.0-preview-378f5fcf7
@metamask-previews/chomp-api-service@6.0.0-preview-378f5fcf7
@metamask-previews/claims-controller@1.0.2-preview-378f5fcf7
@metamask-previews/client-controller@2.0.0-preview-378f5fcf7
@metamask-previews/client-utils@3.0.3-preview-378f5fcf7
@metamask-previews/compliance-controller@3.0.0-preview-378f5fcf7
@metamask-previews/composable-controller@13.0.0-preview-378f5fcf7
@metamask-previews/config-registry-controller@4.0.0-preview-378f5fcf7
@metamask-previews/connectivity-controller@1.0.0-preview-378f5fcf7
@metamask-previews/controller-utils@13.0.0-preview-378f5fcf7
@metamask-previews/core-backend@11.0.0-preview-378f5fcf7
@metamask-previews/cryptography@0.0.0-preview-378f5fcf7
@metamask-previews/delegation-controller@4.0.0-preview-378f5fcf7
@metamask-previews/earn-controller@13.0.2-preview-378f5fcf7
@metamask-previews/eip-5792-middleware@4.0.1-preview-378f5fcf7
@metamask-previews/eip-7702-internal-rpc-middleware@1.0.0-preview-378f5fcf7
@metamask-previews/eip1193-permission-middleware@3.0.0-preview-378f5fcf7
@metamask-previews/eth-block-tracker@16.0.0-preview-378f5fcf7
@metamask-previews/eth-json-rpc-middleware@25.0.0-preview-378f5fcf7
@metamask-previews/eth-json-rpc-provider@7.0.0-preview-378f5fcf7
@metamask-previews/foundryup@2.0.0-preview-378f5fcf7
@metamask-previews/gas-fee-controller@27.0.0-preview-378f5fcf7
@metamask-previews/gator-permissions-controller@6.0.1-preview-378f5fcf7
@metamask-previews/geolocation-controller@2.0.0-preview-378f5fcf7
@metamask-previews/java-tron-up@2.0.0-preview-378f5fcf7
@metamask-previews/json-rpc-engine@11.0.0-preview-378f5fcf7
@metamask-previews/json-rpc-middleware-stream@9.0.0-preview-378f5fcf7
@metamask-previews/keyring-controller@28.1.0-preview-378f5fcf7
@metamask-previews/kyc-controller@0.6.0-preview-378f5fcf7
@metamask-previews/local-node-utils@2.0.0-preview-378f5fcf7
@metamask-previews/logging-controller@10.0.0-preview-378f5fcf7
@metamask-previews/message-manager@15.0.0-preview-378f5fcf7
@metamask-previews/messenger@3.0.0-preview-378f5fcf7
@metamask-previews/messenger-cli@1.0.0-preview-378f5fcf7
@metamask-previews/money-account-api-data-service@2.1.0-preview-378f5fcf7
@metamask-previews/money-account-balance-service@3.1.1-preview-378f5fcf7
@metamask-previews/money-account-controller@2.0.0-preview-378f5fcf7
@metamask-previews/money-account-upgrade-controller@5.1.0-preview-378f5fcf7
@metamask-previews/money-account-utils@2.1.0-preview-378f5fcf7
@metamask-previews/multichain-account-service@14.1.0-preview-378f5fcf7
@metamask-previews/multichain-api-middleware@5.0.0-preview-378f5fcf7
@metamask-previews/multichain-network-controller@4.0.0-preview-378f5fcf7
@metamask-previews/multichain-transactions-controller@8.0.0-preview-378f5fcf7
@metamask-previews/name-controller@10.0.0-preview-378f5fcf7
@metamask-previews/network-connection-banner-controller@1.0.0-preview-378f5fcf7
@metamask-previews/network-controller@37.0.0-preview-378f5fcf7
@metamask-previews/network-enablement-controller@7.0.1-preview-378f5fcf7
@metamask-previews/notification-services-controller@29.0.2-preview-378f5fcf7
@metamask-previews/passkey-controller@4.1.0-preview-378f5fcf7
@metamask-previews/permission-controller@14.0.0-preview-378f5fcf7
@metamask-previews/permission-log-controller@6.0.0-preview-378f5fcf7
@metamask-previews/perps-controller@18.0.1-preview-378f5fcf7
@metamask-previews/phishing-controller@18.1.1-preview-378f5fcf7
@metamask-previews/platform-api-docs@0.2.1-preview-378f5fcf7
@metamask-previews/polling-controller@17.0.0-preview-378f5fcf7
@metamask-previews/preferences-controller@24.0.0-preview-378f5fcf7
@metamask-previews/profile-controller@0.0.0-preview-378f5fcf7
@metamask-previews/profile-metrics-controller@5.1.2-preview-378f5fcf7
@metamask-previews/profile-sync-controller@33.0.0-preview-378f5fcf7
@metamask-previews/ramps-controller@26.1.0-preview-378f5fcf7
@metamask-previews/rate-limit-controller@8.0.0-preview-378f5fcf7
@metamask-previews/react-data-query@2.0.0-preview-378f5fcf7
@metamask-previews/remote-feature-flag-controller@7.0.0-preview-378f5fcf7
@metamask-previews/sample-controllers@6.0.0-preview-378f5fcf7
@metamask-previews/seedless-onboarding-controller@11.0.1-preview-378f5fcf7
@metamask-previews/selected-network-controller@27.0.0-preview-378f5fcf7
@metamask-previews/sentinel-api-service@2.0.0-preview-378f5fcf7
@metamask-previews/shield-controller@7.0.3-preview-378f5fcf7
@metamask-previews/signature-controller@40.0.0-preview-378f5fcf7
@metamask-previews/smart-transactions-controller@27.0.3-preview-378f5fcf7
@metamask-previews/snap-account-service@4.0.0-preview-378f5fcf7
@metamask-previews/social-controllers@3.4.0-preview-378f5fcf7
@metamask-previews/solana-test-validator-up@2.0.0-preview-378f5fcf7
@metamask-previews/stellar-quickstart-up@0.0.0-preview-378f5fcf7
@metamask-previews/storage-service@2.0.0-preview-378f5fcf7
@metamask-previews/subscription-controller@11.0.0-preview-378f5fcf7
@metamask-previews/transaction-controller@72.0.1-preview-378f5fcf7
@metamask-previews/transaction-pay-controller@30.0.0-preview-378f5fcf7
@metamask-previews/user-operation-controller@42.0.1-preview-378f5fcf7
@metamask-previews/utils@12.0.0-preview-378f5fcf7
@metamask-previews/wallet@15.1.0-preview-378f5fcf7
@metamask-previews/wallet-cli@0.0.0-preview-378f5fcf7

geositta
geositta previously approved these changes Oct 1, 2026

@geositta geositta left a comment

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.

Looks good to merge. Thanks for documenting that a rewards source can represent no reduction, and submission resolves the fee again.

# Conflicts:
#	packages/perps-controller/CHANGELOG.md
@tanguyenvn
tanguyenvn added this pull request to the merge queue Oct 2, 2026
Merged via the queue into main with commit 8a4975a Oct 2, 2026
45 checks passed
@tanguyenvn
tanguyenvn deleted the feat/perps-fee-source-on-fee-quote branch October 2, 2026 20:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants