Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: only charge coins which are whitelisted #183

Merged
merged 2 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions x/bundles/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import (
"cosmossdk.io/math"
delegationTypes "github.com/KYVENetwork/chain/x/delegation/types"
"github.com/KYVENetwork/chain/x/funders/types"
pooltypes "github.com/KYVENetwork/chain/x/pool/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
Expand Down Expand Up @@ -48,6 +49,7 @@ type DelegationKeeper interface {
}

type FundersKeeper interface {
GetCoinWhitelist(ctx sdk.Context) (whitelist []types.WhitelistCoinEntry)
ChargeFundersOfPool(ctx sdk.Context, poolId uint64, recipient string) (payout sdk.Coins, err error)
}

Expand Down
64 changes: 48 additions & 16 deletions x/funders/keeper/logic_funders.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,37 @@ func (k Keeper) GetTotalActiveFunding(ctx sdk.Context, poolId uint64) (amounts s
return
}

// GetCoinWhitelist gets the coin whitelist from the params of the funding module
func (k Keeper) GetCoinWhitelist(ctx sdk.Context) (whitelist []types.WhitelistCoinEntry) {
params := k.GetParams(ctx)

for _, entry := range params.CoinWhitelist {
whitelist = append(whitelist, *entry)
}

return
}

// GetCoinWhitelistMap gets the coin whitelist as a map with the denom as key for easier lookup.
// WARNING: Don't use this for setter functions since go maps are non-deterministic!
func (k Keeper) GetCoinWhitelistMap(ctx sdk.Context) (whitelist map[string]types.WhitelistCoinEntry) {
whitelist = make(map[string]types.WhitelistCoinEntry)

w := k.GetCoinWhitelist(ctx)
for _, entry := range w {
whitelist[entry.CoinDenom] = entry
}

return
}

// ChargeFundersOfPool charges all funders of a pool with their amount_per_bundle
// If the amount is lower than the amount_per_bundle,
// the max amount is charged and the funder is removed from the active funders list.
// The amount is transferred from the funders to the recipient module account.
// If there are no more active funders, an event is emitted.
func (k Keeper) ChargeFundersOfPool(ctx sdk.Context, poolId uint64, recipient string) (payouts sdk.Coins, err error) {
// If there are no more active funders, an event is emitted. This method only charges
// coins which are whitelisted.
func (k Keeper) ChargeFundersOfPool(ctx sdk.Context, poolId uint64, recipient string) (sdk.Coins, error) {
// Get funding state for pool
fundingState, found := k.GetFundingState(ctx, poolId)
if !found {
Expand All @@ -49,9 +74,12 @@ func (k Keeper) ChargeFundersOfPool(ctx sdk.Context, poolId uint64, recipient st
return sdk.NewCoins(), nil
}

// This is the amount every funding will be charged
whitelist := k.GetCoinWhitelistMap(ctx)
payouts := sdk.NewCoins()

// Charge every active funder and collect payouts
for _, funding := range activeFundings {
payouts = payouts.Add(funding.ChargeOneBundle()...)
payouts = payouts.Add(funding.ChargeOneBundle(whitelist)...)
if funding.Amounts.IsZero() {
fundingState.SetInactive(&funding)
}
Expand All @@ -68,24 +96,28 @@ func (k Keeper) ChargeFundersOfPool(ctx sdk.Context, poolId uint64, recipient st
})
}

// Move funds to pool module account
if !payouts.IsZero() {
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, recipient, payouts); err != nil {
return sdk.NewCoins(), err
}
if payouts.IsZero() {
return payouts, nil
}

return
// Move funds to recipient module
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, recipient, payouts); err != nil {
return sdk.NewCoins(), err
}

return payouts, nil
}

// GetLowestFunding returns the funding with the lowest amount
// Precondition: len(fundings) > 0
func (k Keeper) GetLowestFunding(fundings []types.Funding, whitelist []*types.WhitelistCoinEntry) (lowestFunding *types.Funding, err error) {
func (k Keeper) GetLowestFunding(ctx sdk.Context, fundings []types.Funding) (lowestFunding *types.Funding, err error) {
if len(fundings) == 0 {
return nil, fmt.Errorf("no active fundings")
}

whitelist := k.GetCoinWhitelistMap(ctx)
lowestFundingIndex := 0

for i := range fundings {
if fundings[i].GetScore(whitelist) < fundings[lowestFundingIndex].GetScore(whitelist) {
lowestFundingIndex = i
Expand Down Expand Up @@ -167,9 +199,7 @@ func (k Keeper) ensureFreeSlot(ctx sdk.Context, newFunding *types.Funding, fundi
return nil
}

params := k.GetParams(ctx)

lowestFunding, err := k.GetLowestFunding(activeFundings, params.CoinWhitelist)
lowestFunding, err := k.GetLowestFunding(ctx, activeFundings)
if err != nil {
return err
}
Expand All @@ -179,9 +209,11 @@ func (k Keeper) ensureFreeSlot(ctx sdk.Context, newFunding *types.Funding, fundi
return nil
}

whitelist := k.GetCoinWhitelistMap(ctx)

// Check if lowest funding is lower than new funding based on amount (amount per bundle is ignored)
if newFunding.GetScore(params.CoinWhitelist) < lowestFunding.GetScore(params.CoinWhitelist) {
return errors.Wrapf(errorsTypes.ErrLogic, types.ErrFundsTooLow.Error(), lowestFunding.GetScore(params.CoinWhitelist))
if newFunding.GetScore(whitelist) < lowestFunding.GetScore(whitelist) {
return errors.Wrapf(errorsTypes.ErrLogic, types.ErrFundsTooLow.Error(), lowestFunding.GetScore(whitelist))
}

// Defund lowest funder
Expand Down
37 changes: 18 additions & 19 deletions x/funders/keeper/logic_funders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ var _ = Describe("logic_funders.go", Ordered, func() {

It("Charge funder that has coins which are not in the whitelist", func() {
// ARRANGE
whitelist = []*funderstypes.WhitelistCoinEntry{
s.App().FundersKeeper.SetParams(s.Ctx(), funderstypes.NewParams([]*funderstypes.WhitelistCoinEntry{
{
CoinDenom: globaltypes.Denom,
MinFundingAmount: 10 * i.KYVE,
Expand All @@ -387,25 +387,24 @@ var _ = Describe("logic_funders.go", Ordered, func() {
MinFundingAmountPerBundle: 1 * i.KYVE,
CoinWeight: math.LegacyNewDec(3),
},
}
s.App().FundersKeeper.SetParams(s.Ctx(), funderstypes.NewParams(whitelist, 20))
}, 20))

// ACT
payout, err := s.App().FundersKeeper.ChargeFundersOfPool(s.Ctx(), 0, pooltypes.ModuleName)
Expect(err).NotTo(HaveOccurred())

// ASSERT
Expect(payout.String()).To(Equal(i.ACoins(11 * i.T_KYVE).String()))
Expect(payout).To(BeEmpty())

fundingAlice, foundAlice := s.App().FundersKeeper.GetFunding(s.Ctx(), i.ALICE, 0)
Expect(foundAlice).To(BeTrue())
Expect(fundingAlice.Amounts.String()).To(Equal(i.ACoins(99 * i.T_KYVE).String()))
Expect(fundingAlice.TotalFunded.String()).To(Equal(i.ACoins(1 * i.T_KYVE).String()))
Expect(fundingAlice.Amounts.String()).To(Equal(i.ACoins(100 * i.T_KYVE).String()))
Expect(fundingAlice.TotalFunded).To(BeEmpty())

fundingBob, foundBob := s.App().FundersKeeper.GetFunding(s.Ctx(), i.BOB, 0)
Expect(foundBob).To(BeTrue())
Expect(fundingBob.Amounts.String()).To(Equal(i.ACoins(40 * i.T_KYVE).String()))
Expect(fundingBob.TotalFunded.String()).To(Equal(i.ACoins(10 * i.T_KYVE).String()))
Expect(fundingBob.Amounts.String()).To(Equal(i.ACoins(50 * i.T_KYVE).String()))
Expect(fundingBob.TotalFunded).To(BeEmpty())

fundingState, _ := s.App().FundersKeeper.GetFundingState(s.Ctx(), 0)
Expect(fundingState.ActiveFunderAddresses).To(HaveLen(2))
Expand All @@ -414,8 +413,8 @@ var _ = Describe("logic_funders.go", Ordered, func() {

fundersBalance := s.GetCoinsFromModule(funderstypes.ModuleName)
poolBalance := s.GetCoinsFromModule(pooltypes.ModuleName)
Expect(fundersBalance.String()).To(Equal(i.ACoins(139 * i.T_KYVE).String()))
Expect(poolBalance.String()).To(Equal(i.ACoins(11 * i.T_KYVE).String()))
Expect(fundersBalance.String()).To(Equal(i.ACoins(150 * i.T_KYVE).String()))
Expect(poolBalance).To(BeEmpty())
})

It("Charge without fundings", func() {
Expand Down Expand Up @@ -458,7 +457,7 @@ var _ = Describe("logic_funders.go", Ordered, func() {
})

It("Check if the lowest funding is returned correctly with one coin", func() {
whitelist := []*funderstypes.WhitelistCoinEntry{
s.App().FundersKeeper.SetParams(s.Ctx(), funderstypes.NewParams([]*funderstypes.WhitelistCoinEntry{
{
CoinDenom: globaltypes.Denom,
MinFundingAmount: 10 * i.KYVE,
Expand All @@ -477,7 +476,7 @@ var _ = Describe("logic_funders.go", Ordered, func() {
CoinDenom: i.C_DENOM,
CoinWeight: math.LegacyNewDec(3),
},
}
}, 20))

fundings := []funderstypes.Funding{
{
Expand All @@ -500,13 +499,13 @@ var _ = Describe("logic_funders.go", Ordered, func() {
},
}

getLowestFunding, err := s.App().FundersKeeper.GetLowestFunding(fundings, whitelist)
getLowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), fundings)
Expect(err).NotTo(HaveOccurred())
Expect(getLowestFunding.FunderAddress).To(Equal(i.DUMMY[2]))
})

It("Check if the lowest funding is returned correctly with multiple coins", func() {
whitelist := []*funderstypes.WhitelistCoinEntry{
s.App().FundersKeeper.SetParams(s.Ctx(), funderstypes.NewParams([]*funderstypes.WhitelistCoinEntry{
{
CoinDenom: globaltypes.Denom,
MinFundingAmount: 10 * i.KYVE,
Expand All @@ -525,7 +524,7 @@ var _ = Describe("logic_funders.go", Ordered, func() {
CoinDenom: i.C_DENOM,
CoinWeight: math.LegacyNewDec(3),
},
}
}, 20))

fundings := []funderstypes.Funding{
{
Expand All @@ -548,13 +547,13 @@ var _ = Describe("logic_funders.go", Ordered, func() {
},
}

getLowestFunding, err := s.App().FundersKeeper.GetLowestFunding(fundings, whitelist)
getLowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), fundings)
Expect(err).NotTo(HaveOccurred())
Expect(getLowestFunding.FunderAddress).To(Equal(i.DUMMY[1]))
})

It("Check if the lowest funding is returned correctly with coins which are not whitelisted", func() {
whitelist := []*funderstypes.WhitelistCoinEntry{
s.App().FundersKeeper.SetParams(s.Ctx(), funderstypes.NewParams([]*funderstypes.WhitelistCoinEntry{
{
CoinDenom: globaltypes.Denom,
MinFundingAmount: 10 * i.KYVE,
Expand All @@ -569,7 +568,7 @@ var _ = Describe("logic_funders.go", Ordered, func() {
CoinDenom: i.B_DENOM,
CoinWeight: math.LegacyNewDec(2),
},
}
}, 20))

fundings := []funderstypes.Funding{
{
Expand All @@ -592,7 +591,7 @@ var _ = Describe("logic_funders.go", Ordered, func() {
},
}

getLowestFunding, err := s.App().FundersKeeper.GetLowestFunding(fundings, whitelist)
getLowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), fundings)
Expect(err).NotTo(HaveOccurred())
Expect(getLowestFunding.FunderAddress).To(Equal(i.DUMMY[2]))
})
Expand Down
14 changes: 6 additions & 8 deletions x/funders/keeper/msg_server_defund_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ var _ = Describe("msg_server_defund_pool.go", Ordered, func() {

fundingState, _ := s.App().FundersKeeper.GetFundingState(s.Ctx(), 0)
activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.BOB))

Expand All @@ -228,7 +228,7 @@ var _ = Describe("msg_server_defund_pool.go", Ordered, func() {
// ASSERT
fundingState, _ = s.App().FundersKeeper.GetFundingState(s.Ctx(), 0)
activeFundings = s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err = s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err = s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))

Expand Down Expand Up @@ -304,7 +304,7 @@ var _ = Describe("msg_server_defund_pool.go", Ordered, func() {

It("Try to partially defund after a coin has been removed from the whitelist", func() {
// ARRANGE
whitelist = []*types.WhitelistCoinEntry{
s.App().FundersKeeper.SetParams(s.Ctx(), types.NewParams([]*types.WhitelistCoinEntry{
{
CoinDenom: globaltypes.Denom,
MinFundingAmount: 10 * i.KYVE,
Expand All @@ -323,8 +323,7 @@ var _ = Describe("msg_server_defund_pool.go", Ordered, func() {
MinFundingAmountPerBundle: 1 * i.KYVE,
CoinWeight: math.LegacyNewDec(3),
},
}
s.App().FundersKeeper.SetParams(s.Ctx(), types.NewParams(whitelist, 20))
}, 20))

// ACT
_, err := s.RunTx(&types.MsgDefundPool{
Expand All @@ -340,7 +339,7 @@ var _ = Describe("msg_server_defund_pool.go", Ordered, func() {

It("Try to fully defund after a coin has been removed from the whitelist", func() {
// ARRANGE
whitelist = []*types.WhitelistCoinEntry{
s.App().FundersKeeper.SetParams(s.Ctx(), types.NewParams([]*types.WhitelistCoinEntry{
{
CoinDenom: globaltypes.Denom,
MinFundingAmount: 10 * i.KYVE,
Expand All @@ -359,8 +358,7 @@ var _ = Describe("msg_server_defund_pool.go", Ordered, func() {
MinFundingAmountPerBundle: 1 * i.KYVE,
CoinWeight: math.LegacyNewDec(3),
},
}
s.App().FundersKeeper.SetParams(s.Ctx(), types.NewParams(whitelist, 20))
}, 20))

// ACT
s.RunTxFundersSuccess(&types.MsgDefundPool{
Expand Down
18 changes: 9 additions & 9 deletions x/funders/keeper/msg_server_fund_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {
Expect(fundingState.ActiveFunderAddresses[0]).To(Equal(i.ALICE))

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))
})
Expand Down Expand Up @@ -222,7 +222,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {
Expect(fundingState.ActiveFunderAddresses[0]).To(Equal(i.ALICE))

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))
})
Expand Down Expand Up @@ -262,7 +262,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {
Expect(fundingState.ActiveFunderAddresses[0]).To(Equal(i.ALICE))

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))
})
Expand Down Expand Up @@ -302,7 +302,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {
Expect(fundingState.ActiveFunderAddresses[0]).To(Equal(i.ALICE))

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))
})
Expand Down Expand Up @@ -384,7 +384,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {
Expect(len(fundingState.ActiveFunderAddresses)).To(Equal(2))

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.BOB))
})
Expand Down Expand Up @@ -420,7 +420,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {
Expect(len(fundingState.ActiveFunderAddresses)).To(Equal(2))

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))
})
Expand Down Expand Up @@ -672,7 +672,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
Expect(activeFundings).To(HaveLen(50))
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))
})
Expand Down Expand Up @@ -718,7 +718,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {

activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
Expect(activeFundings).To(HaveLen(50))
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.BOB))

Expand Down Expand Up @@ -755,7 +755,7 @@ var _ = Describe("msg_server_fund_pool.go", Ordered, func() {
fundingState, _ := s.App().FundersKeeper.GetFundingState(s.Ctx(), 0)
activeFundings := s.App().FundersKeeper.GetActiveFundings(s.Ctx(), fundingState)
Expect(activeFundings).To(HaveLen(50))
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(activeFundings, whitelist)
lowestFunding, err := s.App().FundersKeeper.GetLowestFunding(s.Ctx(), activeFundings)
Expect(err).To(BeNil())
Expect(lowestFunding.FunderAddress).To(Equal(i.ALICE))

Expand Down
Loading
Loading