code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/rewards_test.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_test 17 18 import ( 19 "context" 20 "encoding/hex" 21 "fmt" 22 "math/rand" 23 "reflect" 24 "slices" 25 "strings" 26 "testing" 27 "time" 28 29 "code.vegaprotocol.io/vega/datanode/entities" 30 "code.vegaprotocol.io/vega/datanode/sqlstore" 31 "code.vegaprotocol.io/vega/libs/num" 32 "code.vegaprotocol.io/vega/libs/ptr" 33 34 "github.com/georgysavva/scany/pgxscan" 35 "github.com/google/go-cmp/cmp" 36 "github.com/google/go-cmp/cmp/cmpopts" 37 "github.com/shopspring/decimal" 38 "github.com/stretchr/testify/assert" 39 "github.com/stretchr/testify/require" 40 ) 41 42 func addTestReward(t *testing.T, 43 ctx context.Context, 44 rs *sqlstore.Rewards, 45 party entities.Party, 46 asset entities.Asset, 47 marketID entities.MarketID, 48 epochID int64, 49 rewardType string, 50 timestamp time.Time, 51 block entities.Block, 52 seqNum uint64, 53 amount num.Decimal, 54 txHash entities.TxHash, 55 gameID *entities.GameID, 56 ) entities.Reward { 57 t.Helper() 58 r := entities.Reward{ 59 PartyID: party.ID, 60 AssetID: asset.ID, 61 MarketID: marketID, 62 RewardType: rewardType, 63 EpochID: epochID, 64 Amount: amount, 65 QuantumAmount: amount, 66 PercentOfTotal: 0.2, 67 Timestamp: timestamp.Truncate(time.Microsecond), 68 VegaTime: block.VegaTime, 69 SeqNum: seqNum, 70 TxHash: txHash, 71 GameID: gameID, 72 } 73 require.NoError(t, rs.Add(ctx, r)) 74 return r 75 } 76 77 func rewardLessThan(x, y entities.Reward) bool { 78 if x.EpochID != y.EpochID { 79 return x.EpochID < y.EpochID 80 } 81 if x.PartyID.String() != y.PartyID.String() { 82 return x.PartyID.String() < y.PartyID.String() 83 } 84 if x.AssetID.String() != y.AssetID.String() { 85 return x.AssetID.String() < y.AssetID.String() 86 } 87 return x.Amount.LessThan(y.Amount) 88 } 89 90 func assertRewardsMatch(t *testing.T, expected, actual []entities.Reward) { 91 t.Helper() 92 assert.Empty(t, cmp.Diff(expected, actual, cmpopts.SortSlices(rewardLessThan))) 93 } 94 95 func TestRewards(t *testing.T) { 96 ctx := tempTransaction(t) 97 98 ps := sqlstore.NewParties(connectionSource) 99 as := sqlstore.NewAssets(connectionSource) 100 rs := sqlstore.NewRewards(ctx, connectionSource) 101 bs := sqlstore.NewBlocks(connectionSource) 102 block := addTestBlock(t, ctx, bs) 103 104 asset1 := addTestAsset(t, ctx, as, block) 105 asset2 := addTestAsset(t, ctx, as, block) 106 107 market1 := entities.MarketID("deadbeef") 108 market2 := entities.MarketID("") 109 party1 := addTestParty(t, ctx, ps, block) 110 party2 := addTestParty(t, ctx, ps, block) 111 112 party1ID := party1.ID.String() 113 party2ID := party2.ID.String() 114 asset2ID := asset2.ID.String() 115 116 now := time.Now() 117 amount := num.DecimalFromInt64(100) 118 reward1 := addTestReward(t, ctx, rs, party1, asset1, market1, 1, "RewardMakerPaidFees", now, block, 1, amount, generateTxHash(), nil) 119 reward2 := addTestReward(t, ctx, rs, party1, asset2, market1, 2, "RewardMakerReceivedFees", now, block, 2, amount, generateTxHash(), nil) 120 reward3 := addTestReward(t, ctx, rs, party2, asset1, market2, 3, "GlobalReward", now, block, 3, amount, generateTxHash(), nil) 121 reward4 := addTestReward(t, ctx, rs, party2, asset2, market2, 4, "GlobalReward", now, block, 4, amount, generateTxHash(), nil) 122 reward5 := addTestReward(t, ctx, rs, party2, asset2, market2, 5, "GlobalReward", now, block, 5, amount, generateTxHash(), nil) 123 124 t.Run("GetAll", func(t *testing.T) { 125 expected := []entities.Reward{reward1, reward2, reward3, reward4, reward5} 126 actual, err := rs.GetAll(ctx) 127 require.NoError(t, err) 128 assertRewardsMatch(t, expected, actual) 129 }) 130 131 t.Run("GetByTxHash", func(t *testing.T) { 132 expected := []entities.Reward{reward2} 133 actual, err := rs.GetByTxHash(ctx, reward2.TxHash) 134 require.NoError(t, err) 135 assertRewardsMatch(t, expected, actual) 136 }) 137 138 t.Run("GetSummary", func(t *testing.T) { 139 expected := []entities.RewardSummary{ 140 { 141 AssetID: asset2.ID, 142 PartyID: party1.ID, 143 Amount: decimal.NewFromInt(100), 144 }, 145 { 146 AssetID: asset2.ID, 147 PartyID: party2.ID, 148 Amount: decimal.NewFromInt(200), 149 }, 150 } 151 152 slices.SortFunc(expected, func(i, j entities.RewardSummary) int { 153 return strings.Compare(i.PartyID.String(), j.PartyID.String()) 154 }) 155 156 actual, err := rs.GetSummaries(ctx, []string{party1ID, party2ID}, &asset2ID) 157 require.NoError(t, err) 158 assert.Equal(t, expected, actual) 159 }) 160 161 t.Run("GetByCursor", func(t *testing.T) { 162 expected := []entities.Reward{reward2, reward4, reward5} 163 164 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 165 require.NoError(t, err) 166 167 actual, _, err := rs.GetByCursor(ctx, []string{party1ID, party2ID}, &asset2ID, nil, nil, pagination, nil, nil, nil) 168 require.NoError(t, err) 169 assert.Equal(t, expected, actual) 170 }) 171 } 172 173 func TestEpochRewardSummary(t *testing.T) { 174 ctx := tempTransaction(t) 175 176 ps := sqlstore.NewParties(connectionSource) 177 as := sqlstore.NewAssets(connectionSource) 178 rs := sqlstore.NewRewards(ctx, connectionSource) 179 bs := sqlstore.NewBlocks(connectionSource) 180 block := addTestBlock(t, ctx, bs) 181 182 asset1 := addTestAsset(t, ctx, as, block) 183 asset2 := addTestAsset(t, ctx, as, block) 184 185 market1 := entities.MarketID("deadbeef") 186 market2 := entities.MarketID("") 187 party1 := addTestParty(t, ctx, ps, block) 188 party2 := addTestParty(t, ctx, ps, block) 189 party3 := addTestParty(t, ctx, ps, block) 190 191 now := time.Now() 192 // rewards for epoch1 193 addTestReward(t, ctx, rs, party1, asset1, market1, 1, "RewardMakerPaidFees", now, block, 1, num.DecimalFromInt64(100), generateTxHash(), nil) 194 addTestReward(t, ctx, rs, party2, asset1, market1, 1, "RewardMakerPaidFees", now, block, 2, num.DecimalFromInt64(200), generateTxHash(), nil) 195 addTestReward(t, ctx, rs, party3, asset1, market1, 1, "RewardMakerPaidFees", now, block, 3, num.DecimalFromInt64(300), generateTxHash(), nil) 196 addTestReward(t, ctx, rs, party1, asset1, market2, 1, "RewardMakerPaidFees", now, block, 4, num.DecimalFromInt64(110), generateTxHash(), nil) 197 addTestReward(t, ctx, rs, party2, asset1, market2, 1, "RewardMakerPaidFees", now, block, 5, num.DecimalFromInt64(220), generateTxHash(), nil) 198 addTestReward(t, ctx, rs, party3, asset1, market2, 1, "RewardMakerPaidFees", now, block, 6, num.DecimalFromInt64(330), generateTxHash(), nil) 199 addTestReward(t, ctx, rs, party1, asset2, market1, 1, "RewardMakerPaidFees", now, block, 7, num.DecimalFromInt64(400), generateTxHash(), nil) 200 addTestReward(t, ctx, rs, party2, asset2, market1, 1, "RewardMakerPaidFees", now, block, 8, num.DecimalFromInt64(500), generateTxHash(), nil) 201 addTestReward(t, ctx, rs, party3, asset2, market1, 1, "RewardMakerPaidFees", now, block, 9, num.DecimalFromInt64(600), generateTxHash(), nil) 202 addTestReward(t, ctx, rs, party1, asset2, market2, 1, "RewardMakerPaidFees", now, block, 10, num.DecimalFromInt64(410), generateTxHash(), nil) 203 addTestReward(t, ctx, rs, party2, asset2, market2, 1, "RewardMakerPaidFees", now, block, 11, num.DecimalFromInt64(520), generateTxHash(), nil) 204 addTestReward(t, ctx, rs, party3, asset2, market2, 1, "RewardMakerPaidFees", now, block, 12, num.DecimalFromInt64(630), generateTxHash(), nil) 205 addTestReward(t, ctx, rs, party1, asset1, market1, 1, "RewardMakerReceivedFees", now, block, 13, num.DecimalFromInt64(1000), generateTxHash(), nil) 206 addTestReward(t, ctx, rs, party2, asset1, market1, 1, "RewardMakerReceivedFees", now, block, 14, num.DecimalFromInt64(2000), generateTxHash(), nil) 207 addTestReward(t, ctx, rs, party3, asset1, market1, 1, "RewardMakerReceivedFees", now, block, 15, num.DecimalFromInt64(3000), generateTxHash(), nil) 208 addTestReward(t, ctx, rs, party1, asset1, market2, 1, "GlobalReward", now, block, 16, num.DecimalFromInt64(1100), generateTxHash(), nil) 209 addTestReward(t, ctx, rs, party2, asset1, market2, 1, "GlobalReward", now, block, 17, num.DecimalFromInt64(2200), generateTxHash(), nil) 210 addTestReward(t, ctx, rs, party3, asset1, market2, 1, "GlobalReward", now, block, 18, num.DecimalFromInt64(3300), generateTxHash(), nil) 211 addTestReward(t, ctx, rs, party1, asset2, market1, 1, "RewardMakerReceivedFees", now, block, 19, num.DecimalFromInt64(4000), generateTxHash(), nil) 212 addTestReward(t, ctx, rs, party2, asset2, market1, 1, "RewardMakerReceivedFees", now, block, 20, num.DecimalFromInt64(5000), generateTxHash(), nil) 213 addTestReward(t, ctx, rs, party3, asset2, market1, 1, "RewardMakerReceivedFees", now, block, 21, num.DecimalFromInt64(6000), generateTxHash(), nil) 214 addTestReward(t, ctx, rs, party1, asset2, market2, 1, "GlobalReward", now, block, 22, num.DecimalFromInt64(4100), generateTxHash(), nil) 215 addTestReward(t, ctx, rs, party2, asset2, market2, 1, "GlobalReward", now, block, 23, num.DecimalFromInt64(5200), generateTxHash(), nil) 216 addTestReward(t, ctx, rs, party3, asset2, market2, 1, "GlobalReward", now, block, 24, num.DecimalFromInt64(6300), generateTxHash(), nil) 217 218 // rewards for epoch2 219 addTestReward(t, ctx, rs, party1, asset1, market1, 2, "RewardMakerPaidFees", now, block, 25, num.DecimalFromInt64(10000), generateTxHash(), nil) 220 addTestReward(t, ctx, rs, party2, asset1, market1, 2, "RewardMakerPaidFees", now, block, 26, num.DecimalFromInt64(20000), generateTxHash(), nil) 221 addTestReward(t, ctx, rs, party3, asset1, market1, 2, "RewardMakerPaidFees", now, block, 27, num.DecimalFromInt64(30000), generateTxHash(), nil) 222 addTestReward(t, ctx, rs, party1, asset1, market2, 2, "RewardMakerPaidFees", now, block, 28, num.DecimalFromInt64(11000), generateTxHash(), nil) 223 addTestReward(t, ctx, rs, party2, asset1, market2, 2, "RewardMakerPaidFees", now, block, 29, num.DecimalFromInt64(22000), generateTxHash(), nil) 224 addTestReward(t, ctx, rs, party3, asset1, market2, 2, "RewardMakerPaidFees", now, block, 30, num.DecimalFromInt64(33000), generateTxHash(), nil) 225 addTestReward(t, ctx, rs, party1, asset2, market1, 2, "RewardMakerPaidFees", now, block, 31, num.DecimalFromInt64(40000), generateTxHash(), nil) 226 addTestReward(t, ctx, rs, party2, asset2, market1, 2, "RewardMakerPaidFees", now, block, 32, num.DecimalFromInt64(50000), generateTxHash(), nil) 227 addTestReward(t, ctx, rs, party3, asset2, market1, 2, "RewardMakerPaidFees", now, block, 33, num.DecimalFromInt64(60000), generateTxHash(), nil) 228 addTestReward(t, ctx, rs, party1, asset2, market2, 2, "RewardMakerPaidFees", now, block, 34, num.DecimalFromInt64(41000), generateTxHash(), nil) 229 addTestReward(t, ctx, rs, party2, asset2, market2, 2, "RewardMakerPaidFees", now, block, 35, num.DecimalFromInt64(52000), generateTxHash(), nil) 230 addTestReward(t, ctx, rs, party3, asset2, market2, 2, "RewardMakerPaidFees", now, block, 36, num.DecimalFromInt64(63000), generateTxHash(), nil) 231 addTestReward(t, ctx, rs, party1, asset1, market1, 2, "RewardMakerReceivedFees", now, block, 37, num.DecimalFromInt64(100000), generateTxHash(), nil) 232 addTestReward(t, ctx, rs, party2, asset1, market1, 2, "RewardMakerReceivedFees", now, block, 38, num.DecimalFromInt64(200000), generateTxHash(), nil) 233 addTestReward(t, ctx, rs, party3, asset1, market1, 2, "RewardMakerReceivedFees", now, block, 39, num.DecimalFromInt64(300000), generateTxHash(), nil) 234 addTestReward(t, ctx, rs, party1, asset1, market2, 2, "GlobalReward", now, block, 40, num.DecimalFromInt64(110000), generateTxHash(), nil) 235 addTestReward(t, ctx, rs, party2, asset1, market2, 2, "GlobalReward", now, block, 41, num.DecimalFromInt64(220000), generateTxHash(), nil) 236 addTestReward(t, ctx, rs, party3, asset1, market2, 2, "GlobalReward", now, block, 42, num.DecimalFromInt64(330000), generateTxHash(), nil) 237 addTestReward(t, ctx, rs, party1, asset2, market1, 2, "RewardMakerReceivedFees", now, block, 43, num.DecimalFromInt64(400000), generateTxHash(), nil) 238 addTestReward(t, ctx, rs, party2, asset2, market1, 2, "RewardMakerReceivedFees", now, block, 44, num.DecimalFromInt64(500000), generateTxHash(), nil) 239 addTestReward(t, ctx, rs, party3, asset2, market1, 2, "RewardMakerReceivedFees", now, block, 45, num.DecimalFromInt64(600000), generateTxHash(), nil) 240 addTestReward(t, ctx, rs, party1, asset2, market2, 2, "GlobalReward", now, block, 46, num.DecimalFromInt64(410000), generateTxHash(), nil) 241 addTestReward(t, ctx, rs, party2, asset2, market2, 2, "GlobalReward", now, block, 47, num.DecimalFromInt64(520000), generateTxHash(), nil) 242 addTestReward(t, ctx, rs, party3, asset2, market2, 2, "GlobalReward", now, block, 48, num.DecimalFromInt64(630000), generateTxHash(), nil) 243 244 first := int32(1000) 245 pagination, _ := entities.NewCursorPagination(&first, nil, nil, nil, false) 246 filter := entities.RewardSummaryFilter{} 247 summaries, _, err := rs.GetEpochSummaries(ctx, filter, pagination) 248 require.NoError(t, err) 249 250 // we expect to get all sumarries because we defined no from/to 251 // so 16 summaries 252 // epoch1 / asset1 / market1 / RewardMakerPaidFees = 600 253 // epoch1 / asset1 / market2 / RewardMakerPaidFees = 660 254 // epoch1 / asset2 / market1 / RewardMakerPaidFees = 1500 255 // epoch1 / asset2 / market2 / RewardMakerPaidFees = 1560 256 // epoch1 / asset1 / market1 / RewardMakerReceivedFees = 6000 257 // epoch1 / asset1 / market2 / GlobalReward = 6600 258 // epoch1 / asset2 / market1 / RewardMakerPaidFees = 15000 259 // epoch1 / asset2 / market2 / GlobalReward = 15600 260 261 // epoch2 / asset1 / market1 / RewardMakerPaidFees = 60000 262 // epoch2 / asset1 / market2 / RewardMakerPaidFees = 66000 263 // epoch2 / asset2 / market1 / RewardMakerPaidFees = 150000 264 // epoch2 / asset2 / market2 / RewardMakerPaidFees = 156000 265 // epoch2 / asset1 / market1 / RewardMakerReceivedFees = 600000 266 // epoch2 / asset1 / market2 / GlobalReward = 660000 267 // epoch2 / asset2 / market1 / RewardMakerPaidFees = 1500000 268 // epoch2 / asset2 / market2 / GlobalReward = 1560000 269 270 require.Equal(t, 16, len(summaries)) 271 verifyRewardsForEpoch(t, summaries, 1, asset1.ID.String(), asset2.ID.String()) 272 verifyRewardsForEpoch(t, summaries, 2, asset1.ID.String(), asset2.ID.String()) 273 274 // now request with from = 1 with no to, expect the same result 275 from := uint64(1) 276 filter = entities.RewardSummaryFilter{FromEpoch: &from} 277 summaries, _, _ = rs.GetEpochSummaries(ctx, filter, pagination) 278 require.Equal(t, 16, len(summaries)) 279 verifyRewardsForEpoch(t, summaries, 1, asset1.ID.String(), asset2.ID.String()) 280 verifyRewardsForEpoch(t, summaries, 2, asset1.ID.String(), asset2.ID.String()) 281 282 // now request with from = nil and to = 2, expect the same result 283 to := uint64(2) 284 filter = entities.RewardSummaryFilter{ToEpoch: &to} 285 summaries, _, _ = rs.GetEpochSummaries(ctx, filter, pagination) 286 require.Equal(t, 16, len(summaries)) 287 verifyRewardsForEpoch(t, summaries, 1, asset1.ID.String(), asset2.ID.String()) 288 verifyRewardsForEpoch(t, summaries, 2, asset1.ID.String(), asset2.ID.String()) 289 290 // now request from = 2 to = nil expect only epoch 2 291 from = 2 292 filter = entities.RewardSummaryFilter{FromEpoch: &from} 293 summaries, _, _ = rs.GetEpochSummaries(ctx, filter, pagination) 294 require.Equal(t, 8, len(summaries)) 295 verifyRewardsForEpoch(t, summaries, 2, asset1.ID.String(), asset2.ID.String()) 296 297 // now request to = 1 from = nil expect only epoch 1 298 to = 1 299 filter = entities.RewardSummaryFilter{ToEpoch: &to} 300 summaries, _, _ = rs.GetEpochSummaries(ctx, filter, pagination) 301 require.Equal(t, 8, len(summaries)) 302 verifyRewardsForEpoch(t, summaries, 1, asset1.ID.String(), asset2.ID.String()) 303 304 // now request from = 1 and to = 1 305 from = 1 306 filter = entities.RewardSummaryFilter{FromEpoch: &from, ToEpoch: &to} 307 summaries, _, _ = rs.GetEpochSummaries(ctx, filter, pagination) 308 require.Equal(t, 8, len(summaries)) 309 verifyRewardsForEpoch(t, summaries, 1, asset1.ID.String(), asset2.ID.String()) 310 311 // full filter 312 to = 2 313 filter = entities.RewardSummaryFilter{ 314 FromEpoch: &from, 315 ToEpoch: &to, 316 AssetIDs: []entities.AssetID{asset1.ID, asset2.ID}, 317 MarketIDs: []entities.MarketID{market1, market2}, 318 } 319 summaries, _, err = rs.GetEpochSummaries(ctx, filter, pagination) 320 require.NoError(t, err) 321 require.Equal(t, 16, len(summaries)) 322 verifyRewardsForEpoch(t, summaries, 1, asset1.ID.String(), asset2.ID.String()) 323 } 324 325 func verifyRewardsForEpoch(t *testing.T, summaries []entities.EpochRewardSummary, epoch int, asset1, asset2 string) { 326 t.Helper() 327 m := make(map[string]string, len(summaries)) 328 for _, s := range summaries { 329 id := fmt.Sprintf("%d_%s_%s_%s", s.EpochID, s.AssetID, s.MarketID, s.RewardType) 330 m[id] = s.Amount.String() 331 } 332 if epoch == 1 { 333 require.Equal(t, "600", m["1_"+asset1+"_deadbeef_RewardMakerPaidFees"]) 334 require.Equal(t, "660", m["1_"+asset1+"__RewardMakerPaidFees"]) 335 require.Equal(t, "1500", m["1_"+asset2+"_deadbeef_RewardMakerPaidFees"]) 336 require.Equal(t, "1560", m["1_"+asset2+"__RewardMakerPaidFees"]) 337 require.Equal(t, "6000", m["1_"+asset1+"_deadbeef_RewardMakerReceivedFees"]) 338 require.Equal(t, "6600", m["1_"+asset1+"__GlobalReward"]) 339 require.Equal(t, "15000", m["1_"+asset2+"_deadbeef_RewardMakerReceivedFees"]) 340 require.Equal(t, "15600", m["1_"+asset2+"__GlobalReward"]) 341 } else if epoch == 2 { 342 require.Equal(t, "60000", m["2_"+asset1+"_deadbeef_RewardMakerPaidFees"]) 343 require.Equal(t, "66000", m["2_"+asset1+"__RewardMakerPaidFees"]) 344 require.Equal(t, "150000", m["2_"+asset2+"_deadbeef_RewardMakerPaidFees"]) 345 require.Equal(t, "156000", m["2_"+asset2+"__RewardMakerPaidFees"]) 346 require.Equal(t, "600000", m["2_"+asset1+"_deadbeef_RewardMakerReceivedFees"]) 347 require.Equal(t, "660000", m["2_"+asset1+"__GlobalReward"]) 348 require.Equal(t, "1500000", m["2_"+asset2+"_deadbeef_RewardMakerReceivedFees"]) 349 require.Equal(t, "1560000", m["2_"+asset2+"__GlobalReward"]) 350 } 351 } 352 353 func setupRewardsTest(t *testing.T, ctx context.Context) (*sqlstore.Blocks, *sqlstore.Rewards, *sqlstore.Parties, *sqlstore.Assets) { 354 t.Helper() 355 bs := sqlstore.NewBlocks(connectionSource) 356 rs := sqlstore.NewRewards(ctx, connectionSource) 357 ps := sqlstore.NewParties(connectionSource) 358 as := sqlstore.NewAssets(connectionSource) 359 360 return bs, rs, ps, as 361 } 362 363 func populateTestRewards(ctx context.Context, t *testing.T, bs *sqlstore.Blocks, ps *sqlstore.Parties, as *sqlstore.Assets, rs *sqlstore.Rewards) { 364 t.Helper() 365 partyID := entities.PartyID("89C701D1AE2819263E45538D0B25022988BC2508A02C654462D22E0AFB626A7D") 366 assetID := entities.AssetID("8AA92225C32ADB54E527FCB1AEE2930CBADB4DF6F068AB2C2D667EB057EF00FA") 367 368 rewards := []entities.Reward{ 369 { 370 PartyID: partyID, 371 AssetID: assetID, 372 EpochID: 637, 373 Amount: decimal.NewFromFloat(1), 374 PercentOfTotal: 100, 375 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 376 Timestamp: time.Date(2022, 3, 24, 15, 27, 28, 357155000, time.UTC), 377 VegaTime: time.Date(2022, 3, 24, 15, 27, 28, 357155000, time.UTC), 378 SeqNum: 1, 379 }, 380 { 381 PartyID: partyID, 382 AssetID: assetID, 383 EpochID: 642, 384 Amount: decimal.NewFromFloat(0), 385 PercentOfTotal: 0, 386 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 387 Timestamp: time.Date(2022, 3, 24, 15, 28, 1, 508305000, time.UTC), 388 VegaTime: time.Date(2022, 3, 24, 15, 28, 1, 508305000, time.UTC), 389 SeqNum: 1, 390 }, 391 { 392 PartyID: partyID, 393 AssetID: assetID, 394 EpochID: 643, 395 Amount: decimal.NewFromFloat(1), 396 PercentOfTotal: 100, 397 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 398 Timestamp: time.Date(2022, 3, 24, 15, 28, 8, 168980000, time.UTC), 399 VegaTime: time.Date(2022, 3, 24, 15, 28, 8, 168980000, time.UTC), 400 SeqNum: 1, 401 }, 402 { 403 PartyID: partyID, 404 AssetID: assetID, 405 EpochID: 737, 406 Amount: decimal.NewFromFloat(1), 407 PercentOfTotal: 100, 408 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 409 Timestamp: time.Date(2022, 3, 24, 15, 38, 22, 855711000, time.UTC), 410 VegaTime: time.Date(2022, 3, 24, 15, 38, 22, 855711000, time.UTC), 411 SeqNum: 1, 412 }, 413 { 414 PartyID: partyID, 415 AssetID: assetID, 416 EpochID: 741, 417 Amount: decimal.NewFromFloat(5), 418 PercentOfTotal: 62.5, 419 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 420 Timestamp: time.Date(2022, 3, 24, 15, 38, 49, 338318000, time.UTC), 421 VegaTime: time.Date(2022, 3, 24, 15, 38, 49, 338318000, time.UTC), 422 SeqNum: 1, 423 }, 424 { 425 PartyID: partyID, 426 AssetID: assetID, 427 EpochID: 744, 428 Amount: decimal.NewFromFloat(1), 429 PercentOfTotal: 33.33333333333333, 430 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 431 Timestamp: time.Date(2022, 3, 24, 15, 39, 9, 595917000, time.UTC), 432 VegaTime: time.Date(2022, 3, 24, 15, 39, 9, 595917000, time.UTC), 433 SeqNum: 1, 434 }, 435 { 436 PartyID: partyID, 437 AssetID: assetID, 438 EpochID: 747, 439 Amount: decimal.NewFromFloat(6), 440 PercentOfTotal: 60, 441 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 442 Timestamp: time.Date(2022, 3, 24, 15, 39, 29, 400906000, time.UTC), 443 VegaTime: time.Date(2022, 3, 24, 15, 39, 29, 400906000, time.UTC), 444 SeqNum: 1, 445 }, 446 { 447 PartyID: partyID, 448 AssetID: assetID, 449 EpochID: 757, 450 Amount: decimal.NewFromFloat(6), 451 PercentOfTotal: 60, 452 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 453 Timestamp: time.Date(2022, 3, 24, 15, 40, 34, 750010000, time.UTC), 454 VegaTime: time.Date(2022, 3, 24, 15, 40, 34, 750010000, time.UTC), 455 SeqNum: 1, 456 }, 457 { 458 PartyID: partyID, 459 AssetID: assetID, 460 EpochID: 1025, 461 Amount: decimal.NewFromFloat(1), 462 PercentOfTotal: 50, 463 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 464 Timestamp: time.Date(2022, 3, 24, 16, 9, 52, 556102000, time.UTC), 465 VegaTime: time.Date(2022, 3, 24, 16, 9, 52, 556102000, time.UTC), 466 SeqNum: 1, 467 }, 468 { 469 PartyID: partyID, 470 AssetID: assetID, 471 EpochID: 1027, 472 Amount: decimal.NewFromFloat(1), 473 PercentOfTotal: 100, 474 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 475 Timestamp: time.Date(2022, 3, 24, 16, 10, 5, 602243000, time.UTC), 476 VegaTime: time.Date(2022, 3, 24, 16, 10, 5, 602243000, time.UTC), 477 SeqNum: 1, 478 }, 479 } 480 481 b := addTestBlock(t, ctx, bs) 482 err := ps.Add(ctx, entities.Party{ID: partyID, VegaTime: &b.VegaTime}) 483 require.NoError(t, err) 484 485 err = as.Add(ctx, entities.Asset{ID: assetID, VegaTime: b.VegaTime}) 486 require.NoError(t, err) 487 488 for _, reward := range rewards { 489 addTestBlockForTime(t, ctx, bs, reward.VegaTime) 490 err := rs.Add(ctx, reward) 491 require.NoError(t, err) 492 } 493 } 494 495 func TestRewardsPagination(t *testing.T) { 496 t.Run("should return all the rewards when no paging is provided", testRewardsCursorPaginationNoPagination) 497 t.Run("should return the first page when the first limit is provided with no after cursor", testRewardsCursorPaginationFirstPage) 498 t.Run("should return the last page when the last limit is provided with no before cursor", testRewardsCursorPaginationLastPage) 499 t.Run("should return the page specified by the first limit and after cursor", testRewardsCursorPaginationFirstPageAfter) 500 t.Run("should return the page specified by the last limit and before cursor", testRewardsCursorPaginationLastPageBefore) 501 502 t.Run("should return all the rewards when no paging is provided", testRewardsCursorPaginationNoPaginationNewestFirst) 503 t.Run("should return the first page when the first limit is provided with no after cursor", testRewardsCursorPaginationFirstPageNewestFirst) 504 t.Run("should return the last page when the last limit is provided with no before cursor", testRewardsCursorPaginationLastPageNewestFirst) 505 t.Run("should return the page specified by the first limit and after cursor", testRewardsCursorPaginationFirstPageAfterNewestFirst) 506 t.Run("should return the page specified by the last limit and before cursor", testRewardsCursorPaginationLastPageBeforeNewestFirst) 507 } 508 509 func testRewardsCursorPaginationNoPagination(t *testing.T) { 510 ctx := tempTransaction(t) 511 bs, rs, ps, as := setupRewardsTest(t, ctx) 512 513 populateTestRewards(ctx, t, bs, ps, as, rs) 514 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 515 require.NoError(t, err) 516 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 517 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 518 519 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 520 assert.NoError(t, err) 521 assert.Equal(t, 10, len(got)) 522 assert.Equal(t, int64(637), got[0].EpochID) 523 assert.Equal(t, int64(1027), got[len(got)-1].EpochID) 524 assert.Equal(t, entities.PageInfo{ 525 HasNextPage: false, 526 HasPreviousPage: false, 527 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 637}.String()).Encode(), 528 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 1027}.String()).Encode(), 529 }, pageInfo) 530 } 531 532 func testRewardsCursorPaginationFirstPage(t *testing.T) { 533 ctx := tempTransaction(t) 534 bs, rs, ps, as := setupRewardsTest(t, ctx) 535 536 populateTestRewards(ctx, t, bs, ps, as, rs) 537 first := int32(3) 538 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 539 require.NoError(t, err) 540 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 541 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 542 543 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 544 assert.NoError(t, err) 545 assert.Equal(t, 3, len(got)) 546 assert.Equal(t, int64(637), got[0].EpochID) 547 assert.Equal(t, int64(643), got[len(got)-1].EpochID) 548 assert.Equal(t, entities.PageInfo{ 549 HasNextPage: true, 550 HasPreviousPage: false, 551 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 637}.String()).Encode(), 552 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 643}.String()).Encode(), 553 }, pageInfo) 554 } 555 556 func testRewardsCursorPaginationLastPage(t *testing.T) { 557 ctx := tempTransaction(t) 558 bs, rs, ps, as := setupRewardsTest(t, ctx) 559 560 populateTestRewards(ctx, t, bs, ps, as, rs) 561 last := int32(3) 562 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false) 563 require.NoError(t, err) 564 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 565 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 566 567 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 568 assert.NoError(t, err) 569 assert.Equal(t, 3, len(got)) 570 assert.Equal(t, int64(757), got[0].EpochID) 571 assert.Equal(t, int64(1027), got[len(got)-1].EpochID) 572 assert.Equal(t, entities.PageInfo{ 573 HasNextPage: false, 574 HasPreviousPage: true, 575 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 757}.String()).Encode(), 576 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 1027}.String()).Encode(), 577 }, pageInfo) 578 } 579 580 func testRewardsCursorPaginationFirstPageAfter(t *testing.T) { 581 ctx := tempTransaction(t) 582 bs, rs, ps, as := setupRewardsTest(t, ctx) 583 584 populateTestRewards(ctx, t, bs, ps, as, rs) 585 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 586 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 587 588 first := int32(3) 589 after := entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 643}.String()).Encode() 590 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false) 591 require.NoError(t, err) 592 593 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 594 assert.NoError(t, err) 595 assert.Equal(t, 3, len(got)) 596 assert.Equal(t, int64(737), got[0].EpochID) 597 assert.Equal(t, int64(744), got[len(got)-1].EpochID) 598 assert.Equal(t, entities.PageInfo{ 599 HasNextPage: true, 600 HasPreviousPage: true, 601 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 737}.String()).Encode(), 602 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 744}.String()).Encode(), 603 }, pageInfo) 604 } 605 606 func testRewardsCursorPaginationLastPageBefore(t *testing.T) { 607 ctx := tempTransaction(t) 608 bs, rs, ps, as := setupRewardsTest(t, ctx) 609 610 populateTestRewards(ctx, t, bs, ps, as, rs) 611 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 612 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 613 614 last := int32(3) 615 before := entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 757}.String()).Encode() 616 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false) 617 require.NoError(t, err) 618 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 619 assert.NoError(t, err) 620 assert.Equal(t, 3, len(got)) 621 assert.Equal(t, int64(741), got[0].EpochID) 622 assert.Equal(t, int64(747), got[len(got)-1].EpochID) 623 assert.Equal(t, entities.PageInfo{ 624 HasNextPage: true, 625 HasPreviousPage: true, 626 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 741}.String()).Encode(), 627 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 747}.String()).Encode(), 628 }, pageInfo) 629 } 630 631 func testRewardsCursorPaginationNoPaginationNewestFirst(t *testing.T) { 632 ctx := tempTransaction(t) 633 bs, rs, ps, as := setupRewardsTest(t, ctx) 634 635 populateTestRewards(ctx, t, bs, ps, as, rs) 636 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 637 require.NoError(t, err) 638 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 639 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 640 641 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 642 assert.NoError(t, err) 643 assert.Equal(t, 10, len(got)) 644 assert.Equal(t, int64(1027), got[0].EpochID) 645 assert.Equal(t, int64(637), got[len(got)-1].EpochID) 646 assert.Equal(t, entities.PageInfo{ 647 HasNextPage: false, 648 HasPreviousPage: false, 649 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 1027}.String()).Encode(), 650 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 637}.String()).Encode(), 651 }, pageInfo) 652 } 653 654 func testRewardsCursorPaginationFirstPageNewestFirst(t *testing.T) { 655 ctx := tempTransaction(t) 656 bs, rs, ps, as := setupRewardsTest(t, ctx) 657 658 populateTestRewards(ctx, t, bs, ps, as, rs) 659 first := int32(3) 660 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 661 require.NoError(t, err) 662 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 663 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 664 665 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 666 assert.NoError(t, err) 667 assert.Equal(t, 3, len(got)) 668 assert.Equal(t, int64(1027), got[0].EpochID) 669 assert.Equal(t, int64(757), got[len(got)-1].EpochID) 670 assert.Equal(t, entities.PageInfo{ 671 HasNextPage: true, 672 HasPreviousPage: false, 673 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 1027}.String()).Encode(), 674 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 757}.String()).Encode(), 675 }, pageInfo) 676 } 677 678 func testRewardsCursorPaginationLastPageNewestFirst(t *testing.T) { 679 ctx := tempTransaction(t) 680 bs, rs, ps, as := setupRewardsTest(t, ctx) 681 682 populateTestRewards(ctx, t, bs, ps, as, rs) 683 last := int32(3) 684 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 685 require.NoError(t, err) 686 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 687 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 688 689 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 690 assert.NoError(t, err) 691 assert.Equal(t, 3, len(got)) 692 assert.Equal(t, int64(643), got[0].EpochID) 693 assert.Equal(t, int64(637), got[len(got)-1].EpochID) 694 assert.Equal(t, entities.PageInfo{ 695 HasNextPage: false, 696 HasPreviousPage: true, 697 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 643}.String()).Encode(), 698 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 637}.String()).Encode(), 699 }, pageInfo) 700 } 701 702 func testRewardsCursorPaginationFirstPageAfterNewestFirst(t *testing.T) { 703 ctx := tempTransaction(t) 704 bs, rs, ps, as := setupRewardsTest(t, ctx) 705 706 populateTestRewards(ctx, t, bs, ps, as, rs) 707 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 708 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 709 710 first := int32(3) 711 after := entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 757}.String()).Encode() 712 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 713 require.NoError(t, err) 714 715 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 716 assert.NoError(t, err) 717 assert.Equal(t, 3, len(got)) 718 assert.Equal(t, int64(747), got[0].EpochID) 719 assert.Equal(t, int64(741), got[len(got)-1].EpochID) 720 assert.Equal(t, entities.PageInfo{ 721 HasNextPage: true, 722 HasPreviousPage: true, 723 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 747}.String()).Encode(), 724 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 741}.String()).Encode(), 725 }, pageInfo) 726 } 727 728 func testRewardsCursorPaginationLastPageBeforeNewestFirst(t *testing.T) { 729 ctx := tempTransaction(t) 730 bs, rs, ps, as := setupRewardsTest(t, ctx) 731 732 populateTestRewards(ctx, t, bs, ps, as, rs) 733 partyID := "89c701d1ae2819263e45538d0b25022988bc2508a02c654462d22e0afb626a7d" 734 assetID := "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa" 735 736 last := int32(3) 737 before := entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 643}.String()).Encode() 738 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 739 require.NoError(t, err) 740 got, pageInfo, err := rs.GetByCursor(ctx, []string{partyID}, &assetID, nil, nil, pagination, nil, nil, nil) 741 assert.NoError(t, err) 742 assert.Equal(t, 3, len(got)) 743 assert.Equal(t, int64(744), got[0].EpochID) 744 assert.Equal(t, int64(737), got[len(got)-1].EpochID) 745 assert.Equal(t, entities.PageInfo{ 746 HasNextPage: true, 747 HasPreviousPage: true, 748 StartCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 744}.String()).Encode(), 749 EndCursor: entities.NewCursor(entities.RewardCursor{PartyID: partyID, AssetID: assetID, EpochID: 737}.String()).Encode(), 750 }, pageInfo) 751 } 752 753 func Test_FilterRewardsQuery(t *testing.T) { 754 type args struct { 755 table string 756 inFilter entities.RewardSummaryFilter 757 } 758 tests := []struct { 759 name string 760 args args 761 wantQuery string 762 wantArgs []any 763 wantErr assert.ErrorAssertionFunc 764 }{ 765 { 766 name: "filter with all values", 767 args: args{ 768 table: "test", 769 inFilter: entities.RewardSummaryFilter{ 770 AssetIDs: []entities.AssetID{"8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa"}, 771 MarketIDs: []entities.MarketID{"deadbeef"}, 772 FromEpoch: ptr.From(uint64(123)), 773 ToEpoch: ptr.From(uint64(124)), 774 }, 775 }, 776 wantQuery: ` WHERE asset_id = ANY($1) AND market_id = ANY($2) AND epoch_id >= $3 AND epoch_id <= $4`, 777 wantArgs: []any{ 778 "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa", 779 "deadbeef", 780 ptr.From(uint64(123)), 781 ptr.From(uint64(124)), 782 }, 783 wantErr: assert.NoError, 784 }, { 785 name: "filter with no values", 786 args: args{ 787 table: "test", 788 inFilter: entities.RewardSummaryFilter{}, 789 }, 790 wantQuery: "", 791 wantArgs: []any{}, 792 wantErr: assert.NoError, 793 }, { 794 name: "filter with only asset ids", 795 args: args{ 796 table: "test", 797 inFilter: entities.RewardSummaryFilter{ 798 AssetIDs: []entities.AssetID{"8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa"}, 799 }, 800 }, 801 wantQuery: ` WHERE asset_id = ANY($1)`, 802 wantArgs: []any{ 803 "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa", 804 }, 805 wantErr: assert.NoError, 806 }, { 807 name: "filter with only market ids", 808 args: args{ 809 table: "test", 810 inFilter: entities.RewardSummaryFilter{ 811 MarketIDs: []entities.MarketID{"deadbeef"}, 812 }, 813 }, 814 wantQuery: ` WHERE market_id = ANY($1)`, 815 wantArgs: []any{ 816 "deadbeef", 817 }, 818 wantErr: assert.NoError, 819 }, { 820 name: "filter with only from epoch", 821 args: args{ 822 table: "test", 823 inFilter: entities.RewardSummaryFilter{ 824 FromEpoch: ptr.From(uint64(123)), 825 }, 826 }, 827 wantQuery: ` WHERE epoch_id >= $1`, 828 wantArgs: []any{ 829 ptr.From(uint64(123)), 830 }, 831 wantErr: assert.NoError, 832 }, { 833 name: "filter with only to epoch", 834 args: args{ 835 table: "test", 836 inFilter: entities.RewardSummaryFilter{ 837 ToEpoch: ptr.From(uint64(123)), 838 }, 839 }, 840 wantQuery: ` WHERE epoch_id <= $1`, 841 wantArgs: []any{ 842 ptr.From(uint64(123)), 843 }, 844 wantErr: assert.NoError, 845 }, { 846 name: "filter with only from and to epoch", 847 args: args{ 848 table: "test", 849 inFilter: entities.RewardSummaryFilter{ 850 FromEpoch: ptr.From(uint64(123)), 851 ToEpoch: ptr.From(uint64(124)), 852 }, 853 }, 854 wantQuery: ` WHERE epoch_id >= $1 AND epoch_id <= $2`, 855 wantArgs: []any{ 856 ptr.From(uint64(123)), 857 ptr.From(uint64(124)), 858 }, 859 wantErr: assert.NoError, 860 }, { 861 name: "filter with only asset ids and from epoch", 862 args: args{ 863 table: "test", 864 inFilter: entities.RewardSummaryFilter{ 865 AssetIDs: []entities.AssetID{"8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa"}, 866 FromEpoch: ptr.From(uint64(123)), 867 }, 868 }, 869 wantQuery: ` WHERE asset_id = ANY($1) AND epoch_id >= $2`, 870 wantArgs: []any{ 871 "8aa92225c32adb54e527fcb1aee2930cbadb4df6f068ab2c2d667eb057ef00fa", 872 ptr.From(uint64(123)), 873 }, 874 wantErr: assert.NoError, 875 }, 876 } 877 for _, tt := range tests { 878 t.Run(tt.name, func(t *testing.T) { 879 got, args, err := sqlstore.FilterRewardsQuery(tt.args.inFilter) 880 if !tt.wantErr(t, err, fmt.Sprintf("filterSQL(%v, %v)", tt.args.table, tt.args.inFilter)) { 881 return 882 } 883 assert.Equalf(t, tt.wantQuery, got, "filterSQL(%v, %v)", tt.args.table, tt.args.inFilter) 884 for i, arg := range args { 885 if reflect.TypeOf(arg).Kind() == reflect.Slice { 886 arg = hex.EncodeToString(arg.([][]uint8)[0]) 887 } 888 assert.Equalf(t, tt.wantArgs[i], arg, "filterSQL(%v, %v)", tt.args.table, tt.args.inFilter) 889 } 890 }) 891 } 892 } 893 894 func TestRewardsGameTotals(t *testing.T) { 895 ctx := tempTransaction(t) 896 // teams 897 teams := []entities.Team{ 898 { 899 ID: "deadd00d01", 900 Referrer: "beefbeef01", 901 Name: "aaaa", 902 TeamURL: nil, 903 AvatarURL: nil, 904 Closed: false, 905 CreatedAt: time.Now(), 906 CreatedAtEpoch: 0, 907 VegaTime: time.Now(), 908 }, 909 { 910 ID: "deadd00d02", 911 Referrer: "beefbeef02", 912 Name: "bbbb", 913 TeamURL: nil, 914 AvatarURL: nil, 915 Closed: false, 916 CreatedAt: time.Now(), 917 CreatedAtEpoch: 0, 918 VegaTime: time.Now(), 919 }, 920 { 921 ID: "deadd00d03", 922 Referrer: "beefbeef03", 923 Name: "cccc", 924 TeamURL: nil, 925 AvatarURL: nil, 926 Closed: false, 927 CreatedAt: time.Now(), 928 CreatedAtEpoch: 0, 929 VegaTime: time.Now(), 930 }, 931 } 932 for _, team := range teams { 933 _, err := connectionSource.Exec(ctx, 934 `INSERT INTO teams (id, referrer, name, team_url, avatar_url, closed, created_at_epoch, created_at, vega_time) 935 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, 936 team.ID, team.Referrer, team.Name, team.TeamURL, team.AvatarURL, team.Closed, team.CreatedAtEpoch, team.CreatedAt, team.VegaTime) 937 require.NoError(t, err) 938 } 939 // team members data 940 teamMembers := []entities.TeamMember{ 941 { 942 TeamID: "deadd00d01", 943 PartyID: "deadbeef01", 944 JoinedAt: time.Now(), 945 JoinedAtEpoch: 0, 946 VegaTime: time.Now(), 947 }, 948 { 949 TeamID: "deadd00d02", 950 PartyID: "deadbeef02", 951 JoinedAt: time.Now(), 952 JoinedAtEpoch: 0, 953 VegaTime: time.Now(), 954 }, 955 { 956 TeamID: "deadd00d03", 957 PartyID: "deadbeef03", 958 JoinedAt: time.Now(), 959 JoinedAtEpoch: 0, 960 VegaTime: time.Now(), 961 }, 962 } 963 for _, member := range teamMembers { 964 _, err := connectionSource.Exec(ctx, 965 `INSERT INTO team_members (team_id, party_id, joined_at_epoch, joined_at, vega_time) 966 VALUES ($1, $2, $3, $4, $5)`, 967 member.TeamID, member.PartyID, member.JoinedAtEpoch, member.JoinedAt, member.VegaTime) 968 require.NoError(t, err) 969 } 970 // populate the game reward totals with some test data 971 existingTotals := []entities.RewardTotals{ 972 { 973 GameID: "deadbeef01", 974 PartyID: "cafedaad01", 975 AssetID: "deadbaad01", 976 MarketID: "beefcafe01", 977 EpochID: 1, 978 TeamID: "deadd00d01", 979 TotalRewards: decimal.NewFromFloat(1000), 980 TotalRewardsQuantum: decimal.NewFromFloat(1000), 981 }, 982 { 983 GameID: "deadbeef02", 984 PartyID: "cafedaad02", 985 AssetID: "deadbaad02", 986 MarketID: "beefcafe02", 987 EpochID: 1, 988 TeamID: "deadd00d02", 989 TotalRewards: decimal.NewFromFloat(2000), 990 TotalRewardsQuantum: decimal.NewFromFloat(2000), 991 }, 992 { 993 GameID: "deadbeef03", 994 PartyID: "cafedaad03", 995 AssetID: "deadbaad03", 996 MarketID: "beefcafe03", 997 EpochID: 1, 998 TeamID: "deadd00d03", 999 TotalRewards: decimal.NewFromFloat(3000), 1000 TotalRewardsQuantum: decimal.NewFromFloat(3000), 1001 }, 1002 } 1003 for _, total := range existingTotals { 1004 _, err := connectionSource.Exec(ctx, 1005 `INSERT INTO game_reward_totals (game_id, party_id, asset_id, market_id, epoch_id, team_id, total_rewards, total_rewards_quantum) 1006 VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, 1007 total.GameID, total.PartyID, total.AssetID, total.MarketID, total.EpochID, total.TeamID, total.TotalRewards, total.TotalRewardsQuantum) 1008 require.NoError(t, err) 1009 } 1010 1011 ts := time.Now() 1012 ts2 := ts.Add(time.Minute) 1013 // add rewards 1014 rewardsToAdd := []entities.Reward{ 1015 { 1016 PartyID: "cafedaad01", 1017 AssetID: "deadbaad01", 1018 MarketID: "beefcafe01", 1019 EpochID: 2, 1020 Amount: decimal.NewFromFloat(1000), 1021 QuantumAmount: decimal.NewFromFloat(1000), 1022 PercentOfTotal: 0, 1023 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 1024 Timestamp: ts, 1025 TxHash: "", 1026 VegaTime: ts, 1027 SeqNum: 1, 1028 LockedUntilEpochID: 30, 1029 GameID: ptr.From(entities.GameID("deadbeef01")), 1030 }, 1031 { 1032 PartyID: "cafedaad02", 1033 AssetID: "deadbaad02", 1034 MarketID: "beefcafe02", 1035 EpochID: 2, 1036 Amount: decimal.NewFromFloat(1000), 1037 QuantumAmount: decimal.NewFromFloat(1000), 1038 PercentOfTotal: 0, 1039 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 1040 Timestamp: ts, 1041 TxHash: "", 1042 VegaTime: ts, 1043 SeqNum: 2, 1044 LockedUntilEpochID: 30, 1045 GameID: ptr.From(entities.GameID("deadbeef02")), 1046 }, 1047 { 1048 PartyID: "cafedaad03", 1049 AssetID: "deadbaad03", 1050 MarketID: "beefcafe03", 1051 EpochID: 2, 1052 Amount: decimal.NewFromFloat(1000), 1053 QuantumAmount: decimal.NewFromFloat(1000), 1054 PercentOfTotal: 0, 1055 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 1056 Timestamp: ts, 1057 TxHash: "", 1058 VegaTime: ts, 1059 SeqNum: 3, 1060 LockedUntilEpochID: 30, 1061 GameID: ptr.From(entities.GameID("deadbeef03")), 1062 }, 1063 { 1064 PartyID: "cafedaad01", 1065 AssetID: "deadbaad01", 1066 MarketID: "beefcafe01", 1067 EpochID: 3, 1068 Amount: decimal.NewFromFloat(1000), 1069 QuantumAmount: decimal.NewFromFloat(1000), 1070 PercentOfTotal: 0, 1071 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 1072 Timestamp: ts2, 1073 TxHash: "", 1074 VegaTime: ts2, 1075 SeqNum: 1, 1076 LockedUntilEpochID: 30, 1077 GameID: ptr.From(entities.GameID("deadbeef01")), 1078 }, 1079 { 1080 PartyID: "cafedaad02", 1081 AssetID: "deadbaad02", 1082 MarketID: "beefcafe02", 1083 EpochID: 3, 1084 Amount: decimal.NewFromFloat(1000), 1085 QuantumAmount: decimal.NewFromFloat(1000), 1086 PercentOfTotal: 0, 1087 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 1088 Timestamp: ts2, 1089 TxHash: "", 1090 VegaTime: ts2, 1091 SeqNum: 2, 1092 LockedUntilEpochID: 30, 1093 GameID: ptr.From(entities.GameID("deadbeef02")), 1094 }, 1095 { 1096 PartyID: "cafedaad03", 1097 AssetID: "deadbaad03", 1098 MarketID: "beefcafe03", 1099 EpochID: 3, 1100 Amount: decimal.NewFromFloat(1000), 1101 QuantumAmount: decimal.NewFromFloat(1000), 1102 PercentOfTotal: 0, 1103 RewardType: "ACCOUNT_TYPE_UNSPECIFIED", 1104 Timestamp: ts2, 1105 TxHash: "", 1106 VegaTime: ts2, 1107 SeqNum: 3, 1108 LockedUntilEpochID: 30, 1109 GameID: ptr.From(entities.GameID("deadbeef03")), 1110 }, 1111 } 1112 1113 rs := sqlstore.NewRewards(ctx, connectionSource) 1114 for _, r := range rewardsToAdd { 1115 require.NoError(t, rs.Add(ctx, r)) 1116 } 1117 1118 // Now make sure the totals are updated and correct 1119 testCases := []struct { 1120 game_id entities.GameID 1121 party_id entities.PartyID 1122 epoch_id int64 1123 want decimal.Decimal 1124 }{ 1125 { 1126 game_id: "deadbeef01", 1127 party_id: "cafedaad01", 1128 epoch_id: 2, 1129 want: decimal.NewFromFloat(2000), 1130 }, 1131 { 1132 game_id: "deadbeef01", 1133 party_id: "cafedaad01", 1134 epoch_id: 3, 1135 want: decimal.NewFromFloat(3000), 1136 }, 1137 { 1138 game_id: "deadbeef02", 1139 party_id: "cafedaad02", 1140 epoch_id: 2, 1141 want: decimal.NewFromFloat(3000), 1142 }, 1143 { 1144 game_id: "deadbeef02", 1145 party_id: "cafedaad02", 1146 epoch_id: 3, 1147 want: decimal.NewFromFloat(4000), 1148 }, 1149 } 1150 for _, tc := range testCases { 1151 var totals []entities.RewardTotals 1152 require.NoError(t, pgxscan.Select(ctx, connectionSource, &totals, 1153 `SELECT * FROM game_reward_totals WHERE game_id = $1 AND party_id = $2 AND epoch_id = $3`, 1154 tc.game_id, tc.party_id, tc.epoch_id)) 1155 assert.Equal(t, 1, len(totals)) 1156 assert.True(t, tc.want.Equal(totals[0].TotalRewards), "totals don't match, got: %s, want: %s", totals[0].TotalRewards, tc.want) 1157 assert.True(t, tc.want.Equal(totals[0].TotalRewardsQuantum), "totals don't match, got: %s, want: %s", totals[0].TotalRewardsQuantum, tc.want) 1158 } 1159 } 1160 1161 func filterRewardsByParty(rewards []entities.Reward, partyID entities.PartyID) []entities.Reward { 1162 filtered := make([]entities.Reward, 0) 1163 for _, r := range rewards { 1164 if r.PartyID == partyID { 1165 filtered = append(filtered, r) 1166 } 1167 } 1168 1169 return filtered 1170 } 1171 1172 func filterRewardsByTeam(rewards []entities.Reward, teamID entities.TeamID) []entities.Reward { 1173 filtered := make([]entities.Reward, 0) 1174 for _, r := range rewards { 1175 if r.TeamID != nil && *r.TeamID == teamID { 1176 filtered = append(filtered, r) 1177 } 1178 } 1179 1180 return filtered 1181 } 1182 1183 func filterRewardsByGame(rewards []entities.Reward, gameID entities.GameID) []entities.Reward { 1184 filtered := make([]entities.Reward, 0) 1185 for _, r := range rewards { 1186 if *r.GameID == gameID { 1187 filtered = append(filtered, r) 1188 } 1189 } 1190 1191 return filtered 1192 } 1193 1194 func TestRewardFilterByTeamIDAndGameID(t *testing.T) { 1195 // going to use the games setup because we need to make sure the rewards have game and associated team data too. 1196 ctx := tempTransaction(t) 1197 stores := setupGamesTest(t, ctx) 1198 startingBlock := addTestBlockForTime(t, ctx, stores.blocks, time.Now()) 1199 _, gameIDs, gameRewards, teams, _ := setupGamesData(ctx, t, stores, startingBlock, 50) 1200 rewards := make([]entities.Reward, 0) 1201 src := rand.NewSource(time.Now().UnixNano()) 1202 r := rand.New(src) 1203 // setup, get the unique individuals and from all the rewards 1204 individuals := make(map[entities.PartyID]struct{}) 1205 for _, gr := range gameRewards { 1206 for _, r := range gr { 1207 rewards = append(rewards, r) 1208 individuals[r.PartyID] = struct{}{} 1209 } 1210 } 1211 1212 t.Run("Should return rewards for all teams the party has been a member of if no team ID is provided", func(t *testing.T) { 1213 // create a list of parties so we can pick one at random 1214 parties := make([]entities.PartyID, 0) 1215 for p := range individuals { 1216 parties = append(parties, p) 1217 } 1218 // pick a random party 1219 i := r.Intn(len(parties)) 1220 partyID := parties[i] 1221 page := entities.DefaultCursorPagination(true) 1222 // get the rewards for that party 1223 got, _, err := stores.rewards.GetByCursor(ctx, []string{partyID.String()}, nil, nil, nil, page, nil, nil, nil) 1224 require.NoError(t, err) 1225 want := filterRewardsByParty(rewards, partyID) 1226 // we don't care about the ordering as other tests already validate that, we just want to make sure we have all the rewards for the party 1227 assert.ElementsMatchf(t, want, got, "got: %v, want: %v", got, want) 1228 }) 1229 1230 t.Run("Should return rewards for the specified team if a team ID is provided", func(t *testing.T) { 1231 allTeams := make(map[string]struct{}) 1232 for team := range teams { 1233 allTeams[team] = struct{}{} 1234 } 1235 teamIDs := make([]string, 0) 1236 for team := range allTeams { 1237 teamIDs = append(teamIDs, team) 1238 } 1239 i := r.Intn(len(teamIDs)) 1240 teamID := teamIDs[i] 1241 i = r.Intn(len(teams[teamID])) 1242 party := teams[teamID][i] 1243 page := entities.DefaultCursorPagination(true) 1244 got, _, err := stores.rewards.GetByCursor(ctx, []string{party.ID.String()}, nil, nil, nil, page, ptr.From(teamID), nil, nil) 1245 require.NoError(t, err) 1246 want := filterRewardsByParty(filterRewardsByTeam(rewards, entities.TeamID(teamID)), party.ID) 1247 assert.ElementsMatchf(t, want, got, "got: %v, want: %v", got, want) 1248 }) 1249 1250 t.Run("Should return rewards for the specified game if a game ID is provided", func(t *testing.T) { 1251 i := r.Intn(len(gameIDs)) 1252 gameID := gameIDs[i] 1253 page := entities.DefaultCursorPagination(true) 1254 got, _, err := stores.rewards.GetByCursor(ctx, nil, nil, nil, nil, page, nil, ptr.From(gameID), nil) 1255 require.NoError(t, err) 1256 want := filterRewardsByGame(rewards, entities.GameID(gameID)) 1257 assert.ElementsMatchf(t, want, got, "got: %v, want: %v", got, want) 1258 }) 1259 }