mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 04:47:27 +00:00 
			
		
		
		
	Fix: delete incentive reward factors on full withdraw/repay (#885)
* delete incentive reward index on repay/withdraw * call hook on borrow in all cases * additional types functionality * extend tests to cover fix * update naming convention in tests * update test comment * feat: add set difference unit tests * clarify test names Co-authored-by: karzak <kjydavis3@gmail.com>
This commit is contained in:
		
							parent
							
								
									d601481b95
								
							
						
					
					
						commit
						eb856b5a1f
					
				@ -67,9 +67,7 @@ func (k Keeper) Repay(ctx sdk.Context, sender, owner sdk.AccAddress, coins sdk.C
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Call incentive hook
 | 
						// Call incentive hook
 | 
				
			||||||
	if !borrow.Amount.Empty() {
 | 
						k.AfterBorrowModified(ctx, borrow)
 | 
				
			||||||
		k.AfterBorrowModified(ctx, borrow)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.EventManager().EmitEvent(
 | 
						ctx.EventManager().EmitEvent(
 | 
				
			||||||
		sdk.NewEvent(
 | 
							sdk.NewEvent(
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										29
									
								
								x/incentive/keeper/diff_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								x/incentive/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))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -427,51 +427,71 @@ func (k Keeper) UpdateHardSupplyIndexDenoms(ctx sdk.Context, deposit hardtypes.D
 | 
				
			|||||||
		claim = types.NewHardLiquidityProviderClaim(deposit.Depositor, sdk.Coins{}, nil, nil, nil)
 | 
							claim = types.NewHardLiquidityProviderClaim(deposit.Depositor, sdk.Coins{}, nil, nil, nil)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						depositDenoms := getDenoms(deposit.Amount)
 | 
				
			||||||
 | 
						supplyRewardIndexDenoms := claim.SupplyRewardIndexes.GetCollateralTypes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uniqueDepositDenoms := setDifference(depositDenoms, supplyRewardIndexDenoms)
 | 
				
			||||||
 | 
						uniqueSupplyRewardDenoms := setDifference(supplyRewardIndexDenoms, depositDenoms)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	supplyRewardIndexes := claim.SupplyRewardIndexes
 | 
						supplyRewardIndexes := claim.SupplyRewardIndexes
 | 
				
			||||||
	for _, coin := range deposit.Amount {
 | 
						// Create a new multi-reward index in the claim for every new deposit denom
 | 
				
			||||||
		_, foundUserRewardIndexes := claim.SupplyRewardIndexes.GetRewardIndex(coin.Denom)
 | 
						for _, denom := range uniqueDepositDenoms {
 | 
				
			||||||
 | 
							_, foundUserRewardIndexes := claim.SupplyRewardIndexes.GetRewardIndex(denom)
 | 
				
			||||||
		if !foundUserRewardIndexes {
 | 
							if !foundUserRewardIndexes {
 | 
				
			||||||
			globalSupplyRewardIndexes, foundGlobalSupplyRewardIndexes := k.GetHardSupplyRewardIndexes(ctx, coin.Denom)
 | 
								globalSupplyRewardIndexes, foundGlobalSupplyRewardIndexes := k.GetHardSupplyRewardIndexes(ctx, denom)
 | 
				
			||||||
			var multiRewardIndex types.MultiRewardIndex
 | 
								var multiRewardIndex types.MultiRewardIndex
 | 
				
			||||||
			if foundGlobalSupplyRewardIndexes {
 | 
								if foundGlobalSupplyRewardIndexes {
 | 
				
			||||||
				multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, globalSupplyRewardIndexes)
 | 
									multiRewardIndex = types.NewMultiRewardIndex(denom, globalSupplyRewardIndexes)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, types.RewardIndexes{})
 | 
									multiRewardIndex = types.NewMultiRewardIndex(denom, types.RewardIndexes{})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			supplyRewardIndexes = append(supplyRewardIndexes, multiRewardIndex)
 | 
								supplyRewardIndexes = append(supplyRewardIndexes, multiRewardIndex)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(supplyRewardIndexes) == 0 {
 | 
					
 | 
				
			||||||
		return
 | 
						// Delete multi-reward index from claim if the collateral type is no longer deposited
 | 
				
			||||||
 | 
						for _, denom := range uniqueSupplyRewardDenoms {
 | 
				
			||||||
 | 
							supplyRewardIndexes = supplyRewardIndexes.RemoveRewardIndex(denom)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	claim.SupplyRewardIndexes = supplyRewardIndexes
 | 
						claim.SupplyRewardIndexes = supplyRewardIndexes
 | 
				
			||||||
	k.SetHardLiquidityProviderClaim(ctx, claim)
 | 
						k.SetHardLiquidityProviderClaim(ctx, claim)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateHardBorrowIndexDenoms adds any new borrow denoms to the claim's supply reward index
 | 
					// UpdateHardBorrowIndexDenoms adds any new borrow denoms to the claim's borrow reward index
 | 
				
			||||||
func (k Keeper) UpdateHardBorrowIndexDenoms(ctx sdk.Context, borrow hardtypes.Borrow) {
 | 
					func (k Keeper) UpdateHardBorrowIndexDenoms(ctx sdk.Context, borrow hardtypes.Borrow) {
 | 
				
			||||||
	claim, found := k.GetHardLiquidityProviderClaim(ctx, borrow.Borrower)
 | 
						claim, found := k.GetHardLiquidityProviderClaim(ctx, borrow.Borrower)
 | 
				
			||||||
	if !found {
 | 
						if !found {
 | 
				
			||||||
		claim = types.NewHardLiquidityProviderClaim(borrow.Borrower, sdk.Coins{}, nil, nil, nil)
 | 
							claim = types.NewHardLiquidityProviderClaim(borrow.Borrower, sdk.Coins{}, nil, nil, nil)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						borrowDenoms := getDenoms(borrow.Amount)
 | 
				
			||||||
 | 
						borrowRewardIndexDenoms := claim.BorrowRewardIndexes.GetCollateralTypes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uniqueBorrowDenoms := setDifference(borrowDenoms, borrowRewardIndexDenoms)
 | 
				
			||||||
 | 
						uniqueBorrowRewardDenoms := setDifference(borrowRewardIndexDenoms, borrowDenoms)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	borrowRewardIndexes := claim.BorrowRewardIndexes
 | 
						borrowRewardIndexes := claim.BorrowRewardIndexes
 | 
				
			||||||
	for _, coin := range borrow.Amount {
 | 
						// Create a new multi-reward index in the claim for every new borrow denom
 | 
				
			||||||
		_, foundUserRewardIndexes := claim.BorrowRewardIndexes.GetRewardIndex(coin.Denom)
 | 
						for _, denom := range uniqueBorrowDenoms {
 | 
				
			||||||
 | 
							_, foundUserRewardIndexes := claim.BorrowRewardIndexes.GetRewardIndex(denom)
 | 
				
			||||||
		if !foundUserRewardIndexes {
 | 
							if !foundUserRewardIndexes {
 | 
				
			||||||
			globalBorrowRewardIndexes, foundGlobalBorrowRewardIndexes := k.GetHardBorrowRewardIndexes(ctx, coin.Denom)
 | 
								globalBorrowRewardIndexes, foundGlobalBorrowRewardIndexes := k.GetHardBorrowRewardIndexes(ctx, denom)
 | 
				
			||||||
			var multiRewardIndex types.MultiRewardIndex
 | 
								var multiRewardIndex types.MultiRewardIndex
 | 
				
			||||||
			if foundGlobalBorrowRewardIndexes {
 | 
								if foundGlobalBorrowRewardIndexes {
 | 
				
			||||||
				multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, globalBorrowRewardIndexes)
 | 
									multiRewardIndex = types.NewMultiRewardIndex(denom, globalBorrowRewardIndexes)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				multiRewardIndex = types.NewMultiRewardIndex(coin.Denom, types.RewardIndexes{})
 | 
									multiRewardIndex = types.NewMultiRewardIndex(denom, types.RewardIndexes{})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			borrowRewardIndexes = append(borrowRewardIndexes, multiRewardIndex)
 | 
								borrowRewardIndexes = append(borrowRewardIndexes, multiRewardIndex)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(borrowRewardIndexes) == 0 {
 | 
					
 | 
				
			||||||
		return
 | 
						// Delete multi-reward index from claim if the collateral type is no longer borrowed
 | 
				
			||||||
 | 
						for _, denom := range uniqueBorrowRewardDenoms {
 | 
				
			||||||
 | 
							borrowRewardIndexes = borrowRewardIndexes.RemoveRewardIndex(denom)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	claim.BorrowRewardIndexes = borrowRewardIndexes
 | 
						claim.BorrowRewardIndexes = borrowRewardIndexes
 | 
				
			||||||
	k.SetHardLiquidityProviderClaim(ctx, claim)
 | 
						k.SetHardLiquidityProviderClaim(ctx, claim)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -870,3 +890,27 @@ func (k Keeper) SimulateUSDXMintingSynchronization(ctx sdk.Context, claim types.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return claim
 | 
						return claim
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1702,9 +1702,14 @@ func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
					func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			||||||
 | 
						type depositModification struct {
 | 
				
			||||||
 | 
							coins    sdk.Coins
 | 
				
			||||||
 | 
							withdraw bool
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	type args struct {
 | 
						type args struct {
 | 
				
			||||||
		firstDeposit              sdk.Coins
 | 
							firstDeposit              sdk.Coins
 | 
				
			||||||
		secondDeposit             sdk.Coins
 | 
							modification              depositModification
 | 
				
			||||||
		rewardsPerSecond          sdk.Coins
 | 
							rewardsPerSecond          sdk.Coins
 | 
				
			||||||
		initialTime               time.Time
 | 
							initialTime               time.Time
 | 
				
			||||||
		expectedSupplyIndexDenoms []string
 | 
							expectedSupplyIndexDenoms []string
 | 
				
			||||||
@ -1719,7 +1724,7 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			|||||||
			"single reward denom: update adds one supply reward index",
 | 
								"single reward denom: update adds one supply reward index",
 | 
				
			||||||
			args{
 | 
								args{
 | 
				
			||||||
				firstDeposit:              cs(c("bnb", 10000000000)),
 | 
									firstDeposit:              cs(c("bnb", 10000000000)),
 | 
				
			||||||
				secondDeposit:             cs(c("ukava", 10000000000)),
 | 
									modification:              depositModification{coins: cs(c("ukava", 10000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedSupplyIndexDenoms: []string{"bnb", "ukava"},
 | 
									expectedSupplyIndexDenoms: []string{"bnb", "ukava"},
 | 
				
			||||||
@ -1729,7 +1734,7 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			|||||||
			"single reward denom: update adds multiple supply reward indexes",
 | 
								"single reward denom: update adds multiple supply reward indexes",
 | 
				
			||||||
			args{
 | 
								args{
 | 
				
			||||||
				firstDeposit:              cs(c("bnb", 10000000000)),
 | 
									firstDeposit:              cs(c("bnb", 10000000000)),
 | 
				
			||||||
				secondDeposit:             cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000)),
 | 
									modification:              depositModification{coins: cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"},
 | 
									expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"},
 | 
				
			||||||
@ -1739,7 +1744,7 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			|||||||
			"single reward denom: update doesn't add duplicate supply reward index for same denom",
 | 
								"single reward denom: update doesn't add duplicate supply reward index for same denom",
 | 
				
			||||||
			args{
 | 
								args{
 | 
				
			||||||
				firstDeposit:              cs(c("bnb", 10000000000)),
 | 
									firstDeposit:              cs(c("bnb", 10000000000)),
 | 
				
			||||||
				secondDeposit:             cs(c("bnb", 5000000000)),
 | 
									modification:              depositModification{coins: cs(c("bnb", 5000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedSupplyIndexDenoms: []string{"bnb"},
 | 
									expectedSupplyIndexDenoms: []string{"bnb"},
 | 
				
			||||||
@ -1749,7 +1754,7 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			|||||||
			"multiple reward denoms: update adds one supply reward index",
 | 
								"multiple reward denoms: update adds one supply reward index",
 | 
				
			||||||
			args{
 | 
								args{
 | 
				
			||||||
				firstDeposit:              cs(c("bnb", 10000000000)),
 | 
									firstDeposit:              cs(c("bnb", 10000000000)),
 | 
				
			||||||
				secondDeposit:             cs(c("ukava", 10000000000)),
 | 
									modification:              depositModification{coins: cs(c("ukava", 10000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedSupplyIndexDenoms: []string{"bnb", "ukava"},
 | 
									expectedSupplyIndexDenoms: []string{"bnb", "ukava"},
 | 
				
			||||||
@ -1759,7 +1764,7 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			|||||||
			"multiple reward denoms: update adds multiple supply reward indexes",
 | 
								"multiple reward denoms: update adds multiple supply reward indexes",
 | 
				
			||||||
			args{
 | 
								args{
 | 
				
			||||||
				firstDeposit:              cs(c("bnb", 10000000000)),
 | 
									firstDeposit:              cs(c("bnb", 10000000000)),
 | 
				
			||||||
				secondDeposit:             cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000)),
 | 
									modification:              depositModification{coins: cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"},
 | 
									expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"},
 | 
				
			||||||
@ -1769,12 +1774,42 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			|||||||
			"multiple reward denoms: update doesn't add duplicate supply reward index for same denom",
 | 
								"multiple reward denoms: update doesn't add duplicate supply reward index for same denom",
 | 
				
			||||||
			args{
 | 
								args{
 | 
				
			||||||
				firstDeposit:              cs(c("bnb", 10000000000)),
 | 
									firstDeposit:              cs(c("bnb", 10000000000)),
 | 
				
			||||||
				secondDeposit:             cs(c("bnb", 5000000000)),
 | 
									modification:              depositModification{coins: cs(c("bnb", 5000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedSupplyIndexDenoms: []string{"bnb"},
 | 
									expectedSupplyIndexDenoms: []string{"bnb"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"single reward denom: fully withdrawing a denom deletes the denom's supply reward index",
 | 
				
			||||||
 | 
								args{
 | 
				
			||||||
 | 
									firstDeposit:              cs(c("bnb", 1000000000)),
 | 
				
			||||||
 | 
									modification:              depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true},
 | 
				
			||||||
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
 | 
									expectedSupplyIndexDenoms: []string{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"single reward denom: fully withdrawing a denom deletes only the denom's supply reward index",
 | 
				
			||||||
 | 
								args{
 | 
				
			||||||
 | 
									firstDeposit:              cs(c("bnb", 1000000000), c("ukava", 100000000)),
 | 
				
			||||||
 | 
									modification:              depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true},
 | 
				
			||||||
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
 | 
									expectedSupplyIndexDenoms: []string{"ukava"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"multiple reward denoms: fully repaying a denom deletes the denom's supply reward index",
 | 
				
			||||||
 | 
								args{
 | 
				
			||||||
 | 
									firstDeposit:              cs(c("bnb", 1000000000)),
 | 
				
			||||||
 | 
									modification:              depositModification{coins: cs(c("bnb", 1100000000)), withdraw: true},
 | 
				
			||||||
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
 | 
									expectedSupplyIndexDenoms: []string{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range testCases {
 | 
						for _, tc := range testCases {
 | 
				
			||||||
		suite.Run(tc.name, func() {
 | 
							suite.Run(tc.name, func() {
 | 
				
			||||||
@ -1833,18 +1868,22 @@ func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			suite.Require().True(len(claimAfterFirstDeposit.SupplyRewardIndexes) == len(tc.args.firstDeposit))
 | 
								suite.Require().True(len(claimAfterFirstDeposit.SupplyRewardIndexes) == len(tc.args.firstDeposit))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// User deposits (second time)
 | 
								// User modifies their Deposit by withdrawing or depositing more
 | 
				
			||||||
			err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.secondDeposit)
 | 
								if tc.args.modification.withdraw {
 | 
				
			||||||
 | 
									err = hardKeeper.Withdraw(suite.ctx, userAddr, tc.args.modification.coins)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.modification.coins)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			suite.Require().NoError(err)
 | 
								suite.Require().NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Confirm that the claim contains all expected supply indexes
 | 
								// Confirm that the claim contains all expected supply indexes
 | 
				
			||||||
			claimAfterSecondDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
 | 
								claimAfterModification, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
 | 
				
			||||||
			suite.Require().True(found)
 | 
								suite.Require().True(found)
 | 
				
			||||||
			for _, denom := range tc.args.expectedSupplyIndexDenoms {
 | 
								for _, denom := range tc.args.expectedSupplyIndexDenoms {
 | 
				
			||||||
				_, hasIndex := claimAfterSecondDeposit.HasSupplyRewardIndex(denom)
 | 
									_, hasIndex := claimAfterModification.HasSupplyRewardIndex(denom)
 | 
				
			||||||
				suite.Require().True(hasIndex)
 | 
									suite.Require().True(hasIndex)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			suite.Require().True(len(claimAfterSecondDeposit.SupplyRewardIndexes) == len(tc.args.expectedSupplyIndexDenoms))
 | 
								suite.Require().True(len(claimAfterModification.SupplyRewardIndexes) == len(tc.args.expectedSupplyIndexDenoms))
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -2036,10 +2075,15 @@ func (suite *KeeperTestSuite) TestInitializeHardBorrowRewards() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
					func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			||||||
 | 
						type withdrawModification struct {
 | 
				
			||||||
 | 
							coins sdk.Coins
 | 
				
			||||||
 | 
							repay bool
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	type args struct {
 | 
						type args struct {
 | 
				
			||||||
		initialDeposit            sdk.Coins
 | 
							initialDeposit            sdk.Coins
 | 
				
			||||||
		firstBorrow               sdk.Coins
 | 
							firstBorrow               sdk.Coins
 | 
				
			||||||
		secondBorrow              sdk.Coins
 | 
							modification              withdrawModification
 | 
				
			||||||
		rewardsPerSecond          sdk.Coins
 | 
							rewardsPerSecond          sdk.Coins
 | 
				
			||||||
		initialTime               time.Time
 | 
							initialTime               time.Time
 | 
				
			||||||
		expectedBorrowIndexDenoms []string
 | 
							expectedBorrowIndexDenoms []string
 | 
				
			||||||
@ -2055,7 +2099,7 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
			args{
 | 
								args{
 | 
				
			||||||
				initialDeposit:            cs(c("bnb", 10000000000)),
 | 
									initialDeposit:            cs(c("bnb", 10000000000)),
 | 
				
			||||||
				firstBorrow:               cs(c("bnb", 50000000)),
 | 
									firstBorrow:               cs(c("bnb", 50000000)),
 | 
				
			||||||
				secondBorrow:              cs(c("ukava", 500000000)),
 | 
									modification:              withdrawModification{coins: cs(c("ukava", 500000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedBorrowIndexDenoms: []string{"bnb", "ukava"},
 | 
									expectedBorrowIndexDenoms: []string{"bnb", "ukava"},
 | 
				
			||||||
@ -2066,7 +2110,7 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
			args{
 | 
								args{
 | 
				
			||||||
				initialDeposit:            cs(c("btcb", 10000000000)),
 | 
									initialDeposit:            cs(c("btcb", 10000000000)),
 | 
				
			||||||
				firstBorrow:               cs(c("btcb", 50000000)),
 | 
									firstBorrow:               cs(c("btcb", 50000000)),
 | 
				
			||||||
				secondBorrow:              cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000)),
 | 
									modification:              withdrawModification{coins: cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"},
 | 
									expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"},
 | 
				
			||||||
@ -2077,7 +2121,7 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
			args{
 | 
								args{
 | 
				
			||||||
				initialDeposit:            cs(c("bnb", 100000000000)),
 | 
									initialDeposit:            cs(c("bnb", 100000000000)),
 | 
				
			||||||
				firstBorrow:               cs(c("bnb", 50000000)),
 | 
									firstBorrow:               cs(c("bnb", 50000000)),
 | 
				
			||||||
				secondBorrow:              cs(c("bnb", 50000000000)),
 | 
									modification:              withdrawModification{coins: cs(c("bnb", 50000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedBorrowIndexDenoms: []string{"bnb"},
 | 
									expectedBorrowIndexDenoms: []string{"bnb"},
 | 
				
			||||||
@ -2088,7 +2132,7 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
			args{
 | 
								args{
 | 
				
			||||||
				initialDeposit:            cs(c("bnb", 10000000000)),
 | 
									initialDeposit:            cs(c("bnb", 10000000000)),
 | 
				
			||||||
				firstBorrow:               cs(c("bnb", 50000000)),
 | 
									firstBorrow:               cs(c("bnb", 50000000)),
 | 
				
			||||||
				secondBorrow:              cs(c("ukava", 500000000)),
 | 
									modification:              withdrawModification{coins: cs(c("ukava", 500000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedBorrowIndexDenoms: []string{"bnb", "ukava"},
 | 
									expectedBorrowIndexDenoms: []string{"bnb", "ukava"},
 | 
				
			||||||
@ -2099,7 +2143,7 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
			args{
 | 
								args{
 | 
				
			||||||
				initialDeposit:            cs(c("btcb", 10000000000)),
 | 
									initialDeposit:            cs(c("btcb", 10000000000)),
 | 
				
			||||||
				firstBorrow:               cs(c("btcb", 50000000)),
 | 
									firstBorrow:               cs(c("btcb", 50000000)),
 | 
				
			||||||
				secondBorrow:              cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000)),
 | 
									modification:              withdrawModification{coins: cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"},
 | 
									expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"},
 | 
				
			||||||
@ -2110,12 +2154,45 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
			args{
 | 
								args{
 | 
				
			||||||
				initialDeposit:            cs(c("bnb", 100000000000)),
 | 
									initialDeposit:            cs(c("bnb", 100000000000)),
 | 
				
			||||||
				firstBorrow:               cs(c("bnb", 50000000)),
 | 
									firstBorrow:               cs(c("bnb", 50000000)),
 | 
				
			||||||
				secondBorrow:              cs(c("bnb", 50000000000)),
 | 
									modification:              withdrawModification{coins: cs(c("bnb", 50000000000))},
 | 
				
			||||||
				rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
				initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
				expectedBorrowIndexDenoms: []string{"bnb"},
 | 
									expectedBorrowIndexDenoms: []string{"bnb"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"single reward denom: fully repaying a denom deletes the denom's supply reward index",
 | 
				
			||||||
 | 
								args{
 | 
				
			||||||
 | 
									initialDeposit:            cs(c("bnb", 1000000000)),
 | 
				
			||||||
 | 
									firstBorrow:               cs(c("bnb", 100000000)),
 | 
				
			||||||
 | 
									modification:              withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true},
 | 
				
			||||||
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
 | 
									expectedBorrowIndexDenoms: []string{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"single reward denom: fully repaying a denom deletes only the denom's supply reward index",
 | 
				
			||||||
 | 
								args{
 | 
				
			||||||
 | 
									initialDeposit:            cs(c("bnb", 1000000000)),
 | 
				
			||||||
 | 
									firstBorrow:               cs(c("bnb", 100000000), c("ukava", 10000000)),
 | 
				
			||||||
 | 
									modification:              withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true},
 | 
				
			||||||
 | 
									rewardsPerSecond:          cs(c("hard", 122354)),
 | 
				
			||||||
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
 | 
									expectedBorrowIndexDenoms: []string{"ukava"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"multiple reward denoms: fully repaying a denom deletes the denom's supply reward index",
 | 
				
			||||||
 | 
								args{
 | 
				
			||||||
 | 
									initialDeposit:            cs(c("bnb", 1000000000)),
 | 
				
			||||||
 | 
									firstBorrow:               cs(c("bnb", 100000000), c("ukava", 10000000)),
 | 
				
			||||||
 | 
									modification:              withdrawModification{coins: cs(c("bnb", 1100000000)), repay: true},
 | 
				
			||||||
 | 
									rewardsPerSecond:          cs(c("hard", 122354), c("ukava", 122354)),
 | 
				
			||||||
 | 
									initialTime:               time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
 | 
				
			||||||
 | 
									expectedBorrowIndexDenoms: []string{"ukava"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range testCases {
 | 
						for _, tc := range testCases {
 | 
				
			||||||
		suite.Run(tc.name, func() {
 | 
							suite.Run(tc.name, func() {
 | 
				
			||||||
@ -2124,7 +2201,7 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			// Mint coins to hard module account so it can service borrow requests
 | 
								// Mint coins to hard module account so it can service borrow requests
 | 
				
			||||||
			supplyKeeper := suite.app.GetSupplyKeeper()
 | 
								supplyKeeper := suite.app.GetSupplyKeeper()
 | 
				
			||||||
			hardMaccCoins := tc.args.firstBorrow.Add(tc.args.secondBorrow...)
 | 
								hardMaccCoins := tc.args.firstBorrow.Add(tc.args.modification.coins...)
 | 
				
			||||||
			supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
 | 
								supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Set up generic reward periods
 | 
								// Set up generic reward periods
 | 
				
			||||||
@ -2183,18 +2260,29 @@ func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			suite.Require().True(len(claimAfterFirstBorrow.BorrowRewardIndexes) == len(tc.args.firstBorrow))
 | 
								suite.Require().True(len(claimAfterFirstBorrow.BorrowRewardIndexes) == len(tc.args.firstBorrow))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// User borrows (second time)
 | 
								// User modifies their Borrow by either repaying or borrowing more
 | 
				
			||||||
			err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.secondBorrow)
 | 
								if tc.args.modification.repay {
 | 
				
			||||||
 | 
									err = hardKeeper.Repay(suite.ctx, userAddr, userAddr, tc.args.modification.coins)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.modification.coins)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			suite.Require().NoError(err)
 | 
								suite.Require().NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Confirm that claim's borrow reward indexes contain expected values
 | 
								// Confirm that claim's borrow reward indexes contain expected values
 | 
				
			||||||
			claimAfterSecondBorrow, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
 | 
								claimAfterModification, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
 | 
				
			||||||
			suite.Require().True(found)
 | 
								suite.Require().True(found)
 | 
				
			||||||
			for _, coin := range tc.args.secondBorrow {
 | 
								for _, coin := range tc.args.modification.coins {
 | 
				
			||||||
				_, hasIndex := claimAfterSecondBorrow.HasBorrowRewardIndex(coin.Denom)
 | 
									_, hasIndex := claimAfterModification.HasBorrowRewardIndex(coin.Denom)
 | 
				
			||||||
				suite.Require().True(hasIndex)
 | 
									if tc.args.modification.repay {
 | 
				
			||||||
 | 
										// Only false if denom is repaid in full
 | 
				
			||||||
 | 
										if tc.args.modification.coins.AmountOf(coin.Denom).GTE(tc.args.firstBorrow.AmountOf(coin.Denom)) {
 | 
				
			||||||
 | 
											suite.Require().False(hasIndex)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										suite.Require().True(hasIndex)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			suite.Require().True(len(claimAfterSecondBorrow.BorrowRewardIndexes) == len(tc.args.expectedBorrowIndexDenoms))
 | 
								suite.Require().True(len(claimAfterModification.BorrowRewardIndexes) == len(tc.args.expectedBorrowIndexDenoms))
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -486,6 +486,25 @@ func (mris MultiRewardIndexes) GetRewardIndexIndex(denom string) (int, bool) {
 | 
				
			|||||||
	return -1, false
 | 
						return -1, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetCollateralTypes returns a slice of containing all collateral types
 | 
				
			||||||
 | 
					func (mris MultiRewardIndexes) GetCollateralTypes() []string {
 | 
				
			||||||
 | 
						var collateralTypes []string
 | 
				
			||||||
 | 
						for _, ri := range mris {
 | 
				
			||||||
 | 
							collateralTypes = append(collateralTypes, ri.CollateralType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return collateralTypes
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoveRewardIndex removes a denom's reward interest factor value
 | 
				
			||||||
 | 
					func (mris MultiRewardIndexes) RemoveRewardIndex(denom string) MultiRewardIndexes {
 | 
				
			||||||
 | 
						for i, ri := range mris {
 | 
				
			||||||
 | 
							if ri.CollateralType == denom {
 | 
				
			||||||
 | 
								return append(mris[:i], mris[i+1:]...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mris
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validation for reward indexes
 | 
					// Validate validation for reward indexes
 | 
				
			||||||
func (mris MultiRewardIndexes) Validate() error {
 | 
					func (mris MultiRewardIndexes) Validate() error {
 | 
				
			||||||
	for _, mri := range mris {
 | 
						for _, mri := range mris {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user