code.vegaprotocol.io/vega@v0.79.0/core/fee/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 fee_test
    17  
    18  import (
    19  	"errors"
    20  	"testing"
    21  
    22  	"code.vegaprotocol.io/vega/core/fee"
    23  	"code.vegaprotocol.io/vega/core/fee/mocks"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/num"
    26  	"code.vegaprotocol.io/vega/logging"
    27  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  const (
    35  	testAsset = "ETH"
    36  )
    37  
    38  var (
    39  	testFees = types.Fees{
    40  		Factors: &types.FeeFactors{
    41  			LiquidityFee:      num.DecimalFromFloat(0.1),
    42  			InfrastructureFee: num.DecimalFromFloat(0.05),
    43  			MakerFee:          num.DecimalFromFloat(0.02),
    44  		},
    45  	}
    46  	extendedTestFees = types.Fees{
    47  		Factors: &types.FeeFactors{
    48  			LiquidityFee:      num.DecimalFromFloat(0.1),
    49  			InfrastructureFee: num.DecimalFromFloat(0.05),
    50  			MakerFee:          num.DecimalFromFloat(0.02),
    51  			BuyBackFee:        num.DecimalFromFloat(0.002),
    52  			TreasuryFee:       num.DecimalFromFloat(0.003),
    53  		},
    54  	}
    55  )
    56  
    57  type testFee struct {
    58  	*fee.Engine
    59  }
    60  
    61  func getTestFee(t *testing.T) *testFee {
    62  	t.Helper()
    63  	eng, err := fee.New(
    64  		logging.NewTestLogger(),
    65  		fee.NewDefaultConfig(),
    66  		testFees,
    67  		testAsset,
    68  		num.DecimalFromInt64(1),
    69  	)
    70  	assert.NoError(t, err)
    71  	return &testFee{eng}
    72  }
    73  
    74  func getExtendedTestFee(t *testing.T) *testFee {
    75  	t.Helper()
    76  	eng, err := fee.New(
    77  		logging.NewTestLogger(),
    78  		fee.NewDefaultConfig(),
    79  		extendedTestFees,
    80  		testAsset,
    81  		num.DecimalFromInt64(1),
    82  	)
    83  	assert.NoError(t, err)
    84  	return &testFee{eng}
    85  }
    86  
    87  func TestFeeEngine(t *testing.T) {
    88  	t.Run("update fee factors with invalid input", testUpdateFeeFactorsError)
    89  	t.Run("update fee factors with valid input", testUpdateFeeFactors)
    90  	t.Run("calculate continuous trading fee empty trade", testCalcContinuousTradingErrorEmptyTrade)
    91  	t.Run("calculate continuous trading fee", testCalcContinuousTrading)
    92  	t.Run("calculate continuous trading fee + check amounts", testCalcContinuousTradingAndCheckAmounts)
    93  	t.Run("calculate continuous trading fee + check amounts with discounts and rewards", testCalcContinuousTradingAndCheckAmountsWithDiscount)
    94  	t.Run("calculate auction trading fee empty trade", testCalcAuctionTradingErrorEmptyTrade)
    95  	t.Run("calculate auction trading fee", testCalcAuctionTrading)
    96  	t.Run("calculate batch auction trading fee empty trade", testCalcBatchAuctionTradingErrorEmptyTrade)
    97  	t.Run("calculate batch auction trading fee same batch", testCalcBatchAuctionTradingSameBatch)
    98  	t.Run("calculate batch auction trading fee different batches", testCalcBatchAuctionTradingDifferentBatches)
    99  	t.Run("Build liquidity fee transfers with remainder", testBuildLiquidityFeesRemainder)
   100  	t.Run("calculate closeout fees", testCloseoutFees)
   101  }
   102  
   103  func TestFeeEngineWithBuyBackAndTreasury(t *testing.T) {
   104  	t.Run("update fee factors with invalid input", testUpdateExtendedFeeFactorsError)
   105  	t.Run("update fee factors with valid input", testUpdateExtendedFeeFactors)
   106  	t.Run("calculate continuous trading fee empty trade", testCalcContinuousTradingErrorEmptyTrade)
   107  	t.Run("calculate continuous trading fee", testCalcContinuousTradingExtended)
   108  	t.Run("calculate continuous trading fee + check amounts", testCalcContinuousTradingAndCheckAmountsExtended)
   109  	t.Run("calculate continuous trading fee + check amounts with discounts and rewards", testCalcContinuousTradingAndCheckAmountsWithDiscountExtended)
   110  	t.Run("calculate auction trading fee empty trade", testCalcAuctionTradingErrorEmptyTrade)
   111  	t.Run("calculate auction trading fee", testCalcAuctionTradingExtended)
   112  	t.Run("calculate batch auction trading fee empty trade", testCalcBatchAuctionTradingErrorEmptyTrade)
   113  }
   114  
   115  func testUpdateFeeFactors(t *testing.T) {
   116  	eng := getTestFee(t)
   117  	okFees := types.Fees{
   118  		Factors: &types.FeeFactors{
   119  			LiquidityFee:      num.DecimalFromFloat(0.1),
   120  			InfrastructureFee: num.DecimalFromFloat(0.5),
   121  			MakerFee:          num.DecimalFromFloat(0.25),
   122  		},
   123  	}
   124  	err := eng.UpdateFeeFactors(okFees)
   125  	assert.NoError(t, err)
   126  }
   127  
   128  func testUpdateExtendedFeeFactors(t *testing.T) {
   129  	eng := getExtendedTestFee(t)
   130  	okFees := types.Fees{
   131  		Factors: &types.FeeFactors{
   132  			LiquidityFee:      num.DecimalFromFloat(0.1),
   133  			InfrastructureFee: num.DecimalFromFloat(0.5),
   134  			MakerFee:          num.DecimalFromFloat(0.25),
   135  			BuyBackFee:        num.DecimalFromFloat(0.3),
   136  			TreasuryFee:       num.DecimalFromFloat(0.4),
   137  		},
   138  	}
   139  	err := eng.UpdateFeeFactors(okFees)
   140  	assert.NoError(t, err)
   141  }
   142  
   143  func testUpdateFeeFactorsError(t *testing.T) {
   144  	eng := getTestFee(t)
   145  	koFees := types.Fees{
   146  		Factors: &types.FeeFactors{
   147  			LiquidityFee:      num.DecimalFromFloat(-.1),
   148  			InfrastructureFee: num.DecimalFromFloat(0.5),
   149  			MakerFee:          num.DecimalFromFloat(0.25),
   150  		},
   151  	}
   152  	err := eng.UpdateFeeFactors(koFees)
   153  	assert.Error(t, err)
   154  
   155  	koFees = types.Fees{
   156  		Factors: &types.FeeFactors{
   157  			LiquidityFee:      num.DecimalFromFloat(0.1),
   158  			InfrastructureFee: num.DecimalFromFloat(-.1),
   159  			MakerFee:          num.DecimalFromFloat(0.25),
   160  		},
   161  	}
   162  	err = eng.UpdateFeeFactors(koFees)
   163  	assert.Error(t, err)
   164  	koFees = types.Fees{
   165  		Factors: &types.FeeFactors{
   166  			LiquidityFee:      num.DecimalFromFloat(0.1),
   167  			InfrastructureFee: num.DecimalFromFloat(0.5),
   168  			MakerFee:          num.DecimalFromFloat(-.1),
   169  		},
   170  	}
   171  	err = eng.UpdateFeeFactors(koFees)
   172  	assert.Error(t, err)
   173  }
   174  
   175  func testUpdateExtendedFeeFactorsError(t *testing.T) {
   176  	eng := getExtendedTestFee(t)
   177  	koFees := types.Fees{
   178  		Factors: &types.FeeFactors{
   179  			LiquidityFee:      num.DecimalFromFloat(0.1),
   180  			InfrastructureFee: num.DecimalFromFloat(0.5),
   181  			MakerFee:          num.DecimalFromFloat(0.25),
   182  			BuyBackFee:        num.DecimalFromFloat(-1),
   183  			TreasuryFee:       num.DecimalFromFloat(0.4),
   184  		},
   185  	}
   186  	err := eng.UpdateFeeFactors(koFees)
   187  	assert.Error(t, err)
   188  
   189  	koFees = types.Fees{
   190  		Factors: &types.FeeFactors{
   191  			LiquidityFee:      num.DecimalFromFloat(0.1),
   192  			InfrastructureFee: num.DecimalFromFloat(0.11),
   193  			MakerFee:          num.DecimalFromFloat(0.25),
   194  			BuyBackFee:        num.DecimalFromFloat(0.41),
   195  			TreasuryFee:       num.DecimalFromFloat(-1),
   196  		},
   197  	}
   198  	err = eng.UpdateFeeFactors(koFees)
   199  	assert.Error(t, err)
   200  }
   201  
   202  func testCalcContinuousTradingErrorEmptyTrade(t *testing.T) {
   203  	eng := getTestFee(t)
   204  	ctrl := gomock.NewController(t)
   205  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
   206  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
   207  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
   208  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
   209  
   210  	_, err := eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService)
   211  	assert.EqualError(t, err, fee.ErrEmptyTrades.Error())
   212  
   213  	eng = getExtendedTestFee(t)
   214  	_, err = eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService)
   215  	assert.EqualError(t, err, fee.ErrEmptyTrades.Error())
   216  }
   217  
   218  func testCalcContinuousTradingAndCheckAmounts(t *testing.T) {
   219  	eng := getTestFee(t)
   220  	ctrl := gomock.NewController(t)
   221  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
   222  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
   223  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
   224  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
   225  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   226  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   227  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   228  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
   229  	require.NoError(t, eng.UpdateFeeFactors(types.Fees{
   230  		Factors: &types.FeeFactors{
   231  			MakerFee:          num.DecimalFromFloat(.000250),
   232  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   233  			LiquidityFee:      num.DecimalFromFloat(0.001),
   234  		},
   235  	}))
   236  	trades := []*types.Trade{
   237  		{
   238  			Aggressor: types.SideSell,
   239  			Seller:    "party1",
   240  			Buyer:     "party2",
   241  			Size:      5,
   242  			Price:     num.NewUint(100000),
   243  		},
   244  	}
   245  
   246  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
   247  	assert.NotNil(t, ft)
   248  	assert.Nil(t, err)
   249  	transfers := ft.Transfers()
   250  	var pay, recv, infra, liquidity int
   251  	for _, v := range transfers {
   252  		if v.Type == types.TransferTypeLiquidityFeePay {
   253  			liquidity++
   254  			assert.Equal(t, num.NewUint(500), v.Amount.Amount)
   255  		}
   256  		if v.Type == types.TransferTypeInfrastructureFeePay {
   257  			infra++
   258  			assert.Equal(t, num.NewUint(250), v.Amount.Amount)
   259  		}
   260  		if v.Type == types.TransferTypeMakerFeeReceive {
   261  			recv++
   262  			assert.Equal(t, num.NewUint(125), v.Amount.Amount)
   263  		}
   264  		if v.Type == types.TransferTypeMakerFeePay {
   265  			pay++
   266  			assert.Equal(t, num.NewUint(125), v.Amount.Amount)
   267  		}
   268  	}
   269  
   270  	assert.Equal(t, liquidity, 1)
   271  	assert.Equal(t, infra, 1)
   272  	assert.Equal(t, recv, len(trades))
   273  	assert.Equal(t, pay, len(trades))
   274  	assert.Equal(t, &eventspb.FeesStats{
   275  		Market:                   "",
   276  		Asset:                    testAsset,
   277  		EpochSeq:                 0,
   278  		TotalRewardsReceived:     []*eventspb.PartyAmount{},
   279  		ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{},
   280  		RefereesDiscountApplied: []*eventspb.PartyAmount{
   281  			{
   282  				Party:         "party1",
   283  				Amount:        "0",
   284  				QuantumAmount: "0",
   285  			},
   286  		},
   287  		VolumeDiscountApplied: []*eventspb.PartyAmount{
   288  			{
   289  				Party:         "party1",
   290  				Amount:        "0",
   291  				QuantumAmount: "0",
   292  			},
   293  		},
   294  		TotalMakerFeesReceived: []*eventspb.PartyAmount{
   295  			{
   296  				Party:         "party2",
   297  				Amount:        "125",
   298  				QuantumAmount: "125",
   299  			},
   300  		},
   301  		MakerFeesGenerated: []*eventspb.MakerFeesGenerated{
   302  			{
   303  				Taker: "party1",
   304  				MakerFeesPaid: []*eventspb.PartyAmount{
   305  					{
   306  						Party:         "party2",
   307  						Amount:        "125",
   308  						QuantumAmount: "125",
   309  					},
   310  				},
   311  			},
   312  		},
   313  		TotalFeesPaidAndReceived: []*eventspb.PartyAmount{
   314  			{
   315  				Party:         "party1",
   316  				Amount:        "875",
   317  				QuantumAmount: "875",
   318  			},
   319  			{
   320  				Party:         "party2",
   321  				Amount:        "125",
   322  				QuantumAmount: "125",
   323  			},
   324  		},
   325  	}, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1)))
   326  }
   327  
   328  func testCalcContinuousTradingAndCheckAmountsExtended(t *testing.T) {
   329  	eng := getExtendedTestFee(t)
   330  	ctrl := gomock.NewController(t)
   331  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
   332  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
   333  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
   334  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.0025)).AnyTimes()
   335  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
   336  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   337  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   338  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   339  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
   340  	require.NoError(t, eng.UpdateFeeFactors(types.Fees{
   341  		Factors: &types.FeeFactors{
   342  			MakerFee:          num.DecimalFromFloat(.000250),
   343  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   344  			LiquidityFee:      num.DecimalFromFloat(0.001),
   345  			BuyBackFee:        num.DecimalFromFloat(0.002),
   346  			TreasuryFee:       num.DecimalFromFloat(0.003),
   347  		},
   348  	}))
   349  	trades := []*types.Trade{
   350  		{
   351  			Aggressor: types.SideSell,
   352  			Seller:    "party1",
   353  			Buyer:     "party2",
   354  			Size:      5,
   355  			Price:     num.NewUint(100000),
   356  		},
   357  	}
   358  
   359  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
   360  	assert.NotNil(t, ft)
   361  	assert.Nil(t, err)
   362  	transfers := ft.Transfers()
   363  	var pay, recv, infra, liquidity, bb, treasury, hmp, hmr int
   364  	for _, v := range transfers {
   365  		if v.Type == types.TransferTypeLiquidityFeePay {
   366  			liquidity++
   367  			assert.Equal(t, num.NewUint(500), v.Amount.Amount)
   368  		}
   369  		if v.Type == types.TransferTypeInfrastructureFeePay {
   370  			infra++
   371  			assert.Equal(t, num.NewUint(250), v.Amount.Amount)
   372  		}
   373  		if v.Type == types.TransferTypeMakerFeeReceive {
   374  			recv++
   375  			assert.Equal(t, num.NewUint(125), v.Amount.Amount)
   376  		}
   377  		if v.Type == types.TransferTypeMakerFeePay {
   378  			pay++
   379  			assert.Equal(t, num.NewUint(125), v.Amount.Amount)
   380  		}
   381  		if v.Type == types.TransferTypeBuyBackFeePay {
   382  			bb++
   383  			assert.Equal(t, num.NewUint(500), v.Amount.Amount)
   384  		}
   385  		if v.Type == types.TransferTypeTreasuryPay {
   386  			treasury++
   387  			assert.Equal(t, num.NewUint(750), v.Amount.Amount)
   388  		}
   389  		if v.Type == types.TransferTypeHighMakerRebatePay {
   390  			hmp++
   391  			assert.Equal(t, num.NewUint(1250), v.Amount.Amount)
   392  		}
   393  		if v.Type == types.TransferTypeHighMakerRebateReceive {
   394  			hmr++
   395  			assert.Equal(t, num.NewUint(1250), v.Amount.Amount)
   396  		}
   397  	}
   398  
   399  	assert.Equal(t, liquidity, 1)
   400  	assert.Equal(t, infra, 1)
   401  	assert.Equal(t, bb, 1)
   402  	assert.Equal(t, treasury, 1)
   403  	assert.Equal(t, hmp, 1)
   404  	assert.Equal(t, hmr, 1)
   405  	assert.Equal(t, recv, len(trades))
   406  	assert.Equal(t, pay, len(trades))
   407  	assert.Equal(t, &eventspb.FeesStats{
   408  		Market:                   "",
   409  		Asset:                    testAsset,
   410  		EpochSeq:                 0,
   411  		TotalRewardsReceived:     []*eventspb.PartyAmount{},
   412  		ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{},
   413  		RefereesDiscountApplied: []*eventspb.PartyAmount{
   414  			{
   415  				Party:         "party1",
   416  				Amount:        "0",
   417  				QuantumAmount: "0",
   418  			},
   419  		},
   420  		VolumeDiscountApplied: []*eventspb.PartyAmount{
   421  			{
   422  				Party:         "party1",
   423  				Amount:        "0",
   424  				QuantumAmount: "0",
   425  			},
   426  		},
   427  		TotalMakerFeesReceived: []*eventspb.PartyAmount{
   428  			{
   429  				Party:         "party2",
   430  				Amount:        "125",
   431  				QuantumAmount: "125",
   432  			},
   433  		},
   434  		MakerFeesGenerated: []*eventspb.MakerFeesGenerated{
   435  			{
   436  				Taker: "party1",
   437  				MakerFeesPaid: []*eventspb.PartyAmount{
   438  					{
   439  						Party:         "party2",
   440  						Amount:        "125",
   441  						QuantumAmount: "125",
   442  					},
   443  				},
   444  			},
   445  		},
   446  		TotalFeesPaidAndReceived: []*eventspb.PartyAmount{
   447  			{
   448  				Party:         "party1",
   449  				Amount:        "3375",
   450  				QuantumAmount: "3375",
   451  			},
   452  			{
   453  				Party:         "party2",
   454  				Amount:        "125",
   455  				QuantumAmount: "125",
   456  			},
   457  		},
   458  	}, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1)))
   459  }
   460  
   461  func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) {
   462  	eng := getTestFee(t)
   463  	ctrl := gomock.NewController(t)
   464  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
   465  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
   466  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
   467  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
   468  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{
   469  		Infra:     num.NewDecimalFromFloat(0.3),
   470  		Maker:     num.NewDecimalFromFloat(0.3),
   471  		Liquidity: num.NewDecimalFromFloat(0.3),
   472  	}).AnyTimes()
   473  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{
   474  		Infra:     num.NewDecimalFromFloat(0.2),
   475  		Maker:     num.NewDecimalFromFloat(0.2),
   476  		Liquidity: num.NewDecimalFromFloat(0.2),
   477  	}).AnyTimes()
   478  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(
   479  		types.Factors{
   480  			Infra:     num.NewDecimalFromFloat(0.1),
   481  			Maker:     num.NewDecimalFromFloat(0.1),
   482  			Liquidity: num.NewDecimalFromFloat(0.1),
   483  		})
   484  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party3"), nil).AnyTimes()
   485  	require.NoError(t, eng.UpdateFeeFactors(types.Fees{
   486  		Factors: &types.FeeFactors{
   487  			MakerFee:          num.DecimalFromFloat(.000250),
   488  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   489  			LiquidityFee:      num.DecimalFromFloat(0.001),
   490  		},
   491  	}))
   492  	trades := []*types.Trade{
   493  		{
   494  			Aggressor: types.SideSell,
   495  			Seller:    "party1",
   496  			Buyer:     "party2",
   497  			Size:      5,
   498  			Price:     num.NewUint(100000),
   499  		},
   500  	}
   501  
   502  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
   503  	assert.NotNil(t, ft)
   504  	assert.Nil(t, err)
   505  	transfers := ft.Transfers()
   506  	var pay, recv, infra, liquidity int
   507  	for _, v := range transfers {
   508  		if v.Type == types.TransferTypeLiquidityFeePay {
   509  			liquidity++
   510  			assert.Equal(t, num.NewUint(252), v.Amount.Amount)
   511  		}
   512  		if v.Type == types.TransferTypeInfrastructureFeePay {
   513  			infra++
   514  			assert.Equal(t, num.NewUint(127), v.Amount.Amount)
   515  		}
   516  		if v.Type == types.TransferTypeMakerFeeReceive {
   517  			recv++
   518  			assert.Equal(t, num.NewUint(64), v.Amount.Amount)
   519  		}
   520  		if v.Type == types.TransferTypeMakerFeePay {
   521  			pay++
   522  			assert.Equal(t, num.NewUint(64), v.Amount.Amount)
   523  		}
   524  	}
   525  
   526  	assert.Equal(t, liquidity, 1)
   527  	assert.Equal(t, infra, 1)
   528  	assert.Equal(t, recv, len(trades))
   529  	assert.Equal(t, pay, len(trades))
   530  	assert.Equal(t, &eventspb.FeesStats{
   531  		Asset: testAsset,
   532  		TotalRewardsReceived: []*eventspb.PartyAmount{
   533  			{
   534  				Party:         "party3",
   535  				Amount:        "110",
   536  				QuantumAmount: "110",
   537  			},
   538  		},
   539  		ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{
   540  			{
   541  				Referrer: "party3",
   542  				GeneratedReward: []*eventspb.PartyAmount{
   543  					{
   544  						Party:         "party1",
   545  						Amount:        "110",
   546  						QuantumAmount: "110",
   547  					},
   548  				},
   549  			},
   550  		},
   551  		RefereesDiscountApplied: []*eventspb.PartyAmount{
   552  			{
   553  				Party:         "party1",
   554  				Amount:        "262",
   555  				QuantumAmount: "262",
   556  			},
   557  		},
   558  		VolumeDiscountApplied: []*eventspb.PartyAmount{
   559  			{
   560  				Party:         "party1",
   561  				Amount:        "60",
   562  				QuantumAmount: "60",
   563  			},
   564  		},
   565  		TotalMakerFeesReceived: []*eventspb.PartyAmount{
   566  			{
   567  				Party:         "party2",
   568  				Amount:        "64",
   569  				QuantumAmount: "64",
   570  			},
   571  		},
   572  		MakerFeesGenerated: []*eventspb.MakerFeesGenerated{
   573  			{
   574  				Taker: "party1",
   575  				MakerFeesPaid: []*eventspb.PartyAmount{
   576  					{
   577  						Party:         "party2",
   578  						Amount:        "64",
   579  						QuantumAmount: "64",
   580  					},
   581  				},
   582  			},
   583  		},
   584  		TotalFeesPaidAndReceived: []*eventspb.PartyAmount{
   585  			{
   586  				Party:         "party1",
   587  				Amount:        "443",
   588  				QuantumAmount: "443",
   589  			},
   590  			{
   591  				Party:         "party2",
   592  				Amount:        "64",
   593  				QuantumAmount: "64",
   594  			},
   595  		},
   596  	}, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1)))
   597  }
   598  
   599  func testCalcContinuousTradingAndCheckAmountsWithDiscountExtended(t *testing.T) {
   600  	eng := getExtendedTestFee(t)
   601  	ctrl := gomock.NewController(t)
   602  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
   603  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
   604  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
   605  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.0025)).AnyTimes()
   606  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{
   607  		Infra:     num.NewDecimalFromFloat(0.3),
   608  		Maker:     num.NewDecimalFromFloat(0.3),
   609  		Liquidity: num.NewDecimalFromFloat(0.3),
   610  	}).AnyTimes()
   611  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{
   612  		Infra:     num.NewDecimalFromFloat(0.2),
   613  		Maker:     num.NewDecimalFromFloat(0.2),
   614  		Liquidity: num.NewDecimalFromFloat(0.2),
   615  	}).AnyTimes()
   616  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(
   617  		types.Factors{
   618  			Infra:     num.NewDecimalFromFloat(0.1),
   619  			Maker:     num.NewDecimalFromFloat(0.1),
   620  			Liquidity: num.NewDecimalFromFloat(0.1),
   621  		})
   622  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party3"), nil).AnyTimes()
   623  	require.NoError(t, eng.UpdateFeeFactors(types.Fees{
   624  		Factors: &types.FeeFactors{
   625  			MakerFee:          num.DecimalFromFloat(.000250),
   626  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   627  			LiquidityFee:      num.DecimalFromFloat(0.001),
   628  			BuyBackFee:        num.DecimalFromFloat(0.002),
   629  			TreasuryFee:       num.DecimalFromFloat(0.003),
   630  		},
   631  	}))
   632  	trades := []*types.Trade{
   633  		{
   634  			Aggressor: types.SideSell,
   635  			Seller:    "party1",
   636  			Buyer:     "party2",
   637  			Size:      5,
   638  			Price:     num.NewUint(100000),
   639  		},
   640  	}
   641  
   642  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
   643  	assert.NotNil(t, ft)
   644  	assert.Nil(t, err)
   645  	transfers := ft.Transfers()
   646  	var pay, recv, infra, liquidity, bb, treasury, hmp, hmr int
   647  	for _, v := range transfers {
   648  		if v.Type == types.TransferTypeLiquidityFeePay {
   649  			liquidity++
   650  			assert.Equal(t, num.NewUint(252), v.Amount.Amount)
   651  		}
   652  		if v.Type == types.TransferTypeInfrastructureFeePay {
   653  			infra++
   654  			assert.Equal(t, num.NewUint(127), v.Amount.Amount)
   655  		}
   656  		if v.Type == types.TransferTypeMakerFeeReceive {
   657  			recv++
   658  			assert.Equal(t, num.NewUint(64), v.Amount.Amount)
   659  		}
   660  		if v.Type == types.TransferTypeMakerFeePay {
   661  			pay++
   662  			assert.Equal(t, num.NewUint(64), v.Amount.Amount)
   663  		}
   664  		if v.Type == types.TransferTypeBuyBackFeePay {
   665  			bb++
   666  			assert.Equal(t, num.NewUint(500), v.Amount.Amount)
   667  		}
   668  		if v.Type == types.TransferTypeTreasuryPay {
   669  			treasury++
   670  			assert.Equal(t, num.NewUint(750), v.Amount.Amount)
   671  		}
   672  		if v.Type == types.TransferTypeHighMakerRebatePay {
   673  			hmp++
   674  			assert.Equal(t, num.NewUint(1250), v.Amount.Amount)
   675  		}
   676  		if v.Type == types.TransferTypeHighMakerRebateReceive {
   677  			hmr++
   678  			assert.Equal(t, num.NewUint(1250), v.Amount.Amount)
   679  		}
   680  	}
   681  
   682  	assert.Equal(t, liquidity, 1)
   683  	assert.Equal(t, infra, 1)
   684  	assert.Equal(t, bb, 1)
   685  	assert.Equal(t, treasury, 1)
   686  	assert.Equal(t, hmp, 1)
   687  	assert.Equal(t, hmr, 1)
   688  	assert.Equal(t, recv, len(trades))
   689  	assert.Equal(t, pay, len(trades))
   690  	assert.Equal(t, &eventspb.FeesStats{
   691  		Asset: testAsset,
   692  		TotalRewardsReceived: []*eventspb.PartyAmount{
   693  			{
   694  				Party:         "party3",
   695  				Amount:        "110",
   696  				QuantumAmount: "110",
   697  			},
   698  		},
   699  		ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{
   700  			{
   701  				Referrer: "party3",
   702  				GeneratedReward: []*eventspb.PartyAmount{
   703  					{
   704  						Party:         "party1",
   705  						Amount:        "110",
   706  						QuantumAmount: "110",
   707  					},
   708  				},
   709  			},
   710  		},
   711  		RefereesDiscountApplied: []*eventspb.PartyAmount{
   712  			{
   713  				Party:         "party1",
   714  				Amount:        "262",
   715  				QuantumAmount: "262",
   716  			},
   717  		},
   718  		VolumeDiscountApplied: []*eventspb.PartyAmount{
   719  			{
   720  				Party:         "party1",
   721  				Amount:        "60",
   722  				QuantumAmount: "60",
   723  			},
   724  		},
   725  		TotalMakerFeesReceived: []*eventspb.PartyAmount{
   726  			{
   727  				Party:         "party2",
   728  				Amount:        "64",
   729  				QuantumAmount: "64",
   730  			},
   731  		},
   732  		MakerFeesGenerated: []*eventspb.MakerFeesGenerated{
   733  			{
   734  				Taker: "party1",
   735  				MakerFeesPaid: []*eventspb.PartyAmount{
   736  					{
   737  						Party:         "party2",
   738  						Amount:        "64",
   739  						QuantumAmount: "64",
   740  					},
   741  				},
   742  			},
   743  		},
   744  		TotalFeesPaidAndReceived: []*eventspb.PartyAmount{
   745  			{
   746  				Party:         "party1",
   747  				Amount:        "2943",
   748  				QuantumAmount: "2943",
   749  			},
   750  			{
   751  				Party:         "party2",
   752  				Amount:        "64",
   753  				QuantumAmount: "64",
   754  			},
   755  		},
   756  	}, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1)))
   757  }
   758  
   759  func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t *testing.T, aggressorSide types.Side) {
   760  	t.Helper()
   761  	eng := getTestFee(t)
   762  	ctrl := gomock.NewController(t)
   763  
   764  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
   765  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
   766  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
   767  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
   768  
   769  	eng.UpdateFeeFactors(types.Fees{
   770  		Factors: &types.FeeFactors{
   771  			MakerFee:          num.DecimalFromFloat(.000250),
   772  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   773  			LiquidityFee:      num.DecimalFromFloat(0.001),
   774  		},
   775  	})
   776  
   777  	trades := []*types.Trade{
   778  		{
   779  			Aggressor: aggressorSide,
   780  			Seller:    "party1",
   781  			Buyer:     "party2",
   782  			Size:      5,
   783  			Price:     num.NewUint(100000),
   784  		},
   785  	}
   786  
   787  	aggressor := "party1"
   788  	if aggressorSide == types.SideBuy {
   789  		aggressor = "party2"
   790  	}
   791  
   792  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{
   793  		Infra:     num.NewDecimalFromFloat(0.5),
   794  		Maker:     num.NewDecimalFromFloat(0.5),
   795  		Liquidity: num.NewDecimalFromFloat(0.5),
   796  	}).AnyTimes()
   797  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{
   798  		Infra:     num.NewDecimalFromFloat(0.25),
   799  		Maker:     num.NewDecimalFromFloat(0.25),
   800  		Liquidity: num.NewDecimalFromFloat(0.25),
   801  	})
   802  	discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes()
   803  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{
   804  		Infra:     num.NewDecimalFromFloat(0.3),
   805  		Maker:     num.NewDecimalFromFloat(0.3),
   806  		Liquidity: num.NewDecimalFromFloat(0.3),
   807  	}).AnyTimes()
   808  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.Factors{
   809  		Infra:     num.NewDecimalFromFloat(0.3),
   810  		Maker:     num.NewDecimalFromFloat(0.3),
   811  		Liquidity: num.NewDecimalFromFloat(0.3),
   812  	}).AnyTimes()
   813  
   814  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
   815  	assert.NotNil(t, ft)
   816  	assert.Nil(t, err)
   817  	transfers := ft.Transfers()
   818  	var pay, recv, infra, liquidity, reward int
   819  
   820  	for _, v := range transfers {
   821  		if v.Type == types.TransferTypeLiquidityFeePay {
   822  			liquidity++
   823  			// lf = 500 before discounts and rewards
   824  			// lf = 500 - 0.5 * 500 = 250 after applying referral discount
   825  			// lf = 250 - 0.25 * 250 = 250 - 62 = 188
   826  			// applying rewards
   827  			// lf = 188 - 188 * 0.3 = 188 - 56 = 132
   828  			assert.Equal(t, num.NewUint(132), v.Amount.Amount)
   829  		}
   830  		if v.Type == types.TransferTypeFeeReferrerRewardPay {
   831  			reward++
   832  			// 14 + 56 + 31 = 96
   833  			require.Equal(t, num.NewUint(98), v.Amount.Amount)
   834  		}
   835  		if v.Type == types.TransferTypeFeeReferrerRewardDistribute {
   836  			reward++
   837  		}
   838  		if v.Type == types.TransferTypeInfrastructureFeePay {
   839  			infra++
   840  			// inf = 250 before discounts and rewards
   841  			// inf = 250 - 0.5*250 = 125 after applying referral discount
   842  			// inf = 125 - 0.25*125 = 125-31 = 94
   843  			// applying rewards
   844  			// inf = 94 - 94 *0.3 = 66
   845  			assert.Equal(t, num.NewUint(66), v.Amount.Amount)
   846  		}
   847  		if v.Type == types.TransferTypeMakerFeePay {
   848  			pay++
   849  			// mf = 125 before discounts and rewards
   850  			// inf = 125 - 0.5*125 = 63 after applying referral discount
   851  			// inf = 63 - 0.25*63 = 63-15 = 48
   852  			// applying rewards
   853  			// inf = 48 - 48 *0.3 = 48 - 14 = 34
   854  			assert.Equal(t, num.NewUint(34), v.Amount.Amount)
   855  		}
   856  		if v.Type == types.TransferTypeMakerFeeReceive {
   857  			recv++
   858  			assert.Equal(t, num.NewUint(34), v.Amount.Amount)
   859  		}
   860  	}
   861  
   862  	assert.Equal(t, liquidity, 1)
   863  	assert.Equal(t, infra, 1)
   864  	assert.Equal(t, recv, len(trades))
   865  	assert.Equal(t, pay, len(trades))
   866  	assert.Equal(t, reward, 2)
   867  }
   868  
   869  func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultipeMakers(t *testing.T, aggressorSide types.Side) {
   870  	t.Helper()
   871  	eng := getTestFee(t)
   872  	ctrl := gomock.NewController(t)
   873  
   874  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
   875  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
   876  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
   877  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
   878  
   879  	eng.UpdateFeeFactors(types.Fees{
   880  		Factors: &types.FeeFactors{
   881  			MakerFee:          num.DecimalFromFloat(.000250),
   882  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   883  			LiquidityFee:      num.DecimalFromFloat(0.001),
   884  		},
   885  	})
   886  
   887  	trades := []*types.Trade{
   888  		{
   889  			Aggressor: aggressorSide,
   890  			Seller:    "party1",
   891  			Buyer:     "party2",
   892  			Size:      1,
   893  			Price:     num.NewUint(100000),
   894  		},
   895  		{
   896  			Aggressor: aggressorSide,
   897  			Seller:    "party1",
   898  			Buyer:     "party3",
   899  			Size:      2,
   900  			Price:     num.NewUint(100000),
   901  		},
   902  		{
   903  			Aggressor: aggressorSide,
   904  			Seller:    "party1",
   905  			Buyer:     "party2",
   906  			Size:      2,
   907  			Price:     num.NewUint(100000),
   908  		},
   909  	}
   910  
   911  	aggressor := "party1"
   912  	if aggressorSide == types.SideBuy {
   913  		aggressor = "party2"
   914  	}
   915  
   916  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{
   917  		Infra:     num.NewDecimalFromFloat(0.5),
   918  		Maker:     num.NewDecimalFromFloat(0.5),
   919  		Liquidity: num.NewDecimalFromFloat(0.5),
   920  	}).AnyTimes()
   921  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{
   922  		Infra:     num.NewDecimalFromFloat(0.25),
   923  		Maker:     num.NewDecimalFromFloat(0.25),
   924  		Liquidity: num.NewDecimalFromFloat(0.25),
   925  	}).AnyTimes()
   926  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("referrer"), nil).AnyTimes()
   927  	discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes()
   928  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(aggressor).Return(types.Factors{
   929  		Infra:     num.NewDecimalFromFloat(0.3),
   930  		Maker:     num.NewDecimalFromFloat(0.3),
   931  		Liquidity: num.NewDecimalFromFloat(0.3),
   932  	}).AnyTimes()
   933  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{
   934  		Infra:     num.NewDecimalFromFloat(0.3),
   935  		Maker:     num.NewDecimalFromFloat(0.3),
   936  		Liquidity: num.NewDecimalFromFloat(0.3),
   937  	}).AnyTimes()
   938  
   939  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
   940  	assert.NotNil(t, ft)
   941  	assert.Nil(t, err)
   942  	transfers := ft.Transfers()
   943  	var pay, recv, infra, liquidity, reward int
   944  	totalPaidMakerFee := num.UintZero()
   945  	totalReceivedMakerFee := num.UintZero()
   946  
   947  	for _, v := range transfers {
   948  		if v.Type == types.TransferTypeLiquidityFeePay {
   949  			liquidity++
   950  			// lf1 = 100 - 0.5 * 100 = 50
   951  			// lf1 = 50 - 0.25 * 50 = 50-12 = 38
   952  			// lf1 = 38 - 38 * 0.3 = 38 - 11 = 27
   953  
   954  			// lf2 = 200 - 0.5 * 200 = 100
   955  			// lf2 = 100 - 0.25 * 100 = 75
   956  			// lf2 = 75 - 75 * 0.3 = 75 - 22 = 53
   957  
   958  			// lf3 = 200 - 0.5 * 200 = 100
   959  			// lf3 = 100 - 0.25 * 100 = 75
   960  			// lf3 = 75 - 75 * 0.3 = 75 - 22 = 53
   961  			assert.Equal(t, num.NewUint(133), v.Amount.Amount)
   962  		}
   963  		if v.Type == types.TransferTypeInfrastructureFeePay {
   964  			infra++
   965  			// inf1 = 50 - 0.5 * 50 = 25
   966  			// inf1 = 25 - 0.25 * 25 = 25-6 = 19
   967  			// inf1 = 19 - 19 * 0.3 = 19 - 5 = 14
   968  
   969  			// inf2 = 100 - 0.5 * 100 = 50
   970  			// inf2 = 50 - 0.25 * 50 = 50-12 = 38
   971  			// inf2 = 38 - 38 * 0.3 = 38 - 11 = 27
   972  
   973  			// inf3 = 100 - 0.5 * 100 = 50
   974  			// inf3 = 50 - 0.25 * 50 = 50-12 = 38
   975  			// inf3 = 38 - 38 * 0.3 = 38 - 11 = 27
   976  			assert.Equal(t, num.NewUint(68), v.Amount.Amount)
   977  		}
   978  		if v.Type == types.TransferTypeMakerFeePay {
   979  			pay++
   980  			totalPaidMakerFee.AddSum(v.Amount.Amount)
   981  		}
   982  		if v.Type == types.TransferTypeMakerFeeReceive {
   983  			recv++
   984  			totalReceivedMakerFee.AddSum(v.Amount.Amount)
   985  		}
   986  		if v.Type == types.TransferTypeFeeReferrerRewardPay {
   987  			reward++
   988  			// 55 + 27 + 13
   989  			assert.Equal(t, num.NewUint(95), v.Amount.Amount)
   990  		}
   991  		if v.Type == types.TransferTypeFeeReferrerRewardDistribute {
   992  			reward++
   993  			// 55 + 27 + 13
   994  			assert.Equal(t, num.NewUint(95), v.Amount.Amount)
   995  		}
   996  	}
   997  
   998  	// mf1 = 25 - 0.5 * 25 = 13
   999  	// mf1 = 13 - 0.25 * 13 = 13-3=10
  1000  	// mf1 = 10 - 10 * 0.3 = 10 - 3 = 7
  1001  	// mf2 = 50 - 0.5 * 50 = 25
  1002  	// mf2 = 25 - 0.25 * 25 = 25-6 = 19
  1003  	// mf2 = 19 - 19 * 0.3 = 19 - 5 = 14
  1004  	// mf3 = 50 - 0.5 * 50 = 25
  1005  	// mf3 = 25 - 0.25 * 25 = 25-6 = 19
  1006  	// mf3 = 19 - 19 * 0.3 = 19 - 5 = 14
  1007  	assert.Equal(t, num.NewUint(35), totalPaidMakerFee)
  1008  	assert.Equal(t, num.NewUint(35), totalReceivedMakerFee)
  1009  
  1010  	assert.Equal(t, liquidity, 1)
  1011  	assert.Equal(t, infra, 1)
  1012  	assert.Equal(t, recv, len(trades))
  1013  	assert.Equal(t, pay, len(trades))
  1014  	assert.Equal(t, reward, 2)
  1015  }
  1016  
  1017  func TestCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewards(t *testing.T) {
  1018  	testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t, types.SideSell)
  1019  	testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t, types.SideBuy)
  1020  }
  1021  
  1022  func TestCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsMultiMakers(t *testing.T) {
  1023  	testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultipeMakers(t, types.SideSell)
  1024  	testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultipeMakers(t, types.SideBuy)
  1025  }
  1026  
  1027  func testBuildLiquidityFeesRemainder(t *testing.T) {
  1028  	eng := getTestFee(t)
  1029  	shares := map[string]num.Decimal{
  1030  		"lp1": num.DecimalFromFloat(0.8),
  1031  		"lp2": num.DecimalFromFloat(0.15),
  1032  		"lp3": num.DecimalFromFloat(0.05),
  1033  	}
  1034  	// amount to distribute
  1035  	acc := &types.Account{
  1036  		Balance: num.NewUint(1002),
  1037  	}
  1038  	// 1002 * .8 = 801.6 == 801
  1039  	// 1002 * .15 = 150.3 = 150
  1040  	// 1002 * 0.05 = 50.1 = 50
  1041  	// 801 + 150 + 50 = 1001 -> remainder is 1
  1042  	expRemainder := num.NewUint(1)
  1043  	expFees := map[string]*num.Uint{
  1044  		"lp1": num.NewUint(801),
  1045  		"lp2": num.NewUint(150),
  1046  		"lp3": num.NewUint(50),
  1047  	}
  1048  	ft := eng.BuildLiquidityFeeDistributionTransfer(shares, acc)
  1049  	got := ft.TotalFeesAmountPerParty()
  1050  	for p, amt := range got {
  1051  		require.True(t, amt.EQ(expFees[p]))
  1052  	}
  1053  	// get the total transfer amount from the transfers
  1054  	total := num.UintZero()
  1055  	for _, t := range ft.Transfers() {
  1056  		total.AddSum(t.Amount.Amount)
  1057  	}
  1058  	rem := num.UintZero().Sub(acc.Balance, total)
  1059  	require.True(t, rem.EQ(expRemainder))
  1060  }
  1061  
  1062  func testCalcContinuousTrading(t *testing.T) {
  1063  	eng := getTestFee(t)
  1064  	ctrl := gomock.NewController(t)
  1065  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1066  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1067  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1068  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1069  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1070  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1071  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1072  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party1"), errors.New("not a referrer")).AnyTimes()
  1073  
  1074  	trades := []*types.Trade{
  1075  		{
  1076  			Aggressor: types.SideSell,
  1077  			Seller:    "party1",
  1078  			Buyer:     "party2",
  1079  			Size:      10,
  1080  			Price:     num.NewUint(10000),
  1081  		},
  1082  		{
  1083  			Aggressor: types.SideSell,
  1084  			Seller:    "party1",
  1085  			Buyer:     "party3",
  1086  			Size:      1,
  1087  			Price:     num.NewUint(10300),
  1088  		},
  1089  		{
  1090  			Aggressor: types.SideSell,
  1091  			Seller:    "party1",
  1092  			Buyer:     "party4",
  1093  			Size:      7,
  1094  			Price:     num.NewUint(10300),
  1095  		},
  1096  		{
  1097  			Aggressor: types.SideSell,
  1098  			Seller:    "party1",
  1099  			Buyer:     "party2",
  1100  			Size:      2,
  1101  			Price:     num.NewUint(10500),
  1102  		},
  1103  		{
  1104  			Aggressor: types.SideSell,
  1105  			Seller:    "party1",
  1106  			Buyer:     "party5",
  1107  			Size:      5,
  1108  			Price:     num.NewUint(11000),
  1109  		},
  1110  	}
  1111  
  1112  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
  1113  	assert.NotNil(t, ft)
  1114  	assert.Nil(t, err)
  1115  
  1116  	// get the amounts map
  1117  	feeAmounts := ft.TotalFeesAmountPerParty()
  1118  	party1Amount, ok := feeAmounts["party1"]
  1119  	assert.True(t, ok)
  1120  	assert.Equal(t, num.NewUint(43928), party1Amount)
  1121  
  1122  	// get the transfer and check we have enough of each types
  1123  	transfers := ft.Transfers()
  1124  	var pay, recv, infra, liquidity int
  1125  	for _, v := range transfers {
  1126  		if v.Type == types.TransferTypeLiquidityFeePay {
  1127  			liquidity++
  1128  		}
  1129  		if v.Type == types.TransferTypeInfrastructureFeePay {
  1130  			infra++
  1131  		}
  1132  		if v.Type == types.TransferTypeMakerFeeReceive {
  1133  			recv++
  1134  		}
  1135  		if v.Type == types.TransferTypeMakerFeePay {
  1136  			pay++
  1137  		}
  1138  	}
  1139  
  1140  	assert.Equal(t, liquidity, 1)
  1141  	assert.Equal(t, infra, 1)
  1142  	assert.Equal(t, recv, len(trades))
  1143  	assert.Equal(t, pay, len(trades))
  1144  }
  1145  
  1146  func testCalcContinuousTradingExtended(t *testing.T) {
  1147  	eng := getExtendedTestFee(t)
  1148  	ctrl := gomock.NewController(t)
  1149  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1150  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1151  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1152  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.00005)).AnyTimes()
  1153  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party3")).Return(num.DecimalZero()).AnyTimes()
  1154  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party4")).Return(num.DecimalZero()).AnyTimes()
  1155  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party5")).Return(num.DecimalZero()).AnyTimes()
  1156  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1157  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1158  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1159  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party1"), errors.New("not a referrer")).AnyTimes()
  1160  
  1161  	trades := []*types.Trade{
  1162  		{
  1163  			Aggressor: types.SideSell,
  1164  			Seller:    "party1",
  1165  			Buyer:     "party2",
  1166  			Size:      10,
  1167  			Price:     num.NewUint(10000),
  1168  		},
  1169  		{
  1170  			Aggressor: types.SideSell,
  1171  			Seller:    "party1",
  1172  			Buyer:     "party3",
  1173  			Size:      1,
  1174  			Price:     num.NewUint(10300),
  1175  		},
  1176  		{
  1177  			Aggressor: types.SideSell,
  1178  			Seller:    "party1",
  1179  			Buyer:     "party4",
  1180  			Size:      7,
  1181  			Price:     num.NewUint(10300),
  1182  		},
  1183  		{
  1184  			Aggressor: types.SideSell,
  1185  			Seller:    "party1",
  1186  			Buyer:     "party2",
  1187  			Size:      2,
  1188  			Price:     num.NewUint(10500),
  1189  		},
  1190  		{
  1191  			Aggressor: types.SideSell,
  1192  			Seller:    "party1",
  1193  			Buyer:     "party5",
  1194  			Size:      5,
  1195  			Price:     num.NewUint(11000),
  1196  		},
  1197  	}
  1198  
  1199  	ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
  1200  	assert.NotNil(t, ft)
  1201  	assert.Nil(t, err)
  1202  
  1203  	// get the amounts map
  1204  	feeAmounts := ft.TotalFeesAmountPerParty()
  1205  	party1Amount, ok := feeAmounts["party1"]
  1206  	assert.True(t, ok)
  1207  	assert.Equal(t, num.NewUint(45221), party1Amount)
  1208  
  1209  	// get the transfer and check we have enough of each types
  1210  	transfers := ft.Transfers()
  1211  	var pay, recv, infra, liquidity, highMakerPay, highMakerReceive, bb, treasury int
  1212  	for _, v := range transfers {
  1213  		if v.Type == types.TransferTypeLiquidityFeePay {
  1214  			liquidity++
  1215  		}
  1216  		if v.Type == types.TransferTypeInfrastructureFeePay {
  1217  			infra++
  1218  		}
  1219  		if v.Type == types.TransferTypeMakerFeeReceive {
  1220  			recv++
  1221  		}
  1222  		if v.Type == types.TransferTypeMakerFeePay {
  1223  			pay++
  1224  		}
  1225  		if v.Type == types.TransferTypeHighMakerRebatePay {
  1226  			highMakerPay++
  1227  		}
  1228  		if v.Type == types.TransferTypeHighMakerRebateReceive {
  1229  			highMakerReceive++
  1230  		}
  1231  		if v.Type == types.TransferTypeBuyBackFeePay {
  1232  			bb++
  1233  		}
  1234  		if v.Type == types.TransferTypeTreasuryPay {
  1235  			treasury++
  1236  		}
  1237  	}
  1238  
  1239  	assert.Equal(t, liquidity, 1)
  1240  	assert.Equal(t, infra, 1)
  1241  	assert.Equal(t, highMakerPay, 2)
  1242  	assert.Equal(t, highMakerReceive, 2)
  1243  	assert.Equal(t, recv, len(trades))
  1244  	assert.Equal(t, pay, len(trades))
  1245  	assert.Equal(t, bb, len(trades))
  1246  	assert.Equal(t, treasury, len(trades))
  1247  }
  1248  
  1249  func testCalcAuctionTradingErrorEmptyTrade(t *testing.T) {
  1250  	eng := getTestFee(t)
  1251  	ctrl := gomock.NewController(t)
  1252  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1253  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1254  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1255  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1256  	_, err := eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService)
  1257  	assert.EqualError(t, err, fee.ErrEmptyTrades.Error())
  1258  
  1259  	eng = getExtendedTestFee(t)
  1260  	_, err = eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService)
  1261  	assert.EqualError(t, err, fee.ErrEmptyTrades.Error())
  1262  }
  1263  
  1264  func testCalcAuctionTrading(t *testing.T) {
  1265  	eng := getTestFee(t)
  1266  	ctrl := gomock.NewController(t)
  1267  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1268  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1269  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1270  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1271  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1272  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1273  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1274  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
  1275  	trades := []*types.Trade{
  1276  		{
  1277  			Aggressor: types.SideSell,
  1278  			Seller:    "party1",
  1279  			Buyer:     "party2",
  1280  			Size:      1,
  1281  			Price:     num.NewUint(100),
  1282  		},
  1283  	}
  1284  
  1285  	ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
  1286  	assert.NotNil(t, ft)
  1287  	assert.Nil(t, err)
  1288  
  1289  	// get the amounts map
  1290  	feeAmounts := ft.TotalFeesAmountPerParty()
  1291  	// fees are (100 * 0.1 + 100 * 0.05) = 15
  1292  	// 15 / 2 = 7.5
  1293  	// internally the engine Ceil all fees.
  1294  	// so here we will expect 8 for each
  1295  	party1Amount, ok := feeAmounts["party1"]
  1296  	assert.True(t, ok)
  1297  	assert.Equal(t, num.NewUint(8), party1Amount)
  1298  	party2Amount, ok := feeAmounts["party2"]
  1299  	assert.True(t, ok)
  1300  	assert.Equal(t, num.NewUint(8), party2Amount)
  1301  
  1302  	// get the transfer and check we have enough of each types
  1303  	transfers := ft.Transfers()
  1304  	var pay, recv, infra, liquidity int
  1305  	for _, v := range transfers {
  1306  		if v.Type == types.TransferTypeLiquidityFeePay {
  1307  			liquidity++
  1308  		}
  1309  		if v.Type == types.TransferTypeInfrastructureFeePay {
  1310  			infra++
  1311  		}
  1312  		if v.Type == types.TransferTypeMakerFeeReceive {
  1313  			recv++
  1314  		}
  1315  		if v.Type == types.TransferTypeMakerFeePay {
  1316  			pay++
  1317  		}
  1318  	}
  1319  
  1320  	assert.Equal(t, liquidity, 2)
  1321  	assert.Equal(t, infra, 2)
  1322  	assert.Equal(t, recv, 0)
  1323  	assert.Equal(t, pay, 0)
  1324  }
  1325  
  1326  func testCalcAuctionTradingExtended(t *testing.T) {
  1327  	eng := getExtendedTestFee(t)
  1328  	ctrl := gomock.NewController(t)
  1329  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1330  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1331  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1332  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.0025)).AnyTimes()
  1333  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1334  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1335  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1336  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
  1337  	trades := []*types.Trade{
  1338  		{
  1339  			Aggressor: types.SideSell,
  1340  			Seller:    "party1",
  1341  			Buyer:     "party2",
  1342  			Size:      1,
  1343  			Price:     num.NewUint(100),
  1344  		},
  1345  	}
  1346  
  1347  	ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
  1348  	assert.NotNil(t, ft)
  1349  	assert.Nil(t, err)
  1350  
  1351  	// get the amounts map
  1352  	feeAmounts := ft.TotalFeesAmountPerParty()
  1353  	// fees are (100 * 0.1 + 100 * 0.05) = 15
  1354  	// 15 / 2 = 7.5
  1355  	// internally the engine Ceil all fees.
  1356  	// so here we will expect 8 for each
  1357  	party1Amount, ok := feeAmounts["party1"]
  1358  	assert.True(t, ok)
  1359  	assert.Equal(t, num.NewUint(8), party1Amount)
  1360  	party2Amount, ok := feeAmounts["party2"]
  1361  	assert.True(t, ok)
  1362  	assert.Equal(t, num.NewUint(8), party2Amount)
  1363  
  1364  	// get the transfer and check we have enough of each types
  1365  	transfers := ft.Transfers()
  1366  	var pay, recv, infra, liquidity, hmp, hmr, bb, treasury int
  1367  	for _, v := range transfers {
  1368  		if v.Type == types.TransferTypeLiquidityFeePay {
  1369  			liquidity++
  1370  		}
  1371  		if v.Type == types.TransferTypeInfrastructureFeePay {
  1372  			infra++
  1373  		}
  1374  		if v.Type == types.TransferTypeMakerFeeReceive {
  1375  			recv++
  1376  		}
  1377  		if v.Type == types.TransferTypeMakerFeePay {
  1378  			pay++
  1379  		}
  1380  		if v.Type == types.TransferTypeHighMakerRebatePay {
  1381  			hmp++
  1382  		}
  1383  		if v.Type == types.TransferTypeHighMakerRebateReceive {
  1384  			hmr++
  1385  		}
  1386  		if v.Type == types.TransferTypeBuyBackFeePay {
  1387  			bb++
  1388  		}
  1389  		if v.Type == types.TransferTypeTreasuryPay {
  1390  			treasury++
  1391  		}
  1392  	}
  1393  
  1394  	assert.Equal(t, 2, liquidity)
  1395  	assert.Equal(t, 2, infra)
  1396  	assert.Equal(t, 0, recv)
  1397  	assert.Equal(t, 0, pay)
  1398  	assert.Equal(t, 0, hmp)
  1399  	assert.Equal(t, 0, hmr)
  1400  	assert.Equal(t, 2, bb)
  1401  	assert.Equal(t, 2, treasury)
  1402  }
  1403  
  1404  func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) {
  1405  	eng := getTestFee(t)
  1406  	ctrl := gomock.NewController(t)
  1407  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1408  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1409  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1410  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1411  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors {
  1412  		if p == types.PartyID("party1") {
  1413  			return types.EmptyFactors
  1414  		} else {
  1415  			return types.Factors{
  1416  				Infra:     num.NewDecimalFromFloat(0.5),
  1417  				Maker:     num.NewDecimalFromFloat(0.5),
  1418  				Liquidity: num.NewDecimalFromFloat(0.5),
  1419  			}
  1420  		}
  1421  	}).AnyTimes()
  1422  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors {
  1423  		if p == types.PartyID("party1") {
  1424  			return types.Factors{
  1425  				Infra:     num.NewDecimalFromFloat(0.2),
  1426  				Maker:     num.NewDecimalFromFloat(0.2),
  1427  				Liquidity: num.NewDecimalFromFloat(0.2),
  1428  			}
  1429  		} else {
  1430  			return types.Factors{
  1431  				Infra:     num.NewDecimalFromFloat(0.3),
  1432  				Maker:     num.NewDecimalFromFloat(0.3),
  1433  				Liquidity: num.NewDecimalFromFloat(0.3),
  1434  			}
  1435  		}
  1436  	}).AnyTimes()
  1437  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).DoAndReturn(func(p types.PartyID) (types.PartyID, error) {
  1438  		if p == types.PartyID("party1") {
  1439  			return types.PartyID("referrer"), nil
  1440  		} else {
  1441  			return types.PartyID(""), errors.New("No referrer")
  1442  		}
  1443  	}).AnyTimes()
  1444  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{
  1445  		Infra:     num.NewDecimalFromFloat(0.5),
  1446  		Maker:     num.NewDecimalFromFloat(0.5),
  1447  		Liquidity: num.NewDecimalFromFloat(0.5),
  1448  	}).AnyTimes()
  1449  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.EmptyFactors).AnyTimes()
  1450  
  1451  	trades := []*types.Trade{
  1452  		{
  1453  			Aggressor: types.SideSell,
  1454  			Seller:    "party1",
  1455  			Buyer:     "party2",
  1456  			Size:      1,
  1457  			Price:     num.NewUint(100),
  1458  		},
  1459  	}
  1460  
  1461  	ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
  1462  	assert.NotNil(t, ft)
  1463  	assert.Nil(t, err)
  1464  
  1465  	// get the amounts map
  1466  	feeAmounts := ft.TotalFeesAmountPerParty()
  1467  
  1468  	// liquidity fee before discounts = 0.5 * 0.1 * 100 = 5
  1469  	// party1
  1470  	// lfAfterRefDiscount = 5 - 0
  1471  	// lfAfterVolDiscount = 5 - 0.2*5 = 5-1 = 4
  1472  	// lfAfterReward = 4 - 0.5*4 = 2
  1473  
  1474  	// party2
  1475  	// lfAfterRefDiscount = 5 - 0.5*5 = 3
  1476  	// lfAfterVolDiscount = 3 - 0.3*3 = 3-0 = 3
  1477  	// lfAfterReward = 3 (no referrer)
  1478  
  1479  	// infra fee before discounts = 0.5 * 0.05 * 100 = 3
  1480  	// party1
  1481  	// infAfterRefDiscount = 3 - 0
  1482  	// infAfterVolDiscount = 3 - 0.2*3 = 3
  1483  	// infAfterReward = 3 - 0.5*3 = 2
  1484  
  1485  	// party2
  1486  	// infAfterRefDiscount = 3 - 0.5*3 = 2
  1487  	// infAfterVolDiscount = 2 - 0.3*2 = 2
  1488  	// infAfterReward = 2 (no referrer)
  1489  
  1490  	party1Amount, ok := feeAmounts["party1"]
  1491  	require.True(t, ok)
  1492  	require.Equal(t, num.NewUint(4), party1Amount)
  1493  	party2Amount, ok := feeAmounts["party2"]
  1494  	require.True(t, ok)
  1495  	require.Equal(t, num.NewUint(5), party2Amount)
  1496  
  1497  	// get the transfer and check we have enough of each types
  1498  	transfers := ft.Transfers()
  1499  	var pay, recv, infra, liquidity, reward int
  1500  	totalReward := num.UintZero()
  1501  	for _, v := range transfers {
  1502  		if v.Type == types.TransferTypeLiquidityFeePay {
  1503  			liquidity++
  1504  		}
  1505  		if v.Type == types.TransferTypeInfrastructureFeePay {
  1506  			infra++
  1507  		}
  1508  		if v.Type == types.TransferTypeMakerFeeReceive {
  1509  			recv++
  1510  		}
  1511  		if v.Type == types.TransferTypeMakerFeePay {
  1512  			pay++
  1513  		}
  1514  		if v.Type == types.TransferTypeFeeReferrerRewardPay {
  1515  			reward++
  1516  			totalReward.AddSum(v.Amount.Amount)
  1517  		}
  1518  		if v.Type == types.TransferTypeFeeReferrerRewardDistribute {
  1519  			reward++
  1520  		}
  1521  	}
  1522  	require.Equal(t, num.NewUint(3), totalReward)
  1523  	require.Equal(t, 2, liquidity)
  1524  	require.Equal(t, 2, infra)
  1525  	require.Equal(t, 0, recv)
  1526  	require.Equal(t, 0, pay)
  1527  	require.Equal(t, 2, reward)
  1528  }
  1529  
  1530  func testCalcBatchAuctionTradingErrorEmptyTrade(t *testing.T) {
  1531  	eng := getTestFee(t)
  1532  	ctrl := gomock.NewController(t)
  1533  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1534  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1535  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1536  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1537  	_, err := eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService)
  1538  	assert.EqualError(t, err, fee.ErrEmptyTrades.Error())
  1539  
  1540  	eng = getExtendedTestFee(t)
  1541  	_, err = eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService)
  1542  	assert.EqualError(t, err, fee.ErrEmptyTrades.Error())
  1543  }
  1544  
  1545  func testCalcBatchAuctionTradingSameBatch(t *testing.T) {
  1546  	eng := getTestFee(t)
  1547  	ctrl := gomock.NewController(t)
  1548  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1549  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1550  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1551  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1552  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1553  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1554  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1555  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
  1556  	trades := []*types.Trade{
  1557  		{
  1558  			Aggressor:          types.SideSell,
  1559  			Seller:             "party1",
  1560  			Buyer:              "party2",
  1561  			Size:               1,
  1562  			Price:              num.NewUint(100),
  1563  			SellerAuctionBatch: 10,
  1564  			BuyerAuctionBatch:  10,
  1565  		},
  1566  	}
  1567  
  1568  	ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
  1569  	assert.NotNil(t, ft)
  1570  	assert.Nil(t, err)
  1571  
  1572  	// get the amounts map
  1573  	feeAmounts := ft.TotalFeesAmountPerParty()
  1574  	// fees are (100 * 0.1 + 100 * 0.05) = 15
  1575  	// 15 / 2 = 7.5
  1576  	// internally the engine Ceil all fees.
  1577  	// so here we will expect 8 for each
  1578  	party1Amount, ok := feeAmounts["party1"]
  1579  	assert.True(t, ok)
  1580  	assert.Equal(t, num.NewUint(8), party1Amount)
  1581  	party2Amount, ok := feeAmounts["party2"]
  1582  	assert.True(t, ok)
  1583  	assert.Equal(t, num.NewUint(8), party2Amount)
  1584  
  1585  	// get the transfer and check we have enough of each types
  1586  	transfers := ft.Transfers()
  1587  	var pay, recv, infra, liquidity int
  1588  	for _, v := range transfers {
  1589  		if v.Type == types.TransferTypeLiquidityFeePay {
  1590  			liquidity++
  1591  		}
  1592  		if v.Type == types.TransferTypeInfrastructureFeePay {
  1593  			infra++
  1594  		}
  1595  		if v.Type == types.TransferTypeMakerFeeReceive {
  1596  			recv++
  1597  		}
  1598  		if v.Type == types.TransferTypeMakerFeePay {
  1599  			pay++
  1600  		}
  1601  	}
  1602  
  1603  	assert.Equal(t, liquidity, 2)
  1604  	assert.Equal(t, infra, 2)
  1605  	assert.Equal(t, recv, 0)
  1606  	assert.Equal(t, pay, 0)
  1607  }
  1608  
  1609  func testCalcBatchAuctionTradingDifferentBatches(t *testing.T) {
  1610  	eng := getTestFee(t)
  1611  	ctrl := gomock.NewController(t)
  1612  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1613  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1614  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1615  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1616  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1617  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1618  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1619  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
  1620  	trades := []*types.Trade{
  1621  		{
  1622  			Aggressor:          types.SideSell,
  1623  			Seller:             "party1",
  1624  			Buyer:              "party2",
  1625  			Size:               1,
  1626  			Price:              num.NewUint(100),
  1627  			SellerAuctionBatch: 11,
  1628  			BuyerAuctionBatch:  10,
  1629  		},
  1630  	}
  1631  
  1632  	ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService)
  1633  	assert.NotNil(t, ft)
  1634  	assert.Nil(t, err)
  1635  
  1636  	// get the amounts map
  1637  	feeAmounts := ft.TotalFeesAmountPerParty()
  1638  	// fees are (100 * 0.1 + 100 * 0.05 + 100 *0.02) = 17
  1639  	party1Amount, ok := feeAmounts["party1"]
  1640  	assert.True(t, ok)
  1641  	assert.Equal(t, num.NewUint(17), party1Amount)
  1642  	party2Amount, ok := feeAmounts["party2"]
  1643  	assert.True(t, ok)
  1644  	assert.True(t, party2Amount.IsZero())
  1645  
  1646  	// get the transfer and check we have enough of each types
  1647  	transfers := ft.Transfers()
  1648  	var pay, recv, infra, liquidity int
  1649  	for _, v := range transfers {
  1650  		if v.Type == types.TransferTypeLiquidityFeePay {
  1651  			liquidity++
  1652  		}
  1653  		if v.Type == types.TransferTypeInfrastructureFeePay {
  1654  			infra++
  1655  		}
  1656  		if v.Type == types.TransferTypeMakerFeeReceive {
  1657  			recv++
  1658  		}
  1659  		if v.Type == types.TransferTypeMakerFeePay {
  1660  			pay++
  1661  		}
  1662  	}
  1663  
  1664  	assert.Equal(t, liquidity, 1)
  1665  	assert.Equal(t, infra, 1)
  1666  	assert.Equal(t, recv, 1)
  1667  	assert.Equal(t, pay, 1)
  1668  }
  1669  
  1670  func testCloseoutFees(t *testing.T) {
  1671  	eng := getTestFee(t)
  1672  	ctrl := gomock.NewController(t)
  1673  	referralDiscountService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1674  	referralDiscountService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1675  	discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl)
  1676  	volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl)
  1677  	volumeRebateService := mocks.NewMockVolumeRebateService(ctrl)
  1678  	discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1679  	discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1680  	volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1681  	discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
  1682  	referralDiscountService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
  1683  	volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes()
  1684  	trades := []*types.Trade{
  1685  		{
  1686  			Aggressor: types.SideSell,
  1687  			Seller:    types.NetworkParty,
  1688  			Buyer:     "party1",
  1689  			Size:      1,
  1690  			Price:     num.NewUint(100),
  1691  		},
  1692  		{
  1693  			Aggressor: types.SideSell,
  1694  			Seller:    types.NetworkParty,
  1695  			Buyer:     "party2",
  1696  			Size:      1,
  1697  			Price:     num.NewUint(100),
  1698  		},
  1699  		{
  1700  			Aggressor: types.SideSell,
  1701  			Seller:    types.NetworkParty,
  1702  			Buyer:     "party3",
  1703  			Size:      1,
  1704  			Price:     num.NewUint(100),
  1705  		},
  1706  		{
  1707  			Aggressor: types.SideSell,
  1708  			Seller:    types.NetworkParty,
  1709  			Buyer:     "party4",
  1710  			Size:      1,
  1711  			Price:     num.NewUint(100),
  1712  		},
  1713  	}
  1714  
  1715  	ft, fee := eng.GetFeeForPositionResolution(trades, referralDiscountService, volumeDiscountService, volumeRebateService)
  1716  	assert.NotNil(t, fee)
  1717  	allTransfers := ft.Transfers()
  1718  	// first we have the network -> pay transfers , then 1 transfer per good party
  1719  	// two additional transfers for the buy back and treasury fees
  1720  	assert.Equal(t, len(trades), len(allTransfers)-5)
  1721  	goodPartyTransfers := allTransfers[5:]
  1722  	networkTransfers := allTransfers[:5]
  1723  
  1724  	numTrades := num.NewUint(uint64(len(trades)))
  1725  	// maker fee is 100 * 0.02 == 2
  1726  	// total network fee is 2 * len(trades)
  1727  	feeAmt := num.NewUint(2)
  1728  	total := num.UintZero().Mul(feeAmt, numTrades)
  1729  	// this must be the total fee for the network
  1730  	require.True(t, total.EQ(fee.MakerFee))
  1731  
  1732  	// now check the transfers for the fee payouts
  1733  	for _, trans := range goodPartyTransfers {
  1734  		require.True(t, trans.Amount.Amount.EQ(feeAmt))
  1735  		require.True(t, trans.MinAmount.EQ(num.UintZero()))
  1736  	}
  1737  	// other fees below, making these transfers may be a bit silly at times...
  1738  	// liquidity fees are 100 * 0.1 -> 10 * 4 = 40
  1739  	liqFee := num.UintZero().Mul(numTrades, num.NewUint(10))
  1740  	require.True(t, liqFee.EQ(fee.LiquidityFee))
  1741  	// infraFee is 100 * 0.05 -> 5 * 4 == 20
  1742  	infraFee := num.UintZero().Mul(numTrades, num.NewUint(5))
  1743  	require.True(t, infraFee.EQ(fee.InfrastructureFee))
  1744  	for _, tf := range networkTransfers {
  1745  		require.True(t, num.UintZero().EQ(tf.MinAmount))
  1746  		switch tf.Type {
  1747  		case types.TransferTypeMakerFeePay:
  1748  			require.True(t, tf.Amount.Amount.EQ(fee.MakerFee))
  1749  		case types.TransferTypeLiquidityFeePay:
  1750  			require.True(t, tf.Amount.Amount.EQ(fee.LiquidityFee))
  1751  		case types.TransferTypeInfrastructureFeePay:
  1752  			require.True(t, tf.Amount.Amount.EQ(fee.InfrastructureFee))
  1753  		}
  1754  	}
  1755  }