code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/paid_liquidity_fee_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 "fmt" 21 "strings" 22 23 "code.vegaprotocol.io/vega/datanode/entities" 24 "code.vegaprotocol.io/vega/datanode/metrics" 25 v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2" 26 27 "github.com/georgysavva/scany/pgxscan" 28 ) 29 30 type PaidLiquidityFeesStats struct { 31 *ConnectionSource 32 } 33 34 func NewPaidLiquidityFeesStats(src *ConnectionSource) *PaidLiquidityFeesStats { 35 return &PaidLiquidityFeesStats{ 36 ConnectionSource: src, 37 } 38 } 39 40 func (rfs *PaidLiquidityFeesStats) Add(ctx context.Context, stats *entities.PaidLiquidityFeesStats) error { 41 defer metrics.StartSQLQuery("PaidLiquidityFeesStats", "Add")() 42 // It's possible that a market closes in the same block as an end of epoch event. 43 // In this case, the market close event will cause a paid liquidity fee stats event to be sent 44 // as well as the epoch end event. In this case we just want to ignore the second event. 45 _, err := rfs.Exec( 46 ctx, 47 `INSERT INTO paid_liquidity_fees( 48 market_id, 49 asset_id, 50 epoch_seq, 51 total_fees_paid, 52 fees_paid_per_party, 53 vega_time 54 ) values ($1,$2,$3,$4,$5,$6) ON CONFLICT DO NOTHING`, 55 stats.MarketID, 56 stats.AssetID, 57 stats.EpochSeq, 58 stats.TotalFeesPaid, 59 stats.FeesPerParty, 60 stats.VegaTime, 61 ) 62 return err 63 } 64 65 func (lfs *PaidLiquidityFeesStats) List( 66 ctx context.Context, 67 marketID *entities.MarketID, 68 assetID *entities.AssetID, 69 epochSeq *uint64, 70 partyIDs []string, 71 pagination entities.CursorPagination, 72 epochFrom, epochTo *uint64, 73 ) ([]entities.PaidLiquidityFeesStats, entities.PageInfo, error) { 74 defer metrics.StartSQLQuery("PaidLiquidityFeesStats", "List")() 75 var ( 76 args []interface{} 77 pageInfo entities.PageInfo 78 ) 79 80 query := `SELECT t.market_id, t.asset_id, t.epoch_seq, t.total_fees_paid, array_to_json(array_agg(j)) as fees_per_party 81 FROM paid_liquidity_fees t, jsonb_array_elements(t.fees_paid_per_party) j` 82 83 whereClauses := []string{} 84 85 if (marketID == nil || assetID == nil) && epochSeq == nil && epochFrom == nil && epochTo == nil { 86 whereClauses = append(whereClauses, "epoch_seq = (SELECT MAX(epoch_seq) FROM paid_liquidity_fees)") 87 } 88 89 // to from range set, but wrong way around 90 if epochFrom != nil && epochTo != nil && *epochFrom > *epochTo { 91 epochTo, epochFrom = epochFrom, epochTo 92 } 93 if epochFrom != nil { 94 whereClauses = append(whereClauses, fmt.Sprintf("epoch_seq >= %s", nextBindVar(&args, *epochFrom))) 95 } 96 if epochTo != nil { 97 whereClauses = append(whereClauses, fmt.Sprintf("epoch_seq <= %s", nextBindVar(&args, *epochTo))) 98 } 99 // @TODO remove precise epoch sequence? 100 if epochFrom == nil && epochTo == nil && epochSeq != nil { 101 whereClauses = append(whereClauses, fmt.Sprintf("epoch_seq = %s", nextBindVar(&args, *epochSeq))) 102 } 103 104 if marketID != nil { 105 whereClauses = append(whereClauses, fmt.Sprintf("market_id = %s", nextBindVar(&args, marketID))) 106 } 107 108 if assetID != nil { 109 whereClauses = append(whereClauses, fmt.Sprintf("asset_id = %s", nextBindVar(&args, assetID))) 110 } 111 112 if len(partyIDs) > 0 { 113 parties := strings.Builder{} 114 for i, party := range partyIDs { 115 if i > 0 { 116 parties.WriteString(",") 117 } 118 parties.WriteString(nextBindVar(&args, party)) 119 } 120 121 whereClauses = append(whereClauses, fmt.Sprintf("j->>'party' IN (%s)", parties.String())) 122 } 123 124 var whereStr string 125 if len(whereClauses) > 0 { 126 whereStr = " WHERE " + strings.Join(whereClauses, " AND ") 127 } 128 129 groupByStr := "GROUP BY market_id, asset_id, epoch_seq, vega_time" 130 131 query = fmt.Sprintf("%s %s %s", query, whereStr, groupByStr) 132 133 stats := []entities.PaidLiquidityFeesStats{} 134 135 query, args, err := PaginateQuery[entities.PaidLiquidityFeesStatsCursor]( 136 query, args, paidLiquidityFeesStatsCursorOrdering, pagination) 137 if err != nil { 138 return nil, pageInfo, err 139 } 140 141 if err := pgxscan.Select(ctx, lfs.ConnectionSource, &stats, query, args...); err != nil { 142 return nil, pageInfo, err 143 } 144 145 stats, pageInfo = entities.PageEntities[*v2.PaidLiquidityFeesEdge](stats, pagination) 146 147 return stats, pageInfo, nil 148 }