diff --git a/contract/r/gnoswap/v1/staker/calculate_pool_position_reward.gno b/contract/r/gnoswap/v1/staker/calculate_pool_position_reward.gno index 34e994bb2..30aee24a9 100644 --- a/contract/r/gnoswap/v1/staker/calculate_pool_position_reward.gno +++ b/contract/r/gnoswap/v1/staker/calculate_pool_position_reward.gno @@ -98,16 +98,15 @@ func calculatePositionReward(param CalcPositionRewardParam) []Reward { // while preventing new rewards after tier removal internalRewards, internalPenalties = pool.RewardStateOf(deposit).calculateInternalReward(lastCollectTime, param.CurrentTime) + // Get all incentives including archived ones with unclaimed rewards + allIncentives := pool.incentives.GetIncentivesForRewardCalculation(lastCollectTime, param.CurrentTime) + // Initialize maps for external rewards for i := range externalRewards { externalRewards[i] = make(map[string]int64) externalPenalties[i] = make(map[string]int64) } - // Get all incentives using a wide time range to ensure we get all possible incentives - // We'll filter them individually based on their specific lastCollectTime - allIncentives := pool.incentives.GetAllInTimestamps(0, param.CurrentTime*2) - // Process each external incentive with lazy initialization: // When a new incentive is created, we don't iterate through all deposits for performance. // diff --git a/contract/r/gnoswap/v1/staker/pool_incentive_validation_test.gno b/contract/r/gnoswap/v1/staker/pool_incentive_validation_test.gno index 11ced9d5b..5d046c38c 100644 --- a/contract/r/gnoswap/v1/staker/pool_incentive_validation_test.gno +++ b/contract/r/gnoswap/v1/staker/pool_incentive_validation_test.gno @@ -46,7 +46,7 @@ func TestStakeToken_WithEndedIncentives(t *testing.T) { } // Verify that there are actually no active incentives - activeIncentives := pool.incentives.GetAllInTimestamps(currentTime, currentTime+3600) + activeIncentives := pool.incentives.GetIncentivesForRewardCalculation(currentTime, currentTime+3600) if len(activeIncentives) != 0 { t.Fatalf("expected 0 active incentives, got %d", len(activeIncentives)) } diff --git a/contract/r/gnoswap/v1/staker/reward_calculation_canonical_env_test.gno b/contract/r/gnoswap/v1/staker/reward_calculation_canonical_env_test.gno index fc47c4d7a..cba775465 100644 --- a/contract/r/gnoswap/v1/staker/reward_calculation_canonical_env_test.gno +++ b/contract/r/gnoswap/v1/staker/reward_calculation_canonical_env_test.gno @@ -288,7 +288,7 @@ func (self *canonicalRewardState) EmulateCalcPositionReward(positionId uint64) ( externalPenalties[i] = make(map[string]int64) } - allIncentives := pool.incentives.GetAllInTimestamps(lastCollectTimestamp, currentTimestamp) + allIncentives := pool.incentives.GetIncentivesForRewardCalculation(lastCollectTimestamp, currentTimestamp) for _, incentive := range allIncentives { externalReward, externalPenalty := pool.RewardStateOf(deposit).calculateExternalReward(lastCollectTimestamp, currentTimestamp, incentive) diff --git a/contract/r/gnoswap/v1/staker/reward_calculation_incentives.gno b/contract/r/gnoswap/v1/staker/reward_calculation_incentives.gno index 3726eaf54..adec8fe23 100644 --- a/contract/r/gnoswap/v1/staker/reward_calculation_incentives.gno +++ b/contract/r/gnoswap/v1/staker/reward_calculation_incentives.gno @@ -70,26 +70,39 @@ func retrieveIncentive(tree *avl.Tree, id string) (*ExternalIncentive, bool) { return v, true } -// Get all incentives that is active in given [startTimestamp, endTimestamp) -func (self *Incentives) GetAllInTimestamps(startTimestamp, endTimestamp int64) map[string]*ExternalIncentive { +// GetIncentivesForRewardCalculation returns all incentives (both active and archived) +// that have rewards claimable in the given [startTimestamp, endTimestamp) period +func (self *Incentives) GetIncentivesForRewardCalculation(startTimestamp, endTimestamp int64) map[string]*ExternalIncentive { incentives := make(map[string]*ExternalIncentive) - // Only iterate active incentives (not archived) - self.incentives.Iterate("", "", func(key string, value any) bool { - incentive, ok := value.(*ExternalIncentive) - if !ok { - return false - } - - // incentive is not active - if incentive.startTimestamp > endTimestamp || incentive.endTimestamp < startTimestamp { + // Helper function to collect incentives that overlap with the query period + collectIncentives := func(tree *avl.Tree, skipRefunded bool) { + tree.Iterate("", "", func(key string, value any) bool { + incentive, ok := value.(*ExternalIncentive) + if !ok { + return false + } + + // Skip refunded incentives if requested (for archived incentives) + if skipRefunded && incentive.refunded { + return false + } + + // Check if incentive period overlaps with query period + if incentive.startTimestamp > endTimestamp || incentive.endTimestamp < startTimestamp { + return false + } + + incentives[incentive.incentiveId] = incentive return false - } + }) + } - incentives[incentive.incentiveId] = incentive + // Collect from active incentives (don't skip any) + collectIncentives(self.incentives, false) - return false - }) + // Collect from archived incentives (skip refunded ones) + collectIncentives(self.archivedIncentives, true) return incentives } diff --git a/contract/r/gnoswap/v1/staker/reward_calculation_incentives_test.gno b/contract/r/gnoswap/v1/staker/reward_calculation_incentives_test.gno index 3098fb3bd..4b6425ea1 100644 --- a/contract/r/gnoswap/v1/staker/reward_calculation_incentives_test.gno +++ b/contract/r/gnoswap/v1/staker/reward_calculation_incentives_test.gno @@ -60,76 +60,6 @@ func TestIncentives_ArchiveIncentive(t *testing.T) { } } -func TestIncentives_GetAllInTimestamps_ExcludesArchived(t *testing.T) { - poolPath := "test_pool_timestamps" - creator := testutils.TestAddress("creator") - - incentives := NewIncentives(poolPath) - currentTime := time.Now().Unix() - - activeIncentive := &ExternalIncentive{ - incentiveId: "active_incentive", - targetPoolPath: poolPath, - startTimestamp: currentTime - 1800, - endTimestamp: currentTime + 1800, - rewardToken: GNS_PATH, - rewardAmount: 1000, - refundee: creator, - depositGnsAmount: 100, - rewardPerSecond: 1, - } - - // Create ended incentive (to be archived) - endedIncentive := &ExternalIncentive{ - incentiveId: "ended_incentive", - targetPoolPath: poolPath, - startTimestamp: currentTime - 7200, - endTimestamp: currentTime - 3600, - rewardToken: GNS_PATH, - rewardAmount: 1000, - refundee: creator, - depositGnsAmount: 100, - rewardPerSecond: 1, - } - - // Add both incentives to active collection - incentives.create(creator, activeIncentive) - incentives.create(creator, endedIncentive) - - // Before archiving: GetAllInTimestamps should return only the active incentive - activeIncentives := incentives.GetAllInTimestamps(currentTime, currentTime+1) - if len(activeIncentives) != 1 { - t.Fatalf("expected 1 active incentive, got %d", len(activeIncentives)) - } - if _, exists := activeIncentives[activeIncentive.incentiveId]; !exists { - t.Fatal("active incentive should be included in GetAllInTimestamps") - } - - // Archive the ended incentive - incentives.archiveIncentive(endedIncentive.incentiveId) - - // After archiving: GetAllInTimestamps should still return only the active incentive - activeIncentives = incentives.GetAllInTimestamps(currentTime, currentTime+1) - if len(activeIncentives) != 1 { - t.Fatalf("expected 1 active incentive after archiving, got %d", len(activeIncentives)) - } - if _, exists := activeIncentives[activeIncentive.incentiveId]; !exists { - t.Fatal("active incentive should still be included after archiving") - } - if _, exists := activeIncentives[endedIncentive.incentiveId]; exists { - t.Fatal("archived incentive should NOT be included in GetAllInTimestamps") - } - - // Test with broader time range that would include the ended incentive - broadRangeIncentives := incentives.GetAllInTimestamps(currentTime-7200, currentTime+3600) - if len(broadRangeIncentives) != 1 { - t.Fatalf("expected 1 active incentive with broad range, got %d", len(broadRangeIncentives)) - } - if _, exists := broadRangeIncentives[endedIncentive.incentiveId]; exists { - t.Fatal("archived incentive should NOT be included even with broad time range") - } -} - func TestIncentives_ArchiveEdgeCasesAndMultiple(t *testing.T) { poolPath := "test_pool_combined" creator := testutils.TestAddress("creator")