diff --git a/protocol/app/app.go b/protocol/app/app.go index b5a2c14a24..ca7f12c16d 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -1130,6 +1130,7 @@ func New( clobFlags, rate_limit.NewPanicRateLimiter[sdk.Msg](), daemonLiquidationInfo, + app.RevShareKeeper, ) clobModule := clobmodule.NewAppModule( appCodec, diff --git a/protocol/indexer/events/order_fill.go b/protocol/indexer/events/order_fill.go index 467ad3d259..845993dfb5 100644 --- a/protocol/indexer/events/order_fill.go +++ b/protocol/indexer/events/order_fill.go @@ -2,6 +2,7 @@ package events import ( "fmt" + "math/big" v1 "github.com/dydxprotocol/v4-chain/protocol/indexer/protocol/v1" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" @@ -19,6 +20,7 @@ func NewOrderFillEvent( takerFee int64, totalFilledMaker satypes.BaseQuantums, totalFilledTaker satypes.BaseQuantums, + affiliateRevShareQuoteQuantums *big.Int, ) *OrderFillEventV1 { indexerTakerOrder := v1.OrderToIndexerOrder(takerOrder) return &OrderFillEventV1{ @@ -31,6 +33,8 @@ func NewOrderFillEvent( TakerFee: takerFee, TotalFilledMaker: totalFilledMaker.ToUint64(), TotalFilledTaker: totalFilledTaker.ToUint64(), + // Since revshare is always less than taker fee, this will not overflow. + AffiliateRevShare: affiliateRevShareQuoteQuantums.Uint64(), } } @@ -44,6 +48,7 @@ func NewLiquidationOrderFillEvent( makerFee int64, takerFee int64, totalFilledMaker satypes.BaseQuantums, + affiliateRevShareQuoteQuantums *big.Int, ) *OrderFillEventV1 { if !liquidationTakerOrder.IsLiquidation() { panic(fmt.Sprintf("liquidationTakerOrder is not a liquidation order: %v", liquidationTakerOrder)) @@ -64,5 +69,7 @@ func NewLiquidationOrderFillEvent( TakerFee: takerFee, TotalFilledMaker: totalFilledMaker.ToUint64(), TotalFilledTaker: fillAmount.ToUint64(), + // Since revshare is always less than taker fee, this will not overflow. + AffiliateRevShare: affiliateRevShareQuoteQuantums.Uint64(), } } diff --git a/protocol/indexer/events/order_fill_test.go b/protocol/indexer/events/order_fill_test.go index 573d47a21a..98443d2d76 100644 --- a/protocol/indexer/events/order_fill_test.go +++ b/protocol/indexer/events/order_fill_test.go @@ -1,6 +1,7 @@ package events_test import ( + "math/big" "testing" "github.com/dydxprotocol/v4-chain/protocol/indexer/events" @@ -20,6 +21,7 @@ var ( fillAmount = satypes.BaseQuantums(5) makerFee = int64(-2) takerFee = int64(5) + affiliateRevShare = big.NewInt(0) ) func TestNewOrderFillEvent_Success(t *testing.T) { @@ -31,6 +33,7 @@ func TestNewOrderFillEvent_Success(t *testing.T) { takerFee, fillAmount, fillAmount, + affiliateRevShare, ) expectedOrderFillEventProto := &events.OrderFillEventV1{ @@ -38,11 +41,12 @@ func TestNewOrderFillEvent_Success(t *testing.T) { TakerOrder: &events.OrderFillEventV1_Order{ Order: &indexerTakerOrder, }, - FillAmount: fillAmount.ToUint64(), - MakerFee: makerFee, - TakerFee: takerFee, - TotalFilledMaker: fillAmount.ToUint64(), - TotalFilledTaker: fillAmount.ToUint64(), + FillAmount: fillAmount.ToUint64(), + MakerFee: makerFee, + TakerFee: takerFee, + TotalFilledMaker: fillAmount.ToUint64(), + TotalFilledTaker: fillAmount.ToUint64(), + AffiliateRevShare: affiliateRevShare.Uint64(), } require.Equal(t, expectedOrderFillEventProto, orderFillEvent) } @@ -56,6 +60,7 @@ func TestNewLiquidationOrderFillEvent_Success(t *testing.T) { makerFee, takerFee, fillAmount, + affiliateRevShare, ) expectedLiquidationOrder := events.LiquidationOrderV1{ @@ -71,11 +76,12 @@ func TestNewLiquidationOrderFillEvent_Success(t *testing.T) { TakerOrder: &events.OrderFillEventV1_LiquidationOrder{ LiquidationOrder: &expectedLiquidationOrder, }, - FillAmount: fillAmount.ToUint64(), - MakerFee: makerFee, - TakerFee: takerFee, - TotalFilledMaker: fillAmount.ToUint64(), - TotalFilledTaker: fillAmount.ToUint64(), + FillAmount: fillAmount.ToUint64(), + MakerFee: makerFee, + TakerFee: takerFee, + TotalFilledMaker: fillAmount.ToUint64(), + TotalFilledTaker: fillAmount.ToUint64(), + AffiliateRevShare: affiliateRevShare.Uint64(), } require.Equal(t, expectedOrderFillEventProto, liquidationOrderFillEvent) } diff --git a/protocol/mocks/ClobKeeper.go b/protocol/mocks/ClobKeeper.go index 62ab311a6b..5a65322993 100644 --- a/protocol/mocks/ClobKeeper.go +++ b/protocol/mocks/ClobKeeper.go @@ -1000,7 +1000,7 @@ func (_m *ClobKeeper) ProcessProposerOperations(ctx types.Context, operations [] } // ProcessSingleMatch provides a mock function with given fields: ctx, matchWithOrders -func (_m *ClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, error) { +func (_m *ClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, *big.Int, error) { ret := _m.Called(ctx, matchWithOrders) if len(ret) == 0 { @@ -1010,8 +1010,9 @@ func (_m *ClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clo var r0 bool var r1 subaccountstypes.UpdateResult var r2 subaccountstypes.UpdateResult - var r3 error - if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, error)); ok { + var r3 *big.Int + var r4 error + if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, *big.Int, error)); ok { return rf(ctx, matchWithOrders) } if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MatchWithOrders) bool); ok { @@ -1032,13 +1033,21 @@ func (_m *ClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clo r2 = ret.Get(2).(subaccountstypes.UpdateResult) } - if rf, ok := ret.Get(3).(func(types.Context, *clobtypes.MatchWithOrders) error); ok { + if rf, ok := ret.Get(3).(func(types.Context, *clobtypes.MatchWithOrders) *big.Int); ok { r3 = rf(ctx, matchWithOrders) } else { - r3 = ret.Error(3) + if ret.Get(3) != nil { + r3 = ret.Get(3).(*big.Int) + } + } + + if rf, ok := ret.Get(4).(func(types.Context, *clobtypes.MatchWithOrders) error); ok { + r4 = rf(ctx, matchWithOrders) + } else { + r4 = ret.Error(4) } - return r0, r1, r2, r3 + return r0, r1, r2, r3, r4 } // PruneStateFillAmountsForShortTermOrders provides a mock function with given fields: ctx diff --git a/protocol/mocks/MemClobKeeper.go b/protocol/mocks/MemClobKeeper.go index a897a64e8e..adfdd745f4 100644 --- a/protocol/mocks/MemClobKeeper.go +++ b/protocol/mocks/MemClobKeeper.go @@ -321,7 +321,7 @@ func (_m *MemClobKeeper) OffsetSubaccountPerpetualPosition(ctx types.Context, li } // ProcessSingleMatch provides a mock function with given fields: ctx, matchWithOrders -func (_m *MemClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, error) { +func (_m *MemClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, *big.Int, error) { ret := _m.Called(ctx, matchWithOrders) if len(ret) == 0 { @@ -331,8 +331,9 @@ func (_m *MemClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders * var r0 bool var r1 subaccountstypes.UpdateResult var r2 subaccountstypes.UpdateResult - var r3 error - if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, error)); ok { + var r3 *big.Int + var r4 error + if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, *big.Int, error)); ok { return rf(ctx, matchWithOrders) } if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MatchWithOrders) bool); ok { @@ -353,13 +354,21 @@ func (_m *MemClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders * r2 = ret.Get(2).(subaccountstypes.UpdateResult) } - if rf, ok := ret.Get(3).(func(types.Context, *clobtypes.MatchWithOrders) error); ok { + if rf, ok := ret.Get(3).(func(types.Context, *clobtypes.MatchWithOrders) *big.Int); ok { r3 = rf(ctx, matchWithOrders) } else { - r3 = ret.Error(3) + if ret.Get(3) != nil { + r3 = ret.Get(3).(*big.Int) + } } - return r0, r1, r2, r3 + if rf, ok := ret.Get(4).(func(types.Context, *clobtypes.MatchWithOrders) error); ok { + r4 = rf(ctx, matchWithOrders) + } else { + r4 = ret.Error(4) + } + + return r0, r1, r2, r3, r4 } // ReplayPlaceOrder provides a mock function with given fields: ctx, msg diff --git a/protocol/mocks/mocks/CacheMultiStore.go b/protocol/mocks/mocks/CacheMultiStore.go index 4197ba0380..b5e0d29d24 100644 --- a/protocol/mocks/mocks/CacheMultiStore.go +++ b/protocol/mocks/mocks/CacheMultiStore.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.44.1. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type CacheMultiStore struct { func (_m *CacheMultiStore) CacheMultiStore() types.CacheMultiStore { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheMultiStore") + } + var r0 types.CacheMultiStore if rf, ok := ret.Get(0).(func() types.CacheMultiStore); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *CacheMultiStore) CacheMultiStore() types.CacheMultiStore { func (_m *CacheMultiStore) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { ret := _m.Called(version) + if len(ret) == 0 { + panic("no return value specified for CacheMultiStoreWithVersion") + } + var r0 types.CacheMultiStore var r1 error if rf, ok := ret.Get(0).(func(int64) (types.CacheMultiStore, error)); ok { @@ -60,6 +68,10 @@ func (_m *CacheMultiStore) CacheMultiStoreWithVersion(version int64) (types.Cach func (_m *CacheMultiStore) CacheWrap() types.CacheWrap { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheWrap") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func() types.CacheWrap); ok { r0 = rf() @@ -76,6 +88,10 @@ func (_m *CacheMultiStore) CacheWrap() types.CacheWrap { func (_m *CacheMultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { ret := _m.Called(w, tc) + if len(ret) == 0 { + panic("no return value specified for CacheWrapWithTrace") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func(io.Writer, types.TraceContext) types.CacheWrap); ok { r0 = rf(w, tc) @@ -92,6 +108,10 @@ func (_m *CacheMultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext func (_m *CacheMultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetKVStore") + } + var r0 types.KVStore if rf, ok := ret.Get(0).(func(types.StoreKey) types.KVStore); ok { r0 = rf(_a0) @@ -108,6 +128,10 @@ func (_m *CacheMultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { func (_m *CacheMultiStore) GetStore(_a0 types.StoreKey) types.Store { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetStore") + } + var r0 types.Store if rf, ok := ret.Get(0).(func(types.StoreKey) types.Store); ok { r0 = rf(_a0) @@ -124,6 +148,10 @@ func (_m *CacheMultiStore) GetStore(_a0 types.StoreKey) types.Store { func (_m *CacheMultiStore) GetStoreType() types.StoreType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetStoreType") + } + var r0 types.StoreType if rf, ok := ret.Get(0).(func() types.StoreType); ok { r0 = rf() @@ -138,6 +166,10 @@ func (_m *CacheMultiStore) GetStoreType() types.StoreType { func (_m *CacheMultiStore) LatestVersion() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestVersion") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() @@ -152,6 +184,10 @@ func (_m *CacheMultiStore) LatestVersion() int64 { func (_m *CacheMultiStore) SetTracer(w io.Writer) types.MultiStore { ret := _m.Called(w) + if len(ret) == 0 { + panic("no return value specified for SetTracer") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(io.Writer) types.MultiStore); ok { r0 = rf(w) @@ -168,6 +204,10 @@ func (_m *CacheMultiStore) SetTracer(w io.Writer) types.MultiStore { func (_m *CacheMultiStore) SetTracingContext(_a0 types.TraceContext) types.MultiStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for SetTracingContext") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(types.TraceContext) types.MultiStore); ok { r0 = rf(_a0) @@ -184,6 +224,10 @@ func (_m *CacheMultiStore) SetTracingContext(_a0 types.TraceContext) types.Multi func (_m *CacheMultiStore) TracingEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TracingEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -199,13 +243,12 @@ func (_m *CacheMultiStore) Write() { _m.Called() } -type mockConstructorTestingTNewCacheMultiStore interface { +// NewCacheMultiStore creates a new instance of CacheMultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCacheMultiStore(t interface { mock.TestingT Cleanup(func()) -} - -// NewCacheMultiStore creates a new instance of CacheMultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewCacheMultiStore(t mockConstructorTestingTNewCacheMultiStore) *CacheMultiStore { +}) *CacheMultiStore { mock := &CacheMultiStore{} mock.Mock.Test(t) diff --git a/protocol/mocks/mocks/MultiStore.go b/protocol/mocks/mocks/MultiStore.go index ec8fed1de5..20e9c3830a 100644 --- a/protocol/mocks/mocks/MultiStore.go +++ b/protocol/mocks/mocks/MultiStore.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.44.1. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type MultiStore struct { func (_m *MultiStore) CacheMultiStore() types.CacheMultiStore { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheMultiStore") + } + var r0 types.CacheMultiStore if rf, ok := ret.Get(0).(func() types.CacheMultiStore); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *MultiStore) CacheMultiStore() types.CacheMultiStore { func (_m *MultiStore) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { ret := _m.Called(version) + if len(ret) == 0 { + panic("no return value specified for CacheMultiStoreWithVersion") + } + var r0 types.CacheMultiStore var r1 error if rf, ok := ret.Get(0).(func(int64) (types.CacheMultiStore, error)); ok { @@ -60,6 +68,10 @@ func (_m *MultiStore) CacheMultiStoreWithVersion(version int64) (types.CacheMult func (_m *MultiStore) CacheWrap() types.CacheWrap { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheWrap") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func() types.CacheWrap); ok { r0 = rf() @@ -76,6 +88,10 @@ func (_m *MultiStore) CacheWrap() types.CacheWrap { func (_m *MultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { ret := _m.Called(w, tc) + if len(ret) == 0 { + panic("no return value specified for CacheWrapWithTrace") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func(io.Writer, types.TraceContext) types.CacheWrap); ok { r0 = rf(w, tc) @@ -92,6 +108,10 @@ func (_m *MultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) typ func (_m *MultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetKVStore") + } + var r0 types.KVStore if rf, ok := ret.Get(0).(func(types.StoreKey) types.KVStore); ok { r0 = rf(_a0) @@ -108,6 +128,10 @@ func (_m *MultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { func (_m *MultiStore) GetStore(_a0 types.StoreKey) types.Store { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetStore") + } + var r0 types.Store if rf, ok := ret.Get(0).(func(types.StoreKey) types.Store); ok { r0 = rf(_a0) @@ -124,6 +148,10 @@ func (_m *MultiStore) GetStore(_a0 types.StoreKey) types.Store { func (_m *MultiStore) GetStoreType() types.StoreType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetStoreType") + } + var r0 types.StoreType if rf, ok := ret.Get(0).(func() types.StoreType); ok { r0 = rf() @@ -138,6 +166,10 @@ func (_m *MultiStore) GetStoreType() types.StoreType { func (_m *MultiStore) LatestVersion() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestVersion") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() @@ -152,6 +184,10 @@ func (_m *MultiStore) LatestVersion() int64 { func (_m *MultiStore) SetTracer(w io.Writer) types.MultiStore { ret := _m.Called(w) + if len(ret) == 0 { + panic("no return value specified for SetTracer") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(io.Writer) types.MultiStore); ok { r0 = rf(w) @@ -168,6 +204,10 @@ func (_m *MultiStore) SetTracer(w io.Writer) types.MultiStore { func (_m *MultiStore) SetTracingContext(_a0 types.TraceContext) types.MultiStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for SetTracingContext") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(types.TraceContext) types.MultiStore); ok { r0 = rf(_a0) @@ -184,6 +224,10 @@ func (_m *MultiStore) SetTracingContext(_a0 types.TraceContext) types.MultiStore func (_m *MultiStore) TracingEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TracingEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -194,13 +238,12 @@ func (_m *MultiStore) TracingEnabled() bool { return r0 } -type mockConstructorTestingTNewMultiStore interface { +// NewMultiStore creates a new instance of MultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMultiStore(t interface { mock.TestingT Cleanup(func()) -} - -// NewMultiStore creates a new instance of MultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMultiStore(t mockConstructorTestingTNewMultiStore) *MultiStore { +}) *MultiStore { mock := &MultiStore{} mock.Mock.Test(t) diff --git a/protocol/testutil/keeper/clob.go b/protocol/testutil/keeper/clob.go index b86ed48430..f5bee3fad1 100644 --- a/protocol/testutil/keeper/clob.go +++ b/protocol/testutil/keeper/clob.go @@ -186,6 +186,7 @@ func NewClobKeepersTestContextWithUninitializedMemStore( ks.StatsKeeper, ks.RewardsKeeper, ks.SubaccountsKeeper, + revShareKeeper, indexerEventManager, indexerEventsTransientStoreKey, ) @@ -224,6 +225,7 @@ func createClobKeeper( statsKeeper *statskeeper.Keeper, rewardsKeeper types.RewardsKeeper, saKeeper *subkeeper.Keeper, + revShareKeeper types.RevShareKeeper, indexerEventManager indexer_manager.IndexerEventManager, indexerEventsTransientStoreKey storetypes.StoreKey, ) (*keeper.Keeper, storetypes.StoreKey, storetypes.StoreKey) { @@ -260,6 +262,7 @@ func createClobKeeper( flags.GetDefaultClobFlags(), rate_limit.NewNoOpRateLimiter[sdk.Msg](), liquidationtypes.NewDaemonLiquidationInfo(), + revShareKeeper, ) k.SetAnteHandler(constants.EmptyAnteHandler) diff --git a/protocol/testutil/keeper/listing.go b/protocol/testutil/keeper/listing.go index 0313b0873c..dbf11239f8 100644 --- a/protocol/testutil/keeper/listing.go +++ b/protocol/testutil/keeper/listing.go @@ -151,6 +151,7 @@ func ListingKeepers( statsKeeper, rewardsKeeper, subaccountsKeeper, + revShareKeeper, indexerEventManager, transientStoreKey, ) diff --git a/protocol/testutil/keeper/subaccounts.go b/protocol/testutil/keeper/subaccounts.go index bfbd4079a4..51df4c148e 100644 --- a/protocol/testutil/keeper/subaccounts.go +++ b/protocol/testutil/keeper/subaccounts.go @@ -5,6 +5,7 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/streaming" + affiliateskeeper "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/keeper" revsharekeeper "github.com/dydxprotocol/v4-chain/protocol/x/revshare/keeper" "github.com/cosmos/gogoproto/proto" @@ -41,6 +42,7 @@ func SubaccountsKeepers(t testing.TB, msgSenderEnabled bool) ( assetsKeeper *asskeeper.Keeper, blocktimeKeeper *blocktimekeeper.Keeper, revShareKeeper *revsharekeeper.Keeper, + affiliatesKeeper *affiliateskeeper.Keeper, storeKey storetypes.StoreKey, ) { var mockTimeProvider *mocks.TimeProvider @@ -74,8 +76,9 @@ func SubaccountsKeepers(t testing.TB, msgSenderEnabled bool) ( cdc, stakingKeeper, ) - affiliatesKeeper, _ := createAffiliatesKeeper(stateStore, db, cdc, statsKeeper, transientStoreKey, true) + affiliatesKeeper, _ = createAffiliatesKeeper(stateStore, db, cdc, statsKeeper, transientStoreKey, true) revShareKeeper, _, _ = createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper) + affiliatesKeeper.SetRevShareKeeper(revShareKeeper) marketMapKeeper, _ := createMarketMapKeeper(stateStore, db, cdc) pricesKeeper, _, _, mockTimeProvider = createPricesKeeper( stateStore, @@ -102,7 +105,7 @@ func SubaccountsKeepers(t testing.TB, msgSenderEnabled bool) ( msgSenderEnabled, ) - return []GenesisInitializer{pricesKeeper, perpetualsKeeper, assetsKeeper, revShareKeeper, keeper} + return []GenesisInitializer{pricesKeeper, perpetualsKeeper, assetsKeeper, revShareKeeper, affiliatesKeeper, keeper} }) // Mock time provider response for market creation. @@ -117,6 +120,7 @@ func SubaccountsKeepers(t testing.TB, msgSenderEnabled bool) ( assetsKeeper, blocktimeKeeper, revShareKeeper, + affiliatesKeeper, storeKey } diff --git a/protocol/testutil/memclob/keeper.go b/protocol/testutil/memclob/keeper.go index ce15a95e29..137df08835 100644 --- a/protocol/testutil/memclob/keeper.go +++ b/protocol/testutil/memclob/keeper.go @@ -331,6 +331,7 @@ func (f *FakeMemClobKeeper) ProcessSingleMatch( success bool, takerUpdateResult satypes.UpdateResult, makerUpdateResult satypes.UpdateResult, + affiliateRevSharesQuoteQuantums *big.Int, err error, ) { makerOrder := matchWithOrders.MakerOrder @@ -363,7 +364,7 @@ func (f *FakeMemClobKeeper) ProcessSingleMatch( ) } - return true, satypes.Success, satypes.Success, nil + return true, satypes.Success, satypes.Success, big.NewInt(0), nil } subaccountMatchedOrders := make(map[satypes.SubaccountId][]types.PendingOpenOrder) @@ -410,7 +411,7 @@ func (f *FakeMemClobKeeper) ProcessSingleMatch( } } - return success, takerUpdateResult, makerUpdateResult, nil + return success, takerUpdateResult, makerUpdateResult, big.NewInt(0), nil } func (f *FakeMemClobKeeper) GetStatePosition( diff --git a/protocol/x/clob/e2e/long_term_orders_test.go b/protocol/x/clob/e2e/long_term_orders_test.go index 3d0b69226b..af1dfde636 100644 --- a/protocol/x/clob/e2e/long_term_orders_test.go +++ b/protocol/x/clob/e2e/long_term_orders_test.go @@ -818,6 +818,7 @@ func TestPlaceLongTermOrder(t *testing.T) { 25_000_000, LongTermPlaceOrder_Alice_Num0_Id0_Clob0_Buy1_Price50000_GTBT5.Order.GetBaseQuantums(), LongTermPlaceOrder_Alice_Num0_Id0_Clob0_Buy1_Price50000_GTBT5.Order.GetBaseQuantums(), + big.NewInt(0), ), ), OrderingWithinBlock: &indexer_manager.IndexerTendermintEvent_TransactionIndex{}, @@ -1161,6 +1162,7 @@ func TestPlaceLongTermOrder(t *testing.T) { 25_000_000, PlaceOrder_Bob_Num0_Id0_Clob0_Sell1_Price50000_GTB20.Order.GetBaseQuantums(), PlaceOrder_Bob_Num0_Id0_Clob0_Sell1_Price50000_GTB20.Order.GetBaseQuantums(), + big.NewInt(0), ), ), OrderingWithinBlock: &indexer_manager.IndexerTendermintEvent_TransactionIndex{}, @@ -1331,6 +1333,7 @@ func TestPlaceLongTermOrder(t *testing.T) { 25_000_000, LongTermPlaceOrder_Alice_Num0_Id0_Clob0_Buy2_Price50000_GTBT5.Order.GetBaseQuantums(), PlaceOrder_Bob_Num0_Id1_Clob0_Sell1_Price50000_GTB20.Order.GetBaseQuantums(), + big.NewInt(0), ), ), OrderingWithinBlock: &indexer_manager.IndexerTendermintEvent_TransactionIndex{}, @@ -1729,6 +1732,7 @@ func TestRegression_InvalidTimeInForce(t *testing.T) { 25_000_000, Invalid_TIF_LongTermPlaceOrder_Alice_Num0_Id0_Clob0_Buy1_Price50000_GTBT5.Order.GetBaseQuantums(), Invalid_TIF_LongTermPlaceOrder_Alice_Num0_Id0_Clob0_Buy1_Price50000_GTBT5.Order.GetBaseQuantums(), + big.NewInt(0), ), ), OrderingWithinBlock: &indexer_manager.IndexerTendermintEvent_TransactionIndex{}, diff --git a/protocol/x/clob/e2e/short_term_orders_test.go b/protocol/x/clob/e2e/short_term_orders_test.go index 8a3ee3e374..0312034c76 100644 --- a/protocol/x/clob/e2e/short_term_orders_test.go +++ b/protocol/x/clob/e2e/short_term_orders_test.go @@ -215,6 +215,7 @@ func TestPlaceOrder(t *testing.T) { 0, PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20.Order.GetBaseQuantums(), PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20.Order.GetBaseQuantums(), + big.NewInt(0), ), ), }, @@ -394,6 +395,7 @@ func TestPlaceOrder(t *testing.T) { 0, PlaceOrder_Bob_Num0_Id0_Clob0_Sell5_Price10_GTB20.Order.GetBaseQuantums(), PlaceOrder_Bob_Num0_Id0_Clob0_Sell5_Price10_GTB20.Order.GetBaseQuantums(), + big.NewInt(0), ), ), }, @@ -573,6 +575,7 @@ func TestPlaceOrder(t *testing.T) { 0, PlaceOrder_Bob_Num0_Id0_Clob0_Sell5_Price10_GTB20.Order.GetBaseQuantums(), PlaceOrder_Bob_Num0_Id0_Clob0_Sell5_Price10_GTB20.Order.GetBaseQuantums(), + big.NewInt(0), ), ), }, diff --git a/protocol/x/clob/keeper/keeper.go b/protocol/x/clob/keeper/keeper.go index 8e607d6833..7b530258ba 100644 --- a/protocol/x/clob/keeper/keeper.go +++ b/protocol/x/clob/keeper/keeper.go @@ -42,6 +42,7 @@ type ( pricesKeeper types.PricesKeeper statsKeeper types.StatsKeeper rewardsKeeper types.RewardsKeeper + revshareKeeper types.RevShareKeeper indexerEventManager indexer_manager.IndexerEventManager streamingManager streamingtypes.FullNodeStreamingManager @@ -91,6 +92,7 @@ func NewKeeper( clobFlags flags.ClobFlags, placeCancelOrderRateLimiter rate_limit.RateLimiter[sdk.Msg], daemonLiquidationInfo *liquidationtypes.DaemonLiquidationInfo, + revshareKeeper types.RevShareKeeper, ) *Keeper { keeper := &Keeper{ cdc: cdc, @@ -122,6 +124,7 @@ func NewKeeper( Flags: clobFlags, placeCancelOrderRateLimiter: placeCancelOrderRateLimiter, DaemonLiquidationInfo: daemonLiquidationInfo, + revshareKeeper: revshareKeeper, } // Provide the keeper to the MemClob. diff --git a/protocol/x/clob/keeper/process_operations.go b/protocol/x/clob/keeper/process_operations.go index cddb5fb68b..5dc706e9d0 100644 --- a/protocol/x/clob/keeper/process_operations.go +++ b/protocol/x/clob/keeper/process_operations.go @@ -486,7 +486,7 @@ func (k Keeper) PersistMatchOrdersToState( } makerOrders = append(makerOrders, makerOrder) - _, _, _, err = k.ProcessSingleMatch(ctx, &matchWithOrders) + _, _, _, affiliateRevSharesQuoteQuantums, err := k.ProcessSingleMatch(ctx, &matchWithOrders) if err != nil { return err } @@ -523,6 +523,7 @@ func (k Keeper) PersistMatchOrdersToState( matchWithOrders.TakerFee, totalFilledMaker, totalFilledTaker, + affiliateRevSharesQuoteQuantums, ), ), ) @@ -594,7 +595,7 @@ func (k Keeper) PersistMatchLiquidationToState( // Write the position updates and state fill amounts for this match. // Note stateless validation on the constructed `matchWithOrders` is performed within this function. - _, _, _, err = k.ProcessSingleMatch( + _, _, _, affiliateRevSharesQuoteQuantums, err := k.ProcessSingleMatch( ctx, &matchWithOrders, ) @@ -625,6 +626,7 @@ func (k Keeper) PersistMatchLiquidationToState( matchWithOrders.MakerFee, matchWithOrders.TakerFee, totalFilledMaker, + affiliateRevSharesQuoteQuantums, ), ), ) diff --git a/protocol/x/clob/keeper/process_operations_test.go b/protocol/x/clob/keeper/process_operations_test.go index b7e7555a86..178c737dd6 100644 --- a/protocol/x/clob/keeper/process_operations_test.go +++ b/protocol/x/clob/keeper/process_operations_test.go @@ -2635,6 +2635,7 @@ func setupNewMockEventManager( match.MakerFee, match.TakerFee, match.TotalFilledTaker, + big.NewInt(0), ), ), ).Return() @@ -2654,6 +2655,7 @@ func setupNewMockEventManager( match.TakerFee, match.TotalFilledMaker, match.TotalFilledTaker, + big.NewInt(0), ), ), ).Return() diff --git a/protocol/x/clob/keeper/process_single_match.go b/protocol/x/clob/keeper/process_single_match.go index d10352498e..e35edf5f61 100644 --- a/protocol/x/clob/keeper/process_single_match.go +++ b/protocol/x/clob/keeper/process_single_match.go @@ -45,6 +45,7 @@ func (k Keeper) ProcessSingleMatch( success bool, takerUpdateResult satypes.UpdateResult, makerUpdateResult satypes.UpdateResult, + affiliateRevSharesQuoteQuantums *big.Int, err error, ) { if matchWithOrders.TakerOrder.IsLiquidation() { @@ -72,7 +73,7 @@ func (k Keeper) ProcessSingleMatch( // Perform stateless validation on the match. if err := matchWithOrders.Validate(); err != nil { - return false, takerUpdateResult, makerUpdateResult, errorsmod.Wrapf( + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, errorsmod.Wrapf( err, "ProcessSingleMatch: Invalid MatchWithOrders: %+v", matchWithOrders, @@ -87,7 +88,7 @@ func (k Keeper) ProcessSingleMatch( clobPairId := makerMatchableOrder.GetClobPairId() clobPair, found := k.GetClobPair(ctx, clobPairId) if !found { - return false, takerUpdateResult, makerUpdateResult, types.ErrInvalidClob + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, types.ErrInvalidClob } // Verify that the `fillAmount` is divisible by the `StepBaseQuantums` of the `clobPair`. @@ -95,6 +96,7 @@ func (k Keeper) ProcessSingleMatch( return false, takerUpdateResult, makerUpdateResult, + affiliateRevSharesQuoteQuantums, types.ErrFillAmountNotDivisibleByStepSize } @@ -126,9 +128,10 @@ func (k Keeper) ProcessSingleMatch( } // Retrieve the associated perpetual id for the `ClobPair`. + // TODO(OTE-805): call this outside of ProcessSingleMatch to avoid duplicate calls. perpetualId, err := clobPair.GetPerpetualId() if err != nil { - return false, takerUpdateResult, makerUpdateResult, err + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err } // Calculate taker and maker fee ppms. @@ -154,7 +157,7 @@ func (k Keeper) ProcessSingleMatch( ) if err != nil { - return false, takerUpdateResult, makerUpdateResult, err + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err } } @@ -187,7 +190,7 @@ func (k Keeper) ProcessSingleMatch( ) if err != nil { - return false, takerUpdateResult, makerUpdateResult, err + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err } } @@ -209,11 +212,11 @@ func (k Keeper) ProcessSingleMatch( ) if err != nil { - return false, takerUpdateResult, makerUpdateResult, err + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err } // Update both subaccounts in the matched order atomically. - takerUpdateResult, makerUpdateResult, err = k.persistMatchedOrders( + takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err = k.persistMatchedOrders( ctx, matchWithOrders, perpetualId, @@ -224,7 +227,7 @@ func (k Keeper) ProcessSingleMatch( ) if err != nil { - return false, takerUpdateResult, makerUpdateResult, err + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err } // Update subaccount total quantums liquidated and total insurance fund lost for liquidation orders. @@ -235,7 +238,7 @@ func (k Keeper) ProcessSingleMatch( fillAmount.ToBigInt(), ) if err != nil { - return false, takerUpdateResult, makerUpdateResult, err + return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err } k.UpdateSubaccountLiquidationInfo( @@ -287,7 +290,7 @@ func (k Keeper) ProcessSingleMatch( curMakerPruneableBlockHeight, ) - return true, takerUpdateResult, makerUpdateResult, nil + return true, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, nil } // persistMatchedOrders persists a matched order to the subaccount state, @@ -307,9 +310,11 @@ func (k Keeper) persistMatchedOrders( ) ( takerUpdateResult satypes.UpdateResult, makerUpdateResult satypes.UpdateResult, + affiliateRevSharesQuoteQuantums *big.Int, err error, ) { isTakerLiquidation := matchWithOrders.TakerOrder.IsLiquidation() + affiliateRevSharesQuoteQuantums = big.NewInt(0) // Taker fees and maker fees/rebates are rounded towards positive infinity. bigTakerFeeQuoteQuantums := lib.BigMulPpm(bigFillQuoteQuantums, lib.BigI(takerFeePpm), true) @@ -400,7 +405,7 @@ func (k Keeper) persistMatchedOrders( satypes.Match, ) if err != nil { - return satypes.UpdateCausedError, satypes.UpdateCausedError, err + return satypes.UpdateCausedError, satypes.UpdateCausedError, affiliateRevSharesQuoteQuantums, err } takerUpdateResult = successPerUpdate[0] @@ -412,7 +417,7 @@ func (k Keeper) persistMatchedOrders( successPerUpdate, updates, ); updateResultErr != nil { - return takerUpdateResult, makerUpdateResult, updateResultErr + return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, updateResultErr } if !success { @@ -432,18 +437,45 @@ func (k Keeper) persistMatchedOrders( // keeper. if err := k.subaccountsKeeper.TransferInsuranceFundPayments(ctx, insuranceFundDelta, perpetualId); err != nil { - return takerUpdateResult, makerUpdateResult, err + return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err + } + perpetual, err := k.perpetualsKeeper.GetPerpetual(ctx, perpetualId) + if err != nil { + return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err + } + + fillForProcess := types.FillForProcess{ + TakerAddr: matchWithOrders.TakerOrder.GetSubaccountId().Owner, + TakerFeeQuoteQuantums: bigTakerFeeQuoteQuantums, + MakerAddr: matchWithOrders.MakerOrder.GetSubaccountId().Owner, + MakerFeeQuoteQuantums: bigMakerFeeQuoteQuantums, + FillQuoteQuantums: bigFillQuoteQuantums, + ProductId: perpetualId, + MarketId: perpetual.Params.MarketId, + MonthlyRollingTakerVolumeQuantums: k.statsKeeper.GetUserStats( + ctx, + matchWithOrders.TakerOrder.GetSubaccountId().Owner, + ).TakerNotional, } // 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) + + 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, - bigTotalFeeQuoteQuantums, - perpetualId, + revSharesForFill, + fillForProcess, ); err != nil { - return takerUpdateResult, makerUpdateResult, errorsmod.Wrapf( + return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, errorsmod.Wrapf( types.ErrSubaccountFeeTransferFailed, "persistMatchedOrders: subaccounts (%v, %v) updated, but fee transfer (bigFeeQuoteQuantums: %v)"+ " to fee-collector failed. Err: %v", @@ -460,11 +492,8 @@ func (k Keeper) persistMatchedOrders( // Process fill in x/stats and x/rewards. k.rewardsKeeper.AddRewardSharesForFill( ctx, - matchWithOrders.TakerOrder.GetSubaccountId().Owner, - matchWithOrders.MakerOrder.GetSubaccountId().Owner, - bigFillQuoteQuantums, - bigTakerFeeQuoteQuantums, - bigMakerFeeQuoteQuantums, + fillForProcess, + revSharesForFill, ) k.statsKeeper.RecordFill( @@ -492,7 +521,7 @@ func (k Keeper) persistMatchedOrders( ), ) - return takerUpdateResult, makerUpdateResult, nil + return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, nil } func (k Keeper) setOrderFillAmountsAndPruning( diff --git a/protocol/x/clob/memclob/memclob.go b/protocol/x/clob/memclob/memclob.go index 8054a41e78..9aaefaba05 100644 --- a/protocol/x/clob/memclob/memclob.go +++ b/protocol/x/clob/memclob/memclob.go @@ -1696,7 +1696,7 @@ func (m *MemClobPriceTimePriority) mustPerformTakerOrderMatching( FillAmount: matchedAmount, } - success, takerUpdateResult, makerUpdateResult, err := m.clobKeeper.ProcessSingleMatch(ctx, &matchWithOrders) + success, takerUpdateResult, makerUpdateResult, _, err := m.clobKeeper.ProcessSingleMatch(ctx, &matchWithOrders) if err != nil && !errors.Is(err, satypes.ErrFailedToUpdateSubaccounts) { if errors.Is(err, types.ErrLiquidationExceedsSubaccountMaxInsuranceLost) { // Subaccount has reached max insurance lost block limit. Stop matching. diff --git a/protocol/x/clob/types/clob_keeper.go b/protocol/x/clob/types/clob_keeper.go index 9705439dc4..2d3067b7d7 100644 --- a/protocol/x/clob/types/clob_keeper.go +++ b/protocol/x/clob/types/clob_keeper.go @@ -85,6 +85,7 @@ type ClobKeeper interface { success bool, takerUpdateResult satypes.UpdateResult, makerUpdateResult satypes.UpdateResult, + affiliateRevSharesQuoteQuantums *big.Int, err error, ) SetLongTermOrderPlacement( diff --git a/protocol/x/clob/types/expected_keepers.go b/protocol/x/clob/types/expected_keepers.go index b14c4aeacd..c0f7d7bb22 100644 --- a/protocol/x/clob/types/expected_keepers.go +++ b/protocol/x/clob/types/expected_keepers.go @@ -11,6 +11,8 @@ import ( blocktimetypes "github.com/dydxprotocol/v4-chain/protocol/x/blocktime/types" perpetualsmoduletypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + revsharetypes "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types" + stattypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" ) @@ -82,8 +84,8 @@ type SubaccountsKeeper interface { DistributeFees( ctx sdk.Context, assetId uint32, - quantums *big.Int, - perpetualId uint32, + revSharesForFill revsharetypes.RevSharesForFill, + fillForProcess FillForProcess, ) error SendFinalizedSubaccountUpdates( ctx sdk.Context, @@ -147,6 +149,7 @@ type PricesKeeper interface { type StatsKeeper interface { RecordFill(ctx sdk.Context, takerAddress string, makerAddress string, notional *big.Int) + GetUserStats(ctx sdk.Context, address string) *stattypes.UserStats } // AccountKeeper defines the expected account keeper used for simulations. @@ -163,10 +166,16 @@ type BankKeeper interface { type RewardsKeeper interface { AddRewardSharesForFill( ctx sdk.Context, - takerAddress string, - makerAddress string, - bigFillQuoteQuantums *big.Int, - bigTakerFeeQuoteQuantums *big.Int, - bigMakerFeeQuoteQuantums *big.Int, + fill FillForProcess, + revSharesForFill revsharetypes.RevSharesForFill, + ) +} + +type RevShareKeeper interface { + GetAllRevShares( + ctx sdk.Context, + fill FillForProcess, + ) ( + revsharetypes.RevSharesForFill, error, ) } diff --git a/protocol/x/clob/types/mem_clob_keeper.go b/protocol/x/clob/types/mem_clob_keeper.go index 18898e79e2..a90418ec04 100644 --- a/protocol/x/clob/types/mem_clob_keeper.go +++ b/protocol/x/clob/types/mem_clob_keeper.go @@ -25,6 +25,7 @@ type MemClobKeeper interface { success bool, takerUpdateResult satypes.UpdateResult, makerUpdateResult satypes.UpdateResult, + affiliateRevSharesQuoteQuantums *big.Int, err error, ) CanDeleverageSubaccount( diff --git a/protocol/x/clob/types/types.go b/protocol/x/clob/types/types.go index 1858064651..30e7ef064b 100644 --- a/protocol/x/clob/types/types.go +++ b/protocol/x/clob/types/types.go @@ -16,4 +16,5 @@ type FillForProcess struct { // same block, this volume will not be included in the function // below MonthlyRollingTakerVolumeQuantums uint64 + MarketId uint32 } diff --git a/protocol/x/revshare/keeper/revshare.go b/protocol/x/revshare/keeper/revshare.go index 7545f223ef..aa5a1df2c5 100644 --- a/protocol/x/revshare/keeper/revshare.go +++ b/protocol/x/revshare/keeper/revshare.go @@ -150,8 +150,15 @@ func (k Keeper) ValidateRevShareSafety( func (k Keeper) GetAllRevShares( ctx sdk.Context, fill clobtypes.FillForProcess, -) ([]types.RevShare, error) { +) (types.RevSharesForFill, error) { revShares := []types.RevShare{} + feeSourceToQuoteQuantums := make(map[types.RevShareFeeSource]*big.Int) + 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 + totalFeesShared := big.NewInt(0) takerFees := fill.TakerFeeQuoteQuantums makerFees := fill.MakerFeeQuoteQuantums @@ -159,32 +166,51 @@ func (k Keeper) GetAllRevShares( affiliateRevShares, err := k.getAffiliateRevShares(ctx, fill) if err != nil { - return nil, err + return types.RevSharesForFill{}, err } unconditionalRevShares, err := k.getUnconditionalRevShares(ctx, netFees) if err != nil { - return nil, err + return types.RevSharesForFill{}, err } - marketMapperRevShares, err := k.getMarketMapperRevShare(ctx, fill.ProductId, netFees) + marketMapperRevShares, err := k.getMarketMapperRevShare(ctx, fill.MarketId, netFees) if err != nil { - return nil, err + return types.RevSharesForFill{}, err } revShares = append(revShares, affiliateRevShares...) revShares = append(revShares, unconditionalRevShares...) revShares = append(revShares, marketMapperRevShares...) + var affiliateRevShare *types.RevShare + if len(affiliateRevShares) > 0 { + // There should only be one affiliate rev share per fill + affiliateRevShare = &affiliateRevShares[0] + } + for _, revShare := range revShares { totalFeesShared.Add(totalFeesShared, revShare.QuoteQuantums) + + // Add the rev share to the total for the fee source + feeSourceToQuoteQuantums[revShare.RevShareFeeSource].Add( + feeSourceToQuoteQuantums[revShare.RevShareFeeSource], revShare.QuoteQuantums) + + // Add the rev share ppm to the total for the fee source + feeSourceToRevSharePpm[revShare.RevShareFeeSource] += revShare.RevSharePpm } //check total fees shared is less than or equal to net fees if totalFeesShared.Cmp(netFees) > 0 { - return nil, errorsmod.Wrap(types.ErrTotalFeesSharedExceedsNetFees, "total fees shared exceeds net fees") + return types.RevSharesForFill{}, errorsmod.Wrap( + types.ErrTotalFeesSharedExceedsNetFees, "total fees shared exceeds net fees") } - return revShares, nil + return types.RevSharesForFill{ + AffiliateRevShare: affiliateRevShare, + FeeSourceToQuoteQuantums: feeSourceToQuoteQuantums, + FeeSourceToRevSharePpm: feeSourceToRevSharePpm, + AllRevShares: revShares, + }, nil } func (k Keeper) getAffiliateRevShares( @@ -211,6 +237,7 @@ func (k Keeper) getAffiliateRevShares( RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, RevShareType: types.REV_SHARE_TYPE_AFFILIATE, QuoteQuantums: feesShared, + RevSharePpm: feeSharePpm, }, }, nil } @@ -231,6 +258,7 @@ func (k Keeper) getUnconditionalRevShares( RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, QuoteQuantums: feeShared, + RevSharePpm: revShare.SharePpm, } revShares = append(revShares, revShare) } @@ -257,6 +285,7 @@ func (k Keeper) getMarketMapperRevShare( RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, QuoteQuantums: marketMapperRevshareAmount, + RevSharePpm: revenueSharePpm, }) return revShares, nil diff --git a/protocol/x/revshare/keeper/revshare_test.go b/protocol/x/revshare/keeper/revshare_test.go index dcf12fbcf1..0a8a0cf291 100644 --- a/protocol/x/revshare/keeper/revshare_test.go +++ b/protocol/x/revshare/keeper/revshare_test.go @@ -267,41 +267,62 @@ func TestValidateRevShareSafety(t *testing.T) { } func TestKeeper_GetAllRevShares_Valid(t *testing.T) { + perpetualId := uint32(1) marketId := uint32(1) tests := []struct { - name string - fill clobtypes.FillForProcess - expectedRevShares []types.RevShare - setup func(tApp *testapp.TestApp, ctx sdk.Context, + name string + fill clobtypes.FillForProcess + expectedRevSharesForFill types.RevSharesForFill + setup func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, affiliatesKeeper *affiliateskeeper.Keeper) }{ { name: "Valid revenue share from affiliates, unconditional and market mapper", - expectedRevShares: []types.RevShare{ - - { + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{ + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, + RevShareType: types.REV_SHARE_TYPE_AFFILIATE, + QuoteQuantums: big.NewInt(1_500_000), + RevSharePpm: 150_000, + }, + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(2_400_000), + RevSharePpm: 200_000, + }, + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(3_600_000), + RevSharePpm: 300_000, + }, + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, + QuoteQuantums: big.NewInt(1_200_000), + RevSharePpm: 100_000, + }, + }, + AffiliateRevShare: &types.RevShare{ Recipient: constants.BobAccAddress.String(), RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, RevShareType: types.REV_SHARE_TYPE_AFFILIATE, QuoteQuantums: big.NewInt(1_500_000), + RevSharePpm: 150_000, }, - { - Recipient: constants.BobAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, - QuoteQuantums: big.NewInt(2_400_000), - }, - { - Recipient: constants.AliceAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, - QuoteQuantums: big.NewInt(3_600_000), + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(1_500_000), + types.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(7_200_000), }, - { - Recipient: constants.AliceAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, - QuoteQuantums: big.NewInt(1_200_000), + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 150_000, + types.REV_SHARE_FEE_SOURCE_NET_FEE: 600_000, }, }, fill: clobtypes.FillForProcess{ @@ -310,8 +331,9 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerAddr: constants.BobAccAddress.String(), MakerFeeQuoteQuantums: big.NewInt(2_000_000), FillQuoteQuantums: big.NewInt(100_000_000_000), - ProductId: marketId, + ProductId: perpetualId, MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, + MarketId: marketId, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, affiliatesKeeper *affiliateskeeper.Keeper) { @@ -344,31 +366,51 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { }, { name: "Valid rev-share from affiliates, negative unconditional and market mapper", - expectedRevShares: []types.RevShare{ - - { + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{ + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, + RevShareType: types.REV_SHARE_TYPE_AFFILIATE, + QuoteQuantums: big.NewInt(1_500_000), + RevSharePpm: 150_000, + }, + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(1_600_000), + RevSharePpm: 200_000, + }, + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(2_400_000), + RevSharePpm: 300_000, + }, + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, + QuoteQuantums: big.NewInt(800_000), + RevSharePpm: 100_000, + }, + }, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(1_500_000), + types.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(4_800_000), + }, + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 150_000, + types.REV_SHARE_FEE_SOURCE_NET_FEE: 600_000, + }, + AffiliateRevShare: &types.RevShare{ Recipient: constants.BobAccAddress.String(), RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, RevShareType: types.REV_SHARE_TYPE_AFFILIATE, QuoteQuantums: big.NewInt(1_500_000), - }, - { - Recipient: constants.BobAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, - QuoteQuantums: big.NewInt(1_600_000), - }, - { - Recipient: constants.AliceAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, - QuoteQuantums: big.NewInt(2_400_000), - }, - { - Recipient: constants.AliceAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, - QuoteQuantums: big.NewInt(800_000), + RevSharePpm: 150_000, }, }, fill: clobtypes.FillForProcess{ @@ -377,7 +419,8 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerAddr: constants.BobAccAddress.String(), MakerFeeQuoteQuantums: big.NewInt(-2_000_000), FillQuoteQuantums: big.NewInt(100_000_000_000), - ProductId: marketId, + ProductId: perpetualId, + MarketId: marketId, MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, @@ -417,27 +460,42 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerAddr: constants.BobAccAddress.String(), MakerFeeQuoteQuantums: big.NewInt(2_000_000), FillQuoteQuantums: big.NewInt(100_000_000_000), - ProductId: marketId, + ProductId: perpetualId, MonthlyRollingTakerVolumeQuantums: types.Max30dRefereeVolumeQuantums + 1, + MarketId: marketId, }, - expectedRevShares: []types.RevShare{ - { - Recipient: constants.BobAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, - QuoteQuantums: big.NewInt(2_400_000), + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{ + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(2_400_000), + RevSharePpm: 200_000, + }, + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(3_600_000), + RevSharePpm: 300_000, + }, + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, + QuoteQuantums: big.NewInt(1_200_000), + RevSharePpm: 100_000, + }, }, - { - Recipient: constants.AliceAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, - QuoteQuantums: big.NewInt(3_600_000), + AffiliateRevShare: nil, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(7_200_000), + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(0), }, - { - Recipient: constants.AliceAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, - QuoteQuantums: big.NewInt(1_200_000), + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: 600_000, + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 0, }, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, @@ -470,19 +528,37 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { }, { name: "Valid revenue share with no unconditional rev shares", - expectedRevShares: []types.RevShare{ - - { + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{ + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, + RevShareType: types.REV_SHARE_TYPE_AFFILIATE, + QuoteQuantums: big.NewInt(1_500_000), + RevSharePpm: 150_000, // 15% + }, + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, + QuoteQuantums: big.NewInt(1_200_000), + RevSharePpm: 100_000, // 10% + }, + }, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(1_200_000), + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(1_500_000), + }, + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: 100_000, // 10% + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 150_000, // 15% + }, + AffiliateRevShare: &types.RevShare{ Recipient: constants.BobAccAddress.String(), RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, RevShareType: types.REV_SHARE_TYPE_AFFILIATE, QuoteQuantums: big.NewInt(1_500_000), - }, - { - Recipient: constants.AliceAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, - QuoteQuantums: big.NewInt(1_200_000), + RevSharePpm: 150_000, // 15% }, }, fill: clobtypes.FillForProcess{ @@ -491,7 +567,8 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerAddr: constants.BobAccAddress.String(), MakerFeeQuoteQuantums: big.NewInt(2_000_000), FillQuoteQuantums: big.NewInt(100_000_000_000), - ProductId: marketId, + ProductId: perpetualId, + MarketId: marketId, MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, @@ -512,19 +589,37 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { }, { name: "Valid revenue share with no market mapper rev share", - expectedRevShares: []types.RevShare{ - - { + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{ + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, + RevShareType: types.REV_SHARE_TYPE_AFFILIATE, + QuoteQuantums: big.NewInt(1_500_000), + RevSharePpm: 150_000, // 15% + }, + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(2_400_000), + RevSharePpm: 200_000, // 20% + }, + }, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(2_400_000), + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(1_500_000), + }, + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: 200_000, // 20% + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 150_000, // 15% + }, + AffiliateRevShare: &types.RevShare{ Recipient: constants.BobAccAddress.String(), RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, RevShareType: types.REV_SHARE_TYPE_AFFILIATE, QuoteQuantums: big.NewInt(1_500_000), - }, - { - Recipient: constants.BobAccAddress.String(), - RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_FEE, - RevShareType: types.REV_SHARE_TYPE_UNCONDITIONAL, - QuoteQuantums: big.NewInt(2_400_000), + RevSharePpm: 150_000, // 15% }, }, fill: clobtypes.FillForProcess{ @@ -533,7 +628,8 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerAddr: constants.BobAccAddress.String(), MakerFeeQuoteQuantums: big.NewInt(2_000_000), FillQuoteQuantums: big.NewInt(100_000_000_000), - ProductId: marketId, + ProductId: perpetualId, + MarketId: marketId, MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, @@ -554,15 +650,27 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { }, }, { - name: "No rev shares", - expectedRevShares: []types.RevShare{}, + name: "No rev shares", + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{}, + AffiliateRevShare: nil, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(0), + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(0), + }, + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_NET_FEE: 0, + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 0, + }, + }, fill: clobtypes.FillForProcess{ TakerAddr: constants.AliceAccAddress.String(), TakerFeeQuoteQuantums: big.NewInt(10_000_000), MakerAddr: constants.BobAccAddress.String(), MakerFeeQuoteQuantums: big.NewInt(2_000_000), FillQuoteQuantums: big.NewInt(100_000_000_000), - ProductId: marketId, + ProductId: perpetualId, + MarketId: marketId, MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, @@ -570,7 +678,6 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { }, }, } - for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // Setup @@ -585,15 +692,16 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { keeper.CreateNewMarketRevShare(ctx, marketId) - revShares, err := keeper.GetAllRevShares(ctx, tc.fill) + revSharesForFill, err := keeper.GetAllRevShares(ctx, tc.fill) require.NoError(t, err) - require.Equal(t, tc.expectedRevShares, revShares) + require.Equal(t, tc.expectedRevSharesForFill, revSharesForFill) }) } } func TestKeeper_GetAllRevShares_Invalid(t *testing.T) { + perpetualId := uint32(1) marketId := uint32(1) tests := []struct { name string @@ -716,7 +824,8 @@ func TestKeeper_GetAllRevShares_Invalid(t *testing.T) { MakerAddr: constants.BobAccAddress.String(), MakerFeeQuoteQuantums: big.NewInt(2_000_000), FillQuoteQuantums: big.NewInt(100_000_000_000), - ProductId: uint32(1), + ProductId: perpetualId, + MarketId: marketId, MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, } tApp := testapp.NewTestAppBuilder(t).Build() diff --git a/protocol/x/revshare/types/types.go b/protocol/x/revshare/types/types.go index 8a3dfe65ff..c17a50584d 100644 --- a/protocol/x/revshare/types/types.go +++ b/protocol/x/revshare/types/types.go @@ -34,6 +34,7 @@ type RevShare struct { RevShareFeeSource RevShareFeeSource RevShareType RevShareType QuoteQuantums *big.Int + RevSharePpm uint32 } type RevShareFeeSource int @@ -52,3 +53,10 @@ const ( REV_SHARE_TYPE_UNCONDITIONAL REV_SHARE_TYPE_AFFILIATE ) + +type RevSharesForFill struct { + AffiliateRevShare *RevShare + FeeSourceToQuoteQuantums map[RevShareFeeSource]*big.Int + FeeSourceToRevSharePpm map[RevShareFeeSource]uint32 + AllRevShares []RevShare +} diff --git a/protocol/x/rewards/keeper/keeper.go b/protocol/x/rewards/keeper/keeper.go index 85627b804a..14f14d1aa7 100644 --- a/protocol/x/rewards/keeper/keeper.go +++ b/protocol/x/rewards/keeper/keeper.go @@ -19,6 +19,8 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/lib/log" "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" + 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" ) @@ -118,34 +120,57 @@ func (k Keeper) GetRewardShare( // // Within each block, total reward share score for an address is defined as: // -// reward_share_score = total_taker_fees_paid - max_possible_maker_rebate*taker_volume + total_positive_maker_fees +// reward_share_score = total_taker_fees_paid - total_rev_shared_taker_fee +// - 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: -// - For maker address, positive maker fees are added directly. -// - For taker address, positive taker fees are reduced by the largest possible maker rebate in x/fee-tiers multiplied -// by quote quantums of the fill. +// - 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) +// are added to reward share score. + func (k Keeper) AddRewardSharesForFill( ctx sdk.Context, - takerAddress string, - makerAddress string, - bigFillQuoteQuantums *big.Int, - bigTakerFeeQuoteQuantums *big.Int, - bigMakerFeeQuoteQuantums *big.Int, + fill clobtypes.FillForProcess, + revSharesForFill revsharetypes.RevSharesForFill, ) { // Process reward weight for taker. lowestMakerFee := k.feeTiersKeeper.GetLowestMakerFee(ctx) maxMakerRebatePpm := lib.Min(int32(0), lowestMakerFee) + + totalNetFeeRevSharePpm := uint32(0) + 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 + } + + totalFeeSubNetRevSharePpm := lib.OneMillion - totalNetFeeRevSharePpm + // Calculate quote_quantums * max_maker_rebate. Result is non-positive. - makerRebateMulTakerVolume := lib.BigMulPpm(bigFillQuoteQuantums, lib.BigI(maxMakerRebatePpm), false) - takerWeight := new(big.Int).Add( - bigTakerFeeQuoteQuantums, + makerRebateMulTakerVolume := lib.BigMulPpm(fill.FillQuoteQuantums, lib.BigI(maxMakerRebatePpm), false) + + netTakerFee := new(big.Int).Add( + fill.TakerFeeQuoteQuantums, makerRebateMulTakerVolume, ) + netTakerFee = netTakerFee.Sub( + netTakerFee, + totalTakerFeeRevShareQuantums, + ) + takerWeight := lib.BigMulPpm( + netTakerFee, + lib.BigU(totalFeeSubNetRevSharePpm), + false, + ) if takerWeight.Sign() > 0 { // We aren't concerned with errors here because we've already validated the weight is positive. if err := k.AddRewardShareToAddress( ctx, - takerAddress, + fill.TakerAddr, takerWeight, ); err != nil { log.ErrorLogWithError( @@ -157,12 +182,12 @@ func (k Keeper) AddRewardSharesForFill( } // Process reward weight for maker. - makerWeight := new(big.Int).Set(bigMakerFeeQuoteQuantums) + makerWeight := new(big.Int).Set(lib.BigMulPpm(fill.MakerFeeQuoteQuantums, lib.BigU(totalFeeSubNetRevSharePpm), false)) if makerWeight.Sign() > 0 { // We aren't concerned with errors here because we've already validated the weight is positive. if err := k.AddRewardShareToAddress( ctx, - makerAddress, + fill.MakerAddr, makerWeight, ); err != nil { log.ErrorLogWithError( diff --git a/protocol/x/rewards/keeper/keeper_test.go b/protocol/x/rewards/keeper/keeper_test.go index 55f998f272..d41f89b738 100644 --- a/protocol/x/rewards/keeper/keeper_test.go +++ b/protocol/x/rewards/keeper/keeper_test.go @@ -12,9 +12,12 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/dtypes" testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" big_testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/big" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" keepertest "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" feetierstypes "github.com/dydxprotocol/v4-chain/protocol/x/feetiers/types" pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + revsharetypes "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types" "github.com/dydxprotocol/v4-chain/protocol/x/rewards/types" "github.com/stretchr/testify/require" ) @@ -142,14 +145,13 @@ func TestAddRewardShareToAddress(t *testing.T) { func TestAddRewardSharesForFill(t *testing.T) { makerAddress := TestAddress1 - takerAdderss := TestAddress2 + takerAddress := TestAddress2 tests := map[string]struct { prevTakerRewardShare *types.RewardShare prevMakerRewardShare *types.RewardShare - fillQuoteQuantums *big.Int - takerFeeQuantums *big.Int - makerFeeQuantums *big.Int + fill clobtypes.FillForProcess + revSharesForFill revsharetypes.RevSharesForFill feeTiers []*feetierstypes.PerpetualFeeTier expectedTakerShare types.RewardShare @@ -158,9 +160,22 @@ func TestAddRewardSharesForFill(t *testing.T) { "positive maker fee, positive taker fees reduced by maker rebate, no previous share": { prevTakerRewardShare: nil, prevMakerRewardShare: nil, - fillQuoteQuantums: big.NewInt(800_000_000), // $800 - takerFeeQuantums: big.NewInt(2_000_000), // $2 - makerFeeQuantums: big.NewInt(1_000_000), // $1 + 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: 0, + }, + revSharesForFill: revsharetypes.RevSharesForFill{ + AllRevShares: []revsharetypes.RevShare{}, + FeeSourceToQuoteQuantums: map[revsharetypes.RevShareFeeSource]*big.Int{}, + FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{}, + AffiliateRevShare: nil, + }, feeTiers: []*feetierstypes.PerpetualFeeTier{ { MakerFeePpm: -1_000, // -0.1% @@ -168,7 +183,7 @@ func TestAddRewardSharesForFill(t *testing.T) { }, }, expectedTakerShare: types.RewardShare{ - Address: takerAdderss, + Address: takerAddress, Weight: dtypes.NewInt(1_200_000), // 2 - 0.1% * 800 }, expectedMakerShare: types.RewardShare{ @@ -179,9 +194,22 @@ func TestAddRewardSharesForFill(t *testing.T) { "negative maker fee, positive taker fees reduced by 0.1% maker rebate, no previous share": { prevTakerRewardShare: nil, prevMakerRewardShare: nil, - fillQuoteQuantums: big.NewInt(750_000_000), // $750 - takerFeeQuantums: big.NewInt(2_000_000), // $2 - makerFeeQuantums: big.NewInt(-1_000_000), // $1 + fill: clobtypes.FillForProcess{ + TakerAddr: takerAddress, + TakerFeeQuoteQuantums: big.NewInt(2_000_000), + MakerAddr: makerAddress, + MakerFeeQuoteQuantums: big.NewInt(-1_000_000), + FillQuoteQuantums: big.NewInt(750_000_000), + ProductId: uint32(1), + MarketId: uint32(1), + MonthlyRollingTakerVolumeQuantums: 0, + }, + revSharesForFill: revsharetypes.RevSharesForFill{ + AllRevShares: []revsharetypes.RevShare{}, + FeeSourceToQuoteQuantums: map[revsharetypes.RevShareFeeSource]*big.Int{}, + FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{}, + AffiliateRevShare: nil, + }, feeTiers: []*feetierstypes.PerpetualFeeTier{ { MakerFeePpm: -1_000, // -0.1% @@ -189,7 +217,7 @@ func TestAddRewardSharesForFill(t *testing.T) { }, }, expectedTakerShare: types.RewardShare{ - Address: takerAdderss, + Address: takerAddress, Weight: dtypes.NewInt(1_250_000), // 2 - 0.1% * 750 }, expectedMakerShare: types.RewardShare{ @@ -200,9 +228,22 @@ func TestAddRewardSharesForFill(t *testing.T) { "negative maker fee, positive taker fees reduced by 0.05% maker rebate, no previous share": { prevTakerRewardShare: nil, prevMakerRewardShare: nil, - fillQuoteQuantums: big.NewInt(750_000_000), // $750 - takerFeeQuantums: big.NewInt(2_000_000), // $2 - makerFeeQuantums: big.NewInt(-1_000_000), // $1 + fill: clobtypes.FillForProcess{ + TakerAddr: takerAddress, + TakerFeeQuoteQuantums: big.NewInt(2_000_000), + MakerAddr: makerAddress, + MakerFeeQuoteQuantums: big.NewInt(-1_000_000), + FillQuoteQuantums: big.NewInt(750_000_000), + ProductId: uint32(1), + MarketId: uint32(1), + MonthlyRollingTakerVolumeQuantums: 0, + }, + revSharesForFill: revsharetypes.RevSharesForFill{ + AllRevShares: []revsharetypes.RevShare{}, + FeeSourceToQuoteQuantums: map[revsharetypes.RevShareFeeSource]*big.Int{}, + FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{}, + AffiliateRevShare: nil, + }, feeTiers: []*feetierstypes.PerpetualFeeTier{ { MakerFeePpm: -500, // -0.05% @@ -210,7 +251,7 @@ func TestAddRewardSharesForFill(t *testing.T) { }, }, expectedTakerShare: types.RewardShare{ - Address: takerAdderss, + Address: takerAddress, Weight: dtypes.NewInt(1_625_000), // 2 - 0.05% * 750 }, expectedMakerShare: types.RewardShare{ @@ -220,10 +261,22 @@ func TestAddRewardSharesForFill(t *testing.T) { }, "positive maker fee, positive taker fees offset by maker rebate, no previous share": { prevTakerRewardShare: nil, - prevMakerRewardShare: nil, - fillQuoteQuantums: big.NewInt(750_000_000), // $750 - takerFeeQuantums: big.NewInt(700_000), // $0.7 - makerFeeQuantums: big.NewInt(500_000), // $1 + fill: clobtypes.FillForProcess{ + TakerAddr: takerAddress, + TakerFeeQuoteQuantums: big.NewInt(700_000), + MakerAddr: makerAddress, + MakerFeeQuoteQuantums: big.NewInt(500_000), + FillQuoteQuantums: big.NewInt(750_000_000), + ProductId: uint32(1), + MarketId: uint32(1), + MonthlyRollingTakerVolumeQuantums: 0, + }, + revSharesForFill: revsharetypes.RevSharesForFill{ + AllRevShares: []revsharetypes.RevShare{}, + FeeSourceToQuoteQuantums: map[revsharetypes.RevShareFeeSource]*big.Int{}, + FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{}, + AffiliateRevShare: nil, + }, feeTiers: []*feetierstypes.PerpetualFeeTier{ { MakerFeePpm: -1_000, // -0.1% @@ -231,7 +284,7 @@ func TestAddRewardSharesForFill(t *testing.T) { }, }, expectedTakerShare: types.RewardShare{ - Address: takerAdderss, + Address: takerAddress, Weight: dtypes.NewInt(0), // $0.7 - $750 * 0.1% < 0 }, expectedMakerShare: types.RewardShare{ @@ -242,9 +295,22 @@ func TestAddRewardSharesForFill(t *testing.T) { "positive maker fee, positive taker fees, no maker rebate, no previous share": { prevTakerRewardShare: nil, prevMakerRewardShare: nil, - fillQuoteQuantums: big.NewInt(750_000_000), // $750 - takerFeeQuantums: big.NewInt(700_000), // $0.7 - makerFeeQuantums: big.NewInt(500_000), // $1 + fill: clobtypes.FillForProcess{ + TakerAddr: takerAddress, + TakerFeeQuoteQuantums: big.NewInt(700_000), + MakerAddr: makerAddress, + MakerFeeQuoteQuantums: big.NewInt(500_000), + FillQuoteQuantums: big.NewInt(750_000_000), + ProductId: uint32(1), + MarketId: uint32(1), + MonthlyRollingTakerVolumeQuantums: 0, + }, + revSharesForFill: revsharetypes.RevSharesForFill{ + AllRevShares: []revsharetypes.RevShare{}, + FeeSourceToQuoteQuantums: map[revsharetypes.RevShareFeeSource]*big.Int{}, + FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{}, + AffiliateRevShare: nil, + }, feeTiers: []*feetierstypes.PerpetualFeeTier{ { MakerFeePpm: 1_000, // 0.1% @@ -252,7 +318,7 @@ func TestAddRewardSharesForFill(t *testing.T) { }, }, expectedTakerShare: types.RewardShare{ - Address: takerAdderss, + Address: takerAddress, Weight: dtypes.NewInt(700_000), }, expectedMakerShare: types.RewardShare{ @@ -260,6 +326,170 @@ func TestAddRewardSharesForFill(t *testing.T) { Weight: dtypes.NewInt(500_000), }, }, + "positive maker + taker fees reduced by maker rebate, no previous share with net fee revshare": { + 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: 9, + }, + 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% + }, + }, + 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(0), + }, + FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{ + revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE: 100_000, // 10% + revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE: 0, + }, + AffiliateRevShare: nil, + }, + 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, + Weight: dtypes.NewInt(900_000), // 1 * (1 - 0.1) + }, + }, + "positive maker + taker fees reduced by maker rebate, no previous share with multiple net fee revshare": { + 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: 0, + }, + 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: constants.BobAccAddress.String(), + RevShareFeeSource: revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE, + RevShareType: revsharetypes.REV_SHARE_TYPE_UNCONDITIONAL, + QuoteQuantums: big.NewInt(200_000), + RevSharePpm: 100_000, // 10% + }, + }, + FeeSourceToQuoteQuantums: map[revsharetypes.RevShareFeeSource]*big.Int{ + revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE: big.NewInt(400_000), + revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(0), + }, + FeeSourceToRevSharePpm: map[revsharetypes.RevShareFeeSource]uint32{ + revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE: 200_000, // 20% + revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE: 0, + }, + AffiliateRevShare: nil, + }, + feeTiers: []*feetierstypes.PerpetualFeeTier{ + { + MakerFeePpm: -1_000, // -0.1% + TakerFeePpm: 2_000, // 0.2% + }, + }, + expectedTakerShare: types.RewardShare{ + Address: takerAddress, + Weight: dtypes.NewInt(960_000), // (2 - 0.1% * 800 - 0) * (1 - 0.2) + }, + expectedMakerShare: types.RewardShare{ + Address: makerAddress, + Weight: dtypes.NewInt(800_000), // 1 * (1 - 0.2) + }, + }, + "positive maker + taker fees reduced by maker rebate, no previous share and taker + net fee revshare": { + 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: 0, + }, + 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(900_000), // (2 - 0.1% * 800 - 0.2) * (1 - 0.1) + }, + expectedMakerShare: types.RewardShare{ + Address: makerAddress, + Weight: dtypes.NewInt(900_000), // 1 * (1 - 0.1) + }, + }, } // Run tests. @@ -286,15 +516,12 @@ func TestAddRewardSharesForFill(t *testing.T) { k.AddRewardSharesForFill( ctx, - takerAdderss, - makerAddress, - tc.fillQuoteQuantums, - tc.takerFeeQuantums, - tc.makerFeeQuantums, + tc.fill, + tc.revSharesForFill, ) // Check the new reward shares. - require.Equal(t, tc.expectedTakerShare, k.GetRewardShare(ctx, takerAdderss)) + require.Equal(t, tc.expectedTakerShare, k.GetRewardShare(ctx, takerAddress)) require.Equal(t, tc.expectedMakerShare, k.GetRewardShare(ctx, makerAddress)) }) } diff --git a/protocol/x/sending/client/cli/sending_cli_test.go b/protocol/x/sending/client/cli/sending_cli_test.go index 6195841399..9918b75c30 100644 --- a/protocol/x/sending/client/cli/sending_cli_test.go +++ b/protocol/x/sending/client/cli/sending_cli_test.go @@ -1,5 +1,3 @@ -//go:build all || integration_test - package cli_test import ( diff --git a/protocol/x/subaccounts/genesis_test.go b/protocol/x/subaccounts/genesis_test.go index f286163b24..53ac85cbe3 100644 --- a/protocol/x/subaccounts/genesis_test.go +++ b/protocol/x/subaccounts/genesis_test.go @@ -35,7 +35,7 @@ func TestGenesis(t *testing.T) { }, } - ctx, k, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, k, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) subaccounts.InitGenesis(ctx, *k, genesisState) assertSubaccountUpdateEventsInIndexerBlock(t, k, ctx, 2) got := subaccounts.ExportGenesis(ctx, *k) diff --git a/protocol/x/subaccounts/keeper/grpc_query_collateral_pool_test.go b/protocol/x/subaccounts/keeper/grpc_query_collateral_pool_test.go index 82d4f3551a..ed95297d83 100644 --- a/protocol/x/subaccounts/keeper/grpc_query_collateral_pool_test.go +++ b/protocol/x/subaccounts/keeper/grpc_query_collateral_pool_test.go @@ -52,7 +52,7 @@ func TestQueryCollateralPoolAddress(t *testing.T) { }, } { t.Run(testName, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) keepertest.CreateTestLiquidityTiers(t, ctx, perpetualsKeeper) keepertest.CreateTestPerpetuals(t, ctx, perpetualsKeeper) diff --git a/protocol/x/subaccounts/keeper/grpc_query_subaccount_test.go b/protocol/x/subaccounts/keeper/grpc_query_subaccount_test.go index 3dbc6e8f23..deb3490fc7 100644 --- a/protocol/x/subaccounts/keeper/grpc_query_subaccount_test.go +++ b/protocol/x/subaccounts/keeper/grpc_query_subaccount_test.go @@ -19,7 +19,7 @@ import ( var _ = strconv.IntSize func TestSubaccountQuerySingle(t *testing.T) { - ctx, keeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) msgs := createNSubaccount(keeper, ctx, 2, big.NewInt(1_000)) for _, tc := range []struct { desc string @@ -90,7 +90,7 @@ func TestSubaccountQuerySingle(t *testing.T) { } func TestSubaccountQueryPaginated(t *testing.T) { - ctx, keeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) msgs := createNSubaccount(keeper, ctx, 5, big.NewInt(1_000)) request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllSubaccountRequest { diff --git a/protocol/x/subaccounts/keeper/grpc_query_withdrawal_and_transfers_blocked_info_test.go b/protocol/x/subaccounts/keeper/grpc_query_withdrawal_and_transfers_blocked_info_test.go index 37958fdc53..92fcc41b76 100644 --- a/protocol/x/subaccounts/keeper/grpc_query_withdrawal_and_transfers_blocked_info_test.go +++ b/protocol/x/subaccounts/keeper/grpc_query_withdrawal_and_transfers_blocked_info_test.go @@ -267,7 +267,7 @@ func TestQueryWithdrawalAndTransfersBlockedInfo(t *testing.T) { }, } { t.Run(testName, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, _, blocktimeKeeper, _, _ := keepertest.SubaccountsKeepers( + ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, _, blocktimeKeeper, _, _, _ := keepertest.SubaccountsKeepers( t, true, ) diff --git a/protocol/x/subaccounts/keeper/negative_tnc_subaccount_test.go b/protocol/x/subaccounts/keeper/negative_tnc_subaccount_test.go index 121c91151f..ded2182065 100644 --- a/protocol/x/subaccounts/keeper/negative_tnc_subaccount_test.go +++ b/protocol/x/subaccounts/keeper/negative_tnc_subaccount_test.go @@ -224,7 +224,7 @@ func TestGetSetNegativeTncSubaccountSeenAtBlock(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup keeper state and test parameters. - ctx, subaccountsKeeper, pricesKeeper, perpetualsKeeper, _, _, _, _, _, _ := keepertest.SubaccountsKeepers( + ctx, subaccountsKeeper, pricesKeeper, perpetualsKeeper, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers( t, true, ) @@ -250,7 +250,7 @@ func TestGetSetNegativeTncSubaccountSeenAtBlock(t *testing.T) { func TestGetSetNegativeTncSubaccountSeenAtBlock_PanicsOnDecreasingBlock(t *testing.T) { // Setup keeper state and test parameters. - ctx, subaccountsKeeper, pricesKeeper, perpetualsKeeper, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, subaccountsKeeper, pricesKeeper, perpetualsKeeper, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) keepertest.CreateTestLiquidityTiers(t, ctx, perpetualsKeeper) keepertest.CreateTestPerpetuals(t, ctx, perpetualsKeeper) diff --git a/protocol/x/subaccounts/keeper/safety_heap_state_test.go b/protocol/x/subaccounts/keeper/safety_heap_state_test.go index ae7e8c2383..ff8a6db309 100644 --- a/protocol/x/subaccounts/keeper/safety_heap_state_test.go +++ b/protocol/x/subaccounts/keeper/safety_heap_state_test.go @@ -11,7 +11,7 @@ import ( func TestGetSetSubaccountAtIndex(t *testing.T) { // Setup keeper state and test parameters. - ctx, subaccountsKeeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) + ctx, subaccountsKeeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) // Write a couple of subaccounts to the store. store := subaccountsKeeper.GetSafetyHeapStore(ctx, 0, types.Long) @@ -60,7 +60,7 @@ func TestGetSetSubaccountAtIndex(t *testing.T) { func TestGetSetSubaccountHeapIndex(t *testing.T) { // Setup keeper state and test parameters. - ctx, subaccountsKeeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) + ctx, subaccountsKeeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) // Write a couple of subaccounts to the store. store := subaccountsKeeper.GetSafetyHeapStore(ctx, 0, types.Long) @@ -109,7 +109,7 @@ func TestGetSetSubaccountHeapIndex(t *testing.T) { func TestGetSetSafetyHeapLength(t *testing.T) { // Setup keeper state and test parameters. - ctx, subaccountsKeeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) + ctx, subaccountsKeeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) // Write a couple of subaccounts to the store. store := subaccountsKeeper.GetSafetyHeapStore(ctx, 0, types.Long) diff --git a/protocol/x/subaccounts/keeper/safety_heap_test.go b/protocol/x/subaccounts/keeper/safety_heap_test.go index 4ce95e6537..98fc6aab2e 100644 --- a/protocol/x/subaccounts/keeper/safety_heap_test.go +++ b/protocol/x/subaccounts/keeper/safety_heap_test.go @@ -50,7 +50,7 @@ func TestSafetyHeapInsertRemoveMin(t *testing.T) { for iter := 0; iter < 100; iter++ { // Setup keeper state and test parameters. - ctx, subaccountsKeeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) + ctx, subaccountsKeeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) // Shuffle the subaccounts so that insertion order is random. slices.Shuffle(allSubaccounts) @@ -136,7 +136,7 @@ func TestSafetyHeapInsertRemoveIndex(t *testing.T) { for iter := 0; iter < 100; iter++ { // Setup keeper state and test parameters. - ctx, subaccountsKeeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) + ctx, subaccountsKeeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, false) // Shuffle the subaccounts so that insertion order is random. slices.Shuffle(allSubaccounts) diff --git a/protocol/x/subaccounts/keeper/subaccount_test.go b/protocol/x/subaccounts/keeper/subaccount_test.go index 953d3fdee1..e44a8b5edd 100644 --- a/protocol/x/subaccounts/keeper/subaccount_test.go +++ b/protocol/x/subaccounts/keeper/subaccount_test.go @@ -156,7 +156,7 @@ func TestGetCollateralPool(t *testing.T) { for name, tc := range tests { t.Run( name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _ := keepertest.SubaccountsKeepers( + ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers( t, true, ) @@ -191,7 +191,7 @@ func TestGetCollateralPool(t *testing.T) { } func TestSubaccountGet(t *testing.T) { - ctx, keeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) items := createNSubaccount(keeper, ctx, 10, big.NewInt(1_000)) for _, item := range items { rst := keeper.GetSubaccount(ctx, @@ -205,7 +205,7 @@ func TestSubaccountGet(t *testing.T) { } func TestSubaccountSet_Empty(t *testing.T) { - ctx, keeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keeper.SetSubaccount(ctx, types.Subaccount{ Id: &constants.Alice_Num0, }) @@ -223,7 +223,7 @@ func TestSubaccountSet_Empty(t *testing.T) { } func TestSubaccountGetNonExistent(t *testing.T) { - ctx, keeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) id := types.SubaccountId{ Owner: "non-existent", Number: uint32(123), @@ -237,7 +237,7 @@ func TestSubaccountGetNonExistent(t *testing.T) { } func TestGetAllSubaccount(t *testing.T) { - ctx, keeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) items := createNSubaccount(keeper, ctx, 10, big.NewInt(1_000)) require.Equal( t, @@ -278,7 +278,7 @@ func TestForEachSubaccount(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) items := createNSubaccount(keeper, ctx, tc.numSubaccountsInState, big.NewInt(1_000)) collectedSubaccounts := make([]types.Subaccount, 0) i := 0 @@ -2914,7 +2914,8 @@ func TestUpdateSubaccounts(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, _, bankKeeper, assetsKeeper, _, _, _ := keepertest.SubaccountsKeepers( + ctx, keeper, pricesKeeper, perpetualsKeeper, _, bankKeeper, + assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers( t, tc.msgSenderEnabled, ) @@ -4360,7 +4361,7 @@ func TestUpdateSubaccounts_WithdrawalsBlocked(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _ := keepertest.SubaccountsKeepers( + ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers( t, tc.msgSenderEnabled, ) @@ -5487,7 +5488,7 @@ func TestCanUpdateSubaccounts(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _ := keepertest.SubaccountsKeepers( + ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers( t, true, ) @@ -5945,7 +5946,7 @@ func TestGetNetCollateralAndMarginRequirements(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _ := keepertest.SubaccountsKeepers( + ctx, keeper, pricesKeeper, perpetualsKeeper, _, _, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers( t, true, ) diff --git a/protocol/x/subaccounts/keeper/transfer.go b/protocol/x/subaccounts/keeper/transfer.go index 13a3982241..5724fbd777 100644 --- a/protocol/x/subaccounts/keeper/transfer.go +++ b/protocol/x/subaccounts/keeper/transfer.go @@ -3,16 +3,15 @@ package keeper import ( "math/big" - "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" - - "github.com/dydxprotocol/v4-chain/protocol/lib/log" - errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/dydxprotocol/v4-chain/protocol/lib" + "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/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/subaccounts/types" ) @@ -219,70 +218,74 @@ func (k Keeper) WithdrawFundsFromSubaccountToAccount( func (k Keeper) DistributeFees( ctx sdk.Context, assetId uint32, - quantums *big.Int, - perpetualId uint32, + revSharesForFill revsharetypes.RevSharesForFill, + fill clobtypes.FillForProcess, ) error { // get perpetual - perpetual, err := k.perpetualsKeeper.GetPerpetual(ctx, perpetualId) + totalFeeQuoteQuantums := new(big.Int).Add(fill.TakerFeeQuoteQuantums, fill.MakerFeeQuoteQuantums) + perpetual, err := k.perpetualsKeeper.GetPerpetual(ctx, fill.ProductId) if err != nil { return err } - collateralPoolAddr, err := k.GetCollateralPoolFromPerpetualId(ctx, perpetualId) + collateralPoolAddr, err := k.GetCollateralPoolFromPerpetualId(ctx, fill.ProductId) if err != nil { return err } - // calculate market mapper rev share - marketMapperShare := big.NewInt(0) - revShareAddr, revSharePpm, err := k.revShareKeeper.GetMarketMapperRevenueShareForMarket( - ctx, - perpetual.Params.MarketId, - ) - // Note: The likelihood of this error is very low, and not getting the rev share should not - // prevent the trade from going through. Therefore, we log the error and continue - if err != nil { - log.ErrorLog(ctx, "DistributeFees: failed to get market mapper revenue share", "err", err) - } - if err == nil && revShareAddr != nil { - if revSharePpm >= 1e6 { - log.ErrorLog( - ctx, - "DistributeFees: revSharePpm is greater than or equal to 100%", - "revSharePpm", - revSharePpm, - ) - } else { - // marketMapperShare = quantums * revSharePpm / 1e6 - marketMapperShare.Div( - new(big.Int).Mul(quantums, big.NewInt(int64(revSharePpm))), - big.NewInt(1e6), + // Transfer fees to rev share recipients + for _, revShare := range revSharesForFill.AllRevShares { + // transfer fees to the recipient + recipientAddress, err := sdk.AccAddressFromBech32(revShare.Recipient) + if err != nil { + return err + } + if err := k.TransferFees( + ctx, + assetId, + collateralPoolAddr, + recipientAddress, + revShare.QuoteQuantums, + ); err != nil { + return err + } + + // emit a metric for the amount of fees transferred to the market mapper + if revShare.RevShareType == revsharetypes.REV_SHARE_TYPE_MARKET_MAPPER { + labels := []metrics.Label{ + metrics.GetLabelForIntValue(metrics.MarketId, int(perpetual.Params.MarketId)), + } + metrics.AddSampleWithLabels( + metrics.MarketMapperRevenueDistribution, + metrics.GetMetricValueFromBigInt(revShare.QuoteQuantums), + labels..., ) } } - // Remaining amount goes to the fee collector - feeCollectorShare := new(big.Int).Sub(quantums, marketMapperShare) - - // Emit a metric for the amount of fees transferred to the market mapper - labels := []metrics.Label{ - metrics.GetLabelForIntValue(metrics.MarketId, int(perpetual.Params.MarketId)), + totalTakerFeeRevShareQuantums := big.NewInt(0) + if value, ok := revSharesForFill.FeeSourceToQuoteQuantums[revsharetypes.REV_SHARE_FEE_SOURCE_TAKER_FEE]; ok { + totalTakerFeeRevShareQuantums = value } - metrics.AddSampleWithLabels( - metrics.MarketMapperRevenueDistribution, - metrics.GetMetricValueFromBigInt(marketMapperShare), - labels..., + totalNetFeeRevShareQuantums := big.NewInt(0) + if value, ok := revSharesForFill.FeeSourceToQuoteQuantums[revsharetypes.REV_SHARE_FEE_SOURCE_NET_FEE]; ok { + totalNetFeeRevShareQuantums = value + } + + totalRevShareQuoteQuantums := big.NewInt(0).Add( + totalTakerFeeRevShareQuantums, + totalNetFeeRevShareQuantums, ) - // Transfer fees to the market mapper - if err := k.TransferFees( - ctx, - assetId, - collateralPoolAddr, - revShareAddr, - marketMapperShare, - ); err != nil { - return err + // Remaining amount goes to the fee collector + feeCollectorShare := new(big.Int).Sub( + totalFeeQuoteQuantums, + totalRevShareQuoteQuantums, + ) + + // If Collector fee share is < 0, panic + if feeCollectorShare.Sign() < 0 { + panic("fee collector share is < 0") } // Transfer fees to the fee collector diff --git a/protocol/x/subaccounts/keeper/transfer_test.go b/protocol/x/subaccounts/keeper/transfer_test.go index b1237adcc4..79ea1c5afd 100644 --- a/protocol/x/subaccounts/keeper/transfer_test.go +++ b/protocol/x/subaccounts/keeper/transfer_test.go @@ -20,7 +20,9 @@ import ( keepertest "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper" sample_testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/sample" testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/util" + affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types" asstypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/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/subaccounts/types" "github.com/stretchr/testify/require" @@ -194,7 +196,7 @@ func TestWithdrawFundsFromSubaccountToAccount_DepositFundsFromAccountToSubaccoun for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _ := + ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) @@ -455,7 +457,7 @@ func TestWithdrawFundsFromSubaccountToAccount_DepositFundsFromAccountToSubaccoun for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _ := + ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) keepertest.CreateTestLiquidityTiers(t, ctx, perpetualsKeeper) @@ -728,7 +730,7 @@ func TestTransferFundsFromSubaccountToSubaccount_Success(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _ := + ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) @@ -1054,7 +1056,7 @@ func TestTransferFundsFromSubaccountToSubaccount_Failure(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _ := + ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) @@ -1170,6 +1172,8 @@ func TestTransferFundsFromSubaccountToSubaccount_Failure(t *testing.T) { } func TestDistributeFees(t *testing.T) { + refereeAccAddr := constants.AliceAccAddress.String() + defaultUnconditionalRevSharePpm := uint32(100_000) tests := map[string]struct { skipSetUpUsdc bool @@ -1179,11 +1183,13 @@ func TestDistributeFees(t *testing.T) { marketMapperAccBalance *big.Int // Transfer details. - asset asstypes.Asset - quantums *big.Int - perpetualId uint32 + asset asstypes.Asset + fill clobtypes.FillForProcess - collateralPoolAddr sdk.AccAddress + collateralPoolAddr sdk.AccAddress + affiliateRevShareAcctAddr string + marketMapperRevShareAcctAddr string + unconditionalRevShareAcctAddr string // Revenue share details revshareParams revsharetypes.MarketMapperRevenueShareParams @@ -1191,62 +1197,89 @@ func TestDistributeFees(t *testing.T) { revShareExpiration int64 // Expectations. - expectedErr error - expectedSubaccountsModuleAccBalance *big.Int - expectedFeeModuleAccBalance *big.Int - expectedMarketMapperAccBalance *big.Int + expectedErr error + expectedSubaccountsModuleAccBalance *big.Int + expectedFeeModuleAccBalance *big.Int + expectedMarketMapperAccBalance *big.Int + expectedAffiliateAccBalance *big.Int + expectedUnconditionalRevShareAccBalance *big.Int }{ "success - send to fee-collector module account": { - asset: *constants.Usdc, - feeModuleAccBalance: big.NewInt(2500), - subaccountModuleAccBalance: big.NewInt(600), - marketMapperAccBalance: big.NewInt(0), - quantums: big.NewInt(500), + asset: *constants.Usdc, + feeModuleAccBalance: big.NewInt(2500), + subaccountModuleAccBalance: big.NewInt(600), + marketMapperAccBalance: big.NewInt(0), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(0), + MarketId: uint32(0), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, collateralPoolAddr: types.ModuleAddress, + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", expectedSubaccountsModuleAccBalance: big.NewInt(100), // 600 - 500 expectedFeeModuleAccBalance: big.NewInt(3000), // 500 + 2500 revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), }, - expectedMarketMapperAccBalance: big.NewInt(0), + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), }, "success - send to fee-collector module account from isolated market account": { asset: *constants.Usdc, feeModuleAccBalance: big.NewInt(2500), subaccountModuleAccBalance: big.NewInt(600), - quantums: big.NewInt(500), - perpetualId: 3, // Isolated market perpetual ID + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(3), + MarketId: uint32(3), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, collateralPoolAddr: authtypes.NewModuleAddress( types.ModuleName + ":" + lib.IntToString(3), ), + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", expectedSubaccountsModuleAccBalance: big.NewInt(100), // 600 - 500 expectedFeeModuleAccBalance: big.NewInt(3000), // 500 + 2500 marketMapperAccBalance: big.NewInt(0), revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), }, - expectedMarketMapperAccBalance: big.NewInt(0), - }, - "success - quantums is zero": { - asset: *constants.Usdc, - feeModuleAccBalance: big.NewInt(2500), - subaccountModuleAccBalance: big.NewInt(600), - quantums: big.NewInt(0), - collateralPoolAddr: types.ModuleAddress, - expectedSubaccountsModuleAccBalance: big.NewInt(600), // 600 - expectedFeeModuleAccBalance: big.NewInt(2500), // 2500 - marketMapperAccBalance: big.NewInt(0), - revshareParams: revsharetypes.MarketMapperRevenueShareParams{ - Address: constants.AliceAccAddress.String(), - }, - expectedMarketMapperAccBalance: big.NewInt(0), + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), }, "failure - subaccounts module does not have sufficient funds": { - asset: *constants.Usdc, - feeModuleAccBalance: big.NewInt(2500), - subaccountModuleAccBalance: big.NewInt(300), - quantums: big.NewInt(500), + asset: *constants.Usdc, + feeModuleAccBalance: big.NewInt(2500), + subaccountModuleAccBalance: big.NewInt(300), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(3), + MarketId: uint32(3), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, collateralPoolAddr: types.ModuleAddress, + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", expectedSubaccountsModuleAccBalance: big.NewInt(300), expectedFeeModuleAccBalance: big.NewInt(2500), expectedErr: sdkerrors.ErrInsufficientFunds, @@ -1254,17 +1287,30 @@ func TestDistributeFees(t *testing.T) { revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), }, - expectedMarketMapperAccBalance: big.NewInt(0), + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), }, "failure - isolated markets subaccounts module does not have sufficient funds": { asset: *constants.Usdc, feeModuleAccBalance: big.NewInt(2500), subaccountModuleAccBalance: big.NewInt(300), - quantums: big.NewInt(500), - perpetualId: 3, // Isolated market perpetual ID + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(3), + MarketId: uint32(3), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, collateralPoolAddr: authtypes.NewModuleAddress( types.ModuleName + ":" + lib.IntToString(3), ), + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", expectedSubaccountsModuleAccBalance: big.NewInt(300), expectedFeeModuleAccBalance: big.NewInt(2500), expectedErr: sdkerrors.ErrInsufficientFunds, @@ -1272,15 +1318,29 @@ func TestDistributeFees(t *testing.T) { revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), }, - expectedMarketMapperAccBalance: big.NewInt(0), + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), }, "failure - asset ID doesn't exist": { - feeModuleAccBalance: big.NewInt(1500), - skipSetUpUsdc: true, - asset: *constants.Usdc, - subaccountModuleAccBalance: big.NewInt(500), - quantums: big.NewInt(500), + feeModuleAccBalance: big.NewInt(1500), + skipSetUpUsdc: true, + asset: *constants.Usdc, + subaccountModuleAccBalance: big.NewInt(500), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(3), + MarketId: uint32(3), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, collateralPoolAddr: types.ModuleAddress, + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", expectedErr: asstypes.ErrAssetDoesNotExist, expectedSubaccountsModuleAccBalance: big.NewInt(500), expectedFeeModuleAccBalance: big.NewInt(1500), @@ -1288,14 +1348,28 @@ func TestDistributeFees(t *testing.T) { revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), }, - expectedMarketMapperAccBalance: big.NewInt(0), + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), }, "failure - asset other than USDC not supported": { - feeModuleAccBalance: big.NewInt(1500), - asset: *constants.BtcUsd, - subaccountModuleAccBalance: big.NewInt(500), - quantums: big.NewInt(500), + feeModuleAccBalance: big.NewInt(1500), + asset: *constants.BtcUsd, + subaccountModuleAccBalance: big.NewInt(500), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(3), + MarketId: uint32(3), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, collateralPoolAddr: types.ModuleAddress, + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", expectedErr: types.ErrAssetTransferThroughBankNotImplemented, expectedSubaccountsModuleAccBalance: big.NewInt(500), expectedFeeModuleAccBalance: big.NewInt(1500), @@ -1303,36 +1377,36 @@ func TestDistributeFees(t *testing.T) { revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), }, - expectedMarketMapperAccBalance: big.NewInt(0), - }, - "success - transfer quantums is negative": { - feeModuleAccBalance: big.NewInt(1500), - asset: *constants.Usdc, - subaccountModuleAccBalance: big.NewInt(500), - quantums: big.NewInt(-500), - collateralPoolAddr: types.ModuleAddress, - expectedSubaccountsModuleAccBalance: big.NewInt(1000), - expectedFeeModuleAccBalance: big.NewInt(1000), - marketMapperAccBalance: big.NewInt(0), - revshareParams: revsharetypes.MarketMapperRevenueShareParams{ - Address: constants.AliceAccAddress.String(), - }, - expectedMarketMapperAccBalance: big.NewInt(0), + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), }, "success - distribute fees to market mapper and fee collector": { - asset: *constants.Usdc, - feeModuleAccBalance: big.NewInt(2500), - subaccountModuleAccBalance: big.NewInt(600), - marketMapperAccBalance: big.NewInt(0), - quantums: big.NewInt(500), - expectedSubaccountsModuleAccBalance: big.NewInt(100), // 600 - 500 - expectedFeeModuleAccBalance: big.NewInt(2950), // 2500 + 500 - 50 - expectedMarketMapperAccBalance: big.NewInt(50), // 0 + 50 - perpetualId: 4, + asset: *constants.Usdc, + feeModuleAccBalance: big.NewInt(2500), + subaccountModuleAccBalance: big.NewInt(600), + marketMapperAccBalance: big.NewInt(0), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(4), + MarketId: uint32(4), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, + expectedSubaccountsModuleAccBalance: big.NewInt(100), // 600 - 500 + expectedFeeModuleAccBalance: big.NewInt(2950), // 2500 + 500 - 50 + expectedMarketMapperAccBalance: big.NewInt(50), // 0 + 50 + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), collateralPoolAddr: authtypes.NewModuleAddress( types.ModuleName + ":" + lib.IntToString(4), ), - + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -1342,15 +1416,28 @@ func TestDistributeFees(t *testing.T) { revShareExpiration: 100, }, "success - market mapper rev share expired": { - asset: *constants.Usdc, - feeModuleAccBalance: big.NewInt(2500), - subaccountModuleAccBalance: big.NewInt(600), - marketMapperAccBalance: big.NewInt(0), - quantums: big.NewInt(500), - expectedSubaccountsModuleAccBalance: big.NewInt(100), // 600 - 500 - expectedFeeModuleAccBalance: big.NewInt(3000), // 500 + 2500 - expectedMarketMapperAccBalance: big.NewInt(0), - perpetualId: 4, + asset: *constants.Usdc, + feeModuleAccBalance: big.NewInt(2500), + subaccountModuleAccBalance: big.NewInt(600), + marketMapperAccBalance: big.NewInt(0), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(4), + MarketId: uint32(4), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", + expectedSubaccountsModuleAccBalance: big.NewInt(100), // 600 - 500 + expectedFeeModuleAccBalance: big.NewInt(3000), // 500 + 2500 + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), collateralPoolAddr: authtypes.NewModuleAddress( types.ModuleName + ":" + lib.IntToString(4), ), @@ -1363,20 +1450,32 @@ func TestDistributeFees(t *testing.T) { setRevenueShare: true, revShareExpiration: -10, }, - "success - negative fees to market mapper and fee collector": { - asset: *constants.Usdc, - feeModuleAccBalance: big.NewInt(2500), - subaccountModuleAccBalance: big.NewInt(600), - marketMapperAccBalance: big.NewInt(100), - quantums: big.NewInt(-500), - expectedSubaccountsModuleAccBalance: big.NewInt(1100), // 600 + 500 - expectedFeeModuleAccBalance: big.NewInt(2050), // 2500 - (500 - 50) - expectedMarketMapperAccBalance: big.NewInt(50), // 100 - 50 - perpetualId: 4, + "success - market mapper rev share rounded down to 0": { + asset: *constants.Usdc, + feeModuleAccBalance: big.NewInt(100), + subaccountModuleAccBalance: big.NewInt(200), + marketMapperAccBalance: big.NewInt(0), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(5), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(4), + FillQuoteQuantums: big.NewInt(9), + ProductId: uint32(4), + MarketId: uint32(4), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, + expectedSubaccountsModuleAccBalance: big.NewInt(191), // 200 - 9 + expectedFeeModuleAccBalance: big.NewInt(109), // 100 + 9 + expectedMarketMapperAccBalance: big.NewInt(0), + expectedAffiliateAccBalance: big.NewInt(0), + expectedUnconditionalRevShareAccBalance: big.NewInt(0), collateralPoolAddr: authtypes.NewModuleAddress( types.ModuleName + ":" + lib.IntToString(4), ), - + affiliateRevShareAcctAddr: "", + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: "", revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -1385,19 +1484,32 @@ func TestDistributeFees(t *testing.T) { setRevenueShare: true, revShareExpiration: 100, }, - "success - market mapper rev share rounded down to 0": { - asset: *constants.Usdc, - feeModuleAccBalance: big.NewInt(100), - subaccountModuleAccBalance: big.NewInt(200), - marketMapperAccBalance: big.NewInt(0), - quantums: big.NewInt(9), - expectedSubaccountsModuleAccBalance: big.NewInt(191), // 200 - 9 - expectedFeeModuleAccBalance: big.NewInt(109), // 100 + 9 - expectedMarketMapperAccBalance: big.NewInt(0), - perpetualId: 4, + "success - distribute fees to market mapper, unconditional rev share, affiliate and fee collector": { + asset: *constants.Usdc, + feeModuleAccBalance: big.NewInt(2500), + subaccountModuleAccBalance: big.NewInt(600), + marketMapperAccBalance: big.NewInt(0), + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(250), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(250), + FillQuoteQuantums: big.NewInt(500), + ProductId: uint32(4), + MarketId: uint32(4), + MonthlyRollingTakerVolumeQuantums: 1_000_000, + }, + expectedSubaccountsModuleAccBalance: big.NewInt(100), // 600 - 500 + expectedFeeModuleAccBalance: big.NewInt(2888), // 2500 + 500 - 50 + expectedMarketMapperAccBalance: big.NewInt(50), // 10% of 500 + expectedAffiliateAccBalance: big.NewInt(12), // 5% of 250 + expectedUnconditionalRevShareAccBalance: big.NewInt(50), // 10% of 500 collateralPoolAddr: authtypes.NewModuleAddress( types.ModuleName + ":" + lib.IntToString(4), ), + affiliateRevShareAcctAddr: constants.BobAccAddress.String(), + marketMapperRevShareAcctAddr: constants.AliceAccAddress.String(), + unconditionalRevShareAcctAddr: constants.CarlAccAddress.String(), revshareParams: revsharetypes.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -1412,7 +1524,8 @@ func TestDistributeFees(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, revShareKeeper, _ := + ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, + bankKeeper, assetsKeeper, _, revShareKeeper, affiliatesKeeper, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) keepertest.CreateTestLiquidityTiers(t, ctx, perpetualsKeeper) @@ -1461,7 +1574,7 @@ func TestDistributeFees(t *testing.T) { require.NoError(t, err) } - marketMapperAddr, err := sdk.AccAddressFromBech32(constants.AliceAccAddress.String()) + marketMapperAddr, err := sdk.AccAddressFromBech32(tc.marketMapperRevShareAcctAddr) require.NoError(t, err) if tc.marketMapperAccBalance.Sign() > 0 { @@ -1506,14 +1619,32 @@ func TestDistributeFees(t *testing.T) { if tc.setRevenueShare { revShareKeeper.SetMarketMapperRevShareDetails( ctx, - tc.perpetualId, + tc.fill.MarketId, revsharetypes.MarketMapperRevShareDetails{ ExpirationTs: uint64(ctx.BlockTime().Unix() + tc.revShareExpiration), }, ) } - - err = keeper.DistributeFees(ctx, tc.asset.Id, tc.quantums, tc.perpetualId) + if tc.affiliateRevShareAcctAddr != "" { + err := affiliatesKeeper.RegisterAffiliate(ctx, refereeAccAddr, tc.affiliateRevShareAcctAddr) + require.NoError(t, err) + } + if tc.unconditionalRevShareAcctAddr != "" { + err := affiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.DefaultAffiliateTiers) + require.NoError(t, err) + revShareKeeper.SetUnconditionalRevShareConfigParams(ctx, + revsharetypes.UnconditionalRevShareConfig{ + Configs: []revsharetypes.UnconditionalRevShareConfig_RecipientConfig{ + { + Address: tc.unconditionalRevShareAcctAddr, + SharePpm: defaultUnconditionalRevSharePpm, + }, + }, + }) + } + revSharesForFill, err := revShareKeeper.GetAllRevShares(ctx, tc.fill) + require.NoError(t, err) + err = keeper.DistributeFees(ctx, tc.asset.Id, revSharesForFill, tc.fill) if tc.expectedErr != nil { require.ErrorIs(t, @@ -1551,6 +1682,34 @@ func TestDistributeFees(t *testing.T) { sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.expectedMarketMapperAccBalance)), marketMapperBalance, ) + + // Check the unconditional rev share account balance has been updated as expected. + if tc.expectedUnconditionalRevShareAccBalance.Sign() > 0 { + unconditionalRevShareAddr, err := sdk.AccAddressFromBech32(tc.unconditionalRevShareAcctAddr) + require.NoError(t, err) + unconditionalRevShareBalance := bankKeeper.GetBalance( + ctx, unconditionalRevShareAddr, + tc.asset.Denom, + ) + require.Equal(t, + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.expectedUnconditionalRevShareAccBalance)), + unconditionalRevShareBalance, + ) + } + + // Check the affiliate account balance has been updated as expected. + if tc.expectedAffiliateAccBalance.Sign() > 0 { + affiliateAddr, err := sdk.AccAddressFromBech32(tc.affiliateRevShareAcctAddr) + require.NoError(t, err) + affiliateBalance := bankKeeper.GetBalance( + ctx, affiliateAddr, + tc.asset.Denom, + ) + require.Equal(t, + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.expectedAffiliateAccBalance)), + affiliateBalance, + ) + } }) } } @@ -1668,7 +1827,7 @@ func TestTransferInsuranceFundPayments(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, pricesKeeper, perpsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _ := + ctx, keeper, pricesKeeper, perpsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _, _, _ := keepertest.SubaccountsKeepers(t, true) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) diff --git a/protocol/x/subaccounts/module_test.go b/protocol/x/subaccounts/module_test.go index 10b715ff9f..68532b2995 100644 --- a/protocol/x/subaccounts/module_test.go +++ b/protocol/x/subaccounts/module_test.go @@ -39,7 +39,7 @@ func createAppModule(t *testing.T) subaccounts.AppModule { func createAppModuleWithKeeper(t *testing.T) (subaccounts.AppModule, *sa_keeper.Keeper, sdk.Context) { appCodec := codec.NewProtoCodec(module.InterfaceRegistry) - ctx, keeper, _, _, _, _, _, _, _, _ := keeper.SubaccountsKeepers(t, true) + ctx, keeper, _, _, _, _, _, _, _, _, _ := keeper.SubaccountsKeepers(t, true) return subaccounts.NewAppModule( appCodec,