diff --git a/app/app.go b/app/app.go index 1ba20a3b..9594c1ce 100644 --- a/app/app.go +++ b/app/app.go @@ -418,7 +418,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio pricefeed.NewAppModule(app.pricefeedKeeper, app.accountKeeper), bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), - incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper), + incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper, app.cdpKeeper), committee.NewAppModule(app.committeeKeeper, app.accountKeeper), issuance.NewAppModule(app.issuanceKeeper, app.accountKeeper, app.supplyKeeper), hard.NewAppModule(app.hardKeeper, app.supplyKeeper, app.pricefeedKeeper), @@ -471,7 +471,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio auction.NewAppModule(app.auctionKeeper, app.accountKeeper, app.supplyKeeper), bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), - incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper), + incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper, app.cdpKeeper), committee.NewAppModule(app.committeeKeeper, app.accountKeeper), issuance.NewAppModule(app.issuanceKeeper, app.accountKeeper, app.supplyKeeper), hard.NewAppModule(app.hardKeeper, app.supplyKeeper, app.pricefeedKeeper), diff --git a/x/incentive/genesis.go b/x/incentive/genesis.go index 91bb905c..888cfdf0 100644 --- a/x/incentive/genesis.go +++ b/x/incentive/genesis.go @@ -10,7 +10,7 @@ import ( ) // InitGenesis initializes the store state from a genesis state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeeper, gs types.GenesisState) { +func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeeper, cdpKeeper types.CdpKeeper, gs types.GenesisState) { // check if the module account exists moduleAcc := supplyKeeper.GetModuleAccount(ctx, types.IncentiveMacc) @@ -22,6 +22,13 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err)) } + for _, rp := range gs.Params.RewardPeriods { + _, found := cdpKeeper.GetCollateral(ctx, rp.CollateralType) + if !found { + panic(fmt.Sprintf("usdx minting collateral type %s not found in cdp collateral types", rp.CollateralType)) + } + } + k.SetParams(ctx, gs.Params) for _, gat := range gs.PreviousAccumulationTimes { diff --git a/x/incentive/handler_test.go b/x/incentive/handler_test.go index 4f872099..7af7e2c3 100644 --- a/x/incentive/handler_test.go +++ b/x/incentive/handler_test.go @@ -51,7 +51,7 @@ func (suite *HandlerTestSuite) SetupTest() { incentive.DefaultGenesisAccumulationTimes, incentive.DefaultClaims, ) - tApp.InitializeFromGenesisStates(authGS, app.GenesisState{incentive.ModuleName: incentive.ModuleCdc.MustMarshalJSON(incentiveGS)}) + tApp.InitializeFromGenesisStates(authGS, app.GenesisState{incentive.ModuleName: incentive.ModuleCdc.MustMarshalJSON(incentiveGS)}, NewCDPGenStateMulti(), NewPricefeedGenStateMulti()) suite.addrs = addrs suite.handler = incentive.NewHandler(keeper) diff --git a/x/incentive/integration_test.go b/x/incentive/integration_test.go new file mode 100644 index 00000000..a31e4c30 --- /dev/null +++ b/x/incentive/integration_test.go @@ -0,0 +1,212 @@ +package incentive_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/cdp" + "github.com/kava-labs/kava/x/incentive" + "github.com/kava-labs/kava/x/pricefeed" +) + +func NewCDPGenStateMulti() app.GenesisState { + cdpGenesis := cdp.GenesisState{ + Params: cdp.Params{ + GlobalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + SurplusAuctionThreshold: cdp.DefaultSurplusThreshold, + SurplusAuctionLot: cdp.DefaultSurplusLot, + DebtAuctionThreshold: cdp.DefaultDebtThreshold, + DebtAuctionLot: cdp.DefaultDebtLot, + CollateralParams: cdp.CollateralParams{ + { + Denom: "xrp", + Type: "xrp-a", + LiquidationRatio: sdk.MustNewDecFromStr("2.0"), + DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr + LiquidationPenalty: d("0.05"), + AuctionSize: i(7000000000), + Prefix: 0x20, + SpotMarketID: "xrp:usd", + LiquidationMarketID: "xrp:usd", + ConversionFactor: i(6), + }, + { + Denom: "btc", + Type: "btc-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000000782997609"), // %2.5 apr + LiquidationPenalty: d("0.025"), + AuctionSize: i(10000000), + Prefix: 0x21, + SpotMarketID: "btc:usd", + LiquidationMarketID: "btc:usd", + ConversionFactor: i(8), + }, + { + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000001547125958"), // %5 apr + LiquidationPenalty: d("0.05"), + AuctionSize: i(50000000000), + Prefix: 0x22, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + ConversionFactor: i(8), + }, + { + Denom: "busd", + Type: "busd-a", + LiquidationRatio: d("1.01"), + DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), + StabilityFee: sdk.OneDec(), // %0 apr + LiquidationPenalty: d("0.05"), + AuctionSize: i(10000000000), + Prefix: 0x23, + SpotMarketID: "busd:usd", + LiquidationMarketID: "busd:usd", + ConversionFactor: i(8), + }, + }, + DebtParam: cdp.DebtParam{ + Denom: "usdx", + ReferenceAsset: "usd", + ConversionFactor: i(6), + DebtFloor: i(10000000), + }, + }, + StartingCdpID: cdp.DefaultCdpStartingID, + DebtDenom: cdp.DefaultDebtDenom, + GovDenom: cdp.DefaultGovDenom, + CDPs: cdp.CDPs{}, + PreviousAccumulationTimes: cdp.GenesisAccumulationTimes{ + cdp.NewGenesisAccumulationTime("btc-a", time.Time{}, sdk.OneDec()), + cdp.NewGenesisAccumulationTime("xrp-a", time.Time{}, sdk.OneDec()), + cdp.NewGenesisAccumulationTime("busd-a", time.Time{}, sdk.OneDec()), + cdp.NewGenesisAccumulationTime("bnb-a", time.Time{}, sdk.OneDec()), + }, + TotalPrincipals: cdp.GenesisTotalPrincipals{ + cdp.NewGenesisTotalPrincipal("btc-a", sdk.ZeroInt()), + cdp.NewGenesisTotalPrincipal("xrp-a", sdk.ZeroInt()), + cdp.NewGenesisTotalPrincipal("busd-a", sdk.ZeroInt()), + cdp.NewGenesisTotalPrincipal("bnb-a", sdk.ZeroInt()), + }, + } + return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)} +} + +func NewPricefeedGenStateMulti() app.GenesisState { + pfGenesis := pricefeed.GenesisState{ + Params: pricefeed.Params{ + Markets: []pricefeed.Market{ + {MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true}, + {MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true}, + {MarketID: "bnb:usd", BaseAsset: "bnb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true}, + {MarketID: "busd:usd", BaseAsset: "busd", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true}, + }, + }, + PostedPrices: []pricefeed.PostedPrice{ + { + MarketID: "btc:usd", + OracleAddress: sdk.AccAddress{}, + Price: sdk.MustNewDecFromStr("8000.00"), + Expiry: time.Now().Add(1 * time.Hour), + }, + { + MarketID: "xrp:usd", + OracleAddress: sdk.AccAddress{}, + Price: sdk.MustNewDecFromStr("0.25"), + Expiry: time.Now().Add(1 * time.Hour), + }, + { + MarketID: "bnb:usd", + OracleAddress: sdk.AccAddress{}, + Price: sdk.MustNewDecFromStr("17.25"), + Expiry: time.Now().Add(1 * time.Hour), + }, + { + MarketID: "busd:usd", + OracleAddress: sdk.AccAddress{}, + Price: sdk.OneDec(), + Expiry: time.Now().Add(1 * time.Hour), + }, + }, + } + return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)} +} + +func NewIncentiveGenState(previousAccumTime, endTime time.Time, rewardPeriods ...incentive.RewardPeriod) app.GenesisState { + var accumulationTimes incentive.GenesisAccumulationTimes + for _, rp := range rewardPeriods { + accumulationTimes = append( + accumulationTimes, + incentive.NewGenesisAccumulationTime( + rp.CollateralType, + previousAccumTime, + sdk.ZeroDec(), + ), + ) + } + genesis := incentive.NewGenesisState( + incentive.NewParams( + rewardPeriods, + incentive.Multipliers{ + incentive.NewMultiplier(incentive.Small, 1, d("0.25")), + incentive.NewMultiplier(incentive.Large, 12, d("1.0")), + }, + endTime, + ), + accumulationTimes, + incentive.USDXMintingClaims{}, + ) + return app.GenesisState{incentive.ModuleName: incentive.ModuleCdc.MustMarshalJSON(genesis)} +} + +func NewCDPGenStateHighInterest() app.GenesisState { + cdpGenesis := cdp.GenesisState{ + Params: cdp.Params{ + GlobalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000), + SurplusAuctionThreshold: cdp.DefaultSurplusThreshold, + SurplusAuctionLot: cdp.DefaultSurplusLot, + DebtAuctionThreshold: cdp.DefaultDebtThreshold, + DebtAuctionLot: cdp.DefaultDebtLot, + CollateralParams: cdp.CollateralParams{ + { + Denom: "bnb", + Type: "bnb-a", + LiquidationRatio: sdk.MustNewDecFromStr("1.5"), + DebtLimit: sdk.NewInt64Coin("usdx", 500000000000), + StabilityFee: sdk.MustNewDecFromStr("1.000000051034942716"), // 500% APR + LiquidationPenalty: d("0.05"), + AuctionSize: i(50000000000), + Prefix: 0x22, + SpotMarketID: "bnb:usd", + LiquidationMarketID: "bnb:usd", + ConversionFactor: i(8), + }, + }, + DebtParam: cdp.DebtParam{ + Denom: "usdx", + ReferenceAsset: "usd", + ConversionFactor: i(6), + DebtFloor: i(10000000), + }, + }, + StartingCdpID: cdp.DefaultCdpStartingID, + DebtDenom: cdp.DefaultDebtDenom, + GovDenom: cdp.DefaultGovDenom, + CDPs: cdp.CDPs{}, + PreviousAccumulationTimes: cdp.GenesisAccumulationTimes{ + cdp.NewGenesisAccumulationTime("bnb-a", time.Time{}, sdk.OneDec()), + }, + TotalPrincipals: cdp.GenesisTotalPrincipals{ + cdp.NewGenesisTotalPrincipal("bnb-a", sdk.ZeroInt()), + }, + } + return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)} +} diff --git a/x/incentive/module.go b/x/incentive/module.go index 79548a24..374dd39e 100644 --- a/x/incentive/module.go +++ b/x/incentive/module.go @@ -105,15 +105,17 @@ type AppModule struct { keeper Keeper accountKeeper auth.AccountKeeper supplyKeeper SupplyKeeper + cdpKeeper CdpKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, accountKeeper auth.AccountKeeper, supplyKeeper SupplyKeeper) AppModule { +func NewAppModule(keeper Keeper, accountKeeper auth.AccountKeeper, supplyKeeper SupplyKeeper, cdpKeeper CdpKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: keeper, accountKeeper: accountKeeper, supplyKeeper: supplyKeeper, + cdpKeeper: cdpKeeper, } } @@ -149,7 +151,7 @@ func (am AppModule) NewQuerierHandler() sdk.Querier { func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var gs types.GenesisState types.ModuleCdc.MustUnmarshalJSON(data, &gs) - InitGenesis(ctx, am.keeper, am.supplyKeeper, gs) + InitGenesis(ctx, am.keeper, am.supplyKeeper, am.cdpKeeper, gs) return []abci.ValidatorUpdate{} } diff --git a/x/incentive/types/expected_keepers.go b/x/incentive/types/expected_keepers.go index abc43673..e4e8e8a0 100644 --- a/x/incentive/types/expected_keepers.go +++ b/x/incentive/types/expected_keepers.go @@ -20,6 +20,7 @@ type CdpKeeper interface { GetTotalPrincipal(ctx sdk.Context, collateralType string, principalDenom string) (total sdk.Int) GetCdpByOwnerAndCollateralType(ctx sdk.Context, owner sdk.AccAddress, collateralType string) (cdptypes.CDP, bool) GetInterestFactor(ctx sdk.Context, collateralType string) (sdk.Dec, bool) + GetCollateral(ctx sdk.Context, collateralType string) (cdptypes.CollateralParam, bool) } // AccountKeeper defines the expected keeper interface for interacting with account