From c9b8feba2da1e18569f737a68d87fc175428aa06 Mon Sep 17 00:00:00 2001 From: ze97286 Date: Wed, 17 Jul 2024 14:58:49 +0100 Subject: [PATCH] feat: fee splitting + rebate program + buy back and treasury fees --- CHANGELOG.md | 2 + .../commands/start/sqlsubscribers.go | 14 + commands/proposal_submission.go | 76 +- ...submission_update_referral_program_test.go | 130 +- core/collateral/engine.go | 98 +- core/events/bus.go | 17 + core/events/referral_set.go | 20 +- core/events/volume_rebate_program.go | 172 + core/execution/amm/engine_test.go | 2 +- core/execution/common/interfaces.go | 2 + .../common/liquidity_provision_test.go | 2 +- .../common/market_activity_tracker.go | 80 +- .../market_activity_tracker_internal_test.go | 24 +- .../market_activity_tracker_snapshot.go | 20 + .../common/market_activity_tracker_test.go | 62 +- .../mat_intermediate_scores_internal_test.go | 11 +- core/execution/engine.go | 17 +- core/execution/engine_netparams.go | 48 + core/execution/engine_snapshot_test.go | 22 +- core/execution/future/market.go | 9 +- core/execution/future/market_callbacks.go | 12 + core/execution/future/market_snapshot_test.go | 9 +- core/execution/future/market_test.go | 25 +- core/execution/snapshot_test.go | 21 +- core/execution/spot/market.go | 11 +- core/execution/spot/market_callbacks.go | 12 + core/execution/spot/market_test.go | 12 +- core/fee/engine.go | 165 +- core/fee/engine_test.go | 190 +- core/fee/mocks/mocks.go | 71 +- core/governance/engine.go | 6 + ...ine_update_discount_volume_program_test.go | 38 +- .../engine_update_referral_program_test.go | 124 +- core/governance/market.go | 13 + core/governance/market_cp_restore_test.go | 9 +- core/governance/netparams.go | 16 + core/governance/proposal.go | 9 + core/governance/referral_program.go | 20 +- core/governance/volume_discount_program.go | 10 +- core/governance/volume_rebate_program.go | 53 + .../features/0084-VDPR-012.feature | 14 +- .../features/0084-VDPR-013.feature | 16 +- .../features/0084-VDPR-014.feature | 12 +- .../0083-RFPR-benefit_factors.feature | 90 +- .../0083-RFPR-running_volume.feature | 74 +- .../0083-RFPR-staking_multipliers.feature | 10 +- .../0029-FEES-multiple_discounts.feature | 32 +- core/integration/main_test.go | 42 +- core/integration/setup_test.go | 16 +- .../steps/the_fees_configuration.go | 18 + ...he_referral_benefit_tiers_configuration.go | 44 +- .../steps/the_referral_set_stats_should_be.go | 56 +- .../steps/volume_discount_program.go | 64 +- .../steps/volume_rebate_program.go | 156 + .../integration/stubs/fee_discount_service.go | 4 +- core/netparams/defaults.go | 13 + core/netparams/keys.go | 30 +- core/processor/abci.go | 10 + core/protocol/all_services.go | 21 +- core/protocol/protocol.go | 1 + core/referral/engine.go | 83 +- core/referral/engine_test.go | 180 +- core/referral/snapshot.go | 9 +- core/snapshot/providers.go | 1 + core/types/collateral.go | 2 + core/types/fees.go | 72 +- core/types/governance_proposal.go | 7 + core/types/governance_proposal_terms.go | 26 + .../governance_update_referral_program.go | 19 +- ...vernance_update_volume_discount_program.go | 11 +- ...governance_update_volume_rebate_program.go | 194 + core/types/market_test.go | 4 + core/types/matching_test.go | 6 + core/types/rebate_program.go | 126 + core/types/referral_program.go | 102 +- core/types/referral_set.go | 12 +- core/types/snapshot.go | 1 + core/types/snapshot_nodes.go | 34 + core/types/transfer.go | 7 + core/types/volume_discount_program.go | 15 +- core/volumediscount/engine.go | 16 +- core/volumediscount/engine_test.go | 184 +- core/volumediscount/snapshot.go | 12 +- core/volumerebate/dependencies.go | 31 + core/volumerebate/engine.go | 225 + core/volumerebate/engine_test.go | 389 ++ core/volumerebate/helpers_for_test.go | 98 + core/volumerebate/mocks/mocks.go | 87 + core/volumerebate/snapshot.go | 196 + datanode/api/errors.go | 1 + datanode/api/trading_data_v2.go | 44 + datanode/broker/convert.go | 8 + datanode/entities/entities.go | 2 +- datanode/entities/market.go | 6 + datanode/entities/referral_set_stats.go | 21 +- datanode/entities/trade.go | 97 +- datanode/entities/volume_discount_stats.go | 19 +- datanode/entities/volume_rebate_program.go | 72 + datanode/entities/volume_rebate_stats.go | 97 + .../gateway/graphql/benefit_tier_resolver.go | 66 + datanode/gateway/graphql/generated.go | 4802 ++++++++++++-- datanode/gateway/graphql/gqlgen.yml | 8 + datanode/gateway/graphql/mocks/mocks.go | 40 + datanode/gateway/graphql/models.go | 42 + .../gateway/graphql/referral_set_resolver.go | 15 + datanode/gateway/graphql/resolvers.go | 39 + datanode/gateway/graphql/schema.graphql | 141 +- .../graphql/volume_discount_stats_resolver.go | 22 + .../graphql/volume_rebate_program_resolver.go | 48 + .../graphql/volume_rebate_stats_resolver.go | 39 + datanode/networkhistory/service_test.go | 12 +- datanode/service/stubs.go | 12 + ...ional_referral_set_stats split_factors.sql | 90 + datanode/sqlstore/proposals_test.go | 2 +- datanode/sqlstore/referral_sets.go | 14 +- datanode/sqlstore/referral_sets_test.go | 70 +- datanode/sqlstore/sqlstore.go | 2 + datanode/sqlstore/trades.go | 9 +- datanode/sqlstore/volume_discount_stats.go | 2 +- .../sqlstore/volume_discount_stats_test.go | 10 +- datanode/sqlstore/volume_rebate_program.go | 84 + .../sqlstore/volume_rebate_programs_test.go | 291 + datanode/sqlstore/volume_rebate_stats.go | 102 + datanode/sqlstore/volume_rebate_stats_test.go | 271 + .../sqlsubscribers/volume_rebate_programs.go | 99 + .../volume_rebate_stats_updated.go | 74 + protos/data-node/api/v2/trading_data.pb.go | 5797 ++++++++++------- protos/data-node/api/v2/trading_data.pb.gw.go | 148 + .../data-node/api/v2/trading_data_grpc.pb.go | 86 + protos/embed_test.go | 2 +- .../data-node/api/v2/trading_data.proto | 106 +- .../sources/data-node/grpc-rest-bindings.yml | 4 + .../vega/checkpoint/v1/checkpoint.proto | 3 + protos/sources/vega/events/v1/events.proto | 87 +- protos/sources/vega/governance.proto | 23 + protos/sources/vega/markets.proto | 4 + .../sources/vega/snapshot/v1/snapshot.proto | 30 + protos/sources/vega/vega.proto | 73 +- protos/vega/checkpoint/v1/checkpoint.pb.go | 410 +- protos/vega/events/v1/events.pb.go | 5208 ++++++++------- protos/vega/governance.pb.go | 1627 +++-- protos/vega/markets.pb.go | 573 +- protos/vega/snapshot/v1/snapshot.pb.go | 5475 ++++++++-------- protos/vega/vega.pb.go | 3618 +++++----- 144 files changed, 23021 insertions(+), 11552 deletions(-) create mode 100644 core/events/volume_rebate_program.go create mode 100644 core/governance/volume_rebate_program.go create mode 100644 core/integration/steps/volume_rebate_program.go create mode 100644 core/types/governance_update_volume_rebate_program.go create mode 100644 core/types/rebate_program.go create mode 100644 core/volumerebate/dependencies.go create mode 100644 core/volumerebate/engine.go create mode 100644 core/volumerebate/engine_test.go create mode 100644 core/volumerebate/helpers_for_test.go create mode 100644 core/volumerebate/mocks/mocks.go create mode 100644 core/volumerebate/snapshot.go create mode 100644 datanode/entities/volume_rebate_program.go create mode 100644 datanode/entities/volume_rebate_stats.go create mode 100644 datanode/gateway/graphql/volume_rebate_program_resolver.go create mode 100644 datanode/gateway/graphql/volume_rebate_stats_resolver.go create mode 100644 datanode/sqlstore/migrations/0116_additional_referral_set_stats split_factors.sql create mode 100644 datanode/sqlstore/volume_rebate_program.go create mode 100644 datanode/sqlstore/volume_rebate_programs_test.go create mode 100644 datanode/sqlstore/volume_rebate_stats.go create mode 100644 datanode/sqlstore/volume_rebate_stats_test.go create mode 100644 datanode/sqlsubscribers/volume_rebate_programs.go create mode 100644 datanode/sqlsubscribers/volume_rebate_stats_updated.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 385c54a2d92..e89843d2aa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,8 @@ - [11400](https://github.com/vegaprotocol/vega/issues/11400) - Add support for long block auction. - [11026](https://github.com/vegaprotocol/vega/issues/11026) - Add API flag to get paid liquidity fees for a `vAMM` using the parent key. - [11027](https://github.com/vegaprotocol/vega/issues/11027) - Add API filters to get fees and rewards by market, across epochs. +- [11428](https://github.com/vegaprotocol/vega/issues/11428) - Add buy back and treasury fee and separate discount/reward factors. +- [11468](https://github.com/vegaprotocol/vega/issues/11468) - Added support for volume rebate program. - [10360](https://github.com/vegaprotocol/vega/issues/10360) - Scale funding payment by fraction of period spent outside of auction. - [11452](https://github.com/vegaprotocol/vega/issues/11452) - Implement alternative estimations for fully collateralised capped future. diff --git a/cmd/data-node/commands/start/sqlsubscribers.go b/cmd/data-node/commands/start/sqlsubscribers.go index f381571e78a..87903ce48a6 100644 --- a/cmd/data-node/commands/start/sqlsubscribers.go +++ b/cmd/data-node/commands/start/sqlsubscribers.go @@ -84,6 +84,8 @@ type SQLSubscribers struct { timeWeightedNotionalPositionStore *sqlstore.TimeWeightedNotionalPosition gameScoreStore *sqlstore.GameScores ammPoolsStore *sqlstore.AMMPools + volumeRebateStatsStore *sqlstore.VolumeRebateStats + volumeRebateProgramsStore *sqlstore.VolumeRebatePrograms // Services candleService *candlesv2.Svc @@ -142,6 +144,8 @@ type SQLSubscribers struct { timeWeightedNotionalPositionService *service.TimeWeightedNotionalPosition gameScoreService *service.GameScore ammPoolsService *service.AMMPools + volumeRebateStatsService *service.VolumeRebateStats + volumeRebateProgramService *service.VolumeRebatePrograms // Subscribers accountSub *sqlsubscribers.Account @@ -196,6 +200,8 @@ type SQLSubscribers struct { timeWeightedNotionalPositionSub *sqlsubscribers.TimeWeightedNotionalPosition gameScoreSub *sqlsubscribers.GameScore ammPoolsSub *sqlsubscribers.AMMPools + volumeRebateStatsSub *sqlsubscribers.VolumeRebateStatsUpdated + volumeRebateProgramSub *sqlsubscribers.VolumeRebateProgram } func (s *SQLSubscribers) GetSQLSubscribers() []broker.SQLBrokerSubscriber { @@ -254,6 +260,8 @@ func (s *SQLSubscribers) GetSQLSubscribers() []broker.SQLBrokerSubscriber { s.timeWeightedNotionalPositionSub, s.gameScoreSub, s.ammPoolsSub, + s.volumeRebateProgramSub, + s.volumeRebateStatsSub, } } @@ -317,6 +325,8 @@ func (s *SQLSubscribers) CreateAllStores(ctx context.Context, Log *logging.Logge s.timeWeightedNotionalPositionStore = sqlstore.NewTimeWeightedNotionalPosition(transactionalConnectionSource) s.gameScoreStore = sqlstore.NewGameScores(transactionalConnectionSource) s.ammPoolsStore = sqlstore.NewAMMPools(transactionalConnectionSource) + s.volumeRebateStatsStore = sqlstore.NewVolumeRebateStats(transactionalConnectionSource) + s.volumeRebateProgramsStore = sqlstore.NewVolumeRebatePrograms(transactionalConnectionSource) } func (s *SQLSubscribers) SetupServices(ctx context.Context, log *logging.Logger, cfg service.Config, candlesConfig candlesv2.Config) error { @@ -374,6 +384,8 @@ func (s *SQLSubscribers) SetupServices(ctx context.Context, log *logging.Logger, s.timeWeightedNotionalPositionService = service.NewTimeWeightedNotionalPosition(s.timeWeightedNotionalPositionStore) s.gameScoreService = service.NewGameScore(s.gameScoreStore, log) s.ammPoolsService = service.NewAMMPools(s.ammPoolsStore) + s.volumeRebateStatsService = service.NewVolumeRebateStats(s.volumeRebateStatsStore) + s.volumeRebateProgramService = service.NewVolumeRebatePrograms(s.volumeRebateProgramsStore) s.marketDepthService = service.NewMarketDepth( cfg.MarketDepth, @@ -455,5 +467,7 @@ func (s *SQLSubscribers) SetupSQLSubscribers() { s.marginModesSub = sqlsubscribers.NewMarginModes(s.marginModesService) s.timeWeightedNotionalPositionSub = sqlsubscribers.NewTimeWeightedNotionalPosition(s.timeWeightedNotionalPositionService) s.gameScoreSub = sqlsubscribers.NewGameScore(s.gameScoreStore) + s.volumeRebateStatsSub = sqlsubscribers.NewVolumeRebateStatsUpdated(s.volumeRebateStatsService) + s.volumeRebateProgramSub = sqlsubscribers.NewVolumeRebateProgram(s.volumeRebateProgramService) s.ammPoolsSub = sqlsubscribers.NewAMMPools(s.ammPoolsService, s.marketDepthService) } diff --git a/commands/proposal_submission.go b/commands/proposal_submission.go index 1e6b2d8c8fa..ba804cc7208 100644 --- a/commands/proposal_submission.go +++ b/commands/proposal_submission.go @@ -494,25 +494,73 @@ func checkBenefitTier(index int, tier *vegapb.BenefitTier) Errors { } } - if len(tier.ReferralRewardFactor) == 0 { - errs.AddForProperty(propertyPath+".referral_reward_factor", ErrIsRequired) + if tier.ReferralRewardFactors == nil { + errs.AddForProperty(propertyPath+".referral_reward_factors", ErrIsRequired) } else { - rrf, err := num.DecimalFromString(tier.ReferralRewardFactor) - if err != nil { - errs.AddForProperty(propertyPath+".referral_reward_factor", ErrIsNotValidNumber) - } else if rrf.IsNegative() { - errs.AddForProperty(propertyPath+".referral_reward_factor", ErrMustBePositiveOrZero) + if len(tier.ReferralRewardFactors.InfrastructureRewardFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_reward_factors.infrastructure_reward_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralRewardFactors.InfrastructureRewardFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_reward_factors.infrastructure_reward_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_reward_factors.infrastructure_reward_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralRewardFactors.MakerRewardFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_reward_factors.maker_reward_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralRewardFactors.MakerRewardFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_reward_factors.maker_reward_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_reward_factors.maker_reward_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralRewardFactors.LiquidityRewardFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_reward_factors.liquidity_reward_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralRewardFactors.LiquidityRewardFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_reward_factors.liquidity_reward_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_reward_factors.liquidity_reward_factor", ErrMustBePositiveOrZero) + } } } - if len(tier.ReferralDiscountFactor) == 0 { - errs.AddForProperty(propertyPath+".referral_discount_factor", ErrIsRequired) + if tier.ReferralDiscountFactors == nil { + errs.AddForProperty(propertyPath+".referral_discount_factors", ErrIsRequired) } else { - rdf, err := num.DecimalFromString(tier.ReferralDiscountFactor) - if err != nil { - errs.AddForProperty(propertyPath+".referral_discount_factor", ErrIsNotValidNumber) - } else if rdf.IsNegative() { - errs.AddForProperty(propertyPath+".referral_discount_factor", ErrMustBePositiveOrZero) + if len(tier.ReferralDiscountFactors.InfrastructureDiscountFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_discount_factors.infrastructure_discount_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralDiscountFactors.InfrastructureDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_discount_factors.infrastructure_discount_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_discount_factors.infrastructure_discount_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralDiscountFactors.MakerDiscountFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_discount_factors.maker_discount_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralDiscountFactors.MakerDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_discount_factors.maker_discount_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_discount_factors.maker_discount_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralDiscountFactors.LiquidityDiscountFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_discount_factors.liquidity_discount_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralDiscountFactors.LiquidityDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_discount_factors.liquidity_discount_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_discount_factors.liquidity_discount_factor", ErrMustBePositiveOrZero) + } } } diff --git a/commands/proposal_submission_update_referral_program_test.go b/commands/proposal_submission_update_referral_program_test.go index a987c8e5fff..2df7549bfbf 100644 --- a/commands/proposal_submission_update_referral_program_test.go +++ b/commands/proposal_submission_update_referral_program_test.go @@ -183,6 +183,8 @@ func testSubmissionForReferralProgramUpdateWithoutTierMinimumRunningNotionalTake } func testSubmissionForReferralProgramUpdateWithDuplicateBenefitTierEntriesFails(t *testing.T) { + factors := []string{"1.1", "1.2", "1.3", "1.4", "1.5", "1.6"} + err := checkProposalSubmission(&commandspb.ProposalSubmission{ Terms: &types.ProposalTerms{ Change: &types.ProposalTerms_UpdateReferralProgram{ @@ -192,20 +194,44 @@ func testSubmissionForReferralProgramUpdateWithDuplicateBenefitTierEntriesFails( { MinimumRunningNotionalTakerVolume: "100", MinimumEpochs: "10", - ReferralRewardFactor: "1.1", - ReferralDiscountFactor: "1.2", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: factors[0], + MakerRewardFactor: factors[1], + LiquidityRewardFactor: factors[2], + }, + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: factors[1], + MakerDiscountFactor: factors[2], + LiquidityDiscountFactor: factors[3], + }, }, { MinimumRunningNotionalTakerVolume: "100", MinimumEpochs: "10", - ReferralRewardFactor: "1.2", - ReferralDiscountFactor: "1.3", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: factors[1], + MakerRewardFactor: factors[2], + LiquidityRewardFactor: factors[3], + }, + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: factors[2], + MakerDiscountFactor: factors[3], + LiquidityDiscountFactor: factors[4], + }, }, { MinimumRunningNotionalTakerVolume: "100", MinimumEpochs: "20", - ReferralRewardFactor: "1.3", - ReferralDiscountFactor: "1.4", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: factors[2], + MakerRewardFactor: factors[3], + LiquidityRewardFactor: factors[4], + }, + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: factors[3], + MakerDiscountFactor: factors[4], + LiquidityDiscountFactor: factors[5], + }, }, }, }, @@ -376,8 +402,8 @@ func testSubmissionForReferralProgramUpdateWithoutTierReferralRewardFactorFails( }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factor"), commands.ErrIsRequired) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors"), commands.ErrIsRequired) } func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralRewardFactorFails(t *testing.T) { @@ -388,9 +414,17 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralRewardFac Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralRewardFactor: "qbc", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "qbc", + MakerRewardFactor: "qbc", + LiquidityRewardFactor: "qbc", + }, }, { - ReferralRewardFactor: "0x32", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "0x32", + MakerRewardFactor: "0x32", + LiquidityRewardFactor: "0x32", + }, }, }, }, @@ -399,8 +433,8 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralRewardFac }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factor"), commands.ErrIsNotValidNumber) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.infrastructure_reward_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.infrastructure_reward_factor"), commands.ErrIsNotValidNumber) } func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralRewardFactorFails(t *testing.T) { @@ -411,9 +445,17 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralRewardFact Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralRewardFactor: "-10", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "-10", + MakerRewardFactor: "-10", + LiquidityRewardFactor: "-10", + }, }, { - ReferralRewardFactor: "-1", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "-1", + MakerRewardFactor: "-1", + LiquidityRewardFactor: "-1", + }, }, }, }, @@ -422,8 +464,12 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralRewardFact }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factor"), commands.ErrMustBePositiveOrZero) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.infrastructure_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.infrastructure_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.maker_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.maker_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.liquidity_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.liquidity_reward_factor"), commands.ErrMustBePositiveOrZero) } func testSubmissionForReferralProgramUpdateWithoutTierReferralDiscountFactorFails(t *testing.T) { @@ -434,9 +480,9 @@ func testSubmissionForReferralProgramUpdateWithoutTierReferralDiscountFactorFail Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralDiscountFactor: "", + ReferralDiscountFactors: &types.DiscountFactors{}, }, { - ReferralDiscountFactor: "", + ReferralDiscountFactors: &types.DiscountFactors{}, }, }, }, @@ -445,8 +491,12 @@ func testSubmissionForReferralProgramUpdateWithoutTierReferralDiscountFactorFail }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factor"), commands.ErrIsRequired) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.maker_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.maker_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsRequired) } func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralDiscountFactorFails(t *testing.T) { @@ -457,9 +507,17 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralDiscountF Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralDiscountFactor: "qbc", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "qbc", + LiquidityDiscountFactor: "qbc", + MakerDiscountFactor: "qbc", + }, }, { - ReferralDiscountFactor: "0x32", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "0x32", + LiquidityDiscountFactor: "0x32", + MakerDiscountFactor: "0x32", + }, }, }, }, @@ -468,8 +526,12 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralDiscountF }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factor"), commands.ErrIsNotValidNumber) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.maker_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.maker_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsNotValidNumber) } func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralDiscountFactorFails(t *testing.T) { @@ -480,9 +542,17 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralDiscountFa Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralDiscountFactor: "-10", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "-10", + MakerDiscountFactor: "-10", + LiquidityDiscountFactor: "-10", + }, }, { - ReferralDiscountFactor: "-1", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "-1", + MakerDiscountFactor: "-1", + LiquidityDiscountFactor: "-1", + }, }, }, }, @@ -491,8 +561,12 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralDiscountFa }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factor"), commands.ErrMustBePositiveOrZero) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.infrastructure_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.infrastructure_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.liquidity_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.liquidity_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.maker_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.maker_discount_factor"), commands.ErrMustBePositiveOrZero) } func testSubmissionForReferralProgramUpdateWithoutStakingTierMinimumStakedTokensFails(t *testing.T) { diff --git a/core/collateral/engine.go b/core/collateral/engine.go index 39640306389..bb245e10d92 100644 --- a/core/collateral/engine.go +++ b/core/collateral/engine.go @@ -732,7 +732,7 @@ func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID for _, transfer := range transfers { req, err := e.getSpotFeeTransferRequest( - transfer, makerFee, infraFee, liquiFee, marketID, assetID) + ctx, transfer, makerFee, infraFee, liquiFee, marketID, assetID) if err != nil { e.log.Error("Failed to build transfer request for event", logging.Error(err)) @@ -763,6 +763,7 @@ func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID } func (e *Engine) getSpotFeeTransferRequest( + ctx context.Context, t *types.Transfer, makerFee, infraFee, liquiFee *types.Account, marketID, assetID string, @@ -795,6 +796,13 @@ func (e *Engine) getSpotFeeTransferRequest( return nil, err } + networkTreausry, err := e.GetNetworkTreasuryAccount(assetID) + if err != nil { + return nil, err + } + + buyBackAccount := e.getOrCreateBuyBackFeesAccount(ctx, assetID) + treq := &types.TransferRequest{ Amount: t.Amount.Amount.Clone(), MinAmount: t.Amount.Amount.Clone(), @@ -814,6 +822,20 @@ func (e *Engine) getSpotFeeTransferRequest( treq.FromAccount = []*types.Account{infraFee} treq.ToAccount = []*types.Account{general} return treq, nil + case types.TransferTypeTreasuryPay: + amt := num.Min(treq.Amount, general.Balance.Clone()) + treq.Amount = amt + treq.MinAmount = amt + treq.FromAccount = []*types.Account{general} + treq.ToAccount = []*types.Account{networkTreausry} + return treq, nil + case types.TransferTypeBuyBackFeePay: + amt := num.Min(treq.Amount, general.Balance.Clone()) + treq.Amount = amt + treq.MinAmount = amt + treq.FromAccount = []*types.Account{general} + treq.ToAccount = []*types.Account{buyBackAccount} + return treq, nil case types.TransferTypeLiquidityFeePay: amt := num.Min(treq.Amount, general.Balance.Clone()) treq.Amount = amt @@ -836,6 +858,17 @@ func (e *Engine) getSpotFeeTransferRequest( treq.FromAccount = []*types.Account{makerFee} treq.ToAccount = []*types.Account{general} return treq, nil + case types.TransferTypeHighMakerRebateReceive: + treq.FromAccount = []*types.Account{makerFee} + treq.ToAccount = []*types.Account{general} + return treq, nil + case types.TransferTypeHighMakerRebatePay: + amt := num.Min(treq.Amount, general.Balance.Clone()) + treq.Amount = amt + treq.MinAmount = amt + treq.FromAccount = []*types.Account{general} + treq.ToAccount = []*types.Account{makerFee} + return treq, nil case types.TransferTypeLiquidityFeeAllocate: partyLiquidityFee, err := partyLiquidityFeeAccount() if err != nil { @@ -2234,6 +2267,12 @@ func (e *Engine) getFeeTransferRequest( general *types.Account err error ) + networkTreausry, err := e.GetNetworkTreasuryAccount(assetID) + if err != nil { + return nil, err + } + buyBackAccount := e.getOrCreateBuyBackFeesAccount(ctx, assetID) + if t.Owner == types.NetworkParty { general, err = e.GetMarketInsurancePoolAccount(marketID, assetID) if err != nil { @@ -2295,6 +2334,28 @@ func (e *Engine) getFeeTransferRequest( treq.FromAccount = append(treq.FromAccount, orderMargin) } treq.ToAccount = []*types.Account{infraFee} + case types.TransferTypeTreasuryPay: + margin, err := marginAccount() + if err != nil { + return nil, err + } + orderMargin := orderMarginAccount() + treq.FromAccount = []*types.Account{general, margin} + if orderMargin != nil { + treq.FromAccount = append(treq.FromAccount, orderMargin) + } + treq.ToAccount = []*types.Account{networkTreausry} + case types.TransferTypeBuyBackFeePay: + margin, err := marginAccount() + if err != nil { + return nil, err + } + orderMargin := orderMarginAccount() + treq.FromAccount = []*types.Account{general, margin} + if orderMargin != nil { + treq.FromAccount = append(treq.FromAccount, orderMargin) + } + treq.ToAccount = []*types.Account{buyBackAccount} case types.TransferTypeInfrastructureFeeDistribute: treq.FromAccount = []*types.Account{infraFee} treq.ToAccount = []*types.Account{general} @@ -2326,6 +2387,21 @@ func (e *Engine) getFeeTransferRequest( case types.TransferTypeMakerFeeReceive: treq.FromAccount = []*types.Account{makerFee} treq.ToAccount = []*types.Account{general} + case types.TransferTypeHighMakerRebatePay: + margin, err := marginAccount() + if err != nil { + return nil, err + } + orderMargin := orderMarginAccount() + treq.FromAccount = []*types.Account{general, margin} + if orderMargin != nil { + treq.FromAccount = append(treq.FromAccount, orderMargin) + } + treq.ToAccount = []*types.Account{makerFee} + case types.TransferTypeHighMakerRebateReceive: + treq.FromAccount = []*types.Account{makerFee} + treq.ToAccount = []*types.Account{general} + return treq, nil case types.TransferTypeLiquidityFeeAllocate: partyLiquidityFee, err := partyLiquidityFeeAccount() if err != nil { @@ -4594,6 +4670,26 @@ func (e *Engine) GetNetworkTreasuryAccount(asset string) (*types.Account, error) return e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury)) } +func (e *Engine) getOrCreateBuyBackFeesAccount(ctx context.Context, asset string) *types.Account { + accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury) + acc, err := e.GetAccountByID(accID) + if err == nil { + return acc + } + ntAcc := &types.Account{ + ID: accID, + Asset: asset, + Owner: systemOwner, + Balance: num.UintZero(), + MarketID: noMarket, + Type: types.AccountTypeBuyBackFees, + } + e.accs[accID] = ntAcc + e.addAccountToHashableSlice(ntAcc) + e.broker.Send(events.NewAccountEvent(ctx, *ntAcc)) + return ntAcc +} + func (e *Engine) GetOrCreateNetworkTreasuryAccount(ctx context.Context, asset string) *types.Account { accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury) acc, err := e.GetAccountByID(accID) diff --git a/core/events/bus.go b/core/events/bus.go index bc21bac0741..3fa9e0f6c05 100644 --- a/core/events/bus.go +++ b/core/events/bus.go @@ -174,6 +174,10 @@ const ( CancelledOrdersEvent GameScoresEvent AMMPoolEvent + VolumeRebateProgramStartedEvent + VolumeRebateProgramEndedEvent + VolumeRebateProgramUpdatedEvent + VolumeRebateStatsUpdatedEvent ) var ( @@ -277,6 +281,10 @@ var ( eventspb.BusEventType_BUS_EVENT_TYPE_CANCELLED_ORDERS: CancelledOrdersEvent, eventspb.BusEventType_BUS_EVENT_TYPE_GAME_SCORES: GameScoresEvent, eventspb.BusEventType_BUS_EVENT_TYPE_AMM: AMMPoolEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_STARTED: VolumeRebateProgramStartedEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_ENDED: VolumeRebateProgramEndedEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_UPDATED: VolumeRebateProgramUpdatedEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_STATS_UPDATED: VolumeRebateStatsUpdatedEvent, // If adding a type here, please also add it to datanode/broker/convert.go } @@ -371,6 +379,11 @@ var ( CancelledOrdersEvent: eventspb.BusEventType_BUS_EVENT_TYPE_CANCELLED_ORDERS, GameScoresEvent: eventspb.BusEventType_BUS_EVENT_TYPE_GAME_SCORES, AMMPoolEvent: eventspb.BusEventType_BUS_EVENT_TYPE_AMM, + VolumeRebateProgramStartedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_STARTED, + VolumeRebateProgramEndedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_ENDED, + VolumeRebateProgramUpdatedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_UPDATED, + VolumeRebateStatsUpdatedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_STATS_UPDATED, + // If adding a type here, please also add it to datanode/broker/convert.go } @@ -464,6 +477,10 @@ var ( CancelledOrdersEvent: "CancelledOrdersEvent", GameScoresEvent: "GameScoresEvent", AMMPoolEvent: "AMMPoolEvent", + VolumeRebateProgramStartedEvent: "VolumeRebateProgramStartedEvent", + VolumeRebateProgramEndedEvent: "VolumeRebateProgramEndedEvent", + VolumeRebateProgramUpdatedEvent: "VolumeRebateProgramUpdatedEvent", + VolumeRebateStatsUpdatedEvent: "VolumeRebateStatsUpdatedEvent", } ) diff --git a/core/events/referral_set.go b/core/events/referral_set.go index 7440065ec9c..8b7f67bb124 100644 --- a/core/events/referral_set.go +++ b/core/events/referral_set.go @@ -71,15 +71,13 @@ type ReferralSetStatsUpdated struct { func (t ReferralSetStatsUpdated) Unwrap() *types.ReferralSetStats { volume, _ := num.UintFromString(t.e.ReferralSetRunningNotionalTakerVolume, 10) stats := map[types.PartyID]*types.RefereeStats{} - rewardFactor, _ := num.DecimalFromString(t.e.RewardFactor) rewardsMultiplier, _ := num.DecimalFromString(t.e.RewardsMultiplier) - rewardsFactorMultiplier, _ := num.DecimalFromString(t.e.RewardsFactorMultiplier) - + rewardsFactorsMultiplier := types.FactorsFromRewardFactorsWithDefault(t.e.RewardFactorsMultiplier, t.e.RewardsFactorMultiplier) + rewardFactors := types.FactorsFromRewardFactorsWithDefault(t.e.RewardFactors, t.e.RewardFactor) for _, stat := range t.e.RefereesStats { - discountFactor, _ := num.DecimalFromString(stat.DiscountFactor) - + discountFactors := types.FactorsFromDiscountFactorsWithDefault(stat.DiscountFactors, stat.DiscountFactor) stats[types.PartyID(stat.PartyId)] = &types.RefereeStats{ - DiscountFactor: discountFactor, + DiscountFactors: discountFactors, } } @@ -89,9 +87,9 @@ func (t ReferralSetStatsUpdated) Unwrap() *types.ReferralSetStats { WasEligible: t.e.WasEligible, ReferralSetRunningVolume: volume, RefereesStats: stats, - RewardFactor: rewardFactor, + RewardFactors: rewardFactors, RewardsMultiplier: rewardsMultiplier, - RewardsFactorMultiplier: rewardsFactorMultiplier, + RewardsFactorsMultiplier: rewardsFactorsMultiplier, } } @@ -113,7 +111,7 @@ func NewReferralSetStatsUpdatedEvent(ctx context.Context, update *types.Referral for partyID, stat := range update.RefereesStats { refereesStats = append(refereesStats, &eventspb.RefereeStats{ PartyId: string(partyID), - DiscountFactor: stat.DiscountFactor.String(), + DiscountFactors: stat.DiscountFactors.IntoDiscountFactorsProto(), EpochNotionalTakerVolume: stat.TakerVolume.String(), }) } @@ -131,9 +129,9 @@ func NewReferralSetStatsUpdatedEvent(ctx context.Context, update *types.Referral ReferralSetRunningNotionalTakerVolume: update.ReferralSetRunningVolume.String(), ReferrerTakerVolume: update.ReferrerTakerVolume.String(), RefereesStats: refereesStats, - RewardFactor: update.RewardFactor.String(), + RewardFactors: update.RewardFactors.IntoRewardFactorsProto(), RewardsMultiplier: update.RewardsMultiplier.String(), - RewardsFactorMultiplier: update.RewardsFactorMultiplier.String(), + RewardFactorsMultiplier: update.RewardsFactorsMultiplier.IntoRewardFactorsProto(), }, } } diff --git a/core/events/volume_rebate_program.go b/core/events/volume_rebate_program.go new file mode 100644 index 00000000000..1ff9f86663a --- /dev/null +++ b/core/events/volume_rebate_program.go @@ -0,0 +1,172 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package events + +import ( + "context" + "time" + + "code.vegaprotocol.io/vega/core/types" + "code.vegaprotocol.io/vega/libs/ptr" + eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" +) + +type VolumeRebateProgramStarted struct { + *Base + e *eventspb.VolumeRebateProgramStarted +} + +func (v *VolumeRebateProgramStarted) GetVolumeRebateProgramStarted() *eventspb.VolumeRebateProgramStarted { + return v.e +} + +func (t *VolumeRebateProgramStarted) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(t.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateProgramStarted{ + VolumeRebateProgramStarted: t.e, + } + + return busEvent +} + +func NewVolumeRebateProgramStartedEvent(ctx context.Context, p *types.VolumeRebateProgram, epochTime time.Time, epoch uint64) *VolumeRebateProgramStarted { + return &VolumeRebateProgramStarted{ + Base: newBase(ctx, VolumeRebateProgramStartedEvent), + e: &eventspb.VolumeRebateProgramStarted{ + Program: p.IntoProto(), + StartedAt: epochTime.UnixNano(), + AtEpoch: epoch, + }, + } +} + +func VolumeRebateProgramStartedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateProgramStarted { + return &VolumeRebateProgramStarted{ + Base: newBaseFromBusEvent(ctx, VolumeRebateProgramStartedEvent, be), + e: be.GetVolumeRebateProgramStarted(), + } +} + +type VolumeRebateProgramUpdated struct { + *Base + e *eventspb.VolumeRebateProgramUpdated +} + +func (v *VolumeRebateProgramUpdated) GetVolumeRebateProgramUpdated() *eventspb.VolumeRebateProgramUpdated { + return v.e +} + +func (t *VolumeRebateProgramUpdated) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(t.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateProgramUpdated{ + VolumeRebateProgramUpdated: t.e, + } + + return busEvent +} + +func NewVolumeRebateProgramUpdatedEvent(ctx context.Context, p *types.VolumeRebateProgram, epochTime time.Time, epoch uint64) *VolumeRebateProgramUpdated { + return &VolumeRebateProgramUpdated{ + Base: newBase(ctx, VolumeRebateProgramUpdatedEvent), + e: &eventspb.VolumeRebateProgramUpdated{ + Program: p.IntoProto(), + UpdatedAt: epochTime.UnixNano(), + AtEpoch: epoch, + }, + } +} + +func VolumeRebateProgramUpdatedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateProgramUpdated { + return &VolumeRebateProgramUpdated{ + Base: newBaseFromBusEvent(ctx, VolumeRebateProgramUpdatedEvent, be), + e: be.GetVolumeRebateProgramUpdated(), + } +} + +type VolumeRebateProgramEnded struct { + *Base + e *eventspb.VolumeRebateProgramEnded +} + +func (v *VolumeRebateProgramEnded) GetVolumeRebateProgramEnded() *eventspb.VolumeRebateProgramEnded { + return v.e +} + +func (t *VolumeRebateProgramEnded) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(t.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateProgramEnded{ + VolumeRebateProgramEnded: t.e, + } + + return busEvent +} + +func NewVolumeRebateProgramEndedEvent(ctx context.Context, version uint64, id string, epochTime time.Time, epoch uint64) *VolumeRebateProgramEnded { + return &VolumeRebateProgramEnded{ + Base: newBase(ctx, VolumeRebateProgramEndedEvent), + e: &eventspb.VolumeRebateProgramEnded{ + Version: version, + Id: id, + EndedAt: epochTime.UnixNano(), + AtEpoch: epoch, + }, + } +} + +func VolumeRebateProgramEndedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateProgramEnded { + return &VolumeRebateProgramEnded{ + Base: newBaseFromBusEvent(ctx, VolumeRebateProgramEndedEvent, be), + e: be.GetVolumeRebateProgramEnded(), + } +} + +type VolumeRebateStatsUpdated struct { + *Base + vdsu eventspb.VolumeRebateStatsUpdated +} + +func NewVolumeRebateStatsUpdatedEvent(ctx context.Context, vdsu *eventspb.VolumeRebateStatsUpdated) *VolumeRebateStatsUpdated { + order := &VolumeRebateStatsUpdated{ + Base: newBase(ctx, VolumeRebateStatsUpdatedEvent), + vdsu: *vdsu, + } + return order +} + +func (p *VolumeRebateStatsUpdated) VolumeRebateStatsUpdated() *eventspb.VolumeRebateStatsUpdated { + return ptr.From(p.vdsu) +} + +func (p VolumeRebateStatsUpdated) Proto() eventspb.VolumeRebateStatsUpdated { + return p.vdsu +} + +func (p VolumeRebateStatsUpdated) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(p.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateStatsUpdated{ + VolumeRebateStatsUpdated: ptr.From(p.vdsu), + } + + return busEvent +} + +func VolumeRebateStatsUpdatedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateStatsUpdated { + order := &VolumeRebateStatsUpdated{ + Base: newBaseFromBusEvent(ctx, VolumeRebateStatsUpdatedEvent, be), + vdsu: ptr.UnBox(be.GetVolumeRebateStatsUpdated()), + } + return order +} diff --git a/core/execution/amm/engine_test.go b/core/execution/amm/engine_test.go index a2f0095d3d2..55f06037c2d 100644 --- a/core/execution/amm/engine_test.go +++ b/core/execution/amm/engine_test.go @@ -725,7 +725,7 @@ func getTestEngine(t *testing.T) *tstEngine { teams := cmocks.NewMockTeams(ctrl) balanceChecker := cmocks.NewMockAccountBalanceChecker(ctrl) - mat := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + mat := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, col) parties := cmocks.NewMockParties(ctrl) parties.EXPECT().AssignDeriveKey(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() diff --git a/core/execution/common/interfaces.go b/core/execution/common/interfaces.go index 7ac1b714e3a..a5ecf247111 100644 --- a/core/execution/common/interfaces.go +++ b/core/execution/common/interfaces.go @@ -374,6 +374,8 @@ type CommonMarket interface { OnMarketProbabilityOfTradingTauScalingUpdate(context.Context, num.Decimal) OnMarketValueWindowLengthUpdate(time.Duration) OnFeeFactorsInfrastructureFeeUpdate(context.Context, num.Decimal) + OnFeeFactorsTreasuryFeeUpdate(context.Context, num.Decimal) + OnFeeFactorsBuyBackFeeUpdate(context.Context, num.Decimal) OnFeeFactorsMakerFeeUpdate(context.Context, num.Decimal) OnMarkPriceUpdateMaximumFrequency(context.Context, time.Duration) OnMarketAuctionMinimumDurationUpdate(context.Context, time.Duration) diff --git a/core/execution/common/liquidity_provision_test.go b/core/execution/common/liquidity_provision_test.go index 779311dfc72..2349dfa782c 100644 --- a/core/execution/common/liquidity_provision_test.go +++ b/core/execution/common/liquidity_provision_test.go @@ -83,7 +83,7 @@ func newMarketLiquidity(t *testing.T) *marketLiquidityTest { teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) - marketTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketTracker.OnEpochEvent, marketTracker.OnEpochRestore) amm := ammcmocks.NewMockAMM(ctrl) diff --git a/core/execution/common/market_activity_tracker.go b/core/execution/common/market_activity_tracker.go index 050f8c39b40..38b71792639 100644 --- a/core/execution/common/market_activity_tracker.go +++ b/core/execution/common/market_activity_tracker.go @@ -47,6 +47,10 @@ var ( dScalingFactor = num.DecimalFromInt64(scalingFactor) ) +type QuantumGetter interface { + GetAssetQuantum(asset string) (num.Decimal, error) +} + type twPosition struct { position uint64 // abs last recorded position t time.Time // time of last recorded position @@ -67,6 +71,8 @@ type marketTracker struct { lpFees map[string]*num.Uint infraFees map[string]*num.Uint lpPaidFees map[string]*num.Uint + buybackFeesPaid map[string]*num.Uint + treasuryFeesPaid map[string]*num.Uint totalMakerFeesReceived *num.Uint totalMakerFeesPaid *num.Uint @@ -105,6 +111,7 @@ type MarketActivityTracker struct { teams Teams balanceChecker AccountBalanceChecker eligibilityChecker EligibilityChecker + collateral QuantumGetter currentEpoch uint64 epochStartTime time.Time @@ -120,7 +127,7 @@ type MarketActivityTracker struct { } // NewMarketActivityTracker instantiates the fees tracker. -func NewMarketActivityTracker(log *logging.Logger, teams Teams, balanceChecker AccountBalanceChecker, broker Broker) *MarketActivityTracker { +func NewMarketActivityTracker(log *logging.Logger, teams Teams, balanceChecker AccountBalanceChecker, broker Broker, collateral QuantumGetter) *MarketActivityTracker { mat := &MarketActivityTracker{ log: log, balanceChecker: balanceChecker, @@ -132,6 +139,7 @@ func NewMarketActivityTracker(log *logging.Logger, teams Teams, balanceChecker A ss: &snapshotState{}, takerFeesPaidInEpoch: []map[string]map[string]map[string]*num.Uint{}, broker: broker, + collateral: collateral, } return mat @@ -187,6 +195,8 @@ func (mat *MarketActivityTracker) MarketProposed(asset, marketID, proposer strin lpFees: map[string]*num.Uint{}, infraFees: map[string]*num.Uint{}, lpPaidFees: map[string]*num.Uint{}, + buybackFeesPaid: map[string]*num.Uint{}, + treasuryFeesPaid: map[string]*num.Uint{}, totalMakerFeesReceived: num.UintZero(), totalMakerFeesPaid: num.UintZero(), totalLpFees: num.UintZero(), @@ -467,7 +477,7 @@ func (mat *MarketActivityTracker) RemoveMarket(asset, marketID string) { func (mt *marketTracker) aggregatedFees() map[string]*num.Uint { totalFees := map[string]*num.Uint{} - fees := []map[string]*num.Uint{mt.infraFees, mt.lpPaidFees, mt.makerFeesPaid} + fees := []map[string]*num.Uint{mt.infraFees, mt.lpPaidFees, mt.makerFeesPaid, mt.buybackFeesPaid, mt.treasuryFeesPaid} for _, fee := range fees { for party, paid := range fee { if _, ok := totalFees[party]; !ok { @@ -517,6 +527,63 @@ func (mat *MarketActivityTracker) clearDeletedMarkets() { } } +func (mat *MarketActivityTracker) CalculateTotalMakerContributionInQuantum(windowSize int) (map[string]*num.Uint, map[string]num.Decimal) { + m := map[string]*num.Uint{} + total := num.UintZero() + for ast, trackers := range mat.assetToMarketTrackers { + quantum, err := mat.collateral.GetAssetQuantum(ast) + if err != nil { + continue + } + for _, trckr := range trackers { + for i := 0; i < windowSize; i++ { + idx := len(trckr.epochMakerFeesReceived) - i - 1 + if idx < 0 { + break + } + partyFees := trckr.epochMakerFeesReceived[len(trckr.epochMakerFeesReceived)-i-1] + for party, fees := range partyFees { + if _, ok := m[party]; !ok { + m[party] = num.UintZero() + } + feesInQunatum, overflow := num.UintFromDecimal(fees.ToDecimal().Div(quantum)) + if overflow { + continue + } + m[party].AddSum(feesInQunatum) + total.AddSum(feesInQunatum) + } + } + } + } + if total.IsZero() { + return m, map[string]decimal.Decimal{} + } + totalFrac := num.DecimalZero() + fractions := []*types.PartyContributionScore{} + for p, f := range m { + frac := f.ToDecimal().Div(total.ToDecimal()) + fractions = append(fractions, &types.PartyContributionScore{Party: p, Score: frac}) + totalFrac = totalFrac.Add(frac) + } + capAtOne(fractions, totalFrac) + fracMap := make(map[string]num.Decimal, len(fractions)) + for _, partyFraction := range fractions { + fracMap[partyFraction.Party] = partyFraction.Score + } + return m, fracMap +} + +func capAtOne(partyFractions []*types.PartyContributionScore, total num.Decimal) { + if total.LessThanOrEqual(num.DecimalOne()) { + return + } + + sort.SliceStable(partyFractions, func(i, j int) bool { return partyFractions[i].Score.GreaterThan(partyFractions[j].Score) }) + delta := total.Sub(num.DecimalFromInt64(1)) + partyFractions[0].Score = num.MaxD(num.DecimalZero(), partyFractions[0].Score.Sub(delta)) +} + func (mt *marketTracker) calcFeesAtMilestone() { mt.epochMakerFeesReceived = append(mt.epochMakerFeesReceived, mt.makerFeesReceived) mt.epochMakerFeesPaid = append(mt.epochMakerFeesPaid, mt.makerFeesPaid) @@ -544,6 +611,8 @@ func (mt *marketTracker) clearFeeActivity() { mt.lpFees = map[string]*num.Uint{} mt.infraFees = map[string]*num.Uint{} mt.lpPaidFees = map[string]*num.Uint{} + mt.treasuryFeesPaid = map[string]*num.Uint{} + mt.buybackFeesPaid = map[string]*num.Uint{} mt.epochTotalMakerFeesReceived = append(mt.epochTotalMakerFeesReceived, mt.totalMakerFeesReceived) mt.epochTotalMakerFeesPaid = append(mt.epochTotalMakerFeesPaid, mt.totalMakerFeesPaid) @@ -573,6 +642,13 @@ func (mat *MarketActivityTracker) UpdateFeesFromTransfers(asset, market string, mat.addFees(mt.infraFees, t.Owner, t.Amount.Amount, num.UintZero()) case types.TransferTypeLiquidityFeePay: mat.addFees(mt.lpPaidFees, t.Owner, t.Amount.Amount, num.UintZero()) + case types.TransferTypeBuyBackFeePay: + mat.addFees(mt.buybackFeesPaid, t.Owner, t.Amount.Amount, num.UintZero()) + case types.TransferTypeTreasuryPay: + mat.addFees(mt.treasuryFeesPaid, t.Owner, t.Amount.Amount, num.UintZero()) + case types.TransferTypeHighMakerRebateReceive: + // we count high maker fee receive as maker fees for that purpose. + mat.addFees(mt.makerFeesReceived, t.Owner, t.Amount.Amount, mt.totalMakerFeesReceived) default: } } diff --git a/core/execution/common/market_activity_tracker_internal_test.go b/core/execution/common/market_activity_tracker_internal_test.go index 1e4c68d7e7a..cc60e413fc0 100644 --- a/core/execution/common/market_activity_tracker_internal_test.go +++ b/core/execution/common/market_activity_tracker_internal_test.go @@ -347,7 +347,8 @@ func TestCalculateMetricForIndividualsAvePosition(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -549,7 +550,8 @@ func TestCalculateMetricForPartyAvePosition(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -787,7 +789,8 @@ func TestCalculateMetricForIndividualReturnVolatility(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -993,7 +996,8 @@ func TestCalculateMetricForIndividualsRelativeReturn(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -1214,7 +1218,8 @@ func TestCalculateMetricForPartyRelativeReturn(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -1471,7 +1476,8 @@ func TestCalculateMetricForParty(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -1560,6 +1566,12 @@ func (e *DummyEpochEngine) NotifyOnEpoch(f func(context.Context, types.Epoch), _ e.target = f } +type DummyCollateralEngine struct{} + +func (e DummyCollateralEngine) GetAssetQuantum(asset string) (num.Decimal, error) { + return num.DecimalOne(), nil +} + type DummyEligibilityChecker struct{} func (e *DummyEligibilityChecker) IsEligibleForProposerBonus(marketID string, volumeTraded *num.Uint) bool { diff --git a/core/execution/common/market_activity_tracker_snapshot.go b/core/execution/common/market_activity_tracker_snapshot.go index 38e92fc88f8..4a95fc4f69c 100644 --- a/core/execution/common/market_activity_tracker_snapshot.go +++ b/core/execution/common/market_activity_tracker_snapshot.go @@ -295,6 +295,8 @@ func (mt *marketTracker) IntoProto(market string) *checkpoint.MarketActivityTrac LpFees: marketFeesToProto(mt.lpFees), InfraFees: marketFeesToProto(mt.infraFees), LpPaidFees: marketFeesToProto(mt.lpPaidFees), + BuyBackFees: marketFeesToProto(mt.buybackFeesPaid), + TreasuryFees: marketFeesToProto(mt.treasuryFeesPaid), Proposer: mt.proposer, BonusPaid: paid, ValueTraded: mt.valueTraded.String(), @@ -394,6 +396,8 @@ func marketTrackerFromProto(tracker *checkpoint.MarketActivityTracker) *marketTr makerFeesReceived: map[string]*num.Uint{}, makerFeesPaid: map[string]*num.Uint{}, lpFees: map[string]*num.Uint{}, + buybackFeesPaid: map[string]*num.Uint{}, + treasuryFeesPaid: map[string]*num.Uint{}, infraFees: map[string]*num.Uint{}, lpPaidFees: map[string]*num.Uint{}, totalMakerFeesReceived: num.UintZero(), @@ -467,6 +471,22 @@ func marketTrackerFromProto(tracker *checkpoint.MarketActivityTracker) *marketTr } } + if len(tracker.BuyBackFees) > 0 { + for _, mf := range tracker.BuyBackFees { + fee, _ := num.UintFromString(mf.Fee, 10) + mft.buybackFeesPaid[mf.Party] = fee + mft.allPartiesCache[mf.Party] = struct{}{} + } + } + + if len(tracker.TreasuryFees) > 0 { + for _, mf := range tracker.TreasuryFees { + fee, _ := num.UintFromString(mf.Fee, 10) + mft.treasuryFeesPaid[mf.Party] = fee + mft.allPartiesCache[mf.Party] = struct{}{} + } + } + if len(tracker.LpPaidFees) > 0 { for _, mf := range tracker.LpPaidFees { fee, _ := num.UintFromString(mf.Fee, 10) diff --git a/core/execution/common/market_activity_tracker_test.go b/core/execution/common/market_activity_tracker_test.go index 61b677ace76..082f3145599 100644 --- a/core/execution/common/market_activity_tracker_test.go +++ b/core/execution/common/market_activity_tracker_test.go @@ -62,7 +62,8 @@ func TestMarketTracker(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -140,7 +141,7 @@ func TestMarketTracker(t *testing.T) { teams2 := mocks.NewMockTeams(ctrl) balanceChecker2 := mocks.NewMockAccountBalanceChecker(ctrl) broker = bmocks.NewMockBroker(ctrl) - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams2, balanceChecker2, broker) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams2, balanceChecker2, broker, collateralService) pl := snapshotpb.Payload{} require.NoError(t, proto.Unmarshal(state1, &pl)) @@ -160,7 +161,8 @@ func TestRemoveMarket(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -187,7 +189,8 @@ func TestAddRemoveAMM(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -218,7 +221,8 @@ func TestGetScores(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -368,7 +372,8 @@ func TestGetScoresIndividualsDifferentScopes(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -530,7 +535,8 @@ func TestMarketTrackerStateChange(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) tracker.SetEligibilityChecker(&EligibilityChecker{}) state1, _, err := tracker.GetState(key) @@ -561,7 +567,8 @@ func TestFeesTrackerWith0(t *testing.T) { balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes() broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) @@ -595,7 +602,8 @@ func TestGetLastEpochTakeFees(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -659,7 +667,8 @@ func TestGetLastEpochTakeFeesMultiEpochWindow(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -749,7 +758,8 @@ func TestFeesTracker(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -851,7 +861,7 @@ func TestFeesTracker(t *testing.T) { balanceChecker = mocks.NewMockAccountBalanceChecker(ctrl) balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngineLoad.NotifyOnEpoch(trackerLoad.OnEpochEvent, trackerLoad.OnEpochRestore) pl := snapshotpb.Payload{} @@ -935,7 +945,8 @@ func TestSnapshot(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) pl := snapshotpb.Payload{} require.NoError(t, proto.Unmarshal(state1, &pl)) @@ -958,7 +969,8 @@ func TestCheckpoint(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) require.NoError(t, trackerLoad.Load(context.Background(), b)) @@ -1021,7 +1033,8 @@ func TestSnapshotRoundTripViaEngine(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker2 := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker2 := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) snapshotEngine2, err := snp.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain) require.NoError(t, err) defer snapshotEngine2.Close() @@ -1059,7 +1072,8 @@ func TestMarketProposerBonusScenarios(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -1192,7 +1206,8 @@ func TestPositionMetric(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() @@ -1301,7 +1316,8 @@ func TestRealisedReturnMetric(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() @@ -1405,7 +1421,8 @@ func TestRelativeReturnMetric(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() @@ -1494,7 +1511,8 @@ func TestTeamStatsForMarkets(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) asset1 := vgrand.RandomStr(5) asset2 := vgrand.RandomStr(5) @@ -1606,8 +1624,8 @@ func setupDefaultTrackerForTest(t *testing.T) *common.MarketActivityTracker { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() diff --git a/core/execution/common/mat_intermediate_scores_internal_test.go b/core/execution/common/mat_intermediate_scores_internal_test.go index a60ffc9f120..9f43a2649e4 100644 --- a/core/execution/common/mat_intermediate_scores_internal_test.go +++ b/core/execution/common/mat_intermediate_scores_internal_test.go @@ -47,7 +47,8 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -289,7 +290,7 @@ func TestPublishGameMetricReturnVolatility(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -537,7 +538,7 @@ func TestPublishGameMetricRelativeReturn(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) @@ -757,7 +758,7 @@ func TestPublishGameMetricRealisedReturn(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) @@ -879,7 +880,7 @@ func TestPublishGameMetricFees(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) diff --git a/core/execution/engine.go b/core/execution/engine.go index e1b1bf5a707..5431c1f5cd7 100644 --- a/core/execution/engine.go +++ b/core/execution/engine.go @@ -86,8 +86,10 @@ type Engine struct { assets common.Assets referralDiscountRewardService fee.ReferralDiscountRewardService volumeDiscountService fee.VolumeDiscountService - banking common.Banking - parties common.Parties + volumeRebateService fee.VolumeRebateService + + banking common.Banking + parties common.Parties broker common.Broker timeService common.TimeService @@ -137,6 +139,7 @@ func NewEngine( assets common.Assets, referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, parties common.Parties, delayTransactionsTarget common.DelayTransactionsTarget, @@ -165,9 +168,11 @@ func NewEngine( skipRestoreSuccessors: map[string]struct{}{}, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, - banking: banking, - parties: parties, - delayTransactionsTarget: delayTransactionsTarget, + volumeRebateService: volumeRebateService, + + banking: banking, + parties: parties, + delayTransactionsTarget: delayTransactionsTarget, } // set the eligibility for proposer bonus checker @@ -707,6 +712,7 @@ func (e *Engine) submitMarket(ctx context.Context, marketConfig *types.Market, o e.peggedOrderCountUpdated, e.referralDiscountRewardService, e.volumeDiscountService, + e.volumeRebateService, e.banking, e.parties, ) @@ -790,6 +796,7 @@ func (e *Engine) submitSpotMarket(ctx context.Context, marketConfig *types.Marke e.peggedOrderCountUpdated, e.referralDiscountRewardService, e.volumeDiscountService, + e.volumeRebateService, e.banking, ) if err != nil { diff --git a/core/execution/engine_netparams.go b/core/execution/engine_netparams.go index 85b3033d7ba..c0580f1c1e6 100644 --- a/core/execution/engine_netparams.go +++ b/core/execution/engine_netparams.go @@ -35,6 +35,8 @@ type netParamsValues struct { suppliedStakeToObligationFactor num.Decimal infrastructureFee num.Decimal makerFee num.Decimal + treasuryFee num.Decimal + buyBackFee num.Decimal scalingFactors *types.ScalingFactors maxLiquidityFee num.Decimal bondPenaltyFactor num.Decimal @@ -76,6 +78,8 @@ func defaultNetParamsValues() netParamsValues { suppliedStakeToObligationFactor: num.DecimalFromInt64(-1), infrastructureFee: num.DecimalFromInt64(-1), makerFee: num.DecimalFromInt64(-1), + buyBackFee: num.DecimalFromInt64(-1), + treasuryFee: num.DecimalFromInt64(-1), scalingFactors: nil, maxLiquidityFee: num.DecimalFromInt64(-1), bondPenaltyFactor: num.DecimalFromInt64(-1), @@ -329,6 +333,34 @@ func (e *Engine) OnMarketFeeFactorsMakerFeeUpdate(ctx context.Context, d num.Dec return nil } +func (e *Engine) OnMarketFeeFactorsTreasuryFeeUpdate(ctx context.Context, d num.Decimal) error { + if e.log.IsDebug() { + e.log.Debug("update treasury fee in market fee factors", + logging.Decimal("treasury-fee", d), + ) + } + + for _, mkt := range e.allMarketsCpy { + mkt.OnFeeFactorsTreasuryFeeUpdate(ctx, d) + } + e.npv.treasuryFee = d + return nil +} + +func (e *Engine) OnMarketFeeFactorsBuyBackFeeUpdate(ctx context.Context, d num.Decimal) error { + if e.log.IsDebug() { + e.log.Debug("update buy back fee in market fee factors", + logging.Decimal("buy-back-fee", d), + ) + } + + for _, mkt := range e.allMarketsCpy { + mkt.OnFeeFactorsBuyBackFeeUpdate(ctx, d) + } + e.npv.buyBackFee = d + return nil +} + func (e *Engine) OnMarketFeeFactorsInfrastructureFeeUpdate(ctx context.Context, d num.Decimal) error { if e.log.IsDebug() { e.log.Debug("update infrastructure fee in market fee factors", @@ -503,6 +535,14 @@ func (e *Engine) propagateSpotInitialNetParams(ctx context.Context, mkt *spot.Ma mkt.OnFeeFactorsMakerFeeUpdate(ctx, e.npv.makerFee) } + if !e.npv.buyBackFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsBuyBackFeeUpdate(ctx, e.npv.buyBackFee) + } + + if !e.npv.treasuryFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsTreasuryFeeUpdate(ctx, e.npv.treasuryFee) + } + if e.npv.marketValueWindowLength != -1 { mkt.OnMarketValueWindowLengthUpdate(e.npv.marketValueWindowLength) } @@ -567,6 +607,14 @@ func (e *Engine) propagateInitialNetParamsToFutureMarket(ctx context.Context, mk mkt.OnFeeFactorsMakerFeeUpdate(ctx, e.npv.makerFee) } + if !e.npv.buyBackFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsBuyBackFeeUpdate(ctx, e.npv.buyBackFee) + } + + if !e.npv.treasuryFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsTreasuryFeeUpdate(ctx, e.npv.treasuryFee) + } + if e.npv.scalingFactors != nil { if err := mkt.OnMarginScalingFactorsUpdate(ctx, e.npv.scalingFactors); err != nil { return err diff --git a/core/execution/engine_snapshot_test.go b/core/execution/engine_snapshot_test.go index 9453f95d09b..ed67607aa7b 100644 --- a/core/execution/engine_snapshot_test.go +++ b/core/execution/engine_snapshot_test.go @@ -90,17 +90,18 @@ func getMockedEngine(t *testing.T) *engineFake { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) delayTarget := mocks.NewMockDelayTransactionsTarget(ctrl) delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes() - mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker) - exec := execution.NewEngine(log, execConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, banking, parties, delayTarget) + mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker, collateralService) + exec := execution.NewEngine(log, execConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties, delayTarget) epochEngine.NotifyOnEpoch(mat.OnEpochEvent, mat.OnEpochRestore) return &engineFake{ Engine: exec, @@ -159,16 +160,17 @@ func createEngine(t *testing.T) (*execution.Engine, *gomock.Controller) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() - mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker) + mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker, collateralService) banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) delayTarget := mocks.NewMockDelayTransactionsTarget(ctrl) delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes() - e := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, banking, parties, delayTarget) + e := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties, delayTarget) epochEngine.NotifyOnEpoch(mat.OnEpochEvent, mat.OnEpochRestore) return e, ctrl } diff --git a/core/execution/future/market.go b/core/execution/future/market.go index 4bf2c65722a..820d5f131b2 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -96,6 +96,7 @@ type Market struct { fee *fee.Engine referralDiscountRewardService fee.ReferralDiscountRewardService volumeDiscountService fee.VolumeDiscountService + volumeRebateService fee.VolumeRebateService liquidity *common.MarketLiquidity liquidityEngine common.LiquidityEngine @@ -197,6 +198,7 @@ func NewMarket( peggedOrderNotify func(int64), referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, parties common.Parties, ) (*Market, error) { @@ -347,6 +349,7 @@ func NewMarket( perp: marketType == types.MarketTypePerp, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, + volumeRebateService: volumeRebateService, partyMarginFactor: map[string]num.Decimal{}, banking: banking, markPriceCalculator: common.NewCompositePriceCalculator(ctx, mkt.MarkPriceConfiguration, oracleEngine, timeService), @@ -2693,12 +2696,12 @@ func (m *Market) calcFees(trades []*types.Trade) (events.FeesTransfer, error) { ) if !m.as.InAuction() { - fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsMonitorAuction() { // we are in auction mode - fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsFBA() { - fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } if err != nil { diff --git a/core/execution/future/market_callbacks.go b/core/execution/future/market_callbacks.go index ee8e9d9e0ae..362c6571747 100644 --- a/core/execution/future/market_callbacks.go +++ b/core/execution/future/market_callbacks.go @@ -58,6 +58,18 @@ func (m *Market) OnFeeFactorsMakerFeeUpdate(ctx context.Context, d num.Decimal) m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) } +func (m *Market) OnFeeFactorsTreasuryFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsTreasuryFeeUpdate(d) + m.mkt.Fees.Factors.TreasuryFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + +func (m *Market) OnFeeFactorsBuyBackFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsBuyBackFeeUpdate(d) + m.mkt.Fees.Factors.BuyBackFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + func (m *Market) OnFeeFactorsInfrastructureFeeUpdate(ctx context.Context, d num.Decimal) { m.fee.OnFeeFactorsInfrastructureFeeUpdate(d) m.mkt.Fees.Factors.InfrastructureFee = d diff --git a/core/execution/future/market_snapshot_test.go b/core/execution/future/market_snapshot_test.go index 239a7008213..1ffb1e14178 100644 --- a/core/execution/future/market_snapshot_test.go +++ b/core/execution/future/market_snapshot_test.go @@ -242,8 +242,6 @@ func newMarketFromSnapshot(t *testing.T, ctx context.Context, ctrl *gomock.Contr teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) - epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) broker.EXPECT().Stage(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() @@ -251,11 +249,14 @@ func newMarketFromSnapshot(t *testing.T, ctx context.Context, ctrl *gomock.Contr timeService.EXPECT().GetTimeNow().AnyTimes() collateralEngine := collateral.New(log, collateral.NewDefaultConfig(), timeService, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) + epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) + positionConfig.StreamPositionVerbose = true referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) diff --git a/core/execution/future/market_test.go b/core/execution/future/market_test.go index 8e62e199c84..e243bf1a3f9 100644 --- a/core/execution/future/market_test.go +++ b/core/execution/future/market_test.go @@ -235,22 +235,24 @@ func (tm *testMarket) Run(ctx context.Context, mktCfg types.Market) *testMarket teams := mocks.NewMockTeams(tm.ctrl) bc := mocks.NewMockAccountBalanceChecker(tm.ctrl) broker := bmocks.NewMockBroker(tm.ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(tm.ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(tm.ctrl) + volumeRebate := fmocks.NewMockVolumeRebateService(tm.ctrl) referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeRebate.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() banking := mocks.NewMockBanking(tm.ctrl) parties := mocks.NewMockParties(tm.ctrl) mktEngine, err := future.NewMarket(ctx, tm.log, riskConfig, positionConfig, settlementConfig, matchingConfig, feeConfig, liquidityConfig, collateralEngine, oracleEngine, &mktCfg, tm.timeService, tm.broker, mas, statevarEngine, marketActivityTracker, cfgAsset, - peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, banking, parties, + peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties, ) require.NoError(tm.t, err) @@ -648,22 +650,25 @@ func getTestMarket2WithDP( epoch.EXPECT().NotifyOnEpoch(gomock.Any(), gomock.Any()).Times(1) teams := mocks.NewMockTeams(tm.ctrl) bc := mocks.NewMockAccountBalanceChecker(tm.ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epoch.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(tm.ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(tm.ctrl) + volumeRebate := fmocks.NewMockVolumeRebateService(tm.ctrl) + referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeRebate.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) mktEngine, err := future.NewMarket(context.Background(), log, riskConfig, positionConfig, settlementConfig, matchingConfig, feeConfig, liquidityConfig, collateralEngine, oracleEngine, mktCfg, timeService, broker, mas, statevar, marketActivityTracker, cfgAsset, - peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, banking, parties) + peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties) if err != nil { t.Fatalf("couldn't create a market: %v", err) } diff --git a/core/execution/snapshot_test.go b/core/execution/snapshot_test.go index 6bb7f899871..359475abe5b 100644 --- a/core/execution/snapshot_test.go +++ b/core/execution/snapshot_test.go @@ -610,7 +610,7 @@ func getEngine(t *testing.T, vegaPath paths.Paths, now time.Time) *snapshotTestD ctrl := gomock.NewController(t) teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) ethAsset := types.Asset{ @@ -624,9 +624,10 @@ func getEngine(t *testing.T, vegaPath paths.Paths, now time.Time) *snapshotTestD require.NoError(t, collateralEngine.EnableAsset(context.Background(), ethAsset)) referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) @@ -644,6 +645,7 @@ func getEngine(t *testing.T, vegaPath paths.Paths, now time.Time) *snapshotTestD stubs.NewAssetStub(), referralDiscountReward, volumeDiscount, + volumeRebate, banking, parties, delayTarget, @@ -680,7 +682,7 @@ func getEngineWithParties(t *testing.T, now time.Time, balance *num.Uint, partie ctrl := gomock.NewController(t) teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) ethAsset := types.Asset{ @@ -697,9 +699,11 @@ func getEngineWithParties(t *testing.T, now time.Time, balance *num.Uint, partie } referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) + + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) partiesMock := mocks.NewMockParties(ctrl) @@ -717,6 +721,7 @@ func getEngineWithParties(t *testing.T, now time.Time, balance *num.Uint, partie stubs.NewAssetStub(), referralDiscountReward, volumeDiscount, + volumeRebate, banking, partiesMock, delayTarget, diff --git a/core/execution/spot/market.go b/core/execution/spot/market.go index d44f2b922ab..63b63583eaa 100644 --- a/core/execution/spot/market.go +++ b/core/execution/spot/market.go @@ -82,6 +82,7 @@ type Market struct { fee *fee.Engine referralDiscountRewardService fee.ReferralDiscountRewardService volumeDiscountService fee.VolumeDiscountService + volumeRebateService fee.VolumeRebateService liquidity *common.MarketLiquidity liquidityEngine common.LiquidityEngine @@ -159,6 +160,7 @@ func NewMarket( peggedOrderNotify func(int64), referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, ) (*Market, error) { if len(mkt.ID) == 0 { @@ -232,6 +234,7 @@ func NewMarket( fee: feeEngine, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, + volumeRebateService: volumeRebateService, parties: map[string]struct{}{}, as: as, pMonitor: pMonitor, @@ -3170,15 +3173,15 @@ func (m *Market) calculateFeesForTrades(trades []*types.Trade) (events.FeesTrans err error ) if !m.as.InAuction() { - fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsMonitorAuction() { // we are in auction mode - fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsFBA() { - fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else { if !m.as.IsOpeningAuction() { - fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } } return fees, err diff --git a/core/execution/spot/market_callbacks.go b/core/execution/spot/market_callbacks.go index 77cc9fa13de..436bc46e2ad 100644 --- a/core/execution/spot/market_callbacks.go +++ b/core/execution/spot/market_callbacks.go @@ -46,6 +46,18 @@ func (m *Market) OnMarketProbabilityOfTradingTauScalingUpdate(_ context.Context, m.liquidity.OnProbabilityOfTradingTauScalingUpdate(d) } +func (m *Market) OnFeeFactorsTreasuryFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsTreasuryFeeUpdate(d) + m.mkt.Fees.Factors.TreasuryFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + +func (m *Market) OnFeeFactorsBuyBackFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsBuyBackFeeUpdate(d) + m.mkt.Fees.Factors.BuyBackFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + func (m *Market) OnFeeFactorsMakerFeeUpdate(ctx context.Context, d num.Decimal) { m.fee.OnFeeFactorsMakerFeeUpdate(d) m.mkt.Fees.Factors.MakerFee = d diff --git a/core/execution/spot/market_test.go b/core/execution/spot/market_test.go index c8b84aa9289..41d3393b039 100644 --- a/core/execution/spot/market_test.go +++ b/core/execution/spot/market_test.go @@ -210,7 +210,7 @@ func newTestMarket( teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).Times(1) - mat := common.NewMarketActivityTracker(log, teams, bc, broker) + mat := common.NewMarketActivityTracker(log, teams, bc, broker, collateral) epoch.NotifyOnEpoch(mat.OnEpochEvent, mat.OnEpochRestore) baseAsset := NewAssetStub(base, baseDP) @@ -218,13 +218,15 @@ func newTestMarket( referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(ctrl) + volumeRebate := fmocks.NewMockVolumeRebateService(ctrl) referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeRebate.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() banking := mocks.NewMockBanking(ctrl) - market, _ := spot.NewMarket(log, matching.NewDefaultConfig(), fee.NewDefaultConfig(), liquidity.NewDefaultConfig(), collateral, &mkt, ts, broker, as, statevarEngine, mat, baseAsset, quoteAsset, peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, banking) + market, _ := spot.NewMarket(log, matching.NewDefaultConfig(), fee.NewDefaultConfig(), liquidity.NewDefaultConfig(), collateral, &mkt, ts, broker, as, statevarEngine, mat, baseAsset, quoteAsset, peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking) tm := &testMarket{ market: market, diff --git a/core/fee/engine.go b/core/fee/engine.go index 516988a724e..9d0fff05fd0 100644 --- a/core/fee/engine.go +++ b/core/fee/engine.go @@ -31,15 +31,19 @@ var ( ErrInvalidFeeFactor = errors.New("fee factors must be positive") ) -//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/fee ReferralDiscountRewardService,VolumeDiscountService +//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/fee ReferralDiscountRewardService,VolumeDiscountService,VolumeRebateService type ReferralDiscountRewardService interface { - ReferralDiscountFactorForParty(party types.PartyID) num.Decimal - RewardsFactorMultiplierAppliedForParty(party types.PartyID) num.Decimal + ReferralDiscountFactorsForParty(party types.PartyID) types.Factors + RewardsFactorsMultiplierAppliedForParty(party types.PartyID) types.Factors GetReferrer(referee types.PartyID) (types.PartyID, error) } type VolumeDiscountService interface { - VolumeDiscountFactorForParty(party types.PartyID) num.Decimal + VolumeDiscountFactorForParty(party types.PartyID) types.Factors +} + +type VolumeRebateService interface { + VolumeRebateFactorForParty(party types.PartyID) num.Decimal } type Engine struct { @@ -58,6 +62,8 @@ type factors struct { makerFee num.Decimal infrastructureFee num.Decimal liquidityFee num.Decimal + treasuryFee num.Decimal + buyBackFee num.Decimal } func New( @@ -137,6 +143,8 @@ func (e *Engine) UpdateFeeFactors(fees types.Fees) error { if !fees.Factors.LiquidityFee.IsZero() && fees.Factors.LiquidityFee.IsPositive() { e.f.liquidityFee = fees.Factors.LiquidityFee } + e.f.treasuryFee = fees.Factors.TreasuryFee + e.f.buyBackFee = fees.Factors.BuyBackFee e.feeCfg = fees return nil @@ -155,6 +163,7 @@ func (e *Engine) CalculateForContinuousMode( trades []*types.Trade, referral ReferralDiscountRewardService, volumeDiscountService VolumeDiscountService, + volumeRebateService VolumeRebateService, ) (events.FeesTransfer, error) { if len(trades) <= 0 { return nil, ErrEmptyTrades @@ -179,7 +188,10 @@ func (e *Engine) CalculateForContinuousMode( taker = trade.Seller maker = trade.Buyer } - fee, reward := e.applyDiscountsAndRewards(taker, e.calculateContinuousModeFees(trade), referral, volumeDiscountService) + size := num.NewUint(trade.Size) + // multiply by size + tradeValueForFee := size.Mul(trade.Price, size).ToDecimal().Div(e.positionFactor) + fee, reward := e.applyDiscountsAndRewards(taker, maker, tradeValueForFee, e.calculateContinuousModeFees(trade), referral, volumeDiscountService, volumeRebateService) e.feesStats.RegisterMakerFee(maker, taker, fee.MakerFee) @@ -221,6 +233,47 @@ func (e *Engine) CalculateForContinuousMode( Type: types.TransferTypeMakerFeeReceive, }) + if !fee.HighVolumeMakerFee.IsZero() { + // create a transfer for the aggressor + transfers = append(transfers, &types.Transfer{ + Owner: taker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.HighVolumeMakerFee.Clone(), + }, + Type: types.TransferTypeHighMakerRebatePay, + }) + // create a transfer for the maker + transfersRecv = append(transfersRecv, &types.Transfer{ + Owner: maker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.HighVolumeMakerFee.Clone(), + }, + Type: types.TransferTypeHighMakerRebateReceive, + }) + } + + // create a transfer for the aggressor + transfers = append(transfers, &types.Transfer{ + Owner: taker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.BuyBackFee.Clone(), + }, + Type: types.TransferTypeBuyBackFeePay, + }) + + // create a transfer for the aggressor + transfers = append(transfers, &types.Transfer{ + Owner: taker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.TreasuryFee.Clone(), + }, + Type: types.TransferTypeTreasuryPay, + }) + if reward == nil { continue } @@ -284,6 +337,7 @@ func (e *Engine) CalculateForAuctionMode( trades []*types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, + volumeRebate VolumeRebateService, ) (events.FeesTransfer, error) { if len(trades) <= 0 { return nil, ErrEmptyTrades @@ -299,7 +353,7 @@ func (e *Engine) CalculateForAuctionMode( // for each trades both party needs to pay half of the fees // no maker fees are to be paid here. for _, v := range trades { - buyerFess, sellerFees, newTransfers := e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount) + buyerFess, sellerFees, newTransfers := e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount, volumeRebate) transfers = append(transfers, newTransfers...) // increase the total fee for the parties @@ -333,6 +387,7 @@ func (e *Engine) CalculateForFrequentBatchesAuctionMode( trades []*types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, + volumeRebate VolumeRebateService, ) (events.FeesTransfer, error) { if len(trades) <= 0 { return nil, ErrEmptyTrades @@ -357,7 +412,7 @@ func (e *Engine) CalculateForFrequentBatchesAuctionMode( ) // we are in the same auction, normal auction fees applies if v.BuyerAuctionBatch == v.SellerAuctionBatch { - v.BuyerFee, v.SellerFee, newTransfers = e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount) + v.BuyerFee, v.SellerFee, newTransfers = e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount, volumeRebate) sellerTotalFee = num.Sum(v.BuyerFee.InfrastructureFee, v.BuyerFee.LiquidityFee) buyerTotalFee = num.Sum(v.SellerFee.InfrastructureFee, v.SellerFee.LiquidityFee) } else { @@ -369,7 +424,7 @@ func (e *Engine) CalculateForFrequentBatchesAuctionMode( } // fees are being assign to the trade directly // no need to do add them there as well - ftrnsfr, _ := e.CalculateForContinuousMode([]*types.Trade{v}, referral, volumeDiscount) + ftrnsfr, _ := e.CalculateForContinuousMode([]*types.Trade{v}, referral, volumeDiscount, volumeRebate) newTransfers = ftrnsfr.Transfers() buyerTotalFee = ftrnsfr.TotalFeesAmountPerParty()[v.Buyer] sellerTotalFee = ftrnsfr.TotalFeesAmountPerParty()[v.Seller] @@ -510,6 +565,8 @@ func (e *Engine) getNetworkFeeWithMakerTransfer(fees *types.Fee, current *types. current.MakerFee.AddSum(fees.MakerFee) current.LiquidityFee.AddSum(fees.LiquidityFee) current.InfrastructureFee.AddSum(fees.InfrastructureFee) + current.BuyBackFee.AddSum(fees.BuyBackFee) + current.TreasuryFee.AddSum(fees.TreasuryFee) return current, transfer } @@ -542,21 +599,41 @@ func (e *Engine) getNetworkFeeTransfers(fees *types.Fee) ([]*types.Transfer, *nu MinAmount: num.UintZero(), Type: types.TransferTypeLiquidityFeePay, }, + { + Owner: types.NetworkParty, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fees.BuyBackFee.Clone(), + }, + MinAmount: num.UintZero(), + Type: types.TransferTypeBuyBackFeePay, + }, + { + Owner: types.NetworkParty, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fees.TreasuryFee.Clone(), + }, + MinAmount: num.UintZero(), + Type: types.TransferTypeTreasuryPay, + }, }, num.Sum(fees.MakerFee, fees.InfrastructureFee, fees.LiquidityFee) } -func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService) (*types.Fee, *types.ReferrerReward) { - referralDiscountFactor := referral.ReferralDiscountFactorForParty(types.PartyID(taker)) - volumeDiscountFactor := volumeDiscount.VolumeDiscountFactorForParty(types.PartyID(taker)) +func (e *Engine) applyDiscountsAndRewards(taker string, maker string, tradeValueForFeePurposes num.Decimal, fees *types.Fee, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, volumeRebate VolumeRebateService) (*types.Fee, *types.ReferrerReward) { + referralDiscountFactors := referral.ReferralDiscountFactorsForParty(types.PartyID(taker)) + volumeDiscountFactors := volumeDiscount.VolumeDiscountFactorForParty(types.PartyID(taker)) + highVolumeMakerFee := volumeRebate.VolumeRebateFactorForParty(types.PartyID(maker)).Mul(tradeValueForFeePurposes) + highVolumeMakerFeeI, _ := num.UintFromDecimal(highVolumeMakerFee) mf := fees.MakerFee.Clone() inf := fees.InfrastructureFee.Clone() lf := fees.LiquidityFee.Clone() // calculate referral discounts - referralMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(referralDiscountFactor).Floor()) - referralInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(referralDiscountFactor).Floor()) - referralLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(referralDiscountFactor).Floor()) + referralMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(referralDiscountFactors.Maker).Floor()) + referralInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(referralDiscountFactors.Infra).Floor()) + referralLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(referralDiscountFactors.Liquidity).Floor()) // apply referral discounts mf = mf.Sub(mf, referralMakerDiscount) @@ -564,9 +641,18 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra lf = lf.Sub(lf, referralLfDiscount) // calculate volume discounts - volumeMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(volumeDiscountFactor).Floor()) - volumeInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(volumeDiscountFactor).Floor()) - volumeLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(volumeDiscountFactor).Floor()) + volumeMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(volumeDiscountFactors.Maker).Floor()) + volumeInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(volumeDiscountFactors.Infra).Floor()) + volumeLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(volumeDiscountFactors.Liquidity).Floor()) + + var rebateDiscountFactor num.Decimal + bbAndTreasury := num.Sum(fees.BuyBackFee, fees.TreasuryFee).ToDecimal() + if !bbAndTreasury.IsZero() { + rebateDiscountFactor = num.DecimalOne().Sub(highVolumeMakerFee.Div(bbAndTreasury)) + } + + treasuryFee, _ := num.UintFromDecimal(fees.TreasuryFee.ToDecimal().Mul(rebateDiscountFactor)) + buyBackFee, _ := num.UintFromDecimal(fees.BuyBackFee.ToDecimal().Mul(rebateDiscountFactor)) // apply volume discounts mf = mf.Sub(mf, volumeMakerDiscount) @@ -574,9 +660,12 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra lf = lf.Sub(lf, volumeLfDiscount) f := &types.Fee{ + HighVolumeMakerFee: highVolumeMakerFeeI, MakerFee: mf, LiquidityFee: lf, InfrastructureFee: inf, + BuyBackFee: buyBackFee.Clone(), + TreasuryFee: treasuryFee.Clone(), MakerFeeVolumeDiscount: volumeMakerDiscount, InfrastructureFeeVolumeDiscount: volumeInfDiscount, LiquidityFeeVolumeDiscount: volumeLfDiscount, @@ -604,16 +693,16 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra ) // calculate rewards - factor := referral.RewardsFactorMultiplierAppliedForParty(types.PartyID(taker)) - if factor.IsZero() { + factors := referral.RewardsFactorsMultiplierAppliedForParty(types.PartyID(taker)) + if factors == types.EmptyFactors { return f, nil } referrerReward := types.NewReferrerReward() - referrerReward.MakerFeeReferrerReward, _ = num.UintFromDecimal(factor.Mul(mf.ToDecimal()).Floor()) - referrerReward.InfrastructureFeeReferrerReward, _ = num.UintFromDecimal(factor.Mul(inf.ToDecimal()).Floor()) - referrerReward.LiquidityFeeReferrerReward, _ = num.UintFromDecimal(factor.Mul(lf.ToDecimal()).Floor()) + referrerReward.MakerFeeReferrerReward, _ = num.UintFromDecimal(factors.Maker.Mul(mf.ToDecimal()).Floor()) + referrerReward.InfrastructureFeeReferrerReward, _ = num.UintFromDecimal(factors.Infra.Mul(inf.ToDecimal()).Floor()) + referrerReward.LiquidityFeeReferrerReward, _ = num.UintFromDecimal(factors.Liquidity.Mul(lf.ToDecimal()).Floor()) mf = mf.Sub(mf, referrerReward.MakerFeeReferrerReward) inf = inf.Sub(inf, referrerReward.InfrastructureFeeReferrerReward) @@ -640,10 +729,11 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra return f, referrerReward } -func (e *Engine) getAuctionModeFeesAndTransfers(t *types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService) (*types.Fee, *types.Fee, []*types.Transfer) { +func (e *Engine) getAuctionModeFeesAndTransfers(t *types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, volumeRebate VolumeRebateService) (*types.Fee, *types.Fee, []*types.Transfer) { fee := e.calculateAuctionModeFees(t) - buyerFeers, buyerReferrerRewards := e.applyDiscountsAndRewards(t.Buyer, fee, referral, volumeDiscount) - sellerFeers, sellerReferrerRewards := e.applyDiscountsAndRewards(t.Seller, fee, referral, volumeDiscount) + // in auction there is no maker so there is no rebate, so passing 0 as the trade value + buyerFeers, buyerReferrerRewards := e.applyDiscountsAndRewards(t.Buyer, t.Buyer, num.DecimalZero(), fee, referral, volumeDiscount, volumeRebate) + sellerFeers, sellerReferrerRewards := e.applyDiscountsAndRewards(t.Seller, t.Seller, num.DecimalZero(), fee, referral, volumeDiscount, volumeRebate) transfers := make([]*types.Transfer, 0, 12) transfers = append(transfers, @@ -677,10 +767,15 @@ func (e *Engine) calculateContinuousModeFees(trade *types.Trade) *types.Fee { mf, _ := num.UintFromDecimal(total.Mul(e.f.makerFee).Ceil()) inf, _ := num.UintFromDecimal(total.Mul(e.f.infrastructureFee).Ceil()) lf, _ := num.UintFromDecimal(total.Mul(e.f.liquidityFee).Ceil()) + bbf, _ := num.UintFromDecimal(total.Mul(e.f.buyBackFee).Ceil()) + tf, _ := num.UintFromDecimal(total.Mul(e.f.treasuryFee).Ceil()) return &types.Fee{ - MakerFee: mf, - InfrastructureFee: inf, - LiquidityFee: lf, + MakerFee: mf, + InfrastructureFee: inf, + LiquidityFee: lf, + BuyBackFee: bbf, + TreasuryFee: tf, + HighVolumeMakerFee: num.UintZero(), } } @@ -689,10 +784,14 @@ func (e *Engine) calculateAuctionModeFees(trade *types.Trade) *types.Fee { two := num.DecimalFromInt64(2) inf, _ := num.UintFromDecimal(fee.InfrastructureFee.ToDecimal().Div(two).Ceil()) lf, _ := num.UintFromDecimal(fee.LiquidityFee.ToDecimal().Div(two).Ceil()) + bbf, _ := num.UintFromDecimal(fee.BuyBackFee.ToDecimal().Div(two).Ceil()) + tf, _ := num.UintFromDecimal(fee.TreasuryFee.ToDecimal().Div(two).Ceil()) return &types.Fee{ MakerFee: num.UintZero(), InfrastructureFee: inf, LiquidityFee: lf, + TreasuryFee: tf, + BuyBackFee: bbf, } } @@ -760,6 +859,16 @@ func (e *Engine) OnFeeFactorsMakerFeeUpdate(f num.Decimal) { e.f.makerFee = f } +func (e *Engine) OnFeeFactorsBuyBackFeeUpdate(f num.Decimal) { + e.feeCfg.Factors.BuyBackFee = f + e.f.buyBackFee = f +} + +func (e *Engine) OnFeeFactorsTreasuryFeeUpdate(f num.Decimal) { + e.feeCfg.Factors.TreasuryFee = f + e.f.treasuryFee = f +} + func (e *Engine) OnFeeFactorsInfrastructureFeeUpdate(f num.Decimal) { e.feeCfg.Factors.InfrastructureFee = f e.f.infrastructureFee = f diff --git a/core/fee/engine_test.go b/core/fee/engine_test.go index 30c0889aceb..fb81695e54a 100644 --- a/core/fee/engine_test.go +++ b/core/fee/engine_test.go @@ -131,8 +131,10 @@ func testCalcContinuousTradingErrorEmptyTrade(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - _, err := eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService) + _, err := eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) } @@ -141,9 +143,11 @@ func testCalcContinuousTradingAndCheckAmounts(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() require.NoError(t, eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -162,7 +166,7 @@ func testCalcContinuousTradingAndCheckAmounts(t *testing.T) { }, } - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -249,9 +253,24 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.3)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalFromFloat(0.2)).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.1)).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.2), + Maker: num.NewDecimalFromFloat(0.2), + Liquidity: num.NewDecimalFromFloat(0.2), + }).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return( + types.Factors{ + Infra: num.NewDecimalFromFloat(0.1), + Maker: num.NewDecimalFromFloat(0.1), + Liquidity: num.NewDecimalFromFloat(0.1), + }) discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party3"), nil).AnyTimes() require.NoError(t, eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -270,7 +289,7 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) { }, } - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -374,6 +393,8 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t *te discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -398,13 +419,29 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t *te aggressor = "party2" } - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.5)).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.25)).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + }).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.25), + Maker: num.NewDecimalFromFloat(0.25), + Liquidity: num.NewDecimalFromFloat(0.25), + }) discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party1")).Return(num.DecimalFromFloat(0.3)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.3)).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -466,6 +503,8 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultip discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -504,14 +543,30 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultip aggressor = "party2" } - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.5)).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.25)).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + }).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.25), + Maker: num.NewDecimalFromFloat(0.25), + Liquidity: num.NewDecimalFromFloat(0.25), + }).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("referrer"), nil).AnyTimes() discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(aggressor).Return(num.DecimalFromFloat(0.3)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalFromFloat(0.3)).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(aggressor).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -639,9 +694,11 @@ func testCalcContinuousTrading(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party1"), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ @@ -682,7 +739,7 @@ func testCalcContinuousTrading(t *testing.T) { }, } - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -721,7 +778,9 @@ func testCalcAuctionTradingErrorEmptyTrade(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - _, err := eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + _, err := eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) } @@ -730,9 +789,11 @@ func testCalcAuctionTrading(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -744,7 +805,7 @@ func testCalcAuctionTrading(t *testing.T) { }, } - ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -790,18 +851,32 @@ func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) num.Decimal { + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors { if p == types.PartyID("party1") { - return num.DecimalZero() + return types.EmptyFactors } else { - return num.NewDecimalFromFloat(0.5) + return types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + } } }).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) num.Decimal { + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors { if p == types.PartyID("party1") { - return num.NewDecimalFromFloat(0.2) + return types.Factors{ + Infra: num.NewDecimalFromFloat(0.2), + Maker: num.NewDecimalFromFloat(0.2), + Liquidity: num.NewDecimalFromFloat(0.2), + } } else { - return num.NewDecimalFromFloat(0.3) + return types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + } } }).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).DoAndReturn(func(p types.PartyID) (types.PartyID, error) { @@ -811,8 +886,12 @@ func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) { return types.PartyID(""), errors.New("No referrer") } }).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party1")).Return(num.DecimalFromFloat(0.5)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party2")).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.EmptyFactors).AnyTimes() trades := []*types.Trade{ { @@ -824,7 +903,7 @@ func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) { }, } - ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -898,7 +977,9 @@ func testCalcBatchAuctionTradingErrorEmptyTrade(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - _, err := eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + _, err := eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) } @@ -907,9 +988,11 @@ func testCalcBatchAuctionTradingSameBatch(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -923,7 +1006,7 @@ func testCalcBatchAuctionTradingSameBatch(t *testing.T) { }, } - ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -969,9 +1052,11 @@ func testCalcBatchAuctionTradingDifferentBatches(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -985,7 +1070,7 @@ func testCalcBatchAuctionTradingDifferentBatches(t *testing.T) { }, } - ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -1028,9 +1113,9 @@ func testCloseoutFees(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -1067,9 +1152,10 @@ func testCloseoutFees(t *testing.T) { assert.NotNil(t, fee) allTransfers := ft.Transfers() // first we have the network -> pay transfers, then 1 transfer per good party - assert.Equal(t, len(trades), len(allTransfers)-3) - goodPartyTransfers := allTransfers[3:] - networkTransfers := allTransfers[:3] + // two additional transfers for the buy back and treasury fees + assert.Equal(t, len(trades), len(allTransfers)-5) + goodPartyTransfers := allTransfers[5:] + networkTransfers := allTransfers[:5] numTrades := num.NewUint(uint64(len(trades))) // maker fee is 100 * 0.02 == 2 diff --git a/core/fee/mocks/mocks.go b/core/fee/mocks/mocks.go index 8b4f5a3a07f..b6cbae9005c 100644 --- a/core/fee/mocks/mocks.go +++ b/core/fee/mocks/mocks.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/vega/core/fee (interfaces: ReferralDiscountRewardService,VolumeDiscountService) +// Source: code.vegaprotocol.io/vega/core/fee (interfaces: ReferralDiscountRewardService,VolumeDiscountService,VolumeRebateService) // Package mocks is a generated GoMock package. package mocks @@ -50,32 +50,32 @@ func (mr *MockReferralDiscountRewardServiceMockRecorder) GetReferrer(arg0 interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReferrer", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).GetReferrer), arg0) } -// ReferralDiscountFactorForParty mocks base method. -func (m *MockReferralDiscountRewardService) ReferralDiscountFactorForParty(arg0 types.PartyID) decimal.Decimal { +// ReferralDiscountFactorsForParty mocks base method. +func (m *MockReferralDiscountRewardService) ReferralDiscountFactorsForParty(arg0 types.PartyID) types.Factors { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReferralDiscountFactorForParty", arg0) - ret0, _ := ret[0].(decimal.Decimal) + ret := m.ctrl.Call(m, "ReferralDiscountFactorsForParty", arg0) + ret0, _ := ret[0].(types.Factors) return ret0 } -// ReferralDiscountFactorForParty indicates an expected call of ReferralDiscountFactorForParty. -func (mr *MockReferralDiscountRewardServiceMockRecorder) ReferralDiscountFactorForParty(arg0 interface{}) *gomock.Call { +// ReferralDiscountFactorsForParty indicates an expected call of ReferralDiscountFactorsForParty. +func (mr *MockReferralDiscountRewardServiceMockRecorder) ReferralDiscountFactorsForParty(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReferralDiscountFactorForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).ReferralDiscountFactorForParty), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReferralDiscountFactorsForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).ReferralDiscountFactorsForParty), arg0) } -// RewardsFactorMultiplierAppliedForParty mocks base method. -func (m *MockReferralDiscountRewardService) RewardsFactorMultiplierAppliedForParty(arg0 types.PartyID) decimal.Decimal { +// RewardsFactorsMultiplierAppliedForParty mocks base method. +func (m *MockReferralDiscountRewardService) RewardsFactorsMultiplierAppliedForParty(arg0 types.PartyID) types.Factors { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RewardsFactorMultiplierAppliedForParty", arg0) - ret0, _ := ret[0].(decimal.Decimal) + ret := m.ctrl.Call(m, "RewardsFactorsMultiplierAppliedForParty", arg0) + ret0, _ := ret[0].(types.Factors) return ret0 } -// RewardsFactorMultiplierAppliedForParty indicates an expected call of RewardsFactorMultiplierAppliedForParty. -func (mr *MockReferralDiscountRewardServiceMockRecorder) RewardsFactorMultiplierAppliedForParty(arg0 interface{}) *gomock.Call { +// RewardsFactorsMultiplierAppliedForParty indicates an expected call of RewardsFactorsMultiplierAppliedForParty. +func (mr *MockReferralDiscountRewardServiceMockRecorder) RewardsFactorsMultiplierAppliedForParty(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RewardsFactorMultiplierAppliedForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).RewardsFactorMultiplierAppliedForParty), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RewardsFactorsMultiplierAppliedForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).RewardsFactorsMultiplierAppliedForParty), arg0) } // MockVolumeDiscountService is a mock of VolumeDiscountService interface. @@ -102,10 +102,10 @@ func (m *MockVolumeDiscountService) EXPECT() *MockVolumeDiscountServiceMockRecor } // VolumeDiscountFactorForParty mocks base method. -func (m *MockVolumeDiscountService) VolumeDiscountFactorForParty(arg0 types.PartyID) decimal.Decimal { +func (m *MockVolumeDiscountService) VolumeDiscountFactorForParty(arg0 types.PartyID) types.Factors { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "VolumeDiscountFactorForParty", arg0) - ret0, _ := ret[0].(decimal.Decimal) + ret0, _ := ret[0].(types.Factors) return ret0 } @@ -114,3 +114,40 @@ func (mr *MockVolumeDiscountServiceMockRecorder) VolumeDiscountFactorForParty(ar mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeDiscountFactorForParty", reflect.TypeOf((*MockVolumeDiscountService)(nil).VolumeDiscountFactorForParty), arg0) } + +// MockVolumeRebateService is a mock of VolumeRebateService interface. +type MockVolumeRebateService struct { + ctrl *gomock.Controller + recorder *MockVolumeRebateServiceMockRecorder +} + +// MockVolumeRebateServiceMockRecorder is the mock recorder for MockVolumeRebateService. +type MockVolumeRebateServiceMockRecorder struct { + mock *MockVolumeRebateService +} + +// NewMockVolumeRebateService creates a new mock instance. +func NewMockVolumeRebateService(ctrl *gomock.Controller) *MockVolumeRebateService { + mock := &MockVolumeRebateService{ctrl: ctrl} + mock.recorder = &MockVolumeRebateServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVolumeRebateService) EXPECT() *MockVolumeRebateServiceMockRecorder { + return m.recorder +} + +// VolumeRebateFactorForParty mocks base method. +func (m *MockVolumeRebateService) VolumeRebateFactorForParty(arg0 types.PartyID) decimal.Decimal { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VolumeRebateFactorForParty", arg0) + ret0, _ := ret[0].(decimal.Decimal) + return ret0 +} + +// VolumeRebateFactorForParty indicates an expected call of VolumeRebateFactorForParty. +func (mr *MockVolumeRebateServiceMockRecorder) VolumeRebateFactorForParty(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeRebateFactorForParty", reflect.TypeOf((*MockVolumeRebateService)(nil).VolumeRebateFactorForParty), arg0) +} diff --git a/core/governance/engine.go b/core/governance/engine.go index 87c8fc57446..ece08bc28aa 100644 --- a/core/governance/engine.go +++ b/core/governance/engine.go @@ -296,6 +296,8 @@ func (e *Engine) preEnactProposal(ctx context.Context, p *proposal) (te *ToEnact te.referralProgramChanges = updatedReferralProgramFromProposal(p) case types.ProposalTermsTypeUpdateVolumeDiscountProgram: te.volumeDiscountProgram = updatedVolumeDiscountProgramFromProposal(p) + case types.ProposalTermsTypeUpdateVolumeRebateProgram: + te.volumeRebateProgram = updatedVolumeRebateProgramFromProposal(p) } return //nolint:nakedret } @@ -741,6 +743,8 @@ func (e *Engine) getProposalParams(proposalTerm types.ProposalTerm) (*types.Prop return e.getReferralProgramNetworkParameters(), nil case types.ProposalTermsTypeUpdateVolumeDiscountProgram: return e.getVolumeDiscountProgramNetworkParameters(), nil + case types.ProposalTermsTypeUpdateVolumeRebateProgram: + return e.getVolumeRebateProgramNetworkParameters(), nil default: return nil, ErrUnsupportedProposalType } @@ -1091,6 +1095,8 @@ func (e *Engine) validateChange(terms *types.ProposalTerms) (types.ProposalError return validateUpdateReferralProgram(e.netp, terms.GetUpdateReferralProgram(), terms.EnactmentTimestamp) case types.ProposalTermsTypeUpdateVolumeDiscountProgram: return validateUpdateVolumeDiscountProgram(e.netp, terms.GetUpdateVolumeDiscountProgram()) + case types.ProposalTermsTypeUpdateVolumeRebateProgram: + return validateUpdateVolumeRebateProgram(e.netp, terms.GetUpdateVolumeRebateProgram()) default: return types.ProposalErrorUnspecified, nil } diff --git a/core/governance/engine_update_discount_volume_program_test.go b/core/governance/engine_update_discount_volume_program_test.go index e6c82993b93..b97ebfbd4a9 100644 --- a/core/governance/engine_update_discount_volume_program_test.go +++ b/core/governance/engine_update_discount_volume_program_test.go @@ -61,10 +61,18 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateSucceeds(t *testing.T) VolumeBenefitTiers: []*types.VolumeBenefitTier{ { MinimumRunningNotionalTakerVolume: num.NewUint(10000), - VolumeDiscountFactor: num.DecimalFromFloat(0.001), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumRunningNotionalTakerVolume: num.NewUint(20000), - VolumeDiscountFactor: num.DecimalFromFloat(0.005), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -108,10 +116,18 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateWithTooManyTiersFails(t VolumeBenefitTiers: []*types.VolumeBenefitTier{ { MinimumRunningNotionalTakerVolume: num.NewUint(10000), - VolumeDiscountFactor: num.DecimalFromFloat(0.001), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumRunningNotionalTakerVolume: num.NewUint(20000), - VolumeDiscountFactor: num.DecimalFromFloat(0.005), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -155,10 +171,18 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateWithTooHighDiscountFact VolumeBenefitTiers: []*types.VolumeBenefitTier{ { MinimumRunningNotionalTakerVolume: num.NewUint(10000), - VolumeDiscountFactor: num.DecimalFromFloat(0.001), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumRunningNotionalTakerVolume: num.NewUint(20000), - VolumeDiscountFactor: num.DecimalFromFloat(0.015), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, }, }, }) @@ -175,7 +199,7 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateWithTooHighDiscountFact // then require.EqualError(t, err, - "tier 2 defines a volume discount factor higher than the maximum allowed by the network parameter \"volumeDiscountProgram.maxVolumeDiscountFactor\": maximum is 0.01, but got 0.015", + "tier 2 defines a volume discount infrastructure factor higher than the maximum allowed by the network parameter \"volumeDiscountProgram.maxVolumeDiscountFactor\": maximum is 0.01, but got 0.015", ) require.Nil(t, toSubmit) } diff --git a/core/governance/engine_update_referral_program_test.go b/core/governance/engine_update_referral_program_test.go index d5550a37a20..4bf5f1fea1f 100644 --- a/core/governance/engine_update_referral_program_test.go +++ b/core/governance/engine_update_referral_program_test.go @@ -67,13 +67,29 @@ func testSubmittingProposalForReferralProgramUpdateSucceeds(t *testing.T) { { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.005), - ReferralDiscountFactor: num.DecimalFromFloat(0.005), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -121,13 +137,29 @@ func testSubmittingProposalForReferralProgramUpdateWithTooManyTiersFails(t *test { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.005), - ReferralDiscountFactor: num.DecimalFromFloat(0.005), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -175,13 +207,29 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighRewardFactorFails( { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.015), - ReferralDiscountFactor: num.DecimalFromFloat(0.005), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -198,7 +246,7 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighRewardFactorFails( // then require.EqualError(t, err, - "tier 2 defines a referral reward factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralRewardFactor\": maximum is 0.01, but got 0.015", + "tier 2 defines a referral reward infrastructure factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralRewardFactor\": maximum is 0.01, but got 0.015", ) require.Nil(t, toSubmit) } @@ -232,13 +280,29 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighDiscountFactorFail { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.010), - ReferralDiscountFactor: num.DecimalFromFloat(0.015), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.01), + Maker: num.DecimalFromFloat(0.01), + Liquidity: num.DecimalFromFloat(0.01), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, }, }, }) @@ -255,7 +319,7 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighDiscountFactorFail // then require.EqualError(t, err, - "tier 2 defines a referral discount factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralDiscountFactor\": maximum is 0.01, but got 0.015", + "tier 2 defines a referral discount infrastructure factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralDiscountFactor\": maximum is 0.01, but got 0.015", ) require.Nil(t, toSubmit) } @@ -291,13 +355,29 @@ func testSubmittingProposalForReferralProgramUpdateEndsBeforeEnactsFails(t *test { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.010), - ReferralDiscountFactor: num.DecimalFromFloat(0.015), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.01), + Maker: num.DecimalFromFloat(0.01), + Liquidity: num.DecimalFromFloat(0.01), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, }, }, }, diff --git a/core/governance/market.go b/core/governance/market.go index cbead25e66a..a173c484838 100644 --- a/core/governance/market.go +++ b/core/governance/market.go @@ -226,6 +226,9 @@ func buildMarketFromProposal( // get factors for the market makerFee, _ := netp.Get(netparams.MarketFeeFactorsMakerFee) infraFee, _ := netp.Get(netparams.MarketFeeFactorsInfrastructureFee) + buybackFee, _ := netp.Get(netparams.MarketFeeFactorsBuyBackFee) + treasuryFee, _ := netp.Get(netparams.MarketFeeFactorsTreasuryFee) + // get the margin scaling factors scalingFactors := proto.ScalingFactors{} _ = netp.GetJSONStruct(netparams.MarketMarginScalingFactors, &scalingFactors) @@ -250,6 +253,8 @@ func buildMarketFromProposal( } makerFeeDec, _ := num.DecimalFromString(makerFee) infraFeeDec, _ := num.DecimalFromString(infraFee) + buybackFeeDec, _ := num.DecimalFromString(buybackFee) + treasuryFeeDec, _ := num.DecimalFromString(treasuryFee) // assign here, we want to update this after assigning market variable marginCalc := &types.MarginCalculator{ ScalingFactors: types.ScalingFactorsFromProto(&scalingFactors), @@ -262,6 +267,8 @@ func buildMarketFromProposal( Factors: &types.FeeFactors{ MakerFee: makerFeeDec, InfrastructureFee: infraFeeDec, + TreasuryFee: treasuryFeeDec, + BuyBackFee: buybackFeeDec, }, LiquidityFeeSettings: definition.Changes.LiquidityFeeSettings, }, @@ -312,6 +319,8 @@ func buildSpotMarketFromProposal( // get factors for the market makerFee, _ := netp.Get(netparams.MarketFeeFactorsMakerFee) infraFee, _ := netp.Get(netparams.MarketFeeFactorsInfrastructureFee) + buybackFee, _ := netp.Get(netparams.MarketFeeFactorsBuyBackFee) + treasuryFee, _ := netp.Get(netparams.MarketFeeFactorsTreasuryFee) // get price monitoring parameters if definition.Changes.PriceMonitoringParameters == nil { pmParams := &proto.PriceMonitoringParameters{} @@ -332,6 +341,8 @@ func buildSpotMarketFromProposal( makerFeeDec, _ := num.DecimalFromString(makerFee) infraFeeDec, _ := num.DecimalFromString(infraFee) + buybackFeeDec, _ := num.DecimalFromString(buybackFee) + treasuryFeeDec, _ := num.DecimalFromString(treasuryFee) market := &types.Market{ ID: marketID, DecimalPlaces: definition.Changes.PriceDecimalPlaces, @@ -340,6 +351,8 @@ func buildSpotMarketFromProposal( Factors: &types.FeeFactors{ MakerFee: makerFeeDec, InfrastructureFee: infraFeeDec, + TreasuryFee: treasuryFeeDec, + BuyBackFee: buybackFeeDec, }, LiquidityFeeSettings: definition.Changes.LiquidityFeeSettings, }, diff --git a/core/governance/market_cp_restore_test.go b/core/governance/market_cp_restore_test.go index 15ed96971ce..8b1db22757a 100644 --- a/core/governance/market_cp_restore_test.go +++ b/core/governance/market_cp_restore_test.go @@ -201,19 +201,20 @@ func createExecutionEngine(t *testing.T, tm time.Time) (*execution.Engine, *gove asset, _ := assets.New(context.Background(), log, assets.NewDefaultConfig(), getNodeWallet().Ethereum, nil, nil, broker, primaryBridgeView, secondaryBridgeView, notary, false) teams := emocks.NewMockTeams(ctrl) bc := emocks.NewMockAccountBalanceChecker(ctrl) - marketTracker := common.NewMarketActivityTracker(log, teams, bc, broker) + marketTracker := common.NewMarketActivityTracker(log, teams, bc, broker, collateralService) epochEngine.NotifyOnEpoch(marketTracker.OnEpochEvent, marketTracker.OnEpochRestore) referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := fmocks.NewMockVolumeRebateService(ctrl) referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() execBanking := emocks.NewMockBanking(ctrl) parties := emocks.NewMockParties(ctrl) delayTarget := emocks.NewMockDelayTransactionsTarget(ctrl) delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes() - exec := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, marketTracker, asset, referralDiscountReward, volumeDiscount, execBanking, parties, delayTarget) + exec := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, marketTracker, asset, referralDiscountReward, volumeDiscount, volumeRebateService, execBanking, parties, delayTarget) accounts := mocks.NewMockStakingAccounts(ctrl) witness := mocks.NewMockWitness(ctrl) diff --git a/core/governance/netparams.go b/core/governance/netparams.go index 8075af2561a..2b5426496bb 100644 --- a/core/governance/netparams.go +++ b/core/governance/netparams.go @@ -139,6 +139,22 @@ func (e *Engine) getVolumeDiscountProgramNetworkParameters() *types.ProposalPara ) } +func (e *Engine) getVolumeRebateProgramNetworkParameters() *types.ProposalParameters { + return e.getProposalParametersFromNetParams( + netparams.GovernanceProposalVolumeRebateProgramMinClose, + netparams.GovernanceProposalVolumeRebateProgramMaxClose, + netparams.GovernanceProposalVolumeRebateProgramMinEnact, + netparams.GovernanceProposalVolumeRebateProgramMaxEnact, + netparams.GovernanceProposalVolumeRebateProgramRequiredParticipation, + netparams.GovernanceProposalVolumeRebateProgramRequiredMajority, + netparams.GovernanceProposalVolumeRebateProgramMinProposerBalance, + netparams.GovernanceProposalVolumeRebateProgramMinVoterBalance, + "0", + "0", + "0", + ) +} + func (e *Engine) getNewAssetProposalParameters() *types.ProposalParameters { return e.getProposalParametersFromNetParams( netparams.GovernanceProposalAssetMinClose, diff --git a/core/governance/proposal.go b/core/governance/proposal.go index 3ab67c7c7b1..fd2a1339afb 100644 --- a/core/governance/proposal.go +++ b/core/governance/proposal.go @@ -255,6 +255,7 @@ type ToEnact struct { msu *ToEnactMarketStateUpdate referralProgramChanges *types.ReferralProgram volumeDiscountProgram *types.VolumeDiscountProgram + volumeRebateProgram *types.VolumeRebateProgram } type ToEnactMarketStateUpdate struct{} @@ -278,6 +279,10 @@ func (t ToEnact) IsVolumeDiscountProgramUpdate() bool { return t.volumeDiscountProgram != nil } +func (t ToEnact) IsVolumeRebateProgramUpdate() bool { + return t.volumeRebateProgram != nil +} + func (t ToEnact) IsReferralProgramUpdate() bool { return t.referralProgramChanges != nil } @@ -363,6 +368,10 @@ func (t *ToEnact) VolumeDiscountProgramUpdate() *types.VolumeDiscountProgram { return t.volumeDiscountProgram } +func (t *ToEnact) VolumeRebateProgramUpdate() *types.VolumeRebateProgram { + return t.volumeRebateProgram +} + func (t *ToEnact) UpdateMarket() *types.Market { return t.updatedMarket } diff --git a/core/governance/referral_program.go b/core/governance/referral_program.go index 81812c72011..5685a5ed020 100644 --- a/core/governance/referral_program.go +++ b/core/governance/referral_program.go @@ -39,11 +39,23 @@ func validateUpdateReferralProgram(netp NetParams, p *types.UpdateReferralProgra maxRewardFactor, _ := netp.GetDecimal(netparams.ReferralProgramMaxReferralRewardFactor) maxDiscountFactor, _ := netp.GetDecimal(netparams.ReferralProgramMaxReferralDiscountFactor) for i, tier := range p.Changes.BenefitTiers { - if tier.ReferralRewardFactor.GreaterThan(maxRewardFactor) { - return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactor.String()) + if tier.ReferralRewardFactors.Infra.GreaterThan(maxRewardFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward infrastructure factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactors.Infra.String()) } - if tier.ReferralDiscountFactor.GreaterThan(maxDiscountFactor) { - return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactor.String()) + if tier.ReferralRewardFactors.Maker.GreaterThan(maxRewardFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward maker factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactors.Maker.String()) + } + if tier.ReferralRewardFactors.Liquidity.GreaterThan(maxRewardFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward liquidity factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactors.Liquidity.String()) + } + if tier.ReferralDiscountFactors.Infra.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount infrastructure factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactors.Infra.String()) + } + if tier.ReferralDiscountFactors.Maker.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount maker factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactors.Maker.String()) + } + if tier.ReferralDiscountFactors.Liquidity.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount liquidity factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactors.Liquidity.String()) } } return types.ProposalErrorUnspecified, nil diff --git a/core/governance/volume_discount_program.go b/core/governance/volume_discount_program.go index b7359bff4d9..1c2465d5555 100644 --- a/core/governance/volume_discount_program.go +++ b/core/governance/volume_discount_program.go @@ -30,8 +30,14 @@ func validateUpdateVolumeDiscountProgram(netp NetParams, p *types.UpdateVolumeDi maxDiscountFactor, _ := netp.GetDecimal(netparams.VolumeDiscountProgramMaxVolumeDiscountFactor) for i, tier := range p.Changes.VolumeBenefitTiers { - if tier.VolumeDiscountFactor.GreaterThan(maxDiscountFactor) { - return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactor.String()) + if tier.VolumeDiscountFactors.Infra.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount infrastructure factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactors.Infra.String()) + } + if tier.VolumeDiscountFactors.Maker.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount maker factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactors.Maker.String()) + } + if tier.VolumeDiscountFactors.Liquidity.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount liquidity factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactors.Liquidity.String()) } } return 0, nil diff --git a/core/governance/volume_rebate_program.go b/core/governance/volume_rebate_program.go new file mode 100644 index 00000000000..7760f45e090 --- /dev/null +++ b/core/governance/volume_rebate_program.go @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package governance + +import ( + "fmt" + + "code.vegaprotocol.io/vega/core/netparams" + "code.vegaprotocol.io/vega/core/types" +) + +func validateUpdateVolumeRebateProgram(netp NetParams, p *types.UpdateVolumeRebateProgram) (types.ProposalError, error) { + maxTiers, _ := netp.GetUint(netparams.VolumeRebateProgramMaxBenefitTiers) + if len(p.Changes.VolumeRebateBenefitTiers) > int(maxTiers.Uint64()) { + return types.ProposalErrorInvalidVolumeRebateProgram, fmt.Errorf("the number of tiers in the proposal is higher than the maximum allowed by the network parameter %q: maximum is %s, but got %d", netparams.VolumeRebateProgramMaxBenefitTiers, maxTiers.String(), len(p.Changes.VolumeRebateBenefitTiers)) + } + + treasuryFee, _ := netp.GetDecimal(netparams.MarketFeeFactorsTreasuryFee) + buybackFee, _ := netp.GetDecimal(netparams.MarketFeeFactorsBuyBackFee) + maxRebate := treasuryFee.Add(buybackFee) + + for i, tier := range p.Changes.VolumeRebateBenefitTiers { + if tier.AdditionalMakerRebate.GreaterThan(maxRebate) { + return types.ProposalErrorInvalidVolumeRebateProgram, fmt.Errorf("tier %d defines an additional rebate factor higher than the maximum allowed by the network parameters: maximum is (%s+%s), but got %s", i+1, buybackFee.String(), treasuryFee.String(), tier.AdditionalMakerRebate.String()) + } + } + return 0, nil +} + +func updatedVolumeRebateProgramFromProposal(p *proposal) *types.VolumeRebateProgram { + terms := p.Terms.GetUpdateVolumeRebateProgram() + + return &types.VolumeRebateProgram{ + ID: p.ID, + Version: terms.Changes.Version, + EndOfProgramTimestamp: terms.Changes.EndOfProgramTimestamp, + WindowLength: terms.Changes.WindowLength, + VolumeRebateBenefitTiers: terms.Changes.VolumeRebateBenefitTiers, + } +} diff --git a/core/integration/features/0084-VDPR-012.feature b/core/integration/features/0084-VDPR-012.feature index 2eebd9dcce1..a119e894ea6 100644 --- a/core/integration/features/0084-VDPR-012.feature +++ b/core/integration/features/0084-VDPR-012.feature @@ -25,10 +25,10 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 1000 | 0.001 | - | 2000 | 0.005 | - | 3000 | 0.010 | + | volume | infra factor | liquidity factor | maker factor | + | 1000 | 0.001 | 0.001 | 0.001 | + | 2000 | 0.005 | 0.005 | 0.005 | + | 3000 | 0.010 | 0.010 | 0.010 | And the volume discount program: | id | tiers | closing timestamp | window length | | id1 | VDP-01 | 0 | 4 | @@ -92,18 +92,18 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval And the market data for the market "ETH/MAR24" should be: | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs - And the party "party3" has the following discount factor "0.005" + And the party "party3" has the following discount infra factor "0.005" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | | party3 | ETH/MAR24 | buy | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | | party3 | ETH/MAR24 | sell | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs - And the party "party3" has the following discount factor "0.01" + And the party "party3" has the following discount infra factor "0.01" diff --git a/core/integration/features/0084-VDPR-013.feature b/core/integration/features/0084-VDPR-013.feature index e9e4ca4f88d..bfe487aae0a 100644 --- a/core/integration/features/0084-VDPR-013.feature +++ b/core/integration/features/0084-VDPR-013.feature @@ -26,10 +26,10 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 1000 | 0.01 | - | 2000 | 0.02 | - | 3000 | 0.03 | + | volume | infra factor | liquidity factor | maker factor | + | 1000 | 0.01 | 0.01 | 0.01 | + | 2000 | 0.02 | 0.02 | 0.02 | + | 3000 | 0.03 | 0.03 | 0.03 | And the volume discount program: | id | tiers | closing timestamp | window length | | id1 | VDP-01 | 0 | 4 | @@ -95,7 +95,7 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | And the party "party3" has the following taker notional "0" - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -103,7 +103,7 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs And the party "party3" has the following taker notional "2000" - And the party "party3" has the following discount factor "0.02" + And the party "party3" has the following discount infra factor "0.02" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -111,7 +111,7 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs And the party "party3" has the following taker notional "4000" - And the party "party3" has the following discount factor "0.03" + And the party "party3" has the following discount infra factor "0.03" # now that party3 has a discount, lets do a trade with fees # Volume discount rewards are correctly calculated and transferred for each taker fee component during continuous trading. (0029-FEES-027) @@ -134,7 +134,7 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig And the network moves ahead "1" epochs And the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH/MAR24" - And the party "party3" has the following discount factor "0.03" + And the party "party3" has the following discount infra factor "0.03" Given the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | diff --git a/core/integration/features/0084-VDPR-014.feature b/core/integration/features/0084-VDPR-014.feature index 08263740bbc..c2ce57515f6 100644 --- a/core/integration/features/0084-VDPR-014.feature +++ b/core/integration/features/0084-VDPR-014.feature @@ -23,10 +23,10 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 3000 | 0.01 | - | 4000 | 0.02 | - | 5000 | 0.03 | + | volume | infra factor | liquidity factor | maker factor | + | 3000 | 0.01 | 0.01 | 0.01 | + | 4000 | 0.02 | 0.02 | 0.02 | + | 5000 | 0.03 | 0.03 | 0.03 | And the volume discount program: | id | tiers | closing timestamp | window length | | id1 | VDP-01 | 0 | 4 | @@ -91,7 +91,7 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | And the party "party3" has the following taker notional "0" - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -104,4 +104,4 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ # The taker trades above are not enough for party3 to hit the first level of the discount tier # so we get a zero for the discount factor And the party "party3" has the following taker notional "2000" - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" diff --git a/core/integration/features/referrals/0083-RFPR-benefit_factors.feature b/core/integration/features/referrals/0083-RFPR-benefit_factors.feature index 5b9a65cb655..6dae3544f89 100644 --- a/core/integration/features/referrals/0083-RFPR-benefit_factors.feature +++ b/core/integration/features/referrals/0083-RFPR-benefit_factors.feature @@ -28,9 +28,9 @@ Feature: Setting and applying referee benefit factors # Initalise the referral program then move forwards an epoch to start the program Given the referral benefit tiers "rbt": - | minimum running notional taker volume | minimum epochs | referral reward factor | referral discount factor | - | 2000 | 2 | 0.02 | 0.02 | - | 3000 | 3 | 0.20 | 0.20 | + | minimum running notional taker volume | minimum epochs | referral reward infra factor | referral reward maker factor | referral reward liquidity factor | referral discount infra factor | referral discount maker factor | referral discount liquidity factor | + | 2000 | 2 | 0.02 | 0.02 | 0.02 | 0.02 | 0.02 | 0.02 | + | 3000 | 3 | 0.20 | 0.20 | 0.20 | 0.20 | 0.20 | 0.20 | And the referral staking tiers "rst": | minimum staked tokens | referral reward multiplier | | 1 | 1 | @@ -125,8 +125,8 @@ Feature: Setting and applying referee benefit factors | referee1 | ETH/USD.1.1 | sell | | 1000 | 1 | TYPE_LIMIT | TIF_GTC | When the network moves ahead