code.vegaprotocol.io/vega@v0.79.0/core/governance/engine_update_referral_program_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package governance_test
    17  
    18  import (
    19  	"testing"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/core/events"
    23  	"code.vegaprotocol.io/vega/core/netparams"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/num"
    26  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    27  	vgtest "code.vegaprotocol.io/vega/libs/test"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func TestProposalForUpdateReferralProgram(t *testing.T) {
    34  	t.Run("Submitting a proposal for referral program update succeeds", testSubmittingProposalForReferralProgramUpdateSucceeds)
    35  	t.Run("Submitting a proposal for referral program update with too many tiers fails", testSubmittingProposalForReferralProgramUpdateWithTooManyTiersFails)
    36  	t.Run("Submitting a proposal for referral program update with too high reward factor fails", testSubmittingProposalForReferralProgramUpdateWithTooHighRewardFactorFails)
    37  	t.Run("Submitting a proposal for referral program update with too high discount factor fails", testSubmittingProposalForReferralProgramUpdateWithTooHighDiscountFactorFails)
    38  	t.Run("Submitting a proposal for referral program that ends before it enacted fails", testSubmittingProposalForReferralProgramUpdateEndsBeforeEnactsFails)
    39  }
    40  
    41  func testSubmittingProposalForReferralProgramUpdateSucceeds(t *testing.T) {
    42  	now := time.Now()
    43  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64())
    44  	eng := getTestEngine(t, now)
    45  
    46  	// setup
    47  	eng.broker.EXPECT().Send(gomock.Any()).Times(3)
    48  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinClose, "48h")
    49  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinEnact, "48h")
    50  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinProposerBalance, "1000")
    51  
    52  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralTiers, "2")).Times(1)
    53  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralTiers, "2"))
    54  
    55  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010")).Times(1)
    56  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010"))
    57  
    58  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010")).Times(1)
    59  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010"))
    60  
    61  	// given
    62  	proposer := vgrand.RandomStr(5)
    63  	proposal := eng.newProposalForReferralProgramUpdate(proposer, now, &types.ReferralProgramChanges{
    64  		EndOfProgramTimestamp: now.Add(4 * 48 * time.Hour),
    65  		WindowLength:          15,
    66  		BenefitTiers: []*types.BenefitTier{
    67  			{
    68  				MinimumEpochs:                     num.NewUint(1),
    69  				MinimumRunningNotionalTakerVolume: num.NewUint(10000),
    70  				ReferralRewardFactors: types.Factors{
    71  					Infra:     num.DecimalFromFloat(0.001),
    72  					Maker:     num.DecimalFromFloat(0.001),
    73  					Liquidity: num.DecimalFromFloat(0.001),
    74  				},
    75  				ReferralDiscountFactors: types.Factors{
    76  					Infra:     num.DecimalFromFloat(0.001),
    77  					Maker:     num.DecimalFromFloat(0.001),
    78  					Liquidity: num.DecimalFromFloat(0.001),
    79  				},
    80  			}, {
    81  				MinimumEpochs:                     num.NewUint(7),
    82  				MinimumRunningNotionalTakerVolume: num.NewUint(20000),
    83  				ReferralRewardFactors: types.Factors{
    84  					Infra:     num.DecimalFromFloat(0.005),
    85  					Maker:     num.DecimalFromFloat(0.005),
    86  					Liquidity: num.DecimalFromFloat(0.005),
    87  				},
    88  				ReferralDiscountFactors: types.Factors{
    89  					Infra:     num.DecimalFromFloat(0.005),
    90  					Maker:     num.DecimalFromFloat(0.005),
    91  					Liquidity: num.DecimalFromFloat(0.005),
    92  				},
    93  			},
    94  		},
    95  	})
    96  
    97  	// setup
    98  	eng.ensureTokenBalanceForParty(t, proposer, 1000)
    99  
   100  	// expect
   101  	eng.expectOpenProposalEvent(t, proposer, proposal.ID)
   102  
   103  	// when
   104  	toSubmit, err := eng.submitProposal(t, proposal)
   105  
   106  	// then
   107  	require.NoError(t, err)
   108  	require.NotNil(t, toSubmit)
   109  }
   110  
   111  // testSubmittingProposalForReferralProgramUpdateWithTooManyTiersFails covers 0095-HVMR-002.
   112  func testSubmittingProposalForReferralProgramUpdateWithTooManyTiersFails(t *testing.T) {
   113  	now := time.Now()
   114  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64())
   115  	eng := getTestEngine(t, now)
   116  
   117  	// setup
   118  	eng.broker.EXPECT().Send(gomock.Any()).Times(3)
   119  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinClose, "48h")
   120  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinEnact, "48h")
   121  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinProposerBalance, "1000")
   122  
   123  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralTiers, "1")).Times(1)
   124  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralTiers, "1"))
   125  
   126  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010")).Times(1)
   127  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010"))
   128  
   129  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010")).Times(1)
   130  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010"))
   131  
   132  	// given
   133  	proposer := vgrand.RandomStr(5)
   134  	proposal := eng.newProposalForReferralProgramUpdate(proposer, now, &types.ReferralProgramChanges{
   135  		EndOfProgramTimestamp: now.Add(4 * 48 * time.Hour),
   136  		WindowLength:          15,
   137  		BenefitTiers: []*types.BenefitTier{
   138  			{
   139  				MinimumEpochs:                     num.NewUint(1),
   140  				MinimumRunningNotionalTakerVolume: num.NewUint(10000),
   141  				ReferralRewardFactors: types.Factors{
   142  					Infra:     num.DecimalFromFloat(0.001),
   143  					Maker:     num.DecimalFromFloat(0.001),
   144  					Liquidity: num.DecimalFromFloat(0.001),
   145  				},
   146  				ReferralDiscountFactors: types.Factors{
   147  					Infra:     num.DecimalFromFloat(0.001),
   148  					Maker:     num.DecimalFromFloat(0.001),
   149  					Liquidity: num.DecimalFromFloat(0.001),
   150  				},
   151  			}, {
   152  				MinimumEpochs:                     num.NewUint(7),
   153  				MinimumRunningNotionalTakerVolume: num.NewUint(20000),
   154  				ReferralRewardFactors: types.Factors{
   155  					Infra:     num.DecimalFromFloat(0.005),
   156  					Maker:     num.DecimalFromFloat(0.005),
   157  					Liquidity: num.DecimalFromFloat(0.005),
   158  				},
   159  				ReferralDiscountFactors: types.Factors{
   160  					Infra:     num.DecimalFromFloat(0.005),
   161  					Maker:     num.DecimalFromFloat(0.005),
   162  					Liquidity: num.DecimalFromFloat(0.005),
   163  				},
   164  			},
   165  		},
   166  	})
   167  
   168  	// setup
   169  	eng.ensureTokenBalanceForParty(t, proposer, 1000)
   170  
   171  	// expect
   172  	eng.expectRejectedProposalEvent(t, proposer, proposal.ID, types.ProposalErrorInvalidReferralProgram)
   173  
   174  	// when
   175  	toSubmit, err := eng.submitProposal(t, proposal)
   176  
   177  	// then
   178  	require.Error(t, err)
   179  	require.Nil(t, toSubmit)
   180  }
   181  
   182  func testSubmittingProposalForReferralProgramUpdateWithTooHighRewardFactorFails(t *testing.T) {
   183  	now := time.Now()
   184  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64())
   185  	eng := getTestEngine(t, now)
   186  
   187  	// setup
   188  	eng.broker.EXPECT().Send(gomock.Any()).Times(3)
   189  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinClose, "48h")
   190  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinEnact, "48h")
   191  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinProposerBalance, "1000")
   192  
   193  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralTiers, "2")).Times(1)
   194  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralTiers, "2"))
   195  
   196  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010")).Times(1)
   197  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010"))
   198  
   199  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010")).Times(1)
   200  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010"))
   201  
   202  	// given
   203  	proposer := vgrand.RandomStr(5)
   204  	proposal := eng.newProposalForReferralProgramUpdate(proposer, now, &types.ReferralProgramChanges{
   205  		EndOfProgramTimestamp: now.Add(4 * 48 * time.Hour),
   206  		WindowLength:          15,
   207  		BenefitTiers: []*types.BenefitTier{
   208  			{
   209  				MinimumEpochs:                     num.NewUint(1),
   210  				MinimumRunningNotionalTakerVolume: num.NewUint(10000),
   211  				ReferralRewardFactors: types.Factors{
   212  					Infra:     num.DecimalFromFloat(0.001),
   213  					Maker:     num.DecimalFromFloat(0.001),
   214  					Liquidity: num.DecimalFromFloat(0.001),
   215  				},
   216  				ReferralDiscountFactors: types.Factors{
   217  					Infra:     num.DecimalFromFloat(0.001),
   218  					Maker:     num.DecimalFromFloat(0.001),
   219  					Liquidity: num.DecimalFromFloat(0.001),
   220  				},
   221  			}, {
   222  				MinimumEpochs:                     num.NewUint(7),
   223  				MinimumRunningNotionalTakerVolume: num.NewUint(20000),
   224  				ReferralRewardFactors: types.Factors{
   225  					Infra:     num.DecimalFromFloat(0.015),
   226  					Maker:     num.DecimalFromFloat(0.015),
   227  					Liquidity: num.DecimalFromFloat(0.015),
   228  				},
   229  				ReferralDiscountFactors: types.Factors{
   230  					Infra:     num.DecimalFromFloat(0.005),
   231  					Maker:     num.DecimalFromFloat(0.005),
   232  					Liquidity: num.DecimalFromFloat(0.005),
   233  				},
   234  			},
   235  		},
   236  	})
   237  
   238  	// setup
   239  	eng.ensureTokenBalanceForParty(t, proposer, 1000)
   240  
   241  	// expect
   242  	eng.expectRejectedProposalEvent(t, proposer, proposal.ID, types.ProposalErrorInvalidReferralProgram)
   243  
   244  	// when
   245  	toSubmit, err := eng.submitProposal(t, proposal)
   246  
   247  	// then
   248  	require.EqualError(t,
   249  		err,
   250  		"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",
   251  	)
   252  	require.Nil(t, toSubmit)
   253  }
   254  
   255  func testSubmittingProposalForReferralProgramUpdateWithTooHighDiscountFactorFails(t *testing.T) {
   256  	now := time.Now()
   257  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64())
   258  	eng := getTestEngine(t, now)
   259  
   260  	// setup
   261  	eng.broker.EXPECT().Send(gomock.Any()).Times(3)
   262  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinClose, "48h")
   263  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinEnact, "48h")
   264  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinProposerBalance, "1000")
   265  
   266  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralTiers, "2")).Times(1)
   267  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralTiers, "2"))
   268  
   269  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010")).Times(1)
   270  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010"))
   271  
   272  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010")).Times(1)
   273  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010"))
   274  
   275  	// given
   276  	proposer := vgrand.RandomStr(5)
   277  	proposal := eng.newProposalForReferralProgramUpdate(proposer, now, &types.ReferralProgramChanges{
   278  		EndOfProgramTimestamp: now.Add(4 * 48 * time.Hour),
   279  		WindowLength:          15,
   280  		BenefitTiers: []*types.BenefitTier{
   281  			{
   282  				MinimumEpochs:                     num.NewUint(1),
   283  				MinimumRunningNotionalTakerVolume: num.NewUint(10000),
   284  				ReferralRewardFactors: types.Factors{
   285  					Infra:     num.DecimalFromFloat(0.001),
   286  					Maker:     num.DecimalFromFloat(0.001),
   287  					Liquidity: num.DecimalFromFloat(0.001),
   288  				},
   289  				ReferralDiscountFactors: types.Factors{
   290  					Infra:     num.DecimalFromFloat(0.001),
   291  					Maker:     num.DecimalFromFloat(0.001),
   292  					Liquidity: num.DecimalFromFloat(0.001),
   293  				},
   294  			}, {
   295  				MinimumEpochs:                     num.NewUint(7),
   296  				MinimumRunningNotionalTakerVolume: num.NewUint(20000),
   297  				ReferralRewardFactors: types.Factors{
   298  					Infra:     num.DecimalFromFloat(0.01),
   299  					Maker:     num.DecimalFromFloat(0.01),
   300  					Liquidity: num.DecimalFromFloat(0.01),
   301  				},
   302  				ReferralDiscountFactors: types.Factors{
   303  					Infra:     num.DecimalFromFloat(0.015),
   304  					Maker:     num.DecimalFromFloat(0.015),
   305  					Liquidity: num.DecimalFromFloat(0.015),
   306  				},
   307  			},
   308  		},
   309  	})
   310  
   311  	// setup
   312  	eng.ensureTokenBalanceForParty(t, proposer, 1000)
   313  
   314  	// expect
   315  	eng.expectRejectedProposalEvent(t, proposer, proposal.ID, types.ProposalErrorInvalidReferralProgram)
   316  
   317  	// when
   318  	toSubmit, err := eng.submitProposal(t, proposal)
   319  
   320  	// then
   321  	require.EqualError(t,
   322  		err,
   323  		"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",
   324  	)
   325  	require.Nil(t, toSubmit)
   326  }
   327  
   328  // testSubmittingProposalForReferralProgramUpdateEndsBeforeEnactsFails covers 0095-HVMR-001.
   329  func testSubmittingProposalForReferralProgramUpdateEndsBeforeEnactsFails(t *testing.T) {
   330  	now := time.Now()
   331  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64())
   332  	eng := getTestEngine(t, now)
   333  
   334  	// setup
   335  	eng.broker.EXPECT().Send(gomock.Any()).Times(3)
   336  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinClose, "48h")
   337  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinEnact, "48h")
   338  	eng.netp.Update(ctx, netparams.GovernanceProposalReferralProgramMinProposerBalance, "1000")
   339  
   340  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralTiers, "2")).Times(1)
   341  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralTiers, "2"))
   342  
   343  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010")).Times(1)
   344  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralRewardFactor, "0.010"))
   345  
   346  	eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010")).Times(1)
   347  	require.NoError(t, eng.netp.Update(ctx, netparams.ReferralProgramMaxReferralDiscountFactor, "0.010"))
   348  
   349  	// given
   350  	proposer := vgrand.RandomStr(5)
   351  	rp := &types.ProposalTermsUpdateReferralProgram{
   352  		UpdateReferralProgram: &types.UpdateReferralProgram{
   353  			Changes: &types.ReferralProgramChanges{
   354  				EndOfProgramTimestamp: time.Time{}, // we will set this later
   355  				WindowLength:          15,
   356  				BenefitTiers: []*types.BenefitTier{
   357  					{
   358  						MinimumEpochs:                     num.NewUint(1),
   359  						MinimumRunningNotionalTakerVolume: num.NewUint(10000),
   360  						ReferralRewardFactors: types.Factors{
   361  							Infra:     num.DecimalFromFloat(0.001),
   362  							Maker:     num.DecimalFromFloat(0.001),
   363  							Liquidity: num.DecimalFromFloat(0.001),
   364  						},
   365  						ReferralDiscountFactors: types.Factors{
   366  							Infra:     num.DecimalFromFloat(0.001),
   367  							Maker:     num.DecimalFromFloat(0.001),
   368  							Liquidity: num.DecimalFromFloat(0.001),
   369  						},
   370  					}, {
   371  						MinimumEpochs:                     num.NewUint(7),
   372  						MinimumRunningNotionalTakerVolume: num.NewUint(20000),
   373  						ReferralRewardFactors: types.Factors{
   374  							Infra:     num.DecimalFromFloat(0.01),
   375  							Maker:     num.DecimalFromFloat(0.01),
   376  							Liquidity: num.DecimalFromFloat(0.01),
   377  						},
   378  						ReferralDiscountFactors: types.Factors{
   379  							Infra:     num.DecimalFromFloat(0.015),
   380  							Maker:     num.DecimalFromFloat(0.015),
   381  							Liquidity: num.DecimalFromFloat(0.015),
   382  						},
   383  					},
   384  				},
   385  			},
   386  		},
   387  	}
   388  	proposal := eng.newProposalForReferralProgramUpdate(proposer, now, rp.UpdateReferralProgram.Changes)
   389  	rp.UpdateReferralProgram.Changes.EndOfProgramTimestamp = time.Unix(proposal.Terms.EnactmentTimestamp, 0).Add(-time.Second) // set to end before enacted
   390  	proposal.Terms.Change = rp
   391  
   392  	// setup
   393  	eng.ensureTokenBalanceForParty(t, proposer, 1000)
   394  
   395  	// expect
   396  	eng.expectRejectedProposalEvent(t, proposer, proposal.ID, types.ProposalErrorInvalidReferralProgram)
   397  
   398  	// when
   399  	toSubmit, err := eng.submitProposal(t, proposal)
   400  
   401  	// then
   402  	require.EqualError(t,
   403  		err,
   404  		"the proposal must be enacted before the referral program ends",
   405  	)
   406  	require.Nil(t, toSubmit)
   407  }