code.vegaprotocol.io/vega@v0.79.0/datanode/api/trading_data_v2.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 api 17 18 import ( 19 "archive/zip" 20 "bufio" 21 "bytes" 22 "context" 23 "fmt" 24 "math" 25 "math/rand" 26 "reflect" 27 "slices" 28 "sort" 29 "strconv" 30 "strings" 31 "time" 32 33 "code.vegaprotocol.io/vega/core/banking" 34 "code.vegaprotocol.io/vega/core/events" 35 "code.vegaprotocol.io/vega/core/execution/amm" 36 "code.vegaprotocol.io/vega/core/netparams" 37 "code.vegaprotocol.io/vega/core/risk" 38 "code.vegaprotocol.io/vega/core/types" 39 "code.vegaprotocol.io/vega/datanode/candlesv2" 40 "code.vegaprotocol.io/vega/datanode/entities" 41 "code.vegaprotocol.io/vega/datanode/metrics" 42 "code.vegaprotocol.io/vega/datanode/networkhistory/fsutil" 43 "code.vegaprotocol.io/vega/datanode/networkhistory/segment" 44 "code.vegaprotocol.io/vega/datanode/networkhistory/store" 45 "code.vegaprotocol.io/vega/datanode/service" 46 "code.vegaprotocol.io/vega/datanode/sqlstore" 47 "code.vegaprotocol.io/vega/datanode/vegatime" 48 "code.vegaprotocol.io/vega/libs/crypto" 49 "code.vegaprotocol.io/vega/libs/num" 50 "code.vegaprotocol.io/vega/libs/ptr" 51 "code.vegaprotocol.io/vega/logging" 52 v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2" 53 "code.vegaprotocol.io/vega/protos/vega" 54 cmdsV1 "code.vegaprotocol.io/vega/protos/vega/commands/v1" 55 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 56 v1 "code.vegaprotocol.io/vega/protos/vega/events/v1" 57 "code.vegaprotocol.io/vega/version" 58 59 "github.com/georgysavva/scany/pgxscan" 60 "github.com/pkg/errors" 61 "github.com/shopspring/decimal" 62 "golang.org/x/exp/maps" 63 "golang.org/x/sync/errgroup" 64 "google.golang.org/grpc/metadata" 65 "google.golang.org/protobuf/proto" 66 ) 67 68 const ( 69 networkPartyID = "network" 70 ) 71 72 // When returning an 'initial image' snapshot, how many updates to batch into each page. 73 var snapshotPageSize = 50 74 75 // When sending files in chunks, how much data to send per stream message. 76 var httpBodyChunkSize = 1024 * 1024 77 78 type TradingDataServiceV2 struct { 79 v2.UnimplementedTradingDataServiceServer 80 config Config 81 log *logging.Logger 82 eventService EventService 83 orderService *service.Order 84 networkLimitsService *service.NetworkLimits 85 MarketDataService MarketDataService 86 tradeService *service.Trade 87 multiSigService *service.MultiSig 88 notaryService *service.Notary 89 AssetService AssetService 90 candleService *candlesv2.Svc 91 MarketsService MarketsService 92 partyService *service.Party 93 riskService *service.Risk 94 positionService *service.Position 95 AccountService *service.Account 96 RewardService *service.Reward 97 depositService *service.Deposit 98 withdrawalService *service.Withdrawal 99 oracleSpecService *service.OracleSpec 100 oracleDataService *service.OracleData 101 liquidityProvisionService *service.LiquidityProvision 102 governanceService *service.Governance 103 transfersService *service.Transfer 104 delegationService *service.Delegation 105 marketDepthService *service.MarketDepth 106 nodeService *service.Node 107 EpochService EpochService 108 RiskFactorService RiskFactorService 109 networkParameterService *service.NetworkParameter 110 checkpointService *service.Checkpoint 111 stakeLinkingService *service.StakeLinking 112 ledgerService *service.Ledger 113 keyRotationService *service.KeyRotations 114 ethereumKeyRotationService *service.EthereumKeyRotation 115 blockService BlockService 116 protocolUpgradeService *service.ProtocolUpgrade 117 NetworkHistoryService NetworkHistoryService 118 coreSnapshotService *service.SnapshotData 119 stopOrderService *service.StopOrders 120 fundingPeriodService *service.FundingPeriods 121 partyActivityStreak *service.PartyActivityStreak 122 fundingPaymentService *service.FundingPayment 123 referralProgramService *service.ReferralPrograms 124 ReferralSetsService ReferralSetService 125 teamsService *service.Teams 126 feesStatsService *service.FeesStats 127 VolumeDiscountStatsService VolumeDiscountService 128 volumeDiscountProgramService *service.VolumeDiscountPrograms 129 volumeRebateStatsService *service.VolumeRebateStats 130 volumeRebateProgramService *service.VolumeRebatePrograms 131 paidLiquidityFeesStatsService *service.PaidLiquidityFeesStats 132 partyLockedBalances *service.PartyLockedBalances 133 partyVestingBalances *service.PartyVestingBalances 134 vestingStats *service.VestingStats 135 transactionResults *service.TransactionResults 136 gamesService *service.Games 137 marginModesService *service.MarginModes 138 twNotionalPositionService *service.TimeWeightedNotionalPosition 139 gameScoreService *service.GameScore 140 AMMPoolService AMMService 141 partyDiscountStats PartyStatsSvc 142 } 143 144 func (t *TradingDataServiceV2) SetLogger(l *logging.Logger) { 145 t.log = l 146 } 147 148 func (t *TradingDataServiceV2) GetPartyVestingStats( 149 ctx context.Context, 150 req *v2.GetPartyVestingStatsRequest, 151 ) (*v2.GetPartyVestingStatsResponse, error) { 152 if !crypto.IsValidVegaPubKey(req.PartyId) { 153 return nil, formatE(ErrInvalidPartyID) 154 } 155 156 stats, err := t.vestingStats.GetByPartyID(ctx, req.PartyId) 157 if err != nil { 158 return nil, formatE(err) 159 } 160 161 res := &v2.GetPartyVestingStatsResponse{ 162 PartyId: stats.PartyID.String(), 163 EpochSeq: stats.AtEpoch, 164 RewardBonusMultiplier: stats.RewardBonusMultiplier.String(), 165 QuantumBalance: stats.QuantumBalance.String(), 166 } 167 168 // set minimum values if the summed values are zero 169 if stats.SummedRewardBonusMultiplier.IsZero() { 170 res.SummedRewardBonusMultiplier = res.RewardBonusMultiplier 171 } else { 172 res.SummedRewardBonusMultiplier = stats.SummedRewardBonusMultiplier.String() 173 } 174 175 if stats.SummedQuantumBalance.IsZero() { 176 res.SummedQuantumBalance = res.QuantumBalance 177 } else { 178 res.SummedQuantumBalance = stats.SummedQuantumBalance.String() 179 } 180 181 return res, nil 182 } 183 184 func (t *TradingDataServiceV2) GetVestingBalancesSummary( 185 ctx context.Context, 186 req *v2.GetVestingBalancesSummaryRequest, 187 ) (*v2.GetVestingBalancesSummaryResponse, error) { 188 if !crypto.IsValidVegaPubKey(req.PartyId) { 189 return nil, formatE(ErrInvalidPartyID) 190 } 191 192 var assetId *entities.AssetID 193 if req.AssetId != nil { 194 if !crypto.IsValidVegaID(*req.AssetId) { 195 return nil, formatE(ErrInvalidAssetID) 196 } 197 assetId = ptr.From(entities.AssetID(*req.AssetId)) 198 } 199 200 // then get separately both things 201 vestingBalances, err := t.partyVestingBalances.Get( 202 ctx, ptr.From(entities.PartyID(req.PartyId)), assetId, 203 ) 204 if err != nil { 205 return nil, formatE(err) 206 } 207 208 lockedBalances, err := t.partyLockedBalances.Get( 209 ctx, ptr.From(entities.PartyID(req.PartyId)), assetId, 210 ) 211 if err != nil { 212 return nil, formatE(err) 213 } 214 215 var ( 216 outVesting = make([]*eventspb.PartyVestingBalance, 0, len(vestingBalances)) 217 outLocked = make([]*eventspb.PartyLockedBalance, 0, len(lockedBalances)) 218 epoch *uint64 219 setEpoch = func(e uint64) { 220 if epoch == nil { 221 epoch = ptr.From(e) 222 return 223 } 224 if *epoch < e { 225 epoch = ptr.From(e) 226 } 227 } 228 ) 229 230 for _, v := range vestingBalances { 231 setEpoch(v.AtEpoch) 232 outVesting = append( 233 outVesting, &eventspb.PartyVestingBalance{ 234 Asset: v.AssetID.String(), 235 Balance: v.Balance.String(), 236 }, 237 ) 238 } 239 240 for _, v := range lockedBalances { 241 setEpoch(v.AtEpoch) 242 outLocked = append( 243 outLocked, &eventspb.PartyLockedBalance{ 244 Asset: v.AssetID.String(), 245 UntilEpoch: v.UntilEpoch, 246 Balance: v.Balance.String(), 247 }, 248 ) 249 } 250 251 return &v2.GetVestingBalancesSummaryResponse{ 252 PartyId: req.PartyId, 253 EpochSeq: epoch, 254 VestingBalances: outVesting, 255 LockedBalances: outLocked, 256 }, nil 257 } 258 259 func (t *TradingDataServiceV2) GetPartyActivityStreak(ctx context.Context, req *v2.GetPartyActivityStreakRequest) (*v2.GetPartyActivityStreakResponse, error) { 260 defer metrics.StartAPIRequestAndTimeGRPC("GetPartyActivityStreak")() 261 262 if !crypto.IsValidVegaPubKey(req.PartyId) { 263 return nil, formatE(ErrInvalidPartyID) 264 } 265 266 activityStreak, err := t.partyActivityStreak.Get(ctx, entities.PartyID(req.PartyId), req.Epoch) 267 if err != nil { 268 return nil, formatE(err) 269 } 270 271 return &v2.GetPartyActivityStreakResponse{ 272 ActivityStreak: activityStreak.ToProto(), 273 }, nil 274 } 275 276 func (t *TradingDataServiceV2) ListFundingPayments(ctx context.Context, req *v2.ListFundingPaymentsRequest) (*v2.ListFundingPaymentsResponse, error) { 277 defer metrics.StartAPIRequestAndTimeGRPC("ListFundingPayments")() 278 279 if !crypto.IsValidVegaPubKey(req.PartyId) { 280 return nil, formatE(ErrInvalidPartyID) 281 } 282 283 var marketID *entities.MarketID 284 if req.MarketId != nil { 285 if !crypto.IsValidVegaPubKey(*req.MarketId) { 286 return nil, formatE(ErrInvalidMarketID) 287 } 288 marketID = ptr.From(entities.MarketID(*req.MarketId)) 289 } 290 291 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 292 if err != nil { 293 return nil, formatE(ErrInvalidPagination, err) 294 } 295 296 fundingPayments, pageInfo, err := t.fundingPaymentService.List( 297 ctx, entities.PartyID(req.PartyId), marketID, pagination) 298 if err != nil { 299 return nil, formatE(err) 300 } 301 302 edges, err := makeEdges[*v2.FundingPaymentEdge](fundingPayments) 303 if err != nil { 304 return nil, formatE(err) 305 } 306 307 connection := &v2.FundingPaymentConnection{ 308 Edges: edges, 309 PageInfo: pageInfo.ToProto(), 310 } 311 312 return &v2.ListFundingPaymentsResponse{ 313 FundingPayments: connection, 314 }, nil 315 } 316 317 func (t *TradingDataServiceV2) ListGamePartyScores(ctx context.Context, req *v2.ListGamePartyScoresRequest) (*v2.ListGamePartyScoresResponse, error) { 318 defer metrics.StartAPIRequestAndTimeGRPC("ListGamePartyScore")() 319 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 320 if err != nil { 321 return nil, formatE(ErrInvalidPagination, err) 322 } 323 324 var partyIDs []entities.PartyID 325 var teamIDs []entities.TeamID 326 var gameIDs []entities.GameID 327 var epochFromID *uint64 328 var epochToID *uint64 329 if req.Filter != nil { 330 partyIDs = make([]entities.PartyID, 0, len(req.Filter.PartyIds)) 331 for _, pid := range req.Filter.PartyIds { 332 partyIDs = append(partyIDs, entities.PartyID(pid)) 333 } 334 teamIDs = make([]entities.TeamID, 0, len(req.Filter.TeamIds)) 335 for _, tid := range req.Filter.TeamIds { 336 teamIDs = append(teamIDs, entities.TeamID(tid)) 337 } 338 339 gameIDs = make([]entities.GameID, 0, len(req.Filter.GameIds)) 340 for _, gid := range req.Filter.GameIds { 341 gameIDs = append(gameIDs, entities.GameID(gid)) 342 } 343 epochFromID = req.Filter.EpochFrom 344 epochToID = req.Filter.EpochTo 345 } 346 347 partyScores, pageInfo, err := t.gameScoreService.ListPartyScores( 348 ctx, gameIDs, partyIDs, teamIDs, epochFromID, epochToID, pagination) 349 if err != nil { 350 return nil, formatE(err) 351 } 352 353 edges, err := makeEdges[*v2.GamePartyScoresEdge](partyScores) 354 if err != nil { 355 return nil, formatE(err) 356 } 357 358 connection := &v2.GamePartyScoresConnection{ 359 Edges: edges, 360 PageInfo: pageInfo.ToProto(), 361 } 362 363 return &v2.ListGamePartyScoresResponse{ 364 PartyScores: connection, 365 }, nil 366 } 367 368 func (t *TradingDataServiceV2) ListGameTeamScores(ctx context.Context, req *v2.ListGameTeamScoresRequest) (*v2.ListGameTeamScoresResponse, error) { 369 defer metrics.StartAPIRequestAndTimeGRPC("ListGameTeamScores")() 370 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 371 if err != nil { 372 return nil, formatE(ErrInvalidPagination, err) 373 } 374 var teamIDs []entities.TeamID 375 var gameIDs []entities.GameID 376 var epochFromID *uint64 377 var epochToID *uint64 378 if req.Filter != nil { 379 teamIDs = make([]entities.TeamID, 0, len(req.Filter.TeamIds)) 380 for _, tid := range req.Filter.TeamIds { 381 teamIDs = append(teamIDs, entities.TeamID(tid)) 382 } 383 384 gameIDs = make([]entities.GameID, 0, len(req.Filter.GameIds)) 385 for _, gid := range req.Filter.GameIds { 386 gameIDs = append(gameIDs, entities.GameID(gid)) 387 } 388 epochFromID = req.Filter.EpochFrom 389 epochToID = req.Filter.EpochTo 390 } 391 392 teamScores, pageInfo, err := t.gameScoreService.ListTeamScores( 393 ctx, gameIDs, teamIDs, epochFromID, epochToID, pagination) 394 if err != nil { 395 return nil, formatE(err) 396 } 397 398 edges, err := makeEdges[*v2.GameTeamScoresEdge](teamScores) 399 if err != nil { 400 return nil, formatE(err) 401 } 402 403 connection := &v2.GameTeamScoresConnection{ 404 Edges: edges, 405 PageInfo: pageInfo.ToProto(), 406 } 407 408 return &v2.ListGameTeamScoresResponse{ 409 TeamScores: connection, 410 }, nil 411 } 412 413 // ListAccounts lists accounts matching the request. 414 func (t *TradingDataServiceV2) ListAccounts(ctx context.Context, req *v2.ListAccountsRequest) (*v2.ListAccountsResponse, error) { 415 defer metrics.StartAPIRequestAndTimeGRPC("ListAccountsV2")() 416 417 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 418 if err != nil { 419 return nil, formatE(ErrInvalidPagination, err) 420 } 421 422 var partyPerDerivedKey map[string]string 423 424 if req.Filter != nil { 425 marketIDs := NewVegaIDSlice(req.Filter.MarketIds...) 426 if err := marketIDs.Ensure(); err != nil { 427 return nil, formatE(err, errors.New("one or more market id is invalid")) 428 } 429 req.Filter.MarketIds = marketIDs 430 431 partyIDs := NewVegaIDSlice(req.Filter.PartyIds...) 432 if err := partyIDs.Ensure(); err != nil { 433 return nil, formatE(err, errors.New("one or more party id is invalid")) 434 } 435 436 req.Filter.PartyIds = partyIDs 437 438 if includeDerivedParties := ptr.UnBox(req.IncludeDerivedParties); includeDerivedParties { 439 partyPerDerivedKey = map[string]string{} 440 for _, pid := range req.Filter.PartyIds { 441 derivedKeys, err := t.AMMPoolService.GetSubKeysForParties(ctx, []string{pid}, req.Filter.MarketIds) 442 if err != nil { 443 return nil, formatE(err) 444 } 445 req.Filter.PartyIds = append(req.Filter.PartyIds, derivedKeys...) 446 for _, did := range derivedKeys { 447 partyPerDerivedKey[did] = pid 448 } 449 } 450 } 451 } 452 453 filter, err := entities.AccountFilterFromProto(req.Filter) 454 if err != nil { 455 return nil, formatE(ErrInvalidFilter, err) 456 } 457 458 accountBalances, pageInfo, err := t.AccountService.QueryBalances(ctx, filter, pagination) 459 if err != nil { 460 return nil, formatE(ErrAccountServiceListAccounts, err) 461 } 462 463 edges, err := makeEdges[*v2.AccountEdge](accountBalances, partyPerDerivedKey) 464 if err != nil { 465 return nil, formatE(err) 466 } 467 468 accountsConnection := &v2.AccountsConnection{ 469 Edges: edges, 470 PageInfo: pageInfo.ToProto(), 471 } 472 473 return &v2.ListAccountsResponse{ 474 Accounts: accountsConnection, 475 }, nil 476 } 477 478 // ObserveAccounts streams account balances matching the request. 479 func (t *TradingDataServiceV2) ObserveAccounts(req *v2.ObserveAccountsRequest, srv v2.TradingDataService_ObserveAccountsServer) error { 480 // Wrap context from the request into cancellable. We can close internal chan on error. 481 ctx, cancel := context.WithCancel(srv.Context()) 482 defer cancel() 483 484 partyPerDerivedKey := map[string]string{} 485 486 if includeDerivedParties := ptr.UnBox(req.IncludeDerivedParties); includeDerivedParties { 487 subKeys, err := t.AMMPoolService.GetSubKeysForParties(ctx, []string{req.PartyId}, []string{req.MarketId}) 488 if err != nil { 489 return formatE(err) 490 } 491 // should really only be a single key, but just in case... 492 for _, sk := range subKeys { 493 partyPerDerivedKey[sk] = req.PartyId 494 } 495 } 496 497 // First get the 'initial image' of accounts matching the request and send those 498 if err := t.sendAccountsSnapshot(ctx, req, srv, partyPerDerivedKey); err != nil { 499 return formatE(ErrFailedToSendSnapshot, err) 500 } 501 502 partyPerDerivedKey[req.PartyId] = req.PartyId 503 504 accountsChan, ref := t.AccountService.ObserveAccountBalances( 505 ctx, t.config.StreamRetries, req.MarketId, req.Asset, req.Type, partyPerDerivedKey) 506 507 if t.log.GetLevel() == logging.DebugLevel { 508 t.log.Debug("Accounts subscriber - new rpc stream", logging.Uint64("ref", ref)) 509 } 510 511 return observeBatch(ctx, t.log, "Accounts", accountsChan, ref, func(accounts []entities.AccountBalance) error { 512 protos := make([]*v2.AccountBalance, len(accounts)) 513 for i := 0; i < len(accounts); i++ { 514 var parentPartyID *string 515 if party, ok := partyPerDerivedKey[accounts[i].PartyID.String()]; ok { 516 parentPartyID = &party 517 } 518 519 protos[i] = accounts[i].ToProtoWithParent(parentPartyID) 520 } 521 batches := batch(protos, snapshotPageSize) 522 523 for _, batch := range batches { 524 updates := &v2.AccountUpdates{Accounts: batch} 525 responseUpdates := &v2.ObserveAccountsResponse_Updates{Updates: updates} 526 response := &v2.ObserveAccountsResponse{Response: responseUpdates} 527 if err := srv.Send(response); err != nil { 528 return errors.Wrap(err, "sending accounts updates") 529 } 530 } 531 532 return nil 533 }) 534 } 535 536 func (t *TradingDataServiceV2) sendAccountsSnapshot(ctx context.Context, 537 req *v2.ObserveAccountsRequest, 538 srv v2.TradingDataService_ObserveAccountsServer, 539 partyPerDerivedKey map[string]string, 540 ) error { 541 filter := entities.AccountFilter{ 542 PartyIDs: entities.NewPartyIDSlice(maps.Keys(partyPerDerivedKey)...), 543 } 544 if req.Asset != "" { 545 filter.AssetID = entities.AssetID(req.Asset) 546 } 547 if req.PartyId != "" { 548 filter.PartyIDs = append(filter.PartyIDs, entities.PartyID(req.PartyId)) 549 } 550 if req.MarketId != "" { 551 filter.MarketIDs = append(filter.MarketIDs, entities.MarketID(req.MarketId)) 552 } 553 if req.Type != vega.AccountType_ACCOUNT_TYPE_UNSPECIFIED { 554 filter.AccountTypes = append(filter.AccountTypes, req.Type) 555 } 556 557 accounts, pageInfo, err := t.AccountService.QueryBalances(ctx, filter, entities.CursorPagination{}) 558 if err != nil { 559 return errors.Wrap(err, "fetching account balance initial image") 560 } 561 562 if pageInfo.HasNextPage { 563 return errors.New("initial image spans multiple pages") 564 } 565 566 protos := make([]*v2.AccountBalance, len(accounts)) 567 for i := 0; i < len(accounts); i++ { 568 var parentPartyID *string 569 if party, ok := partyPerDerivedKey[accounts[i].PartyID.String()]; ok { 570 parentPartyID = &party 571 } 572 573 protos[i] = accounts[i].ToProtoWithParent(parentPartyID) 574 } 575 576 batches := batch(protos, snapshotPageSize) 577 for i, batch := range batches { 578 isLast := i == len(batches)-1 579 page := &v2.AccountSnapshotPage{Accounts: batch, LastPage: isLast} 580 snapshot := &v2.ObserveAccountsResponse_Snapshot{Snapshot: page} 581 response := &v2.ObserveAccountsResponse{Response: snapshot} 582 if err := srv.Send(response); err != nil { 583 return errors.Wrap(err, "sending account balance initial image") 584 } 585 } 586 587 return nil 588 } 589 590 // Info returns the version and commit hash of the trading data service. 591 func (t *TradingDataServiceV2) Info(_ context.Context, _ *v2.InfoRequest) (*v2.InfoResponse, error) { 592 defer metrics.StartAPIRequestAndTimeGRPC("InfoV2")() 593 594 return &v2.InfoResponse{ 595 Version: version.Get(), 596 CommitHash: version.GetCommitHash(), 597 }, nil 598 } 599 600 // ListLedgerEntries returns a list of ledger entries matching the request. 601 func (t *TradingDataServiceV2) ListLedgerEntries(ctx context.Context, req *v2.ListLedgerEntriesRequest) (*v2.ListLedgerEntriesResponse, error) { 602 defer metrics.StartAPIRequestAndTimeGRPC("ListLedgerEntriesV2")() 603 604 leFilter, err := entities.LedgerEntryFilterFromProto(req.Filter) 605 if err != nil { 606 return nil, formatE(ErrInvalidFilter, err) 607 } 608 609 dateRange := entities.DateRangeFromProto(req.DateRange) 610 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 611 if err != nil { 612 return nil, formatE(ErrInvalidPagination, err) 613 } 614 615 entries, pageInfo, err := t.ledgerService.Query(ctx, leFilter, dateRange, pagination) 616 if err != nil { 617 if errors.Is(err, sqlstore.ErrLedgerEntryFilterForParty) { 618 return nil, formatE(ErrInvalidFilter, err) 619 } 620 return nil, formatE(ErrLedgerServiceGet, err) 621 } 622 623 edges, err := makeEdges[*v2.AggregatedLedgerEntriesEdge](*entries) 624 if err != nil { 625 return nil, formatE(err) 626 } 627 628 return &v2.ListLedgerEntriesResponse{ 629 LedgerEntries: &v2.AggregatedLedgerEntriesConnection{ 630 Edges: edges, 631 PageInfo: pageInfo.ToProto(), 632 }, 633 }, nil 634 } 635 636 // ExportLedgerEntries returns a list of ledger entries matching the request. 637 func (t *TradingDataServiceV2) ExportLedgerEntries(req *v2.ExportLedgerEntriesRequest, stream v2.TradingDataService_ExportLedgerEntriesServer) error { 638 defer metrics.StartAPIRequestAndTimeGRPC("ExportLedgerEntriesV2")() 639 640 if len(req.PartyId) <= 0 { 641 return formatE(ErrMissingPartyID) 642 } 643 if !crypto.IsValidVegaID(req.PartyId) { 644 return formatE(ErrInvalidPartyID) 645 } 646 647 if req.AssetId != nil && !crypto.IsValidVegaID(*req.AssetId) { 648 return formatE(ErrInvalidAssetID) 649 } 650 651 dateRange := entities.DateRangeFromProto(req.DateRange) 652 timeFormat := strings.ReplaceAll(time.RFC3339, ":", "_") 653 654 var startDateStr, endDateStr string 655 if dateRange.Start != nil { 656 startDateStr = "_" + dateRange.Start.Format(timeFormat) 657 } 658 if dateRange.End != nil { 659 endDateStr = "-" + dateRange.End.Format(timeFormat) 660 } 661 662 assetStr := "" 663 if req.AssetId != nil { 664 assetStr = fmt.Sprintf("_%s", *req.AssetId) 665 } 666 header := metadata.Pairs( 667 "Content-Disposition", 668 fmt.Sprintf("attachment;filename=ledger_entries_%s%s_%s%s.csv", 669 req.PartyId, assetStr, startDateStr, endDateStr)) 670 if err := stream.SendHeader(header); err != nil { 671 return formatE(ErrSendingGRPCHeader, err) 672 } 673 674 httpWriter := &httpBodyWriter{chunkSize: httpBodyChunkSize, contentType: "text/csv", buf: &bytes.Buffer{}, stream: stream} 675 defer httpWriter.Close() 676 677 if err := t.ledgerService.Export(stream.Context(), req.PartyId, req.AssetId, dateRange, httpWriter); err != nil { 678 return formatE(ErrLedgerServiceExport, err) 679 } 680 681 return nil 682 } 683 684 // ListBalanceChanges returns a list of balance changes matching the request. 685 func (t *TradingDataServiceV2) ListBalanceChanges(ctx context.Context, req *v2.ListBalanceChangesRequest) (*v2.ListBalanceChangesResponse, error) { 686 defer metrics.StartAPIRequestAndTimeGRPC("ListBalanceChangesV2")() 687 688 if req.Filter != nil { 689 marketIDs := NewVegaIDSlice(req.Filter.MarketIds...) 690 if err := marketIDs.Ensure(); err != nil { 691 return nil, formatE(err, errors.New("one or more market id is invalid")) 692 } 693 req.Filter.MarketIds = marketIDs 694 partyIDs := NewVegaIDSlice(req.Filter.PartyIds...) 695 if err := partyIDs.Ensure(); err != nil { 696 return nil, formatE(err, errors.New("one or more party id is invalid")) 697 } 698 req.Filter.PartyIds = partyIDs 699 } 700 701 filter, err := entities.AccountFilterFromProto(req.Filter) 702 if err != nil { 703 return nil, formatE(ErrInvalidFilter, err) 704 } 705 706 dateRange := entities.DateRangeFromProto(req.DateRange) 707 // By default we will require a valid date range for this API to help the database find the appropriate data without scanning the entire table 708 dateRangeRequired := true 709 710 // If a pagination object is provided and there is a cursor, a reference date is available from the cursor 711 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 712 if err != nil { 713 return nil, formatE(ErrInvalidPagination, err) 714 } 715 if (pagination.HasForward() && pagination.Forward.HasCursor()) || (pagination.HasBackward() && pagination.Backward.HasCursor()) { 716 dateRangeRequired = false 717 } 718 719 if err = dateRange.Validate(dateRangeRequired); err != nil { 720 return nil, formatE(ErrDateRangeValidationFailed, err) 721 } 722 723 balances, pageInfo, err := t.AccountService.QueryAggregatedBalances(ctx, filter, dateRange, pagination) 724 if err != nil { 725 return nil, formatE(ErrAccountServiceGetBalances, err) 726 } 727 728 edges, err := makeEdges[*v2.AggregatedBalanceEdge](*balances) 729 if err != nil { 730 return nil, formatE(err) 731 } 732 733 return &v2.ListBalanceChangesResponse{ 734 Balances: &v2.AggregatedBalanceConnection{ 735 Edges: edges, 736 PageInfo: pageInfo.ToProto(), 737 }, 738 }, nil 739 } 740 741 // ObserveMarketsDepth subscribes to market depth updates. 742 func (t *TradingDataServiceV2) ObserveMarketsDepth(req *v2.ObserveMarketsDepthRequest, srv v2.TradingDataService_ObserveMarketsDepthServer) error { 743 // Wrap context from the request into cancellable. We can close internal chan on error. 744 ctx, cancel := context.WithCancel(srv.Context()) 745 defer cancel() 746 747 for _, marketID := range req.MarketIds { 748 if !t.marketExistsForID(ctx, marketID) { 749 return formatE(errors.Wrapf(ErrMalformedRequest, "no market found for id: %s", marketID)) 750 } 751 } 752 753 depthChan, ref := t.marketDepthService.ObserveDepth(ctx, t.config.StreamRetries, req.MarketIds) 754 755 if t.log.GetLevel() == logging.DebugLevel { 756 t.log.Debug("Depth subscriber - new rpc stream", logging.Uint64("ref", ref)) 757 } 758 759 return observeBatch(ctx, t.log, "MarketDepth", depthChan, ref, func(tr []*vega.MarketDepth) error { 760 return srv.Send(&v2.ObserveMarketsDepthResponse{ 761 MarketDepth: tr, 762 }) 763 }) 764 } 765 766 // ObserveMarketsDepthUpdates subscribes to market depth updates. 767 func (t *TradingDataServiceV2) ObserveMarketsDepthUpdates(req *v2.ObserveMarketsDepthUpdatesRequest, srv v2.TradingDataService_ObserveMarketsDepthUpdatesServer) error { 768 // Wrap context from the request into cancellable. We can close internal chan on error. 769 ctx, cancel := context.WithCancel(srv.Context()) 770 defer cancel() 771 772 for _, marketID := range req.MarketIds { 773 if !t.marketExistsForID(ctx, marketID) { 774 return formatE(errors.Wrapf(ErrMalformedRequest, "no market found for id: %s", marketID)) 775 } 776 } 777 778 depthChan, ref := t.marketDepthService.ObserveDepthUpdates(ctx, t.config.StreamRetries, req.MarketIds) 779 780 if t.log.GetLevel() == logging.DebugLevel { 781 t.log.Debug("Depth updates subscriber - new rpc stream", logging.Uint64("ref", ref)) 782 } 783 784 return observeBatch(ctx, t.log, "MarketDepthUpdate", depthChan, ref, func(tr []*vega.MarketDepthUpdate) error { 785 return srv.Send(&v2.ObserveMarketsDepthUpdatesResponse{ 786 Update: tr, 787 }) 788 }) 789 } 790 791 // ObserveMarketsData subscribes to market data updates. 792 func (t *TradingDataServiceV2) ObserveMarketsData(req *v2.ObserveMarketsDataRequest, srv v2.TradingDataService_ObserveMarketsDataServer) error { 793 // Wrap context from the request into cancellable. We can close internal chan on error. 794 ctx, cancel := context.WithCancel(srv.Context()) 795 defer cancel() 796 797 for _, marketID := range req.MarketIds { 798 if !t.marketExistsForID(ctx, marketID) { 799 return formatE(errors.Wrapf(ErrMalformedRequest, "no market found for id: %s", marketID)) 800 } 801 } 802 803 ch, ref := t.MarketDataService.ObserveMarketData(ctx, t.config.StreamRetries, req.MarketIds) 804 805 return observeBatch(ctx, t.log, "MarketsData", ch, ref, func(marketData []*entities.MarketData) error { 806 out := make([]*vega.MarketData, 0, len(marketData)) 807 for _, v := range marketData { 808 out = append(out, v.ToProto()) 809 } 810 return srv.Send(&v2.ObserveMarketsDataResponse{MarketData: out}) 811 }) 812 } 813 814 // GetLatestMarketData returns the latest market data for a given market. 815 func (t *TradingDataServiceV2) GetLatestMarketData(ctx context.Context, req *v2.GetLatestMarketDataRequest) (*v2.GetLatestMarketDataResponse, error) { 816 defer metrics.StartAPIRequestAndTimeGRPC("GetLatestMarketData")() 817 818 md, err := t.MarketDataService.GetMarketDataByID(ctx, req.MarketId) 819 if err != nil { 820 return nil, formatE(ErrMarketServiceGetMarketData, err) 821 } 822 823 return &v2.GetLatestMarketDataResponse{ 824 MarketData: md.ToProto(), 825 }, nil 826 } 827 828 // ListLatestMarketData returns the latest market data for every market. 829 func (t *TradingDataServiceV2) ListLatestMarketData(ctx context.Context, _ *v2.ListLatestMarketDataRequest) (*v2.ListLatestMarketDataResponse, error) { 830 defer metrics.StartAPIRequestAndTimeGRPC("ListLatestMarketData")() 831 832 mds, err := t.MarketDataService.GetMarketsData(ctx) 833 if err != nil { 834 return nil, formatE(ErrMarketServiceGetMarketData, err) 835 } 836 837 mdptrs := make([]*vega.MarketData, 0, len(mds)) 838 for _, v := range mds { 839 mdptrs = append(mdptrs, v.ToProto()) 840 } 841 842 return &v2.ListLatestMarketDataResponse{ 843 MarketsData: mdptrs, 844 }, nil 845 } 846 847 // GetLatestMarketDepth returns the latest market depth for a given market. 848 func (t *TradingDataServiceV2) GetLatestMarketDepth(ctx context.Context, req *v2.GetLatestMarketDepthRequest) (*v2.GetLatestMarketDepthResponse, error) { 849 defer metrics.StartAPIRequestAndTimeGRPC("GetLatestMarketDepth")() 850 851 ts, err := t.tradeService.GetLastTradeByMarket(ctx, req.MarketId) 852 if err != nil { 853 return nil, formatE(ErrTradeServiceGetByMarket, err) 854 } 855 856 var lastTrade *vega.Trade 857 if len(ts) > 0 { 858 lastTrade = ts[0].ToProto() 859 } 860 861 depth := t.marketDepthService.GetMarketDepth(req.MarketId, ptr.UnBox(req.MaxDepth)) 862 // Build market depth response, including last trade (if available) 863 return &v2.GetLatestMarketDepthResponse{ 864 Buy: depth.Buy, 865 MarketId: depth.MarketId, 866 Sell: depth.Sell, 867 SequenceNumber: depth.SequenceNumber, 868 LastTrade: lastTrade, 869 }, nil 870 } 871 872 // GetMarketDataHistoryByID returns the market data history for a given market. 873 func (t *TradingDataServiceV2) GetMarketDataHistoryByID(ctx context.Context, req *v2.GetMarketDataHistoryByIDRequest) (*v2.GetMarketDataHistoryByIDResponse, error) { 874 defer metrics.StartAPIRequestAndTimeGRPC("GetMarketDataHistoryV2")() 875 876 marketData, err := t.handleGetMarketDataHistoryWithCursorPagination(ctx, req) 877 if err != nil { 878 return marketData, formatE(ErrMarketServiceGetMarketDataHistory, err) 879 } 880 return marketData, nil 881 } 882 883 func (t *TradingDataServiceV2) handleGetMarketDataHistoryWithCursorPagination(ctx context.Context, req *v2.GetMarketDataHistoryByIDRequest) (*v2.GetMarketDataHistoryByIDResponse, error) { 884 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 885 if err != nil { 886 return nil, errors.Wrap(ErrInvalidPagination, err.Error()) 887 } 888 889 var startTime, endTime *time.Time 890 if req.StartTimestamp != nil { 891 startTime = ptr.From(time.Unix(0, *req.StartTimestamp)) 892 } 893 if req.EndTimestamp != nil { 894 endTime = ptr.From(time.Unix(0, *req.EndTimestamp)) 895 } 896 897 history, pageInfo, err := t.MarketDataService.GetHistoricMarketData(ctx, req.MarketId, startTime, endTime, pagination) 898 if err != nil { 899 return nil, errors.Wrap(err, "could not retrieve historic market data") 900 } 901 902 edges, err := makeEdges[*v2.MarketDataEdge](history) 903 if err != nil { 904 return nil, err 905 } 906 907 connection := v2.MarketDataConnection{ 908 Edges: edges, 909 PageInfo: pageInfo.ToProto(), 910 } 911 912 return &v2.GetMarketDataHistoryByIDResponse{ 913 MarketData: &connection, 914 }, nil 915 } 916 917 // GetNetworkLimits returns the latest network limits. 918 func (t *TradingDataServiceV2) GetNetworkLimits(ctx context.Context, _ *v2.GetNetworkLimitsRequest) (*v2.GetNetworkLimitsResponse, error) { 919 defer metrics.StartAPIRequestAndTimeGRPC("GetNetworkLimitsV2")() 920 921 limits, err := t.networkLimitsService.GetLatest(ctx) 922 if err != nil { 923 return nil, formatE(ErrGetNetworkLimits, err) 924 } 925 926 return &v2.GetNetworkLimitsResponse{ 927 Limits: limits.ToProto(), 928 }, nil 929 } 930 931 // ListCandleData for a given market, time range and interval. Interval must be a valid postgres interval value. 932 func (t *TradingDataServiceV2) ListCandleData(ctx context.Context, req *v2.ListCandleDataRequest) (*v2.ListCandleDataResponse, error) { 933 defer metrics.StartAPIRequestAndTimeGRPC("ListCandleDataV2")() 934 935 var from, to *time.Time 936 if req.FromTimestamp != 0 { 937 from = ptr.From(vegatime.UnixNano(req.FromTimestamp)) 938 } 939 940 if req.ToTimestamp != 0 { 941 to = ptr.From(vegatime.UnixNano(req.ToTimestamp)) 942 } 943 944 if to != nil { 945 if from != nil && to.Before(*from) { 946 return nil, formatE(ErrInvalidCandleTimestampsRange) 947 } 948 } 949 950 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 951 if err != nil { 952 return nil, formatE(ErrInvalidPagination, err) 953 } 954 955 if len(req.CandleId) == 0 { 956 return nil, formatE(ErrMissingCandleID) 957 } 958 959 candles, pageInfo, err := t.candleService.GetCandleDataForTimeSpan(ctx, req.CandleId, from, to, pagination) 960 if err != nil { 961 return nil, formatE(ErrCandleServiceGetCandleData, err) 962 } 963 964 edges, err := makeEdges[*v2.CandleEdge](candles) 965 if err != nil { 966 return nil, formatE(err) 967 } 968 969 connection := v2.CandleDataConnection{ 970 Edges: edges, 971 PageInfo: pageInfo.ToProto(), 972 } 973 974 return &v2.ListCandleDataResponse{ 975 Candles: &connection, 976 }, nil 977 } 978 979 // ObserveCandleData subscribes to candle updates for a given market and interval. Interval must be a valid postgres interval value. 980 func (t *TradingDataServiceV2) ObserveCandleData(req *v2.ObserveCandleDataRequest, srv v2.TradingDataService_ObserveCandleDataServer) error { 981 defer metrics.StartActiveSubscriptionCountGRPC("Candle")() 982 983 ctx, cancel := context.WithCancel(srv.Context()) 984 defer cancel() 985 986 subscriptionID, candlesChan, err := t.candleService.Subscribe(ctx, req.CandleId) 987 defer t.candleService.Unsubscribe(subscriptionID) 988 if err != nil { 989 return formatE(ErrCandleServiceSubscribeToCandles, err) 990 } 991 992 publishedEventStatTicker := time.NewTicker(time.Second) 993 defer publishedEventStatTicker.Stop() 994 995 var publishedEvents int64 996 for { 997 select { 998 case <-publishedEventStatTicker.C: 999 metrics.PublishedEventsAdd("Candle", float64(publishedEvents)) 1000 publishedEvents = 0 1001 case candle, ok := <-candlesChan: 1002 if !ok { 1003 return formatE(ErrChannelClosed) 1004 } 1005 1006 resp := &v2.ObserveCandleDataResponse{ 1007 Candle: candle.ToV2CandleProto(), 1008 } 1009 if err = srv.Send(resp); err != nil { 1010 return formatE(ErrCandleServiceSubscribeToCandles, err) 1011 } 1012 publishedEvents++ 1013 case <-ctx.Done(): 1014 err = ctx.Err() 1015 if err != nil { 1016 return formatE(ErrCandleServiceSubscribeToCandles, err) 1017 } 1018 return nil 1019 } 1020 } 1021 } 1022 1023 // ListCandleIntervals gets all available intervals for a given market along with the corresponding candle id. 1024 func (t *TradingDataServiceV2) ListCandleIntervals(ctx context.Context, req *v2.ListCandleIntervalsRequest) (*v2.ListCandleIntervalsResponse, error) { 1025 defer metrics.StartAPIRequestAndTimeGRPC("ListCandleIntervals")() 1026 1027 if len(req.MarketId) <= 0 { 1028 return nil, formatE(ErrEmptyMissingMarketID) 1029 } 1030 1031 if !crypto.IsValidVegaID(req.MarketId) { 1032 return nil, formatE(ErrInvalidMarketID) 1033 } 1034 1035 mappings, err := t.candleService.GetCandlesForMarket(ctx, req.MarketId) 1036 if err != nil { 1037 return nil, formatE(ErrCandleServiceGetCandlesForMarket, err) 1038 } 1039 1040 intervalToCandleIds := make([]*v2.IntervalToCandleId, 0, len(mappings)) 1041 for interval, candleID := range mappings { 1042 intervalToCandleIds = append(intervalToCandleIds, &v2.IntervalToCandleId{ 1043 Interval: interval, 1044 CandleId: candleID, 1045 }) 1046 } 1047 1048 return &v2.ListCandleIntervalsResponse{ 1049 IntervalToCandleId: intervalToCandleIds, 1050 }, nil 1051 } 1052 1053 // ListERC20MultiSigSignerAddedBundles returns the signature bundles needed to add a new validator to the multisig control ERC20 contract. 1054 func (t *TradingDataServiceV2) ListERC20MultiSigSignerAddedBundles(ctx context.Context, req *v2.ListERC20MultiSigSignerAddedBundlesRequest) (*v2.ListERC20MultiSigSignerAddedBundlesResponse, error) { 1055 defer metrics.StartAPIRequestAndTimeGRPC("GetERC20MultiSigSignerAddedBundlesV2")() 1056 1057 var epochID *int64 1058 if len(req.EpochSeq) > 0 { 1059 e, err := strconv.ParseInt(req.EpochSeq, 10, 64) 1060 if err != nil { 1061 return nil, formatE(ErrEpochIDParse, errors.Wrapf(err, "epochSql: %s", req.EpochSeq)) 1062 } 1063 epochID = &e 1064 } 1065 1066 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1067 if err != nil { 1068 return nil, formatE(ErrInvalidPagination, err) 1069 } 1070 1071 res, pageInfo, err := t.multiSigService.GetAddedEvents(ctx, req.GetNodeId(), req.GetSubmitter(), epochID, req.ChainId, pagination) 1072 if err != nil { 1073 return nil, formatE(ErrMultiSigServiceGetAdded, err) 1074 } 1075 1076 // find bundle for this nodeID, might be multiple if it's added, then removed, then added again?? 1077 edges := make([]*v2.ERC20MultiSigSignerAddedBundleEdge, len(res)) 1078 for i, b := range res { 1079 // it doesn't really make sense to paginate this, so we'll just pass it an empty pagination object and get all available results 1080 resID := b.ID.String() 1081 signatures, _, err := t.notaryService.GetByResourceID(ctx, resID, entities.CursorPagination{}) 1082 if err != nil { 1083 return nil, formatE(ErrNotaryServiceGetByResourceID, errors.Wrapf(err, "resourceID: %s", resID)) 1084 } 1085 1086 edges[i] = &v2.ERC20MultiSigSignerAddedBundleEdge{ 1087 Node: &v2.ERC20MultiSigSignerAddedBundle{ 1088 NewSigner: b.SignerChange.String(), 1089 Submitter: b.Submitter.String(), 1090 Nonce: b.Nonce, 1091 Timestamp: b.VegaTime.UnixNano(), 1092 Signatures: entities.PackNodeSignatures(signatures), 1093 EpochSeq: strconv.FormatInt(b.EpochID, 10), 1094 ChainId: b.ChainID, 1095 }, 1096 Cursor: b.Cursor().Encode(), 1097 } 1098 } 1099 1100 connection := &v2.ERC20MultiSigSignerAddedConnection{ 1101 Edges: edges, 1102 PageInfo: pageInfo.ToProto(), 1103 } 1104 1105 return &v2.ListERC20MultiSigSignerAddedBundlesResponse{ 1106 Bundles: connection, 1107 }, nil 1108 } 1109 1110 // ListERC20MultiSigSignerRemovedBundles returns the signature bundles needed to add a new validator to the multisig control ERC20 contract. 1111 func (t *TradingDataServiceV2) ListERC20MultiSigSignerRemovedBundles(ctx context.Context, req *v2.ListERC20MultiSigSignerRemovedBundlesRequest) (*v2.ListERC20MultiSigSignerRemovedBundlesResponse, error) { 1112 defer metrics.StartAPIRequestAndTimeGRPC("GetERC20MultiSigSignerRemovedBundlesV2")() 1113 1114 var epochID *int64 1115 if len(req.EpochSeq) > 0 { 1116 e, err := strconv.ParseInt(req.EpochSeq, 10, 64) 1117 if err != nil { 1118 return nil, formatE(ErrEpochIDParse, errors.Wrapf(err, "epochSql: %s", req.EpochSeq)) 1119 } 1120 epochID = &e 1121 } 1122 1123 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1124 if err != nil { 1125 return nil, formatE(ErrInvalidPagination, err) 1126 } 1127 1128 res, pageInfo, err := t.multiSigService.GetRemovedEvents(ctx, req.GetNodeId(), req.GetSubmitter(), epochID, req.ChainId, pagination) 1129 if err != nil { 1130 return nil, formatE(ErrMultiSigServiceGetRemoved, err) 1131 } 1132 1133 // find bundle for this nodeID, might be multiple if it's added, then, removed them added again?? 1134 edges := make([]*v2.ERC20MultiSigSignerRemovedBundleEdge, len(res)) 1135 for i, b := range res { 1136 // it doesn't really make sense to paginate this, so we'll just pass it an empty pagination object and get all available results 1137 resID := b.ID.String() 1138 signatures, _, err := t.notaryService.GetByResourceID(ctx, resID, entities.CursorPagination{}) 1139 if err != nil { 1140 return nil, formatE(ErrNotaryServiceGetByResourceID, errors.Wrapf(err, "resourceID: %s", resID)) 1141 } 1142 1143 edges[i] = &v2.ERC20MultiSigSignerRemovedBundleEdge{ 1144 Node: &v2.ERC20MultiSigSignerRemovedBundle{ 1145 OldSigner: b.SignerChange.String(), 1146 Submitter: b.Submitter.String(), 1147 Nonce: b.Nonce, 1148 Timestamp: b.VegaTime.UnixNano(), 1149 Signatures: entities.PackNodeSignatures(signatures), 1150 EpochSeq: strconv.FormatInt(b.EpochID, 10), 1151 ChainId: b.ChainID, 1152 }, 1153 Cursor: b.Cursor().Encode(), 1154 } 1155 } 1156 1157 connection := &v2.ERC20MultiSigSignerRemovedConnection{ 1158 Edges: edges, 1159 PageInfo: pageInfo.ToProto(), 1160 } 1161 1162 return &v2.ListERC20MultiSigSignerRemovedBundlesResponse{ 1163 Bundles: connection, 1164 }, nil 1165 } 1166 1167 // GetERC20SetAssetLimitsBundle returns the signature bundle needed to update the asset limits on the ERC20 contract. 1168 func (t *TradingDataServiceV2) GetERC20SetAssetLimitsBundle(ctx context.Context, req *v2.GetERC20SetAssetLimitsBundleRequest) (*v2.GetERC20SetAssetLimitsBundleResponse, error) { 1169 defer metrics.StartAPIRequestAndTimeGRPC("GetERC20SetAssetLimitsBundleV2")() 1170 1171 if len(req.ProposalId) == 0 { 1172 return nil, formatE(ErrMissingProposalID) 1173 } 1174 1175 if !crypto.IsValidVegaID(req.ProposalId) { 1176 return nil, formatE(ErrInvalidProposalID) 1177 } 1178 1179 proposal, err := t.governanceService.GetProposalByIDWithoutBatch(ctx, req.ProposalId) 1180 if err != nil { 1181 return nil, formatE(ErrGovernanceServiceGet, err) 1182 } 1183 1184 if proposal.IsBatch() { 1185 return nil, formatE(errors.New("can not get bundle for batch proposal")) 1186 } 1187 1188 if proposal.Terms.GetUpdateAsset() == nil { 1189 return nil, formatE(errors.New("not an update asset proposal")) 1190 } 1191 1192 if proposal.Terms.GetUpdateAsset().GetChanges().GetErc20() == nil { 1193 return nil, formatE(errors.New("not an update erc20 asset proposal")) 1194 } 1195 1196 signatures, _, err := t.notaryService.GetByResourceID(ctx, req.ProposalId, entities.CursorPagination{}) 1197 if err != nil { 1198 return nil, formatE(ErrNotaryServiceGetByResourceID, errors.Wrapf(err, "proposalID: %s", req.ProposalId)) 1199 } 1200 1201 asset, err := t.AssetService.GetByID(ctx, proposal.Terms.GetUpdateAsset().AssetId) 1202 if err != nil { 1203 return nil, formatE(ErrAssetServiceGetByID, err) 1204 } 1205 1206 if len(asset.ERC20Contract) == 0 { 1207 return nil, formatE(ErrERC20InvalidTokenContractAddress) 1208 } 1209 1210 nonce, err := num.UintFromHex("0x" + strings.TrimLeft(req.ProposalId, "0")) 1211 if err != nil { 1212 return nil, formatE(ErrInvalidProposalID, errors.Wrapf(err, "proposalID: %s", req.ProposalId)) 1213 } 1214 1215 return &v2.GetERC20SetAssetLimitsBundleResponse{ 1216 AssetSource: asset.ERC20Contract, 1217 Nonce: nonce.String(), 1218 VegaAssetId: asset.ID.String(), 1219 Signatures: entities.PackNodeSignatures(signatures), 1220 LifetimeLimit: proposal.Terms.GetUpdateAsset().GetChanges().GetErc20().LifetimeLimit, 1221 Threshold: proposal.Terms.GetUpdateAsset().GetChanges().GetErc20().WithdrawThreshold, 1222 }, nil 1223 } 1224 1225 func (t *TradingDataServiceV2) GetERC20ListAssetBundle(ctx context.Context, req *v2.GetERC20ListAssetBundleRequest) (*v2.GetERC20ListAssetBundleResponse, error) { 1226 defer metrics.StartAPIRequestAndTimeGRPC("GetERC20ListAssetBundleV2")() 1227 1228 if len(req.AssetId) == 0 { 1229 return nil, formatE(ErrMissingAssetID) 1230 } 1231 1232 if !crypto.IsValidVegaID(req.AssetId) { 1233 return nil, formatE(ErrInvalidAssetID) 1234 } 1235 1236 asset, err := t.AssetService.GetByID(ctx, req.AssetId) 1237 if err != nil { 1238 return nil, formatE(ErrAssetServiceGetByID, err) 1239 } 1240 1241 signatures, _, err := t.notaryService.GetByResourceID(ctx, req.AssetId, entities.CursorPagination{}) 1242 if err != nil { 1243 return nil, formatE(ErrNotaryServiceGetByResourceID, errors.Wrapf(err, "assetID: %s", req.AssetId)) 1244 } 1245 1246 if len(asset.ERC20Contract) == 0 { 1247 return nil, formatE(ErrERC20InvalidTokenContractAddress, err) 1248 } 1249 1250 nonce, err := num.UintFromHex("0x" + strings.TrimLeft(req.AssetId, "0")) 1251 if err != nil { 1252 return nil, formatE(ErrInvalidAssetID, errors.Wrapf(err, "assetID: %s", req.AssetId)) 1253 } 1254 1255 return &v2.GetERC20ListAssetBundleResponse{ 1256 AssetSource: asset.ERC20Contract, 1257 Nonce: nonce.String(), 1258 VegaAssetId: asset.ID.String(), 1259 Signatures: entities.PackNodeSignatures(signatures), 1260 }, nil 1261 } 1262 1263 // GetERC20WithdrawalApproval returns the signature bundle needed to approve a withdrawal on the ERC20 contract. 1264 func (t *TradingDataServiceV2) GetERC20WithdrawalApproval(ctx context.Context, req *v2.GetERC20WithdrawalApprovalRequest) (*v2.GetERC20WithdrawalApprovalResponse, error) { 1265 defer metrics.StartAPIRequestAndTimeGRPC("GetERC20WithdrawalApprovalV2")() 1266 1267 if len(req.WithdrawalId) == 0 { 1268 return nil, formatE(ErrMissingWithdrawalID) 1269 } 1270 1271 if !crypto.IsValidVegaID(req.WithdrawalId) { 1272 return nil, formatE(ErrInvalidWithdrawalID) 1273 } 1274 1275 w, err := t.withdrawalService.GetByID(ctx, req.WithdrawalId) 1276 if err != nil { 1277 return nil, formatE(ErrWithdrawalServiceGet, err) 1278 } 1279 1280 signatures, _, err := t.notaryService.GetByResourceID(ctx, req.WithdrawalId, entities.CursorPagination{}) 1281 if err != nil { 1282 return nil, formatE(ErrNotaryServiceGetByResourceID, errors.Wrapf(err, "withdrawalID: %s", req.WithdrawalId)) 1283 } 1284 1285 assets, err := t.AssetService.GetAll(ctx) 1286 if err != nil { 1287 return nil, formatE(ErrAssetServiceGetAll, err) 1288 } 1289 1290 var address, chainID string 1291 for _, v := range assets { 1292 if v.ID == w.Asset { 1293 address = v.ERC20Contract 1294 chainID = v.ChainID 1295 break 1296 } 1297 } 1298 if len(address) == 0 { 1299 return nil, formatE(ErrERC20InvalidTokenContractAddress) 1300 } 1301 1302 return &v2.GetERC20WithdrawalApprovalResponse{ 1303 AssetSource: address, 1304 Amount: fmt.Sprintf("%v", w.Amount), 1305 Nonce: w.Ref, 1306 TargetAddress: w.Ext.GetErc20().ReceiverAddress, 1307 Signatures: entities.PackNodeSignatures(signatures), 1308 // timestamps is unix nano, contract needs unix. So load if first, and cut nanos 1309 Creation: w.CreatedTimestamp.Unix(), 1310 SourceChainId: chainID, 1311 }, nil 1312 } 1313 1314 // GetLastTrade returns the last trade for a given market. 1315 func (t *TradingDataServiceV2) GetLastTrade(ctx context.Context, req *v2.GetLastTradeRequest) (*v2.GetLastTradeResponse, error) { 1316 defer metrics.StartAPIRequestAndTimeGRPC("GetLastTradeV2")() 1317 1318 if len(req.MarketId) == 0 { 1319 return nil, formatE(ErrEmptyMissingMarketID) 1320 } 1321 1322 if !crypto.IsValidVegaID(req.MarketId) { 1323 return nil, formatE(ErrInvalidMarketID) 1324 } 1325 1326 trades, err := t.tradeService.GetLastTradeByMarket(ctx, req.MarketId) 1327 if err != nil { 1328 return nil, formatE(ErrTradeServiceGetByMarket, err) 1329 } 1330 1331 protoTrades := tradesToProto(trades) 1332 1333 if len(protoTrades) > 0 && protoTrades[0] != nil { 1334 return &v2.GetLastTradeResponse{Trade: protoTrades[0]}, nil 1335 } 1336 // No trades found on the market yet (and no errors) 1337 // this can happen at the beginning of a new market 1338 return &v2.GetLastTradeResponse{}, nil 1339 } 1340 1341 func tradesToProto(trades []entities.Trade) []*vega.Trade { 1342 protoTrades := make([]*vega.Trade, len(trades)) 1343 for i := range trades { 1344 protoTrades[i] = trades[i].ToProto() 1345 } 1346 return protoTrades 1347 } 1348 1349 type filterableIDs interface { 1350 entities.MarketID | entities.PartyID | entities.OrderID 1351 } 1352 1353 func toEntityIDs[T filterableIDs](ids []string) []T { 1354 entityIDs := make([]T, len(ids)) 1355 for i := range ids { 1356 entityIDs[i] = T(ids[i]) 1357 } 1358 return entityIDs 1359 } 1360 1361 // ListTrades lists trades by using a cursor based pagination model. 1362 func (t *TradingDataServiceV2) ListTrades(ctx context.Context, req *v2.ListTradesRequest) (*v2.ListTradesResponse, error) { 1363 defer metrics.StartAPIRequestAndTimeGRPC("ListTradesV2")() 1364 1365 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1366 if err != nil { 1367 return nil, formatE(ErrInvalidPagination, err) 1368 } 1369 1370 dateRange := entities.DateRangeFromProto(req.DateRange) 1371 trades, pageInfo, err := t.tradeService.List(ctx, 1372 toEntityIDs[entities.MarketID](req.GetMarketIds()), 1373 toEntityIDs[entities.PartyID](req.GetPartyIds()), 1374 toEntityIDs[entities.OrderID](req.GetOrderIds()), 1375 pagination, 1376 dateRange) 1377 if err != nil { 1378 return nil, formatE(ErrTradeServiceList, err) 1379 } 1380 1381 edges, err := makeEdges[*v2.TradeEdge](trades) 1382 if err != nil { 1383 return nil, formatE(err) 1384 } 1385 1386 tradesConnection := &v2.TradeConnection{ 1387 Edges: edges, 1388 PageInfo: pageInfo.ToProto(), 1389 } 1390 1391 return &v2.ListTradesResponse{ 1392 Trades: tradesConnection, 1393 }, nil 1394 } 1395 1396 // ObserveTrades opens a subscription to the Trades service. 1397 func (t *TradingDataServiceV2) ObserveTrades(req *v2.ObserveTradesRequest, srv v2.TradingDataService_ObserveTradesServer) error { 1398 // Wrap context from the request into cancellable. We can close internal chan on error. 1399 ctx, cancel := context.WithCancel(srv.Context()) 1400 defer cancel() 1401 1402 tradesChan, ref := t.tradeService.Observe(ctx, t.config.StreamRetries, req.MarketIds, req.PartyIds) 1403 1404 if t.log.GetLevel() == logging.DebugLevel { 1405 t.log.Debug("Trades subscriber - new rpc stream", logging.Uint64("ref", ref)) 1406 } 1407 1408 return observeBatch(ctx, t.log, "Trade", tradesChan, ref, func(trades []*entities.Trade) error { 1409 protos := make([]*vega.Trade, 0, len(trades)) 1410 for _, v := range trades { 1411 protos = append(protos, v.ToProto()) 1412 } 1413 1414 batches := batch(protos, snapshotPageSize) 1415 1416 for _, batch := range batches { 1417 response := &v2.ObserveTradesResponse{Trades: batch} 1418 if err := srv.Send(response); err != nil { 1419 return errors.Wrap(err, "sending trades updates") 1420 } 1421 } 1422 return nil 1423 }) 1424 } 1425 1426 /****************************** Markets **************************************/ 1427 1428 // GetMarket returns a market by its ID. 1429 func (t *TradingDataServiceV2) GetMarket(ctx context.Context, req *v2.GetMarketRequest) (*v2.GetMarketResponse, error) { 1430 defer metrics.StartAPIRequestAndTimeGRPC("MarketByID_SQL")() 1431 1432 if len(req.MarketId) == 0 { 1433 return nil, formatE(ErrEmptyMissingMarketID) 1434 } 1435 1436 if !crypto.IsValidVegaID(req.MarketId) { 1437 return nil, formatE(ErrInvalidMarketID) 1438 } 1439 1440 market, err := t.MarketsService.GetByID(ctx, req.MarketId) 1441 if err != nil { 1442 return nil, formatE(ErrMarketServiceGetByID, err) 1443 } 1444 1445 return &v2.GetMarketResponse{ 1446 Market: market.ToProto(), 1447 }, nil 1448 } 1449 1450 // ListMarkets lists all markets. 1451 func (t *TradingDataServiceV2) ListMarkets(ctx context.Context, req *v2.ListMarketsRequest) (*v2.ListMarketsResponse, error) { 1452 defer metrics.StartAPIRequestAndTimeGRPC("ListMarketsV2")() 1453 1454 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1455 if err != nil { 1456 return nil, formatE(ErrInvalidPagination, err) 1457 } 1458 1459 includeSettled := true 1460 if req.IncludeSettled != nil { 1461 includeSettled = *req.IncludeSettled 1462 } 1463 1464 markets, pageInfo, err := t.MarketsService.GetAllPaged(ctx, "", pagination, includeSettled) 1465 if err != nil { 1466 return nil, formatE(ErrMarketServiceGetAllPaged, err) 1467 } 1468 1469 edges, err := makeEdges[*v2.MarketEdge](markets) 1470 if err != nil { 1471 return nil, formatE(err) 1472 } 1473 1474 marketsConnection := &v2.MarketConnection{ 1475 Edges: edges, 1476 PageInfo: pageInfo.ToProto(), 1477 } 1478 1479 return &v2.ListMarketsResponse{ 1480 Markets: marketsConnection, 1481 }, nil 1482 } 1483 1484 // ListSuccessorMarkets returns the successor chain for a given market. 1485 func (t *TradingDataServiceV2) ListSuccessorMarkets(ctx context.Context, req *v2.ListSuccessorMarketsRequest) (*v2.ListSuccessorMarketsResponse, error) { 1486 defer metrics.StartAPIRequestAndTimeGRPC("ListSuccessorMarkets")() 1487 1488 if len(req.MarketId) == 0 { 1489 return nil, formatE(ErrEmptyMissingMarketID) 1490 } 1491 1492 if !crypto.IsValidVegaID(req.MarketId) { 1493 return nil, formatE(ErrInvalidMarketID) 1494 } 1495 1496 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1497 if err != nil { 1498 return nil, formatE(ErrInvalidPagination, err) 1499 } 1500 1501 markets, pageInfo, err := t.MarketsService.ListSuccessorMarkets(ctx, req.MarketId, req.IncludeFullHistory, pagination) 1502 if err != nil { 1503 return nil, formatE(ErrMarketServiceGetAllPaged, err) 1504 } 1505 1506 edges, err := makeEdges[*v2.SuccessorMarketEdge](markets) 1507 if err != nil { 1508 return nil, formatE(err) 1509 } 1510 1511 for i := range edges { 1512 for j := range edges[i].Node.Proposals { 1513 proposalID := edges[i].Node.Proposals[j].Proposal.Id 1514 node := edges[i].Node.Proposals[j] 1515 node.Yes, node.No, err = t.getVotesByProposal(ctx, proposalID) 1516 if err != nil { 1517 return nil, formatE(ErrGovernanceServiceGetVotes, errors.Wrapf(err, "proposalID: %s", proposalID)) 1518 } 1519 } 1520 } 1521 1522 marketsConnection := &v2.SuccessorMarketConnection{ 1523 Edges: edges, 1524 PageInfo: pageInfo.ToProto(), 1525 } 1526 1527 return &v2.ListSuccessorMarketsResponse{ 1528 SuccessorMarkets: marketsConnection, 1529 }, nil 1530 } 1531 1532 // List all Positions. 1533 // 1534 // Deprecated: Use ListAllPositions instead. 1535 func (t *TradingDataServiceV2) ListPositions(ctx context.Context, req *v2.ListPositionsRequest) (*v2.ListPositionsResponse, error) { 1536 defer metrics.StartAPIRequestAndTimeGRPC("ListPositionsV2")() 1537 1538 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1539 if err != nil { 1540 return nil, formatE(ErrInvalidPagination, err) 1541 } 1542 1543 parties := []entities.PartyID{entities.PartyID(req.PartyId)} 1544 markets := []entities.MarketID{entities.MarketID(req.MarketId)} 1545 1546 positions, pageInfo, err := t.positionService.GetByPartyConnection(ctx, parties, markets, pagination) 1547 if err != nil { 1548 return nil, formatE(ErrPositionServiceGetByParty, err) 1549 } 1550 1551 edges, err := makeEdges[*v2.PositionEdge](positions) 1552 if err != nil { 1553 return nil, formatE(err) 1554 } 1555 1556 PositionsConnection := &v2.PositionConnection{ 1557 Edges: edges, 1558 PageInfo: pageInfo.ToProto(), 1559 } 1560 1561 return &v2.ListPositionsResponse{ 1562 Positions: PositionsConnection, 1563 }, nil 1564 } 1565 1566 // ListAllPositions lists all positions. 1567 func (t *TradingDataServiceV2) ListAllPositions(ctx context.Context, req *v2.ListAllPositionsRequest) (*v2.ListAllPositionsResponse, error) { 1568 defer metrics.StartAPIRequestAndTimeGRPC("ListAllPositions")() 1569 1570 if req.Filter != nil { 1571 marketIDs := NewVegaIDSlice(req.Filter.MarketIds...) 1572 if err := marketIDs.Ensure(); err != nil { 1573 return nil, formatE(err, errors.New("one or more market id is invalid")) 1574 } 1575 req.Filter.MarketIds = marketIDs 1576 1577 partyIDs := NewVegaIDSlice(req.Filter.PartyIds...) 1578 if err := partyIDs.Ensure(); err != nil { 1579 return nil, formatE(err, errors.New("one or more party id is invalid")) 1580 } 1581 req.Filter.PartyIds = partyIDs 1582 1583 // check for derived parties 1584 if ptr.UnBox(req.Filter.IncludeDerivedParties) { 1585 if len(partyIDs) == 0 { 1586 return nil, formatE(newInvalidArgumentError("includeDerivedParties requires a partyId")) 1587 } 1588 1589 derivedParties, err := t.AMMPoolService.GetSubKeysForParties(ctx, partyIDs, marketIDs) 1590 if err != nil { 1591 return nil, formatE(ErrPositionServiceGetByParty, err) 1592 } 1593 slices.Sort(derivedParties) 1594 partyIDs = append(partyIDs, derivedParties...) 1595 } 1596 1597 req.Filter.PartyIds = partyIDs 1598 } 1599 1600 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1601 if err != nil { 1602 return nil, formatE(ErrInvalidPagination, err) 1603 } 1604 1605 var ( 1606 parties []entities.PartyID 1607 markets []entities.MarketID 1608 ) 1609 if req.Filter != nil { 1610 parties = make([]entities.PartyID, len(req.Filter.PartyIds)) 1611 markets = make([]entities.MarketID, len(req.Filter.MarketIds)) 1612 1613 for i, party := range req.Filter.PartyIds { 1614 parties[i] = entities.PartyID(party) 1615 } 1616 1617 for i, market := range req.Filter.MarketIds { 1618 markets[i] = entities.MarketID(market) 1619 } 1620 } 1621 1622 positions, pageInfo, err := t.positionService.GetByPartyConnection(ctx, parties, markets, pagination) 1623 if err != nil { 1624 return nil, formatE(ErrPositionServiceGetByParty, err) 1625 } 1626 1627 edges, err := makeEdges[*v2.PositionEdge](positions) 1628 if err != nil { 1629 return nil, formatE(err) 1630 } 1631 1632 PositionsConnection := &v2.PositionConnection{ 1633 Edges: edges, 1634 PageInfo: pageInfo.ToProto(), 1635 } 1636 1637 return &v2.ListAllPositionsResponse{ 1638 Positions: PositionsConnection, 1639 }, nil 1640 } 1641 1642 // ObservePositions subscribes to a stream of Positions. 1643 func (t *TradingDataServiceV2) ObservePositions(req *v2.ObservePositionsRequest, srv v2.TradingDataService_ObservePositionsServer) error { 1644 // Wrap context from the request into cancellable. We can close internal chan on error. 1645 ctx, cancel := context.WithCancel(srv.Context()) 1646 defer cancel() 1647 1648 // handle derived parties 1649 includeDerivedParties := ptr.UnBox(req.IncludeDerivedParties) 1650 if includeDerivedParties && (req.PartyId == nil || len(*req.PartyId) <= 0) { 1651 return formatE(newInvalidArgumentError("includeDerivedParties requires a partyId")) 1652 } 1653 1654 derivedParties := []string{} 1655 if req.PartyId != nil && len(*req.PartyId) > 0 { 1656 if includeDerivedParties { 1657 partyIDs := []string{*req.PartyId} 1658 1659 var marketIDs []string 1660 if req.MarketId != nil && len(*req.MarketId) > 0 { 1661 marketIDs = []string{*req.MarketId} 1662 } 1663 1664 derivedParties, err := t.AMMPoolService.GetSubKeysForParties(ctx, partyIDs, marketIDs) 1665 if err != nil { 1666 return formatE(err) 1667 } 1668 slices.Sort(derivedParties) 1669 } 1670 } 1671 1672 if err := t.sendPositionsSnapshot(ctx, req, srv, derivedParties); err != nil { 1673 if !errors.Is(err, entities.ErrNotFound) { 1674 return formatE(ErrPositionServiceSendSnapshot, err) 1675 } 1676 } 1677 1678 // add the party to the derived parties 1679 if req.PartyId != nil && len(*req.PartyId) > 0 { 1680 derivedParties = append(derivedParties, *req.PartyId) 1681 } 1682 1683 positionsChan, ref := t.positionService.ObserveMany(ctx, t.config.StreamRetries, ptr.UnBox(req.MarketId), derivedParties...) 1684 1685 if t.log.GetLevel() == logging.DebugLevel { 1686 t.log.Debug("Positions subscriber - new rpc stream", logging.Uint64("ref", ref)) 1687 } 1688 1689 return observeBatch(ctx, t.log, "Position", positionsChan, ref, func(positions []entities.Position) error { 1690 protos := make([]*vega.Position, len(positions)) 1691 for i := 0; i < len(positions); i++ { 1692 protos[i] = positions[i].ToProto() 1693 } 1694 batches := batch(protos, snapshotPageSize) 1695 for _, batch := range batches { 1696 updates := &v2.PositionUpdates{Positions: batch} 1697 responseUpdates := &v2.ObservePositionsResponse_Updates{Updates: updates} 1698 response := &v2.ObservePositionsResponse{Response: responseUpdates} 1699 if err := srv.Send(response); err != nil { 1700 return errors.Wrap(err, "sending initial positions") 1701 } 1702 } 1703 1704 return nil 1705 }) 1706 } 1707 1708 func (t *TradingDataServiceV2) sendPositionsSnapshot(ctx context.Context, req *v2.ObservePositionsRequest, srv v2.TradingDataService_ObservePositionsServer, derivedParties []string) error { 1709 var ( 1710 positions []entities.Position 1711 err error 1712 ) 1713 // TODO: better use a filter struct instead of having 4 different cases here. 1714 // By market and party. 1715 if req.PartyId != nil && req.MarketId != nil { 1716 position, err := t.positionService.GetByMarketAndParty(ctx, *req.MarketId, *req.PartyId) 1717 if err != nil { 1718 return errors.Wrap(err, "getting initial positions by market+party") 1719 } 1720 positions = append(positions, position) 1721 } 1722 1723 // By market. 1724 if req.PartyId == nil && req.MarketId != nil { 1725 positions, err = t.positionService.GetByMarket(ctx, *req.MarketId) 1726 if err != nil { 1727 return errors.Wrap(err, "getting initial positions by market") 1728 } 1729 } 1730 1731 // By party. 1732 if req.PartyId != nil && req.MarketId == nil { 1733 positions, err = t.positionService.GetByParty(ctx, entities.PartyID(*req.PartyId)) 1734 if err != nil { 1735 return errors.Wrap(err, "getting initial positions by party") 1736 } 1737 } 1738 1739 // All the positions. 1740 if req.PartyId == nil && req.MarketId == nil { 1741 positions, err = t.positionService.GetAll(ctx) 1742 if err != nil { 1743 return errors.Wrap(err, "getting initial positions by party") 1744 } 1745 } 1746 1747 // finally handle derived parties 1748 for _, v := range derivedParties { 1749 if req.MarketId != nil { 1750 position, err := t.positionService.GetByMarketAndParty(ctx, *req.MarketId, v) 1751 if err != nil { 1752 return errors.Wrap(err, "getting initial positions by market+party") 1753 } 1754 positions = append(positions, position) 1755 continue 1756 } 1757 1758 derivedPartyPositions, err := t.positionService.GetByParty(ctx, entities.PartyID(v)) 1759 if err != nil { 1760 return errors.Wrap(err, "getting initial positions by party") 1761 } 1762 positions = append(positions, derivedPartyPositions...) 1763 } 1764 1765 protos := make([]*vega.Position, len(positions)) 1766 for i := 0; i < len(positions); i++ { 1767 protos[i] = positions[i].ToProto() 1768 } 1769 1770 batches := batch(protos, snapshotPageSize) 1771 for i, batch := range batches { 1772 isLast := i == len(batches)-1 1773 positionList := &v2.PositionSnapshotPage{Positions: batch, LastPage: isLast} 1774 snapshot := &v2.ObservePositionsResponse_Snapshot{Snapshot: positionList} 1775 response := &v2.ObservePositionsResponse{Response: snapshot} 1776 if err := srv.Send(response); err != nil { 1777 return errors.Wrap(err, "sending initial positions") 1778 } 1779 } 1780 return nil 1781 } 1782 1783 // GetParty returns a Party by ID. 1784 func (t *TradingDataServiceV2) GetParty(ctx context.Context, req *v2.GetPartyRequest) (*v2.GetPartyResponse, error) { 1785 defer metrics.StartAPIRequestAndTimeGRPC("GetParty")() 1786 1787 if len(req.PartyId) == 0 { 1788 return nil, formatE(ErrMissingPartyID) 1789 } 1790 1791 if req.PartyId != networkPartyID && !crypto.IsValidVegaID(req.PartyId) { 1792 return nil, formatE(ErrInvalidPartyID) 1793 } 1794 1795 party, err := t.partyService.GetByID(ctx, req.PartyId) 1796 if err != nil { 1797 return nil, formatE(ErrPartyServiceGetByID, err) 1798 } 1799 1800 return &v2.GetPartyResponse{ 1801 Party: party.ToProto(), 1802 }, nil 1803 } 1804 1805 // ListParties lists Parties. 1806 func (t *TradingDataServiceV2) ListParties(ctx context.Context, req *v2.ListPartiesRequest) (*v2.ListPartiesResponse, error) { 1807 defer metrics.StartAPIRequestAndTimeGRPC("ListPartiesV2")() 1808 1809 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1810 if err != nil { 1811 return nil, formatE(ErrInvalidPagination, err) 1812 } 1813 1814 parties, pageInfo, err := t.partyService.GetAllPaged(ctx, req.PartyId, pagination) 1815 if err != nil { 1816 return nil, formatE(ErrPartyServiceGetAll, err) 1817 } 1818 1819 edges, err := makeEdges[*v2.PartyEdge](parties) 1820 if err != nil { 1821 return nil, formatE(err) 1822 } 1823 1824 partyConnection := &v2.PartyConnection{ 1825 Edges: edges, 1826 PageInfo: pageInfo.ToProto(), 1827 } 1828 1829 return &v2.ListPartiesResponse{ 1830 Parties: partyConnection, 1831 }, nil 1832 } 1833 1834 func (t *TradingDataServiceV2) ListPartiesProfiles(ctx context.Context, req *v2.ListPartiesProfilesRequest) (*v2.ListPartiesProfilesResponse, error) { 1835 defer metrics.StartAPIRequestAndTimeGRPC("ListPartiesProfiles")() 1836 1837 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1838 if err != nil { 1839 return nil, formatE(ErrInvalidPagination, err) 1840 } 1841 1842 parties, pageInfo, err := t.partyService.ListProfiles(ctx, req.Parties, pagination) 1843 if err != nil { 1844 return nil, formatE(ErrPartyServiceListProfiles, err) 1845 } 1846 1847 edges, err := makeEdges[*v2.PartyProfileEdge](parties) 1848 if err != nil { 1849 return nil, formatE(err) 1850 } 1851 1852 connection := &v2.PartiesProfilesConnection{ 1853 Edges: edges, 1854 PageInfo: pageInfo.ToProto(), 1855 } 1856 1857 return &v2.ListPartiesProfilesResponse{ 1858 Profiles: connection, 1859 }, nil 1860 } 1861 1862 // ListMarginLevels lists MarginLevels. 1863 func (t *TradingDataServiceV2) ListMarginLevels(ctx context.Context, req *v2.ListMarginLevelsRequest) (*v2.ListMarginLevelsResponse, error) { 1864 defer metrics.StartAPIRequestAndTimeGRPC("ListMarginLevelsV2")() 1865 1866 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1867 if err != nil { 1868 return nil, formatE(ErrInvalidPagination, err) 1869 } 1870 1871 marginLevels, pageInfo, err := t.riskService.GetMarginLevelsByIDWithCursorPagination(ctx, req.PartyId, req.MarketId, pagination) 1872 if err != nil { 1873 return nil, formatE(ErrRiskServiceGetMarginLevelsByID, err) 1874 } 1875 1876 edges, err := makeEdges[*v2.MarginEdge](marginLevels, ctx, t.AccountService) 1877 if err != nil { 1878 return nil, formatE(err) 1879 } 1880 1881 marginLevelsConnection := &v2.MarginConnection{ 1882 Edges: edges, 1883 PageInfo: pageInfo.ToProto(), 1884 } 1885 1886 return &v2.ListMarginLevelsResponse{ 1887 MarginLevels: marginLevelsConnection, 1888 }, nil 1889 } 1890 1891 // ObserveMarginLevels subscribes to a stream of Margin Levels. 1892 func (t *TradingDataServiceV2) ObserveMarginLevels(req *v2.ObserveMarginLevelsRequest, srv v2.TradingDataService_ObserveMarginLevelsServer) error { 1893 // Wrap context from the request into cancellable. We can close internal chan on error. 1894 ctx, cancel := context.WithCancel(srv.Context()) 1895 defer cancel() 1896 1897 marginLevelsChan, ref := t.riskService.ObserveMarginLevels(ctx, t.config.StreamRetries, req.PartyId, ptr.UnBox(req.MarketId)) 1898 1899 if t.log.GetLevel() == logging.DebugLevel { 1900 t.log.Debug("Margin levels subscriber - new rpc stream", logging.Uint64("ref", ref)) 1901 } 1902 1903 return observe(ctx, t.log, "MarginLevel", marginLevelsChan, ref, func(ml entities.MarginLevels) error { 1904 protoMl, err := ml.ToProto(ctx, t.AccountService) 1905 if err != nil { 1906 return errors.Wrap(err, "converting margin levels to proto") 1907 } 1908 1909 return srv.Send(&v2.ObserveMarginLevelsResponse{ 1910 MarginLevels: protoMl, 1911 }) 1912 }) 1913 } 1914 1915 // ListRewards lists Rewards. 1916 func (t *TradingDataServiceV2) ListRewards(ctx context.Context, req *v2.ListRewardsRequest) (*v2.ListRewardsResponse, error) { 1917 defer metrics.StartAPIRequestAndTimeGRPC("ListRewardsV2")() 1918 1919 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1920 if err != nil { 1921 return nil, formatE(ErrInvalidPagination, err) 1922 } 1923 1924 partyIDs := []string{req.PartyId} 1925 var marketIDs []string 1926 if req.MarketId != nil { 1927 marketIDs = []string{*req.MarketId} 1928 } 1929 if includeDerivedParties := ptr.UnBox(req.IncludeDerivedParties); includeDerivedParties { 1930 subKeys, err := t.AMMPoolService.GetSubKeysForParties(ctx, partyIDs, marketIDs) 1931 if err != nil { 1932 return nil, formatE(err) 1933 } 1934 partyIDs = append(partyIDs, subKeys...) 1935 } 1936 1937 rewards, pageInfo, err := t.RewardService.GetByCursor(ctx, partyIDs, req.AssetId, req.FromEpoch, req.ToEpoch, pagination, req.TeamId, req.GameId, req.MarketId) 1938 if err != nil { 1939 return nil, formatE(ErrGetRewards, err) 1940 } 1941 1942 edges, err := makeEdges[*v2.RewardEdge](rewards) 1943 if err != nil { 1944 return nil, formatE(err) 1945 } 1946 1947 rewardsConnection := &v2.RewardsConnection{ 1948 Edges: edges, 1949 PageInfo: pageInfo.ToProto(), 1950 } 1951 1952 return &v2.ListRewardsResponse{ 1953 Rewards: rewardsConnection, 1954 }, nil 1955 } 1956 1957 // ListRewardSummaries gets reward summaries. 1958 func (t *TradingDataServiceV2) ListRewardSummaries(ctx context.Context, req *v2.ListRewardSummariesRequest) (*v2.ListRewardSummariesResponse, error) { 1959 defer metrics.StartAPIRequestAndTimeGRPC("ListRewardSummariesV2")() 1960 1961 partyIDs := []string{} 1962 if req.PartyId != nil { 1963 partyIDs = []string{*req.PartyId} 1964 } 1965 1966 if includeDerivedParties := ptr.UnBox(req.IncludeDerivedParties); includeDerivedParties { 1967 subKeys, err := t.AMMPoolService.GetSubKeysForParties(ctx, partyIDs, nil) 1968 if err != nil { 1969 return nil, formatE(err) 1970 } 1971 partyIDs = append(partyIDs, subKeys...) 1972 } 1973 1974 summaries, err := t.RewardService.GetSummaries(ctx, partyIDs, req.AssetId) 1975 if err != nil { 1976 return nil, formatE(ErrSummaryServiceGet, err) 1977 } 1978 1979 summaryProtos := make([]*vega.RewardSummary, len(summaries)) 1980 1981 for i, summary := range summaries { 1982 summaryProtos[i] = summary.ToProto() 1983 } 1984 1985 return &v2.ListRewardSummariesResponse{ 1986 Summaries: summaryProtos, 1987 }, nil 1988 } 1989 1990 // ListEpochRewardSummaries gets reward summaries for epoch range. 1991 func (t *TradingDataServiceV2) ListEpochRewardSummaries(ctx context.Context, req *v2.ListEpochRewardSummariesRequest) (*v2.ListEpochRewardSummariesResponse, error) { 1992 defer metrics.StartAPIRequestAndTimeGRPC("ListEpochRewardSummaries")() 1993 1994 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 1995 if err != nil { 1996 return nil, formatE(ErrInvalidPagination, err) 1997 } 1998 1999 filter := entities.RewardSummaryFilterFromProto(req.Filter) 2000 summaries, pageInfo, err := t.RewardService.GetEpochRewardSummaries(ctx, filter, pagination) 2001 if err != nil { 2002 return nil, formatE(ErrSummaryServiceGet, err) 2003 } 2004 2005 edges, err := makeEdges[*v2.EpochRewardSummaryEdge](summaries) 2006 if err != nil { 2007 return nil, formatE(err) 2008 } 2009 2010 connection := v2.EpochRewardSummaryConnection{ 2011 Edges: edges, 2012 PageInfo: pageInfo.ToProto(), 2013 } 2014 2015 return &v2.ListEpochRewardSummariesResponse{ 2016 Summaries: &connection, 2017 }, nil 2018 } 2019 2020 // GetDeposit gets a deposit by ID. 2021 func (t *TradingDataServiceV2) GetDeposit(ctx context.Context, req *v2.GetDepositRequest) (*v2.GetDepositResponse, error) { 2022 defer metrics.StartAPIRequestAndTimeGRPC("GetDepositV2")() 2023 2024 if len(req.Id) == 0 { 2025 return nil, formatE(ErrMissingDepositID) 2026 } 2027 2028 if !crypto.IsValidVegaPubKey(req.Id) { 2029 return nil, formatE(ErrNotAValidVegaID) 2030 } 2031 2032 deposit, err := t.depositService.GetByID(ctx, req.Id) 2033 if err != nil { 2034 return nil, formatE(ErrDepositServiceGet, err) 2035 } 2036 2037 return &v2.GetDepositResponse{ 2038 Deposit: deposit.ToProto(), 2039 }, nil 2040 } 2041 2042 // ListDeposits gets deposits for a party. 2043 func (t *TradingDataServiceV2) ListDeposits(ctx context.Context, req *v2.ListDepositsRequest) (*v2.ListDepositsResponse, error) { 2044 defer metrics.StartAPIRequestAndTimeGRPC("ListDepositsV2")() 2045 2046 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2047 if err != nil { 2048 return nil, formatE(ErrInvalidPagination, err) 2049 } 2050 2051 if len(req.PartyId) > 0 && req.PartyId != networkPartyID && !crypto.IsValidVegaPubKey(req.PartyId) { 2052 return nil, formatE(ErrInvalidPartyID) 2053 } 2054 2055 dateRange := entities.DateRangeFromProto(req.DateRange) 2056 2057 deposits, pageInfo, err := t.depositService.GetByParty(ctx, req.PartyId, false, pagination, dateRange) 2058 if err != nil { 2059 return nil, formatE(ErrDepositServiceGet, err) 2060 } 2061 2062 edges, err := makeEdges[*v2.DepositEdge](deposits) 2063 if err != nil { 2064 return nil, formatE(err) 2065 } 2066 2067 depositConnection := &v2.DepositsConnection{ 2068 Edges: edges, 2069 PageInfo: pageInfo.ToProto(), 2070 } 2071 2072 return &v2.ListDepositsResponse{ 2073 Deposits: depositConnection, 2074 }, nil 2075 } 2076 2077 func makeEdges[T proto.Message, V entities.PagedEntity[T]](inputs []V, args ...any) (edges []T, err error) { 2078 if len(inputs) == 0 { 2079 return 2080 } 2081 edges = make([]T, len(inputs)) 2082 for i, input := range inputs { 2083 edges[i], err = input.ToProtoEdge(args...) 2084 if err != nil { 2085 err = errors.Wrapf(err, "failed to make edge for %v", input) 2086 return 2087 } 2088 } 2089 return 2090 } 2091 2092 // GetWithdrawal gets a withdrawal by ID. 2093 func (t *TradingDataServiceV2) GetWithdrawal(ctx context.Context, req *v2.GetWithdrawalRequest) (*v2.GetWithdrawalResponse, error) { 2094 defer metrics.StartAPIRequestAndTimeGRPC("GetWithdrawalV2")() 2095 2096 if len(req.Id) == 0 { 2097 return nil, formatE(ErrMissingWithdrawalID) 2098 } 2099 2100 if !crypto.IsValidVegaPubKey(req.Id) { 2101 return nil, formatE(ErrInvalidWithdrawalID) 2102 } 2103 2104 withdrawal, err := t.withdrawalService.GetByID(ctx, req.Id) 2105 if err != nil { 2106 return nil, formatE(ErrWithdrawalServiceGet, err) 2107 } 2108 2109 return &v2.GetWithdrawalResponse{ 2110 Withdrawal: withdrawal.ToProto(), 2111 }, nil 2112 } 2113 2114 // ListWithdrawals gets withdrawals for a party. 2115 func (t *TradingDataServiceV2) ListWithdrawals(ctx context.Context, req *v2.ListWithdrawalsRequest) (*v2.ListWithdrawalsResponse, error) { 2116 defer metrics.StartAPIRequestAndTimeGRPC("ListWithdrawalsV2")() 2117 2118 if len(req.PartyId) > 0 && req.PartyId != networkPartyID && !crypto.IsValidVegaPubKey(req.PartyId) { 2119 return nil, formatE(ErrInvalidPartyID) 2120 } 2121 2122 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2123 if err != nil { 2124 return nil, formatE(ErrInvalidPagination, err) 2125 } 2126 2127 dateRange := entities.DateRangeFromProto(req.DateRange) 2128 withdrawals, pageInfo, err := t.withdrawalService.GetByParty(ctx, req.PartyId, false, pagination, dateRange) 2129 if err != nil { 2130 return nil, formatE(ErrWithdrawalServiceGet, err) 2131 } 2132 2133 edges, err := makeEdges[*v2.WithdrawalEdge](withdrawals) 2134 if err != nil { 2135 return nil, formatE(err) 2136 } 2137 2138 depositConnection := &v2.WithdrawalsConnection{ 2139 Edges: edges, 2140 PageInfo: pageInfo.ToProto(), 2141 } 2142 2143 return &v2.ListWithdrawalsResponse{ 2144 Withdrawals: depositConnection, 2145 }, nil 2146 } 2147 2148 // GetAsset gets an asset by ID. 2149 func (t *TradingDataServiceV2) GetAsset(ctx context.Context, req *v2.GetAssetRequest) (*v2.GetAssetResponse, error) { 2150 defer metrics.StartAPIRequestAndTimeGRPC("GetAssetV2")() 2151 2152 if len(req.AssetId) == 0 { 2153 return nil, formatE(ErrMissingAssetID) 2154 } 2155 2156 // TODO: VOTE is a special case used for system tests. Remove this once the system tests are updated to remove the VOTE asset. 2157 if req.AssetId != "VOTE" && !crypto.IsValidVegaPubKey(req.AssetId) { 2158 return nil, formatE(ErrInvalidAssetID) 2159 } 2160 2161 asset, err := t.AssetService.GetByID(ctx, req.AssetId) 2162 if err != nil { 2163 return nil, formatE(ErrAssetServiceGetByID, err) 2164 } 2165 2166 return &v2.GetAssetResponse{ 2167 Asset: asset.ToProto(), 2168 }, nil 2169 } 2170 2171 // ListAssets gets all assets. If an asset ID is provided, it will return a single asset. 2172 func (t *TradingDataServiceV2) ListAssets(ctx context.Context, req *v2.ListAssetsRequest) (*v2.ListAssetsResponse, error) { 2173 defer metrics.StartAPIRequestAndTimeGRPC("ListAssetsV2")() 2174 2175 if assetId := ptr.UnBox(req.AssetId); assetId != "" { 2176 asset, err := t.getSingleAsset(ctx, assetId) 2177 if err != nil { 2178 return nil, formatE(ErrAssetServiceGetByID, err) 2179 } 2180 return asset, nil 2181 } 2182 2183 assets, err := t.getAllAssets(ctx, req.Pagination) 2184 if err != nil { 2185 return nil, formatE(ErrAssetServiceGetAll, err) 2186 } 2187 return assets, nil 2188 } 2189 2190 func (t *TradingDataServiceV2) getSingleAsset(ctx context.Context, assetID string) (*v2.ListAssetsResponse, error) { 2191 asset, err := t.AssetService.GetByID(ctx, assetID) 2192 if err != nil { 2193 return nil, errors.Wrapf(err, "failed to get asset by ID: %s", assetID) 2194 } 2195 2196 edges, err := makeEdges[*v2.AssetEdge]([]entities.Asset{asset}) 2197 if err != nil { 2198 return nil, err 2199 } 2200 2201 connection := &v2.AssetsConnection{ 2202 Edges: edges, 2203 PageInfo: &v2.PageInfo{ 2204 HasNextPage: false, 2205 HasPreviousPage: false, 2206 StartCursor: asset.Cursor().Encode(), 2207 EndCursor: asset.Cursor().Encode(), 2208 }, 2209 } 2210 2211 return &v2.ListAssetsResponse{ 2212 Assets: connection, 2213 }, nil 2214 } 2215 2216 func (t *TradingDataServiceV2) getAllAssets(ctx context.Context, p *v2.Pagination) (*v2.ListAssetsResponse, error) { 2217 pagination, err := entities.CursorPaginationFromProto(p) 2218 if err != nil { 2219 return nil, errors.Wrap(ErrInvalidPagination, err.Error()) 2220 } 2221 2222 assets, pageInfo, err := t.AssetService.GetAllWithCursorPagination(ctx, pagination) 2223 if err != nil { 2224 return nil, errors.Wrap(ErrAssetServiceGetAll, err.Error()) 2225 } 2226 2227 edges, err := makeEdges[*v2.AssetEdge](assets) 2228 if err != nil { 2229 return nil, err 2230 } 2231 2232 connection := &v2.AssetsConnection{ 2233 Edges: edges, 2234 PageInfo: pageInfo.ToProto(), 2235 } 2236 2237 return &v2.ListAssetsResponse{ 2238 Assets: connection, 2239 }, nil 2240 } 2241 2242 // GetOracleSpec gets an oracle spec by ID. 2243 func (t *TradingDataServiceV2) GetOracleSpec(ctx context.Context, req *v2.GetOracleSpecRequest) (*v2.GetOracleSpecResponse, error) { 2244 defer metrics.StartAPIRequestAndTimeGRPC("GetOracleSpecV2")() 2245 2246 if len(req.OracleSpecId) == 0 { 2247 return nil, formatE(ErrMissingOracleSpecID) 2248 } 2249 2250 if !crypto.IsValidVegaPubKey(req.OracleSpecId) { 2251 return nil, formatE(ErrInvalidOracleSpecID) 2252 } 2253 2254 spec, err := t.oracleSpecService.GetSpecByID(ctx, req.OracleSpecId) 2255 if err != nil { 2256 return nil, formatE(ErrOracleSpecServiceGet, errors.Wrapf(err, "OracleSpecId: %s", req.OracleSpecId)) 2257 } 2258 2259 return &v2.GetOracleSpecResponse{ 2260 OracleSpec: &vega.OracleSpec{ 2261 ExternalDataSourceSpec: &vega.ExternalDataSourceSpec{ 2262 Spec: spec.ToProto().ExternalDataSourceSpec.Spec, 2263 }, 2264 }, 2265 }, nil 2266 } 2267 2268 // ListOracleSpecs gets all oracle specs. 2269 func (t *TradingDataServiceV2) ListOracleSpecs(ctx context.Context, req *v2.ListOracleSpecsRequest) (*v2.ListOracleSpecsResponse, error) { 2270 defer metrics.StartAPIRequestAndTimeGRPC("ListOracleSpecsV2")() 2271 2272 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2273 if err != nil { 2274 return nil, formatE(ErrInvalidPagination, err) 2275 } 2276 2277 specs, pageInfo, err := t.oracleSpecService.GetSpecsWithCursorPagination(ctx, "", pagination) 2278 if err != nil { 2279 return nil, formatE(ErrOracleSpecServiceGetAll, err) 2280 } 2281 2282 edges, err := makeEdges[*v2.OracleSpecEdge](specs) 2283 if err != nil { 2284 return nil, formatE(err) 2285 } 2286 2287 connection := &v2.OracleSpecsConnection{ 2288 Edges: edges, 2289 PageInfo: pageInfo.ToProto(), 2290 } 2291 2292 return &v2.ListOracleSpecsResponse{ 2293 OracleSpecs: connection, 2294 }, nil 2295 } 2296 2297 // ListOracleData gets all oracle data. 2298 func (t *TradingDataServiceV2) ListOracleData(ctx context.Context, req *v2.ListOracleDataRequest) (*v2.ListOracleDataResponse, error) { 2299 defer metrics.StartAPIRequestAndTimeGRPC("GetOracleDataConnectionV2")() 2300 2301 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2302 if err != nil { 2303 return nil, formatE(ErrInvalidPagination, err) 2304 } 2305 2306 var ( 2307 data []entities.OracleData 2308 pageInfo entities.PageInfo 2309 ) 2310 2311 oracleSpecID := ptr.UnBox(req.OracleSpecId) 2312 data, pageInfo, err = t.oracleDataService.ListOracleData(ctx, oracleSpecID, pagination) 2313 2314 if err != nil { 2315 return nil, formatE(ErrOracleDataServiceGet, err) 2316 } 2317 2318 edges, err := makeEdges[*v2.OracleDataEdge](data) 2319 if err != nil { 2320 return nil, formatE(err) 2321 } 2322 2323 connection := &v2.OracleDataConnection{ 2324 Edges: edges, 2325 PageInfo: pageInfo.ToProto(), 2326 } 2327 2328 return &v2.ListOracleDataResponse{ 2329 OracleData: connection, 2330 }, nil 2331 } 2332 2333 // ListLiquidityProvisions gets all liquidity provisions. 2334 func (t *TradingDataServiceV2) ListLiquidityProvisions(ctx context.Context, req *v2.ListLiquidityProvisionsRequest) (*v2.ListLiquidityProvisionsResponse, error) { 2335 defer metrics.StartAPIRequestAndTimeGRPC("GetLiquidityProvisionsV2")() 2336 2337 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2338 if err != nil { 2339 return nil, formatE(ErrInvalidPagination, err) 2340 } 2341 2342 partyID := entities.PartyID(ptr.UnBox(req.PartyId)) 2343 marketID := entities.MarketID(ptr.UnBox(req.MarketId)) 2344 reference := ptr.UnBox(req.Reference) 2345 live := ptr.UnBox(req.Live) 2346 2347 lps, pageInfo, err := t.liquidityProvisionService.Get(ctx, partyID, marketID, reference, live, pagination) 2348 if err != nil { 2349 return nil, formatE(ErrLiquidityProvisionServiceGet, errors.Wrapf(err, 2350 "partyID: %s, marketID: %s, reference: %s", partyID, marketID, reference)) 2351 } 2352 2353 provisions := make([]entities.LiquidityProvision, len(lps)) 2354 for i, lp := range lps { 2355 provisions[i] = entities.LiquidityProvision{ 2356 ID: lp.ID, 2357 PartyID: lp.PartyID, 2358 CreatedAt: lp.CreatedAt, 2359 UpdatedAt: lp.UpdatedAt, 2360 MarketID: lp.MarketID, 2361 CommitmentAmount: lp.CommitmentAmount, 2362 Fee: lp.Fee, 2363 Sells: lp.Sells, 2364 Buys: lp.Buys, 2365 Version: lp.Version, 2366 Status: lp.Status, 2367 Reference: lp.Reference, 2368 TxHash: lp.TxHash, 2369 VegaTime: lp.VegaTime, 2370 } 2371 } 2372 2373 edges, err := makeEdges[*v2.LiquidityProvisionsEdge](provisions) 2374 if err != nil { 2375 return nil, formatE(err) 2376 } 2377 2378 liquidityProvisionConnection := &v2.LiquidityProvisionsConnection{ 2379 Edges: edges, 2380 PageInfo: pageInfo.ToProto(), 2381 } 2382 2383 return &v2.ListLiquidityProvisionsResponse{ 2384 LiquidityProvisions: liquidityProvisionConnection, 2385 }, nil 2386 } 2387 2388 // ListAllLiquidityProvisions gets a list of liquidity provisions for a given market. This is similar to the list liquidity provisions API 2389 // but returns a current and pending liquidity provision in the event a provision has been updated by the provider 2390 // but the updated provision will not be active until the next epoch. 2391 func (t *TradingDataServiceV2) ListAllLiquidityProvisions(ctx context.Context, req *v2.ListAllLiquidityProvisionsRequest) (*v2.ListAllLiquidityProvisionsResponse, error) { 2392 defer metrics.StartAPIRequestAndTimeGRPC("GetLiquidityProvisionsWithPending")() 2393 2394 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2395 if err != nil { 2396 return nil, formatE(ErrInvalidPagination, err) 2397 } 2398 2399 partyID := entities.PartyID(ptr.UnBox(req.PartyId)) 2400 marketID := entities.MarketID(ptr.UnBox(req.MarketId)) 2401 reference := ptr.UnBox(req.Reference) 2402 live := ptr.UnBox(req.Live) 2403 2404 lps, pageInfo, err := t.liquidityProvisionService.Get(ctx, partyID, marketID, reference, live, pagination) 2405 if err != nil { 2406 return nil, formatE(ErrLiquidityProvisionServiceGet, errors.Wrapf(err, 2407 "partyID: %s, marketID: %s, reference: %s", partyID, marketID, reference)) 2408 } 2409 2410 edges, err := makeEdges[*v2.LiquidityProvisionWithPendingEdge](lps) 2411 if err != nil { 2412 return nil, formatE(err) 2413 } 2414 2415 liquidityProvisionConnection := &v2.LiquidityProvisionsWithPendingConnection{ 2416 Edges: edges, 2417 PageInfo: pageInfo.ToProto(), 2418 } 2419 2420 return &v2.ListAllLiquidityProvisionsResponse{ 2421 LiquidityProvisions: liquidityProvisionConnection, 2422 }, nil 2423 } 2424 2425 // ObserveLiquidityProvisions subscribes to liquidity provisions. 2426 func (t *TradingDataServiceV2) ObserveLiquidityProvisions(req *v2.ObserveLiquidityProvisionsRequest, srv v2.TradingDataService_ObserveLiquidityProvisionsServer) error { 2427 // Wrap context from the request into cancellable. We can close internal chan on error. 2428 ctx, cancel := context.WithCancel(srv.Context()) 2429 defer cancel() 2430 2431 lpCh, ref := t.liquidityProvisionService.ObserveLiquidityProvisions(ctx, t.config.StreamRetries, req.MarketId, req.PartyId) 2432 2433 if t.log.GetLevel() == logging.DebugLevel { 2434 t.log.Debug("Liquidity Provisions subscriber - new rpc stream", logging.Uint64("ref", ref)) 2435 } 2436 2437 return observeBatch(ctx, t.log, "LiquidityProvision", lpCh, ref, func(lps []entities.LiquidityProvision) error { 2438 protos := make([]*vega.LiquidityProvision, 0, len(lps)) 2439 for _, v := range lps { 2440 protos = append(protos, v.ToProto()) 2441 } 2442 batches := batch(protos, snapshotPageSize) 2443 for _, batch := range batches { 2444 response := &v2.ObserveLiquidityProvisionsResponse{LiquidityProvisions: batch} 2445 if err := srv.Send(response); err != nil { 2446 return errors.Wrap(err, "sending liquidity provisions updates") 2447 } 2448 } 2449 return nil 2450 }) 2451 } 2452 2453 func (t *TradingDataServiceV2) ListLiquidityProviders(ctx context.Context, req *v2.ListLiquidityProvidersRequest) ( 2454 *v2.ListLiquidityProvidersResponse, error, 2455 ) { 2456 defer metrics.StartAPIRequestAndTimeGRPC("ListLiquidityProviders") 2457 2458 if req.MarketId == nil && req.PartyId == nil { 2459 return nil, formatE(ErrMalformedRequest, errors.New("marketId or partyId or both must be provided")) 2460 } 2461 2462 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2463 if err != nil { 2464 return nil, formatE(ErrInvalidPagination, err) 2465 } 2466 2467 var ( 2468 marketID *entities.MarketID 2469 partyID *entities.PartyID 2470 ) 2471 2472 if req.MarketId != nil { 2473 marketID = ptr.From(entities.MarketID(ptr.UnBox(req.MarketId))) 2474 } 2475 2476 if req.PartyId != nil { 2477 partyID = ptr.From(entities.PartyID(ptr.UnBox(req.PartyId))) 2478 } 2479 2480 providers, pageInfo, err := t.liquidityProvisionService.ListProviders(ctx, partyID, marketID, pagination) 2481 if err != nil { 2482 return nil, formatE(ErrLiquidityProvisionServiceGetProviders, errors.Wrapf(err, 2483 "marketID: %s", marketID)) 2484 } 2485 2486 if len(providers) == 0 { 2487 return &v2.ListLiquidityProvidersResponse{ 2488 LiquidityProviders: &v2.LiquidityProviderConnection{ 2489 Edges: []*v2.LiquidityProviderEdge{}, 2490 PageInfo: pageInfo.ToProto(), 2491 }, 2492 }, nil 2493 } 2494 2495 edges, err := makeEdges[*v2.LiquidityProviderEdge](providers) 2496 if err != nil { 2497 return nil, formatE(err) 2498 } 2499 2500 conn := &v2.LiquidityProviderConnection{ 2501 Edges: edges, 2502 PageInfo: pageInfo.ToProto(), 2503 } 2504 2505 return &v2.ListLiquidityProvidersResponse{ 2506 LiquidityProviders: conn, 2507 }, nil 2508 } 2509 2510 func (t *TradingDataServiceV2) ListPaidLiquidityFees(ctx context.Context, req *v2.ListPaidLiquidityFeesRequest) ( 2511 *v2.ListPaidLiquidityFeesResponse, error, 2512 ) { 2513 defer metrics.StartAPIRequestAndTimeGRPC("ListPaidLiquidityFees")() 2514 2515 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2516 if err != nil { 2517 return nil, formatE(ErrInvalidPagination, err) 2518 } 2519 2520 var marketID *entities.MarketID 2521 var marketIDs []string 2522 if req.MarketId != nil { 2523 marketID = ptr.From(entities.MarketID(*req.MarketId)) 2524 marketIDs = []string{*req.MarketId} 2525 } 2526 2527 var assetID *entities.AssetID 2528 if req.AssetId != nil { 2529 assetID = ptr.From(entities.AssetID(*req.AssetId)) 2530 } 2531 2532 partyIDs := req.PartyIds 2533 if req.IncludeDerivedParties != nil && *req.IncludeDerivedParties { 2534 if len(partyIDs) == 0 { 2535 return nil, formatE(ErrMissingPartyID) 2536 } 2537 subKeys, err := t.AMMPoolService.GetSubKeysForParties(ctx, partyIDs, marketIDs) 2538 if err != nil { 2539 return nil, formatE(ErrInvalidFilter, err) 2540 } 2541 partyIDs = append(partyIDs, subKeys...) 2542 } 2543 stats, pageInfo, err := t.paidLiquidityFeesStatsService.List(ctx, marketID, assetID, req.EpochSeq, partyIDs, pagination, req.EpochFrom, req.EpochTo) 2544 if err != nil { 2545 return nil, formatE(ErrListPaidLiquidityFees, err) 2546 } 2547 2548 edges, err := makeEdges[*v2.PaidLiquidityFeesEdge](stats) 2549 if err != nil { 2550 return nil, formatE(err) 2551 } 2552 2553 return &v2.ListPaidLiquidityFeesResponse{ 2554 PaidLiquidityFees: &v2.PaidLiquidityFeesConnection{ 2555 Edges: edges, 2556 PageInfo: pageInfo.ToProto(), 2557 }, 2558 }, nil 2559 } 2560 2561 // GetGovernanceData gets governance data. 2562 func (t *TradingDataServiceV2) GetGovernanceData(ctx context.Context, req *v2.GetGovernanceDataRequest) (*v2.GetGovernanceDataResponse, error) { 2563 defer metrics.StartAPIRequestAndTimeGRPC("GetGovernanceData")() 2564 2565 var ( 2566 proposal entities.Proposal 2567 err error 2568 ) 2569 if req.ProposalId != nil { 2570 proposal, err = t.governanceService.GetProposalByID(ctx, *req.ProposalId) 2571 } else if req.Reference != nil { 2572 proposal, err = t.governanceService.GetProposalByReference(ctx, *req.Reference) 2573 } else { 2574 return nil, formatE(ErrMissingProposalIDOrReference) 2575 } 2576 if err != nil { 2577 return nil, formatE(ErrGovernanceServiceGet, 2578 errors.Wrapf(err, "proposalID: %s, reference: %s", ptr.UnBox(req.ProposalId), ptr.UnBox(req.Reference))) 2579 } 2580 2581 gd, err := t.proposalToGovernanceData(ctx, proposal) 2582 if err != nil { 2583 return nil, formatE(ErrNotMapped, err) 2584 } 2585 2586 return &v2.GetGovernanceDataResponse{ 2587 Data: gd, 2588 }, nil 2589 } 2590 2591 // ListGovernanceData lists governance data using cursor pagination. 2592 func (t *TradingDataServiceV2) ListGovernanceData(ctx context.Context, req *v2.ListGovernanceDataRequest) (*v2.ListGovernanceDataResponse, error) { 2593 defer metrics.StartAPIRequestAndTimeGRPC("ListGovernanceDataV2")() 2594 2595 var state *entities.ProposalState 2596 if req.ProposalState != nil { 2597 state = ptr.From(entities.ProposalState(*req.ProposalState)) 2598 } 2599 2600 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2601 if err != nil { 2602 return nil, formatE(ErrInvalidPagination, err) 2603 } 2604 2605 var ( 2606 proposal entities.Proposal 2607 proposals []entities.Proposal 2608 pageInfo entities.PageInfo 2609 ) 2610 2611 if req.ProposalReference != nil { 2612 proposal, err = t.governanceService.GetProposalByReference(ctx, *req.ProposalReference) 2613 if err != nil { 2614 return nil, formatE(ErrGovernanceServiceGet, 2615 errors.Wrapf(err, "proposalReference: %s", ptr.UnBox(req.ProposalReference))) 2616 } 2617 proposals = []entities.Proposal{proposal} 2618 pageInfo.StartCursor = proposal.Cursor().Encode() 2619 pageInfo.EndCursor = proposal.Cursor().Encode() 2620 } else { 2621 proposals, pageInfo, err = t.governanceService.GetProposals( 2622 ctx, 2623 state, 2624 req.ProposerPartyId, 2625 (*entities.ProposalType)(req.ProposalType), 2626 pagination, 2627 ) 2628 if err != nil { 2629 return nil, formatE(ErrGovernanceServiceGetProposals, errors.Wrapf(err, "ProposerPartyId: %s", ptr.UnBox(req.ProposerPartyId))) 2630 } 2631 } 2632 2633 edges, err := makeEdges[*v2.GovernanceDataEdge](proposals) 2634 if err != nil { 2635 return nil, formatE(err) 2636 } 2637 2638 for i := range edges { 2639 proposalID := edges[i].Node.Proposal.Id 2640 edges[i].Node.Yes, edges[i].Node.No, err = t.getVotesByProposal(ctx, proposalID) 2641 if err != nil { 2642 return nil, formatE(ErrGovernanceServiceGetVotes, errors.Wrapf(err, "proposalID: %s", proposalID)) 2643 } 2644 2645 if len(edges[i].Node.Proposals) > 0 { 2646 edges[i].Node.ProposalType = vega.GovernanceData_TYPE_BATCH 2647 } 2648 } 2649 2650 proposalsConnection := &v2.GovernanceDataConnection{ 2651 Edges: edges, 2652 PageInfo: pageInfo.ToProto(), 2653 } 2654 2655 return &v2.ListGovernanceDataResponse{ 2656 Connection: proposalsConnection, 2657 }, nil 2658 } 2659 2660 func (t *TradingDataServiceV2) getVotesByProposal(ctx context.Context, proposalID string) (yesVotes, noVotes []*vega.Vote, err error) { 2661 var votes []entities.Vote 2662 votes, err = t.governanceService.GetVotes(ctx, &proposalID, nil, nil) 2663 if err != nil { 2664 return 2665 } 2666 for _, vote := range votes { 2667 switch vote.Value { 2668 case entities.VoteValueYes: 2669 yesVotes = append(yesVotes, vote.ToProto()) 2670 case entities.VoteValueNo: 2671 noVotes = append(noVotes, vote.ToProto()) 2672 } 2673 } 2674 return 2675 } 2676 2677 // ListVotes gets all Votes. 2678 func (t *TradingDataServiceV2) ListVotes(ctx context.Context, req *v2.ListVotesRequest) (*v2.ListVotesResponse, error) { 2679 defer metrics.StartAPIRequestAndTimeGRPC("ListVotesV2")() 2680 2681 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2682 if err != nil { 2683 return nil, formatE(ErrInvalidPagination, err) 2684 } 2685 2686 if req.PartyId == nil && req.ProposalId == nil { 2687 return nil, formatE(ErrMissingProposalIDAndPartyID) 2688 } 2689 2690 votes, pageInfo, err := t.governanceService.GetConnection(ctx, req.ProposalId, req.PartyId, pagination) 2691 if err != nil { 2692 return nil, formatE(ErrGovernanceServiceGetVotes, errors.Wrapf(err, 2693 "proposalID: %s, partyID: %s", ptr.UnBox(req.ProposalId), ptr.UnBox(req.PartyId))) 2694 } 2695 2696 edges, err := makeEdges[*v2.VoteEdge](votes) 2697 if err != nil { 2698 return nil, formatE(err) 2699 } 2700 2701 VotesConnection := &v2.VoteConnection{ 2702 Edges: edges, 2703 PageInfo: pageInfo.ToProto(), 2704 } 2705 2706 return &v2.ListVotesResponse{ 2707 Votes: VotesConnection, 2708 }, nil 2709 } 2710 2711 func (t *TradingDataServiceV2) GetTransfer(ctx context.Context, req *v2.GetTransferRequest) (*v2.GetTransferResponse, error) { 2712 defer metrics.StartAPIRequestAndTimeGRPC("GetTransferV2")() 2713 if len(req.TransferId) == 0 { 2714 return nil, formatE(ErrMissingTransferID) 2715 } 2716 transfer, err := t.transfersService.GetByID(ctx, req.TransferId) 2717 if err != nil { 2718 return nil, formatE(err) 2719 } 2720 tp, err := transfer.ToProto(ctx, t.AccountService) 2721 if err != nil { 2722 return nil, formatE(err) 2723 } 2724 fees := make([]*eventspb.TransferFees, 0, len(transfer.Fees)) 2725 for _, f := range transfer.Fees { 2726 fees = append(fees, f.ToProto()) 2727 } 2728 return &v2.GetTransferResponse{ 2729 TransferNode: &v2.TransferNode{ 2730 Transfer: tp, 2731 Fees: fees, 2732 }, 2733 }, nil 2734 } 2735 2736 // ListTransfers lists transfers using cursor pagination. If a pubkey is provided, it will list transfers for that pubkey. 2737 func (t *TradingDataServiceV2) ListTransfers(ctx context.Context, req *v2.ListTransfersRequest) (*v2.ListTransfersResponse, error) { 2738 defer metrics.StartAPIRequestAndTimeGRPC("ListTransfersV2")() 2739 const transfersDefaultPageSize int32 = 50 2740 2741 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2742 // the default size is too big for transfers so if no pagination is provided, set it to something smaller 2743 if req.Pagination == nil { 2744 pagination.Forward.Limit = ptr.From(transfersDefaultPageSize) 2745 } 2746 if err != nil { 2747 return nil, formatE(ErrInvalidPagination, err) 2748 } 2749 2750 var ( 2751 transfers []entities.TransferDetails 2752 pageInfo entities.PageInfo 2753 isReward bool 2754 ) 2755 if req.IsReward != nil { 2756 isReward = *req.IsReward 2757 } 2758 2759 filters := sqlstore.ListTransfersFilters{ 2760 FromEpoch: req.FromEpoch, 2761 ToEpoch: req.ToEpoch, 2762 } 2763 if req.Status != nil { 2764 filters.Status = ptr.From(entities.TransferStatus(*req.Status)) 2765 } 2766 if req.Scope != nil { 2767 filters.Scope = ptr.From(entities.TransferScope(*req.Scope)) 2768 } 2769 if req.GameId != nil { 2770 filters.GameID = ptr.From(entities.GameID(*req.GameId)) 2771 } 2772 filters.FromAccountType = req.FromAccountType 2773 filters.ToAccountType = req.ToAccountType 2774 2775 if req.Pubkey == nil { 2776 if !isReward { 2777 transfers, pageInfo, err = t.transfersService.GetAll(ctx, pagination, filters) 2778 } else { 2779 transfers, pageInfo, err = t.transfersService.GetAllRewards(ctx, pagination, filters) 2780 } 2781 } else { 2782 if isReward && req.Direction != v2.TransferDirection_TRANSFER_DIRECTION_TRANSFER_FROM { 2783 err = errors.Errorf("invalid transfer direction for reward transfers: %v", req.Direction) 2784 return nil, formatE(ErrTransferServiceGet, errors.Wrapf(err, "pubkey: %s", ptr.UnBox(req.Pubkey))) 2785 } 2786 switch req.Direction { 2787 case v2.TransferDirection_TRANSFER_DIRECTION_TRANSFER_FROM: 2788 if isReward { 2789 transfers, pageInfo, err = t.transfersService.GetRewardTransfersFromParty(ctx, pagination, filters, entities.PartyID(*req.Pubkey)) 2790 } else { 2791 transfers, pageInfo, err = t.transfersService.GetTransfersFromParty(ctx, pagination, filters, entities.PartyID(*req.Pubkey)) 2792 } 2793 case v2.TransferDirection_TRANSFER_DIRECTION_TRANSFER_TO: 2794 transfers, pageInfo, err = t.transfersService.GetTransfersToParty(ctx, pagination, filters, entities.PartyID(*req.Pubkey)) 2795 case v2.TransferDirection_TRANSFER_DIRECTION_TRANSFER_TO_OR_FROM: 2796 transfers, pageInfo, err = t.transfersService.GetTransfersToOrFromParty(ctx, pagination, filters, entities.PartyID(*req.Pubkey)) 2797 default: 2798 err = errors.Errorf("unknown transfer direction: %v", req.Direction) 2799 } 2800 } 2801 if err != nil { 2802 t.log.Error("Something went wrong listing transfers", logging.Error(err)) 2803 return nil, formatE(ErrTransferServiceGet, errors.Wrapf(err, "pubkey: %s", ptr.UnBox(req.Pubkey))) 2804 } 2805 2806 edges, err := makeEdges[*v2.TransferEdge](transfers, ctx, t.AccountService) 2807 if err != nil { 2808 t.log.Error("Something went wrong making transfer edges", logging.Error(err)) 2809 return nil, formatE(err) 2810 } 2811 2812 return &v2.ListTransfersResponse{Transfers: &v2.TransferConnection{ 2813 Edges: edges, 2814 PageInfo: pageInfo.ToProto(), 2815 }}, nil 2816 } 2817 2818 // GetOrder gets an order by ID. 2819 func (t *TradingDataServiceV2) GetOrder(ctx context.Context, req *v2.GetOrderRequest) (*v2.GetOrderResponse, error) { 2820 defer metrics.StartAPIRequestAndTimeGRPC("GetOrderV2")() 2821 2822 if len(req.OrderId) == 0 { 2823 return nil, formatE(ErrMissingOrderID) 2824 } 2825 2826 if !crypto.IsValidVegaID(req.OrderId) { 2827 return nil, formatE(ErrInvalidOrderID) 2828 } 2829 2830 if req.Version != nil && *req.Version <= 0 { 2831 return nil, formatE(ErrNegativeOrderVersion) 2832 } 2833 2834 order, err := t.orderService.GetOrder(ctx, req.OrderId, req.Version) 2835 if err != nil { 2836 return nil, formatE(ErrOrderNotFound, errors.Wrapf(err, "orderID: %s", req.OrderId)) 2837 } 2838 2839 return &v2.GetOrderResponse{ 2840 Order: order.ToProto(), 2841 }, nil 2842 } 2843 2844 // ListOrders lists orders using cursor pagination. 2845 func (t *TradingDataServiceV2) ListOrders(ctx context.Context, req *v2.ListOrdersRequest) (*v2.ListOrdersResponse, error) { 2846 defer metrics.StartAPIRequestAndTimeGRPC("ListOrdersV2")() 2847 2848 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2849 if err != nil { 2850 return nil, formatE(ErrInvalidPagination, err) 2851 } 2852 2853 var filter entities.OrderFilter 2854 if req.Filter != nil { 2855 dateRange := entities.DateRangeFromProto(req.Filter.DateRange) 2856 marketIDs := NewVegaIDSlice(req.Filter.MarketIds...) 2857 if err := marketIDs.Ensure(); err != nil { 2858 return nil, formatE(err, errors.New("one or more market id is invalid")) 2859 } 2860 2861 partyIDs := NewVegaIDSlice(req.Filter.PartyIds...) 2862 if err := partyIDs.Ensure(); err != nil { 2863 return nil, formatE(err, errors.New("one or more party id is invalid")) 2864 } 2865 2866 filter = entities.OrderFilter{ 2867 Statuses: req.Filter.Statuses, 2868 Types: req.Filter.Types, 2869 TimeInForces: req.Filter.TimeInForces, 2870 Reference: req.Filter.Reference, 2871 ExcludeLiquidity: req.Filter.ExcludeLiquidity, 2872 LiveOnly: ptr.UnBox(req.Filter.LiveOnly), 2873 PartyIDs: partyIDs, 2874 MarketIDs: marketIDs, 2875 DateRange: &entities.DateRange{Start: dateRange.Start, End: dateRange.End}, 2876 } 2877 } 2878 2879 orders, pageInfo, err := t.orderService.ListOrders(ctx, pagination, filter) 2880 if err != nil { 2881 if errors.Is(err, sqlstore.ErrLastPaginationNotSupported) { 2882 return nil, formatE(ErrLastPaginationNotSupported, err) 2883 } 2884 2885 return nil, formatE(err) 2886 } 2887 2888 edges, err := makeEdges[*v2.OrderEdge](orders) 2889 if err != nil { 2890 return nil, formatE(err) 2891 } 2892 2893 ordersConnection := &v2.OrderConnection{ 2894 Edges: edges, 2895 PageInfo: pageInfo.ToProto(), 2896 } 2897 2898 return &v2.ListOrdersResponse{ 2899 Orders: ordersConnection, 2900 }, nil 2901 } 2902 2903 // ListOrderVersions lists order versions using cursor pagination. 2904 func (t *TradingDataServiceV2) ListOrderVersions(ctx context.Context, req *v2.ListOrderVersionsRequest) (*v2.ListOrderVersionsResponse, error) { 2905 defer metrics.StartAPIRequestAndTimeGRPC("ListOrderVersionsV2")() 2906 2907 if len(req.OrderId) == 0 { 2908 return nil, formatE(ErrMissingOrderID) 2909 } 2910 2911 if !crypto.IsValidVegaID(req.OrderId) { 2912 return nil, formatE(ErrInvalidOrderID) 2913 } 2914 2915 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 2916 if err != nil { 2917 return nil, formatE(ErrInvalidPagination, err) 2918 } 2919 2920 orders, pageInfo, err := t.orderService.ListOrderVersions(ctx, req.OrderId, pagination) 2921 if err != nil { 2922 return nil, formatE(ErrOrderServiceGetVersions, errors.Wrapf(err, "orderID: %s", req.OrderId)) 2923 } 2924 2925 edges, err := makeEdges[*v2.OrderEdge](orders) 2926 if err != nil { 2927 return nil, formatE(err) 2928 } 2929 2930 ordersConnection := &v2.OrderConnection{ 2931 Edges: edges, 2932 PageInfo: pageInfo.ToProto(), 2933 } 2934 2935 return &v2.ListOrderVersionsResponse{ 2936 Orders: ordersConnection, 2937 }, nil 2938 } 2939 2940 type VegaIDsSlice []string 2941 2942 func (s VegaIDsSlice) Ensure() error { 2943 for _, v := range s { 2944 if v != networkPartyID && !crypto.IsValidVegaPubKey(v) { 2945 return ErrInvalidPartyID 2946 } 2947 } 2948 2949 return nil 2950 } 2951 2952 func NewVegaIDSlice(input ...string) VegaIDsSlice { 2953 ids := make(VegaIDsSlice, 0) 2954 for _, id := range input { 2955 if strings.ContainsRune(id, ',') { 2956 ids = append(ids, strings.Split(id, ",")...) 2957 } else { 2958 ids = append(ids, id) 2959 } 2960 } 2961 2962 return ids 2963 } 2964 2965 // ObserveOrders subscribes to a stream of orders. 2966 func (t *TradingDataServiceV2) ObserveOrders(req *v2.ObserveOrdersRequest, srv v2.TradingDataService_ObserveOrdersServer) error { 2967 // Wrap context from the request into cancellable. We can close internal chan on error. 2968 ctx, cancel := context.WithCancel(srv.Context()) 2969 defer cancel() 2970 2971 marketIDs := NewVegaIDSlice(req.MarketIds...) 2972 if err := marketIDs.Ensure(); err != nil { 2973 return formatE(err, errors.New("one or more market id is invalid")) 2974 } 2975 2976 partyIDs := NewVegaIDSlice(req.PartyIds...) 2977 if err := partyIDs.Ensure(); err != nil { 2978 return formatE(err, errors.New("one or more party id is invalid")) 2979 } 2980 2981 if err := t.sendOrdersSnapshot(ctx, req, srv); err != nil { 2982 return formatE(err) 2983 } 2984 ordersChan, ref := t.orderService.ObserveOrders(ctx, t.config.StreamRetries, marketIDs, partyIDs, ptr.UnBox(req.ExcludeLiquidity)) 2985 2986 if t.log.GetLevel() == logging.DebugLevel { 2987 t.log.Debug("Orders subscriber - new rpc stream", logging.Uint64("ref", ref)) 2988 } 2989 2990 return observeBatch(ctx, t.log, "Order", ordersChan, ref, func(orders []entities.Order) error { 2991 protos := make([]*vega.Order, 0, len(orders)) 2992 for _, v := range orders { 2993 protos = append(protos, v.ToProto()) 2994 } 2995 2996 batches := batch(protos, snapshotPageSize) 2997 2998 for _, batch := range batches { 2999 updates := &v2.OrderUpdates{Orders: batch} 3000 responseUpdates := &v2.ObserveOrdersResponse_Updates{Updates: updates} 3001 response := &v2.ObserveOrdersResponse{Response: responseUpdates} 3002 if err := srv.Send(response); err != nil { 3003 return errors.Wrap(err, "sending orders updates") 3004 } 3005 } 3006 return nil 3007 }) 3008 } 3009 3010 func (t *TradingDataServiceV2) sendOrdersSnapshot(ctx context.Context, req *v2.ObserveOrdersRequest, srv v2.TradingDataService_ObserveOrdersServer) error { 3011 orders, pageInfo, err := t.orderService.ListOrders(ctx, entities.CursorPagination{NewestFirst: true}, entities.OrderFilter{ 3012 MarketIDs: req.MarketIds, 3013 PartyIDs: req.PartyIds, 3014 ExcludeLiquidity: ptr.UnBox(req.ExcludeLiquidity), 3015 LiveOnly: true, 3016 }) 3017 if err != nil { 3018 return errors.Wrap(err, "fetching orders initial image") 3019 } 3020 3021 if pageInfo.HasNextPage { 3022 return errors.New("orders initial image spans multiple pages") 3023 } 3024 3025 protos := make([]*vega.Order, len(orders)) 3026 for i := 0; i < len(orders); i++ { 3027 protos[i] = orders[i].ToProto() 3028 } 3029 3030 batches := batch(protos, snapshotPageSize) 3031 3032 for i, batch := range batches { 3033 isLast := i == len(batches)-1 3034 positionList := &v2.OrderSnapshotPage{Orders: batch, LastPage: isLast} 3035 responseSnapshot := &v2.ObserveOrdersResponse_Snapshot{Snapshot: positionList} 3036 response := &v2.ObserveOrdersResponse{Response: responseSnapshot} 3037 if err := srv.Send(response); err != nil { 3038 return errors.Wrap(err, "sending orders initial image") 3039 } 3040 } 3041 return nil 3042 } 3043 3044 // ListDelegations returns a list of delegations using cursor pagination. 3045 func (t *TradingDataServiceV2) ListDelegations(ctx context.Context, req *v2.ListDelegationsRequest) (*v2.ListDelegationsResponse, error) { 3046 defer metrics.StartAPIRequestAndTimeGRPC("ListDelegationsV2")() 3047 3048 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 3049 if err != nil { 3050 return nil, formatE(ErrInvalidPagination, err) 3051 } 3052 3053 var epochID *int64 3054 if req.EpochId != nil { 3055 epochIDVal := *req.EpochId 3056 epoch, err := strconv.ParseInt(epochIDVal, 10, 64) 3057 if err != nil { 3058 return nil, formatE(ErrEpochIDParse, errors.Wrapf(err, "epochID: %s", epochIDVal)) 3059 } 3060 epochID = &epoch 3061 } 3062 3063 delegations, pageInfo, err := t.delegationService.Get(ctx, req.PartyId, req.NodeId, epochID, pagination) 3064 if err != nil { 3065 return nil, formatE(ErrDelegationServiceGet, errors.Wrapf(err, "partyID: %s, nodeID: %s, epochID: %d", 3066 ptr.UnBox(req.PartyId), ptr.UnBox(req.NodeId), epochID)) 3067 } 3068 3069 edges, err := makeEdges[*v2.DelegationEdge](delegations) 3070 if err != nil { 3071 return nil, formatE(err) 3072 } 3073 3074 delegationsConnection := &v2.DelegationsConnection{ 3075 Edges: edges, 3076 PageInfo: pageInfo.ToProto(), 3077 } 3078 3079 return &v2.ListDelegationsResponse{ 3080 Delegations: delegationsConnection, 3081 }, nil 3082 } 3083 3084 func (t *TradingDataServiceV2) marketExistsForID(ctx context.Context, marketID string) bool { 3085 _, err := t.MarketsService.GetByID(ctx, marketID) 3086 return err == nil 3087 } 3088 3089 // GetNetworkData retrieve network data regarding the nodes of the network. 3090 func (t *TradingDataServiceV2) GetNetworkData(ctx context.Context, _ *v2.GetNetworkDataRequest) (*v2.GetNetworkDataResponse, error) { 3091 defer metrics.StartAPIRequestAndTimeGRPC("GetNetworkDataV2")() 3092 3093 epoch, err := t.EpochService.GetCurrent(ctx) 3094 if err != nil { 3095 return nil, formatE(ErrGetEpoch, err) 3096 } 3097 3098 // get the node-y bits 3099 networkData, err := t.nodeService.GetNodeData(ctx, uint64(epoch.ID)) 3100 if err != nil { 3101 return nil, formatE(ErrNodeServiceGetNodeData, errors.Wrapf(err, "epochID: %d", epoch.ID)) 3102 } 3103 3104 // now use network parameters to calculate the maximum nodes allowed in each nodeSet 3105 key := "network.validators.tendermint.number" 3106 np, err := t.networkParameterService.GetByKey(ctx, key) 3107 if err != nil { 3108 return nil, formatE(ErrGetNetworkParameters, errors.Wrapf(err, "key: %s", key)) 3109 } 3110 3111 maxTendermint, err := strconv.ParseUint(np.Value, 10, 32) 3112 if err != nil { 3113 return nil, formatE(ErrGetNetworkParameters, errors.Wrapf(err, "value: %s", np.Value)) 3114 } 3115 3116 key = "network.validators.ersatz.multipleOfTendermintValidators" 3117 np, err = t.networkParameterService.GetByKey(ctx, key) 3118 if err != nil { 3119 return nil, formatE(ErrGetNetworkParameters, errors.Wrapf(err, "key: %s", key)) 3120 } 3121 3122 ersatzFactor, err := strconv.ParseFloat(np.Value, 32) 3123 if err != nil { 3124 return nil, formatE(ErrGetNetworkParameters, errors.Wrapf(err, "value: %s", np.Value)) 3125 } 3126 3127 data := networkData.ToProto() 3128 data.TendermintNodes.Maximum = ptr.From(uint32(maxTendermint)) 3129 data.ErsatzNodes.Maximum = ptr.From(uint32(float64(maxTendermint) * ersatzFactor)) 3130 3131 return &v2.GetNetworkDataResponse{ 3132 NodeData: data, 3133 }, nil 3134 } 3135 3136 // GetNode retrieves information about a given node. 3137 func (t *TradingDataServiceV2) GetNode(ctx context.Context, req *v2.GetNodeRequest) (*v2.GetNodeResponse, error) { 3138 defer metrics.StartAPIRequestAndTimeGRPC("GetNodeV2")() 3139 3140 if len(req.Id) == 0 { 3141 return nil, formatE(ErrMissingNodeID) 3142 } 3143 3144 epoch, err := t.EpochService.GetCurrent(ctx) 3145 if err != nil { 3146 return nil, formatE(ErrGetEpoch, err) 3147 } 3148 3149 node, err := t.nodeService.GetNodeByID(ctx, req.Id, uint64(epoch.ID)) 3150 if err != nil { 3151 return nil, formatE(err) 3152 } 3153 3154 return &v2.GetNodeResponse{ 3155 Node: node.ToProto(), 3156 }, nil 3157 } 3158 3159 // ListNodes returns information about the nodes on the network. 3160 func (t *TradingDataServiceV2) ListNodes(ctx context.Context, req *v2.ListNodesRequest) (*v2.ListNodesResponse, error) { 3161 defer metrics.StartAPIRequestAndTimeGRPC("ListNodesV2")() 3162 3163 var ( 3164 epoch entities.Epoch 3165 err error 3166 ) 3167 if req.EpochSeq == nil || *req.EpochSeq > math.MaxInt64 { 3168 epoch, err = t.EpochService.GetCurrent(ctx) 3169 } else { 3170 epoch, err = t.EpochService.Get(ctx, *req.EpochSeq) 3171 } 3172 if err != nil { 3173 return nil, formatE(ErrGetEpoch, err) 3174 } 3175 3176 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 3177 if err != nil { 3178 return nil, formatE(ErrInvalidPagination, err) 3179 } 3180 3181 nodes, pageInfo, err := t.nodeService.GetNodes(ctx, uint64(epoch.ID), pagination) 3182 if err != nil { 3183 return nil, formatE(ErrNodeServiceGetNodes, err) 3184 } 3185 3186 edges, err := makeEdges[*v2.NodeEdge](nodes) 3187 if err != nil { 3188 return nil, formatE(err) 3189 } 3190 3191 nodesConnection := &v2.NodesConnection{ 3192 Edges: edges, 3193 PageInfo: pageInfo.ToProto(), 3194 } 3195 3196 return &v2.ListNodesResponse{ 3197 Nodes: nodesConnection, 3198 }, nil 3199 } 3200 3201 // ListNodeSignatures returns the signatures for a given node. 3202 func (t *TradingDataServiceV2) ListNodeSignatures(ctx context.Context, req *v2.ListNodeSignaturesRequest) (*v2.ListNodeSignaturesResponse, error) { 3203 defer metrics.StartAPIRequestAndTimeGRPC("ListNodeSignatures")() 3204 3205 if len(req.Id) == 0 { 3206 return nil, formatE(ErrMissingResourceID) 3207 } 3208 3209 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 3210 if err != nil { 3211 return nil, formatE(ErrInvalidPagination, err) 3212 } 3213 3214 sigs, pageInfo, err := t.notaryService.GetByResourceID(ctx, req.Id, pagination) 3215 if err != nil { 3216 return nil, formatE(ErrNotaryServiceGetByResourceID, errors.Wrapf(err, "resourceID: %s", req.Id)) 3217 } 3218 3219 edges, err := makeEdges[*v2.NodeSignatureEdge](sigs) 3220 if err != nil { 3221 return nil, formatE(err) 3222 } 3223 3224 nodeSignatureConnection := &v2.NodeSignaturesConnection{ 3225 Edges: edges, 3226 PageInfo: pageInfo.ToProto(), 3227 } 3228 3229 return &v2.ListNodeSignaturesResponse{ 3230 Signatures: nodeSignatureConnection, 3231 }, nil 3232 } 3233 3234 // GetEpoch retrieves data for a specific epoch, if id omitted it gets the current epoch. 3235 func (t *TradingDataServiceV2) GetEpoch(ctx context.Context, req *v2.GetEpochRequest) (*v2.GetEpochResponse, error) { 3236 defer metrics.StartAPIRequestAndTimeGRPC("GetEpochV2")() 3237 3238 var ( 3239 epoch entities.Epoch 3240 err error 3241 ) 3242 if req.GetId() > 0 { 3243 epoch, err = t.EpochService.Get(ctx, req.GetId()) 3244 } else if req.GetBlock() > 0 { 3245 epoch, err = t.EpochService.GetByBlock(ctx, req.GetBlock()) 3246 } else { 3247 epoch, err = t.EpochService.GetCurrent(ctx) 3248 } 3249 if err != nil { 3250 return nil, formatE(ErrGetEpoch, err) 3251 } 3252 3253 delegations, _, err := t.delegationService.Get(ctx, nil, nil, &epoch.ID, nil) 3254 if err != nil { 3255 return nil, formatE(ErrDelegationServiceGet, err) 3256 } 3257 3258 protoEpoch := epoch.ToProto() 3259 protoEpoch.Delegations = make([]*vega.Delegation, len(delegations)) 3260 for i, delegation := range delegations { 3261 protoEpoch.Delegations[i] = delegation.ToProto() 3262 } 3263 3264 nodes, _, err := t.nodeService.GetNodes(ctx, uint64(epoch.ID), entities.CursorPagination{}) 3265 if err != nil { 3266 return nil, formatE(ErrNodeServiceGetNodes, errors.Wrapf(err, "epochID: %d", epoch.ID)) 3267 } 3268 3269 protoEpoch.Validators = make([]*vega.Node, len(nodes)) 3270 for i, node := range nodes { 3271 protoEpoch.Validators[i] = node.ToProto() 3272 } 3273 3274 return &v2.GetEpochResponse{ 3275 Epoch: protoEpoch, 3276 }, nil 3277 } 3278 3279 // EstimateFee estimates the fee for a given market, price and size. 3280 func (t *TradingDataServiceV2) EstimateFee(ctx context.Context, req *v2.EstimateFeeRequest) (*v2.EstimateFeeResponse, error) { 3281 defer metrics.StartAPIRequestAndTimeGRPC("EstimateFee SQL")() 3282 3283 if len(req.MarketId) == 0 { 3284 return nil, formatE(ErrEmptyMissingMarketID) 3285 } 3286 3287 if !crypto.IsValidVegaID(req.MarketId) { 3288 return nil, formatE(ErrInvalidMarketID) 3289 } 3290 3291 if len(req.Price) == 0 { 3292 return nil, formatE(ErrMissingPrice) 3293 } 3294 3295 fee, err := t.estimateFee(ctx, req.MarketId, req.Price, req.Size, req.Party) 3296 if err != nil { 3297 return nil, formatE(ErrEstimateFee, err) 3298 } 3299 3300 return &v2.EstimateFeeResponse{ 3301 Fee: fee, 3302 }, nil 3303 } 3304 3305 func (t *TradingDataServiceV2) scaleFromMarketToAssetPrice( 3306 ctx context.Context, 3307 mkt entities.Market, 3308 price *num.Uint, 3309 ) (*num.Uint, error) { 3310 priceFactor, err := t.getMarketPriceFactor(ctx, mkt) 3311 if err != nil { 3312 return nil, err 3313 } 3314 price, _ = num.UintFromDecimal(price.ToDecimal().Mul(priceFactor)) 3315 return price, nil 3316 } 3317 3318 func (t *TradingDataServiceV2) scaleDecimalFromMarketToAssetPrice( 3319 price, priceFactor num.Decimal, 3320 ) num.Decimal { 3321 return price.Mul(priceFactor) 3322 } 3323 3324 func (t *TradingDataServiceV2) scaleDecimalFromAssetToMarketPrice( 3325 price, priceFactor num.Decimal, 3326 ) num.Decimal { 3327 return price.Div(priceFactor) 3328 } 3329 3330 func (t *TradingDataServiceV2) getMarketAsset(ctx context.Context, mkt entities.Market) (entities.Asset, error) { 3331 var asset entities.Asset 3332 assetID, err := mkt.ToProto().GetAsset() 3333 if err != nil { 3334 return asset, errors.Wrap(err, "getting asset from market") 3335 } 3336 3337 asset, err = t.AssetService.GetByID(ctx, assetID) 3338 if err != nil { 3339 return asset, errors.Wrapf(ErrAssetServiceGetByID, "assetID: %s", assetID) 3340 } 3341 3342 return asset, nil 3343 } 3344 3345 func (t *TradingDataServiceV2) getMarketPriceFactor( 3346 ctx context.Context, 3347 mkt entities.Market, 3348 ) (num.Decimal, error) { 3349 assetID, err := mkt.ToProto().GetAsset() 3350 if err != nil { 3351 return num.DecimalZero(), errors.Wrap(err, "getting asset from market") 3352 } 3353 3354 asset, err := t.AssetService.GetByID(ctx, assetID) 3355 if err != nil { 3356 return num.DecimalZero(), errors.Wrapf(ErrAssetServiceGetByID, "assetID: %s", assetID) 3357 } 3358 3359 // scale the price if needed 3360 // price is expected in market decimal 3361 priceFactor := num.DecimalOne() 3362 // this could be negative, use decimal 3363 if exp := asset.Decimals - mkt.DecimalPlaces; exp != 0 { 3364 priceFactor = num.DecimalFromInt64(10).Pow(num.DecimalFromInt64(int64(exp))) 3365 } 3366 return priceFactor, nil 3367 } 3368 3369 func (t *TradingDataServiceV2) getMarketFactors( 3370 ctx context.Context, 3371 market entities.Market, 3372 asset entities.Asset, 3373 ) ( 3374 rf entities.RiskFactor, 3375 initialMargin, 3376 slippage, 3377 priceFactor, 3378 positionFactor num.Decimal, 3379 err error, 3380 ) { 3381 if market.TradableInstrument.MarginCalculator == nil || 3382 market.TradableInstrument.MarginCalculator.ScalingFactors == nil || 3383 market.LinearSlippageFactor == nil { 3384 err = ErrEstimateAMMBounds 3385 return 3386 } 3387 3388 initialMargin = num.DecimalFromFloat(market.TradableInstrument.MarginCalculator.ScalingFactors.InitialMargin) 3389 slippage = *market.LinearSlippageFactor 3390 3391 priceFactor = num.DecimalOne() 3392 // this could be negative, use decimal 3393 if exp := asset.Decimals - market.DecimalPlaces; exp != 0 { 3394 priceFactor = num.DecimalFromInt64(10).Pow(num.DecimalFromInt64(int64(exp))) 3395 } 3396 3397 positionFactor = num.DecimalFromInt64(10). 3398 Pow(num.DecimalFromInt64(int64(market.PositionDecimalPlaces))) 3399 3400 rf, err = t.RiskFactorService.GetMarketRiskFactors(ctx, market.ID.String()) 3401 3402 return 3403 } 3404 3405 func (t *TradingDataServiceV2) estimateFee( 3406 ctx context.Context, 3407 market, priceS string, 3408 size uint64, 3409 party *string, 3410 ) (*vega.Fee, error) { 3411 mkt, err := t.MarketsService.GetByID(ctx, market) 3412 if err != nil { 3413 return nil, err 3414 } 3415 3416 price, overflowed := num.UintFromString(priceS, 10) 3417 if overflowed { 3418 return nil, ErrInvalidOrderPrice 3419 } 3420 3421 if price.IsNegative() || price.IsZero() { 3422 return nil, ErrInvalidOrderPrice 3423 } 3424 3425 if size <= 0 { 3426 return nil, ErrInvalidOrderSize 3427 } 3428 3429 price, err = t.scaleFromMarketToAssetPrice(ctx, mkt, price) 3430 if err != nil { 3431 return nil, errors.Wrap(ErrScalingPriceFromMarketToAsset, err.Error()) 3432 } 3433 3434 mdpd := num.DecimalFromFloat(10). 3435 Pow(num.DecimalFromInt64(int64(mkt.PositionDecimalPlaces))) 3436 3437 base := num.DecimalFromUint(price.Mul(price, num.NewUint(size))).Div(mdpd) 3438 maker, infra, liquidity, bb, treasury, err := t.feeFactors(mkt) 3439 if err != nil { 3440 return nil, errors.Wrap(err, "getting fee factors") 3441 } 3442 3443 mf, _ := num.UintFromDecimal(base.Mul(num.NewDecimalFromFloat(maker)).Ceil()) 3444 inf, _ := num.UintFromDecimal(base.Mul(num.NewDecimalFromFloat(infra)).Ceil()) 3445 lf, _ := num.UintFromDecimal(base.Mul(num.NewDecimalFromFloat(liquidity)).Ceil()) 3446 treasuryFee, _ := num.UintFromDecimal(base.Mul(num.NewDecimalFromFloat(treasury)).Ceil()) 3447 buyBackFee, _ := num.UintFromDecimal(base.Mul(num.NewDecimalFromFloat(bb)).Ceil()) 3448 3449 fees := &vega.Fee{ 3450 MakerFee: mf.String(), 3451 InfrastructureFee: inf.String(), 3452 LiquidityFee: lf.String(), 3453 BuyBackFee: buyBackFee.String(), 3454 TreasuryFee: treasuryFee.String(), 3455 } 3456 3457 var referee *entities.PartyID 3458 referrerDiscount := types.EmptyFactors 3459 volumeDiscountFactors := types.EmptyFactors 3460 if party != nil { 3461 epoch, err := t.EpochService.GetCurrent(ctx) 3462 if err != nil { 3463 return nil, formatE(ErrGetEpoch, err) 3464 } 3465 // NB: we need to use the previous epoch because the stats is published at the end of the epoch so we won't have 3466 // information for the current epoch only the previous one. 3467 atEpoch := uint64(epoch.ID) - 1 3468 referee = ptr.From(entities.PartyID(*party)) 3469 stats, _, err := t.ReferralSetsService.GetReferralSetStats(ctx, nil, &atEpoch, referee, entities.DefaultCursorPagination(true)) 3470 if err == nil && len(stats) > 0 { 3471 referrerDiscount = types.FactorsFromDiscountFactorsWithDefault(stats[0].DiscountFactors, "0") 3472 } 3473 vdStats, _, err := t.VolumeDiscountStatsService.Stats(ctx, &atEpoch, party, entities.DefaultCursorPagination(true)) 3474 if err == nil && len(vdStats) > 0 { 3475 volumeDiscountFactors = types.FactorsFromDiscountFactorsWithDefault(vdStats[0].DiscountFactors, "0") 3476 } 3477 referralMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(referrerDiscount.Maker).Floor()) 3478 referralInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(referrerDiscount.Infra).Floor()) 3479 referralLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(referrerDiscount.Liquidity).Floor()) 3480 3481 fees.MakerFeeReferrerDiscount = referralMakerDiscount.String() 3482 fees.InfrastructureFeeReferrerDiscount = referralInfDiscount.String() 3483 fees.LiquidityFeeReferrerDiscount = referralLfDiscount.String() 3484 3485 // apply referral discounts 3486 mf = mf.Sub(mf, referralMakerDiscount) 3487 inf = inf.Sub(inf, referralInfDiscount) 3488 lf = lf.Sub(lf, referralLfDiscount) 3489 3490 volumeMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(volumeDiscountFactors.Maker).Floor()) 3491 volumeInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(volumeDiscountFactors.Infra).Floor()) 3492 volumeLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(volumeDiscountFactors.Liquidity).Floor()) 3493 3494 fees.MakerFeeVolumeDiscount = volumeMakerDiscount.String() 3495 fees.InfrastructureFeeVolumeDiscount = volumeInfDiscount.String() 3496 fees.LiquidityFeeVolumeDiscount = volumeLfDiscount.String() 3497 3498 mf = mf.Sub(mf, volumeMakerDiscount) 3499 inf = inf.Sub(inf, volumeInfDiscount) 3500 lf = lf.Sub(lf, volumeLfDiscount) 3501 3502 fees.MakerFee = mf.String() 3503 fees.InfrastructureFee = inf.String() 3504 fees.LiquidityFee = lf.String() 3505 } 3506 3507 return fees, nil 3508 } 3509 3510 func (t *TradingDataServiceV2) feeFactors(mkt entities.Market) (maker, infra, liquidity, bb, treaury float64, err error) { 3511 if maker, err = strconv.ParseFloat(mkt.Fees.Factors.MakerFee, 64); err != nil { 3512 return 3513 } 3514 if infra, err = strconv.ParseFloat(mkt.Fees.Factors.InfrastructureFee, 64); err != nil { 3515 return 3516 } 3517 if liquidity, err = strconv.ParseFloat(mkt.Fees.Factors.LiquidityFee, 64); err != nil { 3518 return 3519 } 3520 if bb, err = strconv.ParseFloat(mkt.Fees.Factors.BuyBackFee, 64); err != nil { 3521 return 3522 } 3523 if treaury, err = strconv.ParseFloat(mkt.Fees.Factors.TreasuryFee, 64); err != nil { 3524 return 3525 } 3526 return 3527 } 3528 3529 // EstimateMargin estimates the margin required for a given order. 3530 func (t *TradingDataServiceV2) EstimateMargin(ctx context.Context, req *v2.EstimateMarginRequest) (*v2.EstimateMarginResponse, error) { 3531 defer metrics.StartAPIRequestAndTimeGRPC("EstimateMargin SQL")() 3532 3533 margin, err := t.estimateMargin( 3534 ctx, req.Side, req.Type, req.MarketId, req.PartyId, req.Price, req.Size) 3535 if err != nil { 3536 return nil, formatE(ErrEstimateMargin, err) 3537 } 3538 3539 return &v2.EstimateMarginResponse{ 3540 MarginLevels: margin, 3541 }, nil 3542 } 3543 3544 func (t *TradingDataServiceV2) estimateMargin( 3545 ctx context.Context, 3546 rSide vega.Side, 3547 rType vega.Order_Type, 3548 rMarket, rParty, rPrice string, 3549 rSize uint64, 3550 ) (*vega.MarginLevels, error) { 3551 if rSide == vega.Side_SIDE_UNSPECIFIED { 3552 return nil, ErrInvalidOrderSide 3553 } 3554 3555 // first get the risk factors and market data (marketdata->markprice) 3556 rf, err := t.RiskFactorService.GetMarketRiskFactors(ctx, rMarket) 3557 if err != nil { 3558 return nil, err 3559 } 3560 3561 mkt, err := t.MarketsService.GetByID(ctx, rMarket) 3562 if err != nil { 3563 return nil, err 3564 } 3565 3566 mktData, err := t.MarketDataService.GetMarketDataByID(ctx, rMarket) 3567 if err != nil { 3568 return nil, err 3569 } 3570 3571 f, err := num.DecimalFromString(rf.Short.String()) 3572 if err != nil { 3573 return nil, errors.Wrapf(err, "parsing risk factor short: %s", rf.Short.String()) 3574 } 3575 if rSide == vega.Side_SIDE_BUY { 3576 f, err = num.DecimalFromString(rf.Long.String()) 3577 if err != nil { 3578 return nil, errors.Wrapf(err, "parsing risk factor long: %s", rf.Long.String()) 3579 } 3580 } 3581 3582 mktProto := mkt.ToProto() 3583 3584 asset, err := mktProto.GetAsset() 3585 if err != nil { 3586 return nil, errors.Wrap(err, "getting asset from market") 3587 } 3588 3589 // now calculate margin maintenance 3590 priceD, err := num.DecimalFromString(mktData.MarkPrice.String()) 3591 if err != nil { 3592 return nil, errors.Wrapf(err, "parsing mark price: %s", mktData.MarkPrice.String()) 3593 } 3594 3595 // if the order is a limit order, use the limit price to calculate the margin maintenance 3596 if rType == vega.Order_TYPE_LIMIT { 3597 priceD, err = num.DecimalFromString(rPrice) 3598 if err != nil { 3599 return nil, errors.Wrapf(err, "parsing limit price: %s", rPrice) 3600 } 3601 } 3602 3603 price, _ := num.UintFromDecimal(priceD) 3604 if price.IsNegative() || price.IsZero() { 3605 return nil, ErrInvalidOrderPrice 3606 } 3607 price, err = t.scaleFromMarketToAssetPrice(ctx, mkt, price) 3608 if err != nil { 3609 return nil, errors.Wrap(ErrScalingPriceFromMarketToAsset, err.Error()) 3610 } 3611 3612 if rSize <= 0 { 3613 return nil, ErrInvalidOrderSize 3614 } 3615 3616 priceD = price.ToDecimal() 3617 3618 mdpd := num.DecimalFromFloat(10). 3619 Pow(num.DecimalFromInt64(int64(mkt.PositionDecimalPlaces))) 3620 3621 maintenanceMargin := num.DecimalFromFloat(float64(rSize)). 3622 Mul(f).Mul(priceD).Div(mdpd) 3623 3624 return implyMarginLevels(maintenanceMargin, num.DecimalZero(), num.DecimalZero(), mkt.TradableInstrument.MarginCalculator.ScalingFactors, rParty, rMarket, asset, false), nil 3625 } 3626 3627 func implyMarginLevels(maintenanceMargin, orderMargin, marginFactor num.Decimal, scalingFactors *vega.ScalingFactors, partyId, marketId, asset string, isolatedMarginMode bool) *vega.MarginLevels { 3628 marginLevels := &vega.MarginLevels{ 3629 PartyId: partyId, 3630 MarketId: marketId, 3631 Asset: asset, 3632 Timestamp: 0, 3633 MaintenanceMargin: maintenanceMargin.Round(0).String(), 3634 SearchLevel: maintenanceMargin.Mul(num.DecimalFromFloat(scalingFactors.SearchLevel)).Round(0).String(), 3635 InitialMargin: maintenanceMargin.Mul(num.DecimalFromFloat(scalingFactors.InitialMargin)).Round(0).String(), 3636 CollateralReleaseLevel: maintenanceMargin.Mul(num.DecimalFromFloat(scalingFactors.CollateralRelease)).Round(0).String(), 3637 MarginMode: types.MarginModeCrossMargin, 3638 } 3639 if isolatedMarginMode { 3640 marginLevels.OrderMargin = orderMargin.Round(0).String() 3641 marginLevels.SearchLevel = "0" 3642 marginLevels.CollateralReleaseLevel = "0" 3643 marginLevels.MarginMode = types.MarginModeIsolatedMargin 3644 marginLevels.MarginFactor = marginFactor.String() 3645 } 3646 return marginLevels 3647 } 3648 3649 func (t *TradingDataServiceV2) EstimatePosition(ctx context.Context, req *v2.EstimatePositionRequest) (*v2.EstimatePositionResponse, error) { 3650 defer metrics.StartAPIRequestAndTimeGRPC("EstimatePosition")() 3651 3652 if req.MarketId == "" { 3653 return nil, ErrEmptyMissingMarketID 3654 } 3655 3656 marginAccountBalance, err := num.DecimalFromString(req.MarginAccountBalance) 3657 if err != nil { 3658 return nil, formatE(ErrPositionsInvalidAccountBalance, err) 3659 } 3660 orderAccountBalance, err := num.DecimalFromString(req.OrderMarginAccountBalance) 3661 if err != nil { 3662 return nil, formatE(ErrPositionsInvalidAccountBalance, err) 3663 } 3664 mkt, err := t.MarketsService.GetByID(ctx, req.MarketId) 3665 if err != nil { 3666 return nil, formatE(ErrMarketServiceGetByID, err) 3667 } 3668 3669 collateralAvailable := marginAccountBalance 3670 crossMarginMode := req.MarginMode == types.MarginModeCrossMargin 3671 if crossMarginMode { 3672 generalAccountBalance, err := num.DecimalFromString(req.GeneralAccountBalance) 3673 if err != nil { 3674 return nil, formatE(ErrPositionsInvalidAccountBalance, err) 3675 } 3676 collateralAvailable = collateralAvailable.Add(generalAccountBalance).Add(orderAccountBalance) 3677 } 3678 3679 dMarginFactor := num.DecimalZero() 3680 isolatedMarginMode := req.MarginMode == types.MarginModeIsolatedMargin 3681 if isolatedMarginMode { 3682 if req.MarginFactor == nil { 3683 return nil, formatE(ErrMissingMarginFactor, errors.New("margin factor are required with isolated margin")) 3684 } 3685 dMarginFactor, err = num.DecimalFromString(*req.MarginFactor) 3686 if err != nil { 3687 return nil, formatE(ErrMissingMarginFactor, err) 3688 } 3689 } 3690 3691 priceFactor, err := t.getMarketPriceFactor(ctx, mkt) 3692 if err != nil { 3693 return nil, err 3694 } 3695 3696 dPriceFactor := priceFactor 3697 3698 var mdpCap num.Decimal 3699 cap, hasCap := mkt.HasCap() 3700 if hasCap { 3701 mdpCap, err = num.DecimalFromString(cap.MaxPrice) 3702 if err != nil { 3703 formatE(ErrMarketServiceGetByID, err) 3704 } 3705 } 3706 3707 buyOrders := make([]*risk.OrderInfo, 0, len(req.Orders)) 3708 sellOrders := make([]*risk.OrderInfo, 0, len(req.Orders)) 3709 3710 for _, o := range req.Orders { 3711 if o == nil { 3712 continue 3713 } 3714 var price num.Decimal 3715 p, err := num.DecimalFromString(o.Price) 3716 if err != nil { 3717 return nil, formatE(ErrInvalidOrderPrice, err) 3718 } 3719 3720 if p.IsNegative() || !p.IsInteger() { 3721 return nil, ErrInvalidOrderPrice 3722 } 3723 3724 if hasCap && p.GreaterThanOrEqual(mdpCap) { 3725 return nil, formatE(ErrInvalidOrderPrice, errors.New("outside of market-cap range")) 3726 } 3727 3728 price = t.scaleDecimalFromMarketToAssetPrice(p, dPriceFactor) 3729 3730 switch o.Side { 3731 case types.SideBuy: 3732 buyOrders = append(buyOrders, &risk.OrderInfo{TrueRemaining: o.Remaining, Price: price, IsMarketOrder: o.IsMarketOrder}) 3733 case types.SideSell: 3734 sellOrders = append(sellOrders, &risk.OrderInfo{TrueRemaining: o.Remaining, Price: price, IsMarketOrder: o.IsMarketOrder}) 3735 default: 3736 return nil, ErrInvalidOrderSide 3737 } 3738 } 3739 3740 rf, err := t.RiskFactorService.GetMarketRiskFactors(ctx, req.MarketId) 3741 if err != nil { 3742 return nil, formatE(ErrRiskFactorServiceGet, err) 3743 } 3744 3745 mktData, err := t.MarketDataService.GetMarketDataByID(ctx, req.MarketId) 3746 if err != nil { 3747 return nil, formatE(ErrMarketServiceGetMarketData, err) 3748 } 3749 3750 mktProto := mkt.ToProto() 3751 3752 asset, err := mktProto.GetAsset() 3753 if err != nil { 3754 return nil, formatE(err) 3755 } 3756 3757 marketObservable := t.scaleDecimalFromMarketToAssetPrice(mktData.MarkPrice, dPriceFactor) 3758 auctionPrice := t.scaleDecimalFromMarketToAssetPrice(mktData.IndicativePrice, dPriceFactor) 3759 3760 auction := mktData.AuctionEnd > 0 3761 if auction && mktData.MarketTradingMode == types.MarketTradingModeOpeningAuction.String() { 3762 marketObservable = auctionPrice 3763 } 3764 3765 positionFactor := num.DecimalFromFloat(10). 3766 Pow(num.DecimalFromInt64(int64(mkt.PositionDecimalPlaces))) 3767 3768 linearSlippageFactor, err := num.DecimalFromString(mktProto.LinearSlippageFactor) 3769 if err != nil { 3770 return nil, formatE(fmt.Errorf("can't parse linear slippage factor: %s", mktProto.LinearSlippageFactor), err) 3771 } 3772 quadraticSlippageFactor, err := num.DecimalFromString(mktProto.QuadraticSlippageFactor) 3773 if err != nil { 3774 return nil, formatE(fmt.Errorf("can't parse quadratic slippage factor: %s", mktProto.QuadraticSlippageFactor), err) 3775 } 3776 3777 avgEntryPrice, err := num.DecimalFromString(req.AverageEntryPrice) 3778 if err != nil { 3779 return nil, formatE(fmt.Errorf("can't parse average entry price: %s", req.AverageEntryPrice), err) 3780 } 3781 3782 avgEntryPrice = t.scaleDecimalFromMarketToAssetPrice(avgEntryPrice, dPriceFactor) 3783 3784 marginFactorScaledFundingPaymentPerUnitPosition := num.DecimalZero() 3785 3786 if perp := mktProto.TradableInstrument.Instrument.GetPerpetual(); perp != nil { 3787 factor, err := num.DecimalFromString(perp.MarginFundingFactor) 3788 if err != nil { 3789 return nil, formatE(fmt.Errorf("can't parse margin funding factor: %s", perp.MarginFundingFactor), err) 3790 } 3791 if !factor.IsZero() && mktData.ProductData != nil { // for perps it's possible that the product data is not available in the market data 3792 if perpData := mktData.ProductData.GetPerpetualData(); perpData != nil { 3793 fundingPayment, err := num.DecimalFromString(perpData.FundingPayment) 3794 if err != nil { 3795 return nil, formatE(fmt.Errorf("can't parse funding payment from perpetual product data: %s", perpData.FundingPayment), err) 3796 } 3797 fundingRate, err := num.DecimalFromString(perpData.FundingRate) 3798 if err != nil { 3799 return nil, formatE(fmt.Errorf("can't parse funding rate from perpetual product data: %s", perpData.FundingRate), err) 3800 } 3801 if !fundingPayment.IsZero() && !fundingRate.IsZero() { 3802 marginFactorScaledFundingPaymentPerUnitPosition = factor.Mul(fundingPayment) 3803 } 3804 } 3805 } 3806 } 3807 3808 wMaintenance, bMaintenance, orderMargin := t.computeMarginRange( 3809 req.OpenVolume, 3810 buyOrders, 3811 sellOrders, 3812 marketObservable, 3813 positionFactor, 3814 linearSlippageFactor, 3815 quadraticSlippageFactor, 3816 rf, 3817 marginFactorScaledFundingPaymentPerUnitPosition, 3818 auction, 3819 req.MarginMode, 3820 dMarginFactor, 3821 auctionPrice, 3822 cap, 3823 avgEntryPrice, 3824 dPriceFactor, 3825 ) 3826 marginEstimate := &v2.MarginEstimate{ 3827 WorstCase: implyMarginLevels(wMaintenance, orderMargin, dMarginFactor, mkt.TradableInstrument.MarginCalculator.ScalingFactors, "", req.MarketId, asset, isolatedMarginMode), 3828 BestCase: implyMarginLevels(bMaintenance, orderMargin, dMarginFactor, mkt.TradableInstrument.MarginCalculator.ScalingFactors, "", req.MarketId, asset, isolatedMarginMode), 3829 } 3830 3831 var wMarginDelta, bMarginDelta, posMarginDelta num.Decimal 3832 combinedMargin := marginAccountBalance.Add(orderAccountBalance) 3833 if isolatedMarginMode { 3834 var ap *num.Uint = nil 3835 if !auctionPrice.IsZero() { 3836 ap, _ = num.UintFromDecimal(auctionPrice) 3837 } 3838 requiredPositionMargin, requiredOrderMargin := risk.CalculateRequiredMarginInIsolatedMode(req.OpenVolume, avgEntryPrice, marketObservable, buyOrders, sellOrders, positionFactor, dMarginFactor, ap) 3839 posMarginDelta = requiredPositionMargin.Sub(marginAccountBalance) 3840 wMarginDelta = requiredPositionMargin.Add(requiredOrderMargin).Sub(combinedMargin) 3841 bMarginDelta = wMarginDelta 3842 } else { 3843 wInitial, _ := num.DecimalFromString(marginEstimate.WorstCase.InitialMargin) 3844 bInitial, _ := num.DecimalFromString(marginEstimate.BestCase.InitialMargin) 3845 wRelease, _ := num.DecimalFromString(marginEstimate.WorstCase.CollateralReleaseLevel) 3846 bRelease, _ := num.DecimalFromString(marginEstimate.BestCase.CollateralReleaseLevel) 3847 wMarginDifference := wInitial.Sub(combinedMargin) 3848 bMarginDifference := bInitial.Sub(combinedMargin) 3849 if wMarginDifference.IsPositive() || combinedMargin.GreaterThan(wRelease) { 3850 wMarginDelta = wMarginDifference 3851 } 3852 if bMarginDifference.IsPositive() || combinedMargin.GreaterThan(bRelease) { 3853 bMarginDelta = bMarginDifference 3854 } 3855 } 3856 3857 if isolatedMarginMode && ptr.UnBox(req.IncludeRequiredPositionMarginInAvailableCollateral) { 3858 collateralAvailable = collateralAvailable.Add(posMarginDelta) 3859 } 3860 3861 bPositionOnly, bWithBuy, bWithSell := marketObservable, marketObservable, marketObservable 3862 wPositionOnly, wWithBuy, wWithSell := marketObservable, marketObservable, marketObservable 3863 3864 // only calculate liquidation price if collateral available is above maintenance margin, otherwise return current market observable to signify that the theoretical position would be liquidated instantenously 3865 if collateralAvailable.GreaterThanOrEqual(bMaintenance) { 3866 bPositionOnly, bWithBuy, bWithSell, err = risk.CalculateLiquidationPriceWithSlippageFactors(req.OpenVolume, buyOrders, sellOrders, marketObservable, collateralAvailable, positionFactor, num.DecimalZero(), num.DecimalZero(), rf.Long, rf.Short, marginFactorScaledFundingPaymentPerUnitPosition, isolatedMarginMode, dMarginFactor) 3867 if err != nil { 3868 return nil, err 3869 } 3870 } 3871 if collateralAvailable.GreaterThanOrEqual(wMaintenance) { 3872 wPositionOnly, wWithBuy, wWithSell, err = risk.CalculateLiquidationPriceWithSlippageFactors(req.OpenVolume, buyOrders, sellOrders, marketObservable, collateralAvailable, positionFactor, linearSlippageFactor, quadraticSlippageFactor, rf.Long, rf.Short, marginFactorScaledFundingPaymentPerUnitPosition, isolatedMarginMode, dMarginFactor) 3873 if err != nil { 3874 return nil, err 3875 } 3876 } 3877 3878 if ptr.UnBox(req.ScaleLiquidationPriceToMarketDecimals) { 3879 bPositionOnly = t.scaleDecimalFromAssetToMarketPrice(bPositionOnly, dPriceFactor) 3880 bWithBuy = t.scaleDecimalFromAssetToMarketPrice(bWithBuy, dPriceFactor) 3881 bWithSell = t.scaleDecimalFromAssetToMarketPrice(bWithSell, dPriceFactor) 3882 wPositionOnly = t.scaleDecimalFromAssetToMarketPrice(wPositionOnly, dPriceFactor) 3883 wWithBuy = t.scaleDecimalFromAssetToMarketPrice(wWithBuy, dPriceFactor) 3884 wWithSell = t.scaleDecimalFromAssetToMarketPrice(wWithSell, dPriceFactor) 3885 } 3886 3887 f := func(volume int64) (worst, best decimal.Decimal) { 3888 // if party is long, then liquidation is 0 3889 if volume >= 0 { 3890 return num.DecimalZero(), num.DecimalZero() 3891 } 3892 3893 // if its short we use the size of capPrice 3894 return num.MustDecimalFromString(cap.MaxPrice), num.MustDecimalFromString(cap.MaxPrice) // can't fail coming from the DB 3895 } 3896 3897 // no worst or best case in this case, so just setting the same on boths 3898 if hasCap && cap.FullyCollateralised != nil && *cap.FullyCollateralised { 3899 // openVolume first 3900 wPositionOnly, bPositionOnly = f(req.OpenVolume) 3901 3902 // then including buyOrders 3903 incBuyOrders := req.OpenVolume 3904 for _, v := range buyOrders { 3905 incBuyOrders += int64(v.TrueRemaining) 3906 } 3907 wWithBuy, bWithBuy = f(incBuyOrders) 3908 3909 // then including sellOrders 3910 incSellOrders := req.OpenVolume 3911 for _, v := range sellOrders { 3912 incSellOrders += int64(v.TrueRemaining) 3913 } 3914 wWithSell, bWithSell = f(incSellOrders) 3915 } 3916 3917 liquidationEstimate := &v2.LiquidationEstimate{ 3918 WorstCase: &v2.LiquidationPrice{ 3919 OpenVolumeOnly: wPositionOnly.Round(0).String(), 3920 IncludingBuyOrders: wWithBuy.Round(0).String(), 3921 IncludingSellOrders: wWithSell.Round(0).String(), 3922 }, 3923 BestCase: &v2.LiquidationPrice{ 3924 OpenVolumeOnly: bPositionOnly.Round(0).String(), 3925 IncludingBuyOrders: bWithBuy.Round(0).String(), 3926 IncludingSellOrders: bWithSell.Round(0).String(), 3927 }, 3928 } 3929 3930 return &v2.EstimatePositionResponse{ 3931 Margin: marginEstimate, 3932 CollateralIncreaseEstimate: &v2.CollateralIncreaseEstimate{ 3933 WorstCase: wMarginDelta.Round(0).String(), 3934 BestCase: bMarginDelta.Round(0).String(), 3935 }, 3936 Liquidation: liquidationEstimate, 3937 }, nil 3938 } 3939 3940 func (t *TradingDataServiceV2) computeMarginRange( 3941 openVolume int64, 3942 buyOrders, sellOrders []*risk.OrderInfo, 3943 marketObservable, positionFactor, linearSlippageFactor, quadraticSlippageFactor num.Decimal, 3944 riskFactors entities.RiskFactor, 3945 fundingPaymentPerUnitPosition num.Decimal, 3946 auction bool, 3947 marginMode vega.MarginMode, 3948 marginFactor, auctionPrice num.Decimal, 3949 cap *vega.FutureCap, 3950 averageEntryPrice num.Decimal, 3951 priceFactor num.Decimal, 3952 ) (num.Decimal, num.Decimal, num.Decimal) { 3953 bOrders, sOrders := buyOrders, sellOrders 3954 orderMargin := num.DecimalZero() 3955 isolatedMarginMode := marginMode == vega.MarginMode_MARGIN_MODE_ISOLATED_MARGIN 3956 if isolatedMarginMode { 3957 bOrders = []*risk.OrderInfo{} 3958 bNonMarketOrders := []*risk.OrderInfo{} 3959 for _, o := range buyOrders { 3960 if o.IsMarketOrder { 3961 bOrders = append(bOrders, o) 3962 } else { 3963 bNonMarketOrders = append(bNonMarketOrders, o) 3964 } 3965 } 3966 sOrders = []*risk.OrderInfo{} 3967 3968 sNonMarketOrders := []*risk.OrderInfo{} 3969 for _, o := range sellOrders { 3970 if o.IsMarketOrder { 3971 sOrders = append(sOrders, o) 3972 } else { 3973 sNonMarketOrders = append(sNonMarketOrders, o) 3974 } 3975 } 3976 3977 // this is a special case for fully collateralised capped future markets 3978 if cap != nil && cap.FullyCollateralised != nil && *cap.FullyCollateralised { 3979 cappedPrice := t.scaleDecimalFromMarketToAssetPrice( 3980 num.MustDecimalFromString(cap.MaxPrice), 3981 priceFactor, 3982 ) 3983 3984 orderMargin = calcOrderMarginIsolatedModeCappedAndFullyCollateralised(bNonMarketOrders, sNonMarketOrders, cappedPrice) 3985 } else { 3986 orderMargin = risk.CalcOrderMarginIsolatedMode(openVolume, bNonMarketOrders, sNonMarketOrders, positionFactor, marginFactor, auctionPrice) 3987 } 3988 } 3989 3990 var worst, best num.Decimal 3991 // this is a special case for fully collateralised capped future markets 3992 if cap != nil && cap.FullyCollateralised != nil && *cap.FullyCollateralised { 3993 cappedPrice := t.scaleDecimalFromMarketToAssetPrice( 3994 num.MustDecimalFromString(cap.MaxPrice), 3995 priceFactor, 3996 ) 3997 3998 worst = calcPositionMarginCappedAndFullyCollateralised(bOrders, sOrders, cappedPrice, openVolume, averageEntryPrice) 3999 best = worst 4000 } else { 4001 worst = risk.CalculateMaintenanceMarginWithSlippageFactors(openVolume, bOrders, sOrders, marketObservable, positionFactor, linearSlippageFactor, quadraticSlippageFactor, riskFactors.Long, riskFactors.Short, fundingPaymentPerUnitPosition, auction, auctionPrice) 4002 best = risk.CalculateMaintenanceMarginWithSlippageFactors(openVolume, bOrders, sOrders, marketObservable, positionFactor, num.DecimalZero(), num.DecimalZero(), riskFactors.Long, riskFactors.Short, fundingPaymentPerUnitPosition, auction, auctionPrice) 4003 } 4004 4005 return worst, best, orderMargin 4006 } 4007 4008 func calcPositionMarginCappedAndFullyCollateralised( 4009 buyOrders []*risk.OrderInfo, 4010 sellOrders []*risk.OrderInfo, 4011 priceCap num.Decimal, 4012 openVolume int64, 4013 openVolumeAverageEntryPrice decimal.Decimal, 4014 ) decimal.Decimal { 4015 // get average entry price over all orders 4016 // and the final volume. 4017 // then if long: 4018 // - averageEntryPrice * positionSize 4019 // if short: 4020 // - (priceCap - averageEntryPrice) * positionSize 4021 4022 positionSize := openVolume 4023 totalVolume := openVolume 4024 ongoing := openVolumeAverageEntryPrice.Mul(num.DecimalFromInt64(openVolume)) 4025 for _, v := range buyOrders { 4026 price := v.Price 4027 if price.GreaterThan(priceCap) { 4028 price = priceCap 4029 } 4030 4031 size := int64(v.TrueRemaining) 4032 positionSize += size 4033 totalVolume += size 4034 4035 ongoing = ongoing.Add(v.Price.Mul(num.DecimalFromInt64(size))) 4036 } 4037 4038 for _, v := range sellOrders { 4039 price := v.Price 4040 if price.GreaterThan(priceCap) { 4041 price = priceCap 4042 } 4043 4044 size := int64(v.TrueRemaining) 4045 positionSize -= size // only thing changing here really 4046 totalVolume += size 4047 ongoing = ongoing.Add(v.Price.Mul(num.DecimalFromInt64(size))) 4048 } 4049 4050 // no volume, and we want to prevent division by 0 4051 if totalVolume == 0 { 4052 return num.DecimalZero() 4053 } 4054 averageEntryPrice := ongoing.Div(num.DecimalFromInt64(totalVolume)) 4055 4056 if positionSize < 0 { 4057 // short position 4058 positionSize = -positionSize 4059 return priceCap.Sub(averageEntryPrice).Mul(num.DecimalFromInt64(positionSize)) 4060 } 4061 4062 return averageEntryPrice.Mul(num.DecimalFromInt64(positionSize)) 4063 } 4064 4065 func calcOrderMarginIsolatedModeCappedAndFullyCollateralised( 4066 buyOrders []*risk.OrderInfo, 4067 sellOrders []*risk.OrderInfo, 4068 cappedPrice num.Decimal, 4069 ) decimal.Decimal { 4070 // long order margin: 4071 // - price * positionSize 4072 // short order marign: 4073 // - (cappedPrice - price) * positionSize 4074 4075 marginBuy, marginSell := num.DecimalZero(), num.DecimalZero() 4076 4077 for _, v := range buyOrders { 4078 price := v.Price 4079 if v.Price.GreaterThan(cappedPrice) { 4080 price = cappedPrice 4081 } 4082 4083 marginBuy.Add(price.Mul(num.DecimalFromInt64(int64(v.TrueRemaining)))) 4084 } 4085 4086 for _, v := range sellOrders { 4087 price := v.Price 4088 if v.Price.GreaterThan(cappedPrice) { 4089 price = cappedPrice 4090 } 4091 4092 price = cappedPrice.Sub(price) 4093 marginSell.Add(price.Mul(num.DecimalFromInt64(int64(v.TrueRemaining)))) 4094 } 4095 4096 if marginBuy.GreaterThan(marginSell) { 4097 return marginBuy 4098 } 4099 4100 return marginSell 4101 } 4102 4103 // ListNetworkParameters returns a list of network parameters. 4104 func (t *TradingDataServiceV2) ListNetworkParameters(ctx context.Context, req *v2.ListNetworkParametersRequest) (*v2.ListNetworkParametersResponse, error) { 4105 defer metrics.StartAPIRequestAndTimeGRPC("ListNetworkParametersV2")() 4106 4107 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4108 if err != nil { 4109 return nil, formatE(ErrInvalidPagination, err) 4110 } 4111 4112 nps, pageInfo, err := t.networkParameterService.GetAll(ctx, pagination) 4113 if err != nil { 4114 return nil, formatE(ErrGetNetworkParameters, err) 4115 } 4116 4117 edges, err := makeEdges[*v2.NetworkParameterEdge](nps) 4118 if err != nil { 4119 return nil, formatE(err) 4120 } 4121 4122 networkParametersConnection := &v2.NetworkParameterConnection{ 4123 Edges: edges, 4124 PageInfo: pageInfo.ToProto(), 4125 } 4126 4127 return &v2.ListNetworkParametersResponse{ 4128 NetworkParameters: networkParametersConnection, 4129 }, nil 4130 } 4131 4132 // GetNetworkParameter returns a network parameter by key. 4133 func (t *TradingDataServiceV2) GetNetworkParameter(ctx context.Context, req *v2.GetNetworkParameterRequest) (*v2.GetNetworkParameterResponse, error) { 4134 defer metrics.StartAPIRequestAndTimeGRPC("GetNetworkParameter")() 4135 4136 v, err := t.networkParameterService.GetByKey(ctx, req.Key) 4137 if err != nil { 4138 return nil, formatE(ErrGetNetworkParameters, err) 4139 } 4140 4141 if req.Key != v.Key { 4142 return nil, formatE(ErrNetworkParameterNotFound, errors.Wrapf(err, "network parameter: %s", req.Key)) 4143 } 4144 4145 return &v2.GetNetworkParameterResponse{ 4146 NetworkParameter: v.ToProto(), 4147 }, nil 4148 } 4149 4150 // ListCheckpoints returns a list of checkpoints. 4151 func (t *TradingDataServiceV2) ListCheckpoints(ctx context.Context, req *v2.ListCheckpointsRequest) (*v2.ListCheckpointsResponse, error) { 4152 defer metrics.StartAPIRequestAndTimeGRPC("NetworkParametersV2")() 4153 4154 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4155 if err != nil { 4156 return nil, formatE(ErrInvalidPagination, err) 4157 } 4158 4159 checkpoints, pageInfo, err := t.checkpointService.GetAll(ctx, pagination) 4160 if err != nil { 4161 return nil, formatE(ErrCheckpointServiceGet, err) 4162 } 4163 4164 edges, err := makeEdges[*v2.CheckpointEdge](checkpoints) 4165 if err != nil { 4166 return nil, formatE(err) 4167 } 4168 4169 checkpointsConnection := &v2.CheckpointsConnection{ 4170 Edges: edges, 4171 PageInfo: pageInfo.ToProto(), 4172 } 4173 4174 return &v2.ListCheckpointsResponse{ 4175 Checkpoints: checkpointsConnection, 4176 }, nil 4177 } 4178 4179 // GetStake returns the stake for a party and the linkings to that stake. 4180 func (t *TradingDataServiceV2) GetStake(ctx context.Context, req *v2.GetStakeRequest) (*v2.GetStakeResponse, error) { 4181 defer metrics.StartAPIRequestAndTimeGRPC("GetStake")() 4182 4183 if len(req.PartyId) == 0 { 4184 return nil, formatE(ErrMissingPartyID) 4185 } 4186 4187 if req.PartyId != networkPartyID && !crypto.IsValidVegaID(req.PartyId) { 4188 return nil, formatE(ErrInvalidPartyID) 4189 } 4190 4191 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4192 if err != nil { 4193 return nil, formatE(ErrInvalidPagination, err) 4194 } 4195 4196 stake, stakeLinkings, pageInfo, err := t.stakeLinkingService.GetStake(ctx, entities.PartyID(req.PartyId), pagination) 4197 if err != nil { 4198 return nil, formatE(ErrStakeLinkingServiceGet, err) 4199 } 4200 4201 edges, err := makeEdges[*v2.StakeLinkingEdge](stakeLinkings) 4202 if err != nil { 4203 return nil, formatE(err) 4204 } 4205 4206 stakesConnection := &v2.StakesConnection{ 4207 Edges: edges, 4208 PageInfo: pageInfo.ToProto(), 4209 } 4210 4211 return &v2.GetStakeResponse{ 4212 CurrentStakeAvailable: num.UintToString(stake), 4213 StakeLinkings: stakesConnection, 4214 }, nil 4215 } 4216 4217 // GetRiskFactors returns the risk factors for a given market. 4218 func (t *TradingDataServiceV2) GetRiskFactors(ctx context.Context, req *v2.GetRiskFactorsRequest) (*v2.GetRiskFactorsResponse, error) { 4219 defer metrics.StartAPIRequestAndTimeGRPC("GetRiskFactors SQL")() 4220 4221 if len(req.MarketId) == 0 { 4222 return nil, formatE(ErrEmptyMissingMarketID) 4223 } 4224 4225 if !crypto.IsValidVegaID(req.MarketId) { 4226 return nil, formatE(ErrInvalidMarketID) 4227 } 4228 4229 rfs, err := t.RiskFactorService.GetMarketRiskFactors(ctx, req.MarketId) 4230 if err != nil { 4231 return nil, formatE(ErrRiskFactorServiceGet, errors.Wrapf(err, "marketID: %s", req.MarketId)) 4232 } 4233 4234 return &v2.GetRiskFactorsResponse{ 4235 RiskFactor: rfs.ToProto(), 4236 }, nil 4237 } 4238 4239 // ObserveGovernance streams governance updates to the client. 4240 func (t *TradingDataServiceV2) ObserveGovernance(req *v2.ObserveGovernanceRequest, stream v2.TradingDataService_ObserveGovernanceServer) error { 4241 ctx, cfunc := context.WithCancel(stream.Context()) 4242 defer cfunc() 4243 4244 if t.log.GetLevel() == logging.DebugLevel { 4245 t.log.Debug("starting streaming governance updates") 4246 } 4247 ch, ref := t.governanceService.ObserveProposals(ctx, t.config.StreamRetries, req.PartyId) 4248 4249 return observe(ctx, t.log, "Governance", ch, ref, func(proposal entities.Proposal) error { 4250 gd, err := t.proposalToGovernanceData(ctx, proposal) 4251 if err != nil { 4252 return errors.Wrapf(err, "converting proposal to governance data for proposalID: %s", proposal.ID.String()) 4253 } 4254 return stream.Send(&v2.ObserveGovernanceResponse{ 4255 Data: gd, 4256 }) 4257 }) 4258 } 4259 4260 // GetPartyDiscountStats this is just passing the call through to the service. 4261 func (t *TradingDataServiceV2) GetPartyDiscountStats(ctx context.Context, req *v2.GetPartyDiscountStatsRequest) (*v2.GetPartyDiscountStatsResponse, error) { 4262 return t.partyDiscountStats.GetPartyStats(ctx, req.PartyId, req.MarketIds) 4263 } 4264 4265 func (t *TradingDataServiceV2) proposalToGovernanceData(ctx context.Context, proposal entities.Proposal) (*vega.GovernanceData, error) { 4266 yesVotes, err := t.governanceService.GetYesVotesForProposal(ctx, proposal.ID.String()) 4267 if err != nil { 4268 return nil, errors.Wrap(err, "getting yes votes for proposal") 4269 } 4270 4271 noVotes, err := t.governanceService.GetNoVotesForProposal(ctx, proposal.ID.String()) 4272 if err != nil { 4273 return nil, errors.Wrap(err, "getting no votes for proposal") 4274 } 4275 4276 var subProposals []*vega.Proposal 4277 if proposal.IsBatch() { 4278 for _, p := range proposal.Proposals { 4279 subProposals = append(subProposals, p.ToProto()) 4280 } 4281 } 4282 4283 proposalType := vega.GovernanceData_TYPE_SINGLE_OR_UNSPECIFIED 4284 if proposal.IsBatch() { 4285 proposalType = vega.GovernanceData_TYPE_BATCH 4286 } 4287 4288 return &vega.GovernanceData{ 4289 Proposal: proposal.ToProto(), 4290 Yes: voteListToProto(yesVotes), 4291 No: voteListToProto(noVotes), 4292 ProposalType: proposalType, 4293 Proposals: subProposals, 4294 }, nil 4295 } 4296 4297 func voteListToProto(votes []entities.Vote) []*vega.Vote { 4298 protoVotes := make([]*vega.Vote, len(votes)) 4299 for i, vote := range votes { 4300 protoVotes[i] = vote.ToProto() 4301 } 4302 return protoVotes 4303 } 4304 4305 // ObserveVotes streams votes for a given party or proposal. 4306 func (t *TradingDataServiceV2) ObserveVotes(req *v2.ObserveVotesRequest, stream v2.TradingDataService_ObserveVotesServer) error { 4307 if partyID := ptr.UnBox(req.PartyId); partyID != "" { 4308 return t.observePartyVotes(partyID, stream) 4309 } 4310 4311 if proposalID := ptr.UnBox(req.ProposalId); proposalID != "" { 4312 return t.observeProposalVotes(proposalID, stream) 4313 } 4314 4315 return formatE(ErrMissingProposalIDOrPartyID) 4316 } 4317 4318 func (t *TradingDataServiceV2) observePartyVotes(partyID string, stream v2.TradingDataService_ObserveVotesServer) error { 4319 ctx, cfunc := context.WithCancel(stream.Context()) 4320 defer cfunc() 4321 4322 if t.log.GetLevel() == logging.DebugLevel { 4323 t.log.Debug("starting streaming party votes") 4324 } 4325 ch, ref := t.governanceService.ObservePartyVotes(ctx, t.config.StreamRetries, partyID) 4326 4327 return observe(ctx, t.log, "PartyVote", ch, ref, func(vote entities.Vote) error { 4328 return stream.Send(&v2.ObserveVotesResponse{ 4329 Vote: vote.ToProto(), 4330 }) 4331 }) 4332 } 4333 4334 func (t *TradingDataServiceV2) observeProposalVotes(proposalID string, stream v2.TradingDataService_ObserveVotesServer) error { 4335 ctx, cfunc := context.WithCancel(stream.Context()) 4336 defer cfunc() 4337 4338 if t.log.GetLevel() == logging.DebugLevel { 4339 t.log.Debug("starting streaming proposal votes") 4340 } 4341 ch, ref := t.governanceService.ObserveProposalVotes(ctx, t.config.StreamRetries, proposalID) 4342 4343 return observe(ctx, t.log, "ProposalVote", ch, ref, func(p entities.Vote) error { 4344 return stream.Send(&v2.ObserveVotesResponse{ 4345 Vote: p.ToProto(), 4346 }) 4347 }) 4348 } 4349 4350 // GetProtocolUpgradeStatus returns the status of the protocol upgrade process. 4351 func (t *TradingDataServiceV2) GetProtocolUpgradeStatus(context.Context, *v2.GetProtocolUpgradeStatusRequest) (*v2.GetProtocolUpgradeStatusResponse, error) { 4352 ready := t.protocolUpgradeService.GetProtocolUpgradeStarted() 4353 return &v2.GetProtocolUpgradeStatusResponse{ 4354 Ready: ready, 4355 }, nil 4356 } 4357 4358 // ListProtocolUpgradeProposals returns a list of protocol upgrade proposals. 4359 func (t *TradingDataServiceV2) ListProtocolUpgradeProposals(ctx context.Context, req *v2.ListProtocolUpgradeProposalsRequest) (*v2.ListProtocolUpgradeProposalsResponse, error) { 4360 defer metrics.StartAPIRequestAndTimeGRPC("ListProtocolUpgradeProposals")() 4361 4362 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4363 if err != nil { 4364 return nil, formatE(ErrInvalidPagination, err) 4365 } 4366 4367 var status *entities.ProtocolUpgradeProposalStatus 4368 if req.Status != nil { 4369 status = ptr.From(entities.ProtocolUpgradeProposalStatus(*req.Status)) 4370 } 4371 4372 pups, pageInfo, err := t.protocolUpgradeService.ListProposals( 4373 ctx, 4374 status, 4375 req.ApprovedBy, 4376 pagination, 4377 ) 4378 if err != nil { 4379 return nil, formatE(ErrProtocolUpgradeServiceListProposals, err) 4380 } 4381 4382 edges, err := makeEdges[*v2.ProtocolUpgradeProposalEdge](pups) 4383 if err != nil { 4384 return nil, formatE(err) 4385 } 4386 4387 connection := v2.ProtocolUpgradeProposalConnection{ 4388 Edges: edges, 4389 PageInfo: pageInfo.ToProto(), 4390 } 4391 4392 return &v2.ListProtocolUpgradeProposalsResponse{ 4393 ProtocolUpgradeProposals: &connection, 4394 }, nil 4395 } 4396 4397 // ListCoreSnapshots returns a list of core snapshots. 4398 func (t *TradingDataServiceV2) ListCoreSnapshots(ctx context.Context, req *v2.ListCoreSnapshotsRequest) (*v2.ListCoreSnapshotsResponse, error) { 4399 defer metrics.StartAPIRequestAndTimeGRPC("ListCoreSnapshots")() 4400 4401 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4402 if err != nil { 4403 return nil, formatE(ErrInvalidPagination, err) 4404 } 4405 4406 snaps, pageInfo, err := t.coreSnapshotService.ListSnapshots(ctx, pagination) 4407 if err != nil { 4408 return nil, formatE(ErrCoreSnapshotServiceListSnapshots, err) 4409 } 4410 4411 edges, err := makeEdges[*v2.CoreSnapshotEdge](snaps) 4412 if err != nil { 4413 return nil, formatE(err) 4414 } 4415 4416 connection := v2.CoreSnapshotConnection{ 4417 Edges: edges, 4418 PageInfo: pageInfo.ToProto(), 4419 } 4420 4421 return &v2.ListCoreSnapshotsResponse{ 4422 CoreSnapshots: &connection, 4423 }, nil 4424 } 4425 4426 type tradingDataEventBusServerV2 struct { 4427 stream v2.TradingDataService_ObserveEventBusServer 4428 } 4429 4430 // RecvMsg receives a message from the stream. 4431 func (t tradingDataEventBusServerV2) RecvMsg(m interface{}) error { 4432 return t.stream.RecvMsg(m) 4433 } 4434 4435 // Context gets the context from the stream. 4436 func (t tradingDataEventBusServerV2) Context() context.Context { 4437 return t.stream.Context() 4438 } 4439 4440 // Send sends a message to the stream. 4441 func (t tradingDataEventBusServerV2) Send(data []*eventspb.BusEvent) error { 4442 return t.stream.Send(&v2.ObserveEventBusResponse{ 4443 Events: data, 4444 }) 4445 } 4446 4447 // ObserveEventBus subscribes to a stream of events. 4448 func (t *TradingDataServiceV2) ObserveEventBus(stream v2.TradingDataService_ObserveEventBusServer) error { 4449 return observeEventBus(t.log, t.config, tradingDataEventBusServerV2{stream}, t.eventService) 4450 } 4451 4452 // ObserveLedgerMovements subscribes to a stream of ledger movements. 4453 func (t *TradingDataServiceV2) ObserveLedgerMovements(_ *v2.ObserveLedgerMovementsRequest, srv v2.TradingDataService_ObserveLedgerMovementsServer) error { 4454 // Wrap context from the request into cancellable. We can close internal chan in error. 4455 ctx, cancel := context.WithCancel(srv.Context()) 4456 defer cancel() 4457 4458 transferResponsesChan, ref := t.ledgerService.Observe(ctx, t.config.StreamRetries) 4459 4460 if t.log.GetLevel() == logging.DebugLevel { 4461 t.log.Debug("TransferResponses subscriber - new rpc stream", logging.Uint64("ref", ref)) 4462 } 4463 4464 return observe(ctx, t.log, "TransferResponse", transferResponsesChan, ref, func(tr *vega.LedgerMovement) error { 4465 return srv.Send(&v2.ObserveLedgerMovementsResponse{ 4466 LedgerMovement: tr, 4467 }) 4468 }) 4469 } 4470 4471 // ListKeyRotations returns a list of key rotations for a given node. 4472 func (t *TradingDataServiceV2) ListKeyRotations(ctx context.Context, req *v2.ListKeyRotationsRequest) (*v2.ListKeyRotationsResponse, error) { 4473 defer metrics.StartAPIRequestAndTimeGRPC("ListKeyRotations")() 4474 4475 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4476 if err != nil { 4477 return nil, formatE(ErrInvalidPagination, err) 4478 } 4479 4480 if nodeID := ptr.UnBox(req.NodeId); nodeID != "" { 4481 rotations, err := t.getNodeKeyRotations(ctx, nodeID, pagination) 4482 if err != nil { 4483 return nil, formatE(ErrKeyRotationServiceGetPerNode, errors.Wrapf(err, "nodeID: %s", nodeID)) 4484 } 4485 return rotations, nil 4486 } 4487 4488 rotations, err := t.getAllKeyRotations(ctx, pagination) 4489 if err != nil { 4490 return nil, formatE(ErrKeyRotationServiceGetAll, err) 4491 } 4492 return rotations, nil 4493 } 4494 4495 func (t *TradingDataServiceV2) getAllKeyRotations(ctx context.Context, pagination entities.CursorPagination) (*v2.ListKeyRotationsResponse, error) { 4496 return makeKeyRotationResponse( 4497 t.keyRotationService.GetAllPubKeyRotations(ctx, pagination), 4498 ) 4499 } 4500 4501 func (t *TradingDataServiceV2) getNodeKeyRotations(ctx context.Context, nodeID string, pagination entities.CursorPagination) (*v2.ListKeyRotationsResponse, error) { 4502 return makeKeyRotationResponse( 4503 t.keyRotationService.GetPubKeyRotationsPerNode(ctx, nodeID, pagination), 4504 ) 4505 } 4506 4507 func makeKeyRotationResponse(rotations []entities.KeyRotation, pageInfo entities.PageInfo, err error) (*v2.ListKeyRotationsResponse, error) { 4508 if err != nil { 4509 return nil, err 4510 } 4511 4512 edges, err := makeEdges[*v2.KeyRotationEdge](rotations) 4513 if err != nil { 4514 return nil, err 4515 } 4516 4517 keyRotationConnection := &v2.KeyRotationConnection{ 4518 Edges: edges, 4519 PageInfo: pageInfo.ToProto(), 4520 } 4521 4522 return &v2.ListKeyRotationsResponse{ 4523 Rotations: keyRotationConnection, 4524 }, nil 4525 } 4526 4527 // ListEthereumKeyRotations returns a list of Ethereum key rotations. 4528 func (t *TradingDataServiceV2) ListEthereumKeyRotations(ctx context.Context, req *v2.ListEthereumKeyRotationsRequest) (*v2.ListEthereumKeyRotationsResponse, error) { 4529 defer metrics.StartAPIRequestAndTimeGRPC("ListEthereumKeyRotationsV2")() 4530 4531 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4532 if err != nil { 4533 return nil, formatE(ErrInvalidPagination, err) 4534 } 4535 4536 rotations, pageInfo, err := t.ethereumKeyRotationService.List(ctx, entities.NodeID(req.GetNodeId()), pagination) 4537 if err != nil { 4538 return nil, formatE(ErrEthereumKeyRotationServiceGetPerNode, errors.Wrapf(err, "nodeID: %s", req.GetNodeId())) 4539 } 4540 4541 edges, err := makeEdges[*v2.EthereumKeyRotationEdge](rotations) 4542 if err != nil { 4543 return nil, formatE(err) 4544 } 4545 4546 connection := &v2.EthereumKeyRotationsConnection{ 4547 Edges: edges, 4548 PageInfo: pageInfo.ToProto(), 4549 } 4550 4551 return &v2.ListEthereumKeyRotationsResponse{ 4552 KeyRotations: connection, 4553 }, nil 4554 } 4555 4556 // GetVegaTime returns the current vega time. 4557 func (t *TradingDataServiceV2) GetVegaTime(ctx context.Context, _ *v2.GetVegaTimeRequest) (*v2.GetVegaTimeResponse, error) { 4558 defer metrics.StartAPIRequestAndTimeGRPC("GetVegaTimeV2")() 4559 4560 b, err := t.blockService.GetLastBlock(ctx) 4561 if err != nil { 4562 return nil, formatE(ErrBlockServiceGetLast, err) 4563 } 4564 4565 return &v2.GetVegaTimeResponse{ 4566 Timestamp: b.VegaTime.UnixNano(), 4567 }, nil 4568 } 4569 4570 // -- NetworkHistory --. 4571 func (t *TradingDataServiceV2) ExportNetworkHistory(req *v2.ExportNetworkHistoryRequest, stream v2.TradingDataService_ExportNetworkHistoryServer) error { 4572 defer metrics.StartAPIRequestAndTimeGRPC("ExportNetworkHistory")() 4573 4574 if t.NetworkHistoryService == nil || reflect.ValueOf(t.NetworkHistoryService).IsNil() { 4575 return formatE(ErrNetworkHistoryServiceNotInitialised) 4576 } 4577 4578 if req.Table == v2.Table_TABLE_UNSPECIFIED { 4579 return formatE(ErrNetworkHistoryNoTableName, errors.New("empty table name")) 4580 } 4581 4582 tableName := strings.TrimPrefix(strings.ToLower(req.Table.String()), "table_") 4583 4584 allSegments, err := t.NetworkHistoryService.ListAllHistorySegments() 4585 if err != nil { 4586 return formatE(ErrListAllNetworkHistorySegment, err) 4587 } 4588 4589 ch, err := allSegments.ContiguousHistoryInRange(req.FromBlock, req.ToBlock) 4590 if err != nil || len(ch.Segments) == 0 { 4591 return formatE(ErrNetworkHistoryGetContiguousSegments, err) 4592 } 4593 chainID := ch.Segments[0].GetChainId() 4594 4595 header := metadata.Pairs("Content-Disposition", fmt.Sprintf("attachment;filename=%s-%s-%06d-%06d.zip", chainID, tableName, ch.HeightFrom, ch.HeightTo)) 4596 if err := stream.SendHeader(header); err != nil { 4597 return formatE(ErrSendingGRPCHeader, err) 4598 } 4599 4600 grpcWriter := httpBodyWriter{chunkSize: httpBodyChunkSize, contentType: "application/zip", buf: &bytes.Buffer{}, stream: stream} 4601 zipWriter := zip.NewWriter(&grpcWriter) 4602 defer grpcWriter.Close() 4603 defer zipWriter.Close() 4604 4605 partitionedSegments := partitionSegmentsByDBVersion(ch.Segments) 4606 4607 for _, segments := range partitionedSegments { 4608 if len(segments) == 0 { 4609 continue 4610 } 4611 csvFileName := fmt.Sprintf("%s-%s-%03d-%06d-%06d.csv", 4612 segments[0].GetChainId(), 4613 tableName, 4614 segments[0].GetDatabaseVersion(), 4615 segments[0].GetFromHeight(), 4616 segments[len(segments)-1].GetToHeight()) 4617 4618 out, err := zipWriter.Create(csvFileName) 4619 if err != nil { 4620 return formatE(ErrNetworkHistoryCreatingZipFile, err) 4621 } 4622 4623 for i, segment := range segments { 4624 segmentReader, size, err := t.NetworkHistoryService.GetHistorySegmentReader(stream.Context(), segment.GetHistorySegmentId()) 4625 if err != nil { 4626 segmentReader.Close() 4627 return formatE(ErrNetworkHistoryOpeningSegment, err) 4628 } 4629 4630 segmentData, err := fsutil.ReadNetworkHistorySegmentData(segmentReader, size, tableName) 4631 if err != nil { 4632 segmentReader.Close() 4633 return formatE(ErrNetworkHistoryExtractingSegment, err) 4634 } 4635 scanner := bufio.NewScanner(segmentData) 4636 4637 // For all except first segment, skip the header. 4638 if i != 0 { 4639 scanner.Scan() 4640 } 4641 4642 for scanner.Scan() { 4643 out.Write(scanner.Bytes()) 4644 out.Write([]byte("\n")) 4645 } 4646 4647 segmentReader.Close() 4648 } 4649 } 4650 return nil 4651 } 4652 4653 func partitionSegmentsByDBVersion(segments []segment.Full) [][]segment.Full { 4654 partitioned := [][]segment.Full{} 4655 sliceStart := 0 4656 4657 for i, segment := range segments { 4658 sliceVersion := segments[sliceStart].GetDatabaseVersion() 4659 if segment.GetDatabaseVersion() != sliceVersion { 4660 partitioned = append(partitioned, segments[sliceStart:i]) 4661 sliceStart = i 4662 } 4663 } 4664 partitioned = append(partitioned, segments[sliceStart:]) 4665 return partitioned 4666 } 4667 4668 // GetMostRecentNetworkHistorySegment returns the most recent network history segment. 4669 func (t *TradingDataServiceV2) GetMostRecentNetworkHistorySegment(context.Context, *v2.GetMostRecentNetworkHistorySegmentRequest) (*v2.GetMostRecentNetworkHistorySegmentResponse, error) { 4670 defer metrics.StartAPIRequestAndTimeGRPC("GetMostRecentNetworkHistorySegment")() 4671 4672 if t.NetworkHistoryService == nil || reflect.ValueOf(t.NetworkHistoryService).IsNil() { 4673 return nil, formatE(ErrNetworkHistoryServiceNotInitialised) 4674 } 4675 4676 segment, err := t.NetworkHistoryService.GetHighestBlockHeightHistorySegment() 4677 if err != nil { 4678 if errors.Is(err, store.ErrSegmentNotFound) { 4679 return &v2.GetMostRecentNetworkHistorySegmentResponse{ 4680 Segment: nil, 4681 }, nil 4682 } 4683 return nil, formatE(ErrGetMostRecentHistorySegment, err) 4684 } 4685 4686 return &v2.GetMostRecentNetworkHistorySegmentResponse{ 4687 Segment: toHistorySegment(segment), 4688 SwarmKeySeed: t.NetworkHistoryService.GetSwarmKeySeed(), 4689 }, nil 4690 } 4691 4692 // ListAllNetworkHistorySegments returns all network history segments. 4693 func (t *TradingDataServiceV2) ListAllNetworkHistorySegments(context.Context, *v2.ListAllNetworkHistorySegmentsRequest) (*v2.ListAllNetworkHistorySegmentsResponse, error) { 4694 defer metrics.StartAPIRequestAndTimeGRPC("ListAllNetworkHistorySegments")() 4695 4696 if t.NetworkHistoryService == nil || reflect.ValueOf(t.NetworkHistoryService).IsNil() { 4697 return nil, formatE(ErrNetworkHistoryServiceNotInitialised) 4698 } 4699 4700 segments, err := t.NetworkHistoryService.ListAllHistorySegments() 4701 if err != nil { 4702 return nil, formatE(ErrListAllNetworkHistorySegment, err) 4703 } 4704 4705 historySegments := make([]*v2.HistorySegment, 0, len(segments)) 4706 for _, segment := range segments { 4707 historySegments = append(historySegments, toHistorySegment(segment)) 4708 } 4709 4710 // Newest first 4711 sort.Slice(historySegments, func(i, j int) bool { 4712 return historySegments[i].ToHeight > historySegments[j].ToHeight 4713 }) 4714 4715 return &v2.ListAllNetworkHistorySegmentsResponse{ 4716 Segments: historySegments, 4717 }, nil 4718 } 4719 4720 func toHistorySegment(segment segment.Full) *v2.HistorySegment { 4721 return &v2.HistorySegment{ 4722 FromHeight: segment.GetFromHeight(), 4723 ToHeight: segment.GetToHeight(), 4724 ChainId: segment.GetChainId(), 4725 DatabaseVersion: segment.GetDatabaseVersion(), 4726 HistorySegmentId: segment.GetHistorySegmentId(), 4727 PreviousHistorySegmentId: segment.GetPreviousHistorySegmentId(), 4728 } 4729 } 4730 4731 // GetActiveNetworkHistoryPeerAddresses returns the active network history peer addresses. 4732 func (t *TradingDataServiceV2) GetActiveNetworkHistoryPeerAddresses(context.Context, *v2.GetActiveNetworkHistoryPeerAddressesRequest) (*v2.GetActiveNetworkHistoryPeerAddressesResponse, error) { 4733 defer metrics.StartAPIRequestAndTimeGRPC("GetActiveNetworkHistoryPeerAddresses")() 4734 4735 if t.NetworkHistoryService == nil || reflect.ValueOf(t.NetworkHistoryService).IsNil() { 4736 return nil, formatE(ErrNetworkHistoryServiceNotInitialised) 4737 } 4738 4739 return &v2.GetActiveNetworkHistoryPeerAddressesResponse{ 4740 IpAddresses: t.NetworkHistoryService.GetActivePeerIPAddresses(), 4741 }, nil 4742 } 4743 4744 // NetworkHistoryStatus returns the network history status. 4745 func (t *TradingDataServiceV2) GetNetworkHistoryStatus(context.Context, *v2.GetNetworkHistoryStatusRequest) (*v2.GetNetworkHistoryStatusResponse, error) { 4746 defer metrics.StartAPIRequestAndTimeGRPC("GetNetworkHistoryStatus")() 4747 4748 if t.NetworkHistoryService == nil || reflect.ValueOf(t.NetworkHistoryService).IsNil() { 4749 return nil, formatE(ErrNetworkHistoryServiceNotInitialised) 4750 } 4751 4752 connectedPeerAddresses, err := t.NetworkHistoryService.GetConnectedPeerAddresses() 4753 if err != nil { 4754 return nil, formatE(ErrGetConnectedPeerAddresses, err) 4755 } 4756 4757 // A subset of the connected peer addresses are likely to be copied to form another nodes peer set, randomise the list 4758 // to minimise the chance that the same sub set are copied each time. 4759 rand.Shuffle(len(connectedPeerAddresses), func(i, j int) { 4760 connectedPeerAddresses[i], connectedPeerAddresses[j] = connectedPeerAddresses[j], connectedPeerAddresses[i] 4761 }) 4762 4763 ipfsAddress, err := t.NetworkHistoryService.GetIpfsAddress() 4764 if err != nil { 4765 return nil, formatE(ErrGetIpfsAddress, err) 4766 } 4767 4768 return &v2.GetNetworkHistoryStatusResponse{ 4769 IpfsAddress: ipfsAddress, 4770 SwarmKey: t.NetworkHistoryService.GetSwarmKey(), 4771 SwarmKeySeed: t.NetworkHistoryService.GetSwarmKeySeed(), 4772 ConnectedPeers: connectedPeerAddresses, 4773 }, nil 4774 } 4775 4776 // NetworkHistoryBootstrapPeers returns the network history bootstrap peers. 4777 func (t *TradingDataServiceV2) GetNetworkHistoryBootstrapPeers(context.Context, *v2.GetNetworkHistoryBootstrapPeersRequest) (*v2.GetNetworkHistoryBootstrapPeersResponse, error) { 4778 if t.NetworkHistoryService == nil || reflect.ValueOf(t.NetworkHistoryService).IsNil() { 4779 return nil, formatE(ErrNetworkHistoryServiceNotInitialised) 4780 } 4781 4782 return &v2.GetNetworkHistoryBootstrapPeersResponse{BootstrapPeers: t.NetworkHistoryService.GetBootstrapPeers()}, nil 4783 } 4784 4785 // Ping returns a ping response. 4786 func (t *TradingDataServiceV2) Ping(context.Context, *v2.PingRequest) (*v2.PingResponse, error) { 4787 defer metrics.StartAPIRequestAndTimeGRPC("Ping")() 4788 return &v2.PingResponse{}, nil 4789 } 4790 4791 func (t *TradingDataServiceV2) ListEntities(ctx context.Context, req *v2.ListEntitiesRequest) (*v2.ListEntitiesResponse, error) { 4792 defer metrics.StartAPIRequestAndTimeGRPC("ListEntities")() 4793 4794 if len(req.GetTransactionHash()) == 0 { 4795 return nil, formatE(ErrMissingEmptyTxHash) 4796 } 4797 4798 if !crypto.IsValidVegaID(req.GetTransactionHash()) { 4799 return nil, formatE(ErrInvalidTxHash) 4800 } 4801 4802 txHash := entities.TxHash(req.GetTransactionHash()) 4803 eg, ctx := errgroup.WithContext(ctx) 4804 4805 // query 4806 accounts := queryProtoEntities[*vega.Account](ctx, eg, txHash, 4807 t.AccountService.GetByTxHash, ErrAccountServiceGetByTxHash) 4808 4809 orders := queryProtoEntities[*vega.Order](ctx, eg, txHash, 4810 t.orderService.GetByTxHash, ErrOrderServiceGetByTxHash) 4811 4812 positions := queryProtoEntities[*vega.Position](ctx, eg, txHash, 4813 t.positionService.GetByTxHash, ErrPositionsGetByTxHash) 4814 4815 balances := queryProtoEntities[*v2.AccountBalance](ctx, eg, txHash, 4816 t.AccountService.GetBalancesByTxHash, ErrAccountServiceGetBalancesByTxHash) 4817 4818 votes := queryProtoEntities[*vega.Vote](ctx, eg, txHash, 4819 t.governanceService.GetVotesByTxHash, ErrVotesGetByTxHash) 4820 4821 trades := queryProtoEntities[*vega.Trade](ctx, eg, txHash, 4822 t.tradeService.GetByTxHash, ErrTradeServiceGetByTxHash) 4823 4824 oracleSpecs := queryProtoEntities[*vega.OracleSpec](ctx, eg, txHash, 4825 t.oracleSpecService.GetByTxHash, ErrOracleSpecGetByTxHash) 4826 4827 oracleData := queryProtoEntities[*vega.OracleData](ctx, eg, txHash, 4828 t.oracleDataService.GetByTxHash, ErrOracleDataGetByTxHash) 4829 4830 markets := queryProtoEntities[*vega.Market](ctx, eg, txHash, 4831 t.MarketsService.GetByTxHash, ErrMarketServiceGetByTxHash) 4832 4833 parties := queryProtoEntities[*vega.Party](ctx, eg, txHash, 4834 t.partyService.GetByTxHash, ErrPartyServiceGetByTxHash) 4835 4836 rewards := queryProtoEntities[*vega.Reward](ctx, eg, txHash, 4837 t.RewardService.GetByTxHash, ErrRewardsGetByTxHash) 4838 4839 deposits := queryProtoEntities[*vega.Deposit](ctx, eg, txHash, 4840 t.depositService.GetByTxHash, ErrDepositsGetByTxHash) 4841 4842 withdrawals := queryProtoEntities[*vega.Withdrawal](ctx, eg, txHash, 4843 t.withdrawalService.GetByTxHash, ErrWithdrawalsGetByTxHash) 4844 4845 assets := queryProtoEntities[*vega.Asset](ctx, eg, txHash, 4846 t.AssetService.GetByTxHash, ErrAssetsGetByTxHash) 4847 4848 lps := queryProtoEntities[*vega.LiquidityProvision](ctx, eg, txHash, 4849 t.liquidityProvisionService.GetByTxHash, ErrLiquidityProvisionGetByTxHash) 4850 4851 proposals := queryProtoEntities[*vega.Proposal](ctx, eg, txHash, 4852 t.governanceService.GetProposalsByTxHash, ErrProposalsGetByTxHash) 4853 4854 delegations := queryProtoEntities[*vega.Delegation](ctx, eg, txHash, 4855 t.delegationService.GetByTxHash, ErrDelegationsGetByTxHash) 4856 4857 signatures := queryProtoEntities[*cmdsV1.NodeSignature](ctx, eg, txHash, 4858 t.notaryService.GetByTxHash, ErrSignaturesGetByTxHash) 4859 4860 netParams := queryProtoEntities[*vega.NetworkParameter](ctx, eg, txHash, 4861 t.networkParameterService.GetByTxHash, ErrNetworkParametersGetByTxHash) 4862 4863 keyRotations := queryProtoEntities[*v1.KeyRotation](ctx, eg, txHash, 4864 t.keyRotationService.GetByTxHash, ErrKeyRotationsGetByTxHash) 4865 4866 ethKeyRotations := queryProtoEntities[*v1.EthereumKeyRotation](ctx, eg, txHash, 4867 t.ethereumKeyRotationService.GetByTxHash, ErrEthereumKeyRotationsGetByTxHash) 4868 4869 pups := queryProtoEntities[*v1.ProtocolUpgradeEvent](ctx, eg, txHash, 4870 t.protocolUpgradeService.GetByTxHash, ErrEthereumKeyRotationsGetByTxHash) 4871 4872 nodes := queryProtoEntities[*v2.NodeBasic](ctx, eg, txHash, 4873 t.nodeService.GetByTxHash, ErrNodeServiceGetByTxHash) 4874 4875 // query and map 4876 ledgerEntries := queryAndMapEntities(ctx, eg, txHash, 4877 t.ledgerService.GetByTxHash, 4878 func(item entities.LedgerEntry) (*vega.LedgerEntry, error) { 4879 return item.ToProto(ctx, t.AccountService) 4880 }, 4881 ErrLedgerEntriesGetByTxHash, 4882 ) 4883 4884 transfers := queryAndMapEntities(ctx, eg, txHash, 4885 t.transfersService.GetByTxHash, 4886 func(item entities.Transfer) (*v1.Transfer, error) { 4887 return item.ToProto(ctx, t.AccountService) 4888 }, 4889 ErrTransfersGetByTxHash, 4890 ) 4891 4892 marginLevels := queryAndMapEntities(ctx, eg, txHash, 4893 t.riskService.GetByTxHash, 4894 func(item entities.MarginLevels) (*vega.MarginLevels, error) { 4895 return item.ToProto(ctx, t.AccountService) 4896 }, 4897 ErrMarginLevelsGetByTxHash, 4898 ) 4899 4900 addedEvents := queryAndMapEntities(ctx, eg, txHash, 4901 t.multiSigService.GetAddedByTxHash, 4902 func(item entities.ERC20MultiSigSignerAddedEvent) (*v2.ERC20MultiSigSignerAddedBundle, error) { 4903 return item.ToDataNodeApiV2Proto(ctx, t.notaryService) 4904 }, 4905 ErrERC20MultiSigSignerAddedEventGetByTxHash, 4906 ) 4907 4908 removedEvents := queryAndMapEntities(ctx, eg, txHash, 4909 t.multiSigService.GetRemovedByTxHash, 4910 func(item entities.ERC20MultiSigSignerRemovedEvent) (*v2.ERC20MultiSigSignerRemovedBundle, error) { 4911 return item.ToDataNodeApiV2Proto(ctx, t.notaryService) 4912 }, 4913 ErrERC20MultiSigSignerRemovedEventGetByTxHash, 4914 ) 4915 4916 if err := eg.Wait(); err != nil { 4917 return nil, err 4918 } 4919 4920 return &v2.ListEntitiesResponse{ 4921 Accounts: <-accounts, 4922 Orders: <-orders, 4923 Positions: <-positions, 4924 LedgerEntries: <-ledgerEntries, 4925 BalanceChanges: <-balances, 4926 Transfers: <-transfers, 4927 Votes: <-votes, 4928 Erc20MultiSigSignerAddedBundles: <-addedEvents, 4929 Erc20MultiSigSignerRemovedBundles: <-removedEvents, 4930 Trades: <-trades, 4931 OracleSpecs: <-oracleSpecs, 4932 OracleData: <-oracleData, 4933 Markets: <-markets, 4934 Parties: <-parties, 4935 MarginLevels: <-marginLevels, 4936 Rewards: <-rewards, 4937 Deposits: <-deposits, 4938 Withdrawals: <-withdrawals, 4939 Assets: <-assets, 4940 LiquidityProvisions: <-lps, 4941 Proposals: <-proposals, 4942 Delegations: <-delegations, 4943 Nodes: <-nodes, 4944 NodeSignatures: <-signatures, 4945 NetworkParameters: <-netParams, 4946 KeyRotations: <-keyRotations, 4947 EthereumKeyRotations: <-ethKeyRotations, 4948 ProtocolUpgradeProposals: <-pups, 4949 }, nil 4950 } 4951 4952 func batch[T any](in []T, batchSize int) [][]T { 4953 batches := make([][]T, 0, (len(in)+batchSize-1)/batchSize) 4954 for batchSize < len(in) { 4955 in, batches = in[batchSize:], append(batches, in[0:batchSize:batchSize]) 4956 } 4957 batches = append(batches, in) 4958 return batches 4959 } 4960 4961 func (t *TradingDataServiceV2) GetStopOrder(ctx context.Context, req *v2.GetStopOrderRequest) (*v2.GetStopOrderResponse, error) { 4962 defer metrics.StartAPIRequestAndTimeGRPC("GetStopOrder")() 4963 4964 if len(req.OrderId) == 0 { 4965 return nil, formatE(ErrMissingOrderID) 4966 } 4967 4968 if !crypto.IsValidVegaID(req.OrderId) { 4969 return nil, formatE(ErrInvalidOrderID) 4970 } 4971 4972 order, err := t.stopOrderService.GetStopOrder(ctx, req.OrderId) 4973 if err != nil { 4974 return nil, formatE(ErrOrderNotFound, errors.Wrapf(err, "orderID: %s", req.OrderId)) 4975 } 4976 4977 return &v2.GetStopOrderResponse{ 4978 Order: order.ToProto(), 4979 }, nil 4980 } 4981 4982 func (t *TradingDataServiceV2) ListStopOrders(ctx context.Context, req *v2.ListStopOrdersRequest) (*v2.ListStopOrdersResponse, error) { 4983 defer metrics.StartAPIRequestAndTimeGRPC("GetStopOrder")() 4984 4985 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 4986 if err != nil { 4987 return nil, formatE(ErrInvalidPagination, err) 4988 } 4989 4990 var filter entities.StopOrderFilter 4991 if req.Filter != nil { 4992 dateRange := entities.DateRangeFromProto(req.Filter.DateRange) 4993 liveOnly := false 4994 4995 if req.Filter.LiveOnly != nil { 4996 liveOnly = *req.Filter.LiveOnly 4997 } 4998 4999 marketIDs := NewVegaIDSlice(req.Filter.MarketIds...) 5000 if err := marketIDs.Ensure(); err != nil { 5001 return nil, formatE(err, errors.New("one or more market id is invalid")) 5002 } 5003 5004 partyIDs := NewVegaIDSlice(req.Filter.PartyIds...) 5005 if err := partyIDs.Ensure(); err != nil { 5006 return nil, formatE(err, errors.New("one or more party id is invalid")) 5007 } 5008 filter = entities.StopOrderFilter{ 5009 Statuses: stopOrderStatusesFromProto(req.Filter.Statuses), 5010 ExpiryStrategy: stopOrderExpiryStrategyFromProto(req.Filter.ExpiryStrategies), 5011 PartyIDs: partyIDs, 5012 MarketIDs: marketIDs, 5013 DateRange: &entities.DateRange{Start: dateRange.Start, End: dateRange.End}, 5014 LiveOnly: liveOnly, 5015 } 5016 } 5017 5018 orders, pageInfo, err := t.stopOrderService.ListStopOrders(ctx, filter, pagination) 5019 if err != nil { 5020 if errors.Is(err, sqlstore.ErrLastPaginationNotSupported) { 5021 return nil, formatE(ErrLastPaginationNotSupported, err) 5022 } 5023 5024 return nil, formatE(err) 5025 } 5026 5027 edges, err := makeEdges[*v2.StopOrderEdge](orders) 5028 if err != nil { 5029 return nil, formatE(err) 5030 } 5031 5032 ordersConnection := &v2.StopOrderConnection{ 5033 Edges: edges, 5034 PageInfo: pageInfo.ToProto(), 5035 } 5036 5037 return &v2.ListStopOrdersResponse{ 5038 Orders: ordersConnection, 5039 }, nil 5040 } 5041 5042 func stopOrderStatusesFromProto(statuses []vega.StopOrder_Status) []entities.StopOrderStatus { 5043 s := make([]entities.StopOrderStatus, len(statuses)) 5044 for i := range statuses { 5045 s[i] = entities.StopOrderStatus(statuses[i]) 5046 } 5047 return s 5048 } 5049 5050 func stopOrderExpiryStrategyFromProto(strategies []vega.StopOrder_ExpiryStrategy) []entities.StopOrderExpiryStrategy { 5051 es := make([]entities.StopOrderExpiryStrategy, len(strategies)) 5052 for i := range strategies { 5053 es[i] = entities.StopOrderExpiryStrategy(strategies[i]) 5054 } 5055 return es 5056 } 5057 5058 func (t *TradingDataServiceV2) ListFundingPeriods(ctx context.Context, req *v2.ListFundingPeriodsRequest) (*v2.ListFundingPeriodsResponse, error) { 5059 defer metrics.StartAPIRequestAndTimeGRPC("ListFundingPeriods")() 5060 5061 if len(req.MarketId) == 0 { 5062 return nil, formatE(ErrEmptyMissingMarketID) 5063 } 5064 5065 if !crypto.IsValidVegaID(req.MarketId) { 5066 return nil, formatE(ErrInvalidMarketID) 5067 } 5068 5069 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5070 if err != nil { 5071 return nil, formatE(ErrInvalidPagination, err) 5072 } 5073 5074 dateRange := entities.DateRangeFromProto(req.DateRange) 5075 periods, pageInfo, err := t.fundingPeriodService.ListFundingPeriods(ctx, entities.MarketID(req.MarketId), dateRange, pagination) 5076 if err != nil { 5077 return nil, formatE(ErrListFundingPeriod, err) 5078 } 5079 5080 edges, err := makeEdges[*v2.FundingPeriodEdge](periods) 5081 if err != nil { 5082 return nil, formatE(err) 5083 } 5084 5085 connection := &v2.FundingPeriodConnection{ 5086 Edges: edges, 5087 PageInfo: pageInfo.ToProto(), 5088 } 5089 5090 return &v2.ListFundingPeriodsResponse{ 5091 FundingPeriods: connection, 5092 }, nil 5093 } 5094 5095 func (t *TradingDataServiceV2) ListFundingPeriodDataPoints(ctx context.Context, req *v2.ListFundingPeriodDataPointsRequest) ( 5096 *v2.ListFundingPeriodDataPointsResponse, error, 5097 ) { 5098 defer metrics.StartAPIRequestAndTimeGRPC("ListFundingPeriodDataPoints")() 5099 5100 if len(req.MarketId) == 0 { 5101 return nil, formatE(ErrEmptyMissingMarketID) 5102 } 5103 5104 if !crypto.IsValidVegaID(req.MarketId) { 5105 return nil, formatE(ErrInvalidMarketID) 5106 } 5107 5108 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5109 if err != nil { 5110 return nil, formatE(ErrInvalidPagination, err) 5111 } 5112 5113 dateRange := entities.DateRangeFromProto(req.DateRange) 5114 dataPoints, pageInfo, err := t.fundingPeriodService.ListFundingPeriodDataPoints(ctx, 5115 entities.MarketID(req.MarketId), 5116 dateRange, 5117 (*entities.FundingPeriodDataPointSource)(req.Source), 5118 req.Seq, 5119 pagination, 5120 ) 5121 if err != nil { 5122 return nil, formatE(ErrListFundingPeriodDataPoints, err) 5123 } 5124 5125 edges, err := makeEdges[*v2.FundingPeriodDataPointEdge](dataPoints) 5126 if err != nil { 5127 return nil, formatE(err) 5128 } 5129 5130 connection := &v2.FundingPeriodDataPointConnection{ 5131 Edges: edges, 5132 PageInfo: pageInfo.ToProto(), 5133 } 5134 5135 return &v2.ListFundingPeriodDataPointsResponse{ 5136 FundingPeriodDataPoints: connection, 5137 }, nil 5138 } 5139 5140 func (t *TradingDataServiceV2) GetCurrentReferralProgram(ctx context.Context, _ *v2.GetCurrentReferralProgramRequest) ( 5141 *v2.GetCurrentReferralProgramResponse, error, 5142 ) { 5143 defer metrics.StartAPIRequestAndTimeGRPC("GetCurrentReferralProgram")() 5144 referralProgram, err := t.referralProgramService.GetCurrentReferralProgram(ctx) 5145 if err != nil { 5146 return nil, formatE(ErrGetCurrentReferralProgram, err) 5147 } 5148 5149 return &v2.GetCurrentReferralProgramResponse{ 5150 CurrentReferralProgram: referralProgram.ToProto(), 5151 }, nil 5152 } 5153 5154 func (t *TradingDataServiceV2) ListReferralSets(ctx context.Context, req *v2.ListReferralSetsRequest) ( 5155 *v2.ListReferralSetsResponse, error, 5156 ) { 5157 defer metrics.StartAPIRequestAndTimeGRPC("ListReferralSets")() 5158 5159 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5160 if err != nil { 5161 return nil, formatE(ErrInvalidPagination, err) 5162 } 5163 5164 var ( 5165 id *entities.ReferralSetID 5166 referrer *entities.PartyID 5167 referee *entities.PartyID 5168 ) 5169 5170 if req.ReferralSetId != nil { 5171 id = ptr.From(entities.ReferralSetID(*req.ReferralSetId)) 5172 } 5173 5174 if req.Referrer != nil { 5175 referrer = ptr.From(entities.PartyID(*req.Referrer)) 5176 } 5177 5178 if req.Referee != nil { 5179 referee = ptr.From(entities.PartyID(*req.Referee)) 5180 } 5181 5182 sets, pageInfo, err := t.ReferralSetsService.ListReferralSets(ctx, id, referrer, referee, pagination) 5183 if err != nil { 5184 return nil, formatE(err) 5185 } 5186 5187 edges, err := makeEdges[*v2.ReferralSetEdge](sets) 5188 if err != nil { 5189 return nil, formatE(err) 5190 } 5191 5192 connection := &v2.ReferralSetConnection{ 5193 Edges: edges, 5194 PageInfo: pageInfo.ToProto(), 5195 } 5196 5197 return &v2.ListReferralSetsResponse{ 5198 ReferralSets: connection, 5199 }, nil 5200 } 5201 5202 func (t *TradingDataServiceV2) ListReferralSetReferees(ctx context.Context, req *v2.ListReferralSetRefereesRequest) ( 5203 *v2.ListReferralSetRefereesResponse, error, 5204 ) { 5205 defer metrics.StartAPIRequestAndTimeGRPC("ListReferralSetReferees")() 5206 5207 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5208 if err != nil { 5209 return nil, formatE(ErrInvalidPagination, err) 5210 } 5211 5212 var ( 5213 id *entities.ReferralSetID 5214 referrer *entities.PartyID 5215 referee *entities.PartyID 5216 epochsToAggregate uint32 = 30 // default is 30 epochs 5217 ) 5218 5219 if req.ReferralSetId != nil { 5220 id = ptr.From(entities.ReferralSetID(*req.ReferralSetId)) 5221 } 5222 5223 if req.Referrer != nil { 5224 referrer = ptr.From(entities.PartyID(*req.Referrer)) 5225 } 5226 5227 if req.Referee != nil { 5228 referee = ptr.From(entities.PartyID(*req.Referee)) 5229 } 5230 5231 if req.AggregationEpochs != nil { 5232 epochsToAggregate = *req.AggregationEpochs 5233 } 5234 5235 referees, pageInfo, err := t.ReferralSetsService.ListReferralSetReferees(ctx, id, referrer, referee, pagination, epochsToAggregate) 5236 if err != nil { 5237 return nil, formatE(err) 5238 } 5239 5240 edges, err := makeEdges[*v2.ReferralSetRefereeEdge](referees) 5241 if err != nil { 5242 return nil, formatE(err) 5243 } 5244 5245 connection := &v2.ReferralSetRefereeConnection{ 5246 Edges: edges, 5247 PageInfo: pageInfo.ToProto(), 5248 } 5249 5250 return &v2.ListReferralSetRefereesResponse{ 5251 ReferralSetReferees: connection, 5252 }, nil 5253 } 5254 5255 func (t *TradingDataServiceV2) GetReferralSetStats(ctx context.Context, req *v2.GetReferralSetStatsRequest) ( 5256 *v2.GetReferralSetStatsResponse, error, 5257 ) { 5258 defer metrics.StartAPIRequestAndTimeGRPC("GetReferralSetStats")() 5259 5260 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5261 if err != nil { 5262 return nil, formatE(ErrInvalidPagination, err) 5263 } 5264 5265 var setID *entities.ReferralSetID 5266 if req.ReferralSetId != nil { 5267 setID = ptr.From(entities.ReferralSetID(*req.ReferralSetId)) 5268 } 5269 5270 var referee *entities.PartyID 5271 if req.Referee != nil { 5272 referee = ptr.From(entities.PartyID(*req.Referee)) 5273 } 5274 5275 stats, pageInfo, err := t.ReferralSetsService.GetReferralSetStats(ctx, setID, req.AtEpoch, referee, pagination) 5276 if err != nil { 5277 return nil, formatE(ErrGetReferralSetStats, err) 5278 } 5279 5280 edges, err := makeEdges[*v2.ReferralSetStatsEdge](stats) 5281 if err != nil { 5282 return nil, formatE(err) 5283 } 5284 5285 return &v2.GetReferralSetStatsResponse{ 5286 Stats: &v2.ReferralSetStatsConnection{ 5287 Edges: edges, 5288 PageInfo: pageInfo.ToProto(), 5289 }, 5290 }, nil 5291 } 5292 5293 func (t *TradingDataServiceV2) ListTeams(ctx context.Context, req *v2.ListTeamsRequest) (*v2.ListTeamsResponse, error) { 5294 defer metrics.StartAPIRequestAndTimeGRPC("ListTeams")() 5295 5296 if req.TeamId == nil && req.PartyId == nil { 5297 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5298 if err != nil { 5299 return nil, formatE(ErrInvalidPagination, err) 5300 } 5301 5302 return listTeams(ctx, t.teamsService, pagination) 5303 } 5304 5305 var ( 5306 teamID entities.TeamID 5307 partyID entities.PartyID 5308 ) 5309 5310 if req.TeamId != nil { 5311 teamID = entities.TeamID(*req.TeamId) 5312 } 5313 5314 if req.PartyId != nil { 5315 partyID = entities.PartyID(*req.PartyId) 5316 } 5317 5318 return listTeam(ctx, t.teamsService, teamID, partyID) 5319 } 5320 5321 func listTeam(ctx context.Context, teamsService *service.Teams, teamID entities.TeamID, partyID entities.PartyID) (*v2.ListTeamsResponse, error) { 5322 team, err := teamsService.GetTeam(ctx, teamID, partyID) 5323 if err != nil { 5324 if pgxscan.NotFound(err) { 5325 return &v2.ListTeamsResponse{ 5326 Teams: &v2.TeamConnection{ 5327 Edges: []*v2.TeamEdge{}, 5328 }, 5329 }, nil 5330 } 5331 5332 return nil, formatE(ErrListTeams, err) 5333 } 5334 5335 if team == nil { 5336 return nil, nil 5337 } 5338 5339 edges, err := makeEdges[*v2.TeamEdge]([]entities.Team{*team}) 5340 if err != nil { 5341 return nil, formatE(err) 5342 } 5343 5344 return &v2.ListTeamsResponse{ 5345 Teams: &v2.TeamConnection{ 5346 Edges: edges, 5347 PageInfo: &v2.PageInfo{ 5348 HasNextPage: false, 5349 HasPreviousPage: false, 5350 StartCursor: team.Cursor().Encode(), 5351 EndCursor: team.Cursor().Encode(), 5352 }, 5353 }, 5354 }, nil 5355 } 5356 5357 func listTeams(ctx context.Context, teamsService *service.Teams, pagination entities.CursorPagination) (*v2.ListTeamsResponse, error) { 5358 teams, pageInfo, err := teamsService.ListTeams(ctx, pagination) 5359 if err != nil { 5360 return nil, formatE(ErrListTeams, err) 5361 } 5362 edges, err := makeEdges[*v2.TeamEdge](teams) 5363 if err != nil { 5364 return nil, formatE(err) 5365 } 5366 connection := &v2.TeamConnection{ 5367 Edges: edges, 5368 PageInfo: pageInfo.ToProto(), 5369 } 5370 5371 return &v2.ListTeamsResponse{ 5372 Teams: connection, 5373 }, nil 5374 } 5375 5376 func (t *TradingDataServiceV2) ListTeamsStatistics(ctx context.Context, req *v2.ListTeamsStatisticsRequest) (*v2.ListTeamsStatisticsResponse, error) { 5377 defer metrics.StartAPIRequestAndTimeGRPC("ListTeamsStatistics")() 5378 5379 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5380 if err != nil { 5381 return nil, formatE(ErrInvalidPagination, err) 5382 } 5383 5384 filters := sqlstore.ListTeamsStatisticsFilters{ 5385 AggregationEpochs: 10, 5386 } 5387 if req.TeamId != nil { 5388 filters.TeamID = ptr.From(entities.TeamID(*req.TeamId)) 5389 } 5390 if req.AggregationEpochs != nil { 5391 filters.AggregationEpochs = *req.AggregationEpochs 5392 } 5393 5394 stats, pageInfo, err := t.teamsService.ListTeamsStatistics(ctx, pagination, filters) 5395 if err != nil { 5396 return nil, formatE(ErrListTeamStatistics, err) 5397 } 5398 5399 edges, err := makeEdges[*v2.TeamStatisticsEdge](stats) 5400 if err != nil { 5401 return nil, formatE(err) 5402 } 5403 5404 return &v2.ListTeamsStatisticsResponse{ 5405 Statistics: &v2.TeamsStatisticsConnection{ 5406 Edges: edges, 5407 PageInfo: pageInfo.ToProto(), 5408 }, 5409 }, nil 5410 } 5411 5412 func (t *TradingDataServiceV2) ListTeamMembersStatistics(ctx context.Context, req *v2.ListTeamMembersStatisticsRequest) (*v2.ListTeamMembersStatisticsResponse, error) { 5413 defer metrics.StartAPIRequestAndTimeGRPC("ListTeamMembersStatistics")() 5414 5415 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5416 if err != nil { 5417 return nil, formatE(ErrInvalidPagination, err) 5418 } 5419 5420 filters := sqlstore.ListTeamMembersStatisticsFilters{ 5421 TeamID: entities.TeamID(req.TeamId), 5422 AggregationEpochs: 10, 5423 } 5424 5425 if req.PartyId != nil { 5426 filters.PartyID = ptr.From(entities.PartyID(*req.PartyId)) 5427 } 5428 5429 if req.AggregationEpochs != nil && *req.AggregationEpochs > 0 { 5430 filters.AggregationEpochs = *req.AggregationEpochs 5431 } 5432 5433 stats, pageInfo, err := t.teamsService.ListTeamMembersStatistics(ctx, pagination, filters) 5434 if err != nil { 5435 return nil, formatE(ErrListTeamReferees, err) 5436 } 5437 5438 edges, err := makeEdges[*v2.TeamMemberStatisticsEdge](stats) 5439 if err != nil { 5440 return nil, formatE(err) 5441 } 5442 5443 return &v2.ListTeamMembersStatisticsResponse{ 5444 Statistics: &v2.TeamMembersStatisticsConnection{ 5445 Edges: edges, 5446 PageInfo: pageInfo.ToProto(), 5447 }, 5448 }, nil 5449 } 5450 5451 func (t *TradingDataServiceV2) ListTeamReferees(ctx context.Context, req *v2.ListTeamRefereesRequest) (*v2.ListTeamRefereesResponse, error) { 5452 defer metrics.StartAPIRequestAndTimeGRPC("ListTeamReferees")() 5453 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5454 if err != nil { 5455 return nil, formatE(ErrInvalidPagination, err) 5456 } 5457 5458 teamID := entities.TeamID(req.TeamId) 5459 5460 referees, pageInfo, err := t.teamsService.ListReferees(ctx, teamID, pagination) 5461 if err != nil { 5462 return nil, formatE(ErrListTeamReferees, err) 5463 } 5464 5465 edges, err := makeEdges[*v2.TeamRefereeEdge](referees) 5466 if err != nil { 5467 return nil, formatE(err) 5468 } 5469 5470 connection := &v2.TeamRefereeConnection{ 5471 Edges: edges, 5472 PageInfo: pageInfo.ToProto(), 5473 } 5474 5475 return &v2.ListTeamRefereesResponse{ 5476 TeamReferees: connection, 5477 }, nil 5478 } 5479 5480 func (t *TradingDataServiceV2) ListTeamRefereeHistory(ctx context.Context, req *v2.ListTeamRefereeHistoryRequest) (*v2.ListTeamRefereeHistoryResponse, error) { 5481 defer metrics.StartAPIRequestAndTimeGRPC("ListTeamRefereeHistory")() 5482 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5483 if err != nil { 5484 return nil, formatE(ErrInvalidPagination, err) 5485 } 5486 5487 refereeID := entities.PartyID(req.Referee) 5488 5489 history, pageInfo, err := t.teamsService.ListRefereeHistory(ctx, refereeID, pagination) 5490 if err != nil { 5491 return nil, formatE(ErrListTeamRefereeHistory, err) 5492 } 5493 5494 edges, err := makeEdges[*v2.TeamRefereeHistoryEdge](history) 5495 if err != nil { 5496 return nil, formatE(err) 5497 } 5498 5499 connection := &v2.TeamRefereeHistoryConnection{ 5500 Edges: edges, 5501 PageInfo: pageInfo.ToProto(), 5502 } 5503 5504 return &v2.ListTeamRefereeHistoryResponse{ 5505 TeamRefereeHistory: connection, 5506 }, nil 5507 } 5508 5509 func (t *TradingDataServiceV2) GetFeesStats(ctx context.Context, req *v2.GetFeesStatsRequest) (*v2.GetFeesStatsResponse, error) { 5510 if req.MarketId == nil && req.AssetId == nil { 5511 return nil, formatE(ErrFeesStatsRequest) 5512 } 5513 5514 var ( 5515 marketID *entities.MarketID 5516 assetID *entities.AssetID 5517 ) 5518 5519 if req.MarketId != nil { 5520 marketID = ptr.From(entities.MarketID(*req.MarketId)) 5521 } 5522 5523 if req.AssetId != nil { 5524 assetID = ptr.From(entities.AssetID(*req.AssetId)) 5525 } 5526 5527 stats, err := t.feesStatsService.GetFeesStats(ctx, marketID, assetID, req.EpochSeq, req.PartyId, req.EpochFrom, req.EpochTo) 5528 if err != nil { 5529 return nil, formatE(ErrGetFeesStats, err) 5530 } 5531 5532 return &v2.GetFeesStatsResponse{ 5533 FeesStats: stats.ToProto(), 5534 }, nil 5535 } 5536 5537 func (t *TradingDataServiceV2) GetFeesStatsForParty(ctx context.Context, req *v2.GetFeesStatsForPartyRequest) (*v2.GetFeesStatsForPartyResponse, error) { 5538 var assetID *entities.AssetID 5539 if req.AssetId != nil { 5540 assetID = ptr.From(entities.AssetID(*req.AssetId)) 5541 } 5542 5543 stats, err := t.feesStatsService.StatsForParty(ctx, entities.PartyID(req.PartyId), assetID, req.FromEpoch, req.ToEpoch) 5544 if err != nil { 5545 return nil, formatE(ErrGetFeesStatsForParty, err) 5546 } 5547 5548 statsProto := make([]*v2.FeesStatsForParty, 0, len(stats)) 5549 for _, s := range stats { 5550 statsProto = append(statsProto, &v2.FeesStatsForParty{ 5551 AssetId: s.AssetID.String(), 5552 TotalRewardsReceived: s.TotalRewardsReceived, 5553 RefereesDiscountApplied: s.RefereesDiscountApplied, 5554 VolumeDiscountApplied: s.VolumeDiscountApplied, 5555 TotalMakerFeesReceived: s.TotalMakerFeesReceived, 5556 }) 5557 } 5558 return &v2.GetFeesStatsForPartyResponse{ 5559 FeesStatsForParty: statsProto, 5560 }, nil 5561 } 5562 5563 func (t *TradingDataServiceV2) GetCurrentVolumeDiscountProgram(ctx context.Context, _ *v2.GetCurrentVolumeDiscountProgramRequest) ( 5564 *v2.GetCurrentVolumeDiscountProgramResponse, error, 5565 ) { 5566 defer metrics.StartAPIRequestAndTimeGRPC("GetCurrentVolumeDiscountProgram")() 5567 volumeDiscountProgram, err := t.volumeDiscountProgramService.GetCurrentVolumeDiscountProgram(ctx) 5568 if err != nil { 5569 return nil, formatE(ErrGetCurrentVolumeDiscountProgram, err) 5570 } 5571 5572 return &v2.GetCurrentVolumeDiscountProgramResponse{ 5573 CurrentVolumeDiscountProgram: volumeDiscountProgram.ToProto(), 5574 }, nil 5575 } 5576 5577 func (t *TradingDataServiceV2) GetCurrentVolumeRebateProgram(ctx context.Context, _ *v2.GetCurrentVolumeRebateProgramRequest) ( 5578 *v2.GetCurrentVolumeRebateProgramResponse, error, 5579 ) { 5580 defer metrics.StartAPIRequestAndTimeGRPC("GetCurrentVolumeRebateProgram")() 5581 volumeRebateProgram, err := t.volumeRebateProgramService.GetCurrentVolumeRebateProgram(ctx) 5582 if err != nil { 5583 return nil, formatE(ErrGetCurrentVolumeDiscountProgram, err) 5584 } 5585 5586 return &v2.GetCurrentVolumeRebateProgramResponse{ 5587 CurrentVolumeRebateProgram: volumeRebateProgram.ToProto(), 5588 }, nil 5589 } 5590 5591 func (t *TradingDataServiceV2) GetVolumeRebateStats(ctx context.Context, req *v2.GetVolumeRebateStatsRequest) ( 5592 *v2.GetVolumeRebateStatsResponse, error, 5593 ) { 5594 defer metrics.StartAPIRequestAndTimeGRPC("GetVolumeRebateStats")() 5595 5596 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5597 if err != nil { 5598 return nil, formatE(ErrInvalidPagination, err) 5599 } 5600 5601 stats, pageInfo, err := t.volumeRebateStatsService.Stats(ctx, req.AtEpoch, req.PartyId, pagination) 5602 if err != nil { 5603 return nil, formatE(ErrGetVolumeRebateStats, err) 5604 } 5605 5606 edges, err := makeEdges[*v2.VolumeRebateStatsEdge](stats) 5607 if err != nil { 5608 return nil, formatE(err) 5609 } 5610 5611 return &v2.GetVolumeRebateStatsResponse{ 5612 Stats: &v2.VolumeRebateStatsConnection{ 5613 Edges: edges, 5614 PageInfo: pageInfo.ToProto(), 5615 }, 5616 }, nil 5617 } 5618 5619 func (t *TradingDataServiceV2) GetVolumeDiscountStats(ctx context.Context, req *v2.GetVolumeDiscountStatsRequest) ( 5620 *v2.GetVolumeDiscountStatsResponse, error, 5621 ) { 5622 defer metrics.StartAPIRequestAndTimeGRPC("GetVolumeDiscountStats")() 5623 5624 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5625 if err != nil { 5626 return nil, formatE(ErrInvalidPagination, err) 5627 } 5628 5629 stats, pageInfo, err := t.VolumeDiscountStatsService.Stats(ctx, req.AtEpoch, req.PartyId, pagination) 5630 if err != nil { 5631 return nil, formatE(ErrGetVolumeDiscountStats, err) 5632 } 5633 5634 edges, err := makeEdges[*v2.VolumeDiscountStatsEdge](stats) 5635 if err != nil { 5636 return nil, formatE(err) 5637 } 5638 5639 return &v2.GetVolumeDiscountStatsResponse{ 5640 Stats: &v2.VolumeDiscountStatsConnection{ 5641 Edges: edges, 5642 PageInfo: pageInfo.ToProto(), 5643 }, 5644 }, nil 5645 } 5646 5647 // ObserveTransactionResults opens a subscription to the transaction results. 5648 func (t *TradingDataServiceV2) ObserveTransactionResults(req *v2.ObserveTransactionResultsRequest, srv v2.TradingDataService_ObserveTransactionResultsServer) error { 5649 // Wrap context from the request into cancellable. We can close internal chan on error. 5650 ctx, cancel := context.WithCancel(srv.Context()) 5651 defer cancel() 5652 5653 tradesChan, ref := t.transactionResults.Observe(ctx, t.config.StreamRetries, req.PartyIds, req.Hashes, req.Status) 5654 5655 if t.log.GetLevel() == logging.DebugLevel { 5656 t.log.Debug("Transaction results subscriber - new rpc stream", logging.Uint64("ref", ref)) 5657 } 5658 5659 return observeBatch(ctx, t.log, "TransactionResults", tradesChan, ref, func(results []events.TransactionResult) error { 5660 protos := make([]*eventspb.TransactionResult, 0, len(results)) 5661 for _, v := range results { 5662 p := v.Proto() 5663 protos = append(protos, &p) 5664 } 5665 5666 batches := batch(protos, snapshotPageSize) 5667 5668 for _, batch := range batches { 5669 response := &v2.ObserveTransactionResultsResponse{TransactionResults: batch} 5670 if err := srv.Send(response); err != nil { 5671 return errors.Wrap(err, "sending transaction results") 5672 } 5673 } 5674 return nil 5675 }) 5676 } 5677 5678 func (t *TradingDataServiceV2) EstimateTransferFee(ctx context.Context, req *v2.EstimateTransferFeeRequest) ( 5679 *v2.EstimateTransferFeeResponse, error, 5680 ) { 5681 defer metrics.StartAPIRequestAndTimeGRPC("EstimateTransferFee")() 5682 5683 amount, overflow := num.UintFromString(req.Amount, 10) 5684 if overflow || amount.IsNegative() { 5685 return nil, formatE(ErrInvalidTransferAmount) 5686 } 5687 5688 transferFeeMaxQuantumAmountParam, err := t.networkParameterService.GetByKey(ctx, netparams.TransferFeeMaxQuantumAmount) 5689 if err != nil { 5690 return nil, formatE(ErrGetNetworkParameters, errors.Wrapf(err, "key: %s", netparams.TransferFeeMaxQuantumAmount)) 5691 } 5692 5693 transferFeeMaxQuantumAmount, _ := num.DecimalFromString(transferFeeMaxQuantumAmountParam.Value) 5694 5695 transferFeeFactorParam, err := t.networkParameterService.GetByKey(ctx, netparams.TransferFeeFactor) 5696 if err != nil { 5697 return nil, formatE(ErrGetNetworkParameters, errors.Wrapf(err, "key: %s", netparams.TransferFeeFactor)) 5698 } 5699 5700 transferFeeFactor, _ := num.DecimalFromString(transferFeeFactorParam.Value) 5701 5702 asset, err := t.AssetService.GetByID(ctx, req.AssetId) 5703 if err != nil { 5704 return nil, formatE(ErrAssetServiceGetByID, err) 5705 } 5706 5707 // if we can't get a transfer fee discount, the discount should just be 0, i.e. no discount available 5708 transferFeesDiscount, err := t.transfersService.GetCurrentTransferFeeDiscount( 5709 ctx, 5710 entities.PartyID(req.FromAccount), 5711 entities.AssetID(req.AssetId), 5712 ) 5713 if err != nil { 5714 transferFeesDiscount = &entities.TransferFeesDiscount{ 5715 Amount: decimal.Zero, 5716 } 5717 } 5718 5719 accumulatedDiscount, _ := num.UintFromDecimal(transferFeesDiscount.Amount) 5720 5721 fee, discount := banking.EstimateFee( 5722 asset.Quantum, 5723 transferFeeMaxQuantumAmount, 5724 transferFeeFactor, 5725 amount, 5726 accumulatedDiscount, 5727 req.FromAccount, 5728 req.FromAccountType, 5729 req.FromAmmKey, 5730 req.ToAccount, 5731 // irrelvant here 5732 vega.AccountType_ACCOUNT_TYPE_UNSPECIFIED, 5733 ) 5734 5735 return &v2.EstimateTransferFeeResponse{ 5736 Fee: fee.String(), 5737 Discount: discount.String(), 5738 }, nil 5739 } 5740 5741 func (t *TradingDataServiceV2) GetTotalTransferFeeDiscount(ctx context.Context, req *v2.GetTotalTransferFeeDiscountRequest) ( 5742 *v2.GetTotalTransferFeeDiscountResponse, error, 5743 ) { 5744 defer metrics.StartAPIRequestAndTimeGRPC("GetTotalTransferFeeDiscount")() 5745 5746 transferFeesDiscount, err := t.transfersService.GetCurrentTransferFeeDiscount( 5747 ctx, 5748 entities.PartyID(req.PartyId), 5749 entities.AssetID(req.AssetId), 5750 ) 5751 if err != nil { 5752 return nil, formatE(ErrTransferServiceGetFeeDiscount, err) 5753 } 5754 5755 accumulatedDiscount, _ := num.UintFromDecimal(transferFeesDiscount.Amount) 5756 return &v2.GetTotalTransferFeeDiscountResponse{ 5757 TotalDiscount: accumulatedDiscount.String(), 5758 }, nil 5759 } 5760 5761 func (t *TradingDataServiceV2) ListGames(ctx context.Context, req *v2.ListGamesRequest) (*v2.ListGamesResponse, error) { 5762 defer metrics.StartAPIRequestAndTimeGRPC("ListGames")() 5763 5764 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5765 if err != nil { 5766 return nil, formatE(ErrInvalidPagination, err) 5767 } 5768 5769 var ( 5770 teamID *entities.TeamID 5771 partyID *entities.PartyID 5772 ) 5773 5774 if req.TeamId != nil { 5775 teamID = ptr.From(entities.TeamID(*req.TeamId)) 5776 } 5777 5778 if req.PartyId != nil { 5779 partyID = ptr.From(entities.PartyID(*req.PartyId)) 5780 } 5781 5782 games, pageInfo, err := t.gamesService.ListGames(ctx, req.GameId, req.EntityScope, req.EpochFrom, req.EpochTo, teamID, partyID, pagination) 5783 if err != nil { 5784 return nil, formatE(ErrListGames, err) 5785 } 5786 5787 edges, err := makeEdges[*v2.GameEdge](games) 5788 if err != nil { 5789 return nil, formatE(err) 5790 } 5791 return &v2.ListGamesResponse{ 5792 Games: &v2.GamesConnection{ 5793 Edges: edges, 5794 PageInfo: pageInfo.ToProto(), 5795 }, 5796 }, nil 5797 } 5798 5799 func (t *TradingDataServiceV2) ListPartyMarginModes(ctx context.Context, req *v2.ListPartyMarginModesRequest) (*v2.ListPartyMarginModesResponse, error) { 5800 defer metrics.StartAPIRequestAndTimeGRPC("ListPartyMarginModes")() 5801 5802 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5803 if err != nil { 5804 return nil, formatE(ErrInvalidPagination, err) 5805 } 5806 5807 filters := sqlstore.ListPartyMarginModesFilters{} 5808 if req.MarketId != nil { 5809 filters.MarketID = ptr.From(entities.MarketID(*req.MarketId)) 5810 } 5811 if req.PartyId != nil { 5812 filters.PartyID = ptr.From(entities.PartyID(*req.PartyId)) 5813 } 5814 5815 modes, pageInfo, err := t.marginModesService.ListPartyMarginModes(ctx, pagination, filters) 5816 if err != nil { 5817 return nil, formatE(ErrListPartyMarginModes, err) 5818 } 5819 5820 edges, err := makeEdges[*v2.PartyMarginModeEdge](modes) 5821 if err != nil { 5822 return nil, formatE(err) 5823 } 5824 5825 return &v2.ListPartyMarginModesResponse{ 5826 PartyMarginModes: &v2.PartyMarginModesConnection{ 5827 Edges: edges, 5828 PageInfo: pageInfo.ToProto(), 5829 }, 5830 }, nil 5831 } 5832 5833 func (t *TradingDataServiceV2) GetTimeWeightedNotionalPosition(ctx context.Context, req *v2.GetTimeWeightedNotionalPositionRequest) (*v2.GetTimeWeightedNotionalPositionResponse, error) { 5834 defer metrics.StartAPIRequestAndTimeGRPC("GetTimeWeightedNotionalPosition")() 5835 5836 if req.PartyId == "" { 5837 return nil, formatE(ErrInvalidPartyID) 5838 } 5839 5840 if req.AssetId == "" { 5841 return nil, formatE(ErrInvalidAssetID) 5842 } 5843 5844 if req.GameId == "" { 5845 return nil, formatE(ErrInvalidGameID) 5846 } 5847 5848 partyID := entities.PartyID(req.PartyId) 5849 assetID := entities.AssetID(req.AssetId) 5850 gameID := entities.GameID(req.GameId) 5851 5852 pos, err := t.twNotionalPositionService.Get(ctx, assetID, partyID, gameID, req.AtEpoch) 5853 if err != nil { 5854 return nil, formatE(ErrGetTimeWeightedNotionalPosition, err) 5855 } 5856 5857 return &v2.GetTimeWeightedNotionalPositionResponse{ 5858 TimeWeightedNotionalPosition: pos.ToProto(), 5859 }, nil 5860 } 5861 5862 func (t *TradingDataServiceV2) ListAMMs(ctx context.Context, req *v2.ListAMMsRequest) (*v2.ListAMMsResponse, error) { 5863 defer metrics.StartAPIRequestAndTimeGRPC("ListAMMs")() 5864 5865 pagination, err := entities.CursorPaginationFromProto(req.Pagination) 5866 if err != nil { 5867 return nil, formatE(ErrInvalidPagination, err) 5868 } 5869 5870 var ( 5871 pools []entities.AMMPool 5872 pageInfo entities.PageInfo 5873 ) 5874 5875 liveOnly := ptr.UnBox(req.LiveOnly) 5876 if liveOnly && req.Status != nil { 5877 return nil, formatE(ErrCannotFilterByStatusWhenLiveOnly) 5878 } 5879 5880 if req.AmmPartyId != nil { 5881 pools, pageInfo, err = t.AMMPoolService.ListBySubAccount(ctx, *req.AmmPartyId, liveOnly, pagination) 5882 } else if req.Id != nil { 5883 pools, pageInfo, err = t.AMMPoolService.ListByPool(ctx, *req.Id, liveOnly, pagination) 5884 } else if req.PartyId != nil && req.MarketId == nil && req.Status == nil { 5885 // keeping the cases where one parameter is set to change nothing about the behaviour, except for the combining of filters 5886 pools, pageInfo, err = t.AMMPoolService.ListByParty(ctx, *req.PartyId, liveOnly, pagination) 5887 } else if req.MarketId != nil && req.PartyId == nil && req.Status == nil { 5888 // same: keeping this here for consistency. 5889 pools, pageInfo, err = t.AMMPoolService.ListByMarket(ctx, *req.MarketId, liveOnly, pagination) 5890 } else if req.Status != nil && req.PartyId == nil && req.MarketId == nil { 5891 // again, this should be handled by the combined filter method 5892 pools, pageInfo, err = t.AMMPoolService.ListByStatus(ctx, entities.AMMStatus(*req.Status), pagination) 5893 } else if req.PartyId != nil || req.MarketId != nil || req.Status != nil { 5894 var status *entities.AMMStatus 5895 if req.Status != nil { 5896 status = ptr.From(entities.AMMStatus(*req.Status)) 5897 } 5898 pools, pageInfo, err = t.AMMPoolService.ListByPartyMarketStatus(ctx, req.PartyId, req.MarketId, status, liveOnly, pagination) 5899 } else { 5900 pools, pageInfo, err = t.AMMPoolService.ListAll(ctx, liveOnly, pagination) 5901 } 5902 5903 if err != nil { 5904 return nil, formatE(ErrListAMMPools, err) 5905 } 5906 5907 edges, err := makeEdges[*v2.AMMEdge](pools) 5908 if err != nil { 5909 return nil, formatE(err) 5910 } 5911 5912 return &v2.ListAMMsResponse{ 5913 Amms: &v2.AMMConnection{ 5914 Edges: edges, 5915 PageInfo: pageInfo.ToProto(), 5916 }, 5917 }, nil 5918 } 5919 5920 func (t *TradingDataServiceV2) EstimateAMMBounds(ctx context.Context, req *v2.EstimateAMMBoundsRequest) (*v2.EstimateAMMBoundsResponse, error) { 5921 defer metrics.StartAPIRequestAndTimeGRPC("EstimateAMMBounds")() 5922 5923 // validate the easy bits 5924 if req.MarketId == "" { 5925 return nil, formatE(ErrEmptyMissingMarketID) 5926 } 5927 5928 if req.UpperPrice == nil && req.LowerPrice == nil { 5929 return nil, formatE(ErrInvalidLowerPrice) 5930 } 5931 5932 // get the market details and extract all the factors for bits 5933 market, err := t.MarketsService.GetByID(ctx, req.MarketId) 5934 if err != nil { 5935 return nil, formatE(ErrInvalidMarketID, err) 5936 } 5937 5938 asset, err := t.getMarketAsset(ctx, market) 5939 if err != nil { 5940 return nil, formatE(ErrInvalidMarketID, err) 5941 } 5942 5943 riskFactors, initialMargin, slippage, priceFactor, positionFactor, err := t.getMarketFactors(ctx, market, asset) 5944 if err != nil { 5945 return nil, formatE(ErrEstimateAMMBounds) 5946 } 5947 5948 // now validate the parameters and scale to asset DP 5949 5950 basePrice, overflow := num.UintFromString(req.BasePrice, 10) 5951 if overflow || basePrice.IsNegative() { 5952 return nil, formatE(ErrInvalidBasePrice) 5953 } 5954 basePrice, _ = num.UintFromDecimal(basePrice.ToDecimal().Mul(priceFactor)) 5955 5956 var upperPrice *num.Uint 5957 if req.UpperPrice != nil { 5958 upperP, overflow := num.UintFromString(*req.UpperPrice, 10) 5959 if overflow || upperP.IsNegative() { 5960 return nil, formatE(ErrInvalidUpperPrice) 5961 } 5962 upperPrice, _ = num.UintFromDecimal(upperP.ToDecimal().Mul(priceFactor)) 5963 5964 if upperPrice.LTE(basePrice) { 5965 return nil, formatE(ErrInvalidUpperPrice) 5966 } 5967 } 5968 5969 var lowerPrice *num.Uint 5970 if req.LowerPrice != nil { 5971 lowerP, overflow := num.UintFromString(*req.LowerPrice, 10) 5972 if overflow || lowerP.IsNegative() { 5973 return nil, formatE(ErrInvalidLowerPrice) 5974 } 5975 lowerPrice, _ = num.UintFromDecimal(lowerP.ToDecimal().Mul(priceFactor)) 5976 5977 if lowerPrice.GTE(basePrice) { 5978 return nil, formatE(ErrInvalidLowerPrice) 5979 } 5980 } 5981 5982 var leverageLowerPrice, leverageUpperPrice *num.Decimal 5983 if req.LeverageAtLowerPrice != nil { 5984 llPrice, err := num.DecimalFromString(*req.LeverageAtLowerPrice) 5985 if err != nil || llPrice.IsNegative() { 5986 return nil, formatE(ErrInvalidLeverageAtLowerPrice, err) 5987 } 5988 leverageLowerPrice = &llPrice 5989 } 5990 5991 if req.LeverageAtUpperPrice != nil { 5992 luPrice, err := num.DecimalFromString(*req.LeverageAtUpperPrice) 5993 if err != nil || luPrice.IsNegative() { 5994 return nil, formatE(ErrInvalidLeverageAtUpperPrice, err) 5995 } 5996 leverageUpperPrice = &luPrice 5997 } 5998 5999 commitmentAmount, overflow := num.UintFromString(req.CommitmentAmount, 10) 6000 if overflow || commitmentAmount.IsNegative() { 6001 return nil, formatE(ErrInvalidCommitmentAmount) 6002 } 6003 6004 if leverageLowerPrice == nil { 6005 leverageLowerPrice = &riskFactors.Short 6006 } 6007 if leverageUpperPrice == nil { 6008 leverageUpperPrice = &riskFactors.Long 6009 } 6010 6011 sqrt := amm.NewSqrter() 6012 6013 estimatedBounds := amm.EstimateBounds( 6014 sqrt, 6015 lowerPrice, 6016 basePrice, 6017 upperPrice, 6018 *leverageLowerPrice, 6019 *leverageUpperPrice, 6020 commitmentAmount, 6021 slippage, 6022 initialMargin, 6023 riskFactors.Short, 6024 riskFactors.Long, 6025 priceFactor, 6026 positionFactor, 6027 market.AllowedEmptyAMMLevels, 6028 ) 6029 6030 // liquidation prices are in asset DP, convert back to market DP 6031 lowerLiq := estimatedBounds.LiquidationPriceAtLower.Div(priceFactor).Truncate(0) 6032 upperLiq := estimatedBounds.LiquidationPriceAtUpper.Div(priceFactor).Truncate(0) 6033 6034 var status *v2.EstimateAMMBoundsResponse_AMMError 6035 switch { 6036 case estimatedBounds.TooWideLower && estimatedBounds.TooWideUpper: 6037 status = ptr.From(v2.EstimateAMMBoundsResponse_AMM_ERROR_BOTH_BOUNDS_TOO_WIDE) 6038 case estimatedBounds.TooWideLower: 6039 status = ptr.From(v2.EstimateAMMBoundsResponse_AMM_ERROR_LOWER_BOUND_TOO_WIDE) 6040 case estimatedBounds.TooWideUpper: 6041 status = ptr.From(v2.EstimateAMMBoundsResponse_AMM_ERROR_UPPER_BOUND_TOO_WIDE) 6042 } 6043 6044 np, err := t.networkParameterService.GetByKey(ctx, netparams.MarketAMMMinCommitmentQuantum) 6045 if err != nil { 6046 return nil, formatE(ErrEstimateAMMBounds) 6047 } 6048 6049 minQuantum, _ := num.DecimalFromString(np.Value) 6050 quantumCommitment := commitmentAmount.ToDecimal().Div(asset.Quantum) 6051 6052 if quantumCommitment.LessThan(minQuantum) { 6053 status = ptr.From(v2.EstimateAMMBoundsResponse_AMM_ERROR_COMMITMENT_BELOW_MINIMUM) 6054 } 6055 6056 return &v2.EstimateAMMBoundsResponse{ 6057 PositionSizeAtUpper: estimatedBounds.PositionSizeAtUpper.Truncate(0).String(), 6058 PositionSizeAtLower: estimatedBounds.PositionSizeAtLower.Truncate(0).String(), 6059 LossOnCommitmentAtUpper: estimatedBounds.LossOnCommitmentAtUpper.Truncate(0).String(), 6060 LossOnCommitmentAtLower: estimatedBounds.LossOnCommitmentAtLower.Truncate(0).String(), 6061 LiquidationPriceAtUpper: upperLiq.String(), 6062 LiquidationPriceAtLower: lowerLiq.String(), 6063 AmmError: status, 6064 }, nil 6065 }