Skip to content

Commit

Permalink
[OTE-817] update trading rewards with new logic (#2300)
Browse files Browse the repository at this point in the history
Co-authored-by: Teddy Ding <[email protected]>
  • Loading branch information
affanv14 and teddyding authored Sep 24, 2024
1 parent b0034d0 commit cc59dd2
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 48 deletions.
63 changes: 32 additions & 31 deletions protocol/testing/e2e/trading_rewards/trading_rewards_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package trading_rewards_test

import (
sdkmath "cosmossdk.io/math"
"github.com/cosmos/gogoproto/proto"
"github.com/dydxprotocol/v4-chain/protocol/app/flags"
"math/big"
"testing"
"time"

sdkmath "cosmossdk.io/math"
"github.com/cosmos/gogoproto/proto"
"github.com/dydxprotocol/v4-chain/protocol/app/flags"

"github.com/cometbft/cometbft/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
Expand Down Expand Up @@ -229,20 +230,20 @@ func TestTradingRewards(t *testing.T) {
},
{
AccAddress: RewardsTreasuryAccAddress,
// Total of ~5.06 full coins have vested, which is less than calculated
// rewards (~5.5 full coins). So all reward tokens were distributed.
// Total of ~5.06 full coins have vested, calculated rewards are
// ~1.99 full coins. So remaining rewards are ~3.07 full coins.
Balance: big_testutil.MustFirst(new(big.Int).SetString(
"0",
"3077645653924902967",
10,
)),
},
{
AccAddress: constants.AliceAccAddress,
// starting balance + ~5.06 full coins rewards
// starting balance + ~1.99 full coins rewards
Balance: new(big.Int).Add(
TestAccountStartingTokenBalance,
big_testutil.MustFirst(new(big.Int).SetString(
"5068012730847979890",
"1990367076923076923",
10,
)),
),
Expand All @@ -258,7 +259,7 @@ func TestTradingRewards(t *testing.T) {
TradingRewards: []*indexerevents.AddressTradingReward{
{
Owner: constants.AliceAccAddress.String(),
DenomAmount: dtypes.NewIntFromUint64(5068012730847979890),
DenomAmount: dtypes.NewIntFromUint64(1990367076923076923),
},
},
},
Expand All @@ -277,10 +278,10 @@ func TestTradingRewards(t *testing.T) {
},
{
AccAddress: RewardsTreasuryAccAddress,
// ~25.34 full coins. Note this is exactly 10x the amount vested per block,
// balance + ~25.34 full coins. Note this is exactly 10x the amount vested per block,
// since 10 blocks has passed since the last check.
Balance: big_testutil.MustFirst(new(big.Int).SetString(
"25340063654239899450",
"28417709308164802417",
10,
)),
},
Expand All @@ -300,23 +301,23 @@ func TestTradingRewards(t *testing.T) {
{
AccAddress: constants.BobAccAddress,
// Starting balance: 500000000000000000000000
// Total rewards = (TakerFee - TakerVolume * MaxMakerRebate) * 0.99
// = ($28003 * 0.05% - $28003 * 0.011%) * 0.99
// = ($14.0015 - $3.08033) 0.99 = $10.8119583
// Reward tokens = $10.8119583 / $1.95 = 5.544594 full coins
// Total rewards = (TakerFee - TakerVolume * MaxMakerRebate - (takerFee * MaxPossibleTakerFeeRevShare)) * 0.99
// = ($28003 * 0.05% - $28003 * 0.011% - $28003 * 0.05% * 0.5) * 0.99
// = ($14.0015 - $3.08033 - $7.00075) 0.99 = $3.8812158
// Reward tokens = $3.8812158 / $1.95 = 1.9903670769 full coins
Balance: new(big.Int).Add(
TestAccountStartingTokenBalance,
big_testutil.MustFirst(new(big.Int).SetString(
"5544594000000000000",
"1990367076923076923",
10,
)),
),
},
{
AccAddress: RewardsTreasuryAccAddress,
// 25.34 + 2.534 - 5.544594 ~= 22.329 full coins
// balance + 25.34 + 2.534 - 1.9903670769 ~= 22.329 full coins
Balance: big_testutil.MustFirst(new(big.Int).SetString(
"22329476019663889395",
"28961348596665715439",
10,
)),
},
Expand All @@ -326,7 +327,7 @@ func TestTradingRewards(t *testing.T) {
TradingRewards: []*indexerevents.AddressTradingReward{
{
Owner: constants.BobAccAddress.String(),
DenomAmount: dtypes.NewIntFromUint64(5544594000000000000),
DenomAmount: dtypes.NewIntFromUint64(1990367076923076923),
},
},
},
Expand Down Expand Up @@ -482,8 +483,8 @@ func TestTradingRewards(t *testing.T) {
// - Carl and Dave: $12.519
// Total rewards tokens distributed: ~25.34 (less than the value of net fees)
// Entitled reward tokens:
// - Alice and Bob: 8.0539
// - Carl and Dave: 4.616
// - Alice and Bob: 3.98073
// - Carl and Dave: 2.28156
ExpectedBalances: []expectedBalance{
{
AccAddress: RewardsVesterAccAddress,
Expand All @@ -495,9 +496,9 @@ func TestTradingRewards(t *testing.T) {
},
{
AccAddress: RewardsTreasuryAccAddress,
// All vested rewards were distributed, only rounding dusts left.
// 12.52458 full coins distributed, ~12.815 full coins remaining
Balance: big_testutil.MustFirst(new(big.Int).SetString(
"2",
"12815456885009130222",
10,
)),
},
Expand All @@ -506,7 +507,7 @@ func TestTradingRewards(t *testing.T) {
Balance: new(big.Int).Add(
TestAccountStartingTokenBalance,
big_testutil.MustFirst(new(big.Int).SetString(
"8053910091363583686",
"3980734153846153845",
10,
)),
),
Expand All @@ -516,7 +517,7 @@ func TestTradingRewards(t *testing.T) {
Balance: new(big.Int).Add(
TestAccountStartingTokenBalance,
big_testutil.MustFirst(new(big.Int).SetString(
"8053910091363583686",
"3980734153846153845",
10,
)),
),
Expand All @@ -526,7 +527,7 @@ func TestTradingRewards(t *testing.T) {
Balance: new(big.Int).Add(
TestAccountStartingTokenBalance,
big_testutil.MustFirst(new(big.Int).SetString(
"4616121735756366038",
"2281569230769230769",
10,
)),
),
Expand All @@ -536,7 +537,7 @@ func TestTradingRewards(t *testing.T) {
Balance: new(big.Int).Add(
TestAccountStartingTokenBalance,
big_testutil.MustFirst(new(big.Int).SetString(
"4616121735756366038",
"2281569230769230769",
10,
)),
),
Expand All @@ -547,19 +548,19 @@ func TestTradingRewards(t *testing.T) {
TradingRewards: []*indexerevents.AddressTradingReward{
{
Owner: constants.BobAccAddress.String(),
DenomAmount: dtypes.NewIntFromUint64(8053910091363583686),
DenomAmount: dtypes.NewIntFromUint64(3980734153846153845),
},
{
Owner: constants.AliceAccAddress.String(),
DenomAmount: dtypes.NewIntFromUint64(8053910091363583686),
DenomAmount: dtypes.NewIntFromUint64(3980734153846153845),
},
{
Owner: constants.CarlAccAddress.String(),
DenomAmount: dtypes.NewIntFromUint64(4616121735756366038),
DenomAmount: dtypes.NewIntFromUint64(2281569230769230769),
},
{
Owner: constants.DaveAccAddress.String(),
DenomAmount: dtypes.NewIntFromUint64(4616121735756366038),
DenomAmount: dtypes.NewIntFromUint64(2281569230769230769),
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion protocol/x/revshare/keeper/revshare.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func (k Keeper) getAffiliateRevShares(
) ([]types.RevShare, error) {
takerAddr := fill.TakerAddr
takerFee := fill.TakerFeeQuoteQuantums
if fill.MonthlyRollingTakerVolumeQuantums >= types.Max30dRefereeVolumeQuantums {
if fill.MonthlyRollingTakerVolumeQuantums >= types.MaxReferee30dVolumeForAffiliateShareQuantums {
return nil, nil
}

Expand Down
2 changes: 1 addition & 1 deletion protocol/x/revshare/keeper/revshare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) {
MakerFeeQuoteQuantums: big.NewInt(2_000_000),
FillQuoteQuantums: big.NewInt(100_000_000_000),
ProductId: perpetualId,
MonthlyRollingTakerVolumeQuantums: types.Max30dRefereeVolumeQuantums + 1,
MonthlyRollingTakerVolumeQuantums: types.MaxReferee30dVolumeForAffiliateShareQuantums + 1,
MarketId: marketId,
},
expectedRevSharesForFill: types.RevSharesForFill{
Expand Down
4 changes: 2 additions & 2 deletions protocol/x/revshare/types/constants.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package types

const (
// 25 million USDC
Max30dRefereeVolumeQuantums = uint64(25_000_000_000_000)
// 50 million USDC
MaxReferee30dVolumeForAffiliateShareQuantums = uint64(50_000_000_000_000)
)
24 changes: 18 additions & 6 deletions protocol/x/rewards/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/dydxprotocol/v4-chain/protocol/lib"
"github.com/dydxprotocol/v4-chain/protocol/lib/log"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types"
clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
revsharetypes "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types"
"github.com/dydxprotocol/v4-chain/protocol/x/rewards/types"
Expand Down Expand Up @@ -120,15 +121,20 @@ func (k Keeper) GetRewardShare(
//
// Within each block, total reward share score for an address is defined as:
//
// reward_share_score = total_taker_fees_paid - total_rev_shared_taker_fee
// reward_share_score = total_taker_fees_paid - max_possible_taker_fee_rev_share
// - max_possible_maker_rebate * taker_volume + total_positive_maker_fees - total_rev_shared_maker_fee
//
// Hence, for each fill, increment reward share score as follow:
// - Let F = sum(percentages of general rev-share) (excluding taker only rev share i.e. affiliate)
// - For maker address, positive_maker_fees * (1 - F) are added to reward share score.
// - For taker address, (positive_taker_fees - max_possible_maker_rebate
// * fill_quote_quantum - taker_fee_rev_share) * (1 - F)
// * fill_quote_quantum - max_possible_taker_fee_rev_share) * (1 - F)
// are added to reward share score.
// max_possible_taker_fee_rev_share is 0 when taker trailing volume is > MaxReferee30dVolumeForAffiliateShareQuantums,
// since taker_fee_share is only affiliate at the moment, and they don’t generate affiliate rev share.
// When taker volume ≤ MaxReferee30dVolumeForAffiliateShareQuantums,
// max_possible_taker_fee_rev_share = max_vip_affiliate_share * taker_fee
// regardless of if the taker has an affiliate or not.

func (k Keeper) AddRewardSharesForFill(
ctx sdk.Context,
Expand All @@ -143,9 +149,15 @@ func (k Keeper) AddRewardSharesForFill(
if value, ok := revSharesForFill.FeeSourceToRevSharePpm[revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE]; ok {
totalNetFeeRevSharePpm = value
}
totalTakerFeeRevShareQuantums := big.NewInt(0)
if value, ok := revSharesForFill.FeeSourceToQuoteQuantums[revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE]; ok {
totalTakerFeeRevShareQuantums = value
maxPossibleTakerFeeRevShare := big.NewInt(0)

// taker revshare is always 0 if taker rolling volume is greater than or equal
// to Max30dTakerVolumeQuantums, so no need to reduce score by `max_possible_taker_fee_rev_share`
if fill.MonthlyRollingTakerVolumeQuantums < revsharetypes.MaxReferee30dVolumeForAffiliateShareQuantums {
maxPossibleTakerFeeRevShare = lib.BigMulPpm(fill.TakerFeeQuoteQuantums,
lib.BigU(affiliatetypes.AffiliatesRevSharePpmCap),
false,
)
}

totalFeeSubNetRevSharePpm := lib.OneMillion - totalNetFeeRevSharePpm
Expand All @@ -159,7 +171,7 @@ func (k Keeper) AddRewardSharesForFill(
)
netTakerFee = netTakerFee.Sub(
netTakerFee,
totalTakerFeeRevShareQuantums,
maxPossibleTakerFeeRevShare,
)
takerWeight := lib.BigMulPpm(
netTakerFee,
Expand Down
75 changes: 68 additions & 7 deletions protocol/x/rewards/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func TestAddRewardSharesForFill(t *testing.T) {
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(1_200_000), // 2 - 0.1% * 800
Weight: dtypes.NewInt(200_000), // 2 - 0.1% * 800 -(2 * 0.5)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Expand Down Expand Up @@ -218,7 +218,7 @@ func TestAddRewardSharesForFill(t *testing.T) {
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(1_250_000), // 2 - 0.1% * 750
Weight: dtypes.NewInt(250_000), // 2 - 0.1% * 750 - (2 * 0.5)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Expand Down Expand Up @@ -252,7 +252,7 @@ func TestAddRewardSharesForFill(t *testing.T) {
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(1_625_000), // 2 - 0.05% * 750
Weight: dtypes.NewInt(625_000), // 2 - 0.05% * 750 - (2 * 0.5)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Expand Down Expand Up @@ -319,7 +319,7 @@ func TestAddRewardSharesForFill(t *testing.T) {
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(700_000),
Weight: dtypes.NewInt(350_000), // 0.7 - (0.7 * 0.5)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Expand Down Expand Up @@ -367,7 +367,7 @@ func TestAddRewardSharesForFill(t *testing.T) {
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(1_080_000), // (2 - 0.1% * 800 - 0) * (1 - 0.1)
Weight: dtypes.NewInt(180_000), // (2 - 0.1% * 800 - 0.5*2) * (1 - 0.1)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Expand Down Expand Up @@ -422,7 +422,7 @@ func TestAddRewardSharesForFill(t *testing.T) {
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(960_000), // (2 - 0.1% * 800 - 0) * (1 - 0.2)
Weight: dtypes.NewInt(160_000), // (2 - 0.1% * 800 - 0.5*2) * (1 - 0.2)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Expand Down Expand Up @@ -483,7 +483,68 @@ func TestAddRewardSharesForFill(t *testing.T) {
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(900_000), // (2 - 0.1% * 800 - 0.2) * (1 - 0.1)
Weight: dtypes.NewInt(180_000), // (2 - 0.1% * 800 - 1) * (1 - 0.1)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Weight: dtypes.NewInt(900_000), // 1 * (1 - 0.1)
},
},
"positive maker + taker fees reduced by maker rebate, taker + net fee revshare,rolling taker volume > 50 mil": {
prevTakerRewardShare: nil,
prevMakerRewardShare: nil,
fill: clobtypes.FillForProcess{
TakerAddr: takerAddress,
TakerFeeQuoteQuantums: big.NewInt(2_000_000),
MakerAddr: makerAddress,
MakerFeeQuoteQuantums: big.NewInt(1_000_000),
FillQuoteQuantums: big.NewInt(800_000_000),
ProductId: uint32(1),
MarketId: uint32(1),
MonthlyRollingTakerVolumeQuantums: 60_000_000_000_000,
},
revSharesForFill: revsharetypes.RevSharesForFill{
AllRevShares: []revsharetypes.RevShare{
{
Recipient: constants.AliceAccAddress.String(),
RevShareFeeSource: revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE,
RevShareType: revsharetypes.REV_SHARE_TYPE_UNCONDITIONAL,
QuoteQuantums: big.NewInt(200_000),
RevSharePpm: 100_000, // 10%
},
{
Recipient: takerAddress,
RevShareFeeSource: revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE,
RevShareType: revsharetypes.REV_SHARE_TYPE_AFFILIATE,
QuoteQuantums: big.NewInt(200_000),
RevSharePpm: 100_000, // 10%
},
},
FeeSourceToQuoteQuantums: map[revsharetypes.RevShareFeeSource]*big.Int{
revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(200_000),
revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(200_000),
},
FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{
revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE: 100_000, // 10%
revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE: 100_000, // 10%
},
AffiliateRevShare: &revsharetypes.RevShare{
Recipient: takerAddress,
RevShareFeeSource: revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE,
RevShareType: revsharetypes.REV_SHARE_TYPE_AFFILIATE,
QuoteQuantums: big.NewInt(200_000),
RevSharePpm: 100_000, // 10%
},
},
feeTiers: []*feetierstypes.PerpetualFeeTier{
{
MakerFeePpm: -1_000, // -0.1%
TakerFeePpm: 2_000, // 0.2%
},
},
expectedTakerShare: types.RewardShare{
Address: takerAddress,
Weight: dtypes.NewInt(1_080_000), // (2 - 0.1% * 800 - 0) * (1 - 0.1)
},
expectedMakerShare: types.RewardShare{
Address: makerAddress,
Expand Down

0 comments on commit cc59dd2

Please sign in to comment.