code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/volume_discount_stats.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 sqlstore
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"strings"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/datanode/entities"
    26  	"code.vegaprotocol.io/vega/datanode/metrics"
    27  	v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2"
    28  	"code.vegaprotocol.io/vega/protos/vega"
    29  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    30  
    31  	"github.com/georgysavva/scany/pgxscan"
    32  )
    33  
    34  var volumeDiscountStatsOrdering = TableOrdering{
    35  	ColumnOrdering{Name: "at_epoch", Sorting: DESC},
    36  	ColumnOrdering{Name: "party_id", Sorting: ASC, Ref: "stats->>'party_id'"},
    37  }
    38  
    39  type (
    40  	VolumeDiscountStats struct {
    41  		*ConnectionSource
    42  	}
    43  )
    44  
    45  func NewVolumeDiscountStats(connectionSource *ConnectionSource) *VolumeDiscountStats {
    46  	return &VolumeDiscountStats{
    47  		ConnectionSource: connectionSource,
    48  	}
    49  }
    50  
    51  func (s *VolumeDiscountStats) Add(ctx context.Context, stats *entities.VolumeDiscountStats) error {
    52  	defer metrics.StartSQLQuery("VolumeDiscountStats", "Add")()
    53  	_, err := s.Exec(
    54  		ctx,
    55  		`INSERT INTO volume_discount_stats(at_epoch, parties_volume_discount_stats, vega_time)
    56  			values ($1, $2, $3)`,
    57  		stats.AtEpoch,
    58  		stats.PartiesVolumeDiscountStats,
    59  		stats.VegaTime,
    60  	)
    61  
    62  	return err
    63  }
    64  
    65  func (s *VolumeDiscountStats) LatestStats(ctx context.Context, partyID string) (entities.VolumeDiscountStats, error) {
    66  	query := `SELECT * FROM volume_discount_stats WHERE
    67  	at_epoch = (SELECT id - 1 from current_epochs ORDER BY id DESC, vega_time DESC FETCH FIRST ROW ONLY) AND
    68  	EXISTS (SELECT TRUE FROM jsonb_array_elements(parties_volume_discount_stats) ps WHERE ps->>'party_id' = '$1')`
    69  	ent := []entities.VolumeDiscountStats{}
    70  	if err := pgxscan.Select(ctx, s.ConnectionSource, &ent, query, partyID); err != nil {
    71  		return entities.VolumeDiscountStats{}, err
    72  	}
    73  	if len(ent) == 0 {
    74  		return entities.VolumeDiscountStats{}, nil
    75  	}
    76  	final := ent[0]
    77  	stats := make([]*eventspb.PartyVolumeDiscountStats, 0, len(ent))
    78  	for _, e := range ent {
    79  		for _, stat := range e.PartiesVolumeDiscountStats {
    80  			if stat.PartyId == partyID {
    81  				stats = append(stats, stat)
    82  			}
    83  		}
    84  	}
    85  	// running volume and discount factor to be used to match the volume discount program
    86  	final.PartiesVolumeDiscountStats = stats
    87  	return final, nil
    88  }
    89  
    90  func (s *VolumeDiscountStats) Stats(ctx context.Context, atEpoch *uint64, partyID *string, pagination entities.CursorPagination) ([]entities.FlattenVolumeDiscountStats, entities.PageInfo, error) {
    91  	defer metrics.StartSQLQuery("VolumeDiscountStats", "VolumeDiscountStats")()
    92  
    93  	var (
    94  		args     []any
    95  		pageInfo entities.PageInfo
    96  	)
    97  
    98  	filters := []string{}
    99  	filters = append(filters, "jsonb_typeof(parties_volume_discount_stats) != 'null'")
   100  
   101  	if atEpoch != nil {
   102  		filters = append(filters, fmt.Sprintf("at_epoch = %s", nextBindVar(&args, atEpoch)))
   103  	}
   104  	if partyID != nil {
   105  		filters = append(filters, fmt.Sprintf("stats->>'party_id' = %s", nextBindVar(&args, partyID)))
   106  	}
   107  
   108  	if partyID == nil && atEpoch == nil {
   109  		filters = append(filters, "at_epoch = (SELECT MAX(at_epoch) FROM volume_discount_stats)")
   110  	}
   111  
   112  	stats := []struct {
   113  		AtEpoch         uint64
   114  		PartyID         string
   115  		RunningVolume   string
   116  		DiscountFactors string
   117  		VegaTime        time.Time
   118  	}{}
   119  	query := `select at_epoch, stats->>'party_id' as party_id, stats->>'running_volume' as running_volume, stats->>'discount_factors' as discount_factors, vega_time from volume_discount_stats, jsonb_array_elements(parties_volume_discount_stats) AS stats`
   120  
   121  	if len(filters) > 0 {
   122  		query = fmt.Sprintf("%s where %s", query, strings.Join(filters, " and "))
   123  	}
   124  
   125  	query, args, err := PaginateQuery[entities.VolumeDiscountStatsCursor](query, args, volumeDiscountStatsOrdering, pagination)
   126  	if err != nil {
   127  		return nil, pageInfo, err
   128  	}
   129  
   130  	if err := pgxscan.Select(ctx, s.ConnectionSource, &stats, query, args...); err != nil {
   131  		return nil, pageInfo, err
   132  	}
   133  
   134  	flattenStats := []entities.FlattenVolumeDiscountStats{}
   135  	for _, stat := range stats {
   136  		discountFactors := &vega.DiscountFactors{}
   137  		if err := json.Unmarshal([]byte(stat.DiscountFactors), discountFactors); err != nil {
   138  			return nil, pageInfo, err
   139  		}
   140  
   141  		flattenStats = append(flattenStats, entities.FlattenVolumeDiscountStats{
   142  			AtEpoch:         stat.AtEpoch,
   143  			PartyID:         stat.PartyID,
   144  			DiscountFactors: discountFactors,
   145  			RunningVolume:   stat.RunningVolume,
   146  			VegaTime:        stat.VegaTime,
   147  		})
   148  	}
   149  
   150  	flattenStats, pageInfo = entities.PageEntities[*v2.VolumeDiscountStatsEdge](flattenStats, pagination)
   151  
   152  	return flattenStats, pageInfo, nil
   153  }