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  }