mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +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