1042 lines
38 KiB
Go
1042 lines
38 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
|
|
"cosmossdk.io/collections"
|
|
"cosmossdk.io/core/header"
|
|
"cosmossdk.io/math"
|
|
"cosmossdk.io/x/protocolpool/types"
|
|
|
|
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
)
|
|
|
|
var (
|
|
recipientAddr = sdk.AccAddress([]byte("to1__________________"))
|
|
|
|
fooCoin = sdk.NewInt64Coin("foo", 100)
|
|
fooCoin2 = sdk.NewInt64Coin("foo", 50)
|
|
)
|
|
|
|
func (suite *KeeperTestSuite) TestMsgSubmitBudgetProposal() {
|
|
invalidCoin := sdk.NewInt64Coin("foo", 0)
|
|
startTime := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(10 * time.Second)
|
|
invalidStartTime := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(-15 * time.Second)
|
|
period := time.Duration(60) * time.Second
|
|
zeroPeriod := time.Duration(0) * time.Second
|
|
recipientStrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipientAddr)
|
|
suite.Require().NoError(err)
|
|
testCases := map[string]struct {
|
|
input *types.MsgSubmitBudgetProposal
|
|
expErr bool
|
|
expErrMsg string
|
|
}{
|
|
"empty recipient address": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: "",
|
|
BudgetPerTranche: &fooCoin,
|
|
StartTime: &startTime,
|
|
Tranches: 2,
|
|
Period: &period,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "empty address string is not allowed",
|
|
},
|
|
"empty authority": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: "",
|
|
RecipientAddress: recipientStrAddr,
|
|
BudgetPerTranche: &fooCoin,
|
|
StartTime: &startTime,
|
|
Tranches: 2,
|
|
Period: &period,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "empty address string is not allowed",
|
|
},
|
|
"invalid authority": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: "invalid_authority",
|
|
RecipientAddress: recipientStrAddr,
|
|
BudgetPerTranche: &fooCoin,
|
|
StartTime: &startTime,
|
|
Tranches: 2,
|
|
Period: &period,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid authority",
|
|
},
|
|
"invalid budget": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipientStrAddr,
|
|
BudgetPerTranche: &invalidCoin,
|
|
StartTime: &startTime,
|
|
Tranches: 2,
|
|
Period: &period,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "budget per tranche cannot be zero",
|
|
},
|
|
"invalid start time": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipientStrAddr,
|
|
BudgetPerTranche: &fooCoin,
|
|
StartTime: &invalidStartTime,
|
|
Tranches: 2,
|
|
Period: &period,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "start time cannot be less than the current block time",
|
|
},
|
|
"invalid tranches": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipientStrAddr,
|
|
BudgetPerTranche: &fooCoin,
|
|
StartTime: &startTime,
|
|
Tranches: 0,
|
|
Period: &period,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "tranches must be greater than zero",
|
|
},
|
|
"invalid period": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipientStrAddr,
|
|
BudgetPerTranche: &fooCoin,
|
|
StartTime: &startTime,
|
|
Tranches: 2,
|
|
Period: &zeroPeriod,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "period length should be greater than zero",
|
|
},
|
|
"all good": {
|
|
input: &types.MsgSubmitBudgetProposal{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipientStrAddr,
|
|
BudgetPerTranche: &fooCoin2,
|
|
StartTime: &startTime,
|
|
Tranches: 2,
|
|
Period: &period,
|
|
},
|
|
expErr: false,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
suite.Run(name, func() {
|
|
suite.SetupTest()
|
|
|
|
_, err := suite.msgServer.SubmitBudgetProposal(suite.ctx, tc.input)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestMsgClaimBudget() {
|
|
startTime := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(-70 * time.Second)
|
|
period := time.Duration(60) * time.Second
|
|
recipientStrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipientAddr)
|
|
suite.Require().NoError(err)
|
|
|
|
testCases := map[string]struct {
|
|
preRun func()
|
|
postRun func()
|
|
recipientAddress sdk.AccAddress
|
|
expErr bool
|
|
expErrMsg string
|
|
claimableFunds sdk.Coin
|
|
}{
|
|
"empty recipient addr": {
|
|
recipientAddress: sdk.AccAddress(""),
|
|
expErr: true,
|
|
expErrMsg: "invalid recipient address: empty address string is not allowed",
|
|
},
|
|
"no budget found": {
|
|
recipientAddress: sdk.AccAddress([]byte("acc1__________")),
|
|
expErr: true,
|
|
expErrMsg: "no budget found for recipient",
|
|
},
|
|
"claiming before last claimed at": {
|
|
preRun: func() {
|
|
startTime := startTime.Add(3600 * time.Second)
|
|
// Prepare the budget proposal with a future last claimed at time
|
|
budget := types.Budget{
|
|
RecipientAddress: recipientStrAddr,
|
|
TranchesLeft: 2,
|
|
Period: &period,
|
|
LastClaimedAt: &startTime,
|
|
BudgetPerTranche: &fooCoin2,
|
|
}
|
|
err := suite.poolKeeper.BudgetProposal.Set(suite.ctx, recipientAddr, budget)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: recipientAddr,
|
|
expErr: true,
|
|
expErrMsg: "distribution has not started yet",
|
|
},
|
|
"budget period has not passed": {
|
|
preRun: func() {
|
|
startTime := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(-50 * time.Second)
|
|
// Prepare the budget proposal with start time and a short period
|
|
budget := types.Budget{
|
|
RecipientAddress: recipientStrAddr,
|
|
LastClaimedAt: &startTime,
|
|
TranchesLeft: 1,
|
|
Period: &period,
|
|
BudgetPerTranche: &fooCoin2,
|
|
}
|
|
err := suite.poolKeeper.BudgetProposal.Set(suite.ctx, recipientAddr, budget)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: recipientAddr,
|
|
expErr: true,
|
|
expErrMsg: "budget period has not passed yet",
|
|
},
|
|
"valid claim": {
|
|
preRun: func() {
|
|
// Prepare the budget proposal with valid start time and period
|
|
budget := types.Budget{
|
|
RecipientAddress: recipientStrAddr,
|
|
LastClaimedAt: &startTime,
|
|
TranchesLeft: 2,
|
|
Period: &period,
|
|
BudgetPerTranche: &fooCoin2,
|
|
}
|
|
err := suite.poolKeeper.BudgetProposal.Set(suite.ctx, recipientAddr, budget)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: recipientAddr,
|
|
expErr: false,
|
|
claimableFunds: sdk.NewInt64Coin("foo", 50),
|
|
},
|
|
"claiming budget after a long time": {
|
|
preRun: func() {
|
|
// Prepare the budget proposal with valid start time and period
|
|
budget := types.Budget{
|
|
RecipientAddress: recipientStrAddr,
|
|
LastClaimedAt: &startTime,
|
|
TranchesLeft: 2,
|
|
Period: &period,
|
|
BudgetPerTranche: &fooCoin2,
|
|
}
|
|
err := suite.poolKeeper.BudgetProposal.Set(suite.ctx, recipientAddr, budget)
|
|
suite.Require().NoError(err)
|
|
|
|
// fast forward the block time by 240 hours
|
|
hinfo := suite.environment.HeaderService.HeaderInfo(suite.ctx)
|
|
hinfo.Time = hinfo.Time.Add(240 * time.Hour)
|
|
suite.ctx = suite.ctx.WithHeaderInfo(hinfo)
|
|
},
|
|
recipientAddress: recipientAddr,
|
|
claimableFunds: sdk.NewInt64Coin("foo", 100), // claiming the whole budget, 2 * 50foo = 100foo
|
|
postRun: func() {
|
|
prop, err := suite.poolKeeper.BudgetProposal.Get(suite.ctx, recipientAddr)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(uint64(0), prop.TranchesLeft)
|
|
// check if the lastClaimedAt is correct (in this case 2 periods after the start time)
|
|
suite.Require().Equal(startTime.Add(period*time.Duration(2)), *prop.LastClaimedAt)
|
|
},
|
|
},
|
|
"double claim attempt with budget period not passed": {
|
|
preRun: func() {
|
|
// Prepare the budget proposal with valid start time and period
|
|
budget := types.Budget{
|
|
RecipientAddress: recipientStrAddr,
|
|
LastClaimedAt: &startTime,
|
|
TranchesLeft: 2,
|
|
Period: &period,
|
|
BudgetPerTranche: &fooCoin2,
|
|
}
|
|
err := suite.poolKeeper.BudgetProposal.Set(suite.ctx, recipientAddr, budget)
|
|
suite.Require().NoError(err)
|
|
|
|
// Claim the funds once
|
|
msg := &types.MsgClaimBudget{
|
|
RecipientAddress: recipientStrAddr,
|
|
}
|
|
suite.mockSendCoinsFromModuleToAccount(recipientAddr)
|
|
_, err = suite.msgServer.ClaimBudget(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: recipientAddr,
|
|
expErr: true,
|
|
expErrMsg: "budget period has not passed yet",
|
|
},
|
|
"valid double claim attempt": {
|
|
preRun: func() {
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
startTimeBeforeMonth := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(-oneMonthInSeconds) * time.Second)
|
|
oneMonthPeriod := time.Duration(oneMonthInSeconds) * time.Second
|
|
// Prepare the budget proposal with valid start time and period of 1 month (in seconds)
|
|
budget := types.Budget{
|
|
RecipientAddress: recipientStrAddr,
|
|
LastClaimedAt: &startTimeBeforeMonth,
|
|
TranchesLeft: 2,
|
|
Period: &oneMonthPeriod,
|
|
BudgetPerTranche: &fooCoin2,
|
|
}
|
|
err := suite.poolKeeper.BudgetProposal.Set(suite.ctx, recipientAddr, budget)
|
|
suite.Require().NoError(err)
|
|
|
|
// Claim the funds once
|
|
msg := &types.MsgClaimBudget{
|
|
RecipientAddress: recipientStrAddr,
|
|
}
|
|
suite.mockSendCoinsFromModuleToAccount(recipientAddr)
|
|
_, err = suite.msgServer.ClaimBudget(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
|
|
// Create a new context with an updated block time to simulate a delay
|
|
newBlockTime := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
suite.ctx = suite.ctx.WithHeaderInfo(header.Info{
|
|
Time: newBlockTime,
|
|
})
|
|
},
|
|
recipientAddress: recipientAddr,
|
|
expErr: false,
|
|
claimableFunds: sdk.NewInt64Coin("foo", 50),
|
|
},
|
|
"budget ended for recipient": {
|
|
preRun: func() {
|
|
// Prepare the budget proposal with valid start time and period
|
|
budget := types.Budget{
|
|
RecipientAddress: recipientStrAddr,
|
|
LastClaimedAt: &startTime,
|
|
TranchesLeft: 2,
|
|
Period: &period,
|
|
BudgetPerTranche: &fooCoin2,
|
|
}
|
|
err := suite.poolKeeper.BudgetProposal.Set(suite.ctx, recipientAddr, budget)
|
|
suite.Require().NoError(err)
|
|
|
|
// Claim the funds once
|
|
msg := &types.MsgClaimBudget{
|
|
RecipientAddress: recipientStrAddr,
|
|
}
|
|
suite.mockSendCoinsFromModuleToAccount(recipientAddr)
|
|
_, err = suite.msgServer.ClaimBudget(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
|
|
// Create a new context with an updated block time to simulate a delay
|
|
newBlockTime := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(60 * time.Second)
|
|
suite.ctx = suite.ctx.WithHeaderInfo(header.Info{
|
|
Time: newBlockTime,
|
|
})
|
|
|
|
// Claim the funds twice
|
|
msg = &types.MsgClaimBudget{
|
|
RecipientAddress: recipientStrAddr,
|
|
}
|
|
suite.mockSendCoinsFromModuleToAccount(recipientAddr)
|
|
_, err = suite.msgServer.ClaimBudget(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: recipientAddr,
|
|
expErr: true,
|
|
expErrMsg: "budget ended for recipient",
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
suite.Run(name, func() {
|
|
suite.SetupTest()
|
|
if tc.preRun != nil {
|
|
tc.preRun()
|
|
}
|
|
addr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(tc.recipientAddress)
|
|
suite.Require().NoError(err)
|
|
msg := &types.MsgClaimBudget{
|
|
RecipientAddress: addr,
|
|
}
|
|
suite.mockSendCoinsFromModuleToAccount(tc.recipientAddress)
|
|
resp, err := suite.msgServer.ClaimBudget(suite.ctx, msg)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(tc.claimableFunds, resp.Amount)
|
|
}
|
|
|
|
if tc.postRun != nil {
|
|
tc.postRun()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestWithdrawContinuousFund() {
|
|
addressCodec := codectestutil.CodecOptions{}.GetAddressCodec()
|
|
recipient := sdk.AccAddress([]byte("recipientAddr1__________________"))
|
|
recipientStrAddr, err := addressCodec.BytesToString(recipient)
|
|
suite.Require().NoError(err)
|
|
recipient2 := sdk.AccAddress([]byte("recipientAddr2___________________"))
|
|
recipient2StrAddr, err := addressCodec.BytesToString(recipient2)
|
|
suite.Require().NoError(err)
|
|
recipient3 := sdk.AccAddress([]byte("recipientAddr3___________________"))
|
|
recipient3StrAddr, err := addressCodec.BytesToString(recipient3)
|
|
suite.Require().NoError(err)
|
|
testCases := map[string]struct {
|
|
preRun func()
|
|
recipientAddress []sdk.AccAddress
|
|
expErr bool
|
|
expErrMsg string
|
|
withdrawnAmount sdk.Coin
|
|
}{
|
|
"empty recipient": {
|
|
recipientAddress: []sdk.AccAddress{sdk.AccAddress([]byte(""))},
|
|
expErr: true,
|
|
expErrMsg: "invalid recipient address",
|
|
},
|
|
"recipient with no continuous fund": {
|
|
recipientAddress: []sdk.AccAddress{recipient},
|
|
expErr: true,
|
|
expErrMsg: "error while withdrawing recipient funds for recipient: no recipient found",
|
|
},
|
|
"funds percentage > 100": {
|
|
preRun: func() {
|
|
// Set fund 1
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
// Set continuous fund
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
|
suite.Require().NoError(err)
|
|
// Set recipient fund percentage and recipient fund distribution
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
|
|
// Set fund 2
|
|
percentage, err = math.LegacyNewDecFromStr("0.9")
|
|
suite.Require().NoError(err)
|
|
cf = types.ContinuousFund{
|
|
Recipient: recipient2StrAddr,
|
|
Percentage: percentage,
|
|
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
|
|
suite.Require().NoError(err)
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient2, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
|
|
// Set ToDistribute
|
|
err = suite.poolKeeper.Distributions.Set(suite.ctx, suite.ctx.HeaderInfo().Time, math.NewInt(100000))
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: []sdk.AccAddress{recipient},
|
|
expErr: true,
|
|
expErrMsg: "error while iterating all the continuous funds: total funds percentage cannot exceed 100",
|
|
},
|
|
"expired case with no funds left to withdraw": {
|
|
preRun: func() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(-1) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
// Set continuous fund
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: []sdk.AccAddress{recipient},
|
|
expErr: true,
|
|
expErrMsg: "error while withdrawing recipient funds for recipient: no recipient found",
|
|
},
|
|
"valid case with ToDistribute amount zero": {
|
|
preRun: func() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
// Set continuous fund
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
|
suite.Require().NoError(err)
|
|
// Set recipient fund percentage and recipient fund distribution
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
err = suite.poolKeeper.Distributions.Set(suite.ctx, suite.ctx.HeaderInfo().Time, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
suite.mockStreamFunds(math.NewInt(0))
|
|
},
|
|
recipientAddress: []sdk.AccAddress{recipient},
|
|
expErr: false,
|
|
withdrawnAmount: sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt()),
|
|
},
|
|
"valid case with empty expiry": {
|
|
preRun: func() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
}
|
|
// Set continuous fund
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
|
suite.Require().NoError(err)
|
|
// Set recipient fund percentage and recipient fund distribution
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
suite.mockStreamFunds(math.NewInt(100000))
|
|
err = suite.poolKeeper.SetToDistribute(suite.ctx)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: []sdk.AccAddress{recipient},
|
|
expErr: false,
|
|
withdrawnAmount: sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(20000)),
|
|
},
|
|
"valid case": {
|
|
preRun: func() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
// Set continuous fund
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
|
suite.Require().NoError(err)
|
|
// Set recipient fund percentage and recipient fund distribution
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
suite.mockStreamFunds(math.NewInt(100000))
|
|
err = suite.poolKeeper.SetToDistribute(suite.ctx)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: []sdk.AccAddress{recipient},
|
|
expErr: false,
|
|
withdrawnAmount: sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(20000)),
|
|
},
|
|
"valid case with multiple funds": {
|
|
preRun: func() {
|
|
// Set continuous fund 1
|
|
percentage, err := math.LegacyNewDecFromStr("0.3")
|
|
suite.Require().NoError(err)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
// Set continuous fund
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient, cf)
|
|
suite.Require().NoError(err)
|
|
// Set recipient fund percentage and recipient fund distribution
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
|
|
// Set continuous fund 2
|
|
percentage2, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
cf = types.ContinuousFund{
|
|
Recipient: recipient2StrAddr,
|
|
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
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient2, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
|
|
// Set continuous fund 3
|
|
percentage3, err := math.LegacyNewDecFromStr("0.3")
|
|
suite.Require().NoError(err)
|
|
cf = types.ContinuousFund{
|
|
Recipient: recipient3StrAddr,
|
|
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
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient3, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
|
|
suite.mockStreamFunds(math.NewInt(100000))
|
|
err = suite.poolKeeper.SetToDistribute(suite.ctx)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddress: []sdk.AccAddress{recipient, recipient2, recipient3},
|
|
expErr: false,
|
|
withdrawnAmount: sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(30000)),
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
suite.Run(name, func() {
|
|
suite.SetupTest()
|
|
if tc.preRun != nil {
|
|
tc.preRun()
|
|
}
|
|
addr, err := addressCodec.BytesToString(tc.recipientAddress[0])
|
|
suite.Require().NoError(err)
|
|
msg := &types.MsgWithdrawContinuousFund{
|
|
RecipientAddress: addr,
|
|
}
|
|
|
|
suite.mockWithdrawContinuousFund()
|
|
|
|
resp, err := suite.msgServer.WithdrawContinuousFund(suite.ctx, msg)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(tc.withdrawnAmount, resp.Amount)
|
|
|
|
// this condition is valid only for request with multiple continuous funds
|
|
if len(tc.recipientAddress) > 1 {
|
|
toClaim, err := suite.poolKeeper.RecipientFundDistribution.Get(suite.ctx, tc.recipientAddress[1])
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(toClaim, math.NewInt(20000))
|
|
toClaim, err = suite.poolKeeper.RecipientFundDistribution.Get(suite.ctx, tc.recipientAddress[2])
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(toClaim, math.NewInt(30000))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestCreateContinuousFund() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
negativePercentage, err := math.LegacyNewDecFromStr("-0.2")
|
|
suite.Require().NoError(err)
|
|
invalidExpirty := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(-15 * time.Second)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
recipientStrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipientAddr)
|
|
suite.Require().NoError(err)
|
|
testCases := map[string]struct {
|
|
preRun func()
|
|
input *types.MsgCreateContinuousFund
|
|
expErr bool
|
|
expErrMsg string
|
|
}{
|
|
"empty recipient address": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: "",
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "empty address string is not allowed",
|
|
},
|
|
"empty authority": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: "",
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "empty address string is not allowed",
|
|
},
|
|
"invalid authority": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: "invalid_authority",
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "invalid authority",
|
|
},
|
|
"zero percentage": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientStrAddr,
|
|
Percentage: math.LegacyNewDec(0),
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "percentage cannot be zero or empty",
|
|
},
|
|
"negative percentage": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientStrAddr,
|
|
Percentage: negativePercentage,
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "percentage cannot be negative",
|
|
},
|
|
"invalid percentage": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientStrAddr,
|
|
Percentage: math.LegacyNewDec(1),
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "percentage cannot be greater than or equal to one",
|
|
},
|
|
"invalid expiry": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &invalidExpirty,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "expiry time cannot be less than the current block time",
|
|
},
|
|
"all good": {
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: false,
|
|
},
|
|
"total funds percentage > 100": {
|
|
preRun: func() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.9")
|
|
suite.Require().NoError(err)
|
|
recipient2 := sdk.AccAddress([]byte("recipientAddr2___________________"))
|
|
recipient2StrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipient2)
|
|
suite.Require().NoError(err)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipient2StrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &time.Time{},
|
|
}
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient2, cf)
|
|
suite.Require().NoError(err)
|
|
},
|
|
input: &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "cannot set continuous fund proposal\ntotal funds percentage exceeds 100\ncurrent total percentage: 90",
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
suite.Run(name, func() {
|
|
suite.SetupTest()
|
|
if tc.preRun != nil {
|
|
tc.preRun()
|
|
}
|
|
|
|
_, err := suite.msgServer.CreateContinuousFund(suite.ctx, tc.input)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
suite.Require().NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCancelContinuousFund tests the cancellation of a continuous fund.
|
|
// It verifies various scenarios such as canceling a fund with an empty recipient,
|
|
// canceling a fund with no recipient found, canceling a fund with unclaimed funds for the recipient,
|
|
// and canceling a fund with no errors.
|
|
func (suite *KeeperTestSuite) TestCancelContinuousFund() {
|
|
recipientStrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipientAddr)
|
|
suite.Require().NoError(err)
|
|
recipient2 := sdk.AccAddress([]byte("recipientAddr2___________________"))
|
|
recipient2StrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipient2)
|
|
suite.Require().NoError(err)
|
|
recipient3 := sdk.AccAddress([]byte("recipientAddr3___________________"))
|
|
recipient3StrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipient3)
|
|
suite.Require().NoError(err)
|
|
|
|
testCases := map[string]struct {
|
|
preRun func()
|
|
recipientAddr sdk.AccAddress
|
|
expErr bool
|
|
expErrMsg string
|
|
postRun func()
|
|
withdrawnFunds sdk.Coin
|
|
}{
|
|
"empty recipient": {
|
|
preRun: func() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: "",
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipientAddr, cf)
|
|
suite.Require().NoError(err)
|
|
},
|
|
expErr: true,
|
|
expErrMsg: "empty address string is not allowed",
|
|
},
|
|
"all good with unclaimed funds for recipient": {
|
|
preRun: func() {
|
|
// Set fund 1
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipientStrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
// Set continuous fund
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipientAddr, cf)
|
|
suite.Require().NoError(err)
|
|
// Set recipient fund percentage and recipient fund distribution
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipientAddr, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
|
|
// Set fund 2
|
|
percentage, err = math.LegacyNewDecFromStr("0.3")
|
|
suite.Require().NoError(err)
|
|
cf = types.ContinuousFund{
|
|
Recipient: recipient2StrAddr,
|
|
Percentage: percentage,
|
|
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
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient2, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
|
|
// Set ToDistribute
|
|
suite.mockStreamFunds(math.NewInt(100000))
|
|
err = suite.poolKeeper.SetToDistribute(suite.ctx)
|
|
suite.Require().NoError(err)
|
|
|
|
// withdraw funds for fund request 2
|
|
suite.mockWithdrawContinuousFund()
|
|
msg := &types.MsgWithdrawContinuousFund{RecipientAddress: recipient2StrAddr}
|
|
_, err = suite.msgServer.WithdrawContinuousFund(suite.ctx, msg)
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddr: recipientAddr,
|
|
expErr: false,
|
|
postRun: func() {
|
|
_, err := suite.poolKeeper.ContinuousFund.Get(suite.ctx, recipientAddr)
|
|
suite.Require().Error(err)
|
|
suite.Require().ErrorIs(err, collections.ErrNotFound)
|
|
},
|
|
withdrawnFunds: sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(20000)),
|
|
},
|
|
"all good": {
|
|
preRun: func() {
|
|
percentage, err := math.LegacyNewDecFromStr("0.2")
|
|
suite.Require().NoError(err)
|
|
oneMonthInSeconds := int64(30 * 24 * 60 * 60) // Approximate number of seconds in 1 month
|
|
expiry := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(time.Duration(oneMonthInSeconds) * time.Second)
|
|
cf := types.ContinuousFund{
|
|
Recipient: recipient3StrAddr,
|
|
Percentage: percentage,
|
|
Expiry: &expiry,
|
|
}
|
|
suite.mockWithdrawContinuousFund()
|
|
err = suite.poolKeeper.ContinuousFund.Set(suite.ctx, recipient3, cf)
|
|
suite.Require().NoError(err)
|
|
err = suite.poolKeeper.RecipientFundDistribution.Set(suite.ctx, recipient3, math.ZeroInt())
|
|
suite.Require().NoError(err)
|
|
},
|
|
recipientAddr: recipient3,
|
|
expErr: false,
|
|
postRun: func() {
|
|
_, err := suite.poolKeeper.ContinuousFund.Get(suite.ctx, recipient3)
|
|
suite.Require().Error(err)
|
|
suite.Require().ErrorIs(err, collections.ErrNotFound)
|
|
},
|
|
withdrawnFunds: sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)),
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
suite.Run(name, func() {
|
|
suite.SetupTest()
|
|
if tc.preRun != nil {
|
|
tc.preRun()
|
|
}
|
|
addr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(tc.recipientAddr)
|
|
suite.Require().NoError(err)
|
|
msg := &types.MsgCancelContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: addr,
|
|
}
|
|
resp, err := suite.msgServer.CancelContinuousFund(suite.ctx, msg)
|
|
if tc.expErr {
|
|
suite.Require().Error(err)
|
|
suite.Require().Contains(err.Error(), tc.expErrMsg)
|
|
} else {
|
|
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.ContinuousFund.Get(suite.ctx, tc.recipientAddr)
|
|
suite.Require().Contains(err.Error(), "collections: not found")
|
|
_, err = suite.poolKeeper.RecipientFundDistribution.Get(suite.ctx, tc.recipientAddr)
|
|
suite.Require().Contains(err.Error(), "collections: not found")
|
|
}
|
|
if tc.postRun != nil {
|
|
tc.postRun()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestWithdrawExpiredFunds checks that a continuous fund cannot be withdrawn if it has expired.
|
|
// There was a case in which an expired continuous fund would keep getting funds allocated when
|
|
// other funds were withdrawn. These funds would then get withdrawn if CancelContinuousFund was called.
|
|
func (suite *KeeperTestSuite) TestWithdrawExpiredFunds() {
|
|
suite.SetupTest()
|
|
recipientStrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipientAddr)
|
|
suite.Require().NoError(err)
|
|
recipient2 := sdk.AccAddress([]byte("recipientAddr2___________________"))
|
|
recipient2StrAddr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(recipient2)
|
|
suite.Require().NoError(err)
|
|
|
|
expiration := suite.environment.HeaderService.HeaderInfo(suite.ctx).Time.Add(24 * time.Hour)
|
|
_, err = suite.msgServer.CreateContinuousFund(suite.ctx, &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientStrAddr,
|
|
Percentage: math.LegacyMustNewDecFromStr("0.5"),
|
|
Expiry: &expiration,
|
|
})
|
|
suite.Require().NoError(err)
|
|
|
|
_, err = suite.msgServer.CreateContinuousFund(suite.ctx, &types.MsgCreateContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipient2StrAddr,
|
|
Percentage: math.LegacyMustNewDecFromStr("0.5"),
|
|
})
|
|
suite.Require().NoError(err)
|
|
|
|
suite.mockStreamFunds(math.NewInt(100000))
|
|
err = suite.poolKeeper.SetToDistribute(suite.ctx)
|
|
suite.Require().NoError(err)
|
|
|
|
suite.mockWithdrawContinuousFund()
|
|
_, err = suite.msgServer.WithdrawContinuousFund(suite.ctx, &types.MsgWithdrawContinuousFund{RecipientAddress: recipientStrAddr})
|
|
suite.Require().NoError(err)
|
|
|
|
header := suite.ctx.HeaderInfo()
|
|
header.Time = expiration.Add(1 * time.Second)
|
|
suite.ctx = suite.ctx.WithHeaderInfo(header)
|
|
|
|
// If we keep calling WithdrawContinuousFund, it should not error and return always an amount of 0
|
|
withdrawRes, err := suite.msgServer.WithdrawContinuousFund(suite.ctx, &types.MsgWithdrawContinuousFund{RecipientAddress: recipientStrAddr})
|
|
suite.Require().True(withdrawRes.Amount.IsZero())
|
|
suite.Require().NoError(err)
|
|
|
|
withdrawRes, err = suite.msgServer.WithdrawContinuousFund(suite.ctx, &types.MsgWithdrawContinuousFund{RecipientAddress: recipientStrAddr})
|
|
suite.Require().True(withdrawRes.Amount.IsZero())
|
|
suite.Require().NoError(err)
|
|
|
|
suite.mockStreamFunds(math.NewInt(100000))
|
|
err = suite.poolKeeper.SetToDistribute(suite.ctx)
|
|
suite.Require().NoError(err)
|
|
|
|
suite.mockWithdrawContinuousFund()
|
|
_, err = suite.msgServer.WithdrawContinuousFund(suite.ctx, &types.MsgWithdrawContinuousFund{RecipientAddress: recipient2StrAddr})
|
|
suite.Require().NoError(err)
|
|
|
|
res, err := suite.msgServer.CancelContinuousFund(suite.ctx, &types.MsgCancelContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipient2StrAddr,
|
|
})
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)), res.WithdrawnAllocatedFund)
|
|
|
|
// canceling an expired continuous fund, won't error
|
|
res, err = suite.msgServer.CancelContinuousFund(suite.ctx, &types.MsgCancelContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipientStrAddr,
|
|
})
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)), res.WithdrawnAllocatedFund)
|
|
|
|
// if we try to cancel again the same continuout fund, it won't error, it will still distribute funds if needed.
|
|
res, err = suite.msgServer.CancelContinuousFund(suite.ctx, &types.MsgCancelContinuousFund{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
RecipientAddress: recipientStrAddr,
|
|
})
|
|
suite.Require().NoError(err)
|
|
suite.Require().True(res.WithdrawnAllocatedFund.IsNil())
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestFundCommunityPool() {
|
|
sender := []byte("fundingAddr1____________________")
|
|
addrCodec := codectestutil.CodecOptions{}.GetAddressCodec()
|
|
senderAddr, err := addrCodec.BytesToString(sender)
|
|
suite.Require().NoError(err)
|
|
|
|
amount := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000000))
|
|
suite.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), sender, types.ModuleName, amount).Return(nil).Times(1)
|
|
|
|
_, err = suite.msgServer.FundCommunityPool(suite.ctx, &types.MsgFundCommunityPool{
|
|
Amount: amount,
|
|
Depositor: senderAddr,
|
|
})
|
|
suite.Require().NoError(err)
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestCommunityPoolSpend() {
|
|
recipient := []byte("fundingAddr1____________________")
|
|
addrCodec := codectestutil.CodecOptions{}.GetAddressCodec()
|
|
recipientAddr, err := addrCodec.BytesToString(recipient)
|
|
suite.Require().NoError(err)
|
|
|
|
amount := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000000))
|
|
suite.bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, recipient, amount).Return(nil).Times(1)
|
|
|
|
_, err = suite.msgServer.CommunityPoolSpend(suite.ctx, &types.MsgCommunityPoolSpend{
|
|
Authority: suite.poolKeeper.GetAuthority(),
|
|
Recipient: recipientAddr,
|
|
Amount: amount,
|
|
})
|
|
suite.Require().NoError(err)
|
|
}
|