mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 02:17:31 +00:00 
			
		
		
		
	Incentive savings hooks + init/sync of savings claims (#1209)
* update savings module macc balances getter * add savings keeper to incentive module * add savings keeper to incentive module #2 * savings reward syncing * claim savings reward * update txs, queries * update txs, queries #2 * update claim test * add savings keeper to incentive module in app.go * re-commit files to disk * define and call hooks * keeper methods for init/sync savings reward * update other tests for easier extendibility * init savings reward test * add helper methods to global incentive unit tester * sync savings test progress * savings init fix + completed tests * sync savings updates + tests * nit: simplify false check * fix: calculate set difference of incoming deposit denoms Co-authored-by: karzak <kjydavis3@gmail.com>
This commit is contained in:
		
							parent
							
								
									c2e53f2d00
								
							
						
					
					
						commit
						eaeaf20e83
					
				@ -168,10 +168,10 @@ func (h Hooks) BeforePoolDepositModified(ctx sdk.Context, poolID string, deposit
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// AfterSavingsDepositCreated function that runs after a deposit is created
 | 
					// AfterSavingsDepositCreated function that runs after a deposit is created
 | 
				
			||||||
func (h Hooks) AfterSavingsDepositCreated(ctx sdk.Context, deposit savingstypes.Deposit) {
 | 
					func (h Hooks) AfterSavingsDepositCreated(ctx sdk.Context, deposit savingstypes.Deposit) {
 | 
				
			||||||
	// TODO: InitializeSavingsReward
 | 
						h.k.InitializeSavingsReward(ctx, deposit)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BeforeSavingsDepositModified function that runs before a deposit is modified
 | 
					// BeforeSavingsDepositModified function that runs before a deposit is modified
 | 
				
			||||||
func (h Hooks) BeforeSavingsDepositModified(ctx sdk.Context, deposit savingstypes.Deposit, incomingDenoms []string) {
 | 
					func (h Hooks) BeforeSavingsDepositModified(ctx sdk.Context, deposit savingstypes.Deposit, incomingDenoms []string) {
 | 
				
			||||||
	// TODO: SynchronizeSavingsReward
 | 
						h.k.SynchronizeSavingsReward(ctx, deposit, incomingDenoms)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -38,30 +38,54 @@ func (k Keeper) AccumulateSavingsRewards(ctx sdk.Context, rewardPeriod types.Mul
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (k Keeper) SynchronizeSavingsClaim(ctx sdk.Context, owner sdk.AccAddress) {
 | 
					// InitializeSavingsReward initializes a savings claim by creating the claim and
 | 
				
			||||||
	deposit, found := k.savingsKeeper.GetDeposit(ctx, owner)
 | 
					// setting the reward factor indexes
 | 
				
			||||||
 | 
					func (k Keeper) InitializeSavingsReward(ctx sdk.Context, deposit savingstypes.Deposit) {
 | 
				
			||||||
 | 
						claim, found := k.GetSavingsClaim(ctx, deposit.Depositor)
 | 
				
			||||||
	if !found {
 | 
						if !found {
 | 
				
			||||||
		return
 | 
							claim = types.NewSavingsClaim(deposit.Depositor, sdk.Coins{}, nil)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	k.SynchronizeSavingsReward(ctx, deposit)
 | 
					
 | 
				
			||||||
 | 
						rewardIndexes := claim.RewardIndexes
 | 
				
			||||||
 | 
						for _, coin := range deposit.Amount {
 | 
				
			||||||
 | 
							globalRewardIndexes, found := k.GetSavingsRewardIndexes(ctx, coin.Denom)
 | 
				
			||||||
 | 
							if !found {
 | 
				
			||||||
 | 
								globalRewardIndexes = types.RewardIndexes{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rewardIndexes = rewardIndexes.With(coin.Denom, globalRewardIndexes)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						claim.RewardIndexes = rewardIndexes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						k.SetSavingsClaim(ctx, claim)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SynchronizeSavingsReward updates the claim object by adding any accumulated rewards
 | 
					// SynchronizeSavingsReward updates the claim object by adding any accumulated rewards
 | 
				
			||||||
// and updating the reward index value
 | 
					// and updating the reward index value
 | 
				
			||||||
func (k Keeper) SynchronizeSavingsReward(ctx sdk.Context, deposit savingstypes.Deposit) {
 | 
					func (k Keeper) SynchronizeSavingsReward(ctx sdk.Context, deposit savingstypes.Deposit, incomingDenoms []string) {
 | 
				
			||||||
	claim, found := k.GetSavingsClaim(ctx, deposit.Depositor)
 | 
						claim, found := k.GetSavingsClaim(ctx, deposit.Depositor)
 | 
				
			||||||
	if !found {
 | 
						if !found {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Source shares for savings deposits is the deposit amount
 | 
						// Set the reward factor on claim to the global reward factor for each incoming denom
 | 
				
			||||||
	for _, coin := range deposit.Amount {
 | 
						for _, denom := range incomingDenoms {
 | 
				
			||||||
		claim = k.synchronizeSingleSavingsReward(ctx, claim, coin.Denom, coin.Amount.ToDec())
 | 
							globalRewardIndexes, found := k.GetSavingsRewardIndexes(ctx, denom)
 | 
				
			||||||
 | 
							if !found {
 | 
				
			||||||
 | 
								globalRewardIndexes = types.RewardIndexes{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							claim.RewardIndexes = claim.RewardIndexes.With(denom, globalRewardIndexes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Existing denoms have their reward indexes + reward amount synced
 | 
				
			||||||
 | 
						existingDenoms := setDifference(getDenoms(deposit.Amount), incomingDenoms)
 | 
				
			||||||
 | 
						for _, denom := range existingDenoms {
 | 
				
			||||||
 | 
							claim = k.synchronizeSingleSavingsReward(ctx, claim, denom, deposit.Amount.AmountOf(denom).ToDec())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	k.SetSavingsClaim(ctx, claim)
 | 
						k.SetSavingsClaim(ctx, claim)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// synchronizeSingleSavingsReward synchronizes a single rewarded savings denom in a s claim.
 | 
					// synchronizeSingleSavingsReward synchronizes a single rewarded savings denom in a savings claim.
 | 
				
			||||||
// It returns the claim without setting in the store.
 | 
					// It returns the claim without setting in the store.
 | 
				
			||||||
// The public methods for accessing and modifying claims are preferred over this one. Direct modification of claims is easy to get wrong.
 | 
					// The public methods for accessing and modifying claims are preferred over this one. Direct modification of claims is easy to get wrong.
 | 
				
			||||||
func (k Keeper) synchronizeSingleSavingsReward(ctx sdk.Context, claim types.SavingsClaim, denom string, sourceShares sdk.Dec) types.SavingsClaim {
 | 
					func (k Keeper) synchronizeSingleSavingsReward(ctx sdk.Context, claim types.SavingsClaim, denom string, sourceShares sdk.Dec) types.SavingsClaim {
 | 
				
			||||||
@ -115,3 +139,13 @@ func (k Keeper) GetSynchronizedSavingsClaim(ctx sdk.Context, owner sdk.AccAddres
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return claim, true
 | 
						return claim, true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SynchronizeSavingsClaim syncs a savings reward claim from its store
 | 
				
			||||||
 | 
					func (k Keeper) SynchronizeSavingsClaim(ctx sdk.Context, owner sdk.AccAddress) {
 | 
				
			||||||
 | 
						deposit, found := k.savingsKeeper.GetDeposit(ctx, owner)
 | 
				
			||||||
 | 
						if !found {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						k.SynchronizeSavingsReward(ctx, deposit, []string{})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										194
									
								
								x/incentive/keeper/rewards_savings_init_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								x/incentive/keeper/rewards_savings_init_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,194 @@
 | 
				
			|||||||
 | 
					package keeper_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/suite"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdk "github.com/cosmos/cosmos-sdk/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/x/incentive/types"
 | 
				
			||||||
 | 
						savingstypes "github.com/kava-labs/kava/x/savings/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// InitializeSavingsRewardTests runs unit tests for the keeper.InitializeSavingsReward method
 | 
				
			||||||
 | 
					type InitializeSavingsRewardTests struct {
 | 
				
			||||||
 | 
						unitTester
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestInitializeSavingsRewardTests(t *testing.T) {
 | 
				
			||||||
 | 
						suite.Run(t, new(InitializeSavingsRewardTests))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *InitializeSavingsRewardTests) TestClaimAddedWhenClaimDoesNotExistAndNoRewards() {
 | 
				
			||||||
 | 
						// When a claim doesn't exist, and a user deposits to a non-rewarded pool;
 | 
				
			||||||
 | 
						// then a claim is added with no rewards and no indexes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no global indexes stored as this pool is not rewarded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						owner := arbitraryAddress()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						amount := sdk.NewCoin("test", sdk.OneInt())
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(owner, sdk.NewCoins(amount))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						suite.keeper.InitializeSavingsReward(suite.ctx, deposit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, found := suite.keeper.GetSavingsClaim(suite.ctx, owner)
 | 
				
			||||||
 | 
						suite.True(found)
 | 
				
			||||||
 | 
						// A new claim should have empty indexes. It doesn't strictly need the poolID either.
 | 
				
			||||||
 | 
						expectedIndexes := types.MultiRewardIndexes{{
 | 
				
			||||||
 | 
							CollateralType: amount.Denom,
 | 
				
			||||||
 | 
							RewardIndexes:  nil,
 | 
				
			||||||
 | 
						}}
 | 
				
			||||||
 | 
						suite.Equal(expectedIndexes, syncedClaim.RewardIndexes)
 | 
				
			||||||
 | 
						// a new claim should start with 0 rewards
 | 
				
			||||||
 | 
						suite.Equal(sdk.Coins(nil), syncedClaim.Reward)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *InitializeSavingsRewardTests) TestClaimAddedWhenClaimDoesNotExistAndRewardsExist() {
 | 
				
			||||||
 | 
						// When a claim doesn't exist, and a user deposits to a rewarded pool;
 | 
				
			||||||
 | 
						// then a claim is added with no rewards and indexes matching the global indexes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						amount := sdk.NewCoin("test", sdk.OneInt())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						globalIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: amount.Denom,
 | 
				
			||||||
 | 
								RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										CollateralType: "rewarddenom",
 | 
				
			||||||
 | 
										RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeGlobalSavingsIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						owner := arbitraryAddress()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(owner, sdk.NewCoins(amount))
 | 
				
			||||||
 | 
						suite.keeper.InitializeSavingsReward(suite.ctx, deposit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, found := suite.keeper.GetSavingsClaim(suite.ctx, owner)
 | 
				
			||||||
 | 
						suite.True(found)
 | 
				
			||||||
 | 
						// a new claim should start with the current global indexes
 | 
				
			||||||
 | 
						suite.Equal(globalIndexes, syncedClaim.RewardIndexes)
 | 
				
			||||||
 | 
						// a new claim should start with 0 rewards
 | 
				
			||||||
 | 
						suite.Equal(sdk.Coins(nil), syncedClaim.Reward)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *InitializeSavingsRewardTests) TestClaimUpdatedWhenClaimExistsAndNoRewards() {
 | 
				
			||||||
 | 
						// When a claim exists, and a user deposits to a new non-rewarded denom;
 | 
				
			||||||
 | 
						// then the claim's rewards don't change
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						preexistingDenom := "preexisting"
 | 
				
			||||||
 | 
						preexistingIndexes := types.RewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: "rewarddenom",
 | 
				
			||||||
 | 
								RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						claim := types.SavingsClaim{
 | 
				
			||||||
 | 
							BaseMultiClaim: types.BaseMultiClaim{
 | 
				
			||||||
 | 
								Owner:  arbitraryAddress(),
 | 
				
			||||||
 | 
								Reward: arbitraryCoins(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RewardIndexes: types.MultiRewardIndexes{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									CollateralType: preexistingDenom,
 | 
				
			||||||
 | 
									RewardIndexes:  preexistingIndexes,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeSavingsClaim(claim)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no global indexes stored as the new denom is not rewarded
 | 
				
			||||||
 | 
						newDenom := "test"
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(claim.Owner, sdk.NewCoins(sdk.NewCoin(newDenom, sdk.OneInt())))
 | 
				
			||||||
 | 
						suite.keeper.InitializeSavingsReward(suite.ctx, deposit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, found := suite.keeper.GetSavingsClaim(suite.ctx, claim.Owner)
 | 
				
			||||||
 | 
						suite.True(found)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The preexisting indexes shouldn't be changed. It doesn't strictly need the new denom either.
 | 
				
			||||||
 | 
						expectedIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: preexistingDenom,
 | 
				
			||||||
 | 
								RewardIndexes:  preexistingIndexes,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: newDenom,
 | 
				
			||||||
 | 
								RewardIndexes:  nil,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.Equal(expectedIndexes, syncedClaim.RewardIndexes)
 | 
				
			||||||
 | 
						// init should never alter the rewards
 | 
				
			||||||
 | 
						suite.Equal(claim.Reward, syncedClaim.Reward)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *InitializeSavingsRewardTests) TestClaimUpdatedWhenClaimExistsAndRewardsExist() {
 | 
				
			||||||
 | 
						// When a claim exists, and a user deposits to a new rewarded denom;
 | 
				
			||||||
 | 
						// then the claim's rewards don't change and the indexes are updated to match the global indexes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						preexistingDenom := "preexisting"
 | 
				
			||||||
 | 
						preexistingIndexes := types.RewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: "rewarddenom",
 | 
				
			||||||
 | 
								RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newDenom := "test"
 | 
				
			||||||
 | 
						newIndexes := types.RewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: "otherrewarddenom",
 | 
				
			||||||
 | 
								RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						claim := types.SavingsClaim{
 | 
				
			||||||
 | 
							BaseMultiClaim: types.BaseMultiClaim{
 | 
				
			||||||
 | 
								Owner:  arbitraryAddress(),
 | 
				
			||||||
 | 
								Reward: arbitraryCoins(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RewardIndexes: types.MultiRewardIndexes{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									CollateralType: preexistingDenom,
 | 
				
			||||||
 | 
									RewardIndexes:  preexistingIndexes,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeSavingsClaim(claim)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						globalIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: preexistingDenom,
 | 
				
			||||||
 | 
								RewardIndexes:  increaseRewardFactors(preexistingIndexes),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: newDenom,
 | 
				
			||||||
 | 
								RewardIndexes:  newIndexes,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeGlobalSavingsIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(claim.Owner, sdk.NewCoins(sdk.NewCoin(newDenom, sdk.OneInt())))
 | 
				
			||||||
 | 
						suite.keeper.InitializeSavingsReward(suite.ctx, deposit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, _ := suite.keeper.GetSavingsClaim(suite.ctx, claim.Owner)
 | 
				
			||||||
 | 
						// only the indexes for the new denom should be updated
 | 
				
			||||||
 | 
						expectedIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: preexistingDenom,
 | 
				
			||||||
 | 
								RewardIndexes:  preexistingIndexes,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: newDenom,
 | 
				
			||||||
 | 
								RewardIndexes:  newIndexes,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.Equal(expectedIndexes, syncedClaim.RewardIndexes)
 | 
				
			||||||
 | 
						// init should never alter the rewards
 | 
				
			||||||
 | 
						suite.Equal(claim.Reward, syncedClaim.Reward)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										245
									
								
								x/incentive/keeper/rewards_savings_sync_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								x/incentive/keeper/rewards_savings_sync_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,245 @@
 | 
				
			|||||||
 | 
					package keeper_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdk "github.com/cosmos/cosmos-sdk/types"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/suite"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/x/incentive/types"
 | 
				
			||||||
 | 
						savingstypes "github.com/kava-labs/kava/x/savings/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SynchronizeSavingsRewardTests runs unit tests for the keeper.SynchronizeSavingsReward method
 | 
				
			||||||
 | 
					type SynchronizeSavingsRewardTests struct {
 | 
				
			||||||
 | 
						unitTester
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSynchronizeSavingsReward(t *testing.T) {
 | 
				
			||||||
 | 
						suite.Run(t, new(SynchronizeSavingsRewardTests))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *SynchronizeSavingsRewardTests) TestClaimUpdatedWhenGlobalIndexesHaveIncreased() {
 | 
				
			||||||
 | 
						// This is the normal case
 | 
				
			||||||
 | 
						// Given some time has passed (meaning the global indexes have increased)
 | 
				
			||||||
 | 
						// When the claim is synced
 | 
				
			||||||
 | 
						// The user earns rewards for the time passed, and the claim indexes are updated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						originalReward := arbitraryCoins()
 | 
				
			||||||
 | 
						denom := "test"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						claim := types.SavingsClaim{
 | 
				
			||||||
 | 
							BaseMultiClaim: types.BaseMultiClaim{
 | 
				
			||||||
 | 
								Owner:  arbitraryAddress(),
 | 
				
			||||||
 | 
								Reward: originalReward,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RewardIndexes: types.MultiRewardIndexes{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									CollateralType: denom,
 | 
				
			||||||
 | 
									RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											CollateralType: "rewarddenom",
 | 
				
			||||||
 | 
											RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeSavingsClaim(claim)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						globalIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: denom,
 | 
				
			||||||
 | 
								RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										CollateralType: "rewarddenom",
 | 
				
			||||||
 | 
										RewardFactor:   d("2000.002"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeGlobalSavingsIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userShares := i(1e9)
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(claim.Owner, sdk.NewCoins(sdk.NewCoin(denom, userShares)))
 | 
				
			||||||
 | 
						suite.keeper.SynchronizeSavingsReward(suite.ctx, deposit, []string{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, _ := suite.keeper.GetSavingsClaim(suite.ctx, claim.Owner)
 | 
				
			||||||
 | 
						// indexes updated from global
 | 
				
			||||||
 | 
						suite.Equal(globalIndexes, syncedClaim.RewardIndexes)
 | 
				
			||||||
 | 
						// new reward is (new index - old index) * user shares
 | 
				
			||||||
 | 
						suite.Equal(
 | 
				
			||||||
 | 
							cs(c("rewarddenom", 1_000_001_000_000)).Add(originalReward...),
 | 
				
			||||||
 | 
							syncedClaim.Reward,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *SynchronizeSavingsRewardTests) TestClaimUnchangedWhenGlobalIndexesUnchanged() {
 | 
				
			||||||
 | 
						denom := "test"
 | 
				
			||||||
 | 
						unchangingIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: denom,
 | 
				
			||||||
 | 
								RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										CollateralType: "rewarddenom",
 | 
				
			||||||
 | 
										RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						claim := types.SavingsClaim{
 | 
				
			||||||
 | 
							BaseMultiClaim: types.BaseMultiClaim{
 | 
				
			||||||
 | 
								Owner:  arbitraryAddress(),
 | 
				
			||||||
 | 
								Reward: arbitraryCoins(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RewardIndexes: unchangingIndexes,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeSavingsClaim(claim)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						suite.storeGlobalSavingsIndexes(unchangingIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userShares := i(1e9)
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(claim.Owner, sdk.NewCoins(sdk.NewCoin(denom, userShares)))
 | 
				
			||||||
 | 
						suite.keeper.SynchronizeSavingsReward(suite.ctx, deposit, []string{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, _ := suite.keeper.GetSavingsClaim(suite.ctx, claim.Owner)
 | 
				
			||||||
 | 
						// claim should have the same rewards and indexes as before
 | 
				
			||||||
 | 
						suite.Equal(claim, syncedClaim)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *SynchronizeSavingsRewardTests) TestClaimUpdatedWhenNewRewardAdded() {
 | 
				
			||||||
 | 
						originalReward := arbitraryCoins()
 | 
				
			||||||
 | 
						newlyRewardedDenom := "newlyRewardedDenom"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						claim := types.SavingsClaim{
 | 
				
			||||||
 | 
							BaseMultiClaim: types.BaseMultiClaim{
 | 
				
			||||||
 | 
								Owner:  arbitraryAddress(),
 | 
				
			||||||
 | 
								Reward: originalReward,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RewardIndexes: types.MultiRewardIndexes{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									CollateralType: "currentlyRewardedDenom",
 | 
				
			||||||
 | 
									RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											CollateralType: "reward",
 | 
				
			||||||
 | 
											RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeSavingsClaim(claim)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						globalIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: "currentlyRewardedDenom",
 | 
				
			||||||
 | 
								RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										CollateralType: "reward",
 | 
				
			||||||
 | 
										RewardFactor:   d("2000.002"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: newlyRewardedDenom,
 | 
				
			||||||
 | 
								RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										CollateralType: "otherreward",
 | 
				
			||||||
 | 
										// Indexes start at 0 when the reward is added by gov,
 | 
				
			||||||
 | 
										// so this represents the syncing happening some time later.
 | 
				
			||||||
 | 
										RewardFactor: d("1000.001"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeGlobalSavingsIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userShares := i(1e9)
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(claim.Owner,
 | 
				
			||||||
 | 
							sdk.NewCoins(
 | 
				
			||||||
 | 
								sdk.NewCoin("currentlyRewardedDenom", userShares),
 | 
				
			||||||
 | 
								sdk.NewCoin(newlyRewardedDenom, userShares),
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						suite.keeper.SynchronizeSavingsReward(suite.ctx, deposit, []string{newlyRewardedDenom})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, _ := suite.keeper.GetSavingsClaim(suite.ctx, claim.Owner)
 | 
				
			||||||
 | 
						// the new indexes should be added to the claim and the old ones should be updated
 | 
				
			||||||
 | 
						suite.Equal(globalIndexes, syncedClaim.RewardIndexes)
 | 
				
			||||||
 | 
						// new reward is (new index - old index) * shares for the synced deposit
 | 
				
			||||||
 | 
						// The old index for `newlyrewarded` isn't in the claim, so it's added starting at 0 for calculating the reward.
 | 
				
			||||||
 | 
						suite.Equal(
 | 
				
			||||||
 | 
							cs(c("reward", 1_000_001_000_000)).Add(originalReward...),
 | 
				
			||||||
 | 
							syncedClaim.Reward,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *SynchronizeSavingsRewardTests) TestClaimUpdatedWhenNewRewardDenomAdded() {
 | 
				
			||||||
 | 
						// When a new reward coin is added (via gov) to an already rewarded denom (that the user has already deposited to), and the claim is synced;
 | 
				
			||||||
 | 
						// Then the user earns rewards for the time since the reward was added, and the new indexes are added.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						originalReward := arbitraryCoins()
 | 
				
			||||||
 | 
						denom := "base"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						claim := types.SavingsClaim{
 | 
				
			||||||
 | 
							BaseMultiClaim: types.BaseMultiClaim{
 | 
				
			||||||
 | 
								Owner:  arbitraryAddress(),
 | 
				
			||||||
 | 
								Reward: originalReward,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RewardIndexes: types.MultiRewardIndexes{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									CollateralType: denom,
 | 
				
			||||||
 | 
									RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											CollateralType: "reward",
 | 
				
			||||||
 | 
											RewardFactor:   d("1000.001"),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeSavingsClaim(claim)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						globalIndexes := types.MultiRewardIndexes{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								CollateralType: denom,
 | 
				
			||||||
 | 
								RewardIndexes: types.RewardIndexes{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										CollateralType: "reward",
 | 
				
			||||||
 | 
										RewardFactor:   d("2000.002"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										CollateralType: "otherreward",
 | 
				
			||||||
 | 
										// Indexes start at 0 when the reward is added by gov,
 | 
				
			||||||
 | 
										// so this represents the syncing happening some time later.
 | 
				
			||||||
 | 
										RewardFactor: d("1000.001"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.storeGlobalSavingsIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userShares := i(1e9)
 | 
				
			||||||
 | 
						deposit := savingstypes.NewDeposit(claim.Owner, sdk.NewCoins(sdk.NewCoin(denom, userShares)))
 | 
				
			||||||
 | 
						suite.keeper.SynchronizeSavingsReward(suite.ctx, deposit, []string{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syncedClaim, _ := suite.keeper.GetSavingsClaim(suite.ctx, claim.Owner)
 | 
				
			||||||
 | 
						// indexes should have the new reward denom added
 | 
				
			||||||
 | 
						suite.Equal(globalIndexes, syncedClaim.RewardIndexes)
 | 
				
			||||||
 | 
						// new reward is (new index - old index) * shares
 | 
				
			||||||
 | 
						// The old index for `otherreward` isn't in the claim, so it's added starting at 0 for calculating the reward.
 | 
				
			||||||
 | 
						suite.Equal(
 | 
				
			||||||
 | 
							cs(c("reward", 1_000_001_000_000), c("otherreward", 1_000_001_000_000)).Add(originalReward...),
 | 
				
			||||||
 | 
							syncedClaim.Reward,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getDenoms(coins sdk.Coins) []string {
 | 
				
			||||||
 | 
						denoms := []string{}
 | 
				
			||||||
 | 
						for _, coin := range coins {
 | 
				
			||||||
 | 
							denoms = append(denoms, coin.Denom)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return denoms
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -32,7 +32,7 @@ func (suite *InitializeHardSupplyRewardTests) TestClaimIndexesAreSetWhenClaimExi
 | 
				
			|||||||
	globalIndexes := nonEmptyMultiRewardIndexes
 | 
						globalIndexes := nonEmptyMultiRewardIndexes
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,7 +46,7 @@ func (suite *InitializeHardSupplyRewardTests) TestClaimIndexesAreSetWhenClaimDoe
 | 
				
			|||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	owner := arbitraryAddress()
 | 
						owner := arbitraryAddress()
 | 
				
			||||||
	deposit := NewDepositBuilder(owner).
 | 
						deposit := NewHardDepositBuilder(owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,7 +66,7 @@ func (suite *InitializeHardSupplyRewardTests) TestClaimIndexesAreSetEmptyForMiss
 | 
				
			|||||||
	// This happens when a deposit denom has no rewards associated with it.
 | 
						// This happens when a deposit denom has no rewards associated with it.
 | 
				
			||||||
	expectedIndexes := appendUniqueEmptyMultiRewardIndex(globalIndexes)
 | 
						expectedIndexes := appendUniqueEmptyMultiRewardIndex(globalIndexes)
 | 
				
			||||||
	depositedDenoms := extractCollateralTypes(expectedIndexes)
 | 
						depositedDenoms := extractCollateralTypes(expectedIndexes)
 | 
				
			||||||
	deposit := NewDepositBuilder(owner).
 | 
						deposit := NewHardDepositBuilder(owner).
 | 
				
			||||||
		WithArbitrarySourceShares(depositedDenoms...).
 | 
							WithArbitrarySourceShares(depositedDenoms...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,7 @@ func (suite *SynchronizeHardSupplyRewardTests) TestClaimIndexesAreUpdatedWhenGlo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	globalIndexes := increaseAllRewardFactors(nonEmptyMultiRewardIndexes)
 | 
						globalIndexes := increaseAllRewardFactors(nonEmptyMultiRewardIndexes)
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(claim.SupplyRewardIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(claim.SupplyRewardIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,7 +56,7 @@ func (suite *SynchronizeHardSupplyRewardTests) TestClaimIndexesAreUnchangedWhenG
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(unchangingIndexes)
 | 
						suite.storeGlobalSupplyIndexes(unchangingIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(unchangingIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(unchangingIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -80,7 +80,7 @@ func (suite *SynchronizeHardSupplyRewardTests) TestClaimIndexesAreUpdatedWhenNew
 | 
				
			|||||||
	globalIndexes := appendUniqueMultiRewardIndex(nonEmptyMultiRewardIndexes)
 | 
						globalIndexes := appendUniqueMultiRewardIndex(nonEmptyMultiRewardIndexes)
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -105,7 +105,7 @@ func (suite *SynchronizeHardSupplyRewardTests) TestClaimIndexesAreUpdatedWhenNew
 | 
				
			|||||||
	globalIndexes := appendUniqueRewardIndexToFirstItem(nonEmptyMultiRewardIndexes)
 | 
						globalIndexes := appendUniqueRewardIndexToFirstItem(nonEmptyMultiRewardIndexes)
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -154,7 +154,7 @@ func (suite *SynchronizeHardSupplyRewardTests) TestRewardIsIncrementedWhenGlobal
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithSourceShares("depositdenom", 1e9).
 | 
							WithSourceShares("depositdenom", 1e9).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -216,7 +216,7 @@ func (suite *SynchronizeHardSupplyRewardTests) TestRewardIsIncrementedWhenNewRew
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithSourceShares("rewarded", 1e9).
 | 
							WithSourceShares("rewarded", 1e9).
 | 
				
			||||||
		WithSourceShares("newlyrewarded", 1e9).
 | 
							WithSourceShares("newlyrewarded", 1e9).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
@ -274,7 +274,7 @@ func (suite *SynchronizeHardSupplyRewardTests) TestRewardIsIncrementedWhenNewRew
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithSourceShares("deposited", 1e9).
 | 
							WithSourceShares("deposited", 1e9).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -289,26 +289,26 @@ func (suite *SynchronizeHardSupplyRewardTests) TestRewardIsIncrementedWhenNewRew
 | 
				
			|||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DepositBuilder is a tool for creating a hard deposit in tests.
 | 
					// HardDepositBuilder is a tool for creating a hard deposit in tests.
 | 
				
			||||||
// The builder inherits from hard.Deposit, so fields can be accessed directly if a helper method doesn't exist.
 | 
					// The builder inherits from hard.Deposit, so fields can be accessed directly if a helper method doesn't exist.
 | 
				
			||||||
type DepositBuilder struct {
 | 
					type HardDepositBuilder struct {
 | 
				
			||||||
	hardtypes.Deposit
 | 
						hardtypes.Deposit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDepositBuilder creates a DepositBuilder containing an empty deposit.
 | 
					// NewHardDepositBuilder creates a HardDepositBuilder containing an empty deposit.
 | 
				
			||||||
func NewDepositBuilder(depositor sdk.AccAddress) DepositBuilder {
 | 
					func NewHardDepositBuilder(depositor sdk.AccAddress) HardDepositBuilder {
 | 
				
			||||||
	return DepositBuilder{
 | 
						return HardDepositBuilder{
 | 
				
			||||||
		Deposit: hardtypes.Deposit{
 | 
							Deposit: hardtypes.Deposit{
 | 
				
			||||||
			Depositor: depositor,
 | 
								Depositor: depositor,
 | 
				
			||||||
		}}
 | 
							}}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Build assembles and returns the final deposit.
 | 
					// Build assembles and returns the final deposit.
 | 
				
			||||||
func (builder DepositBuilder) Build() hardtypes.Deposit { return builder.Deposit }
 | 
					func (builder HardDepositBuilder) Build() hardtypes.Deposit { return builder.Deposit }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithSourceShares adds a deposit amount and factor such that the source shares for this deposit is equal to specified.
 | 
					// WithSourceShares adds a deposit amount and factor such that the source shares for this deposit is equal to specified.
 | 
				
			||||||
// With a factor of 1, the deposit amount is the source shares. This picks an arbitrary factor to ensure factors are accounted for in production code.
 | 
					// With a factor of 1, the deposit amount is the source shares. This picks an arbitrary factor to ensure factors are accounted for in production code.
 | 
				
			||||||
func (builder DepositBuilder) WithSourceShares(denom string, shares int64) DepositBuilder {
 | 
					func (builder HardDepositBuilder) WithSourceShares(denom string, shares int64) HardDepositBuilder {
 | 
				
			||||||
	if !builder.Amount.AmountOf(denom).Equal(sdk.ZeroInt()) {
 | 
						if !builder.Amount.AmountOf(denom).Equal(sdk.ZeroInt()) {
 | 
				
			||||||
		panic("adding to amount with existing denom not implemented")
 | 
							panic("adding to amount with existing denom not implemented")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -328,7 +328,7 @@ func (builder DepositBuilder) WithSourceShares(denom string, shares int64) Depos
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithArbitrarySourceShares adds arbitrary deposit amounts and indexes for each specified denom.
 | 
					// WithArbitrarySourceShares adds arbitrary deposit amounts and indexes for each specified denom.
 | 
				
			||||||
func (builder DepositBuilder) WithArbitrarySourceShares(denoms ...string) DepositBuilder {
 | 
					func (builder HardDepositBuilder) WithArbitrarySourceShares(denoms ...string) HardDepositBuilder {
 | 
				
			||||||
	const arbitraryShares = 1e9
 | 
						const arbitraryShares = 1e9
 | 
				
			||||||
	for _, denom := range denoms {
 | 
						for _, denom := range denoms {
 | 
				
			||||||
		builder = builder.WithSourceShares(denom, arbitraryShares)
 | 
							builder = builder.WithSourceShares(denom, arbitraryShares)
 | 
				
			||||||
 | 
				
			|||||||
@ -29,7 +29,7 @@ func (suite *UpdateHardSupplyIndexDenomsTests) TestClaimIndexesAreRemovedForDeno
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// remove one denom from the indexes already in the deposit
 | 
						// remove one denom from the indexes already in the deposit
 | 
				
			||||||
	expectedIndexes := claim.SupplyRewardIndexes[1:]
 | 
						expectedIndexes := claim.SupplyRewardIndexes[1:]
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(expectedIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(expectedIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,7 +50,7 @@ func (suite *UpdateHardSupplyIndexDenomsTests) TestClaimIndexesAreAddedForNewlyS
 | 
				
			|||||||
	globalIndexes := appendUniqueMultiRewardIndex(claim.SupplyRewardIndexes)
 | 
						globalIndexes := appendUniqueMultiRewardIndex(claim.SupplyRewardIndexes)
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
						suite.storeGlobalSupplyIndexes(globalIndexes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(globalIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -72,7 +72,7 @@ func (suite *UpdateHardSupplyIndexDenomsTests) TestClaimIndexesAreUnchangedWhenS
 | 
				
			|||||||
	// UpdateHardSupplyIndexDenoms should ignore the new values.
 | 
						// UpdateHardSupplyIndexDenoms should ignore the new values.
 | 
				
			||||||
	suite.storeGlobalSupplyIndexes(increaseAllRewardFactors(claim.SupplyRewardIndexes))
 | 
						suite.storeGlobalSupplyIndexes(increaseAllRewardFactors(claim.SupplyRewardIndexes))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(extractCollateralTypes(claim.SupplyRewardIndexes)...).
 | 
							WithArbitrarySourceShares(extractCollateralTypes(claim.SupplyRewardIndexes)...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -95,7 +95,7 @@ func (suite *UpdateHardSupplyIndexDenomsTests) TestEmptyClaimIndexesAreAddedForN
 | 
				
			|||||||
	// add a denom to the deposited amount that is not in the global or claim's indexes
 | 
						// add a denom to the deposited amount that is not in the global or claim's indexes
 | 
				
			||||||
	expectedIndexes := appendUniqueEmptyMultiRewardIndex(claim.SupplyRewardIndexes)
 | 
						expectedIndexes := appendUniqueEmptyMultiRewardIndex(claim.SupplyRewardIndexes)
 | 
				
			||||||
	depositedDenoms := extractCollateralTypes(expectedIndexes)
 | 
						depositedDenoms := extractCollateralTypes(expectedIndexes)
 | 
				
			||||||
	deposit := NewDepositBuilder(claim.Owner).
 | 
						deposit := NewHardDepositBuilder(claim.Owner).
 | 
				
			||||||
		WithArbitrarySourceShares(depositedDenoms...).
 | 
							WithArbitrarySourceShares(depositedDenoms...).
 | 
				
			||||||
		Build()
 | 
							Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -92,6 +92,11 @@ func (suite *unitTester) storeGlobalSwapIndexes(indexes types.MultiRewardIndexes
 | 
				
			|||||||
		suite.keeper.SetSwapRewardIndexes(suite.ctx, i.CollateralType, i.RewardIndexes)
 | 
							suite.keeper.SetSwapRewardIndexes(suite.ctx, i.CollateralType, i.RewardIndexes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					func (suite *unitTester) storeGlobalSavingsIndexes(indexes types.MultiRewardIndexes) {
 | 
				
			||||||
 | 
						for _, i := range indexes {
 | 
				
			||||||
 | 
							suite.keeper.SetSavingsRewardIndexes(suite.ctx, i.CollateralType, i.RewardIndexes)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (suite *unitTester) storeHardClaim(claim types.HardLiquidityProviderClaim) {
 | 
					func (suite *unitTester) storeHardClaim(claim types.HardLiquidityProviderClaim) {
 | 
				
			||||||
	suite.keeper.SetHardLiquidityProviderClaim(suite.ctx, claim)
 | 
						suite.keeper.SetHardLiquidityProviderClaim(suite.ctx, claim)
 | 
				
			||||||
@ -102,6 +107,9 @@ func (suite *unitTester) storeDelegatorClaim(claim types.DelegatorClaim) {
 | 
				
			|||||||
func (suite *unitTester) storeSwapClaim(claim types.SwapClaim) {
 | 
					func (suite *unitTester) storeSwapClaim(claim types.SwapClaim) {
 | 
				
			||||||
	suite.keeper.SetSwapClaim(suite.ctx, claim)
 | 
						suite.keeper.SetSwapClaim(suite.ctx, claim)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					func (suite *unitTester) storeSavingsClaim(claim types.SavingsClaim) {
 | 
				
			||||||
 | 
						suite.keeper.SetSavingsClaim(suite.ctx, claim)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// fakeParamSubspace is a stub paramSpace to simplify keeper unit test setup.
 | 
					// fakeParamSubspace is a stub paramSpace to simplify keeper unit test setup.
 | 
				
			||||||
type fakeParamSubspace struct {
 | 
					type fakeParamSubspace struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -21,13 +21,20 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	currDeposit, foundDeposit := k.GetDeposit(ctx, depositor)
 | 
						currDeposit, foundDeposit := k.GetDeposit(ctx, depositor)
 | 
				
			||||||
	amount := coins
 | 
					
 | 
				
			||||||
 | 
						deposit := types.NewDeposit(depositor, coins)
 | 
				
			||||||
	if foundDeposit {
 | 
						if foundDeposit {
 | 
				
			||||||
		amount = amount.Add(currDeposit.Amount...)
 | 
							deposit.Amount = deposit.Amount.Add(currDeposit.Amount...)
 | 
				
			||||||
 | 
							k.hooks.BeforeSavingsDepositModified(ctx, deposit, setDifference(getDenoms(coins), getDenoms(deposit.Amount)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	deposit := types.NewDeposit(depositor, amount)
 | 
					
 | 
				
			||||||
	k.SetDeposit(ctx, deposit)
 | 
						k.SetDeposit(ctx, deposit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !foundDeposit {
 | 
				
			||||||
 | 
							k.hooks.AfterSavingsDepositCreated(ctx, deposit)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.EventManager().EmitEvent(
 | 
						ctx.EventManager().EmitEvent(
 | 
				
			||||||
		sdk.NewEvent(
 | 
							sdk.NewEvent(
 | 
				
			||||||
			types.EventTypeSavingsDeposit,
 | 
								types.EventTypeSavingsDeposit,
 | 
				
			||||||
@ -43,7 +50,7 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
 | 
				
			|||||||
func (k Keeper) ValidateDeposit(ctx sdk.Context, coins sdk.Coins) error {
 | 
					func (k Keeper) ValidateDeposit(ctx sdk.Context, coins sdk.Coins) error {
 | 
				
			||||||
	for _, coin := range coins {
 | 
						for _, coin := range coins {
 | 
				
			||||||
		supported := k.IsDenomSupported(ctx, coin.Denom)
 | 
							supported := k.IsDenomSupported(ctx, coin.Denom)
 | 
				
			||||||
		if supported == false {
 | 
							if !supported {
 | 
				
			||||||
			return sdkerrors.Wrapf(types.ErrInvalidDepositDenom, ": %s", coin.Denom)
 | 
								return sdkerrors.Wrapf(types.ErrInvalidDepositDenom, ": %s", coin.Denom)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -56,3 +63,27 @@ func (k Keeper) GetTotalDeposited(ctx sdk.Context, depositDenom string) (total s
 | 
				
			|||||||
	macc := k.accountKeeper.GetModuleAccount(ctx, types.ModuleAccountName)
 | 
						macc := k.accountKeeper.GetModuleAccount(ctx, types.ModuleAccountName)
 | 
				
			||||||
	return k.bankKeeper.GetBalance(ctx, macc.GetAddress(), depositDenom).Amount
 | 
						return k.bankKeeper.GetBalance(ctx, macc.GetAddress(), depositDenom).Amount
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set setDifference: A - B
 | 
				
			||||||
 | 
					func setDifference(a, b []string) (diff []string) {
 | 
				
			||||||
 | 
						m := make(map[string]bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range b {
 | 
				
			||||||
 | 
							m[item] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, item := range a {
 | 
				
			||||||
 | 
							if _, ok := m[item]; !ok {
 | 
				
			||||||
 | 
								diff = append(diff, item)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getDenoms(coins sdk.Coins) []string {
 | 
				
			||||||
 | 
						denoms := []string{}
 | 
				
			||||||
 | 
						for _, coin := range coins {
 | 
				
			||||||
 | 
							denoms = append(denoms, coin.Denom)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return denoms
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										29
									
								
								x/savings/keeper/diff_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								x/savings/keeper/diff_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					package keeper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSetDiff(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name     string
 | 
				
			||||||
 | 
							setA     []string
 | 
				
			||||||
 | 
							setB     []string
 | 
				
			||||||
 | 
							expected []string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{"empty", []string{}, []string{}, []string(nil)},
 | 
				
			||||||
 | 
							{"diff equal sets", []string{"busd", "usdx"}, []string{"busd", "usdx"}, []string(nil)},
 | 
				
			||||||
 | 
							{"diff set empty", []string{"bnb", "ukava", "usdx"}, []string{}, []string{"bnb", "ukava", "usdx"}},
 | 
				
			||||||
 | 
							{"input set empty", []string{}, []string{"bnb", "ukava", "usdx"}, []string(nil)},
 | 
				
			||||||
 | 
							{"diff set with common elements", []string{"bnb", "btcb", "usdx", "xrpb"}, []string{"bnb", "usdx"}, []string{"btcb", "xrpb"}},
 | 
				
			||||||
 | 
							{"diff set with all common elements", []string{"bnb", "usdx"}, []string{"bnb", "btcb", "usdx", "xrpb"}, []string(nil)},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								require.Equal(t, tt.expected, setDifference(tt.setA, tt.setB))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user