refactor(x/protocolpool)!: Reducing complexity and removing some bugs (#20786)
This commit is contained in:
parent
488e74cc5d
commit
d426a5db67
29
tests/integration/protocolpool/app_config.go
Normal file
29
tests/integration/protocolpool/app_config.go
Normal file
@ -0,0 +1,29 @@
|
||||
package protocolpool
|
||||
|
||||
import (
|
||||
_ "cosmossdk.io/x/accounts" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/auth" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/auth/tx/config" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/bank" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/consensus" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/distribution" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/mint" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/protocolpool" // import as blank for app wiring
|
||||
_ "cosmossdk.io/x/staking" // import as blank for app wiring
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/configurator"
|
||||
_ "github.com/cosmos/cosmos-sdk/x/genutil" // import as blank for app wiring
|
||||
)
|
||||
|
||||
var AppConfig = configurator.NewAppConfig(
|
||||
configurator.AccountsModule(),
|
||||
configurator.AuthModule(),
|
||||
configurator.BankModule(),
|
||||
configurator.StakingModule(),
|
||||
configurator.TxModule(),
|
||||
configurator.ConsensusModule(),
|
||||
configurator.GenutilModule(),
|
||||
configurator.MintModule(),
|
||||
configurator.DistributionModule(),
|
||||
configurator.ProtocolPoolModule(),
|
||||
)
|
||||
138
tests/integration/protocolpool/module_test.go
Normal file
138
tests/integration/protocolpool/module_test.go
Normal file
@ -0,0 +1,138 @@
|
||||
package protocolpool
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/core/header"
|
||||
"cosmossdk.io/depinject"
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
authkeeper "cosmossdk.io/x/auth/keeper"
|
||||
authtypes "cosmossdk.io/x/auth/types"
|
||||
bankkeeper "cosmossdk.io/x/bank/keeper"
|
||||
"cosmossdk.io/x/mint/types"
|
||||
protocolpoolkeeper "cosmossdk.io/x/protocolpool/keeper"
|
||||
protocolpooltypes "cosmossdk.io/x/protocolpool/types"
|
||||
stakingkeeper "cosmossdk.io/x/staking/keeper"
|
||||
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// TestWithdrawAnytime tests if withdrawing funds many times vs withdrawing funds once
|
||||
// yield the same end balance.
|
||||
func TestWithdrawAnytime(t *testing.T) {
|
||||
var accountKeeper authkeeper.AccountKeeper
|
||||
var protocolpoolKeeper protocolpoolkeeper.Keeper
|
||||
var bankKeeper bankkeeper.Keeper
|
||||
var stakingKeeper *stakingkeeper.Keeper
|
||||
|
||||
app, err := simtestutil.SetupAtGenesis(
|
||||
depinject.Configs(
|
||||
AppConfig,
|
||||
depinject.Supply(log.NewNopLogger()),
|
||||
), &accountKeeper, &protocolpoolKeeper, &bankKeeper, &stakingKeeper)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := app.BaseApp.NewContext(false).WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1})
|
||||
acc := accountKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.ModuleName))
|
||||
require.NotNil(t, acc)
|
||||
|
||||
testAddrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 5, math.NewInt(1))
|
||||
testAddr0Str, err := accountKeeper.AddressCodec().BytesToString(testAddrs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
msgServer := protocolpoolkeeper.NewMsgServerImpl(protocolpoolKeeper)
|
||||
_, err = msgServer.CreateContinuousFund(
|
||||
ctx,
|
||||
&protocolpooltypes.MsgCreateContinuousFund{
|
||||
Authority: protocolpoolKeeper.GetAuthority(),
|
||||
Recipient: testAddr0Str,
|
||||
Percentage: math.LegacyMustNewDecFromStr("0.5"),
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// increase the community pool by a bunch
|
||||
for i := 0; i < 30; i++ {
|
||||
ctx, err = simtestutil.NextBlock(app, ctx, time.Minute)
|
||||
require.NoError(t, err)
|
||||
|
||||
// withdraw funds randomly, but it must always land on the same end balance
|
||||
if rand.Intn(100) > 50 {
|
||||
_, err = msgServer.WithdrawContinuousFund(ctx, &protocolpooltypes.MsgWithdrawContinuousFund{
|
||||
RecipientAddress: testAddr0Str,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
pool, err := protocolpoolKeeper.GetCommunityPool(ctx)
|
||||
require.NoError(t, err)
|
||||
require.True(t, pool.IsAllGT(sdk.NewCoins(sdk.NewInt64Coin("stake", 100000))))
|
||||
|
||||
_, err = msgServer.WithdrawContinuousFund(ctx, &protocolpooltypes.MsgWithdrawContinuousFund{
|
||||
RecipientAddress: testAddr0Str,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
endBalance := bankKeeper.GetBalance(ctx, testAddrs[0], sdk.DefaultBondDenom)
|
||||
require.Equal(t, "11883031stake", endBalance.String())
|
||||
}
|
||||
|
||||
// TestExpireInTheMiddle tests if a continuous fund that expires without anyone
|
||||
// calling the withdraw function, the funds are still distributed correctly.
|
||||
func TestExpireInTheMiddle(t *testing.T) {
|
||||
t.Skip("This is a bug @facu found, will fix in another PR")
|
||||
var accountKeeper authkeeper.AccountKeeper
|
||||
var protocolpoolKeeper protocolpoolkeeper.Keeper
|
||||
var bankKeeper bankkeeper.Keeper
|
||||
var stakingKeeper *stakingkeeper.Keeper
|
||||
|
||||
app, err := simtestutil.SetupAtGenesis(
|
||||
depinject.Configs(
|
||||
AppConfig,
|
||||
depinject.Supply(log.NewNopLogger()),
|
||||
), &accountKeeper, &protocolpoolKeeper, &bankKeeper, &stakingKeeper)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := app.BaseApp.NewContext(false).WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1})
|
||||
acc := accountKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.ModuleName))
|
||||
require.NotNil(t, acc)
|
||||
|
||||
testAddrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 5, math.NewInt(1))
|
||||
testAddr0Str, err := accountKeeper.AddressCodec().BytesToString(testAddrs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
msgServer := protocolpoolkeeper.NewMsgServerImpl(protocolpoolKeeper)
|
||||
|
||||
expirationTime := ctx.BlockTime().Add(time.Minute * 2)
|
||||
_, err = msgServer.CreateContinuousFund(
|
||||
ctx,
|
||||
&protocolpooltypes.MsgCreateContinuousFund{
|
||||
Authority: protocolpoolKeeper.GetAuthority(),
|
||||
Recipient: testAddr0Str,
|
||||
Percentage: math.LegacyMustNewDecFromStr("0.1"),
|
||||
Expiry: &expirationTime,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// increase the community pool by a bunch
|
||||
for i := 0; i < 30; i++ {
|
||||
ctx, err = simtestutil.NextBlock(app, ctx, time.Minute)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
_, err = msgServer.WithdrawContinuousFund(ctx, &protocolpooltypes.MsgWithdrawContinuousFund{
|
||||
RecipientAddress: testAddr0Str,
|
||||
})
|
||||
require.Error(t, err)
|
||||
|
||||
endBalance := bankKeeper.GetBalance(ctx, testAddrs[0], sdk.DefaultBondDenom)
|
||||
require.Equal(t, "158441stake", endBalance.String())
|
||||
}
|
||||
42
x/protocolpool/keeper/genesis_test.go
Normal file
42
x/protocolpool/keeper/genesis_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/protocolpool/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestInitGenesis() {
|
||||
hour := time.Hour
|
||||
gs := types.NewGenesisState(
|
||||
[]*types.ContinuousFund{
|
||||
{
|
||||
Recipient: "cosmos1qy3529yj3v4xw2z3vz3vz3vz3vz3vz3v3k0vyf",
|
||||
Percentage: math.LegacyMustNewDecFromStr("0.1"),
|
||||
Expiry: nil,
|
||||
},
|
||||
},
|
||||
[]*types.Budget{
|
||||
{
|
||||
RecipientAddress: "cosmos1qy3529yj3v4xw2z3vz3vz3vz3vz3vz3v3k0vyf",
|
||||
ClaimedAmount: &sdk.Coin{},
|
||||
LastClaimedAt: &time.Time{},
|
||||
TranchesLeft: 10,
|
||||
BudgetPerTranche: &sdk.Coin{Denom: "stake", Amount: math.NewInt(100)},
|
||||
Period: &hour,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
err := suite.poolKeeper.InitGenesis(suite.ctx, gs)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Export
|
||||
exportedGenState, err := suite.poolKeeper.ExportGenesis(suite.ctx)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(gs.ContinuousFund, exportedGenState.ContinuousFund)
|
||||
suite.Require().Equal(gs.Budget, exportedGenState.Budget)
|
||||
}
|
||||
@ -33,11 +33,9 @@ type Keeper struct {
|
||||
Schema collections.Schema
|
||||
BudgetProposal collections.Map[sdk.AccAddress, types.Budget]
|
||||
ContinuousFund collections.Map[sdk.AccAddress, types.ContinuousFund]
|
||||
// RecipientFundPercentage key: RecipientAddr | value: Percentage in math.Int
|
||||
RecipientFundPercentage collections.Map[sdk.AccAddress, math.Int]
|
||||
// RecipientFundDistribution key: RecipientAddr | value: Claimable amount
|
||||
RecipientFundDistribution collections.Map[sdk.AccAddress, math.Int]
|
||||
// ToDistribute is to keep track of funds distributed
|
||||
// ToDistribute is to keep track of funds to be distributed. It gets zeroed out in iterateAndUpdateFundsDistribution.
|
||||
ToDistribute collections.Item[math.Int]
|
||||
}
|
||||
|
||||
@ -63,7 +61,6 @@ func NewKeeper(cdc codec.BinaryCodec, env appmodule.Environment, ak types.Accoun
|
||||
authority: authority,
|
||||
BudgetProposal: collections.NewMap(sb, types.BudgetKey, "budget", sdk.AccAddressKey, codec.CollValue[types.Budget](cdc)),
|
||||
ContinuousFund: collections.NewMap(sb, types.ContinuousFundKey, "continuous_fund", sdk.AccAddressKey, codec.CollValue[types.ContinuousFund](cdc)),
|
||||
RecipientFundPercentage: collections.NewMap(sb, types.RecipientFundPercentageKey, "recipient_fund_percentage", sdk.AccAddressKey, sdk.IntValue),
|
||||
RecipientFundDistribution: collections.NewMap(sb, types.RecipientFundDistributionKey, "recipient_fund_distribution", sdk.AccAddressKey, sdk.IntValue),
|
||||
ToDistribute: collections.NewItem(sb, types.ToDistributeKey, "to_distribute", sdk.IntValue),
|
||||
}
|
||||
@ -125,20 +122,13 @@ func (k Keeper) withdrawContinuousFund(ctx context.Context, recipientAddr string
|
||||
return sdk.Coin{}, fmt.Errorf("cannot withdraw continuous funds: continuous fund expired for recipient: %s", recipientAddr)
|
||||
}
|
||||
|
||||
toDistributeAmount, err := k.ToDistribute.Get(ctx)
|
||||
err = k.IterateAndUpdateFundsDistribution(ctx)
|
||||
if err != nil {
|
||||
return sdk.Coin{}, err
|
||||
}
|
||||
|
||||
if !toDistributeAmount.Equal(math.ZeroInt()) {
|
||||
err = k.iterateAndUpdateFundsDistribution(ctx, toDistributeAmount)
|
||||
if err != nil {
|
||||
return sdk.Coin{}, fmt.Errorf("error while iterating all the continuous funds: %w", err)
|
||||
}
|
||||
return sdk.Coin{}, fmt.Errorf("error while iterating all the continuous funds: %w", err)
|
||||
}
|
||||
|
||||
// withdraw continuous fund
|
||||
withdrawnAmount, err := k.withdrawRecipientFunds(ctx, recipientAddr)
|
||||
withdrawnAmount, err := k.withdrawRecipientFunds(ctx, recipient)
|
||||
if err != nil {
|
||||
return sdk.Coin{}, fmt.Errorf("error while withdrawing recipient funds for recipient: %s", recipientAddr)
|
||||
}
|
||||
@ -146,12 +136,7 @@ func (k Keeper) withdrawContinuousFund(ctx context.Context, recipientAddr string
|
||||
return withdrawnAmount, nil
|
||||
}
|
||||
|
||||
func (k Keeper) withdrawRecipientFunds(ctx context.Context, recipientAddr string) (sdk.Coin, error) {
|
||||
recipient, err := k.authKeeper.AddressCodec().StringToBytes(recipientAddr)
|
||||
if err != nil {
|
||||
return sdk.Coin{}, sdkerrors.ErrInvalidAddress.Wrapf("invalid recipient address: %s", err)
|
||||
}
|
||||
|
||||
func (k Keeper) withdrawRecipientFunds(ctx context.Context, recipient []byte) (sdk.Coin, error) {
|
||||
// get allocated continuous fund
|
||||
fundsAllocated, err := k.RecipientFundDistribution.Get(ctx, recipient)
|
||||
if err != nil {
|
||||
@ -170,7 +155,7 @@ func (k Keeper) withdrawRecipientFunds(ctx context.Context, recipientAddr string
|
||||
withdrawnAmount := sdk.NewCoin(denom, fundsAllocated)
|
||||
err = k.DistributeFromStreamFunds(ctx, sdk.NewCoins(withdrawnAmount), recipient)
|
||||
if err != nil {
|
||||
return sdk.Coin{}, fmt.Errorf("error while distributing funds to the recipient %s: %w", recipientAddr, err)
|
||||
return sdk.Coin{}, fmt.Errorf("error while distributing funds: %w", err)
|
||||
}
|
||||
|
||||
// reset fund distribution
|
||||
@ -181,7 +166,8 @@ func (k Keeper) withdrawRecipientFunds(ctx context.Context, recipientAddr string
|
||||
return withdrawnAmount, nil
|
||||
}
|
||||
|
||||
// SetToDistribute sets the amount to be distributed among recipients.
|
||||
// SetToDistribute sets the amount to be distributed among recipients, usually called by x/distribution while allocating
|
||||
// reward and fee distribution.
|
||||
// This could be only set by the authority address.
|
||||
func (k Keeper) SetToDistribute(ctx context.Context, amount sdk.Coins, addr string) error {
|
||||
authAddr, err := k.authKeeper.AddressCodec().StringToBytes(addr)
|
||||
@ -201,46 +187,49 @@ func (k Keeper) SetToDistribute(ctx context.Context, amount sdk.Coins, addr stri
|
||||
return err
|
||||
}
|
||||
|
||||
totalStreamFundsPercentage := math.ZeroInt()
|
||||
err = k.RecipientFundPercentage.Walk(ctx, nil, func(key sdk.AccAddress, value math.Int) (stop bool, err error) {
|
||||
totalStreamFundsPercentage = totalStreamFundsPercentage.Add(value)
|
||||
totalStreamFundsPercentage := math.LegacyZeroDec()
|
||||
err = k.ContinuousFund.Walk(ctx, nil, func(key sdk.AccAddress, cf types.ContinuousFund) (stop bool, err error) {
|
||||
// Check if the continuous fund has expired
|
||||
if cf.Expiry != nil && cf.Expiry.Before(k.HeaderService.HeaderInfo(ctx).Time) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
totalStreamFundsPercentage = totalStreamFundsPercentage.Add(cf.Percentage)
|
||||
if totalStreamFundsPercentage.GT(math.LegacyOneDec()) {
|
||||
return true, fmt.Errorf("total funds percentage cannot exceed 100")
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
if totalStreamFundsPercentage.GT(math.NewInt(100)) {
|
||||
return fmt.Errorf("total funds percentage cannot exceed 100")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if percentage is 0 then return early
|
||||
if totalStreamFundsPercentage.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// send streaming funds to the stream module account
|
||||
if err := k.sendFundsToStreamModule(ctx, denom, totalStreamFundsPercentage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.ToDistribute.Set(ctx, amount.AmountOf(denom))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while setting ToDistribute: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k Keeper) sendFundsToStreamModule(ctx context.Context, denom string, percentage math.Int) error {
|
||||
totalPoolAmt, err := k.GetCommunityPool(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
poolAmt := totalPoolAmt.AmountOf(denom)
|
||||
poolAmtDec := sdk.NewDecCoins(sdk.NewDecCoin(denom, poolAmt))
|
||||
amt := poolAmtDec.MulDec(math.LegacyNewDecFromIntWithPrec(percentage, 2))
|
||||
streamAmt := sdk.NewCoins(sdk.NewCoin(denom, amt.AmountOf(denom).TruncateInt()))
|
||||
|
||||
// Send streaming funds to the StreamModuleAccount
|
||||
toDistributeAmt := math.LegacyNewDecFromInt(amount.AmountOf(denom)).Mul(totalStreamFundsPercentage).TruncateInt()
|
||||
streamAmt := sdk.NewCoins(sdk.NewCoin(denom, toDistributeAmt))
|
||||
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.StreamAccount, streamAmt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
amountToDistribute, err := k.ToDistribute.Get(ctx)
|
||||
if err != nil {
|
||||
if errors.Is(err, collections.ErrNotFound) {
|
||||
amountToDistribute = math.ZeroInt()
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = k.ToDistribute.Set(ctx, amountToDistribute.Add(amount.AmountOf(denom)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while setting ToDistribute: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -254,79 +243,56 @@ func (k Keeper) hasPermission(addr []byte) (bool, error) {
|
||||
return bytes.Equal(authAcc, addr), nil
|
||||
}
|
||||
|
||||
type recipientFund struct {
|
||||
RecipientAddr string
|
||||
Percentage math.Int
|
||||
}
|
||||
|
||||
func (k Keeper) iterateAndUpdateFundsDistribution(ctx context.Context, toDistributeAmount math.Int) error {
|
||||
totalPercentageToBeDistributed := math.ZeroInt()
|
||||
|
||||
recipientFundList := []recipientFund{}
|
||||
|
||||
// Calculate totalPercentageToBeDistributed and store values
|
||||
err := k.RecipientFundPercentage.Walk(ctx, nil, func(key sdk.AccAddress, value math.Int) (stop bool, err error) {
|
||||
addr, err := k.authKeeper.AddressCodec().BytesToString(key)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
cf, err := k.ContinuousFund.Get(ctx, key)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
// Check if the continuous fund has expired
|
||||
if cf.Expiry != nil && cf.Expiry.Before(k.HeaderService.HeaderInfo(ctx).Time) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
totalPercentageToBeDistributed = totalPercentageToBeDistributed.Add(value)
|
||||
recipientFundList = append(recipientFundList, recipientFund{
|
||||
RecipientAddr: addr,
|
||||
Percentage: value,
|
||||
})
|
||||
return false, nil
|
||||
})
|
||||
func (k Keeper) IterateAndUpdateFundsDistribution(ctx context.Context) error {
|
||||
toDistributeAmount, err := k.ToDistribute.Get(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if totalPercentageToBeDistributed.GT(math.NewInt(100)) {
|
||||
return fmt.Errorf("total funds percentage cannot exceed 100")
|
||||
|
||||
// if there are no funds to distribute, return
|
||||
if toDistributeAmount.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
totalPercentageToBeDistributed := math.LegacyZeroDec()
|
||||
|
||||
denom, err := k.stakingKeeper.BondDenom(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toDistributeDec := sdk.NewDecCoins(sdk.NewDecCoin(denom, toDistributeAmount))
|
||||
toDistributeDec := sdk.NewDecCoin(denom, toDistributeAmount)
|
||||
|
||||
// Calculate the funds to be distributed based on the total percentage to be distributed
|
||||
totalAmountToBeDistributed := toDistributeDec.MulDec(math.LegacyNewDecFromIntWithPrec(totalPercentageToBeDistributed, 2))
|
||||
totalDistrAmount := totalAmountToBeDistributed.AmountOf(denom)
|
||||
|
||||
for _, value := range recipientFundList {
|
||||
// Calculate the funds to be distributed based on the percentage
|
||||
decValue := math.LegacyNewDecFromIntWithPrec(value.Percentage, 2)
|
||||
percentage := math.LegacyNewDecFromIntWithPrec(totalPercentageToBeDistributed, 2)
|
||||
recipientAmount := totalDistrAmount.Mul(decValue).Quo(percentage)
|
||||
recipientCoins := recipientAmount.TruncateInt()
|
||||
|
||||
key, err := k.authKeeper.AddressCodec().StringToBytes(value.RecipientAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
// Calculate totalPercentageToBeDistributed and store values
|
||||
err = k.ContinuousFund.Walk(ctx, nil, func(key sdk.AccAddress, cf types.ContinuousFund) (stop bool, err error) {
|
||||
// Check if the continuous fund has expired
|
||||
if cf.Expiry != nil && cf.Expiry.Before(k.HeaderService.HeaderInfo(ctx).Time) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// sanity check for max percentage
|
||||
totalPercentageToBeDistributed = totalPercentageToBeDistributed.Add(cf.Percentage)
|
||||
if totalPercentageToBeDistributed.GT(math.LegacyOneDec()) {
|
||||
return true, fmt.Errorf("total funds percentage cannot exceed 100")
|
||||
}
|
||||
|
||||
// Calculate the funds to be distributed based on the percentage
|
||||
recipientAmount := toDistributeDec.Amount.Mul(cf.Percentage).TruncateInt()
|
||||
|
||||
// Set funds to be claimed
|
||||
toClaim, err := k.RecipientFundDistribution.Get(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
return true, err
|
||||
}
|
||||
amount := toClaim.Add(recipientCoins)
|
||||
amount := toClaim.Add(recipientAmount)
|
||||
err = k.RecipientFundDistribution.Set(ctx, key, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the coins to be distributed from toDistribute to 0
|
||||
|
||||
@ -113,3 +113,45 @@ func (s *KeeperTestSuite) mockStreamFunds() {
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(KeeperTestSuite))
|
||||
}
|
||||
|
||||
func (s *KeeperTestSuite) TestIterateAndUpdateFundsDistribution() {
|
||||
// We'll create 2 continuous funds of 30% each, and the total pool is 1000000, meaning each fund should get 300000
|
||||
|
||||
s.SetupTest()
|
||||
s.authKeeper.EXPECT().GetModuleAccount(s.ctx, types.ModuleName).Return(poolAcc).AnyTimes()
|
||||
distrBal := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(1000000)))
|
||||
s.bankKeeper.EXPECT().GetAllBalances(s.ctx, poolAcc.GetAddress()).Return(distrBal).AnyTimes()
|
||||
s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(s.ctx, poolAcc.GetName(), streamAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(600000)))).AnyTimes()
|
||||
|
||||
_, err := s.msgServer.CreateContinuousFund(s.ctx, &types.MsgCreateContinuousFund{
|
||||
Authority: s.poolKeeper.GetAuthority(),
|
||||
Recipient: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2srklj6",
|
||||
Percentage: math.LegacyMustNewDecFromStr("0.3"),
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
_, err = s.msgServer.CreateContinuousFund(s.ctx, &types.MsgCreateContinuousFund{
|
||||
Authority: s.poolKeeper.GetAuthority(),
|
||||
Recipient: "cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r",
|
||||
Percentage: math.LegacyMustNewDecFromStr("0.3"),
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
_ = s.poolKeeper.SetToDistribute(s.ctx, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1000000))), s.poolKeeper.GetAuthority())
|
||||
|
||||
err = s.poolKeeper.IterateAndUpdateFundsDistribution(s.ctx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.poolKeeper.RecipientFundDistribution.Walk(s.ctx, nil, func(key sdk.AccAddress, value math.Int) (stop bool, err error) {
|
||||
strAddr, err := s.authKeeper.AddressCodec().BytesToString(key)
|
||||
s.Require().NoError(err)
|
||||
|
||||
if strAddr == "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2srklj6" {
|
||||
s.Require().Equal(value, math.NewInt(300000))
|
||||
} else if strAddr == "cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r" {
|
||||
s.Require().Equal(value, math.NewInt(300000))
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
@ -126,18 +126,17 @@ func (k MsgServer) CreateContinuousFund(ctx context.Context, msg *types.MsgCreat
|
||||
|
||||
// Check if total funds percentage exceeds 100%
|
||||
// If exceeds, we should not setup continuous fund proposal.
|
||||
totalStreamFundsPercentage := math.ZeroInt()
|
||||
err = k.Keeper.RecipientFundPercentage.Walk(ctx, nil, func(key sdk.AccAddress, value math.Int) (stop bool, err error) {
|
||||
totalStreamFundsPercentage = totalStreamFundsPercentage.Add(value)
|
||||
totalStreamFundsPercentage := math.LegacyZeroDec()
|
||||
err = k.Keeper.ContinuousFund.Walk(ctx, nil, func(key sdk.AccAddress, value types.ContinuousFund) (stop bool, err error) {
|
||||
totalStreamFundsPercentage = totalStreamFundsPercentage.Add(value.Percentage)
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
percentage := msg.Percentage.MulInt64(100)
|
||||
totalStreamFundsPercentage = totalStreamFundsPercentage.Add(percentage.TruncateInt())
|
||||
if totalStreamFundsPercentage.GT(math.NewInt(100)) {
|
||||
return nil, fmt.Errorf("cannot set continuous fund proposal\ntotal funds percentage exceeds 100\ncurrent total percentage: %v", totalStreamFundsPercentage.Sub(percentage.TruncateInt()))
|
||||
totalStreamFundsPercentage = totalStreamFundsPercentage.Add(msg.Percentage)
|
||||
if totalStreamFundsPercentage.GT(math.LegacyOneDec()) {
|
||||
return nil, fmt.Errorf("cannot set continuous fund proposal\ntotal funds percentage exceeds 100\ncurrent total percentage: %s", totalStreamFundsPercentage.Sub(msg.Percentage).MulInt64(100).TruncateInt().String())
|
||||
}
|
||||
|
||||
// Create continuous fund proposal
|
||||
@ -153,11 +152,6 @@ func (k MsgServer) CreateContinuousFund(ctx context.Context, msg *types.MsgCreat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set recipient fund percentage & distribution
|
||||
err = k.RecipientFundPercentage.Set(ctx, recipient, percentage.TruncateInt())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = k.RecipientFundDistribution.Set(ctx, recipient, math.ZeroInt())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -171,9 +165,6 @@ func (k MsgServer) WithdrawContinuousFund(ctx context.Context, msg *types.MsgWit
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if amount.IsNil() {
|
||||
k.Logger.Info(fmt.Sprintf("no distribution amount found for recipient %s", msg.RecipientAddress))
|
||||
}
|
||||
|
||||
return &types.MsgWithdrawContinuousFundResponse{Amount: amount}, nil
|
||||
}
|
||||
@ -200,7 +191,7 @@ func (k MsgServer) CancelContinuousFund(ctx context.Context, msg *types.MsgCance
|
||||
}
|
||||
|
||||
// withdraw funds if any are allocated
|
||||
withdrawnFunds, err := k.withdrawRecipientFunds(ctx, msg.RecipientAddress)
|
||||
withdrawnFunds, err := k.withdrawRecipientFunds(ctx, recipient)
|
||||
if err != nil && !errorspkg.Is(err, types.ErrNoRecipientFund) {
|
||||
return nil, fmt.Errorf("error while withdrawing already allocated funds for recipient %s: %w", msg.RecipientAddress, err)
|
||||
}
|
||||
@ -209,10 +200,6 @@ func (k MsgServer) CancelContinuousFund(ctx context.Context, msg *types.MsgCance
|
||||
return nil, fmt.Errorf("failed to remove continuous fund for recipient %s: %w", msg.RecipientAddress, err)
|
||||
}
|
||||
|
||||
if err := k.RecipientFundPercentage.Remove(ctx, recipient); err != nil {
|
||||
return nil, fmt.Errorf("failed to remove recipient fund percentage for recipient %s: %w", msg.RecipientAddress, err)
|
||||
}
|
||||
|
||||
if err := k.RecipientFundDistribution.Remove(ctx, recipient); err != nil {
|
||||
return nil, fmt.Errorf("failed to remove recipient fund distribution for recipient %s: %w", msg.RecipientAddress, err)
|
||||
}
|
||||
|
||||
@ -421,9 +421,6 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage := percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -439,8 +436,6 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient2, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage = percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient2, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient2, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
@ -486,9 +481,6 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage := percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.ToDistribute.Set(suite.ctx, math.ZeroInt())
|
||||
@ -510,9 +502,6 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage := percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
toDistribute := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100000)))
|
||||
@ -539,9 +528,6 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage := percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
toDistribute := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100000)))
|
||||
@ -569,9 +555,6 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage := percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -580,16 +563,13 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
suite.Require().NoError(err)
|
||||
cf = types.ContinuousFund{
|
||||
Recipient: recipient2StrAddr,
|
||||
Percentage: percentage,
|
||||
Percentage: percentage2,
|
||||
Expiry: &expiry,
|
||||
}
|
||||
// Set continuous fund
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient2, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage = percentage2.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient2, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient2, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -598,16 +578,13 @@ func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
||||
suite.Require().NoError(err)
|
||||
cf = types.ContinuousFund{
|
||||
Recipient: recipient3StrAddr,
|
||||
Percentage: percentage2,
|
||||
Percentage: percentage3,
|
||||
Expiry: &expiry,
|
||||
}
|
||||
// Set continuous fund
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient3, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage = percentage3.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient3, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient3, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -767,9 +744,6 @@ func (suite *KeeperTestSuite) TestCreateContinuousFund() {
|
||||
}
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient2, cf)
|
||||
suite.Require().NoError(err)
|
||||
intPercentage := percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient2, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
input: &types.MsgCreateContinuousFund{
|
||||
Authority: suite.poolKeeper.GetAuthority(),
|
||||
@ -860,9 +834,6 @@ func (suite *KeeperTestSuite) TestCancelContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipientAddr, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage := percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipientAddr, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipientAddr, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -878,9 +849,6 @@ func (suite *KeeperTestSuite) TestCancelContinuousFund() {
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient2, cf)
|
||||
suite.Require().NoError(err)
|
||||
// Set recipient fund percentage and recipient fund distribution
|
||||
intPercentage = percentage.MulInt64(100)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient2, intPercentage.TruncateInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient2, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -919,8 +887,6 @@ func (suite *KeeperTestSuite) TestCancelContinuousFund() {
|
||||
suite.mockWithdrawContinuousFund()
|
||||
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient3, cf)
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundPercentage.Set(suite.ctx, recipient3, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient3, math.ZeroInt())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
@ -955,8 +921,6 @@ func (suite *KeeperTestSuite) TestCancelContinuousFund() {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(tc.withdrawnFunds, resp.WithdrawnAllocatedFund)
|
||||
// All items below should return error as they are removed from the store
|
||||
_, err := suite.poolKeeper.RecipientFundPercentage.Get(suite.ctx, tc.recipientAddr)
|
||||
suite.Require().Contains(err.Error(), "collections: not found")
|
||||
_, err = suite.poolKeeper.ContinuousFund.Get(suite.ctx, tc.recipientAddr)
|
||||
suite.Require().Contains(err.Error(), "collections: not found")
|
||||
_, err = suite.poolKeeper.RecipientFundDistribution.Get(suite.ctx, tc.recipientAddr)
|
||||
|
||||
@ -9,7 +9,6 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"cosmossdk.io/core/appmodule"
|
||||
"cosmossdk.io/core/legacy"
|
||||
"cosmossdk.io/core/registry"
|
||||
"cosmossdk.io/x/protocolpool/keeper"
|
||||
"cosmossdk.io/x/protocolpool/types"
|
||||
@ -24,7 +23,6 @@ const ConsensusVersion = 1
|
||||
|
||||
var (
|
||||
_ module.HasName = AppModule{}
|
||||
_ module.HasAminoCodec = AppModule{}
|
||||
_ module.HasGRPCGateway = AppModule{}
|
||||
_ module.AppModuleSimulation = AppModule{}
|
||||
|
||||
@ -60,9 +58,6 @@ func (AppModule) IsAppModule() {}
|
||||
// Name returns the pool module's name.
|
||||
func (AppModule) Name() string { return types.ModuleName }
|
||||
|
||||
// RegisterLegacyAminoCodec registers the pool module's types on the LegacyAmino codec.
|
||||
func (AppModule) RegisterLegacyAminoCodec(cdc legacy.Amino) {}
|
||||
|
||||
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes
|
||||
func (AppModule) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwruntime.ServeMux) {
|
||||
if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil {
|
||||
@ -104,10 +99,8 @@ func (am AppModule) InitGenesis(ctx context.Context, data json.RawMessage) error
|
||||
if err := am.cdc.UnmarshalJSON(data, &genesisState); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := am.keeper.InitGenesis(ctx, &genesisState); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
return am.keeper.InitGenesis(ctx, &genesisState)
|
||||
}
|
||||
|
||||
// ExportGenesis returns the exported genesis state as raw bytes for the protocolpool module.
|
||||
|
||||
@ -129,20 +129,6 @@ func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gom
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr)
|
||||
}
|
||||
|
||||
// MintCoins mocks base method.
|
||||
func (m *MockBankKeeper) MintCoins(ctx context.Context, moduleName string, amt types.Coins) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "MintCoins", ctx, moduleName, amt)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// MintCoins indicates an expected call of MintCoins.
|
||||
func (mr *MockBankKeeperMockRecorder) MintCoins(ctx, moduleName, amt interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MintCoins", reflect.TypeOf((*MockBankKeeper)(nil).MintCoins), ctx, moduleName, amt)
|
||||
}
|
||||
|
||||
// SendCoinsFromAccountToModule mocks base method.
|
||||
func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@ -4,5 +4,5 @@ import "cosmossdk.io/errors"
|
||||
|
||||
var (
|
||||
ErrInvalidSigner = errors.Register(ModuleName, 2, "expected authority account as only signer for community pool spend message")
|
||||
ErrNoRecipientFund = errors.Register(ModuleName, 3, "no recipient fund found for recipient")
|
||||
ErrNoRecipientFund = errors.Register(ModuleName, 3, "no recipient found")
|
||||
)
|
||||
|
||||
@ -17,7 +17,6 @@ type AccountKeeper interface {
|
||||
|
||||
// BankKeeper defines the expected interface needed to retrieve account balances.
|
||||
type BankKeeper interface {
|
||||
MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
|
||||
GetAllBalances(ctx context.Context, addr sdk.AccAddress) sdk.Coins
|
||||
SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins
|
||||
SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
"cosmossdk.io/errors"
|
||||
"cosmossdk.io/math"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
@ -45,12 +44,11 @@ func validateBudget(bp Budget) error {
|
||||
}
|
||||
|
||||
// Validate BudgetPerTranche
|
||||
amount := sdk.NewCoins(*bp.BudgetPerTranche)
|
||||
if amount.IsZero() {
|
||||
if bp.BudgetPerTranche == nil || bp.BudgetPerTranche.IsZero() {
|
||||
return fmt.Errorf("budget per tranche cannot be zero")
|
||||
}
|
||||
if err := amount.Validate(); err != nil {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidCoins, amount.String())
|
||||
if err := bp.BudgetPerTranche.Validate(); err != nil {
|
||||
return errors.Wrap(sdkerrors.ErrInvalidCoins, bp.BudgetPerTranche.String())
|
||||
}
|
||||
|
||||
if bp.TranchesLeft == 0 {
|
||||
@ -69,7 +67,7 @@ func validateContinuousFund(cf ContinuousFund) error {
|
||||
}
|
||||
|
||||
// Validate percentage
|
||||
if cf.Percentage.IsZero() || cf.Percentage.IsNil() {
|
||||
if cf.Percentage.IsNil() || cf.Percentage.IsZero() {
|
||||
return fmt.Errorf("percentage cannot be zero or empty")
|
||||
}
|
||||
if cf.Percentage.IsNegative() {
|
||||
|
||||
@ -18,9 +18,9 @@ func NewMsgFundCommunityPool(amount sdk.Coins, depositor string) *MsgFundCommuni
|
||||
}
|
||||
}
|
||||
|
||||
// NewCommunityPoolSpend returns a new CommunityPoolSpend with authority, recipient and
|
||||
// NewMsgCommunityPoolSpend returns a new CommunityPoolSpend with authority, recipient and
|
||||
// a spending amount.
|
||||
func NewCommunityPoolSpend(amount sdk.Coins, authority, recipient string) *MsgCommunityPoolSpend {
|
||||
func NewMsgCommunityPoolSpend(amount sdk.Coins, authority, recipient string) *MsgCommunityPoolSpend {
|
||||
return &MsgCommunityPoolSpend{
|
||||
Authority: authority,
|
||||
Recipient: recipient,
|
||||
|
||||
230
x/protocolpool/types/types_test.go
Normal file
230
x/protocolpool/types/types_test.go
Normal file
@ -0,0 +1,230 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
time "time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
|
||||
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var hour = time.Hour
|
||||
|
||||
func TestRegisterInterfaces(t *testing.T) {
|
||||
interfaceRegistry := codectestutil.NewCodecOptionsWithPrefixes("cosmos", "cosmosval").NewInterfaceRegistry()
|
||||
RegisterInterfaces(interfaceRegistry)
|
||||
require.NoError(t, interfaceRegistry.EnsureRegistered(&MsgFundCommunityPool{}))
|
||||
require.NoError(t, interfaceRegistry.EnsureRegistered(&MsgCommunityPoolSpend{}))
|
||||
require.NoError(t, interfaceRegistry.EnsureRegistered(&MsgSubmitBudgetProposal{}))
|
||||
require.NoError(t, interfaceRegistry.EnsureRegistered(&MsgClaimBudget{}))
|
||||
require.NoError(t, interfaceRegistry.EnsureRegistered(&MsgCreateContinuousFund{}))
|
||||
require.NoError(t, interfaceRegistry.EnsureRegistered(&MsgCancelContinuousFund{}))
|
||||
require.NoError(t, interfaceRegistry.EnsureRegistered(&MsgWithdrawContinuousFund{}))
|
||||
}
|
||||
|
||||
func TestNewMsgFundCommunityPool(t *testing.T) {
|
||||
amount := sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(100)))
|
||||
depositor := "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4"
|
||||
msg := NewMsgFundCommunityPool(amount, depositor)
|
||||
require.Equal(t, amount, msg.Amount)
|
||||
require.Equal(t, depositor, msg.Depositor)
|
||||
}
|
||||
|
||||
func TestNewMsgCommunityPoolSpend(t *testing.T) {
|
||||
amount := sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(100)))
|
||||
authority := "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4"
|
||||
recipient := "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z5"
|
||||
msg := NewMsgCommunityPoolSpend(amount, authority, recipient)
|
||||
require.Equal(t, amount, msg.Amount)
|
||||
require.Equal(t, authority, msg.Authority)
|
||||
require.Equal(t, recipient, msg.Recipient)
|
||||
}
|
||||
|
||||
func TestValidateGenesis(t *testing.T) {
|
||||
defaultGenesis := DefaultGenesisState()
|
||||
err := ValidateGenesis(defaultGenesis)
|
||||
require.NoError(t, err)
|
||||
|
||||
gs := NewGenesisState(
|
||||
[]*ContinuousFund{
|
||||
{
|
||||
Recipient: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
Percentage: math.LegacyMustNewDecFromStr("0.1"),
|
||||
Expiry: nil,
|
||||
},
|
||||
},
|
||||
[]*Budget{
|
||||
{
|
||||
RecipientAddress: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
ClaimedAmount: &sdk.Coin{},
|
||||
LastClaimedAt: &time.Time{},
|
||||
TranchesLeft: 10,
|
||||
BudgetPerTranche: &sdk.Coin{Denom: "stake", Amount: math.NewInt(100)},
|
||||
Period: &hour,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
err = ValidateGenesis(gs)
|
||||
require.NoError(t, err)
|
||||
|
||||
gs.Budget[0].RecipientAddress = ""
|
||||
err = ValidateGenesis(gs)
|
||||
require.EqualError(t, err, "recipient cannot be empty")
|
||||
|
||||
gs.ContinuousFund[0].Recipient = ""
|
||||
err = ValidateGenesis(gs)
|
||||
require.EqualError(t, err, "recipient cannot be empty")
|
||||
}
|
||||
|
||||
func TestValidateBudget(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
budget Budget
|
||||
expErrMsg string
|
||||
}{
|
||||
{
|
||||
"valid budget",
|
||||
Budget{
|
||||
RecipientAddress: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
ClaimedAmount: &sdk.Coin{},
|
||||
LastClaimedAt: &time.Time{},
|
||||
TranchesLeft: 10,
|
||||
BudgetPerTranche: &sdk.Coin{Denom: "stake", Amount: math.NewInt(100)},
|
||||
Period: &hour,
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"empty recipient",
|
||||
Budget{
|
||||
RecipientAddress: "",
|
||||
},
|
||||
"recipient cannot be empty",
|
||||
},
|
||||
{
|
||||
"zero budget per tranche",
|
||||
Budget{
|
||||
RecipientAddress: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
BudgetPerTranche: &sdk.Coin{Denom: "stake", Amount: math.NewInt(0)},
|
||||
},
|
||||
"budget per tranche cannot be zero",
|
||||
},
|
||||
{
|
||||
"nil budget per tranche",
|
||||
Budget{
|
||||
RecipientAddress: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
BudgetPerTranche: nil,
|
||||
},
|
||||
"budget per tranche cannot be zero",
|
||||
},
|
||||
{
|
||||
"negative budget per tranche",
|
||||
Budget{
|
||||
RecipientAddress: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
BudgetPerTranche: &sdk.Coin{Denom: "stake", Amount: math.NewInt(-100)},
|
||||
},
|
||||
"-100stake: invalid coins",
|
||||
},
|
||||
{
|
||||
"zero tranches left",
|
||||
Budget{
|
||||
RecipientAddress: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
TranchesLeft: 0,
|
||||
BudgetPerTranche: &sdk.Coin{Denom: "stake", Amount: math.NewInt(100)},
|
||||
},
|
||||
"invalid budget proposal: tranches must be greater than zero",
|
||||
},
|
||||
{
|
||||
"zero period",
|
||||
Budget{
|
||||
RecipientAddress: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
ClaimedAmount: &sdk.Coin{},
|
||||
LastClaimedAt: &time.Time{},
|
||||
TranchesLeft: 10,
|
||||
BudgetPerTranche: &sdk.Coin{Denom: "stake", Amount: math.NewInt(100)},
|
||||
Period: nil,
|
||||
},
|
||||
"invalid budget proposal: period length should be greater than zero",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := validateBudget(tc.budget)
|
||||
if tc.expErrMsg == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, tc.expErrMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateContinuousFund(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
cf ContinuousFund
|
||||
expErrMsg string
|
||||
}{
|
||||
{
|
||||
"valid continuous fund",
|
||||
ContinuousFund{
|
||||
Recipient: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
Percentage: math.LegacyMustNewDecFromStr("0.1"),
|
||||
Expiry: nil,
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"empty recipient",
|
||||
ContinuousFund{
|
||||
Recipient: "",
|
||||
},
|
||||
"recipient cannot be empty",
|
||||
},
|
||||
{
|
||||
"zero percentage",
|
||||
ContinuousFund{
|
||||
Recipient: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
Percentage: math.LegacyZeroDec(),
|
||||
},
|
||||
"percentage cannot be zero or empty",
|
||||
},
|
||||
{
|
||||
"nil percentage",
|
||||
ContinuousFund{
|
||||
Recipient: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
Percentage: math.LegacyDec{},
|
||||
},
|
||||
"percentage cannot be zero or empty",
|
||||
},
|
||||
{
|
||||
"negative percentage",
|
||||
ContinuousFund{
|
||||
Recipient: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
Percentage: math.LegacyMustNewDecFromStr("-0.1"),
|
||||
},
|
||||
"percentage cannot be negative",
|
||||
},
|
||||
{
|
||||
"percentage exceeds 100%",
|
||||
ContinuousFund{
|
||||
Recipient: "cosmos1qypq2q2l8z4wz2z2l8z4wz2z2l8z4wz2z2l8z4",
|
||||
Percentage: math.LegacyMustNewDecFromStr("1.1"),
|
||||
},
|
||||
"percentage cannot be greater than one",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := validateContinuousFund(tc.cf)
|
||||
if tc.expErrMsg == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, tc.expErrMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user