code.vegaprotocol.io/vega@v0.79.0/core/execution/common/market_activity_tracker_snapshot.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
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"slices"
    22  	"sort"
    23  	"strings"
    24  	"time"
    25  
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  	"code.vegaprotocol.io/vega/libs/proto"
    29  	checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1"
    30  	snapshot "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    31  
    32  	"golang.org/x/exp/maps"
    33  )
    34  
    35  var (
    36  	key                        = (&types.PayloadMarketActivityTracker{}).Key()
    37  	ErrSnapshotKeyDoesNotExist = errors.New("unknown key for market activity tracker snapshot")
    38  	hashKeys                   = []string{key}
    39  )
    40  
    41  type snapshotState struct {
    42  	serialised []byte
    43  }
    44  
    45  func (mat *MarketActivityTracker) Namespace() types.SnapshotNamespace {
    46  	return types.MarketActivityTrackerSnapshot
    47  }
    48  
    49  func (mat *MarketActivityTracker) Keys() []string {
    50  	return hashKeys
    51  }
    52  
    53  func (mat *MarketActivityTracker) Stopped() bool {
    54  	return false
    55  }
    56  
    57  func returnsDataToProto(returnsData map[string]num.Decimal) []*checkpoint.ReturnsData {
    58  	parties := make([]string, 0, len(returnsData))
    59  	for k := range returnsData {
    60  		parties = append(parties, k)
    61  	}
    62  	sort.Strings(parties)
    63  	data := make([]*checkpoint.ReturnsData, 0, len(parties))
    64  	for _, party := range parties {
    65  		rd := &checkpoint.ReturnsData{Party: party}
    66  		rd.Return, _ = returnsData[party].MarshalBinary()
    67  		data = append(data, rd)
    68  	}
    69  	return data
    70  }
    71  
    72  func epochReturnDataToProto(epochData []map[string]num.Decimal) []*checkpoint.EpochReturnsData {
    73  	ret := make([]*checkpoint.EpochReturnsData, 0, len(epochData))
    74  	for _, v := range epochData {
    75  		ed := make([]*checkpoint.ReturnsData, 0, len(v))
    76  		keys := make([]string, 0, len(v))
    77  		for k := range v {
    78  			keys = append(keys, k)
    79  		}
    80  		sort.Strings(keys)
    81  		for _, party := range keys {
    82  			retData := &checkpoint.ReturnsData{
    83  				Party: party,
    84  			}
    85  			retData.Return, _ = v[party].MarshalBinary()
    86  			ed = append(ed, retData)
    87  		}
    88  		ret = append(ret, &checkpoint.EpochReturnsData{Returns: ed})
    89  	}
    90  	return ret
    91  }
    92  
    93  func epochEligitbilityToProto(eligibilityData map[string][]map[string]struct{}) []*checkpoint.GameEligibilityTracker {
    94  	res := make([]*checkpoint.GameEligibilityTracker, 0, len(eligibilityData))
    95  	gameIDs := make([]string, 0, len(eligibilityData))
    96  	for k := range eligibilityData {
    97  		gameIDs = append(gameIDs, k)
    98  	}
    99  	sort.Strings(gameIDs)
   100  	for _, gameID := range gameIDs {
   101  		epochs := eligibilityData[gameID]
   102  		get := &checkpoint.GameEligibilityTracker{
   103  			GameId:           gameID,
   104  			EpochEligibility: make([]*checkpoint.EpochEligibility, 0, len(epochs)),
   105  		}
   106  		for _, epoch := range epochs {
   107  			epochEligibleParties := make([]string, 0, len(epoch))
   108  			for party := range epoch {
   109  				epochEligibleParties = append(epochEligibleParties, party)
   110  			}
   111  			sort.Strings(epochEligibleParties)
   112  			get.EpochEligibility = append(get.EpochEligibility, &checkpoint.EpochEligibility{
   113  				EligibleParties: epochEligibleParties,
   114  			})
   115  		}
   116  		res = append(res, get)
   117  	}
   118  	return res
   119  }
   120  
   121  func epochTakerFeesToProto(epochData []map[string]map[string]map[string]*num.Uint) []*checkpoint.EpochPartyTakerFees {
   122  	ret := make([]*checkpoint.EpochPartyTakerFees, 0, len(epochData))
   123  	for _, epoch := range epochData {
   124  		ed := []*checkpoint.AssetMarketPartyTakerFees{}
   125  		assets := make([]string, 0, len(epoch))
   126  		for k := range epoch {
   127  			assets = append(assets, k)
   128  		}
   129  		sort.Strings(assets)
   130  		for _, asset := range assets {
   131  			assetData := epoch[asset]
   132  			markets := make([]string, 0, len(assetData))
   133  			for market := range assetData {
   134  				markets = append(markets, market)
   135  			}
   136  			sort.Strings(markets)
   137  			for _, market := range markets {
   138  				takerFees := assetData[market]
   139  				parties := make([]string, 0, len(takerFees))
   140  				for party := range takerFees {
   141  					parties = append(parties, party)
   142  				}
   143  				sort.Strings(parties)
   144  				partyFees := make([]*checkpoint.PartyTakerFees, 0, len(parties))
   145  				for _, party := range parties {
   146  					fee := takerFees[party].Bytes()
   147  					partyFees = append(partyFees, &checkpoint.PartyTakerFees{
   148  						Party:     party,
   149  						TakerFees: fee[:],
   150  					})
   151  				}
   152  				ed = append(ed, &checkpoint.AssetMarketPartyTakerFees{
   153  					Asset:     asset,
   154  					Market:    market,
   155  					TakerFees: partyFees,
   156  				})
   157  			}
   158  		}
   159  		ret = append(ret, &checkpoint.EpochPartyTakerFees{EpochPartyTakerFeesPaid: ed})
   160  	}
   161  	return ret
   162  }
   163  
   164  func timeWeightedNotionalToProto(twNotional map[string]*twNotional) []*checkpoint.TWNotionalData {
   165  	parties := make([]string, 0, len(twNotional))
   166  	for k := range twNotional {
   167  		parties = append(parties, k)
   168  	}
   169  	sort.Strings(parties)
   170  	data := make([]*checkpoint.TWNotionalData, 0, len(parties))
   171  	for _, party := range parties {
   172  		pd := twNotional[party]
   173  		pdProto := &checkpoint.TWNotionalData{
   174  			Party: party,
   175  			Time:  pd.t.UnixNano(),
   176  		}
   177  		b := pd.notional.Bytes()
   178  		pdProto.Notional = b[:]
   179  		twb := pd.currentEpochTWNotional.Bytes()
   180  		pdProto.TwNotional = twb[:]
   181  
   182  		pb := pd.price.Bytes()
   183  		pdProto.Price = pb[:]
   184  		data = append(data, pdProto)
   185  	}
   186  	return data
   187  }
   188  
   189  func timeWeightedNotionalHistoryToProto(partyNotionalHistory []map[string]*num.Uint) []*checkpoint.EpochTimeWeightedNotionalData {
   190  	ret := make([]*checkpoint.EpochTimeWeightedNotionalData, 0, len(partyNotionalHistory))
   191  	for _, v := range partyNotionalHistory {
   192  		keys := make([]string, 0, len(v))
   193  		for k := range v {
   194  			keys = append(keys, k)
   195  		}
   196  		sort.Strings(keys)
   197  		epochData := &checkpoint.EpochTimeWeightedNotionalData{PartyTimeWeightedNotionals: make([]*checkpoint.PartyTimeWeightedNotional, 0, len(keys))}
   198  		for _, party := range keys {
   199  			partyData := &checkpoint.PartyTimeWeightedNotional{Party: party}
   200  			b := v[party].Bytes()
   201  			partyData.TwNotional = b[:]
   202  			epochData.PartyTimeWeightedNotionals = append(epochData.PartyTimeWeightedNotionals, partyData)
   203  		}
   204  		ret = append(ret, epochData)
   205  	}
   206  	return ret
   207  }
   208  
   209  func timeWeightedPositionHistoryToProto(partyPositionsHistory []map[string]uint64) []*checkpoint.EpochTimeWeightPositionData {
   210  	ret := make([]*checkpoint.EpochTimeWeightPositionData, 0, len(partyPositionsHistory))
   211  	for _, v := range partyPositionsHistory {
   212  		keys := make([]string, 0, len(v))
   213  		for k := range v {
   214  			keys = append(keys, k)
   215  		}
   216  		sort.Strings(keys)
   217  		epochData := &checkpoint.EpochTimeWeightPositionData{PartyTimeWeightedPositions: make([]*checkpoint.PartyTimeWeightedPosition, 0, len(keys))}
   218  		for _, party := range keys {
   219  			epochData.PartyTimeWeightedPositions = append(epochData.PartyTimeWeightedPositions, &checkpoint.PartyTimeWeightedPosition{Party: party, TwPosition: v[party]})
   220  		}
   221  		ret = append(ret, epochData)
   222  	}
   223  	return ret
   224  }
   225  
   226  func timeWeightedPositionToProto(partyPositions map[string]*twPosition) []*checkpoint.TWPositionData {
   227  	parties := make([]string, 0, len(partyPositions))
   228  	for k := range partyPositions {
   229  		parties = append(parties, k)
   230  	}
   231  	sort.Strings(parties)
   232  	data := make([]*checkpoint.TWPositionData, 0, len(parties))
   233  	for _, party := range parties {
   234  		pd := partyPositions[party]
   235  		pdProto := &checkpoint.TWPositionData{
   236  			Party:      party,
   237  			Time:       pd.t.UnixNano(),
   238  			Position:   pd.position,
   239  			TwPosition: pd.currentEpochTWPosition,
   240  		}
   241  		data = append(data, pdProto)
   242  	}
   243  	return data
   244  }
   245  
   246  func marketToPartyTakerNotionalToProto(stats map[string]map[string]*num.Uint) []*checkpoint.MarketToPartyTakerNotionalVolume {
   247  	ret := make([]*checkpoint.MarketToPartyTakerNotionalVolume, 0, len(stats))
   248  	for marketID, partiesStats := range stats {
   249  		ret = append(ret, &checkpoint.MarketToPartyTakerNotionalVolume{
   250  			Market:              marketID,
   251  			TakerNotionalVolume: takerNotionalToProto(partiesStats),
   252  		})
   253  	}
   254  
   255  	slices.SortStableFunc(ret, func(a, b *checkpoint.MarketToPartyTakerNotionalVolume) int {
   256  		return strings.Compare(a.Market, b.Market)
   257  	})
   258  
   259  	return ret
   260  }
   261  
   262  func takerNotionalToProto(takerNotional map[string]*num.Uint) []*checkpoint.TakerNotionalVolume {
   263  	ret := make([]*checkpoint.TakerNotionalVolume, 0, len(takerNotional))
   264  	for k, u := range takerNotional {
   265  		var b []byte
   266  		if u != nil {
   267  			bb := u.Bytes()
   268  			b = bb[:]
   269  		}
   270  		ret = append(ret, &checkpoint.TakerNotionalVolume{Party: k, Volume: b})
   271  	}
   272  	sort.Slice(ret, func(i, j int) bool {
   273  		return ret[i].Party < ret[j].Party
   274  	})
   275  	return ret
   276  }
   277  
   278  func marketFeesHistoryToProto(feeHistory []map[string]*num.Uint) []*checkpoint.EpochPartyFees {
   279  	data := make([]*checkpoint.EpochPartyFees, 0, len(feeHistory))
   280  	for _, v := range feeHistory {
   281  		keys := make([]string, 0, len(v))
   282  		for k := range v {
   283  			keys = append(keys, k)
   284  		}
   285  		sort.Strings(keys)
   286  		partyFees := &checkpoint.EpochPartyFees{PartyFees: make([]*checkpoint.PartyFeesHistory, 0, len(keys))}
   287  		for _, party := range keys {
   288  			pfh := &checkpoint.PartyFeesHistory{Party: party}
   289  			b := v[party].Bytes()
   290  			pfh.Fee = b[:]
   291  			partyFees.PartyFees = append(partyFees.PartyFees, pfh)
   292  		}
   293  		data = append(data, partyFees)
   294  	}
   295  	return data
   296  }
   297  
   298  func marketFeesToProto(partyFees map[string]*num.Uint) []*checkpoint.PartyFees {
   299  	parties := make([]string, 0, len(partyFees))
   300  	for k := range partyFees {
   301  		parties = append(parties, k)
   302  	}
   303  	sort.Strings(parties)
   304  	pf := make([]*checkpoint.PartyFees, 0, len(parties))
   305  	for _, party := range parties {
   306  		pf = append(pf, &checkpoint.PartyFees{Party: party, Fee: partyFees[party].String()})
   307  	}
   308  	return pf
   309  }
   310  
   311  func (mt *marketTracker) IntoProto(market string) *checkpoint.MarketActivityTracker {
   312  	paid := make([]string, 0, len(mt.proposersPaid))
   313  	for k := range mt.proposersPaid {
   314  		paid = append(paid, k)
   315  	}
   316  	sort.Strings(paid)
   317  
   318  	ammParties := maps.Keys(mt.ammPartiesCache)
   319  	sort.Strings(ammParties)
   320  
   321  	epochNotionalVolume := make([]string, 0, len(mt.epochNotionalVolume))
   322  	for _, env := range mt.epochNotionalVolume {
   323  		epochNotionalVolume = append(epochNotionalVolume, env.String())
   324  	}
   325  
   326  	return &checkpoint.MarketActivityTracker{
   327  		Market:                          market,
   328  		Asset:                           mt.asset,
   329  		MakerFeesReceived:               marketFeesToProto(mt.makerFeesReceived),
   330  		MakerFeesPaid:                   marketFeesToProto(mt.makerFeesPaid),
   331  		LpFees:                          marketFeesToProto(mt.lpFees),
   332  		InfraFees:                       marketFeesToProto(mt.infraFees),
   333  		LpPaidFees:                      marketFeesToProto(mt.lpPaidFees),
   334  		BuyBackFees:                     marketFeesToProto(mt.buybackFeesPaid),
   335  		TreasuryFees:                    marketFeesToProto(mt.treasuryFeesPaid),
   336  		Proposer:                        mt.proposer,
   337  		BonusPaid:                       paid,
   338  		ValueTraded:                     mt.valueTraded.String(),
   339  		ReadyToDelete:                   mt.readyToDelete,
   340  		TimeWeightedPosition:            timeWeightedPositionToProto(mt.twPosition),
   341  		TimeWeightedNotional:            timeWeightedNotionalToProto(mt.twNotional),
   342  		ReturnsData:                     returnsDataToProto(mt.partyM2M),
   343  		MakerFeesReceivedHistory:        marketFeesHistoryToProto(mt.epochMakerFeesReceived),
   344  		MakerFeesPaidHistory:            marketFeesHistoryToProto(mt.epochMakerFeesPaid),
   345  		LpFeesHistory:                   marketFeesHistoryToProto(mt.epochLpFees),
   346  		TimeWeightedPositionDataHistory: timeWeightedPositionHistoryToProto(mt.epochTimeWeightedPosition),
   347  		TimeWeightedNotionalDataHistory: timeWeightedNotionalHistoryToProto(mt.epochTimeWeightedNotional),
   348  		ReturnsDataHistory:              epochReturnDataToProto(mt.epochPartyM2M),
   349  		RealisedReturns:                 returnsDataToProto(mt.partyRealisedReturn),
   350  		RealisedReturnsHistory:          epochReturnDataToProto(mt.epochPartyRealisedReturn),
   351  		AmmParties:                      ammParties,
   352  		NotionalVolumeForEpoch:          mt.notionalVolumeForEpoch.String(),
   353  		EpochNotionalVolume:             epochNotionalVolume,
   354  	}
   355  }
   356  
   357  func (mat *MarketActivityTracker) serialiseFeesTracker() *snapshot.MarketTracker {
   358  	marketActivity := []*checkpoint.MarketActivityTracker{}
   359  	assets := make([]string, 0, len(mat.assetToMarketTrackers))
   360  	for k := range mat.assetToMarketTrackers {
   361  		assets = append(assets, k)
   362  	}
   363  	sort.Strings(assets)
   364  	for _, asset := range assets {
   365  		markets := make([]string, 0, len(mat.assetToMarketTrackers[asset]))
   366  		assetMarketTrackers := mat.assetToMarketTrackers[asset]
   367  		for k := range mat.assetToMarketTrackers[asset] {
   368  			markets = append(markets, k)
   369  		}
   370  		sort.Strings(markets)
   371  
   372  		for _, market := range markets {
   373  			marketActivity = append(marketActivity, assetMarketTrackers[market].IntoProto(market))
   374  		}
   375  	}
   376  
   377  	return &snapshot.MarketTracker{
   378  		MarketActivity:                   marketActivity,
   379  		TakerNotionalVolume:              takerNotionalToProto(mat.partyTakerNotionalVolume),
   380  		MarketToPartyTakerNotionalVolume: marketToPartyTakerNotionalToProto(mat.marketToPartyTakerNotionalVolume),
   381  		EpochTakerFees:                   epochTakerFeesToProto(mat.takerFeesPaidInEpoch),
   382  		GameEligibilityTracker:           epochEligitbilityToProto(mat.eligibilityInEpoch),
   383  	}
   384  }
   385  
   386  // get the serialised form and hash of the given key.
   387  func (mat *MarketActivityTracker) serialise(k string) ([]byte, error) {
   388  	if k != key {
   389  		return nil, ErrSnapshotKeyDoesNotExist
   390  	}
   391  	payload := types.Payload{
   392  		Data: &types.PayloadMarketActivityTracker{
   393  			MarketActivityData: mat.serialiseFeesTracker(),
   394  		},
   395  	}
   396  	x := payload.IntoProto()
   397  	data, err := proto.Marshal(x)
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  
   402  	mat.ss.serialised = data
   403  	return data, nil
   404  }
   405  
   406  func (mat *MarketActivityTracker) GetState(k string) ([]byte, []types.StateProvider, error) {
   407  	state, err := mat.serialise(k)
   408  	return state, nil, err
   409  }
   410  
   411  func (mat *MarketActivityTracker) LoadState(_ context.Context, p *types.Payload) ([]types.StateProvider, error) {
   412  	if mat.Namespace() != p.Data.Namespace() {
   413  		return nil, types.ErrInvalidSnapshotNamespace
   414  	}
   415  	// see what we're reloading
   416  	switch pl := p.Data.(type) {
   417  	case *types.PayloadMarketActivityTracker:
   418  		mat.restore(pl.MarketActivityData)
   419  		var err error
   420  		mat.ss.serialised, err = proto.Marshal(p.IntoProto())
   421  		return nil, err
   422  	default:
   423  		return nil, types.ErrUnknownSnapshotType
   424  	}
   425  }
   426  
   427  func marketTrackerFromProto(tracker *checkpoint.MarketActivityTracker) *marketTracker {
   428  	valueTrades, _ := num.UintFromString(tracker.ValueTraded, 10)
   429  	notionalVolumeForEpoch := num.UintZero()
   430  	if len(tracker.NotionalVolumeForEpoch) > 0 {
   431  		notionalVolumeForEpoch, _ = num.UintFromString(tracker.NotionalVolumeForEpoch, 10)
   432  	}
   433  	epochNotionalVolume := []*num.Uint{}
   434  	for _, envStr := range tracker.EpochNotionalVolume {
   435  		env, _ := num.UintFromString(envStr, 10)
   436  		epochNotionalVolume = append(epochNotionalVolume, env)
   437  	}
   438  
   439  	mft := &marketTracker{
   440  		asset:                  tracker.Asset,
   441  		proposer:               tracker.Proposer,
   442  		proposersPaid:          map[string]struct{}{},
   443  		readyToDelete:          tracker.ReadyToDelete,
   444  		valueTraded:            valueTrades,
   445  		makerFeesReceived:      map[string]*num.Uint{},
   446  		makerFeesPaid:          map[string]*num.Uint{},
   447  		lpFees:                 map[string]*num.Uint{},
   448  		buybackFeesPaid:        map[string]*num.Uint{},
   449  		treasuryFeesPaid:       map[string]*num.Uint{},
   450  		infraFees:              map[string]*num.Uint{},
   451  		lpPaidFees:             map[string]*num.Uint{},
   452  		totalMakerFeesReceived: num.UintZero(),
   453  		totalMakerFeesPaid:     num.UintZero(),
   454  		totalLpFees:            num.UintZero(),
   455  		twPosition:             map[string]*twPosition{},
   456  		partyM2M:               map[string]num.Decimal{},
   457  		partyRealisedReturn:    map[string]num.Decimal{},
   458  		twNotional:             map[string]*twNotional{},
   459  
   460  		epochTotalMakerFeesReceived: []*num.Uint{},
   461  		epochTotalMakerFeesPaid:     []*num.Uint{},
   462  		epochTotalLpFees:            []*num.Uint{},
   463  		epochMakerFeesReceived:      []map[string]*num.Uint{},
   464  		epochMakerFeesPaid:          []map[string]*num.Uint{},
   465  		epochLpFees:                 []map[string]*num.Uint{},
   466  		epochPartyM2M:               []map[string]num.Decimal{},
   467  		epochPartyRealisedReturn:    []map[string]num.Decimal{},
   468  		epochTimeWeightedPosition:   []map[string]uint64{},
   469  		epochTimeWeightedNotional:   []map[string]*num.Uint{},
   470  		allPartiesCache:             map[string]struct{}{},
   471  		ammPartiesCache:             map[string]struct{}{},
   472  		notionalVolumeForEpoch:      notionalVolumeForEpoch,
   473  		epochNotionalVolume:         epochNotionalVolume,
   474  	}
   475  
   476  	for _, party := range tracker.AmmParties {
   477  		mft.ammPartiesCache[party] = struct{}{}
   478  	}
   479  
   480  	for _, bpfpa := range tracker.BonusPaid {
   481  		mft.proposersPaid[bpfpa] = struct{}{}
   482  	}
   483  
   484  	if len(tracker.MakerFeesReceived) > 0 {
   485  		total := num.UintZero()
   486  		for _, mf := range tracker.MakerFeesReceived {
   487  			fee, _ := num.UintFromString(mf.Fee, 10)
   488  			total.AddSum(fee)
   489  			mft.makerFeesReceived[mf.Party] = fee
   490  			mft.allPartiesCache[mf.Party] = struct{}{}
   491  		}
   492  		mft.totalMakerFeesReceived = total
   493  	}
   494  
   495  	if len(tracker.MakerFeesPaid) > 0 {
   496  		total := num.UintZero()
   497  		for _, mf := range tracker.MakerFeesPaid {
   498  			fee, _ := num.UintFromString(mf.Fee, 10)
   499  			total.AddSum(fee)
   500  			mft.makerFeesPaid[mf.Party] = fee
   501  			mft.allPartiesCache[mf.Party] = struct{}{}
   502  		}
   503  		mft.totalMakerFeesPaid = total
   504  	}
   505  
   506  	if len(tracker.LpFees) > 0 {
   507  		total := num.UintZero()
   508  		for _, mf := range tracker.LpFees {
   509  			fee, _ := num.UintFromString(mf.Fee, 10)
   510  			total.AddSum(fee)
   511  			mft.lpFees[mf.Party] = fee
   512  			mft.allPartiesCache[mf.Party] = struct{}{}
   513  		}
   514  		mft.totalLpFees = total
   515  	}
   516  
   517  	if len(tracker.InfraFees) > 0 {
   518  		for _, mf := range tracker.InfraFees {
   519  			fee, _ := num.UintFromString(mf.Fee, 10)
   520  			mft.infraFees[mf.Party] = fee
   521  			mft.allPartiesCache[mf.Party] = struct{}{}
   522  		}
   523  	}
   524  
   525  	if len(tracker.BuyBackFees) > 0 {
   526  		for _, mf := range tracker.BuyBackFees {
   527  			fee, _ := num.UintFromString(mf.Fee, 10)
   528  			mft.buybackFeesPaid[mf.Party] = fee
   529  			mft.allPartiesCache[mf.Party] = struct{}{}
   530  		}
   531  	}
   532  
   533  	if len(tracker.TreasuryFees) > 0 {
   534  		for _, mf := range tracker.TreasuryFees {
   535  			fee, _ := num.UintFromString(mf.Fee, 10)
   536  			mft.treasuryFeesPaid[mf.Party] = fee
   537  			mft.allPartiesCache[mf.Party] = struct{}{}
   538  		}
   539  	}
   540  
   541  	if len(tracker.LpPaidFees) > 0 {
   542  		for _, mf := range tracker.LpPaidFees {
   543  			fee, _ := num.UintFromString(mf.Fee, 10)
   544  			mft.lpPaidFees[mf.Party] = fee
   545  			mft.allPartiesCache[mf.Party] = struct{}{}
   546  		}
   547  	}
   548  
   549  	if len(tracker.TimeWeightedPosition) > 0 {
   550  		for _, tp := range tracker.TimeWeightedPosition {
   551  			mft.twPosition[tp.Party] = &twPosition{
   552  				position:               tp.Position,
   553  				t:                      time.Unix(0, tp.Time),
   554  				currentEpochTWPosition: tp.TwPosition,
   555  			}
   556  			mft.allPartiesCache[tp.Party] = struct{}{}
   557  		}
   558  	}
   559  
   560  	if len(tracker.TimeWeightedNotional) > 0 {
   561  		for _, tn := range tracker.TimeWeightedNotional {
   562  			mft.twNotional[tn.Party] = &twNotional{
   563  				notional:               num.UintFromBytes(tn.Notional),
   564  				t:                      time.Unix(0, tn.Time),
   565  				currentEpochTWNotional: num.UintFromBytes(tn.TwNotional),
   566  			}
   567  			if len(tn.Price) > 0 {
   568  				mft.twNotional[tn.Party].price = num.UintFromBytes(tn.Price)
   569  			}
   570  			mft.allPartiesCache[tn.Party] = struct{}{}
   571  		}
   572  	}
   573  
   574  	if len(tracker.ReturnsData) > 0 {
   575  		for _, rd := range tracker.ReturnsData {
   576  			ret, _ := num.UnmarshalBinaryDecimal(rd.Return)
   577  			mft.partyM2M[rd.Party] = ret
   578  			mft.allPartiesCache[rd.Party] = struct{}{}
   579  		}
   580  	}
   581  
   582  	if len(tracker.RealisedReturns) > 0 {
   583  		for _, rd := range tracker.RealisedReturns {
   584  			ret, _ := num.UnmarshalBinaryDecimal(rd.Return)
   585  			mft.partyRealisedReturn[rd.Party] = ret
   586  			mft.allPartiesCache[rd.Party] = struct{}{}
   587  		}
   588  	}
   589  
   590  	mft.epochMakerFeesPaid = loadFeesHistory(tracker.MakerFeesPaidHistory, mft.allPartiesCache)
   591  	mft.epochMakerFeesReceived = loadFeesHistory(tracker.MakerFeesReceivedHistory, mft.allPartiesCache)
   592  	mft.epochLpFees = loadFeesHistory(tracker.LpFeesHistory, mft.allPartiesCache)
   593  
   594  	mft.epochTotalMakerFeesPaid = updateTotalHistory(mft.epochMakerFeesPaid)
   595  	mft.epochTotalMakerFeesReceived = updateTotalHistory(mft.epochMakerFeesReceived)
   596  	mft.epochTotalLpFees = updateTotalHistory(mft.epochLpFees)
   597  
   598  	if len(tracker.TimeWeightedPositionDataHistory) > 0 {
   599  		for _, etwnd := range tracker.TimeWeightedPositionDataHistory {
   600  			m := make(map[string]uint64, len(etwnd.PartyTimeWeightedPositions))
   601  			for _, partyPositions := range etwnd.PartyTimeWeightedPositions {
   602  				m[partyPositions.Party] = partyPositions.TwPosition
   603  				mft.allPartiesCache[partyPositions.Party] = struct{}{}
   604  			}
   605  			mft.epochTimeWeightedPosition = append(mft.epochTimeWeightedPosition, m)
   606  		}
   607  	}
   608  
   609  	if len(tracker.TimeWeightedNotionalDataHistory) > 0 {
   610  		for _, etwnd := range tracker.TimeWeightedNotionalDataHistory {
   611  			m := make(map[string]*num.Uint, len(etwnd.PartyTimeWeightedNotionals))
   612  			for _, partyNotionals := range etwnd.PartyTimeWeightedNotionals {
   613  				m[partyNotionals.Party] = num.UintFromBytes(partyNotionals.TwNotional)
   614  				mft.allPartiesCache[partyNotionals.Party] = struct{}{}
   615  			}
   616  			mft.epochTimeWeightedNotional = append(mft.epochTimeWeightedNotional, m)
   617  		}
   618  	}
   619  
   620  	if len(tracker.ReturnsDataHistory) > 0 {
   621  		for _, erd := range tracker.ReturnsDataHistory {
   622  			returns := make(map[string]num.Decimal, len(erd.Returns))
   623  			for _, rd := range erd.Returns {
   624  				ret, _ := num.UnmarshalBinaryDecimal(rd.Return)
   625  				returns[rd.Party] = ret
   626  				mft.allPartiesCache[rd.Party] = struct{}{}
   627  			}
   628  			mft.epochPartyM2M = append(mft.epochPartyM2M, returns)
   629  		}
   630  	}
   631  
   632  	if len(tracker.RealisedReturnsHistory) > 0 {
   633  		for _, erd := range tracker.RealisedReturnsHistory {
   634  			returns := make(map[string]num.Decimal, len(erd.Returns))
   635  			for _, rd := range erd.Returns {
   636  				ret, _ := num.UnmarshalBinaryDecimal(rd.Return)
   637  				returns[rd.Party] = ret
   638  				mft.allPartiesCache[rd.Party] = struct{}{}
   639  			}
   640  			mft.epochPartyRealisedReturn = append(mft.epochPartyRealisedReturn, returns)
   641  		}
   642  	}
   643  
   644  	mft.asset = tracker.Asset
   645  	return mft
   646  }
   647  
   648  func updateTotalHistory(data []map[string]*num.Uint) []*num.Uint {
   649  	ret := make([]*num.Uint, 0, len(data))
   650  	for _, v := range data {
   651  		total := num.UintZero()
   652  		for _, u := range v {
   653  			total.AddSum(u)
   654  		}
   655  		ret = append(ret, total)
   656  	}
   657  	return ret
   658  }
   659  
   660  func loadFeesHistory(cpFeesData []*checkpoint.EpochPartyFees, allParties map[string]struct{}) []map[string]*num.Uint {
   661  	feeData := make([]map[string]*num.Uint, 0, len(cpFeesData))
   662  	for _, pfd := range cpFeesData {
   663  		epochTotal := num.UintZero()
   664  		m := make(map[string]*num.Uint, len(pfd.PartyFees))
   665  		for _, pfh := range pfd.PartyFees {
   666  			fee := num.UintFromBytes(pfh.Fee)
   667  			m[pfh.Party] = fee
   668  			epochTotal.AddSum(fee)
   669  			allParties[pfh.Party] = struct{}{}
   670  		}
   671  		feeData = append(feeData, m)
   672  	}
   673  	return feeData
   674  }
   675  
   676  func (mat *MarketActivityTracker) restore(tracker *snapshot.MarketTracker) {
   677  	for _, data := range tracker.MarketActivity {
   678  		if _, ok := mat.assetToMarketTrackers[data.Asset]; !ok {
   679  			mat.assetToMarketTrackers[data.Asset] = map[string]*marketTracker{}
   680  		}
   681  		mat.assetToMarketTrackers[data.Asset][data.Market] = marketTrackerFromProto(data)
   682  	}
   683  	for _, tnv := range tracker.TakerNotionalVolume {
   684  		if len(tnv.Volume) > 0 {
   685  			mat.partyTakerNotionalVolume[tnv.Party] = num.UintFromBytes(tnv.Volume)
   686  		}
   687  	}
   688  	for _, marketToPartyStats := range tracker.MarketToPartyTakerNotionalVolume {
   689  		mat.marketToPartyTakerNotionalVolume[marketToPartyStats.Market] = map[string]*num.Uint{}
   690  		for _, partyStats := range marketToPartyStats.TakerNotionalVolume {
   691  			if len(partyStats.Volume) > 0 {
   692  				mat.marketToPartyTakerNotionalVolume[marketToPartyStats.Market][partyStats.Party] = num.UintFromBytes(partyStats.Volume)
   693  			}
   694  		}
   695  	}
   696  	if tracker.EpochTakerFees != nil {
   697  		for _, epochData := range tracker.EpochTakerFees {
   698  			epochMap := map[string]map[string]map[string]*num.Uint{}
   699  			for _, assetMarketParty := range epochData.EpochPartyTakerFeesPaid {
   700  				if _, ok := epochMap[assetMarketParty.Asset]; !ok {
   701  					epochMap[assetMarketParty.Asset] = map[string]map[string]*num.Uint{}
   702  				}
   703  				if _, ok := epochMap[assetMarketParty.Asset][assetMarketParty.Market]; !ok {
   704  					epochMap[assetMarketParty.Asset][assetMarketParty.Market] = map[string]*num.Uint{}
   705  				}
   706  				for _, tf := range assetMarketParty.TakerFees {
   707  					epochMap[assetMarketParty.Asset][assetMarketParty.Market][tf.Party] = num.UintFromBytes(tf.TakerFees)
   708  				}
   709  			}
   710  			mat.takerFeesPaidInEpoch = append(mat.takerFeesPaidInEpoch, epochMap)
   711  		}
   712  	}
   713  	if tracker.GameEligibilityTracker != nil {
   714  		for _, get := range tracker.GameEligibilityTracker {
   715  			mat.eligibilityInEpoch[get.GameId] = make([]map[string]struct{}, len(get.EpochEligibility))
   716  			for i, epoch := range get.EpochEligibility {
   717  				mat.eligibilityInEpoch[get.GameId][i] = make(map[string]struct{}, len(epoch.EligibleParties))
   718  				for _, party := range epoch.EligibleParties {
   719  					mat.eligibilityInEpoch[get.GameId][i][party] = struct{}{}
   720  				}
   721  			}
   722  		}
   723  	}
   724  }
   725  
   726  // OnEpochRestore is called when the state of the epoch changes, we only care about new epochs starting.
   727  func (mat *MarketActivityTracker) OnEpochRestore(_ context.Context, epoch types.Epoch) {
   728  	mat.currentEpoch = epoch.Seq
   729  	mat.epochStartTime = epoch.StartTime
   730  }