Skip to content

Commit

Permalink
Merge pull request #11578 from vegaprotocol/rebate_remove_invariant
Browse files Browse the repository at this point in the history
fix: Replace additional rebate validation with a cap
  • Loading branch information
jeremyletang authored Aug 14, 2024
2 parents dea2d94 + 414d453 commit 1e2ed42
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 68 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
- [11546](https://github.com/vegaprotocol/vega/issues/11546) - Add validation to market proposals metadata.
- [11562](https://github.com/vegaprotocol/vega/issues/11562) - Update average notional metric with mark price at the end of the epoch and when calculating live score.
- [11570](https://github.com/vegaprotocol/vega/issues/11570) - Include the required set of parties for evaluation for eligible entities reward.
- [11576](https://github.com/vegaprotocol/vega/issues/11576) - Replace additional rebate validation with a cap.


### 🐛 Fixes

Expand Down
48 changes: 0 additions & 48 deletions core/governance/engine_update_volume_rebate_program_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import (
func TestProposalForUpdateVolumeRebateProgram(t *testing.T) {
t.Run("Submitting a proposal for referral program update succeeds", testSubmittingProposalForVolumeRebateProgramUpdateSucceeds)
t.Run("Submitting a proposal for referral program update with too many tiers fails", testSubmittingProposalForVolumeRebateProgramUpdateWithTooManyTiersFails)
t.Run("Submitting a proposal for referral program update with too high rebate factor fails", testSubmittingProposalForVolumeRebateProgramUpdateWithTooHighRebateFactorFails)
}

func testSubmittingProposalForVolumeRebateProgramUpdateSucceeds(t *testing.T) {
Expand Down Expand Up @@ -123,50 +122,3 @@ func testSubmittingProposalForVolumeRebateProgramUpdateWithTooManyTiersFails(t *
require.Error(t, err)
require.Nil(t, toSubmit)
}

func testSubmittingProposalForVolumeRebateProgramUpdateWithTooHighRebateFactorFails(t *testing.T) {
now := time.Now()
ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64())
eng := getTestEngine(t, now)

// setup
eng.broker.EXPECT().Send(gomock.Any()).Times(3)
eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinClose, "48h")
eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinEnact, "48h")
eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinProposerBalance, "1000")

eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.VolumeRebateProgramMaxBenefitTiers, "2")).Times(1)
require.NoError(t, eng.netp.Update(ctx, netparams.VolumeRebateProgramMaxBenefitTiers, "2"))

// given
proposer := vgrand.RandomStr(5)
proposal := eng.newProposalForVolumeRebateProgramUpdate(proposer, now, &types.VolumeRebateProgramChanges{
EndOfProgramTimestamp: now.Add(4 * 48 * time.Hour),
WindowLength: 15,
VolumeRebateBenefitTiers: []*types.VolumeRebateBenefitTier{
{
MinimumPartyMakerVolumeFraction: num.DecimalFromFloat(0.1),
AdditionalMakerRebate: num.DecimalFromFloat(0.01),
}, {
MinimumPartyMakerVolumeFraction: num.DecimalFromFloat(0.2),
AdditionalMakerRebate: num.DecimalFromFloat(0.02),
},
},
})

// setup
eng.ensureTokenBalanceForParty(t, proposer, 1000)

// expect
eng.expectRejectedProposalEvent(t, proposer, proposal.ID, types.ProposalErrorInvalidVolumeRebateProgram)

// when
toSubmit, err := eng.submitProposal(t, proposal)

// then
require.EqualError(t,
err,
"tier 1 defines an additional rebate factor higher than the maximum allowed by the network parameters: maximum is (0.0001+0.0001), but got 0.01",
)
require.Nil(t, toSubmit)
}
10 changes: 0 additions & 10 deletions core/governance/volume_rebate_program.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,6 @@ func validateUpdateVolumeRebateProgram(netp NetParams, p *types.UpdateVolumeReba
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
}

Expand Down
8 changes: 8 additions & 0 deletions core/integration/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,14 @@ func (e *executionTestSetup) registerTimeServiceCallbacks() {

func (e *executionTestSetup) registerNetParamsCallbacks() error {
return e.netParams.Watch(
netparams.WatchParam{
Param: netparams.MarketFeeFactorsBuyBackFee,
Watcher: e.volumeRebateProgram.OnMarketFeeFactorsBuyBackFeeUpdate,
},
netparams.WatchParam{
Param: netparams.MarketFeeFactorsTreasuryFee,
Watcher: e.volumeRebateProgram.OnMarketFeeFactorsTreasuryFeeUpdate,
},
netparams.WatchParam{
Param: netparams.StakingAndDelegationRewardMinimumValidatorStake,
Watcher: e.topology.OnMinDelegationUpdated,
Expand Down
8 changes: 8 additions & 0 deletions core/protocol/all_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,14 @@ func (svcs *allServices) setupNetParameters(powWatchers []netparams.WatchParam)
Param: netparams.MarketFeeFactorsBuyBackFee,
Watcher: svcs.executionEngine.OnMarketFeeFactorsBuyBackFeeUpdate,
},
{
Param: netparams.MarketFeeFactorsTreasuryFee,
Watcher: svcs.volumeRebate.OnMarketFeeFactorsTreasuryFeeUpdate,
},
{
Param: netparams.MarketFeeFactorsBuyBackFee,
Watcher: svcs.volumeRebate.OnMarketFeeFactorsBuyBackFeeUpdate,
},
{
Param: netparams.MarketValueWindowLength,
Watcher: svcs.executionEngine.OnMarketValueWindowLengthUpdate,
Expand Down
28 changes: 25 additions & 3 deletions core/volumerebate/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ type Engine struct {
newProgram *types.VolumeRebateProgram
programHasEnded bool

factorsByParty map[types.PartyID]types.VolumeRebateStats
factorsByParty map[types.PartyID]types.VolumeRebateStats
buyBackFee num.Decimal
treasureFee num.Decimal
maxAdditionalRebate num.Decimal
}

func New(broker Broker, marketActivityTracker MarketActivityTracker) *Engine {
Expand Down Expand Up @@ -99,7 +102,10 @@ func (e *Engine) VolumeRebateFactorForParty(party types.PartyID) num.Decimal {
return num.DecimalZero()
}

return factors.RebateFactor
// this is needed here again because the factors are calculated at the end of the epoch and
// the fee factors may change during the epoch so to ensure the factor is capped at any time
// we apply the min again here
return e.effectiveAdditionalRebate(factors.RebateFactor)
}

func (e *Engine) MakerVolumeFractionForParty(party types.PartyID) num.Decimal {
Expand Down Expand Up @@ -198,7 +204,7 @@ func (e *Engine) computeFactorsByParty(ctx context.Context, epoch uint64) {
tier := e.currentProgram.VolumeRebateBenefitTiers[i]
if makerFraction.GreaterThanOrEqual(tier.MinimumPartyMakerVolumeFraction) {
e.factorsByParty[party] = types.VolumeRebateStats{
RebateFactor: tier.AdditionalMakerRebate,
RebateFactor: e.effectiveAdditionalRebate(tier.AdditionalMakerRebate),
}
evt.Stats = append(evt.Stats, &eventspb.PartyVolumeRebateStats{
PartyId: party.String(),
Expand All @@ -223,3 +229,19 @@ func (e *Engine) computeFactorsByParty(ctx context.Context, epoch uint64) {

e.broker.Send(events.NewVolumeRebateStatsUpdatedEvent(ctx, evt))
}

func (e *Engine) OnMarketFeeFactorsTreasuryFeeUpdate(ctx context.Context, d num.Decimal) error {
e.treasureFee = d
e.maxAdditionalRebate = e.treasureFee.Add(e.buyBackFee)
return nil
}

func (e *Engine) OnMarketFeeFactorsBuyBackFeeUpdate(ctx context.Context, d num.Decimal) error {
e.buyBackFee = d
e.maxAdditionalRebate = e.treasureFee.Add(e.buyBackFee)
return nil
}

func (e *Engine) effectiveAdditionalRebate(tierRebate num.Decimal) num.Decimal {
return num.MinD(e.maxAdditionalRebate, tierRebate)
}
24 changes: 17 additions & 7 deletions core/volumerebate/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ func TestRebateFactor(t *testing.T) {
broker := mocks.NewMockBroker(ctrl)
marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl)
engine := volumerebate.NewSnapshottedEngine(broker, marketActivityTracker)

engine.OnMarketFeeFactorsBuyBackFeeUpdate(context.Background(), num.NewDecimalFromFloat(0.5))
engine.OnMarketFeeFactorsTreasuryFeeUpdate(context.Background(), num.NewDecimalFromFloat(0.5))
currentTime := time.Now()

p1 := &types.VolumeRebateProgram{
Expand Down Expand Up @@ -198,6 +199,8 @@ func TestRebateFactor(t *testing.T) {
hashWithEpochNotionalsData, _, err := engine.GetState(key)
require.NoError(t, err)
loadedEngine := assertSnapshotMatches(t, key, hashWithEpochNotionalsData)
loadedEngine.OnMarketFeeFactorsBuyBackFeeUpdate(context.Background(), num.NewDecimalFromFloat(0.5))
loadedEngine.OnMarketFeeFactorsTreasuryFeeUpdate(context.Background(), num.NewDecimalFromFloat(0.5))

// party does not exist
require.Equal(t, num.DecimalZero(), engine.VolumeRebateFactorForParty("p8"))
Expand Down Expand Up @@ -253,6 +256,8 @@ func TestRebateFactorWithWindow(t *testing.T) {
broker := mocks.NewMockBroker(ctrl)
marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl)
engine := volumerebate.NewSnapshottedEngine(broker, marketActivityTracker)
engine.OnMarketFeeFactorsBuyBackFeeUpdate(context.Background(), num.DecimalFromFloat(0.5))
engine.OnMarketFeeFactorsTreasuryFeeUpdate(context.Background(), num.DecimalFromFloat(0.5))
currentTime := time.Now()

p1 := &types.VolumeRebateProgram{
Expand Down Expand Up @@ -318,6 +323,9 @@ func TestRebateFactorWithWindow(t *testing.T) {
// volume 5000
require.Equal(t, "1", engine.VolumeRebateFactorForParty("p7").String())

engine.OnMarketFeeFactorsBuyBackFeeUpdate(context.Background(), num.DecimalFromFloat(0.1))
engine.OnMarketFeeFactorsTreasuryFeeUpdate(context.Background(), num.DecimalFromFloat(0.2))

// running for another epoch
marketActivityTracker.EXPECT().CalculateTotalMakerContributionInQuantum(gomock.Any()).Return(map[string]*num.Uint{
"p8": num.NewUint(2000),
Expand All @@ -342,6 +350,8 @@ func TestRebateFactorWithWindow(t *testing.T) {
hashAfter2Epochs, _, err := engine.GetState(key)
require.NoError(t, err)
loadedEngine := assertSnapshotMatches(t, key, hashAfter2Epochs)
loadedEngine.OnMarketFeeFactorsBuyBackFeeUpdate(context.Background(), num.NewDecimalFromFloat(0.5))
loadedEngine.OnMarketFeeFactorsTreasuryFeeUpdate(context.Background(), num.NewDecimalFromFloat(0.5))

// fraction 0.2 => rebate 0.2
require.Equal(t, "0.2", engine.VolumeRebateFactorForParty("p8").String())
Expand All @@ -358,12 +368,12 @@ func TestRebateFactorWithWindow(t *testing.T) {
// nothing this time
require.Equal(t, "0", engine.VolumeRebateFactorForParty("p4").String())
require.Equal(t, "0", loadedEngine.VolumeRebateFactorForParty("p4").String())
// fraction 0.4 => rebate 0.5
require.Equal(t, "1", engine.VolumeRebateFactorForParty("p5").String())
require.Equal(t, "1", loadedEngine.VolumeRebateFactorForParty("p5").String())
// fraction 0.4 => rebate 0.5
require.Equal(t, "1", engine.VolumeRebateFactorForParty("p6").String())
require.Equal(t, "1", loadedEngine.VolumeRebateFactorForParty("p6").String())
// fraction 0.4 => rebate 1 => capped at 0.3
require.Equal(t, "0.3", engine.VolumeRebateFactorForParty("p5").String())
require.Equal(t, "0.3", loadedEngine.VolumeRebateFactorForParty("p5").String())
// fraction 0.4 => rebate 1 => capped at 0.3
require.Equal(t, "0.3", engine.VolumeRebateFactorForParty("p6").String())
require.Equal(t, "0.3", loadedEngine.VolumeRebateFactorForParty("p6").String())
// nothing this time
require.Equal(t, "0", engine.VolumeRebateFactorForParty("p7").String())
require.Equal(t, "0", loadedEngine.VolumeRebateFactorForParty("p7").String())
Expand Down

0 comments on commit 1e2ed42

Please sign in to comment.