Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OTE-816] Update Revshare logic for affiliates #2298

Merged
merged 14 commits into from
Sep 24, 2024
1 change: 0 additions & 1 deletion protocol/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,6 @@ func New(
app.BankKeeper,
app.PerpetualsKeeper,
app.BlockTimeKeeper,
app.RevShareKeeper,
app.IndexerEventManager,
app.FullNodeStreamingManager,
)
Expand Down
1 change: 0 additions & 1 deletion protocol/testutil/keeper/clob.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ func NewClobKeepersTestContextWithUninitializedMemStore(
bankKeeper,
ks.PerpetualsKeeper,
ks.BlockTimeKeeper,
revShareKeeper,
indexerEventsTransientStoreKey,
true,
)
Expand Down
1 change: 0 additions & 1 deletion protocol/testutil/keeper/listing.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ func ListingKeepers(
bankKeeper,
perpetualsKeeper,
blockTimeKeeper,
revShareKeeper,
transientStoreKey,
true,
)
Expand Down
1 change: 0 additions & 1 deletion protocol/testutil/keeper/sending.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ func SendingKeepersWithSubaccountsKeeper(t testing.TB, saKeeper types.Subaccount
ks.BankKeeper,
ks.PerpetualsKeeper,
blockTimeKeeper,
revShareKeeper,
transientStoreKey,
true,
)
Expand Down
3 changes: 0 additions & 3 deletions protocol/testutil/keeper/subaccounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ func SubaccountsKeepers(t testing.TB, msgSenderEnabled bool) (
bankKeeper,
perpetualsKeeper,
blocktimeKeeper,
revShareKeeper,
transientStoreKey,
msgSenderEnabled,
)
Expand Down Expand Up @@ -132,7 +131,6 @@ func createSubaccountsKeeper(
bk types.BankKeeper,
pk *perpskeeper.Keeper,
btk *blocktimekeeper.Keeper,
rsk *revsharekeeper.Keeper,
transientStoreKey storetypes.StoreKey,
msgSenderEnabled bool,
) (*keeper.Keeper, storetypes.StoreKey) {
Expand All @@ -151,7 +149,6 @@ func createSubaccountsKeeper(
bk,
pk,
btk,
rsk,
mockIndexerEventsManager,
streaming.NewNoopGrpcStreamingManager(),
)
Expand Down
10 changes: 5 additions & 5 deletions protocol/x/clob/keeper/process_single_match.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types"
"github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
revsharetypes "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types"
satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types"
gometrics "github.com/hashicorp/go-metrics"
)
Expand Down Expand Up @@ -464,14 +465,13 @@ func (k Keeper) persistMatchedOrders(
// Distribute the fee amount from subacounts module to fee collector and rev share accounts
bigTotalFeeQuoteQuantums := new(big.Int).Add(bigTakerFeeQuoteQuantums, bigMakerFeeQuoteQuantums)
revSharesForFill, err := k.revshareKeeper.GetAllRevShares(ctx, fillForProcess, affiliatesWhitelistMap)

if err != nil {
revSharesForFill = revsharetypes.RevSharesForFill{}
log.ErrorLog(ctx, "error getting rev shares for fill", "error", err)
}
Comment on lines +468 to +471
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider revising the error handling strategy for GetAllRevShares

The addition of error handling for GetAllRevShares is a good practice. However, the current implementation might mask potential issues:

  1. The function continues execution with an empty revSharesForFill struct when an error occurs.
  2. The error is logged but not propagated, which could lead to silent failures in revenue sharing calculations.

Consider the following suggestions:

  1. Evaluate whether continuing with an empty revSharesForFill is appropriate in all scenarios.
  2. Consider propagating the error or adding a mechanism to flag that revenue sharing couldn't be calculated.
  3. Add a comment explaining the rationale behind this error handling approach for future maintainers.

Here's a potential alternative implementation:

 revSharesForFill, err := k.revshareKeeper.GetAllRevShares(ctx, fillForProcess, affiliatesWhitelistMap)
 if err != nil {
-    revSharesForFill = revsharetypes.RevSharesForFill{}
-    log.ErrorLog(ctx, "error getting rev shares for fill", "error", err)
+    // Log the error
+    log.ErrorLog(ctx, "error getting rev shares for fill", "error", err)
+    // Consider whether to return the error or continue with a flag
+    return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, 
+        fmt.Errorf("failed to get rev shares: %w", err)
 }

This approach would propagate the error, allowing the caller to decide how to handle it.

Committable suggestion was skipped due to low confidence.

if revSharesForFill.AffiliateRevShare != nil {
affiliateRevSharesQuoteQuantums = revSharesForFill.AffiliateRevShare.QuoteQuantums
}

if err != nil {
return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err
}
if err := k.subaccountsKeeper.DistributeFees(
ctx,
assettypes.AssetUsdc.Id,
Expand Down
41 changes: 21 additions & 20 deletions protocol/x/revshare/keeper/revshare.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package keeper
import (
"math/big"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/dydxprotocol/v4-chain/protocol/lib"
Expand Down Expand Up @@ -163,25 +162,28 @@ func (k Keeper) GetAllRevShares(
feeSourceToRevSharePpm := make(map[types.RevShareFeeSource]uint32)
feeSourceToQuoteQuantums[types.REV_SHARE_FEE_SOURCE_TAKER_FEE] = big.NewInt(0)
feeSourceToRevSharePpm[types.REV_SHARE_FEE_SOURCE_TAKER_FEE] = 0
feeSourceToQuoteQuantums[types.REV_SHARE_FEE_SOURCE_NET_FEE] = big.NewInt(0)
feeSourceToRevSharePpm[types.REV_SHARE_FEE_SOURCE_NET_FEE] = 0
feeSourceToQuoteQuantums[types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE] = big.NewInt(0)
feeSourceToRevSharePpm[types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE] = 0

totalFeesShared := big.NewInt(0)
takerFees := fill.TakerFeeQuoteQuantums
makerFees := fill.MakerFeeQuoteQuantums
Copy link
Contributor

@teddyding teddyding Sep 19, 2024

Choose a reason for hiding this comment

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

Let's also change REV_SHARE_FEE_SOURCE_NET_FEE to REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE to disambiguate

NET_FEE could be understood as taker fee minus rebates

netFees := big.NewInt(0).Add(takerFees, makerFees)

affiliateRevShares, err := k.getAffiliateRevShares(ctx, fill, affiliatesWhitelistMap)
affiliateRevShares, affiliateFeesShared, err := k.getAffiliateRevShares(ctx, fill, affiliatesWhitelistMap)
if err != nil {
return types.RevSharesForFill{}, err
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment on caller function persistMatchedOrders:

Instead propagating error returned from GetAllRevShares (which would ultimately fail the match transaction), it's better to just pass in empty RevSharesForFill to DistributeFees. This way if any rev share calculation encounters issue, the chain will continue to be able to process matches (without rev share).

Order matching + rev share both working > Only order matching working > Order matching not working

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sounds good. I'll add some error logs to catch in case it fails

Copy link
Contributor

Choose a reason for hiding this comment

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

Did we do this in this PR? Or plan to do in another one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is done in the PR . Check here. Also updated tests in revshare_test.go

Copy link
Contributor

@teddyding teddyding Sep 24, 2024

Choose a reason for hiding this comment

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

Oh, the original comment was suggesting that we keep GetAllRevShares behavior as this but handle error differently in persistMatchedOrders, since it's the caller responsibility to determine what to do with the returned error from GetAllRevShares

Changing GetAllRevShares behavior works too but we are changing the signature/expectation of the function (e.g. it doesn't make sense to have this function return error anymore, it's always nil)

We should either:

  • Keep GetAllRevShares as is, and just log out error (don't propogate errors further up) in persistMatchedOrders.
  • Fully change signature of GetAllRevShares so it doesn't return errors anymore, maybe even rename it to GetAllValidRevShares

The first seems slightly cleaner to me since we just need one error log instead of multiple

}
netFeesSubAffiliateFeesShared := new(big.Int).Sub(netFees, affiliateFeesShared)
if netFeesSubAffiliateFeesShared.Sign() <= 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: move to immediately below line 129

return types.RevSharesForFill{}, types.ErrAffiliateFeesSharedExceedsNetFees
}

unconditionalRevShares, err := k.getUnconditionalRevShares(ctx, netFees)
unconditionalRevShares, err := k.getUnconditionalRevShares(ctx, netFeesSubAffiliateFeesShared)
teddyding marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return types.RevSharesForFill{}, err
}

marketMapperRevShares, err := k.getMarketMapperRevShare(ctx, fill.MarketId, netFees)
marketMapperRevShares, err := k.getMarketMapperRevShare(ctx, fill.MarketId, netFeesSubAffiliateFeesShared)
if err != nil {
return types.RevSharesForFill{}, err
}
Expand All @@ -208,8 +210,7 @@ func (k Keeper) GetAllRevShares(
}
//check total fees shared is less than or equal to net fees
if totalFeesShared.Cmp(netFees) > 0 {
return types.RevSharesForFill{}, errorsmod.Wrap(
types.ErrTotalFeesSharedExceedsNetFees, "total fees shared exceeds net fees")
return types.RevSharesForFill{}, types.ErrTotalFeesSharedExceedsNetFees
}

return types.RevSharesForFill{
Expand All @@ -224,20 +225,20 @@ func (k Keeper) getAffiliateRevShares(
ctx sdk.Context,
fill clobtypes.FillForProcess,
affiliatesWhitelistMap map[string]uint32,
) ([]types.RevShare, error) {
) ([]types.RevShare, *big.Int, error) {
takerAddr := fill.TakerAddr
takerFee := fill.TakerFeeQuoteQuantums
if fill.MonthlyRollingTakerVolumeQuantums >= types.MaxReferee30dVolumeForAffiliateShareQuantums {
return nil, nil
return nil, big.NewInt(0), nil
}

takerAffiliateAddr, feeSharePpm, exists, err := k.affiliatesKeeper.GetTakerFeeShare(
ctx, takerAddr, affiliatesWhitelistMap)
if err != nil {
return nil, err
return nil, big.NewInt(0), err
Copy link
Contributor

Choose a reason for hiding this comment

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

Return nil for affiliateFeesShared when an error occurs

In the error case at line 237, consider returning nil for affiliateFeesShared instead of big.NewInt(0). This makes it clear that the value is invalid due to the error, aligning with common Go practices.

Apply this diff to adjust the return value:

-return nil, big.NewInt(0), err
+return nil, nil, err
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
return nil, big.NewInt(0), err
return nil, nil, err

}
if !exists {
return nil, nil
return nil, big.NewInt(0), nil
}
feesShared := lib.BigMulPpm(takerFee, lib.BigU(feeSharePpm), false)
return []types.RevShare{
Expand All @@ -248,23 +249,23 @@ func (k Keeper) getAffiliateRevShares(
QuoteQuantums: feesShared,
RevSharePpm: feeSharePpm,
},
}, nil
}, feesShared, nil
}

func (k Keeper) getUnconditionalRevShares(
ctx sdk.Context,
netFees *big.Int,
netFeesSubAffiliateFeesShared *big.Int,
) ([]types.RevShare, error) {
revShares := []types.RevShare{}
unconditionalRevShareConfig, err := k.GetUnconditionalRevShareConfigParams(ctx)
if err != nil {
return nil, err
}
for _, revShare := range unconditionalRevShareConfig.Configs {
feeShared := lib.BigMulPpm(netFees, lib.BigU(revShare.SharePpm), false)
feeShared := lib.BigMulPpm(netFeesSubAffiliateFeesShared, lib.BigU(revShare.SharePpm), false)
revShare := types.RevShare{
Recipient: revShare.Address,
RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE,
RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE,
RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL,
QuoteQuantums: feeShared,
RevSharePpm: revShare.SharePpm,
Expand All @@ -277,7 +278,7 @@ func (k Keeper) getUnconditionalRevShares(
func (k Keeper) getMarketMapperRevShare(
ctx sdk.Context,
marketId uint32,
netFees *big.Int,
netFeesSubAffiliateFeesShared *big.Int,
) ([]types.RevShare, error) {
revShares := []types.RevShare{}
marketMapperRevshareAddress, revenueSharePpm, err := k.GetMarketMapperRevenueShareForMarket(ctx, marketId)
Expand All @@ -288,10 +289,10 @@ func (k Keeper) getMarketMapperRevShare(
return nil, nil
}

marketMapperRevshareAmount := lib.BigMulPpm(netFees, lib.BigU(revenueSharePpm), false)
marketMapperRevshareAmount := lib.BigMulPpm(netFeesSubAffiliateFeesShared, lib.BigU(revenueSharePpm), false)
revShares = append(revShares, types.RevShare{
Recipient: marketMapperRevshareAddress.String(),
RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE,
RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE,
RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER,
QuoteQuantums: marketMapperRevshareAmount,
RevSharePpm: revenueSharePpm,
Expand Down
Loading
Loading