code.vegaprotocol.io/vega@v0.79.0/core/volumediscount/engine_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 volumediscount_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/events"
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/core/volumediscount"
    27  	"code.vegaprotocol.io/vega/core/volumediscount/mocks"
    28  	"code.vegaprotocol.io/vega/libs/num"
    29  	"code.vegaprotocol.io/vega/libs/proto"
    30  	"code.vegaprotocol.io/vega/protos/vega"
    31  	snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    32  
    33  	"github.com/golang/mock/gomock"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  func assertSnapshotMatches(t *testing.T, key string, expectedHash []byte) *volumediscount.SnapshottedEngine {
    38  	t.Helper()
    39  
    40  	loadCtrl := gomock.NewController(t)
    41  	loadBroker := mocks.NewMockBroker(loadCtrl)
    42  	loadMarketActivityTracker := mocks.NewMockMarketActivityTracker(loadCtrl)
    43  	loadEngine := volumediscount.NewSnapshottedEngine(loadBroker, loadMarketActivityTracker)
    44  
    45  	pl := snapshotpb.Payload{}
    46  	require.NoError(t, proto.Unmarshal(expectedHash, &pl))
    47  
    48  	loadEngine.LoadState(context.Background(), types.PayloadFromProto(&pl))
    49  	loadedHashEmpty, _, err := loadEngine.GetState(key)
    50  	require.NoError(t, err)
    51  	require.True(t, bytes.Equal(expectedHash, loadedHashEmpty))
    52  	return loadEngine
    53  }
    54  
    55  func TestVolumeDiscountProgramLifecycle(t *testing.T) {
    56  	key := (&types.PayloadVolumeDiscountProgram{}).Key()
    57  	ctrl := gomock.NewController(t)
    58  	broker := mocks.NewMockBroker(ctrl)
    59  	marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl)
    60  	engine := volumediscount.NewSnapshottedEngine(broker, marketActivityTracker)
    61  
    62  	// test snapshot with empty engine
    63  	hashEmpty, _, err := engine.GetState(key)
    64  	require.NoError(t, err)
    65  	assertSnapshotMatches(t, key, hashEmpty)
    66  
    67  	now := time.Now()
    68  
    69  	p1 := &types.VolumeDiscountProgram{
    70  		ID:                    "1",
    71  		Version:               0,
    72  		EndOfProgramTimestamp: now.Add(time.Hour * 1),
    73  		WindowLength:          1,
    74  		VolumeBenefitTiers: []*types.VolumeBenefitTier{
    75  			{MinimumRunningNotionalTakerVolume: num.NewUint(1000), VolumeDiscountFactors: types.Factors{
    76  				Infra:     num.DecimalFromFloat(0.1),
    77  				Maker:     num.DecimalFromFloat(0.1),
    78  				Liquidity: num.DecimalFromFloat(0.1),
    79  			}},
    80  			{MinimumRunningNotionalTakerVolume: num.NewUint(2000), VolumeDiscountFactors: types.Factors{
    81  				Infra:     num.DecimalFromFloat(0.2),
    82  				Maker:     num.DecimalFromFloat(0.2),
    83  				Liquidity: num.DecimalFromFloat(0.2),
    84  			}},
    85  			{MinimumRunningNotionalTakerVolume: num.NewUint(3000), VolumeDiscountFactors: types.Factors{
    86  				Infra:     num.DecimalFromFloat(0.5),
    87  				Maker:     num.DecimalFromFloat(0.5),
    88  				Liquidity: num.DecimalFromFloat(0.5),
    89  			}},
    90  			{MinimumRunningNotionalTakerVolume: num.NewUint(4000), VolumeDiscountFactors: types.Factors{
    91  				Infra:     num.DecimalFromFloat(1),
    92  				Maker:     num.DecimalFromFloat(1),
    93  				Liquidity: num.DecimalFromFloat(1),
    94  			}},
    95  		},
    96  	}
    97  	// add the program
    98  	engine.UpdateProgram(p1)
    99  
   100  	// expect an event for the started program
   101  	broker.EXPECT().Send(eventMatcher[*events.VolumeDiscountProgramStarted]{}).DoAndReturn(func(evt events.Event) {
   102  		e := evt.(*events.VolumeDiscountProgramStarted)
   103  		require.Equal(t, p1.IntoProto(), e.GetVolumeDiscountProgramStarted().Program)
   104  	}).Times(1)
   105  	// we expect the stats to be updated when a new program starts
   106  	broker.EXPECT().Send(eventMatcher[*events.VolumeDiscountStatsUpdated]{}).Times(1)
   107  
   108  	// activate the program
   109  	engine.OnEpoch(context.Background(), types.Epoch{Action: vega.EpochAction_EPOCH_ACTION_START, StartTime: now})
   110  
   111  	// check snapshot with new program
   112  	hashWithNew, _, err := engine.GetState(key)
   113  	require.NoError(t, err)
   114  	assertSnapshotMatches(t, key, hashWithNew)
   115  
   116  	// add a new program
   117  	p2 := &types.VolumeDiscountProgram{
   118  		ID:                    "1",
   119  		Version:               1,
   120  		EndOfProgramTimestamp: now.Add(time.Hour * 2),
   121  		WindowLength:          1,
   122  		VolumeBenefitTiers: []*types.VolumeBenefitTier{
   123  			{MinimumRunningNotionalTakerVolume: num.NewUint(2000), VolumeDiscountFactors: types.Factors{
   124  				Maker:     num.DecimalFromFloat(0.2),
   125  				Infra:     num.DecimalFromFloat(0.2),
   126  				Liquidity: num.DecimalFromFloat(0.2),
   127  			}},
   128  			{MinimumRunningNotionalTakerVolume: num.NewUint(3000), VolumeDiscountFactors: types.Factors{
   129  				Maker:     num.DecimalFromFloat(0.5),
   130  				Infra:     num.DecimalFromFloat(0.5),
   131  				Liquidity: num.DecimalFromFloat(0.5),
   132  			}},
   133  			{MinimumRunningNotionalTakerVolume: num.NewUint(1000), VolumeDiscountFactors: types.Factors{
   134  				Maker:     num.DecimalFromFloat(0.1),
   135  				Infra:     num.DecimalFromFloat(0.1),
   136  				Liquidity: num.DecimalFromFloat(0.1),
   137  			}},
   138  			{MinimumRunningNotionalTakerVolume: num.NewUint(4000), VolumeDiscountFactors: types.Factors{
   139  				Maker:     num.DecimalFromFloat(1),
   140  				Infra:     num.DecimalFromFloat(1),
   141  				Liquidity: num.DecimalFromFloat(1),
   142  			}},
   143  		},
   144  	}
   145  	// add the new program
   146  	engine.UpdateProgram(p2)
   147  
   148  	// check snapshot with new program and current
   149  	hashWithNewAndCurrent, _, err := engine.GetState(key)
   150  	require.NoError(t, err)
   151  	assertSnapshotMatches(t, key, hashWithNewAndCurrent)
   152  
   153  	// // expect a program updated event
   154  	broker.EXPECT().Send(eventMatcher[*events.VolumeDiscountProgramUpdated]{}).DoAndReturn(func(evt events.Event) {
   155  		e := evt.(*events.VolumeDiscountProgramUpdated)
   156  		require.Equal(t, p2.IntoProto(), e.GetVolumeDiscountProgramUpdated().Program)
   157  	}).Times(1)
   158  	// expect the stats updated event
   159  	expectStatsUpdated(t, broker)
   160  	engine.OnEpoch(context.Background(), types.Epoch{Action: vega.EpochAction_EPOCH_ACTION_START, StartTime: now.Add(time.Hour * 1)})
   161  
   162  	// // expire the program
   163  	broker.EXPECT().Send(eventMatcher[*events.VolumeDiscountProgramEnded]{}).DoAndReturn(func(evt events.Event) {
   164  		e := evt.(*events.VolumeDiscountProgramEnded)
   165  		require.Equal(t, p2.Version, e.GetVolumeDiscountProgramEnded().Version)
   166  	}).Times(1)
   167  	engine.OnEpoch(context.Background(), types.Epoch{Action: vega.EpochAction_EPOCH_ACTION_START, StartTime: now.Add(time.Hour * 2)})
   168  
   169  	// check snapshot with terminated program
   170  	hashWithPostTermination, _, err := engine.GetState(key)
   171  	require.NoError(t, err)
   172  	assertSnapshotMatches(t, key, hashWithPostTermination)
   173  }
   174  
   175  func TestDiscountFactor(t *testing.T) {
   176  	key := (&types.PayloadVolumeDiscountProgram{}).Key()
   177  	ctrl := gomock.NewController(t)
   178  	broker := mocks.NewMockBroker(ctrl)
   179  	marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl)
   180  	engine := volumediscount.NewSnapshottedEngine(broker, marketActivityTracker)
   181  
   182  	currentTime := time.Now()
   183  
   184  	p1 := &types.VolumeDiscountProgram{
   185  		ID:                    "1",
   186  		Version:               0,
   187  		EndOfProgramTimestamp: currentTime.Add(time.Hour * 1),
   188  		WindowLength:          1,
   189  		VolumeBenefitTiers: []*types.VolumeBenefitTier{
   190  			{MinimumRunningNotionalTakerVolume: num.NewUint(1000), VolumeDiscountFactors: types.Factors{
   191  				Maker:     num.DecimalFromFloat(0.1),
   192  				Infra:     num.DecimalFromFloat(0.1),
   193  				Liquidity: num.DecimalFromFloat(0.1),
   194  			}},
   195  			{MinimumRunningNotionalTakerVolume: num.NewUint(2000), VolumeDiscountFactors: types.Factors{
   196  				Maker:     num.DecimalFromFloat(0.2),
   197  				Infra:     num.DecimalFromFloat(0.2),
   198  				Liquidity: num.DecimalFromFloat(0.2),
   199  			}},
   200  			{MinimumRunningNotionalTakerVolume: num.NewUint(3000), VolumeDiscountFactors: types.Factors{
   201  				Maker:     num.DecimalFromFloat(0.5),
   202  				Infra:     num.DecimalFromFloat(0.5),
   203  				Liquidity: num.DecimalFromFloat(0.5),
   204  			}},
   205  			{MinimumRunningNotionalTakerVolume: num.NewUint(4000), VolumeDiscountFactors: types.Factors{
   206  				Maker:     num.DecimalFromFloat(1),
   207  				Infra:     num.DecimalFromFloat(1),
   208  				Liquidity: num.DecimalFromFloat(1),
   209  			}},
   210  		},
   211  	}
   212  	// add the program
   213  	engine.UpdateProgram(p1)
   214  
   215  	// activate the program
   216  	currentEpoch := uint64(1)
   217  	expectProgramStarted(t, broker, p1)
   218  	expectStatsUpdated(t, broker)
   219  	startEpoch(t, engine, currentEpoch, currentTime)
   220  
   221  	// so now we have a program active so at the end of the epoch lets return for some parties some notional
   222  	marketActivityTracker.EXPECT().NotionalTakerVolumeForAllParties().Return(map[types.PartyID]*num.Uint{
   223  		"p1": num.NewUint(900),
   224  		"p2": num.NewUint(1000),
   225  		"p3": num.NewUint(1001),
   226  		"p4": num.NewUint(2000),
   227  		"p5": num.NewUint(3000),
   228  		"p6": num.NewUint(4000),
   229  		"p7": num.NewUint(5000),
   230  	}).Times(1)
   231  
   232  	// end the epoch to get the market activity recorded
   233  	expectStatsUpdatedWithUnqualifiedParties(t, broker)
   234  	currentTime = currentTime.Add(1 * time.Minute)
   235  	endEpoch(t, engine, currentEpoch, currentTime.Add(1*time.Minute))
   236  
   237  	// start a new epoch for the discount factors to be in place
   238  	currentEpoch += 1
   239  	startEpoch(t, engine, currentEpoch, currentTime)
   240  
   241  	// check snapshot with terminated program
   242  	hashWithEpochNotionalsData, _, err := engine.GetState(key)
   243  	require.NoError(t, err)
   244  	loadedEngine := assertSnapshotMatches(t, key, hashWithEpochNotionalsData)
   245  
   246  	// party does not exist
   247  	require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p8").Infra)
   248  	require.Equal(t, num.DecimalZero(), loadedEngine.VolumeDiscountFactorForParty("p8").Infra)
   249  	// party is not eligible
   250  	require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p1").Infra)
   251  	require.Equal(t, num.DecimalZero(), loadedEngine.VolumeDiscountFactorForParty("p1").Infra)
   252  	// volume between 1000/2000
   253  	require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p2").Infra.String())
   254  	require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p2").Infra.String())
   255  	require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p3").Infra.String())
   256  	require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p3").Infra.String())
   257  
   258  	// volume 2000<=x<3000
   259  	require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p4").Infra.String())
   260  	require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p4").Infra.String())
   261  
   262  	// volume 3000<=x<4000
   263  	require.Equal(t, "0.5", engine.VolumeDiscountFactorForParty("p5").Infra.String())
   264  	require.Equal(t, "0.5", loadedEngine.VolumeDiscountFactorForParty("p5").Infra.String())
   265  
   266  	// volume >=4000
   267  	require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p6").Infra.String())
   268  	require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p6").Infra.String())
   269  	require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p7").Infra.String())
   270  	require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p7").Infra.String())
   271  
   272  	marketActivityTracker.EXPECT().NotionalTakerVolumeForAllParties().Return(map[types.PartyID]*num.Uint{}).Times(1)
   273  
   274  	expectStatsUpdated(t, broker)
   275  	currentTime = p1.EndOfProgramTimestamp
   276  	endEpoch(t, engine, currentEpoch, currentTime)
   277  
   278  	// terminate the program
   279  	currentEpoch += 1
   280  	expectProgramEnded(t, broker, p1)
   281  	startEpoch(t, engine, currentEpoch, currentTime)
   282  
   283  	hashAfterProgramEnded, _, err := engine.GetState(key)
   284  	require.NoError(t, err)
   285  	loadedEngine = assertSnapshotMatches(t, key, hashAfterProgramEnded)
   286  
   287  	// no discount for terminated program
   288  	for _, p := range []string{"p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8"} {
   289  		require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty(types.PartyID(p)).Infra)
   290  		require.Equal(t, num.DecimalZero(), loadedEngine.VolumeDiscountFactorForParty(types.PartyID(p)).Infra)
   291  	}
   292  }
   293  
   294  func TestDiscountFactorWithWindow(t *testing.T) {
   295  	key := (&types.PayloadVolumeDiscountProgram{}).Key()
   296  	ctrl := gomock.NewController(t)
   297  	broker := mocks.NewMockBroker(ctrl)
   298  	marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl)
   299  	engine := volumediscount.NewSnapshottedEngine(broker, marketActivityTracker)
   300  
   301  	currentTime := time.Now()
   302  
   303  	p1 := &types.VolumeDiscountProgram{
   304  		ID:                    "1",
   305  		Version:               0,
   306  		EndOfProgramTimestamp: currentTime.Add(time.Hour * 1),
   307  		WindowLength:          2,
   308  		VolumeBenefitTiers: []*types.VolumeBenefitTier{
   309  			{MinimumRunningNotionalTakerVolume: num.NewUint(1000), VolumeDiscountFactors: types.Factors{
   310  				Maker:     num.DecimalFromFloat(0.1),
   311  				Infra:     num.DecimalFromFloat(0.1),
   312  				Liquidity: num.DecimalFromFloat(0.1),
   313  			}},
   314  			{MinimumRunningNotionalTakerVolume: num.NewUint(2000), VolumeDiscountFactors: types.Factors{
   315  				Maker:     num.DecimalFromFloat(0.2),
   316  				Infra:     num.DecimalFromFloat(0.2),
   317  				Liquidity: num.DecimalFromFloat(0.2),
   318  			}},
   319  			{MinimumRunningNotionalTakerVolume: num.NewUint(3000), VolumeDiscountFactors: types.Factors{
   320  				Maker:     num.DecimalFromFloat(0.5),
   321  				Infra:     num.DecimalFromFloat(0.5),
   322  				Liquidity: num.DecimalFromFloat(0.5),
   323  			}},
   324  			{MinimumRunningNotionalTakerVolume: num.NewUint(4000), VolumeDiscountFactors: types.Factors{
   325  				Maker:     num.DecimalFromFloat(1),
   326  				Infra:     num.DecimalFromFloat(1),
   327  				Liquidity: num.DecimalFromFloat(1),
   328  			}},
   329  		},
   330  	}
   331  	// add the program
   332  	engine.UpdateProgram(p1)
   333  
   334  	// expect an event for the started program
   335  	expectProgramStarted(t, broker, p1)
   336  	expectStatsUpdated(t, broker)
   337  	// activate the program
   338  	currentEpoch := uint64(1)
   339  	startEpoch(t, engine, currentEpoch, currentTime)
   340  
   341  	// so now we have a program active so at the end of the epoch lets return for some parties some notional
   342  	marketActivityTracker.EXPECT().NotionalTakerVolumeForAllParties().Return(map[types.PartyID]*num.Uint{
   343  		"p1": num.NewUint(900),
   344  		"p2": num.NewUint(1000),
   345  		"p3": num.NewUint(1001),
   346  		"p4": num.NewUint(2000),
   347  		"p5": num.NewUint(3000),
   348  		"p6": num.NewUint(4000),
   349  		"p7": num.NewUint(5000),
   350  	}).Times(1)
   351  
   352  	expectStatsUpdatedWithUnqualifiedParties(t, broker)
   353  	currentTime = currentTime.Add(1 * time.Minute)
   354  	endEpoch(t, engine, currentEpoch, currentTime)
   355  	// start a new epoch for the discount factors to be in place
   356  
   357  	// party does not exist
   358  	require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p8").Infra)
   359  	// volume 900
   360  	require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p1").Infra)
   361  	// volume 1000
   362  	require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p2").Infra.String())
   363  	// volume 1001
   364  	require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p3").Infra.String())
   365  	// volume 2000
   366  	require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p4").Infra.String())
   367  	// volume 3000
   368  	require.Equal(t, "0.5", engine.VolumeDiscountFactorForParty("p5").Infra.String())
   369  	// volume 4000
   370  	require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p6").Infra.String())
   371  	// volume 5000
   372  	require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p7").Infra.String())
   373  
   374  	// running for another epoch
   375  	marketActivityTracker.EXPECT().NotionalTakerVolumeForAllParties().Return(map[types.PartyID]*num.Uint{
   376  		"p8": num.NewUint(2000),
   377  		"p1": num.NewUint(1500),
   378  		"p5": num.NewUint(4000),
   379  		"p6": num.NewUint(4000),
   380  	}).Times(1)
   381  
   382  	expectStatsUpdated(t, broker)
   383  	currentTime = currentTime.Add(1 * time.Minute)
   384  	endEpoch(t, engine, currentEpoch, currentTime)
   385  
   386  	currentEpoch += 1
   387  	startEpoch(t, engine, currentEpoch, currentTime)
   388  
   389  	hashAfter2Epochs, _, err := engine.GetState(key)
   390  	require.NoError(t, err)
   391  	loadedEngine := assertSnapshotMatches(t, key, hashAfter2Epochs)
   392  
   393  	// now p8 exists and the volume is 2000
   394  	require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p8").Infra.String())
   395  	require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p8").Infra.String())
   396  	// volume 2400
   397  	require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p1").Infra.String())
   398  	require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p1").Infra.String())
   399  	// volume 1000
   400  	require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p2").Infra.String())
   401  	require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p2").Infra.String())
   402  	// volume 1001
   403  	require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p3").Infra.String())
   404  	require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p3").Infra.String())
   405  	// volume 2000
   406  	require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p4").Infra.String())
   407  	require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p4").Infra.String())
   408  	// volume 7000
   409  	require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p5").Infra.String())
   410  	require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p5").Infra.String())
   411  	// volume 8000
   412  	require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p6").Infra.String())
   413  	require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p6").Infra.String())
   414  	// volume 5000
   415  	require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p7").Infra.String())
   416  	require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p7").Infra.String())
   417  
   418  	marketActivityTracker.EXPECT().NotionalTakerVolumeForAllParties().Return(map[types.PartyID]*num.Uint{}).Times(1)
   419  
   420  	expectStatsUpdated(t, broker)
   421  	currentTime = p1.EndOfProgramTimestamp
   422  	endEpoch(t, engine, currentEpoch, currentTime)
   423  
   424  	expectProgramEnded(t, broker, p1)
   425  	currentEpoch += 1
   426  	startEpoch(t, engine, currentEpoch, currentTime)
   427  
   428  	hashAfterProgramEnded, _, err := engine.GetState(key)
   429  	require.NoError(t, err)
   430  	loadedEngine = assertSnapshotMatches(t, key, hashAfterProgramEnded)
   431  
   432  	// no discount for terminated program
   433  	for _, p := range []string{"p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8"} {
   434  		require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty(types.PartyID(p)).Infra)
   435  		require.Equal(t, num.DecimalZero(), loadedEngine.VolumeDiscountFactorForParty(types.PartyID(p)).Infra)
   436  	}
   437  }