code.vegaprotocol.io/vega@v0.79.0/core/execution/common/market_activity_tracker_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 common_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	bmocks "code.vegaprotocol.io/vega/core/broker/mocks"
    25  	"code.vegaprotocol.io/vega/core/execution/common"
    26  	"code.vegaprotocol.io/vega/core/execution/common/mocks"
    27  	"code.vegaprotocol.io/vega/core/integration/stubs"
    28  	snp "code.vegaprotocol.io/vega/core/snapshot"
    29  	"code.vegaprotocol.io/vega/core/stats"
    30  	"code.vegaprotocol.io/vega/core/types"
    31  	"code.vegaprotocol.io/vega/libs/num"
    32  	"code.vegaprotocol.io/vega/libs/proto"
    33  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    34  	vgtest "code.vegaprotocol.io/vega/libs/test"
    35  	"code.vegaprotocol.io/vega/logging"
    36  	"code.vegaprotocol.io/vega/paths"
    37  	vgproto "code.vegaprotocol.io/vega/protos/vega"
    38  	snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    39  
    40  	"github.com/golang/mock/gomock"
    41  	"github.com/stretchr/testify/assert"
    42  	"github.com/stretchr/testify/require"
    43  )
    44  
    45  type TestEpochEngine struct {
    46  	target func(context.Context, types.Epoch)
    47  }
    48  
    49  func (e *TestEpochEngine) NotifyOnEpoch(f func(context.Context, types.Epoch), _ func(context.Context, types.Epoch)) {
    50  	e.target = f
    51  }
    52  
    53  type EligibilityChecker struct{}
    54  
    55  func (e *EligibilityChecker) IsEligibleForProposerBonus(marketID string, volumeTraded *num.Uint) bool {
    56  	return volumeTraded.GT(num.NewUint(5000))
    57  }
    58  
    59  func TestMarketTracker(t *testing.T) {
    60  	ctrl := gomock.NewController(t)
    61  	teams := mocks.NewMockTeams(ctrl)
    62  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
    63  
    64  	broker := bmocks.NewMockBroker(ctrl)
    65  	collateralService := mocks.NewMockCollateral(ctrl)
    66  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
    67  	tracker.SetEligibilityChecker(&EligibilityChecker{})
    68  
    69  	tracker.MarketProposed("asset1", "market1", "me")
    70  	tracker.MarketProposed("asset1", "market2", "me2")
    71  
    72  	assert.True(t, tracker.MarketTrackedForAsset("market1", "asset1"))
    73  	assert.False(t, tracker.MarketTrackedForAsset("market1", "asset2"))
    74  
    75  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
    76  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
    77  
    78  	tracker.AddValueTraded("asset1", "market1", num.NewUint(1000))
    79  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
    80  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
    81  
    82  	tracker.AddValueTraded("asset1", "market2", num.NewUint(4000))
    83  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
    84  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
    85  
    86  	tracker.AddValueTraded("asset1", "market2", num.NewUint(1001))
    87  	tracker.AddValueTraded("asset1", "market1", num.NewUint(4001))
    88  
    89  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
    90  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
    91  
    92  	// mark as paid
    93  	tracker.MarkPaidProposer("asset1", "market1", "VEGA", []string{}, "zohar")
    94  	tracker.MarkPaidProposer("asset1", "market2", "VEGA", []string{}, "zohar")
    95  
    96  	// check if eligible for the same combo, expect false
    97  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
    98  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
    99  
   100  	// now check for another funder
   101  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "jeremy"))
   102  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "jeremy"))
   103  
   104  	// mark as paid
   105  	tracker.MarkPaidProposer("asset1", "market1", "VEGA", []string{}, "jeremy")
   106  	tracker.MarkPaidProposer("asset1", "market2", "VEGA", []string{}, "jeremy")
   107  
   108  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "jeremy"))
   109  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "jeremy"))
   110  
   111  	// check for another payout asset
   112  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{}, "zohar"))
   113  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{}, "zohar"))
   114  
   115  	tracker.MarkPaidProposer("asset1", "market1", "USDC", []string{}, "zohar")
   116  	tracker.MarkPaidProposer("asset1", "market2", "USDC", []string{}, "zohar")
   117  
   118  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{}, "zohar"))
   119  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{}, "zohar"))
   120  
   121  	// check for another market scope
   122  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{"market1"}, "zohar"))
   123  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{"market2"}, "zohar"))
   124  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{"market1", "market2"}, "zohar"))
   125  	require.Equal(t, true, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{"market2", "market2"}, "zohar"))
   126  
   127  	tracker.MarkPaidProposer("asset1", "market1", "USDC", []string{"market1"}, "zohar")
   128  	tracker.MarkPaidProposer("asset1", "market2", "USDC", []string{"market2"}, "zohar")
   129  	tracker.MarkPaidProposer("asset1", "market1", "USDC", []string{"market1", "market2"}, "zohar")
   130  	tracker.MarkPaidProposer("asset1", "market2", "USDC", []string{"market1", "market2"}, "zohar")
   131  
   132  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{"market1"}, "zohar"))
   133  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{"market2"}, "zohar"))
   134  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{"market1", "market2"}, "zohar"))
   135  	require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{"market1", "market2"}, "zohar"))
   136  
   137  	// take a snapshot
   138  	key := (&types.PayloadMarketActivityTracker{}).Key()
   139  	state1, _, err := tracker.GetState(key)
   140  	require.NoError(t, err)
   141  	teams2 := mocks.NewMockTeams(ctrl)
   142  	balanceChecker2 := mocks.NewMockAccountBalanceChecker(ctrl)
   143  	broker = bmocks.NewMockBroker(ctrl)
   144  	trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams2, balanceChecker2, broker, collateralService)
   145  	pl := snapshotpb.Payload{}
   146  	require.NoError(t, proto.Unmarshal(state1, &pl))
   147  
   148  	additionalProvider, err := trackerLoad.LoadState(context.Background(), types.PayloadFromProto(&pl))
   149  	require.NoError(t, err)
   150  	assert.Nil(t, additionalProvider)
   151  
   152  	state2, _, err := trackerLoad.GetState(key)
   153  	require.NoError(t, err)
   154  	require.True(t, bytes.Equal(state1, state2))
   155  }
   156  
   157  func TestRemoveMarket(t *testing.T) {
   158  	epochService := &TestEpochEngine{}
   159  	ctrl := gomock.NewController(t)
   160  	teams := mocks.NewMockTeams(ctrl)
   161  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   162  
   163  	broker := bmocks.NewMockBroker(ctrl)
   164  	collateralService := mocks.NewMockCollateral(ctrl)
   165  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   166  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   167  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   168  	tracker.MarketProposed("asset1", "market1", "me")
   169  	tracker.MarketProposed("asset1", "market2", "me2")
   170  	require.Equal(t, 2, len(tracker.GetAllMarketIDs()))
   171  	require.Equal(t, "market1", tracker.GetAllMarketIDs()[0])
   172  	require.Equal(t, "market2", tracker.GetAllMarketIDs()[1])
   173  
   174  	// remove the market - this should only mark the market for removal
   175  	tracker.RemoveMarket("asset1", "market1")
   176  	require.Equal(t, 2, len(tracker.GetAllMarketIDs()))
   177  	require.Equal(t, "market1", tracker.GetAllMarketIDs()[0])
   178  	require.Equal(t, "market2", tracker.GetAllMarketIDs()[1])
   179  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START})
   180  
   181  	require.Equal(t, 1, len(tracker.GetAllMarketIDs()))
   182  	require.Equal(t, "market2", tracker.GetAllMarketIDs()[0])
   183  }
   184  
   185  func TestAddRemoveAMM(t *testing.T) {
   186  	epochService := &TestEpochEngine{}
   187  	ctrl := gomock.NewController(t)
   188  	teams := mocks.NewMockTeams(ctrl)
   189  	broker := bmocks.NewMockBroker(ctrl)
   190  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   191  
   192  	collateralService := mocks.NewMockCollateral(ctrl)
   193  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   194  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   195  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   196  	tracker.MarketProposed("asset1", "market1", "me")
   197  	tracker.MarketProposed("asset1", "market2", "me2")
   198  	require.Equal(t, 2, len(tracker.GetAllMarketIDs()))
   199  	require.Equal(t, "market1", tracker.GetAllMarketIDs()[0])
   200  	require.Equal(t, "market2", tracker.GetAllMarketIDs()[1])
   201  
   202  	tracker.AddAMMSubAccount("asset1", "market1", "sub1")
   203  	tracker.AddAMMSubAccount("asset1", "market1", "sub2")
   204  
   205  	require.Equal(t, map[string]struct{}{"sub1": {}, "sub2": {}}, tracker.GetAllAMMParties("asset1", nil))
   206  
   207  	tracker.RemoveAMMParty("asset1", "market1", "sub2")
   208  	require.Equal(t, map[string]struct{}{"sub1": {}}, tracker.GetAllAMMParties("asset1", nil))
   209  
   210  	tracker.RemoveAMMParty("asset1", "market1", "sub1")
   211  	require.Equal(t, map[string]struct{}{}, tracker.GetAllAMMParties("asset1", nil))
   212  }
   213  
   214  func TestCalculateTotalMakerContributionInQuantum(t *testing.T) {
   215  	// ctx := context.Background()
   216  	epochService := &TestEpochEngine{}
   217  	ctrl := gomock.NewController(t)
   218  	teams := mocks.NewMockTeams(ctrl)
   219  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   220  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
   221  	broker := bmocks.NewMockBroker(ctrl)
   222  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   223  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   224  	collateralService := mocks.NewMockCollateral(ctrl)
   225  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   226  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   227  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   228  	tracker.MarketProposed("asset1", "market1", "me")
   229  	tracker.MarketProposed("asset1", "market2", "me2")
   230  	tracker.MarketProposed("asset1", "market4", "me4")
   231  	tracker.MarketProposed("asset2", "market3", "me3")
   232  
   233  	collateralService.EXPECT().GetAssetQuantum("asset1").Return(num.DecimalOne(), nil).AnyTimes()
   234  	collateralService.EXPECT().GetAssetQuantum("asset2").Return(num.DecimalTwo(), nil).AnyTimes()
   235  	// no fees generated expect empty slice
   236  	epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   237  	epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   238  
   239  	// expect no makers fee contributors
   240  	contribution, fraction := tracker.CalculateTotalMakerContributionInQuantum(1)
   241  	require.Equal(t, 0, len(fraction))
   242  	require.Equal(t, 0, len(contribution))
   243  
   244  	contribution, fraction = tracker.CalculateTotalMakerContributionInQuantum(2)
   245  	require.Equal(t, 0, len(fraction))
   246  	require.Equal(t, 0, len(contribution))
   247  
   248  	epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   249  
   250  	// now lets get some fees paid
   251  	// update with a few transfers
   252  	transfersM1 := []*types.Transfer{
   253  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
   254  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}},
   255  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}},
   256  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   257  	}
   258  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   259  
   260  	transfersM2 := []*types.Transfer{
   261  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(500)}},
   262  	}
   263  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   264  
   265  	transfersM3 := []*types.Transfer{
   266  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(450)}},
   267  	}
   268  	tracker.UpdateFeesFromTransfers("asset2", "market3", transfersM3)
   269  	epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   270  
   271  	contribution, fraction = tracker.CalculateTotalMakerContributionInQuantum(1)
   272  	// party 1 received in total:
   273  	// asset1 : 1000
   274  	// party 2 received in total:
   275  	// asset1 : 1500
   276  	// asset2: 450/2 = 225
   277  	require.Equal(t, 2, len(fraction))
   278  	require.Equal(t, 2, len(contribution))
   279  	require.Equal(t, "1000", contribution["party1"].String())
   280  	require.Equal(t, "1725", contribution["party2"].String())
   281  	require.Equal(t, "0.3669724770642202", fraction["party1"].String())
   282  	require.Equal(t, "0.6330275229357798", fraction["party2"].String())
   283  
   284  	epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   285  
   286  	transfersM4 := []*types.Transfer{
   287  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(2000)}},
   288  	}
   289  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM4)
   290  
   291  	transfersM5 := []*types.Transfer{
   292  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(550)}},
   293  	}
   294  	tracker.UpdateFeesFromTransfers("asset2", "market3", transfersM5)
   295  	epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   296  
   297  	// party 1 received in total this epoch:
   298  	// asset1 : 2000
   299  	// party 2 received in total:
   300  	// asset2: 550/2 = 275
   301  	// in total for both epochs:
   302  	// party1 = 3000
   303  	// party2 = 2000
   304  	contribution, fraction = tracker.CalculateTotalMakerContributionInQuantum(2)
   305  	require.Equal(t, 2, len(fraction))
   306  	require.Equal(t, 2, len(contribution))
   307  	require.Equal(t, "3000", contribution["party1"].String())
   308  	require.Equal(t, "2000", contribution["party2"].String())
   309  	require.Equal(t, "0.6", fraction["party1"].String())
   310  	require.Equal(t, "0.4", fraction["party2"].String())
   311  }
   312  
   313  func TestGetScores(t *testing.T) {
   314  	ctx := context.Background()
   315  	epochService := &TestEpochEngine{}
   316  	ctrl := gomock.NewController(t)
   317  	teams := mocks.NewMockTeams(ctrl)
   318  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   319  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
   320  	broker := bmocks.NewMockBroker(ctrl)
   321  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   322  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   323  	collateralService := mocks.NewMockCollateral(ctrl)
   324  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   325  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   326  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   327  	tracker.MarketProposed("asset1", "market1", "me")
   328  	tracker.MarketProposed("asset1", "market2", "me2")
   329  	tracker.MarketProposed("asset1", "market4", "me4")
   330  	tracker.MarketProposed("asset2", "market3", "me3")
   331  
   332  	// no fees generated expect empty slice
   333  	epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   334  	epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   335  
   336  	// asset1, asset2 no market scoping
   337  
   338  	for _, asset := range []string{"asset1", "asset2"} {
   339  		for _, m := range []vgproto.DispatchMetric{vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED} {
   340  			scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: asset, Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
   341  			require.Equal(t, 0, len(scores))
   342  		}
   343  	}
   344  
   345  	// asset1 one market in scope
   346  	for _, market := range []string{"market1", "market2", "market4"} {
   347  		for _, m := range []vgproto.DispatchMetric{vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED} {
   348  			scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{market}})
   349  			require.Equal(t, 0, len(scores))
   350  		}
   351  	}
   352  
   353  	// asset2 one market in scope
   354  	for _, m := range []vgproto.DispatchMetric{vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED} {
   355  		scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market3"}})
   356  		require.Equal(t, 0, len(scores))
   357  	}
   358  
   359  	epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   360  
   361  	// update with a few transfers
   362  	transfersM1 := []*types.Transfer{
   363  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
   364  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   365  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   366  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}},
   367  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(300)}},
   368  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   369  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}},
   370  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
   371  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(700)}},
   372  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   373  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   374  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1000)}},
   375  	}
   376  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   377  
   378  	transfersM2 := []*types.Transfer{
   379  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(500)}},
   380  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1500)}},
   381  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1500)}},
   382  	}
   383  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   384  
   385  	transfersM3 := []*types.Transfer{
   386  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(500)}},
   387  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(450)}},
   388  	}
   389  	tracker.UpdateFeesFromTransfers("asset2", "market3", transfersM3)
   390  
   391  	epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   392  
   393  	// looking across all markets in asset 1 with window length 1:
   394  	// party1: 800
   395  	// partt2: 3200
   396  	// total = 4000
   397  	// party1 = 800/4000 = 0.2
   398  	// party2 = 3200/4000 = 0.8
   399  	scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
   400  	require.Equal(t, 2, len(scores))
   401  
   402  	require.Equal(t, "party1", scores[0].Party)
   403  	require.Equal(t, "0.2", scores[0].Score.String())
   404  	require.Equal(t, "party2", scores[1].Party)
   405  	require.Equal(t, "0.8", scores[1].Score.String())
   406  
   407  	// now look only on market 1:
   408  	// party1 = 800/2500 = 0.32
   409  	// partt2 = 1700/2500 = 0.68
   410  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market1"}})
   411  	require.Equal(t, 2, len(scores))
   412  
   413  	require.Equal(t, "party1", scores[0].Party)
   414  	require.Equal(t, "0.32", scores[0].Score.String())
   415  	require.Equal(t, "party2", scores[1].Party)
   416  	require.Equal(t, "0.68", scores[1].Score.String())
   417  
   418  	// now look only on market 2:
   419  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market2"}})
   420  	require.Equal(t, 2, len(scores))
   421  	require.Equal(t, "party2", scores[1].Party)
   422  	require.Equal(t, "1", scores[1].Score.String())
   423  	require.Equal(t, true, scores[1].IsEligible)
   424  	require.Equal(t, "party1", scores[0].Party)
   425  	require.Equal(t, false, scores[0].IsEligible)
   426  	require.Equal(t, "0", scores[0].Score.String())
   427  
   428  	// now look at asset2 with no market qualifer
   429  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset2", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
   430  	require.Equal(t, 2, len(scores))
   431  	require.Equal(t, "party1", scores[0].Party)
   432  	require.Equal(t, "1", scores[0].Score.String())
   433  	require.Equal(t, true, scores[0].IsEligible)
   434  	require.Equal(t, "party2", scores[1].Party)
   435  	require.Equal(t, "0", scores[1].Score.String())
   436  	require.Equal(t, false, scores[1].IsEligible)
   437  
   438  	epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   439  	transfersM1 = []*types.Transfer{
   440  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1200)}},
   441  	}
   442  	transfersM2 = []*types.Transfer{
   443  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
   444  	}
   445  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   446  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   447  	epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   448  
   449  	// looking across all markets in asset 1 with window length 2:
   450  	// party1: 800 + 1200 = 2000
   451  	// partt2: 3200 + 800 = 4000
   452  	// total = 4000 + 2000 = 6000
   453  	// party1 = 2000/6000 = 1/3
   454  	// party2 = 4000/6000 = 2/3
   455  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
   456  	require.Equal(t, 2, len(scores))
   457  
   458  	require.Equal(t, "party1", scores[0].Party)
   459  	require.Equal(t, "0.3333333333333333", scores[0].Score.String())
   460  	require.Equal(t, "party2", scores[1].Party)
   461  	require.Equal(t, "0.6666666666666667", scores[1].Score.String())
   462  }
   463  
   464  func TestGetScoresIndividualsDifferentScopes(t *testing.T) {
   465  	ctx := context.Background()
   466  	epochService := &TestEpochEngine{}
   467  	ctrl := gomock.NewController(t)
   468  	teams := mocks.NewMockTeams(ctrl)
   469  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   470  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
   471  	broker := bmocks.NewMockBroker(ctrl)
   472  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   473  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   474  	collateralService := mocks.NewMockCollateral(ctrl)
   475  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   476  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   477  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   478  	tracker.MarketProposed("asset1", "market1", "me")
   479  	tracker.MarketProposed("asset1", "market2", "me2")
   480  	tracker.MarketProposed("asset1", "market4", "me4")
   481  	tracker.MarketProposed("asset2", "market3", "me3")
   482  
   483  	// no fees generated expect empty slice
   484  	epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   485  	epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   486  
   487  	// asset1, asset2 no market scoping
   488  
   489  	for _, asset := range []string{"asset1", "asset2"} {
   490  		for _, m := range []vgproto.DispatchMetric{vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED} {
   491  			scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: asset, Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
   492  			require.Equal(t, 0, len(scores))
   493  		}
   494  	}
   495  
   496  	// asset1 one market in scope
   497  	for _, market := range []string{"market1", "market2", "market4"} {
   498  		for _, m := range []vgproto.DispatchMetric{vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED} {
   499  			scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{market}})
   500  			require.Equal(t, 0, len(scores))
   501  		}
   502  	}
   503  
   504  	// asset2 one market in scope
   505  	for _, m := range []vgproto.DispatchMetric{vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED} {
   506  		scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market3"}})
   507  		require.Equal(t, 0, len(scores))
   508  	}
   509  
   510  	epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   511  
   512  	tracker.AddAMMSubAccount("asset1", "market1", "party1")
   513  
   514  	// update with a few transfers
   515  	transfersM1 := []*types.Transfer{
   516  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
   517  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   518  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   519  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}},
   520  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(300)}},
   521  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   522  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}},
   523  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
   524  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(700)}},
   525  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   526  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   527  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1000)}},
   528  	}
   529  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   530  
   531  	transfersM2 := []*types.Transfer{
   532  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(500)}},
   533  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1500)}},
   534  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1500)}},
   535  	}
   536  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   537  
   538  	transfersM3 := []*types.Transfer{
   539  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(500)}},
   540  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(450)}},
   541  	}
   542  	tracker.UpdateFeesFromTransfers("asset2", "market3", transfersM3)
   543  
   544  	epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   545  
   546  	// looking across all markets in asset 1 with window length 1:
   547  	// party1: 800
   548  	// partt2: 3200
   549  	// total = 4000
   550  	// party1 = 800/4000 = 0.2
   551  	// party2 = 3200/4000 = 0.8
   552  	scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
   553  	require.Equal(t, 2, len(scores))
   554  
   555  	require.Equal(t, "party1", scores[0].Party)
   556  	require.Equal(t, "0.2", scores[0].Score.String())
   557  	require.Equal(t, "party2", scores[1].Party)
   558  	require.Equal(t, "0.8", scores[1].Score.String())
   559  
   560  	// looking across all markets in asset 1 with window length 1 and AMM scope:
   561  	// party1: 800
   562  	// partt2: 3200
   563  	// total = 4000
   564  	// party1 = 800/4000 = 0.2
   565  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_AMM, WindowLength: 1})
   566  	require.Equal(t, 1, len(scores))
   567  
   568  	require.Equal(t, "party1", scores[0].Party)
   569  	require.Equal(t, "0.2", scores[0].Score.String())
   570  
   571  	// now look only on market 1:
   572  	// party1 = 800/2500 = 0.32
   573  	// partt2 = 1700/2500 = 0.68
   574  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market1"}})
   575  	require.Equal(t, 2, len(scores))
   576  
   577  	require.Equal(t, "party1", scores[0].Party)
   578  	require.Equal(t, "0.32", scores[0].Score.String())
   579  	require.Equal(t, "party2", scores[1].Party)
   580  	require.Equal(t, "0.68", scores[1].Score.String())
   581  
   582  	// now look only on market 2:
   583  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market2"}})
   584  	require.Equal(t, 2, len(scores))
   585  
   586  	require.Equal(t, "party1", scores[0].Party)
   587  	require.Equal(t, "0", scores[0].Score.String())
   588  	require.Equal(t, false, scores[0].IsEligible)
   589  	require.Equal(t, "party2", scores[1].Party)
   590  	require.Equal(t, "1", scores[1].Score.String())
   591  	require.Equal(t, true, scores[1].IsEligible)
   592  
   593  	// now look at asset2 with no market qualifer
   594  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset2", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
   595  	require.Equal(t, 2, len(scores))
   596  	require.Equal(t, "party1", scores[0].Party)
   597  	require.Equal(t, true, scores[0].IsEligible)
   598  	require.Equal(t, "1", scores[0].Score.String())
   599  	require.Equal(t, "party2", scores[1].Party)
   600  	require.Equal(t, "0", scores[1].Score.String())
   601  	require.Equal(t, false, scores[1].IsEligible)
   602  
   603  	epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   604  	transfersM1 = []*types.Transfer{
   605  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1200)}},
   606  	}
   607  	transfersM2 = []*types.Transfer{
   608  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
   609  	}
   610  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   611  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   612  	epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   613  
   614  	// looking across all markets in asset 1 with window length 2:
   615  	// party1: 800 + 1200 = 2000
   616  	// partt2: 3200 + 800 = 4000
   617  	// total = 4000 + 2000 = 6000
   618  	// party1 = 2000/6000 = 1/3
   619  	// party2 = 4000/6000 = 2/3
   620  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
   621  	require.Equal(t, 2, len(scores))
   622  
   623  	require.Equal(t, "party1", scores[0].Party)
   624  	require.Equal(t, "0.3333333333333333", scores[0].Score.String())
   625  	require.Equal(t, "party2", scores[1].Party)
   626  	require.Equal(t, "0.6666666666666667", scores[1].Score.String())
   627  }
   628  
   629  func TestMarketTrackerStateChange(t *testing.T) {
   630  	key := (&types.PayloadMarketActivityTracker{}).Key()
   631  
   632  	ctrl := gomock.NewController(t)
   633  	teams := mocks.NewMockTeams(ctrl)
   634  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   635  	broker := bmocks.NewMockBroker(ctrl)
   636  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   637  	collateralService := mocks.NewMockCollateral(ctrl)
   638  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   639  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   640  
   641  	state1, _, err := tracker.GetState(key)
   642  	require.NoError(t, err)
   643  
   644  	tracker.MarketProposed("asset1", "market1", "me")
   645  	tracker.MarketProposed("asset1", "market2", "me2")
   646  
   647  	state2, _, err := tracker.GetState(key)
   648  	require.NoError(t, err)
   649  	require.False(t, bytes.Equal(state1, state2))
   650  
   651  	tracker.AddValueTraded("asset1", "market1", num.NewUint(1000))
   652  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
   653  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
   654  
   655  	state3, _, err := tracker.GetState(key)
   656  	require.NoError(t, err)
   657  	require.False(t, bytes.Equal(state1, state3))
   658  }
   659  
   660  func TestFeesTrackerWith0(t *testing.T) {
   661  	epochEngine := &TestEpochEngine{}
   662  	ctx := context.Background()
   663  	ctrl := gomock.NewController(t)
   664  	teams := mocks.NewMockTeams(ctrl)
   665  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   666  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
   667  	broker := bmocks.NewMockBroker(ctrl)
   668  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   669  	collateralService := mocks.NewMockCollateral(ctrl)
   670  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   671  	epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   672  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   673  
   674  	tracker.MarketProposed("asset1", "market1", "me")
   675  	transfersM1 := []*types.Transfer{
   676  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   677  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   678  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   679  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   680  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   681  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   682  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   683  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   684  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   685  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   686  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   687  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.UintZero()}},
   688  	}
   689  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   690  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   691  	scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market1"}})
   692  	require.Equal(t, 2, len(scores))
   693  	require.Equal(t, false, scores[0].IsEligible)
   694  	require.Equal(t, false, scores[1].IsEligible)
   695  }
   696  
   697  func TestGetLastEpochTakeFees(t *testing.T) {
   698  	epochEngine := &TestEpochEngine{}
   699  	ctx := context.Background()
   700  	ctrl := gomock.NewController(t)
   701  	teams := mocks.NewMockTeams(ctrl)
   702  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   703  	broker := bmocks.NewMockBroker(ctrl)
   704  	collateralService := mocks.NewMockCollateral(ctrl)
   705  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   706  	epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   707  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   708  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   709  
   710  	partyScores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3})
   711  	require.Equal(t, 0, len(partyScores))
   712  
   713  	tracker.MarketProposed("asset1", "market1", "me")
   714  	tracker.MarketProposed("asset1", "market2", "me2")
   715  
   716  	// update with a few transfers
   717  	transfersM1 := []*types.Transfer{
   718  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
   719  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   720  		{Owner: "party1", Type: types.TransferTypeInfrastructureFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(110)}},
   721  		{Owner: "party1", Type: types.TransferTypeLiquidityFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(10)}},
   722  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   723  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}},
   724  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(300)}},
   725  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   726  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}},
   727  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
   728  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(700)}},
   729  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   730  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   731  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1000)}},
   732  	}
   733  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   734  
   735  	transfersM2 := []*types.Transfer{
   736  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(150)}},
   737  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(150)}},
   738  	}
   739  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   740  
   741  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   742  
   743  	m1 := tracker.GetLastEpochTakeFees("asset1", []string{"market1"}, 1)
   744  	require.Equal(t, 2, len(m1))
   745  	require.Equal(t, "620", m1["party1"].String())
   746  	require.Equal(t, "1000", m1["party2"].String())
   747  	m2 := tracker.GetLastEpochTakeFees("asset1", []string{"market2"}, 1)
   748  	require.Equal(t, 1, len(m2))
   749  	require.Equal(t, "150", m2["party2"].String())
   750  
   751  	mAll := tracker.GetLastEpochTakeFees("asset1", []string{"market1", "market2"}, 1)
   752  	require.Equal(t, "620", mAll["party1"].String())
   753  	require.Equal(t, "1150", mAll["party2"].String())
   754  
   755  	mNoMarkets := tracker.GetLastEpochTakeFees("asset1", []string{}, 1)
   756  	require.Equal(t, "620", mNoMarkets["party1"].String())
   757  	require.Equal(t, "1150", mNoMarkets["party2"].String())
   758  
   759  	require.Equal(t, mAll, mNoMarkets)
   760  }
   761  
   762  func TestGetLastEpochTakeFeesMultiEpochWindow(t *testing.T) {
   763  	epochEngine := &TestEpochEngine{}
   764  	ctx := context.Background()
   765  	ctrl := gomock.NewController(t)
   766  	teams := mocks.NewMockTeams(ctrl)
   767  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   768  	broker := bmocks.NewMockBroker(ctrl)
   769  	collateralService := mocks.NewMockCollateral(ctrl)
   770  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   771  	epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   772  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   773  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   774  
   775  	partyScores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3})
   776  	require.Equal(t, 0, len(partyScores))
   777  
   778  	tracker.MarketProposed("asset1", "market1", "me")
   779  	tracker.MarketProposed("asset1", "market2", "me2")
   780  
   781  	// update with a few transfers
   782  	transfersM1 := []*types.Transfer{
   783  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
   784  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   785  		{Owner: "party1", Type: types.TransferTypeInfrastructureFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(110)}},
   786  		{Owner: "party1", Type: types.TransferTypeLiquidityFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(10)}},
   787  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   788  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}},
   789  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(300)}},
   790  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   791  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}},
   792  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
   793  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(700)}},
   794  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   795  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   796  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1000)}},
   797  	}
   798  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   799  
   800  	transfersM2 := []*types.Transfer{
   801  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(150)}},
   802  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(150)}},
   803  	}
   804  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   805  
   806  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   807  	epochEngine.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   808  
   809  	// double the fees for a window of 2
   810  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   811  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   812  
   813  	epochEngine.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   814  	m1 := tracker.GetLastEpochTakeFees("asset1", []string{"market1"}, 1)
   815  	require.Equal(t, 2, len(m1))
   816  	require.Equal(t, "620", m1["party1"].String())
   817  	require.Equal(t, "1000", m1["party2"].String())
   818  
   819  	m1 = tracker.GetLastEpochTakeFees("asset1", []string{"market1"}, 2)
   820  	require.Equal(t, 2, len(m1))
   821  	require.Equal(t, "1240", m1["party1"].String())
   822  	require.Equal(t, "2000", m1["party2"].String())
   823  
   824  	m2 := tracker.GetLastEpochTakeFees("asset1", []string{"market2"}, 1)
   825  	require.Equal(t, 1, len(m2))
   826  	require.Equal(t, "150", m2["party2"].String())
   827  
   828  	m2 = tracker.GetLastEpochTakeFees("asset1", []string{"market2"}, 2)
   829  	require.Equal(t, 1, len(m2))
   830  	require.Equal(t, "300", m2["party2"].String())
   831  
   832  	mAll := tracker.GetLastEpochTakeFees("asset1", []string{"market1", "market2"}, 1)
   833  	require.Equal(t, "620", mAll["party1"].String())
   834  	require.Equal(t, "1150", mAll["party2"].String())
   835  
   836  	mAll = tracker.GetLastEpochTakeFees("asset1", []string{"market1", "market2"}, 2)
   837  	require.Equal(t, "1240", mAll["party1"].String())
   838  	require.Equal(t, "2300", mAll["party2"].String())
   839  
   840  	mNoMarkets := tracker.GetLastEpochTakeFees("asset1", []string{}, 1)
   841  	require.Equal(t, "620", mNoMarkets["party1"].String())
   842  	require.Equal(t, "1150", mNoMarkets["party2"].String())
   843  
   844  	mNoMarkets = tracker.GetLastEpochTakeFees("asset1", []string{}, 2)
   845  	require.Equal(t, "1240", mNoMarkets["party1"].String())
   846  	require.Equal(t, "2300", mNoMarkets["party2"].String())
   847  	require.Equal(t, mAll, mNoMarkets)
   848  }
   849  
   850  func TestFeesTracker(t *testing.T) {
   851  	epochEngine := &TestEpochEngine{}
   852  	ctx := context.Background()
   853  	ctrl := gomock.NewController(t)
   854  	teams := mocks.NewMockTeams(ctrl)
   855  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
   856  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
   857  	broker := bmocks.NewMockBroker(ctrl)
   858  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   859  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   860  	collateralService := mocks.NewMockCollateral(ctrl)
   861  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   862  	epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
   863  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   864  	tracker.SetEligibilityChecker(&EligibilityChecker{})
   865  
   866  	partyScores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3})
   867  	require.Equal(t, 0, len(partyScores))
   868  
   869  	key := (&types.PayloadMarketActivityTracker{}).Key()
   870  	state1, _, err := tracker.GetState(key)
   871  	require.NoError(t, err)
   872  
   873  	tracker.MarketProposed("asset1", "market1", "me")
   874  	tracker.MarketProposed("asset1", "market2", "me2")
   875  
   876  	// update with a few transfers
   877  	transfersM1 := []*types.Transfer{
   878  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
   879  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   880  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   881  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}},
   882  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(300)}},
   883  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   884  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}},
   885  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
   886  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(700)}},
   887  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
   888  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
   889  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1000)}},
   890  	}
   891  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
   892  
   893  	transfersM2 := []*types.Transfer{
   894  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(150)}},
   895  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(150)}},
   896  	}
   897  	tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2)
   898  
   899  	epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   900  	epochEngine.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START})
   901  
   902  	// asset1, types.TransferTypeMakerFeeReceive
   903  	// party1 received 500
   904  	// party2 received 1500
   905  	scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market1"}})
   906  	require.Equal(t, 2, len(scores))
   907  	require.Equal(t, "0.25", scores[0].Score.String())
   908  	require.Equal(t, "party1", scores[0].Party)
   909  	require.Equal(t, "0.75", scores[1].Score.String())
   910  	require.Equal(t, "party2", scores[1].Party)
   911  
   912  	// asset1 TransferTypeMakerFeePay
   913  	// party1 paid 500
   914  	// party2 paid 1000
   915  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market1"}})
   916  	require.Equal(t, 2, len(scores))
   917  	require.Equal(t, "0.3333333333333333", scores[0].Score.String())
   918  	require.Equal(t, "party1", scores[0].Party)
   919  	require.Equal(t, "0.6666666666666667", scores[1].Score.String())
   920  	require.Equal(t, "party2", scores[1].Party)
   921  
   922  	// asset1 TransferTypeLiquidityFeeNetDistribute
   923  	// party1 paid 800
   924  	// party2 paid 1700
   925  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market1"}})
   926  	require.Equal(t, 2, len(scores))
   927  	require.Equal(t, "0.32", scores[0].Score.String())
   928  	require.Equal(t, "party1", scores[0].Party)
   929  	require.Equal(t, "0.68", scores[1].Score.String())
   930  	require.Equal(t, "party2", scores[1].Party)
   931  
   932  	// asset2 TransferTypeMakerFeePay
   933  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market2"}})
   934  	require.Equal(t, 2, len(scores))
   935  	require.Equal(t, "1", scores[0].Score.String())
   936  	require.Equal(t, "party1", scores[0].Party)
   937  	require.Equal(t, true, scores[0].IsEligible)
   938  	require.Equal(t, "0", scores[1].Score.String())
   939  	require.Equal(t, "party2", scores[1].Party)
   940  	require.Equal(t, false, scores[1].IsEligible)
   941  
   942  	// asset2 TransferTypeMakerFeePay
   943  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market2"}})
   944  	require.Equal(t, 2, len(scores))
   945  	require.Equal(t, "0", scores[0].Score.String())
   946  	require.Equal(t, "party1", scores[0].Party)
   947  	require.Equal(t, false, scores[0].IsEligible)
   948  	require.Equal(t, "1", scores[1].Score.String())
   949  	require.Equal(t, "party2", scores[1].Party)
   950  	require.Equal(t, true, scores[1].IsEligible)
   951  
   952  	// check state has changed
   953  	state2, _, err := tracker.GetState(key)
   954  	require.NoError(t, err)
   955  	require.False(t, bytes.Equal(state1, state2))
   956  
   957  	epochEngineLoad := &TestEpochEngine{}
   958  	ctrl = gomock.NewController(t)
   959  	teams = mocks.NewMockTeams(ctrl)
   960  	balanceChecker = mocks.NewMockAccountBalanceChecker(ctrl)
   961  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
   962  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   963  	trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
   964  	epochEngineLoad.NotifyOnEpoch(trackerLoad.OnEpochEvent, trackerLoad.OnEpochRestore)
   965  
   966  	pl := snapshotpb.Payload{}
   967  	require.NoError(t, proto.Unmarshal(state2, &pl))
   968  
   969  	additionalProvider, err := trackerLoad.LoadState(context.Background(), types.PayloadFromProto(&pl))
   970  	require.NoError(t, err)
   971  	assert.Nil(t, additionalProvider)
   972  
   973  	state3, _, err := trackerLoad.GetState(key)
   974  	require.NoError(t, err)
   975  	require.True(t, bytes.Equal(state2, state3))
   976  
   977  	// check a restored party exist in the restored engine
   978  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market2"}})
   979  	require.Equal(t, 2, len(scores))
   980  	require.Equal(t, "1", scores[0].Score.String())
   981  	require.Equal(t, "party1", scores[0].Party)
   982  	require.Equal(t, true, scores[0].IsEligible)
   983  	require.Equal(t, "0", scores[1].Score.String())
   984  	require.Equal(t, "party2", scores[1].Party)
   985  	require.Equal(t, false, scores[1].IsEligible)
   986  
   987  	// end the epoch
   988  	epochEngineLoad.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_END})
   989  
   990  	// // NewEngine epoch should scrub the state an produce a difference hash
   991  	state4, _, err := trackerLoad.GetState(key)
   992  	require.NoError(t, err)
   993  	require.False(t, bytes.Equal(state3, state4))
   994  
   995  	// // new epoch, we expect the metrics to have been reset
   996  
   997  	metrics := []vgproto.DispatchMetric{vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vgproto.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED}
   998  	for _, m := range metrics {
   999  		scores = trackerLoad.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market1"}})
  1000  		require.Equal(t, 2, len(scores))
  1001  		require.Equal(t, "0", scores[0].Score.String())
  1002  		require.Equal(t, "party1", scores[0].Party)
  1003  		require.Equal(t, false, scores[0].IsEligible)
  1004  		require.Equal(t, "0", scores[1].Score.String())
  1005  		require.Equal(t, "party2", scores[1].Party)
  1006  		require.Equal(t, false, scores[1].IsEligible)
  1007  		scores = trackerLoad.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "asset1", Metric: m, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, Markets: []string{"market2"}})
  1008  		require.Equal(t, 2, len(scores))
  1009  		require.Equal(t, "0", scores[0].Score.String())
  1010  		require.Equal(t, "party1", scores[0].Party)
  1011  		require.Equal(t, false, scores[0].IsEligible)
  1012  		require.Equal(t, "0", scores[1].Score.String())
  1013  		require.Equal(t, "party2", scores[1].Party)
  1014  		require.Equal(t, false, scores[1].IsEligible)
  1015  	}
  1016  }
  1017  
  1018  func TestDecimalSerialisation(t *testing.T) {
  1019  	d := num.DecimalE()
  1020  	b, err := d.MarshalBinary()
  1021  	require.NoError(t, err)
  1022  	dd, err := num.UnmarshalBinaryDecimal(b)
  1023  	require.NoError(t, err)
  1024  	require.Equal(t, d, dd)
  1025  }
  1026  
  1027  func TestUintSerialisation(t *testing.T) {
  1028  	ui, _ := num.UintFromString("1000000000000000000", 10)
  1029  	b := ui.Bytes()
  1030  	bb := b[:]
  1031  	uiLoad := num.UintFromBytes(bb)
  1032  	require.Equal(t, ui, uiLoad)
  1033  }
  1034  
  1035  func TestSnapshot(t *testing.T) {
  1036  	tracker := setupDefaultTrackerForTest(t)
  1037  
  1038  	// take a snapshot
  1039  	key := (&types.PayloadMarketActivityTracker{}).Key()
  1040  	state1, _, err := tracker.GetState(key)
  1041  	require.NoError(t, err)
  1042  
  1043  	ctrl := gomock.NewController(t)
  1044  	teams := mocks.NewMockTeams(ctrl)
  1045  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1046  	broker := bmocks.NewMockBroker(ctrl)
  1047  	collateralService := mocks.NewMockCollateral(ctrl)
  1048  	trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1049  	pl := snapshotpb.Payload{}
  1050  	require.NoError(t, proto.Unmarshal(state1, &pl))
  1051  
  1052  	additionalProvider, err := trackerLoad.LoadState(context.Background(), types.PayloadFromProto(&pl))
  1053  	require.NoError(t, err)
  1054  	assert.Nil(t, additionalProvider)
  1055  
  1056  	state2, _, err := trackerLoad.GetState(key)
  1057  	require.NoError(t, err)
  1058  	require.True(t, bytes.Equal(state1, state2))
  1059  }
  1060  
  1061  func TestCheckpoint(t *testing.T) {
  1062  	tracker := setupDefaultTrackerForTest(t)
  1063  
  1064  	b, err := tracker.Checkpoint()
  1065  	require.NoError(t, err)
  1066  
  1067  	ctrl := gomock.NewController(t)
  1068  	teams := mocks.NewMockTeams(ctrl)
  1069  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1070  	broker := bmocks.NewMockBroker(ctrl)
  1071  	collateralService := mocks.NewMockCollateral(ctrl)
  1072  	trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1073  
  1074  	require.NoError(t, trackerLoad.Load(context.Background(), b))
  1075  
  1076  	bLoad, err := trackerLoad.Checkpoint()
  1077  	require.NoError(t, err)
  1078  	require.True(t, bytes.Equal(b, bLoad))
  1079  }
  1080  
  1081  func TestSnapshotRoundTripViaEngine(t *testing.T) {
  1082  	transfersM5 := []*types.Transfer{
  1083  		{Owner: "party3", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
  1084  		{Owner: "party3", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
  1085  		{Owner: "party3", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
  1086  	}
  1087  	transfersM6 := []*types.Transfer{
  1088  		{Owner: "party4", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(500)}},
  1089  		{Owner: "party4", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(1500)}},
  1090  		{Owner: "party4", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(1500)}},
  1091  	}
  1092  
  1093  	ctx := vgtest.VegaContext("chainid", 100)
  1094  	tracker1 := setupDefaultTrackerForTest(t)
  1095  	now := time.Now()
  1096  	log := logging.NewTestLogger()
  1097  	timeService := stubs.NewTimeStub()
  1098  	timeService.SetTime(now)
  1099  	statsData := stats.New(log, stats.NewDefaultConfig())
  1100  	config := snp.DefaultConfig()
  1101  	vegaPath := paths.New(t.TempDir())
  1102  
  1103  	snapshotEngine1, err := snp.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain)
  1104  	require.NoError(t, err)
  1105  	snapshotEngine1CloseFn := vgtest.OnlyOnce(snapshotEngine1.Close)
  1106  	defer snapshotEngine1CloseFn()
  1107  
  1108  	snapshotEngine1.AddProviders(tracker1)
  1109  
  1110  	require.NoError(t, snapshotEngine1.Start(ctx))
  1111  
  1112  	hash1, err := snapshotEngine1.SnapshotNow(ctx)
  1113  	require.NoError(t, err)
  1114  
  1115  	tracker1.SetEligibilityChecker(&EligibilityChecker{})
  1116  	tracker1.MarketProposed("asset1", "market5", "meeeee")
  1117  	tracker1.MarketProposed("asset2", "market6", "meeeeeee")
  1118  	tracker1.UpdateFeesFromTransfers("asset1", "market5", transfersM5)
  1119  	tracker1.UpdateFeesFromTransfers("asset2", "market6", transfersM6)
  1120  
  1121  	state1 := map[string][]byte{}
  1122  	for _, key := range tracker1.Keys() {
  1123  		state, additionalProvider, err := tracker1.GetState(key)
  1124  		require.NoError(t, err)
  1125  		assert.Empty(t, additionalProvider)
  1126  		state1[key] = state
  1127  	}
  1128  
  1129  	snapshotEngine1CloseFn()
  1130  
  1131  	ctrl := gomock.NewController(t)
  1132  	teams := mocks.NewMockTeams(ctrl)
  1133  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1134  	broker := bmocks.NewMockBroker(ctrl)
  1135  	collateralService := mocks.NewMockCollateral(ctrl)
  1136  	tracker2 := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1137  	snapshotEngine2, err := snp.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain)
  1138  	require.NoError(t, err)
  1139  	defer snapshotEngine2.Close()
  1140  
  1141  	snapshotEngine2.AddProviders(tracker2)
  1142  
  1143  	// This triggers the state restoration from the local snapshot.
  1144  	require.NoError(t, snapshotEngine2.Start(ctx))
  1145  
  1146  	// Comparing the hash after restoration, to ensure it produces the same result.
  1147  	hash2, _, _ := snapshotEngine2.Info()
  1148  	require.Equal(t, hash1, hash2)
  1149  
  1150  	tracker2.MarketProposed("asset1", "market5", "meeeee")
  1151  	tracker2.MarketProposed("asset2", "market6", "meeeeeee")
  1152  	tracker2.UpdateFeesFromTransfers("asset1", "market5", transfersM5)
  1153  	tracker2.UpdateFeesFromTransfers("asset2", "market6", transfersM6)
  1154  
  1155  	state2 := map[string][]byte{}
  1156  	for _, key := range tracker2.Keys() {
  1157  		state, additionalProvider, err := tracker2.GetState(key)
  1158  		require.NoError(t, err)
  1159  		assert.Empty(t, additionalProvider)
  1160  		state2[key] = state
  1161  	}
  1162  
  1163  	for key := range state1 {
  1164  		assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key)
  1165  	}
  1166  }
  1167  
  1168  func TestMarketProposerBonusScenarios(t *testing.T) {
  1169  	epochService := &TestEpochEngine{}
  1170  	ctrl := gomock.NewController(t)
  1171  	teams := mocks.NewMockTeams(ctrl)
  1172  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1173  	broker := bmocks.NewMockBroker(ctrl)
  1174  	collateralService := mocks.NewMockCollateral(ctrl)
  1175  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1176  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
  1177  	tracker.SetEligibilityChecker(&EligibilityChecker{})
  1178  
  1179  	// setup 4 market for settlement asset1 2 of them proposed by the same proposer, and 2 markets for settlement asset 2
  1180  	tracker.MarketProposed("asset1", "market1", "me")
  1181  	tracker.MarketProposed("asset1", "market2", "me")
  1182  	tracker.MarketProposed("asset1", "market3", "me2")
  1183  	tracker.MarketProposed("asset1", "market4", "me3")
  1184  	tracker.MarketProposed("asset2", "market5", "me")
  1185  	tracker.MarketProposed("asset2", "market6", "me2")
  1186  
  1187  	// no trading done so far so expect no one to be eligible for bonus
  1188  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
  1189  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset2", []string{}, "VEGA", "zohar", []string{})))
  1190  
  1191  	// market1 goes above the threshold only it should be eligible
  1192  	tracker.AddValueTraded("asset1", "market1", num.NewUint(5001))
  1193  	require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{})))
  1194  	require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me"})))
  1195  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"not me"})))
  1196  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1197  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1198  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1199  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1200  	tracker.MarkPaidProposer("asset1", "market1", "VEGA", []string{"market1", "market2", "market3"}, "zohar")
  1201  
  1202  	// now market 2 and 3 become eligible
  1203  	tracker.AddValueTraded("asset1", "market2", num.NewUint(5001))
  1204  	tracker.AddValueTraded("asset1", "market3", num.NewUint(5001))
  1205  	require.Equal(t, 2, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{})))
  1206  	require.Equal(t, 2, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me", "me2"})))
  1207  	require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me", "not me"})))
  1208  	require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me2", "not me"})))
  1209  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"not me"})))
  1210  
  1211  	// show that only markets 2 and 3 are now eligible with this combo
  1212  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1213  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1214  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1215  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1216  	tracker.MarkPaidProposer("asset1", "market2", "VEGA", []string{"market1", "market2", "market3"}, "zohar")
  1217  	tracker.MarkPaidProposer("asset1", "market3", "VEGA", []string{"market1", "market2", "market3"}, "zohar")
  1218  
  1219  	// now market4 goes above the threshold but no one gets paid by this combo
  1220  	tracker.AddValueTraded("asset1", "market4", num.NewUint(5001))
  1221  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{})))
  1222  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1223  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1224  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1225  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
  1226  
  1227  	// now "all" is funded by zohar
  1228  	require.Equal(t, 4, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
  1229  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
  1230  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
  1231  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{}, "zohar"))
  1232  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{}, "zohar"))
  1233  
  1234  	tracker.MarkPaidProposer("asset1", "market1", "VEGA", []string{}, "zohar")
  1235  	tracker.MarkPaidProposer("asset1", "market2", "VEGA", []string{}, "zohar")
  1236  	tracker.MarkPaidProposer("asset1", "market3", "VEGA", []string{}, "zohar")
  1237  	tracker.MarkPaidProposer("asset1", "market4", "VEGA", []string{}, "zohar")
  1238  
  1239  	// everyone were paid so next time no one is eligible
  1240  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
  1241  
  1242  	// a new market is proposed and gets over the limit
  1243  	tracker.MarketProposed("asset1", "market7", "mememe")
  1244  	tracker.AddValueTraded("asset1", "market7", num.NewUint(5001))
  1245  
  1246  	// only the new market should be eligible for the "all" combo funded by zohar
  1247  	require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
  1248  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
  1249  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
  1250  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{}, "zohar"))
  1251  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{}, "zohar"))
  1252  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market7", "VEGA", []string{}, "zohar"))
  1253  	tracker.MarkPaidProposer("asset1", "market7", "VEGA", []string{}, "zohar")
  1254  
  1255  	// check that they are no longer eligible for this combo of all
  1256  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
  1257  
  1258  	// check new combo
  1259  	require.Equal(t, 3, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "VEGA", "zohar", []string{})))
  1260  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
  1261  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
  1262  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
  1263  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
  1264  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market7", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
  1265  
  1266  	tracker.MarkPaidProposer("asset1", "market1", "VEGA", []string{"market1", "market3", "market7"}, "zohar")
  1267  	tracker.MarkPaidProposer("asset1", "market3", "VEGA", []string{"market1", "market3", "market7"}, "zohar")
  1268  	tracker.MarkPaidProposer("asset1", "market7", "VEGA", []string{"market1", "market3", "market7"}, "zohar")
  1269  
  1270  	// now that they're marked as paid check they're no longer eligible
  1271  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "VEGA", "zohar", []string{})))
  1272  
  1273  	// check new asset for the same combo
  1274  	require.Equal(t, 3, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "USDC", "zohar", []string{})))
  1275  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
  1276  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
  1277  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
  1278  	require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
  1279  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market7", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
  1280  
  1281  	tracker.MarkPaidProposer("asset1", "market1", "USDC", []string{"market1", "market3", "market7"}, "zohar")
  1282  	tracker.MarkPaidProposer("asset1", "market3", "USDC", []string{"market1", "market3", "market7"}, "zohar")
  1283  	tracker.MarkPaidProposer("asset1", "market7", "USDC", []string{"market1", "market3", "market7"}, "zohar")
  1284  
  1285  	// now that they're marked as paid check they're no longer eligible
  1286  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "USDC", "zohar", []string{})))
  1287  
  1288  	// check new funder for the all combo
  1289  	require.Equal(t, 5, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "jeremy", []string{})))
  1290  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "jeremy"))
  1291  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "jeremy"))
  1292  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{}, "jeremy"))
  1293  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{}, "jeremy"))
  1294  	require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market7", "VEGA", []string{}, "jeremy"))
  1295  
  1296  	tracker.MarkPaidProposer("asset1", "market1", "VEGA", []string{}, "jeremy")
  1297  	tracker.MarkPaidProposer("asset1", "market2", "VEGA", []string{}, "jeremy")
  1298  	tracker.MarkPaidProposer("asset1", "market3", "VEGA", []string{}, "jeremy")
  1299  	tracker.MarkPaidProposer("asset1", "market4", "VEGA", []string{}, "jeremy")
  1300  	tracker.MarkPaidProposer("asset1", "market7", "VEGA", []string{}, "jeremy")
  1301  	require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "jeremy", []string{})))
  1302  }
  1303  
  1304  func TestNotionalMetric(t *testing.T) {
  1305  	epochService := &TestEpochEngine{}
  1306  	ctx := context.Background()
  1307  	ctrl := gomock.NewController(t)
  1308  	teams := mocks.NewMockTeams(ctrl)
  1309  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1310  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
  1311  	broker := bmocks.NewMockBroker(ctrl)
  1312  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
  1313  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
  1314  	collateralService := mocks.NewMockCollateral(ctrl)
  1315  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1316  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
  1317  
  1318  	epochStartTime := time.Now()
  1319  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime})
  1320  	tracker.SetEligibilityChecker(&EligibilityChecker{})
  1321  	tracker.MarketProposed("a1", "m1", "p1")
  1322  
  1323  	// 100 seconds into the epoch record a position of 100
  1324  	tracker.RecordPosition("a1", "p1", "m1", 100, num.NewUint(1), num.DecimalOne(), epochStartTime.Add(100*time.Second))
  1325  
  1326  	// 200 seconds later record another position
  1327  	// pBar = 100 * 300/400 = 75
  1328  	tracker.RecordPosition("a1", "p1", "m1", -200, num.NewUint(2), num.DecimalOne(), epochStartTime.Add(400*time.Second))
  1329  
  1330  	// now end the epoch after 600 seconds
  1331  	// pBar = (1 - 600/1000) * 75 + 200 * 600/1000 = 150
  1332  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime, EndTime: epochStartTime.Add(1000 * time.Second)})
  1333  
  1334  	scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1335  	require.Equal(t, 1, len(scores))
  1336  	require.Equal(t, "p1", scores[0].Party)
  1337  	require.Equal(t, "0.000027", scores[0].Score.String())
  1338  
  1339  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1340  	require.Equal(t, 1, len(scores))
  1341  	require.Equal(t, "p1", scores[0].Party)
  1342  	require.Equal(t, "0.0000135", scores[0].Score.String())
  1343  
  1344  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3})
  1345  	require.Equal(t, 1, len(scores))
  1346  	require.Equal(t, "p1", scores[0].Party)
  1347  	require.Equal(t, "0.000009", scores[0].Score.String())
  1348  
  1349  	// qualifying the market to m1, expect the same result
  1350  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m1"}})
  1351  	require.Equal(t, 1, len(scores))
  1352  	require.Equal(t, "p1", scores[0].Party)
  1353  	require.Equal(t, "0.000009", scores[0].Score.String())
  1354  
  1355  	// qualifying the market to m2, expect the same result
  1356  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m2"}})
  1357  	require.Equal(t, 0, len(scores))
  1358  
  1359  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime.Add(1000 * time.Second)})
  1360  
  1361  	// 600 seconds into the epoch new position is recorded for p1
  1362  	// pBar = 0 * 150 + 1 * 200 = 200
  1363  	tracker.RecordPosition("a1", "p1", "m1", 100, num.NewUint(3), num.DecimalOne(), epochStartTime.Add(1600*time.Second))
  1364  
  1365  	// end the epoch
  1366  	// pBar = 0.6 * 200 + 0.4 * 100 = 160
  1367  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime.Add(1000 * time.Second), EndTime: epochStartTime.Add(2000 * time.Second)})
  1368  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1369  	require.Equal(t, 1, len(scores))
  1370  	require.Equal(t, "p1", scores[0].Party)
  1371  	require.Equal(t, "0.000036", scores[0].Score.String())
  1372  
  1373  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1374  	require.Equal(t, 1, len(scores))
  1375  	require.Equal(t, "p1", scores[0].Party)
  1376  	require.Equal(t, "0.0000315", scores[0].Score.String())
  1377  
  1378  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5})
  1379  	require.Equal(t, 1, len(scores))
  1380  	require.Equal(t, "p1", scores[0].Party)
  1381  	require.Equal(t, "0.0000126", scores[0].Score.String())
  1382  
  1383  	// qualify to m1
  1384  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m1"}})
  1385  	require.Equal(t, 1, len(scores))
  1386  	require.Equal(t, "p1", scores[0].Party)
  1387  	require.Equal(t, "0.0000126", scores[0].Score.String())
  1388  
  1389  	// qualify to m2
  1390  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m2"}})
  1391  	require.Equal(t, 0, len(scores))
  1392  
  1393  	// now lets lets at an epoch with no activity
  1394  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime.Add(2000 * time.Second)})
  1395  	// end the epoch
  1396  	// pBar = 0 * 160 + 1 * 100 = 100
  1397  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime.Add(2000 * time.Second), EndTime: epochStartTime.Add(3000 * time.Second)})
  1398  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1399  	require.Equal(t, 1, len(scores))
  1400  	require.Equal(t, "p1", scores[0].Party)
  1401  	require.Equal(t, "0.00003", scores[0].Score.String())
  1402  
  1403  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1404  	require.Equal(t, 1, len(scores))
  1405  	require.Equal(t, "p1", scores[0].Party)
  1406  	require.Equal(t, "0.000033", scores[0].Score.String())
  1407  
  1408  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 4})
  1409  	require.Equal(t, 1, len(scores))
  1410  	require.Equal(t, "p1", scores[0].Party)
  1411  	require.Equal(t, "0.00002325", scores[0].Score.String())
  1412  }
  1413  
  1414  func TestRealisedReturnMetric(t *testing.T) {
  1415  	epochService := &TestEpochEngine{}
  1416  	ctx := context.Background()
  1417  	ctrl := gomock.NewController(t)
  1418  	teams := mocks.NewMockTeams(ctrl)
  1419  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1420  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
  1421  	broker := bmocks.NewMockBroker(ctrl)
  1422  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
  1423  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
  1424  	collateralService := mocks.NewMockCollateral(ctrl)
  1425  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1426  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
  1427  
  1428  	epochStartTime := time.Now()
  1429  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime})
  1430  	tracker.SetEligibilityChecker(&EligibilityChecker{})
  1431  	tracker.MarketProposed("a1", "m1", "p1")
  1432  
  1433  	tracker.RecordFundingPayment("a1", "p1", "m1", num.NewDecimalFromFloat(100))
  1434  	tracker.RecordFundingPayment("a1", "p1", "m1", num.NewDecimalFromFloat(-200))
  1435  	tracker.RecordRealisedPosition("a1", "p1", "m1", num.DecimalFromFloat(130))
  1436  
  1437  	// now end the epoch after 600 seconds
  1438  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime, EndTime: epochStartTime.Add(1000 * time.Second)})
  1439  
  1440  	scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1441  	require.Equal(t, 1, len(scores))
  1442  	require.Equal(t, "p1", scores[0].Party)
  1443  	require.Equal(t, "30", scores[0].Score.String())
  1444  
  1445  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1446  	require.Equal(t, 1, len(scores))
  1447  	require.Equal(t, "p1", scores[0].Party)
  1448  	require.Equal(t, "15", scores[0].Score.String())
  1449  
  1450  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3})
  1451  	require.Equal(t, 1, len(scores))
  1452  	require.Equal(t, "p1", scores[0].Party)
  1453  	require.Equal(t, "10", scores[0].Score.String())
  1454  
  1455  	// qualifying the market to m1, expect the same result
  1456  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m1"}})
  1457  	require.Equal(t, 1, len(scores))
  1458  	require.Equal(t, "p1", scores[0].Party)
  1459  	require.Equal(t, "10", scores[0].Score.String())
  1460  
  1461  	// qualifying the market to m2, expect the same result
  1462  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m2"}})
  1463  	require.Equal(t, 0, len(scores))
  1464  
  1465  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime.Add(1000 * time.Second)})
  1466  
  1467  	tracker.RecordRealisedPosition("a1", "p1", "m1", num.DecimalFromFloat(-45))
  1468  
  1469  	// end the epoch
  1470  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime.Add(1000 * time.Second), EndTime: epochStartTime.Add(2000 * time.Second)})
  1471  
  1472  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1473  	require.Equal(t, 1, len(scores))
  1474  	require.Equal(t, "p1", scores[0].Party)
  1475  	require.Equal(t, "-45", scores[0].Score.String())
  1476  
  1477  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1478  	require.Equal(t, 1, len(scores))
  1479  	require.Equal(t, "p1", scores[0].Party)
  1480  	require.Equal(t, "-7.5", scores[0].Score.String())
  1481  
  1482  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5})
  1483  	require.Equal(t, 1, len(scores))
  1484  	require.Equal(t, "p1", scores[0].Party)
  1485  	require.Equal(t, "-3", scores[0].Score.String())
  1486  
  1487  	// qualify to m1
  1488  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m1"}})
  1489  	require.Equal(t, 1, len(scores))
  1490  	require.Equal(t, "p1", scores[0].Party)
  1491  	require.Equal(t, "-3", scores[0].Score.String())
  1492  
  1493  	// qualify to m2
  1494  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m2"}})
  1495  	require.Equal(t, 0, len(scores))
  1496  
  1497  	// now lets lets at an epoch with no activity
  1498  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime.Add(2000 * time.Second)})
  1499  	// end the epoch
  1500  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime.Add(2000 * time.Second), EndTime: epochStartTime.Add(3000 * time.Second)})
  1501  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1502  	require.Equal(t, 1, len(scores))
  1503  	require.Equal(t, "p1", scores[0].Party)
  1504  	require.Equal(t, "0", scores[0].Score.String())
  1505  	require.Equal(t, false, scores[0].IsEligible)
  1506  
  1507  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1508  	require.Equal(t, 1, len(scores))
  1509  	require.Equal(t, "p1", scores[0].Party)
  1510  	require.Equal(t, "-22.5", scores[0].Score.String())
  1511  
  1512  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 4})
  1513  	require.Equal(t, 1, len(scores))
  1514  	require.Equal(t, "p1", scores[0].Party)
  1515  	require.Equal(t, "-3.75", scores[0].Score.String())
  1516  }
  1517  
  1518  func TestRelativeReturnMetric(t *testing.T) {
  1519  	ctx := context.Background()
  1520  	epochService := &TestEpochEngine{}
  1521  	ctrl := gomock.NewController(t)
  1522  	teams := mocks.NewMockTeams(ctrl)
  1523  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1524  	balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes()
  1525  
  1526  	broker := bmocks.NewMockBroker(ctrl)
  1527  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
  1528  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
  1529  	collateralService := mocks.NewMockCollateral(ctrl)
  1530  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1531  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
  1532  
  1533  	epochStartTime := time.Now()
  1534  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime})
  1535  	tracker.SetEligibilityChecker(&EligibilityChecker{})
  1536  	tracker.MarketProposed("a1", "m1", "p1")
  1537  
  1538  	tracker.RecordPosition("a1", "p1", "m1", 100, num.NewUint(1), num.DecimalOne(), epochStartTime.Add(100*time.Second))
  1539  	tracker.RecordPosition("a1", "p1", "m1", -200, num.NewUint(2), num.DecimalOne(), epochStartTime.Add(400*time.Second))
  1540  
  1541  	tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(100))
  1542  	tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(-120))
  1543  	tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(150))
  1544  	tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(-115))
  1545  
  1546  	// end the epoch
  1547  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime, EndTime: epochStartTime.Add(1000 * time.Second)})
  1548  
  1549  	// the total m2m = 15
  1550  	// the time weighted position for the epoch = 150
  1551  	// therefore the r = 15/150 = 0.1
  1552  
  1553  	// window size 1
  1554  	scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1555  	require.Equal(t, 1, len(scores))
  1556  	require.Equal(t, "p1", scores[0].Party)
  1557  	require.Equal(t, "0.1", scores[0].Score.String())
  1558  
  1559  	// window size 2
  1560  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1561  	require.Equal(t, 1, len(scores))
  1562  	require.Equal(t, "p1", scores[0].Party)
  1563  	require.Equal(t, "0.05", scores[0].Score.String())
  1564  
  1565  	// window size 3
  1566  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3})
  1567  	require.Equal(t, 1, len(scores))
  1568  	require.Equal(t, "p1", scores[0].Party)
  1569  	require.Equal(t, "0.0333333333333333", scores[0].Score.String())
  1570  
  1571  	// add only this market in scope, expect same result
  1572  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m1"}})
  1573  	require.Equal(t, 1, len(scores))
  1574  	require.Equal(t, "p1", scores[0].Party)
  1575  	require.Equal(t, "0.0333333333333333", scores[0].Score.String())
  1576  
  1577  	// add market scope with the wrong market:
  1578  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m2"}})
  1579  	require.Equal(t, 0, len(scores))
  1580  
  1581  	// add a scope with a different market, expect nothing back
  1582  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m2"}})
  1583  	require.Equal(t, 0, len(scores))
  1584  
  1585  	// lets run another epoch and make some loss
  1586  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime.Add(1000 * time.Second)})
  1587  	tracker.RecordPosition("a1", "p1", "m1", 100, num.NewUint(2), num.DecimalOne(), epochStartTime.Add(1600*time.Second))
  1588  	tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(-8))
  1589  
  1590  	// end the epoch
  1591  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime.Add(1000 * time.Second), EndTime: epochStartTime.Add(2000 * time.Second)})
  1592  
  1593  	// total m2m = -8
  1594  	// the time weighted position for the epoch = 160
  1595  	// therefore r = -0.05
  1596  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1})
  1597  	require.Equal(t, 1, len(scores))
  1598  	require.Equal(t, "p1", scores[0].Party)
  1599  	require.Equal(t, "-0.05", scores[0].Score.String())
  1600  
  1601  	// with window size=2 we get (0.1-0.05)/2 = 0.025
  1602  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2})
  1603  	require.Equal(t, 1, len(scores))
  1604  	require.Equal(t, "p1", scores[0].Party)
  1605  	require.Equal(t, "0.025", scores[0].Score.String())
  1606  
  1607  	// with window size=4 we get (0.1-0.05)/4 = 0.0125
  1608  	scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 4})
  1609  	require.Equal(t, 1, len(scores))
  1610  	require.Equal(t, "p1", scores[0].Party)
  1611  	require.Equal(t, "0.0125", scores[0].Score.String())
  1612  }
  1613  
  1614  func TestTeamStatsForMarkets(t *testing.T) {
  1615  	ctrl := gomock.NewController(t)
  1616  	teams := mocks.NewMockTeams(ctrl)
  1617  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1618  	broker := bmocks.NewMockBroker(ctrl)
  1619  	collateralService := mocks.NewMockCollateral(ctrl)
  1620  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1621  
  1622  	asset1 := vgrand.RandomStr(5)
  1623  	asset2 := vgrand.RandomStr(5)
  1624  	asset3 := vgrand.RandomStr(5)
  1625  
  1626  	market1 := "market1"
  1627  	market2 := "market2"
  1628  	market3 := "market3"
  1629  	market4 := "market4"
  1630  	market5 := "market5"
  1631  	market6 := "market6"
  1632  	market7 := "market7"
  1633  
  1634  	team1 := "team1"
  1635  	team2 := "team2"
  1636  	member11 := "member11"
  1637  	member12 := "member12"
  1638  	member21 := "member21"
  1639  	member22 := "member22"
  1640  	lonewolf1 := "lone-wolf1"
  1641  	lonewolf2 := "lone-wolf2"
  1642  
  1643  	// 1. Need markets with different assets.
  1644  	tracker.MarketProposed(asset1, market1, vgrand.RandomStr(5))
  1645  	tracker.MarketProposed(asset1, market2, vgrand.RandomStr(5))
  1646  	tracker.MarketProposed(asset1, market3, vgrand.RandomStr(5))
  1647  
  1648  	tracker.MarketProposed(asset2, market4, vgrand.RandomStr(5))
  1649  	tracker.MarketProposed(asset2, market5, vgrand.RandomStr(5))
  1650  
  1651  	tracker.MarketProposed(asset3, market6, vgrand.RandomStr(5))
  1652  	tracker.MarketProposed(asset3, market7, vgrand.RandomStr(5))
  1653  
  1654  	// 2. Need to define teams.
  1655  	teams.EXPECT().GetAllTeamsWithParties(uint64(0)).Return(map[string][]string{
  1656  		team1: {member11, member12},
  1657  		team2: {member21, member22},
  1658  	}).Times(1)
  1659  
  1660  	// 3. Need parties generating volume on these markets.
  1661  	tracker.RecordNotionalTakerVolume(market1, member11, num.NewUint(1))
  1662  	tracker.RecordNotionalTakerVolume(market1, member12, num.NewUint(2))
  1663  	tracker.RecordNotionalTakerVolume(market1, member21, num.NewUint(3))
  1664  	tracker.RecordNotionalTakerVolume(market1, member22, num.NewUint(4))
  1665  	tracker.RecordNotionalTakerVolume(market1, lonewolf1, num.NewUint(5))
  1666  	tracker.RecordNotionalTakerVolume(market1, lonewolf2, num.NewUint(6))
  1667  
  1668  	tracker.RecordNotionalTakerVolume(market2, member11, num.NewUint(1))
  1669  	tracker.RecordNotionalTakerVolume(market2, member12, num.NewUint(2))
  1670  	tracker.RecordNotionalTakerVolume(market2, member21, num.NewUint(3))
  1671  	tracker.RecordNotionalTakerVolume(market2, member22, num.NewUint(4))
  1672  	tracker.RecordNotionalTakerVolume(market2, lonewolf1, num.NewUint(5))
  1673  	tracker.RecordNotionalTakerVolume(market2, lonewolf2, num.NewUint(6))
  1674  
  1675  	// No participation of team 2 in market 3.
  1676  	tracker.RecordNotionalTakerVolume(market3, member11, num.NewUint(1))
  1677  	tracker.RecordNotionalTakerVolume(market3, member12, num.NewUint(2))
  1678  	tracker.RecordNotionalTakerVolume(market3, lonewolf1, num.NewUint(5))
  1679  	tracker.RecordNotionalTakerVolume(market3, lonewolf2, num.NewUint(6))
  1680  
  1681  	// No participation of team 1 in market 4.
  1682  	tracker.RecordNotionalTakerVolume(market4, member21, num.NewUint(3))
  1683  	tracker.RecordNotionalTakerVolume(market4, member22, num.NewUint(4))
  1684  	tracker.RecordNotionalTakerVolume(market4, lonewolf1, num.NewUint(5))
  1685  	tracker.RecordNotionalTakerVolume(market4, lonewolf2, num.NewUint(6))
  1686  
  1687  	// Market 5 is not expected to be filtered on, so none of these volume
  1688  	// should show up in the stats.
  1689  	tracker.RecordNotionalTakerVolume(market5, member11, num.NewUint(1000))
  1690  	tracker.RecordNotionalTakerVolume(market5, member12, num.NewUint(2000))
  1691  	tracker.RecordNotionalTakerVolume(market5, member12, num.NewUint(3000))
  1692  	tracker.RecordNotionalTakerVolume(market5, member22, num.NewUint(4000))
  1693  	tracker.RecordNotionalTakerVolume(market5, lonewolf1, num.NewUint(5000))
  1694  	tracker.RecordNotionalTakerVolume(market5, lonewolf2, num.NewUint(6000))
  1695  
  1696  	// Nobody likes market 6. So, no participation of any kind.
  1697  
  1698  	// Only lone-wolves in market 7.
  1699  	tracker.RecordNotionalTakerVolume(market7, lonewolf1, num.NewUint(5))
  1700  	tracker.RecordNotionalTakerVolume(market7, lonewolf2, num.NewUint(6))
  1701  
  1702  	// Regarding the dataset above, this should result in gathering the data from
  1703  	// the market 1, 2, 3, 4, and 7, but not 5 and 6, because:
  1704  	//   - we want all markets from asset 1 -> market 1, 2, and 3.
  1705  	//   - we want specific market 1, 3, 4, and 7.
  1706  	//
  1707  	// NB: It's on purpose we have duplicated references to the market 1 and 3, so
  1708  	// we can ensure it's duplicated and we don't add up stats from a market multiple
  1709  	// times.
  1710  	teamsStats := tracker.TeamStatsForMarkets([]string{asset1}, []string{market1, market3, market4, market7})
  1711  
  1712  	assert.Equal(t, map[string]map[string]*num.Uint{
  1713  		team1: {
  1714  			member11: num.NewUint(3),
  1715  			member12: num.NewUint(6),
  1716  		},
  1717  		team2: {
  1718  			member21: num.NewUint(9),
  1719  			member22: num.NewUint(12),
  1720  		},
  1721  	}, teamsStats)
  1722  }
  1723  
  1724  func setupDefaultTrackerForTest(t *testing.T) *common.MarketActivityTracker {
  1725  	t.Helper()
  1726  
  1727  	epochService := &TestEpochEngine{}
  1728  	ctrl := gomock.NewController(t)
  1729  	teams := mocks.NewMockTeams(ctrl)
  1730  	balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl)
  1731  	broker := bmocks.NewMockBroker(ctrl)
  1732  	collateralService := mocks.NewMockCollateral(ctrl)
  1733  	tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService)
  1734  	epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore)
  1735  
  1736  	epochStartTime := time.Now()
  1737  	epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime})
  1738  	tracker.SetEligibilityChecker(&EligibilityChecker{})
  1739  
  1740  	tracker.MarketProposed("asset1", "market1", "me")
  1741  	tracker.MarketProposed("asset1", "market2", "me2")
  1742  	tracker.MarketProposed("asset1", "market4", "me4")
  1743  	tracker.MarketProposed("asset2", "market3", "me3")
  1744  
  1745  	tracker.RecordPosition("asset1", "p1", "market1", 100, num.NewUint(1), num.DecimalOne(), time.Now())
  1746  	tracker.RecordPosition("asset1", "p1", "market2", 200, num.NewUint(2), num.DecimalOne(), time.Now())
  1747  	tracker.RecordPosition("asset1", "p2", "market1", 300, num.NewUint(3), num.DecimalOne(), time.Now())
  1748  	tracker.RecordPosition("asset1", "p3", "market2", 400, num.NewUint(4), num.DecimalOne(), time.Now())
  1749  	tracker.RecordPosition("asset1", "p3", "market4", 500, num.NewUint(5), num.DecimalOne(), time.Now())
  1750  	tracker.RecordPosition("asset2", "p4", "market3", 600, num.NewUint(6), num.DecimalOne(), time.Now())
  1751  
  1752  	tracker.RecordM2M("asset1", "p1", "market1", num.DecimalOne())
  1753  	tracker.RecordM2M("asset1", "p1", "market2", num.DecimalFromInt64(5))
  1754  
  1755  	tracker.RecordNotionalTakerVolume("market1", "p1", num.NewUint(10))
  1756  	tracker.RecordNotionalTakerVolume("market1", "p2", num.NewUint(10))
  1757  	tracker.RecordNotionalTakerVolume("market1", "p3", num.NewUint(10))
  1758  	tracker.RecordNotionalTakerVolume("market1", "p4", num.NewUint(10))
  1759  	tracker.RecordNotionalTakerVolume("market2", "p1", num.NewUint(20))
  1760  	tracker.RecordNotionalTakerVolume("market2", "p2", num.NewUint(20))
  1761  	tracker.RecordNotionalTakerVolume("market2", "p3", num.NewUint(20))
  1762  	tracker.RecordNotionalTakerVolume("market2", "p4", num.NewUint(20))
  1763  	tracker.RecordNotionalTakerVolume("market3", "p1", num.NewUint(30))
  1764  	tracker.RecordNotionalTakerVolume("market3", "p2", num.NewUint(30))
  1765  	tracker.RecordNotionalTakerVolume("market3", "p3", num.NewUint(30))
  1766  	tracker.RecordNotionalTakerVolume("market3", "p4", num.NewUint(30))
  1767  	tracker.RecordNotionalTakerVolume("market4", "p1", num.NewUint(40))
  1768  	tracker.RecordNotionalTakerVolume("market4", "p2", num.NewUint(40))
  1769  	tracker.RecordNotionalTakerVolume("market4", "p3", num.NewUint(40))
  1770  	tracker.RecordNotionalTakerVolume("market4", "p4", num.NewUint(40))
  1771  
  1772  	// update with a few transfers
  1773  	transfersM1 := []*types.Transfer{
  1774  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}},
  1775  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
  1776  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
  1777  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}},
  1778  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(300)}},
  1779  		{Owner: "party1", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
  1780  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}},
  1781  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(800)}},
  1782  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(700)}},
  1783  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}},
  1784  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(200)}},
  1785  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1000)}},
  1786  	}
  1787  	tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1)
  1788  
  1789  	transfersM2 := []*types.Transfer{
  1790  		{Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(500)}},
  1791  		{Owner: "party2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1500)}},
  1792  		{Owner: "party2", Type: types.TransferTypeLiquidityFeeNetDistribute, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(1500)}},
  1793  	}
  1794  	tracker.UpdateFeesFromTransfers("asset2", "market2", transfersM2)
  1795  
  1796  	transfersM3 := []*types.Transfer{
  1797  		{Owner: "party1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(500)}},
  1798  		{Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(450)}},
  1799  	}
  1800  	tracker.UpdateFeesFromTransfers("asset2", "market3", transfersM3)
  1801  
  1802  	return tracker
  1803  }