code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/referral_programs_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 sqlstore_test
    17  
    18  import (
    19  	"testing"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/datanode/entities"
    23  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    24  	"code.vegaprotocol.io/vega/libs/ptr"
    25  	"code.vegaprotocol.io/vega/protos/vega"
    26  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    27  
    28  	"github.com/georgysavva/scany/pgxscan"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func setupReferralProgramTest(t *testing.T) (*sqlstore.Blocks, *sqlstore.ReferralPrograms) {
    34  	t.Helper()
    35  
    36  	bs := sqlstore.NewBlocks(connectionSource)
    37  	rs := sqlstore.NewReferralPrograms(connectionSource)
    38  
    39  	return bs, rs
    40  }
    41  
    42  func TestReferralPrograms_AddReferralProgram(t *testing.T) {
    43  	bs, rs := setupReferralProgramTest(t)
    44  	ctx := tempTransaction(t)
    45  
    46  	block := addTestBlock(t, ctx, bs)
    47  	block2 := addTestBlock(t, ctx, bs)
    48  
    49  	t.Run("AddReferralProgram should create a new referral program record", func(t *testing.T) {
    50  		endTime := block.VegaTime.Add(time.Hour)
    51  		endTime2 := block2.VegaTime.Add(time.Hour)
    52  
    53  		programs := []*eventspb.ReferralProgramStarted{
    54  			{
    55  				Program: &vega.ReferralProgram{
    56  					Version: 1,
    57  					Id:      GenerateID(),
    58  					BenefitTiers: []*vega.BenefitTier{
    59  						{
    60  							MinimumRunningNotionalTakerVolume: "1000",
    61  							MinimumEpochs:                     "10",
    62  							ReferralRewardFactor:              "0.0001",
    63  							ReferralDiscountFactor:            "0.0001",
    64  							ReferralRewardFactors: &vega.RewardFactors{
    65  								InfrastructureRewardFactor: "0.00002",
    66  								LiquidityRewardFactor:      "0.00004",
    67  								MakerRewardFactor:          "0.00004",
    68  							},
    69  							ReferralDiscountFactors: &vega.DiscountFactors{
    70  								InfrastructureDiscountFactor: "0.00002",
    71  								LiquidityDiscountFactor:      "0.00004",
    72  								MakerDiscountFactor:          "0.00004",
    73  							},
    74  							TierNumber: ptr.From(uint64(1)),
    75  						},
    76  						{
    77  							MinimumRunningNotionalTakerVolume: "10000",
    78  							MinimumEpochs:                     "100",
    79  							ReferralRewardFactor:              "0.001",
    80  							ReferralDiscountFactor:            "0.001",
    81  							ReferralRewardFactors: &vega.RewardFactors{
    82  								InfrastructureRewardFactor: "0.0002",
    83  								LiquidityRewardFactor:      "0.0004",
    84  								MakerRewardFactor:          "0.0004",
    85  							},
    86  							ReferralDiscountFactors: &vega.DiscountFactors{
    87  								InfrastructureDiscountFactor: "0.0002",
    88  								LiquidityDiscountFactor:      "0.0004",
    89  								MakerDiscountFactor:          "0.0004",
    90  							},
    91  							TierNumber: ptr.From(uint64(2)),
    92  						},
    93  					},
    94  					EndOfProgramTimestamp: endTime.Unix(),
    95  					WindowLength:          100,
    96  					StakingTiers: []*vega.StakingTier{
    97  						{
    98  							MinimumStakedTokens:      "1000",
    99  							ReferralRewardMultiplier: "1.0",
   100  						},
   101  						{
   102  							MinimumStakedTokens:      "10000",
   103  							ReferralRewardMultiplier: "1.1",
   104  						},
   105  					},
   106  				},
   107  			},
   108  			{
   109  				Program: &vega.ReferralProgram{
   110  					Version: 1,
   111  					Id:      GenerateID(),
   112  					BenefitTiers: []*vega.BenefitTier{
   113  						{
   114  							MinimumRunningNotionalTakerVolume: "2000",
   115  							MinimumEpochs:                     "20",
   116  							ReferralRewardFactor:              "0.0002",
   117  							ReferralDiscountFactor:            "0.0002",
   118  							ReferralRewardFactors: &vega.RewardFactors{
   119  								InfrastructureRewardFactor: "0.00004",
   120  								LiquidityRewardFactor:      "0.00008",
   121  								MakerRewardFactor:          "0.00008",
   122  							},
   123  							ReferralDiscountFactors: &vega.DiscountFactors{
   124  								InfrastructureDiscountFactor: "0.00004",
   125  								LiquidityDiscountFactor:      "0.00008",
   126  								MakerDiscountFactor:          "0.00008",
   127  							},
   128  							TierNumber: ptr.From(uint64(1)),
   129  						},
   130  						{
   131  							MinimumRunningNotionalTakerVolume: "20000",
   132  							MinimumEpochs:                     "200",
   133  							ReferralRewardFactor:              "0.002",
   134  							ReferralDiscountFactor:            "0.002",
   135  							ReferralRewardFactors: &vega.RewardFactors{
   136  								InfrastructureRewardFactor: "0.0004",
   137  								LiquidityRewardFactor:      "0.0008",
   138  								MakerRewardFactor:          "0.0008",
   139  							},
   140  							ReferralDiscountFactors: &vega.DiscountFactors{
   141  								InfrastructureDiscountFactor: "0.0004",
   142  								LiquidityDiscountFactor:      "0.0008",
   143  								MakerDiscountFactor:          "0.0008",
   144  							},
   145  							TierNumber: ptr.From(uint64(2)),
   146  						},
   147  					},
   148  					EndOfProgramTimestamp: endTime2.Unix(),
   149  					WindowLength:          200,
   150  					StakingTiers: []*vega.StakingTier{
   151  						{
   152  							MinimumStakedTokens:      "1000",
   153  							ReferralRewardMultiplier: "1.0",
   154  						},
   155  						{
   156  							MinimumStakedTokens:      "10000",
   157  							ReferralRewardMultiplier: "1.1",
   158  						},
   159  					},
   160  				},
   161  			},
   162  		}
   163  
   164  		want := entities.ReferralProgramFromProto(programs[0].Program, block.VegaTime, 0)
   165  		err := rs.AddReferralProgram(ctx, want)
   166  		require.NoError(t, err)
   167  
   168  		var got []entities.ReferralProgram
   169  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM referral_programs")
   170  		require.NoError(t, err)
   171  		require.Len(t, got, 1)
   172  		assert.Equal(t, *want, got[0])
   173  
   174  		want2 := entities.ReferralProgramFromProto(programs[1].Program, block2.VegaTime, 0)
   175  		err = rs.AddReferralProgram(ctx, want2)
   176  		require.NoError(t, err)
   177  
   178  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM referral_programs")
   179  		require.NoError(t, err)
   180  		require.Len(t, got, 2)
   181  		wantAll := []entities.ReferralProgram{*want, *want2}
   182  		assert.Equal(t, wantAll, got)
   183  	})
   184  }
   185  
   186  func getReferralEvents(t *testing.T, endTime time.Time) (*eventspb.ReferralProgramStarted,
   187  	*eventspb.ReferralProgramUpdated, *eventspb.ReferralProgramEnded,
   188  ) {
   189  	t.Helper()
   190  
   191  	started := eventspb.ReferralProgramStarted{
   192  		Program: &vega.ReferralProgram{
   193  			Version: 1,
   194  			Id:      GenerateID(),
   195  			BenefitTiers: []*vega.BenefitTier{
   196  				{
   197  					MinimumRunningNotionalTakerVolume: "1000",
   198  					MinimumEpochs:                     "10",
   199  					ReferralRewardFactor:              "0.0001",
   200  					ReferralDiscountFactor:            "0.0001",
   201  					ReferralRewardFactors: &vega.RewardFactors{
   202  						InfrastructureRewardFactor: "0.00002",
   203  						LiquidityRewardFactor:      "0.00004",
   204  						MakerRewardFactor:          "0.00004",
   205  					},
   206  					ReferralDiscountFactors: &vega.DiscountFactors{
   207  						InfrastructureDiscountFactor: "0.00002",
   208  						LiquidityDiscountFactor:      "0.00004",
   209  						MakerDiscountFactor:          "0.00004",
   210  					},
   211  					TierNumber: ptr.From(uint64(1)),
   212  				},
   213  				{
   214  					MinimumRunningNotionalTakerVolume: "10000",
   215  					MinimumEpochs:                     "100",
   216  					ReferralRewardFactor:              "0.001",
   217  					ReferralDiscountFactor:            "0.001",
   218  					ReferralRewardFactors: &vega.RewardFactors{
   219  						InfrastructureRewardFactor: "0.0002",
   220  						LiquidityRewardFactor:      "0.0004",
   221  						MakerRewardFactor:          "0.0004",
   222  					},
   223  					ReferralDiscountFactors: &vega.DiscountFactors{
   224  						InfrastructureDiscountFactor: "0.0002",
   225  						LiquidityDiscountFactor:      "0.0004",
   226  						MakerDiscountFactor:          "0.0004",
   227  					},
   228  					TierNumber: ptr.From(uint64(2)),
   229  				},
   230  			},
   231  			EndOfProgramTimestamp: endTime.Unix(),
   232  			WindowLength:          100,
   233  			StakingTiers: []*vega.StakingTier{
   234  				{
   235  					MinimumStakedTokens:      "1000",
   236  					ReferralRewardMultiplier: "1.0",
   237  				},
   238  				{
   239  					MinimumStakedTokens:      "10000",
   240  					ReferralRewardMultiplier: "1.1",
   241  				},
   242  			},
   243  		},
   244  		StartedAt: endTime.Add(-1 * time.Hour).UnixNano(),
   245  		AtEpoch:   40,
   246  	}
   247  
   248  	updated := eventspb.ReferralProgramUpdated{
   249  		Program: &vega.ReferralProgram{
   250  			Version: 2,
   251  			Id:      started.Program.Id,
   252  			BenefitTiers: []*vega.BenefitTier{
   253  				{
   254  					MinimumRunningNotionalTakerVolume: "2000",
   255  					MinimumEpochs:                     "20",
   256  					ReferralRewardFactor:              "0.0002",
   257  					ReferralDiscountFactor:            "0.0002",
   258  					ReferralRewardFactors: &vega.RewardFactors{
   259  						InfrastructureRewardFactor: "0.0004",
   260  						LiquidityRewardFactor:      "0.0008",
   261  						MakerRewardFactor:          "0.0008",
   262  					},
   263  					ReferralDiscountFactors: &vega.DiscountFactors{
   264  						InfrastructureDiscountFactor: "0.0004",
   265  						LiquidityDiscountFactor:      "0.0008",
   266  						MakerDiscountFactor:          "0.0008",
   267  					},
   268  					TierNumber: ptr.From(uint64(1)),
   269  				},
   270  				{
   271  					MinimumRunningNotionalTakerVolume: "20000",
   272  					MinimumEpochs:                     "200",
   273  					ReferralRewardFactor:              "0.002",
   274  					ReferralDiscountFactor:            "0.002",
   275  					ReferralRewardFactors: &vega.RewardFactors{
   276  						InfrastructureRewardFactor: "0.004",
   277  						LiquidityRewardFactor:      "0.008",
   278  						MakerRewardFactor:          "0.008",
   279  					},
   280  					ReferralDiscountFactors: &vega.DiscountFactors{
   281  						InfrastructureDiscountFactor: "0.004",
   282  						LiquidityDiscountFactor:      "0.008",
   283  						MakerDiscountFactor:          "0.008",
   284  					},
   285  					TierNumber: ptr.From(uint64(2)),
   286  				},
   287  			},
   288  			EndOfProgramTimestamp: endTime.Unix(),
   289  			WindowLength:          200,
   290  			StakingTiers: []*vega.StakingTier{
   291  				{
   292  					MinimumStakedTokens:      "1000",
   293  					ReferralRewardMultiplier: "1.0",
   294  				},
   295  				{
   296  					MinimumStakedTokens:      "10000",
   297  					ReferralRewardMultiplier: "1.1",
   298  				},
   299  			},
   300  		},
   301  		UpdatedAt: endTime.Add(-30 * time.Minute).UnixNano(),
   302  		AtEpoch:   41,
   303  	}
   304  
   305  	ended := eventspb.ReferralProgramEnded{
   306  		Version: 2,
   307  		Id:      started.Program.Id,
   308  		EndedAt: endTime.UnixNano(),
   309  		AtEpoch: 42,
   310  	}
   311  
   312  	return &started, &updated, &ended
   313  }
   314  
   315  func TestReferralPrograms_UpdateReferralProgram(t *testing.T) {
   316  	bs, rs := setupReferralProgramTest(t)
   317  	ctx := tempTransaction(t)
   318  
   319  	block := addTestBlock(t, ctx, bs)
   320  	endTime := block.VegaTime.Add(time.Hour)
   321  	started, updated, _ := getReferralEvents(t, endTime)
   322  
   323  	var want, wantUpdated *entities.ReferralProgram
   324  	t.Run("UpdateReferralProgram should create a new referral program record with the updated data", func(t *testing.T) {
   325  		want = entities.ReferralProgramFromProto(started.Program, block.VegaTime, 0)
   326  		err := rs.AddReferralProgram(ctx, want)
   327  		require.NoError(t, err)
   328  
   329  		var got []entities.ReferralProgram
   330  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM referral_programs")
   331  		require.NoError(t, err)
   332  
   333  		require.Len(t, got, 1)
   334  		assert.Equal(t, *want, got[0])
   335  
   336  		block = addTestBlock(t, ctx, bs)
   337  		wantUpdated = entities.ReferralProgramFromProto(updated.Program, block.VegaTime, 0)
   338  		err = rs.UpdateReferralProgram(ctx, wantUpdated)
   339  		require.NoError(t, err)
   340  
   341  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM referral_programs")
   342  		require.NoError(t, err)
   343  
   344  		require.Len(t, got, 2)
   345  
   346  		wantAll := []entities.ReferralProgram{*want, *wantUpdated}
   347  		assert.Equal(t, wantAll, got)
   348  	})
   349  
   350  	t.Run("The current_referral view should list the updated referral program record", func(t *testing.T) {
   351  		var got []entities.ReferralProgram
   352  		err := pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM current_referral_program")
   353  		require.NoError(t, err)
   354  		require.Len(t, got, 1)
   355  		assert.Equal(t, *wantUpdated, got[0])
   356  	})
   357  }
   358  
   359  func TestReferralPrograms_EndReferralProgram(t *testing.T) {
   360  	bs, rs := setupReferralProgramTest(t)
   361  	ctx := tempTransaction(t)
   362  
   363  	t.Run("EndReferralProgram should create a new referral program record with the data from the current referral program and set the ended_at timestamp", func(t *testing.T) {
   364  		block := addTestBlock(t, ctx, bs)
   365  		endTime := block.VegaTime.Add(time.Hour)
   366  		startedEvent, updatedEvent, endedEvent := getReferralEvents(t, endTime)
   367  
   368  		started := entities.ReferralProgramFromProto(startedEvent.Program, block.VegaTime, 1)
   369  		err := rs.AddReferralProgram(ctx, started)
   370  		require.NoError(t, err)
   371  
   372  		block = addTestBlock(t, ctx, bs)
   373  		updated := entities.ReferralProgramFromProto(updatedEvent.Program, block.VegaTime, 2)
   374  		err = rs.UpdateReferralProgram(ctx, updated)
   375  		require.NoError(t, err)
   376  
   377  		block = addTestBlock(t, ctx, bs)
   378  		err = rs.EndReferralProgram(ctx, endedEvent.Version, endTime, block.VegaTime, 3)
   379  		require.NoError(t, err)
   380  
   381  		ended := entities.ReferralProgramFromProto(updatedEvent.Program, block.VegaTime, 3)
   382  		ended.Version = endedEvent.Version
   383  		ended.EndedAt = &endTime
   384  
   385  		var got []entities.ReferralProgram
   386  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM referral_programs order by vega_time")
   387  		require.NoError(t, err)
   388  		require.Len(t, got, 3)
   389  		wantAll := []entities.ReferralProgram{*started, *updated, *ended}
   390  		assert.Equal(t, wantAll, got)
   391  
   392  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM current_referral_program")
   393  		require.NoError(t, err)
   394  		require.Len(t, got, 1)
   395  		assert.Equal(t, *ended, got[0])
   396  	})
   397  }
   398  
   399  func TestReferralPrograms_GetCurrentReferralProgram(t *testing.T) {
   400  	bs, rs := setupReferralProgramTest(t)
   401  	ctx := tempTransaction(t)
   402  
   403  	t.Run("GetCurrentReferralProgram should return the current referral program information", func(t *testing.T) {
   404  		block := addTestBlock(t, ctx, bs)
   405  		endTime := block.VegaTime.Add(time.Hour)
   406  		startedEvent, updatedEvent, endedEvent := getReferralEvents(t, endTime)
   407  
   408  		started := entities.ReferralProgramFromProto(startedEvent.Program, block.VegaTime, 1)
   409  		err := rs.AddReferralProgram(ctx, started)
   410  		require.NoError(t, err)
   411  
   412  		got, err := rs.GetCurrentReferralProgram(ctx)
   413  		require.NoError(t, err)
   414  		assert.Equal(t, *started, got)
   415  
   416  		block = addTestBlock(t, ctx, bs)
   417  		updated := entities.ReferralProgramFromProto(updatedEvent.Program, block.VegaTime, 2)
   418  		err = rs.UpdateReferralProgram(ctx, updated)
   419  		require.NoError(t, err)
   420  
   421  		got, err = rs.GetCurrentReferralProgram(ctx)
   422  		require.NoError(t, err)
   423  		assert.Equal(t, *updated, got)
   424  
   425  		block = addTestBlock(t, ctx, bs)
   426  		err = rs.EndReferralProgram(ctx, endedEvent.Version, endTime, block.VegaTime, 3)
   427  		require.NoError(t, err)
   428  
   429  		updatedProgram := entities.ReferralProgramFromProto(updatedEvent.Program, block.VegaTime, 3)
   430  		updatedProgram.Version = endedEvent.Version
   431  		updatedProgram.EndedAt = &endTime
   432  
   433  		got, err = rs.GetCurrentReferralProgram(ctx)
   434  		require.NoError(t, err)
   435  		assert.Equal(t, *updatedProgram, got)
   436  	})
   437  }
   438  
   439  func TestReferralPrograms_StartAndEndInSameBlock(t *testing.T) {
   440  	bs, rs := setupReferralProgramTest(t)
   441  	ctx := tempTransaction(t)
   442  
   443  	t.Run("Data node should allow a referral program to be started and ended in the same block", func(t *testing.T) {
   444  		block := addTestBlock(t, ctx, bs)
   445  		endTime := block.VegaTime
   446  		startedEvent, updatedEvent, endedEvent := getReferralEvents(t, endTime)
   447  
   448  		started := entities.ReferralProgramFromProto(startedEvent.Program, block.VegaTime, 1)
   449  		err := rs.AddReferralProgram(ctx, started)
   450  		require.NoError(t, err)
   451  
   452  		got, err := rs.GetCurrentReferralProgram(ctx)
   453  		require.NoError(t, err)
   454  		assert.Equal(t, *started, got)
   455  
   456  		updated := entities.ReferralProgramFromProto(updatedEvent.Program, block.VegaTime, 2)
   457  		err = rs.UpdateReferralProgram(ctx, updated)
   458  		require.NoError(t, err)
   459  
   460  		got, err = rs.GetCurrentReferralProgram(ctx)
   461  		require.NoError(t, err)
   462  		assert.Equal(t, *updated, got)
   463  
   464  		err = rs.EndReferralProgram(ctx, endedEvent.Version, endTime, block.VegaTime, 3)
   465  		require.NoError(t, err)
   466  
   467  		ended := entities.ReferralProgramFromProto(updatedEvent.Program, block.VegaTime, 3)
   468  		ended.Version = endedEvent.Version
   469  		ended.EndedAt = &endTime
   470  
   471  		got, err = rs.GetCurrentReferralProgram(ctx)
   472  		require.NoError(t, err)
   473  		assert.Equal(t, *ended, got)
   474  	})
   475  }