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 }