mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 11:17:28 +00:00 
			
		
		
		
	* point community pool lend proposals at fee pool * update community pool lend proposal tests * remove unused begin blocker * increase test coverage * fix x/community proposal comments
		
			
				
	
	
		
			329 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package keeper_test
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						|
	"github.com/stretchr/testify/suite"
 | 
						|
	abcitypes "github.com/tendermint/tendermint/abci/types"
 | 
						|
	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
 | 
						|
	tmtime "github.com/tendermint/tendermint/types/time"
 | 
						|
 | 
						|
	"github.com/kava-labs/kava/app"
 | 
						|
	"github.com/kava-labs/kava/x/community/keeper"
 | 
						|
	"github.com/kava-labs/kava/x/community/testutil"
 | 
						|
	"github.com/kava-labs/kava/x/community/types"
 | 
						|
	hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
 | 
						|
	hardtypes "github.com/kava-labs/kava/x/hard/types"
 | 
						|
	pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
 | 
						|
)
 | 
						|
 | 
						|
const chainID = "kavatest_2221-1"
 | 
						|
 | 
						|
func ukava(amt int64) sdk.Coins {
 | 
						|
	return sdk.NewCoins(sdk.NewInt64Coin("ukava", amt))
 | 
						|
}
 | 
						|
func usdx(amt int64) sdk.Coins {
 | 
						|
	return sdk.NewCoins(sdk.NewInt64Coin("usdx", amt))
 | 
						|
}
 | 
						|
func otherdenom(amt int64) sdk.Coins {
 | 
						|
	return sdk.NewCoins(sdk.NewInt64Coin("other-denom", amt))
 | 
						|
}
 | 
						|
 | 
						|
type proposalTestSuite struct {
 | 
						|
	suite.Suite
 | 
						|
 | 
						|
	App         app.TestApp
 | 
						|
	Ctx         sdk.Context
 | 
						|
	Keeper      keeper.Keeper
 | 
						|
	MaccAddress sdk.AccAddress
 | 
						|
 | 
						|
	hardKeeper hardkeeper.Keeper
 | 
						|
}
 | 
						|
 | 
						|
func TestProposalTestSuite(t *testing.T) {
 | 
						|
	suite.Run(t, new(proposalTestSuite))
 | 
						|
}
 | 
						|
 | 
						|
func (suite *proposalTestSuite) SetupTest() {
 | 
						|
	app.SetSDKConfig()
 | 
						|
 | 
						|
	genTime := tmtime.Now()
 | 
						|
 | 
						|
	hardGS, pricefeedGS := testutil.NewLendGenesisBuilder().
 | 
						|
		WithMarket("ukava", "kava:usd", sdk.OneDec()).
 | 
						|
		WithMarket("usdx", "usdx:usd", sdk.OneDec()).
 | 
						|
		Build()
 | 
						|
 | 
						|
	tApp := app.NewTestApp()
 | 
						|
	ctx := tApp.NewContext(true, tmproto.Header{
 | 
						|
		Height:  1,
 | 
						|
		Time:    genTime,
 | 
						|
		ChainID: chainID,
 | 
						|
	})
 | 
						|
 | 
						|
	tApp.InitializeFromGenesisStatesWithTimeAndChainID(
 | 
						|
		genTime, chainID,
 | 
						|
		app.GenesisState{hardtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(&hardGS)},
 | 
						|
		app.GenesisState{pricefeedtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(&pricefeedGS)},
 | 
						|
	)
 | 
						|
 | 
						|
	suite.App = tApp
 | 
						|
	suite.Ctx = ctx
 | 
						|
	suite.Keeper = tApp.GetCommunityKeeper()
 | 
						|
	suite.MaccAddress = tApp.GetAccountKeeper().GetModuleAddress(types.ModuleAccountName)
 | 
						|
	suite.hardKeeper = suite.App.GetHardKeeper()
 | 
						|
 | 
						|
	// give the community pool some funds
 | 
						|
	// ukava
 | 
						|
	suite.FundCommunityPool(ukava(1e10))
 | 
						|
	// usdx
 | 
						|
	suite.FundCommunityPool(usdx(1e10))
 | 
						|
	// other-denom
 | 
						|
	suite.FundCommunityPool(otherdenom(1e10))
 | 
						|
}
 | 
						|
 | 
						|
func (suite *proposalTestSuite) NextBlock() {
 | 
						|
	newTime := suite.Ctx.BlockTime().Add(6 * time.Second)
 | 
						|
	newHeight := suite.Ctx.BlockHeight() + 1
 | 
						|
 | 
						|
	suite.App.EndBlocker(suite.Ctx, abcitypes.RequestEndBlock{})
 | 
						|
	suite.Ctx = suite.Ctx.WithBlockTime(newTime).WithBlockHeight(newHeight).WithChainID(chainID)
 | 
						|
	suite.App.BeginBlocker(suite.Ctx, abcitypes.RequestBeginBlock{})
 | 
						|
}
 | 
						|
 | 
						|
func (suite *proposalTestSuite) FundCommunityPool(coins sdk.Coins) {
 | 
						|
	// mint to ephemeral account
 | 
						|
	ephemeralAcc := app.RandomAddress()
 | 
						|
	suite.NoError(suite.App.FundAccount(suite.Ctx, ephemeralAcc, coins))
 | 
						|
	// fund community pool with newly minted coins
 | 
						|
	suite.NoError(suite.App.GetDistrKeeper().FundCommunityPool(suite.Ctx, coins, ephemeralAcc))
 | 
						|
}
 | 
						|
 | 
						|
func (suite *proposalTestSuite) GetCommunityPoolBalance() sdk.Coins {
 | 
						|
	balance, change := suite.App.GetDistrKeeper().GetFeePoolCommunityCoins(suite.Ctx).TruncateDecimal()
 | 
						|
	// expect no decimal dust
 | 
						|
	suite.True(sdk.NewDecCoins().IsEqual(change), "expected no decimal dust in community pool")
 | 
						|
	return balance
 | 
						|
}
 | 
						|
 | 
						|
func (suite *proposalTestSuite) CheckCommunityPoolBalance(expected sdk.Coins) {
 | 
						|
	actual := suite.GetCommunityPoolBalance()
 | 
						|
	// check that balance is expected
 | 
						|
	suite.True(expected.IsEqual(actual), fmt.Sprintf("unexpected balance in community pool\nexpected: %s\nactual: %s", expected, actual))
 | 
						|
}
 | 
						|
 | 
						|
func (suite *proposalTestSuite) TestCommunityLendDepositProposal() {
 | 
						|
	testCases := []struct {
 | 
						|
		name             string
 | 
						|
		proposals        []*types.CommunityPoolLendDepositProposal
 | 
						|
		expectedErr      string
 | 
						|
		expectedDeposits []sdk.Coins
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "valid - one proposal, one denom",
 | 
						|
			proposals: []*types.CommunityPoolLendDepositProposal{
 | 
						|
				{Amount: ukava(1e8)},
 | 
						|
			},
 | 
						|
			expectedErr:      "",
 | 
						|
			expectedDeposits: []sdk.Coins{ukava(1e8)},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid - one proposal, multiple denoms",
 | 
						|
			proposals: []*types.CommunityPoolLendDepositProposal{
 | 
						|
				{Amount: ukava(1e8).Add(usdx(1e8)...)},
 | 
						|
			},
 | 
						|
			expectedErr:      "",
 | 
						|
			expectedDeposits: []sdk.Coins{ukava(1e8).Add(usdx(1e8)...)},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid - multiple proposals, same denom",
 | 
						|
			proposals: []*types.CommunityPoolLendDepositProposal{
 | 
						|
				{Amount: ukava(1e8)},
 | 
						|
				{Amount: ukava(1e9)},
 | 
						|
			},
 | 
						|
			expectedErr:      "",
 | 
						|
			expectedDeposits: []sdk.Coins{ukava(1e8 + 1e9)},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid - multiple proposals, different denoms",
 | 
						|
			proposals: []*types.CommunityPoolLendDepositProposal{
 | 
						|
				{Amount: ukava(1e8)},
 | 
						|
				{Amount: usdx(1e8)},
 | 
						|
			},
 | 
						|
			expectedErr:      "",
 | 
						|
			expectedDeposits: []sdk.Coins{ukava(1e8).Add(usdx(1e8)...)},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid - insufficient balance",
 | 
						|
			proposals: []*types.CommunityPoolLendDepositProposal{
 | 
						|
				{
 | 
						|
					Description: "more coins than i have!",
 | 
						|
					Amount:      ukava(1e11),
 | 
						|
				},
 | 
						|
			},
 | 
						|
			expectedErr:      "community pool does not have sufficient coins to distribute",
 | 
						|
			expectedDeposits: []sdk.Coins{},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid - invalid lend deposit (unsupported denom)",
 | 
						|
			proposals: []*types.CommunityPoolLendDepositProposal{
 | 
						|
				{Amount: otherdenom(1e9)},
 | 
						|
			},
 | 
						|
			expectedErr:      "invalid deposit denom",
 | 
						|
			expectedDeposits: []sdk.Coins{},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tc := range testCases {
 | 
						|
		suite.Run(tc.name, func() {
 | 
						|
			suite.SetupTest()
 | 
						|
 | 
						|
			for _, proposal := range tc.proposals {
 | 
						|
				err := keeper.HandleCommunityPoolLendDepositProposal(suite.Ctx, suite.Keeper, proposal)
 | 
						|
				if tc.expectedErr == "" {
 | 
						|
					suite.NoError(err)
 | 
						|
				} else {
 | 
						|
					suite.ErrorContains(err, tc.expectedErr)
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			deposits := suite.hardKeeper.GetDepositsByUser(suite.Ctx, suite.MaccAddress)
 | 
						|
			suite.Len(deposits, len(tc.expectedDeposits), "expected a deposit to lend")
 | 
						|
			for _, amt := range tc.expectedDeposits {
 | 
						|
				suite.Equal(amt, deposits[0].Amount, "expected amount to match")
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (suite *proposalTestSuite) TestCommunityLendWithdrawProposal() {
 | 
						|
	testCases := []struct {
 | 
						|
		name               string
 | 
						|
		initialDeposit     sdk.Coins
 | 
						|
		proposals          []*types.CommunityPoolLendWithdrawProposal
 | 
						|
		expectedErr        string
 | 
						|
		expectedWithdrawal sdk.Coins
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			// in the week it would take a proposal to pass, the position would have grown
 | 
						|
			// to withdraw the entire position, it'd be safest to set a very high withdraw
 | 
						|
			name:           "valid - requesting withdrawal of more than total will withdraw all",
 | 
						|
			initialDeposit: ukava(1e9),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: ukava(1e12)},
 | 
						|
			},
 | 
						|
			expectedErr:        "",
 | 
						|
			expectedWithdrawal: ukava(1e9),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "valid - single proposal, single denom, full withdrawal",
 | 
						|
			initialDeposit: ukava(1e9),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: ukava(1e9)},
 | 
						|
			},
 | 
						|
			expectedErr:        "",
 | 
						|
			expectedWithdrawal: ukava(1e9),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "valid - single proposal, multiple denoms, full withdrawal",
 | 
						|
			initialDeposit: ukava(1e9).Add(usdx(1e9)...),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: ukava(1e9).Add(usdx(1e9)...)},
 | 
						|
			},
 | 
						|
			expectedErr:        "",
 | 
						|
			expectedWithdrawal: ukava(1e9).Add(usdx(1e9)...),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "valid - single proposal, partial withdrawal",
 | 
						|
			initialDeposit: ukava(1e9).Add(usdx(1e9)...),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: ukava(1e8).Add(usdx(1e9)...)},
 | 
						|
			},
 | 
						|
			expectedErr:        "",
 | 
						|
			expectedWithdrawal: ukava(1e8).Add(usdx(1e9)...),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "valid - multiple proposals, full withdrawal",
 | 
						|
			initialDeposit: ukava(1e9).Add(usdx(1e9)...),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: ukava(1e9)},
 | 
						|
				{Amount: usdx(1e9)},
 | 
						|
			},
 | 
						|
			expectedErr:        "",
 | 
						|
			expectedWithdrawal: ukava(1e9).Add(usdx(1e9)...),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "valid - multiple proposals, partial withdrawal",
 | 
						|
			initialDeposit: ukava(1e9).Add(usdx(1e9)...),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: ukava(1e8)},
 | 
						|
				{Amount: usdx(1e8)},
 | 
						|
			},
 | 
						|
			expectedErr:        "",
 | 
						|
			expectedWithdrawal: ukava(1e8).Add(usdx(1e8)...),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "invalid - nonexistent position, has no deposits",
 | 
						|
			initialDeposit: sdk.NewCoins(),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: ukava(1e8)},
 | 
						|
			},
 | 
						|
			expectedErr:        "deposit not found",
 | 
						|
			expectedWithdrawal: sdk.NewCoins(),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "invalid - nonexistent position, has deposits of different denom",
 | 
						|
			initialDeposit: ukava(1e8),
 | 
						|
			proposals: []*types.CommunityPoolLendWithdrawProposal{
 | 
						|
				{Amount: usdx(1e8)},
 | 
						|
			},
 | 
						|
			expectedErr:        "no coins of this type deposited",
 | 
						|
			expectedWithdrawal: sdk.NewCoins(),
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tc := range testCases {
 | 
						|
		suite.Run(tc.name, func() {
 | 
						|
			suite.SetupTest()
 | 
						|
 | 
						|
			// setup initial deposit
 | 
						|
			if !tc.initialDeposit.IsZero() {
 | 
						|
				deposit := types.NewCommunityPoolLendDepositProposal("initial deposit", "has coins", tc.initialDeposit)
 | 
						|
				err := keeper.HandleCommunityPoolLendDepositProposal(suite.Ctx, suite.Keeper, deposit)
 | 
						|
				suite.NoError(err, "unexpected error while seeding lend deposit")
 | 
						|
			}
 | 
						|
 | 
						|
			beforeBalance := suite.GetCommunityPoolBalance()
 | 
						|
 | 
						|
			// run the proposals
 | 
						|
			for i, proposal := range tc.proposals {
 | 
						|
				fmt.Println("submitting proposal ", i, " ", suite.Ctx.ChainID())
 | 
						|
				err := keeper.HandleCommunityPoolLendWithdrawProposal(suite.Ctx, suite.Keeper, proposal)
 | 
						|
				if tc.expectedErr == "" {
 | 
						|
					suite.NoError(err)
 | 
						|
				} else {
 | 
						|
					suite.ErrorContains(err, tc.expectedErr)
 | 
						|
				}
 | 
						|
 | 
						|
				suite.NextBlock()
 | 
						|
			}
 | 
						|
 | 
						|
			// expect funds to be removed from hard deposit
 | 
						|
			expectedRemaining := tc.initialDeposit.Sub(tc.expectedWithdrawal)
 | 
						|
			deposits := suite.hardKeeper.GetDepositsByUser(suite.Ctx, suite.MaccAddress)
 | 
						|
			if expectedRemaining.IsZero() {
 | 
						|
				suite.Len(deposits, 0, "expected all deposits to be withdrawn")
 | 
						|
			} else {
 | 
						|
				suite.Len(deposits, 1, "expected user to have remaining deposit")
 | 
						|
				suite.Equal(expectedRemaining, deposits[0].Amount)
 | 
						|
			}
 | 
						|
 | 
						|
			// expect funds to be distributed back to community pool
 | 
						|
			suite.CheckCommunityPoolBalance(beforeBalance.Add(tc.expectedWithdrawal...))
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |