Add x/community Lend proposals (#1425)

* implement & register x/community lend proposals

* register proposals in x/community codec

* allow x/community macc to receive funds

* init lend from genesis in proposal tests

* test CommunityLendWithdrawProposal

* helpful comment on x/community keeper deps

* use RouteKey in module.go
This commit is contained in:
Robert Pirtle 2022-12-12 16:38:27 -08:00 committed by GitHub
parent a7f3b7732b
commit 7c58fb5303
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 474 additions and 28 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@
# Output of the go coverage tool # Output of the go coverage tool
*.out *.out
cover.html
# Exclude build files # Exclude build files
vendor vendor

View File

@ -508,20 +508,6 @@ func NewApp(
app.evmutilKeeper.SetEvmKeeper(app.evmKeeper) app.evmutilKeeper.SetEvmKeeper(app.evmKeeper)
app.communityKeeper = communitykeeper.NewKeeper(
app.accountKeeper,
app.bankKeeper,
)
app.kavadistKeeper = kavadistkeeper.NewKeeper(
appCodec,
keys[kavadisttypes.StoreKey],
kavadistSubspace,
app.bankKeeper,
app.accountKeeper,
app.communityKeeper,
app.loadBlockedMaccAddrs(),
)
app.transferKeeper = ibctransferkeeper.NewKeeper( app.transferKeeper = ibctransferkeeper.NewKeeper(
appCodec, appCodec,
keys[ibctransfertypes.StoreKey], keys[ibctransfertypes.StoreKey],
@ -620,6 +606,23 @@ func NewApp(
&savingsKeeper, &savingsKeeper,
communitytypes.ModuleAccountName, communitytypes.ModuleAccountName,
) )
// x/community's deposit/withdraw to lend proposals depend on hard keeper.
app.communityKeeper = communitykeeper.NewKeeper(
app.accountKeeper,
app.bankKeeper,
hardKeeper,
)
app.kavadistKeeper = kavadistkeeper.NewKeeper(
appCodec,
keys[kavadisttypes.StoreKey],
kavadistSubspace,
app.bankKeeper,
app.accountKeeper,
app.communityKeeper,
app.loadBlockedMaccAddrs(),
)
app.kavamintKeeper = kavamintkeeper.NewKeeper( app.kavamintKeeper = kavamintkeeper.NewKeeper(
appCodec, appCodec,
keys[kavaminttypes.StoreKey], keys[kavaminttypes.StoreKey],
@ -694,6 +697,7 @@ func NewApp(
AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)). AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)).
AddRoute(kavadisttypes.RouterKey, kavadist.NewCommunityPoolMultiSpendProposalHandler(app.kavadistKeeper)). AddRoute(kavadisttypes.RouterKey, kavadist.NewCommunityPoolMultiSpendProposalHandler(app.kavadistKeeper)).
AddRoute(earntypes.RouterKey, earn.NewCommunityPoolProposalHandler(app.earnKeeper)). AddRoute(earntypes.RouterKey, earn.NewCommunityPoolProposalHandler(app.earnKeeper)).
AddRoute(communitytypes.RouterKey, community.NewCommunityPoolProposalHandler(app.communityKeeper)).
AddRoute(committeetypes.RouterKey, committee.NewProposalHandler(app.committeeKeeper)) AddRoute(committeetypes.RouterKey, committee.NewProposalHandler(app.committeeKeeper))
app.govKeeper = govkeeper.NewKeeper( app.govKeeper = govkeeper.NewKeeper(
appCodec, appCodec,
@ -1046,14 +1050,22 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) {
// loadBlockedMaccAddrs returns a map indicating the blocked status of each module account address // loadBlockedMaccAddrs returns a map indicating the blocked status of each module account address
func (app *App) loadBlockedMaccAddrs() map[string]bool { func (app *App) loadBlockedMaccAddrs() map[string]bool {
modAccAddrs := app.ModuleAccountAddrs() modAccAddrs := app.ModuleAccountAddrs()
kavadistMaccAddr := app.accountKeeper.GetModuleAddress(kavadisttypes.ModuleName) allowedMaccs := map[string]bool{
earnMaccAddr := app.accountKeeper.GetModuleAddress(earntypes.ModuleName) // kavadist
liquidMaccAddr := app.accountKeeper.GetModuleAddress(liquidtypes.ModuleName) app.accountKeeper.GetModuleAddress(kavadisttypes.ModuleName).String(): true,
kavadistFundMaccAddr := app.accountKeeper.GetModuleAddress(kavadisttypes.FundModuleAccount) // earn
app.accountKeeper.GetModuleAddress(earntypes.ModuleName).String(): true,
// liquid
app.accountKeeper.GetModuleAddress(liquidtypes.ModuleName).String(): true,
// kavadist fund
app.accountKeeper.GetModuleAddress(kavadisttypes.FundModuleAccount).String(): true,
// community
app.accountKeeper.GetModuleAddress(communitytypes.ModuleAccountName).String(): true,
}
for addr := range modAccAddrs { for addr := range modAccAddrs {
// Set the kavadist and earn module account address as unblocked // Set allowed module accounts as unblocked
if addr == kavadistMaccAddr.String() || addr == earnMaccAddr.String() || addr == liquidMaccAddr.String() || addr == kavadistFundMaccAddr.String() { if allowedMaccs[addr] {
modAccAddrs[addr] = false modAccAddrs[addr] = false
} }
} }

View File

@ -8,7 +8,6 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/kava-labs/kava/x/community" "github.com/kava-labs/kava/x/community"
"github.com/kava-labs/kava/x/community/testutil" "github.com/kava-labs/kava/x/community/testutil"
"github.com/kava-labs/kava/x/community/types"
) )
type genesisTestSuite struct { type genesisTestSuite struct {
@ -32,12 +31,8 @@ func (suite *genesisTestSuite) TestInitGenesis() {
community.InitGenesis(suite.Ctx, suite.Keeper, accountKeeper) community.InitGenesis(suite.Ctx, suite.Keeper, accountKeeper)
}) })
communityPoolAddress := accountKeeper.GetModuleAddress(types.ModuleAccountName)
// hello, greppers!
suite.Equal("kava17d2wax0zhjrrecvaszuyxdf5wcu5a0p4qlx3t5", communityPoolAddress.String())
// check for module account this way b/c GetModuleAccount creates if not existing. // check for module account this way b/c GetModuleAccount creates if not existing.
acc := accountKeeper.GetAccount(suite.Ctx, communityPoolAddress) acc := accountKeeper.GetAccount(suite.Ctx, suite.MaccAddress)
suite.NotNil(acc) suite.NotNil(acc)
_, ok := acc.(authtypes.ModuleAccountI) _, ok := acc.(authtypes.ModuleAccountI)
suite.True(ok) suite.True(ok)

24
x/community/handler.go Normal file
View File

@ -0,0 +1,24 @@
package community
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/kava-labs/kava/x/community/keeper"
"github.com/kava-labs/kava/x/community/types"
)
// NewCommunityPoolProposalHandler handles x/community proposals.
func NewCommunityPoolProposalHandler(k keeper.Keeper) govtypes.Handler {
return func(ctx sdk.Context, content govtypes.Content) error {
switch c := content.(type) {
case *types.CommunityPoolLendDepositProposal:
return keeper.HandleCommunityPoolLendDepositProposal(ctx, k, c)
case *types.CommunityPoolLendWithdrawProposal:
return keeper.HandleCommunityPoolLendWithdrawProposal(ctx, k, c)
default:
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized community proposal content type: %T", c)
}
}
}

View File

@ -12,11 +12,12 @@ import (
// Keeper of the community store // Keeper of the community store
type Keeper struct { type Keeper struct {
bankKeeper types.BankKeeper bankKeeper types.BankKeeper
hardKeeper types.HardKeeper
moduleAddress sdk.AccAddress moduleAddress sdk.AccAddress
} }
// NewKeeper creates a new community Keeper instance // NewKeeper creates a new community Keeper instance
func NewKeeper(ak types.AccountKeeper, bk types.BankKeeper) Keeper { func NewKeeper(ak types.AccountKeeper, bk types.BankKeeper, hk types.HardKeeper) Keeper {
// ensure community module account is set // ensure community module account is set
addr := ak.GetModuleAddress(types.ModuleAccountName) addr := ak.GetModuleAddress(types.ModuleAccountName)
if addr == nil { if addr == nil {
@ -25,6 +26,7 @@ func NewKeeper(ak types.AccountKeeper, bk types.BankKeeper) Keeper {
return Keeper{ return Keeper{
bankKeeper: bk, bankKeeper: bk,
hardKeeper: hk,
moduleAddress: addr, moduleAddress: addr,
} }
} }

View File

@ -0,0 +1,17 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/community/types"
)
// HandleCommunityPoolLendDepositProposal is a handler for executing a passed community pool lend deposit proposal.
func HandleCommunityPoolLendDepositProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolLendDepositProposal) error {
return k.hardKeeper.Deposit(ctx, k.moduleAddress, p.Amount)
}
// HandleCommunityPoolLendWithdrawProposal is a handler for executing a passed community pool lend withdraw proposal.
func HandleCommunityPoolLendWithdrawProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolLendWithdrawProposal) error {
return k.hardKeeper.Withdraw(ctx, k.moduleAddress, p.Amount)
}

View File

@ -0,0 +1,312 @@
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
err := suite.App.FundModuleAccount(suite.Ctx, types.ModuleAccountName, ukava(1e10))
suite.NoError(err)
// usdx
err = suite.App.FundModuleAccount(suite.Ctx, types.ModuleAccountName, usdx(1e10))
suite.NoError(err)
// other-denom
err = suite.App.FundModuleAccount(suite.Ctx, types.ModuleAccountName, otherdenom(1e10))
suite.NoError(err)
}
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) 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: "insufficient funds",
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.Keeper.GetModuleAccountBalance(suite.Ctx)
// 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.App.CheckBalance(suite.T(), suite.Ctx, suite.MaccAddress, beforeBalance.Add(tc.expectedWithdrawal...))
})
}
}

View File

@ -109,7 +109,7 @@ func (am AppModule) Route() sdk.Route { return sdk.Route{} }
// QuerierRoute module querier route name // QuerierRoute module querier route name
// Deprecated: unused but necessary to fulfill AppModule interface // Deprecated: unused but necessary to fulfill AppModule interface
func (AppModule) QuerierRoute() string { return "" } func (AppModule) QuerierRoute() string { return types.RouterKey }
// LegacyQuerierHandler returns no sdk.Querier. // LegacyQuerierHandler returns no sdk.Querier.
// Deprecated: unused but necessary to fulfill AppModule interface // Deprecated: unused but necessary to fulfill AppModule interface

View File

@ -9,6 +9,7 @@ import (
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/community/keeper" "github.com/kava-labs/kava/x/community/keeper"
"github.com/kava-labs/kava/x/community/types"
) )
// Test suite used for all community tests // Test suite used for all community tests
@ -17,6 +18,8 @@ type Suite struct {
App app.TestApp App app.TestApp
Ctx sdk.Context Ctx sdk.Context
Keeper keeper.Keeper Keeper keeper.Keeper
MaccAddress sdk.AccAddress
} }
// The default state used by each test // The default state used by each test
@ -30,6 +33,10 @@ func (suite *Suite) SetupTest() {
suite.App = tApp suite.App = tApp
suite.Ctx = ctx suite.Ctx = ctx
suite.Keeper = tApp.GetCommunityKeeper() suite.Keeper = tApp.GetCommunityKeeper()
communityPoolAddress := tApp.GetAccountKeeper().GetModuleAddress(types.ModuleAccountName)
// hello, greppers!
suite.Equal("kava17d2wax0zhjrrecvaszuyxdf5wcu5a0p4qlx3t5", communityPoolAddress.String())
suite.MaccAddress = communityPoolAddress
} }
// CreateFundedAccount creates a random account and mints `coins` to it. // CreateFundedAccount creates a random account and mints `coins` to it.

View File

@ -0,0 +1,60 @@
package testutil
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
hardtypes "github.com/kava-labs/kava/x/hard/types"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
)
// lendGenesisBuilder builds the Hard and Pricefeed genesis states for setting up Kava Lend
type lendGenesisBuilder struct {
hardMarkets []hardtypes.MoneyMarket
pfMarkets []pricefeedtypes.Market
prices []pricefeedtypes.PostedPrice
}
func NewLendGenesisBuilder() lendGenesisBuilder {
return lendGenesisBuilder{}
}
func (b lendGenesisBuilder) Build() (hardtypes.GenesisState, pricefeedtypes.GenesisState) {
hardGS := hardtypes.DefaultGenesisState()
hardGS.Params.MoneyMarkets = b.hardMarkets
pricefeedGS := pricefeedtypes.DefaultGenesisState()
pricefeedGS.Params.Markets = b.pfMarkets
pricefeedGS.PostedPrices = b.prices
return hardGS, pricefeedGS
}
func (b lendGenesisBuilder) WithMarket(denom, spotMarketId string, price sdk.Dec) lendGenesisBuilder {
// add hard money market
b.hardMarkets = append(b.hardMarkets,
hardtypes.NewMoneyMarket(
denom,
hardtypes.NewBorrowLimit(false, sdk.NewDec(1e15), sdk.MustNewDecFromStr("0.6")),
spotMarketId,
sdk.NewInt(1e6),
hardtypes.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")),
sdk.MustNewDecFromStr("0.05"),
sdk.ZeroDec(),
),
)
// add pricefeed
b.pfMarkets = append(b.pfMarkets,
pricefeedtypes.Market{MarketID: spotMarketId, BaseAsset: denom, QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
)
b.prices = append(b.prices,
pricefeedtypes.PostedPrice{
MarketID: spotMarketId,
OracleAddress: sdk.AccAddress{},
Price: price,
Expiry: time.Now().Add(100 * time.Hour),
},
)
return b
}

View File

@ -6,6 +6,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/types/msgservice"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
@ -13,6 +14,8 @@ import (
// RegisterLegacyAminoCodec registers all the necessary types and interfaces for the module. // RegisterLegacyAminoCodec registers all the necessary types and interfaces for the module.
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgFundCommunityPool{}, "community/MsgFundCommunityPool", nil) cdc.RegisterConcrete(&MsgFundCommunityPool{}, "community/MsgFundCommunityPool", nil)
cdc.RegisterConcrete(&CommunityPoolLendDepositProposal{}, "kava/CommunityPoolLendDepositProposal", nil)
cdc.RegisterConcrete(&CommunityPoolLendWithdrawProposal{}, "kava/CommunityPoolLendWithdrawProposal", nil)
} }
// RegisterInterfaces registers proto messages under their interfaces for unmarshalling, // RegisterInterfaces registers proto messages under their interfaces for unmarshalling,
@ -21,6 +24,10 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil), registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgFundCommunityPool{}, &MsgFundCommunityPool{},
) )
registry.RegisterImplementations((*govtypes.Content)(nil),
&CommunityPoolLendDepositProposal{},
&CommunityPoolLendWithdrawProposal{},
)
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
} }

View File

@ -18,3 +18,9 @@ type BankKeeper interface {
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
} }
// HardKeeper defines the contract needed to be fulfilled for Kava Lend dependencies.
type HardKeeper interface {
Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
}

View File

@ -7,6 +7,9 @@ const (
// ModuleAccountName is the name of the module's account // ModuleAccountName is the name of the module's account
ModuleAccountName = ModuleName ModuleAccountName = ModuleName
// RouterKey is the top-level router key for the module
RouterKey = ModuleName
// Query endpoints supported by community // Query endpoints supported by community
QueryBalance = "balance" QueryBalance = "balance"
) )