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  }