From 1ab2e9965f800f1c2153d45d7ec4723248b5b7bc Mon Sep 17 00:00:00 2001 From: Kevin Davis Date: Fri, 19 Feb 2021 14:05:42 -0700 Subject: [PATCH] Add committee migrations (#818) * fix: collateral param permission * wip: kava-5 committee updates * feat: add committee migrations * rename functions so git recognizes diffs better * rename * remove auction_size param * address review comments * add check ltv index count to collateral param perms * fix: update legacy migration * use function to define collateral param in test * use go 1.15 in circle * address review comments * fix: no nil values for collateral params --- migrate/v0_11/migrate.go | 2 +- migrate/v0_13/migrate.go | 125 ++++++++++- migrate/v0_13/migrate_test.go | 40 +++- .../testdata/kava-4-committee-state.json | 1 + x/committee/alias.go | 3 + x/committee/legacy/v0_11/types.go | 51 ++++- x/committee/legacy/v0_13/types.go | 88 +++++++- x/committee/types/param_permissions_test.go | 118 ++++------ x/committee/types/permissions.go | 206 +++++++++++++++--- 9 files changed, 511 insertions(+), 123 deletions(-) create mode 100644 migrate/v0_13/testdata/kava-4-committee-state.json diff --git a/migrate/v0_11/migrate.go b/migrate/v0_11/migrate.go index 7000a3c8..78077b80 100644 --- a/migrate/v0_11/migrate.go +++ b/migrate/v0_11/migrate.go @@ -24,7 +24,7 @@ import ( v0_9bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9" v0_11cdp "github.com/kava-labs/kava/x/cdp/legacy/v0_11" v0_9cdp "github.com/kava-labs/kava/x/cdp/legacy/v0_9" - v0_11committee "github.com/kava-labs/kava/x/committee" + v0_11committee "github.com/kava-labs/kava/x/committee/legacy/v0_11" v0_9committee "github.com/kava-labs/kava/x/committee/legacy/v0_9" v0_11harvest "github.com/kava-labs/kava/x/hard/legacy/v0_11" v0_11incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_11" diff --git a/migrate/v0_13/migrate.go b/migrate/v0_13/migrate.go index de697118..7d478bc8 100644 --- a/migrate/v0_13/migrate.go +++ b/migrate/v0_13/migrate.go @@ -11,10 +11,12 @@ import ( v0_13cdp "github.com/kava-labs/kava/x/cdp" v0_11cdp "github.com/kava-labs/kava/x/cdp/legacy/v0_11" + v0_13committee "github.com/kava-labs/kava/x/committee" + v0_11committee "github.com/kava-labs/kava/x/committee/legacy/v0_11" ) -// MigrateCDP migrates from a v0.11 cdp genesis state to a v0.13 cdp genesis state -func MigrateCDP(oldGenState v0_11cdp.GenesisState) v0_13cdp.GenesisState { +// CDP migrates from a v0.11 cdp genesis state to a v0.13 cdp genesis state +func CDP(oldGenState v0_11cdp.GenesisState) v0_13cdp.GenesisState { var newCDPs v0_13cdp.CDPs var newDeposits v0_13cdp.Deposits var newCollateralParams v0_13cdp.CollateralParams @@ -78,8 +80,8 @@ func MigrateCDP(oldGenState v0_11cdp.GenesisState) v0_13cdp.GenesisState { ) } -// MigrateAuth migrates from a v0.11 auth genesis state to a v0.13 -func MigrateAuth(genesisState auth.GenesisState) auth.GenesisState { +// Auth migrates from a v0.11 auth genesis state to a v0.13 +func Auth(genesisState auth.GenesisState) auth.GenesisState { savingsRateMaccCoins := sdk.NewCoins() savingsMaccAddr := supply.NewModuleAddress(v0_11cdp.SavingsRateMacc) savingsRateMaccIndex := 0 @@ -108,6 +110,121 @@ func MigrateAuth(genesisState auth.GenesisState) auth.GenesisState { return genesisState } +// Committee migrates from a v0.11 (or v0.12) committee genesis state to a v0.13 committee genesis stat +func Committee(genesisState v0_11committee.GenesisState) v0_13committee.GenesisState { + committees := []v0_13committee.Committee{} + votes := []v0_13committee.Vote{} + proposals := []v0_13committee.Proposal{} + + var newStabilityCommittee v0_13committee.Committee + var newSafetyCommittee v0_13committee.Committee + + for _, com := range genesisState.Committees { + if com.ID == 1 { + newStabilityCommittee.Description = com.Description + newStabilityCommittee.ID = com.ID + newStabilityCommittee.Members = com.Members + newStabilityCommittee.VoteThreshold = com.VoteThreshold + newStabilityCommittee.ProposalDuration = com.ProposalDuration + var newStabilityCommitteePermissions []v0_13committee.Permission + var newStabilitySubParamPermissions v0_13committee.SubParamChangePermission + + for _, perm := range com.Permissions { + subPerm, ok := perm.(v0_11committee.SubParamChangePermission) + if ok { + // update AllowedParams + var newAllowedParams v0_13committee.AllowedParams + for _, ap := range subPerm.AllowedParams { + newAP := v0_13committee.AllowedParam(ap) + newAllowedParams = append(newAllowedParams, newAP) + } + newStabilitySubParamPermissions.AllowedParams = newAllowedParams + + // update AllowedCollateralParams + var newCollateralParams v0_13committee.AllowedCollateralParams + for _, cp := range subPerm.AllowedCollateralParams { + newCP := v0_13committee.NewAllowedCollateralParam( + cp.Type, + cp.Denom, + cp.LiquidationRatio, + cp.DebtLimit, + cp.StabilityFee, + cp.AuctionSize, + cp.LiquidationPenalty, + cp.Prefix, + cp.SpotMarketID, + cp.LiquidationMarketID, + cp.ConversionFactor, + true, + true, + ) + newCollateralParams = append(newCollateralParams, newCP) + } + newStabilitySubParamPermissions.AllowedCollateralParams = newCollateralParams + + // update AllowedDebtParam + newDP := v0_13committee.AllowedDebtParam{ + Denom: subPerm.AllowedDebtParam.Denom, + ReferenceAsset: subPerm.AllowedDebtParam.ReferenceAsset, + ConversionFactor: subPerm.AllowedDebtParam.ConversionFactor, + DebtFloor: subPerm.AllowedDebtParam.DebtFloor, + } + newStabilitySubParamPermissions.AllowedDebtParam = newDP + + // update AllowedAssetParams + var newAssetParams v0_13committee.AllowedAssetParams + for _, ap := range subPerm.AllowedAssetParams { + newAP := v0_13committee.AllowedAssetParam(ap) + newAssetParams = append(newAssetParams, newAP) + } + newStabilitySubParamPermissions.AllowedAssetParams = newAssetParams + + // Update Allowed Markets + var newMarketParams v0_13committee.AllowedMarkets + for _, mp := range subPerm.AllowedMarkets { + newMP := v0_13committee.AllowedMarket(mp) + newMarketParams = append(newMarketParams, newMP) + } + newStabilitySubParamPermissions.AllowedMarkets = newMarketParams + + // Add hard money market committee permissions + var newMoneyMarketParams v0_13committee.AllowedMoneyMarkets + hardMMDenoms := []string{"bnb", "busd", "btcb", "xrpb", "usdx", "kava", "hard"} + for _, mmDenom := range hardMMDenoms { + newMoneyMarketParam := v0_13committee.NewAllowedMoneyMarket(mmDenom, true, false, false, true, true, true) + newMoneyMarketParams = append(newMoneyMarketParams, newMoneyMarketParam) + } + newStabilitySubParamPermissions.AllowedMoneyMarkets = newMoneyMarketParams + newStabilityCommitteePermissions = append(newStabilityCommitteePermissions, newStabilitySubParamPermissions) + } + } + newStabilityCommitteePermissions = append(newStabilityCommitteePermissions, v0_13committee.TextPermission{}) + newStabilityCommittee.Permissions = newStabilityCommitteePermissions + committees = append(committees, newStabilityCommittee) + } else { + newSafetyCommittee.ID = com.ID + newSafetyCommittee.Description = com.Description + newSafetyCommittee.Members = com.Members + newSafetyCommittee.Permissions = []v0_13committee.Permission{v0_13committee.SoftwareUpgradePermission{}} + newSafetyCommittee.VoteThreshold = com.VoteThreshold + newSafetyCommittee.ProposalDuration = com.ProposalDuration + committees = append(committees, newSafetyCommittee) + } + } + + for _, v := range genesisState.Votes { + votes = append(votes, v0_13committee.Vote(v)) + } + + for _, p := range genesisState.Proposals { + newPubProp := v0_13committee.PubProposal(p.PubProposal) + newProp := v0_13committee.NewProposal(newPubProp, p.ID, p.CommitteeID, p.Deadline) + proposals = append(proposals, newProp) + } + return v0_13committee.NewGenesisState( + genesisState.NextProposalID, committees, proposals, votes) +} + func removeIndex(accs authexported.GenesisAccounts, index int) authexported.GenesisAccounts { ret := make(authexported.GenesisAccounts, 0) ret = append(ret, accs[:index]...) diff --git a/migrate/v0_13/migrate_test.go b/migrate/v0_13/migrate_test.go index cf5e0f7f..002ad3ba 100644 --- a/migrate/v0_13/migrate_test.go +++ b/migrate/v0_13/migrate_test.go @@ -6,11 +6,14 @@ import ( "path/filepath" "testing" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/kava-labs/kava/app" v0_11cdp "github.com/kava-labs/kava/x/cdp/legacy/v0_11" + v0_13committee "github.com/kava-labs/kava/x/committee" + v0_11committee "github.com/kava-labs/kava/x/committee/legacy/v0_11" "github.com/stretchr/testify/require" ) @@ -23,7 +26,7 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestMigrateCdp(t *testing.T) { +func TestCDP(t *testing.T) { bz, err := ioutil.ReadFile(filepath.Join("testdata", "kava-4-cdp-state-block-500000.json")) require.NoError(t, err) var oldGenState v0_11cdp.GenesisState @@ -32,7 +35,7 @@ func TestMigrateCdp(t *testing.T) { cdc.MustUnmarshalJSON(bz, &oldGenState) }) - newGenState := MigrateCDP(oldGenState) + newGenState := CDP(oldGenState) err = newGenState.Validate() require.NoError(t, err) @@ -43,7 +46,7 @@ func TestMigrateCdp(t *testing.T) { } -func TestMigrateAuth(t *testing.T) { +func TestAuth(t *testing.T) { bz, err := ioutil.ReadFile(filepath.Join("testdata", "kava-4-auth-state-block-500000.json")) require.NoError(t, err) var oldGenState auth.GenesisState @@ -51,9 +54,38 @@ func TestMigrateAuth(t *testing.T) { require.NotPanics(t, func() { cdc.MustUnmarshalJSON(bz, &oldGenState) }) - newGenState := MigrateAuth(oldGenState) + newGenState := Auth(oldGenState) err = auth.ValidateGenesis(newGenState) require.NoError(t, err) require.Equal(t, len(oldGenState.Accounts), len(newGenState.Accounts)+1) } + +func TestCommittee(t *testing.T) { + bz, err := ioutil.ReadFile(filepath.Join("testdata", "kava-4-committee-state.json")) + require.NoError(t, err) + var oldGenState v0_11committee.GenesisState + cdc := codec.New() + sdk.RegisterCodec(cdc) + v0_11committee.RegisterCodec(cdc) + require.NotPanics(t, func() { + cdc.MustUnmarshalJSON(bz, &oldGenState) + }) + + newGenState := Committee(oldGenState) + err = newGenState.Validate() + require.NoError(t, err) + + require.Equal(t, len(oldGenState.Committees), len(newGenState.Committees)) + + for i := 0; i < len(oldGenState.Committees); i++ { + require.Equal(t, len(oldGenState.Committees[i].Permissions), len(newGenState.Committees[i].Permissions)) + } + + oldSPCP := oldGenState.Committees[0].Permissions[0].(v0_11committee.SubParamChangePermission) + newSPCP := newGenState.Committees[0].Permissions[0].(v0_13committee.SubParamChangePermission) + require.Equal(t, len(oldSPCP.AllowedParams), len(newSPCP.AllowedParams)) + require.Equal(t, len(oldSPCP.AllowedAssetParams), len(newSPCP.AllowedAssetParams)) + require.Equal(t, len(oldSPCP.AllowedCollateralParams), len(newSPCP.AllowedCollateralParams)) + require.Equal(t, len(oldSPCP.AllowedMarkets), len(newSPCP.AllowedMarkets)) +} diff --git a/migrate/v0_13/testdata/kava-4-committee-state.json b/migrate/v0_13/testdata/kava-4-committee-state.json new file mode 100644 index 00000000..25359885 --- /dev/null +++ b/migrate/v0_13/testdata/kava-4-committee-state.json @@ -0,0 +1 @@ +{"committees":[{"description":"Kava Stability Committee","id":"1","members":["kava1gru35up50ql2wxhegr880qy6ynl63ujlv8gum2","kava1sc3mh3pkas5e7xd269am4xm5mp6zweyzmhjagj","kava1c9ye54e3pzwm3e0zpdlel6pnavrj9qqv6e8r4h","kava1m7p6sjqrz6mylz776ct48wj6lpnpcd0z82209d","kava1a9pmkzk570egv3sflu3uwdf3gejl7qfy9hghzl"],"permissions":[{"type":"kava/SubParamChangePermission","value":{"allowed_asset_params":[{"active":true,"coin_id":false,"denom":"bnb","limit":true,"max_swap_amount":true,"min_block_lock":true},{"active":true,"coin_id":true,"denom":"busd","limit":true,"max_swap_amount":true,"min_block_lock":true},{"active":true,"coin_id":false,"denom":"btcb","limit":true,"max_swap_amount":true,"min_block_lock":true},{"active":true,"coin_id":false,"denom":"xrpb","limit":true,"max_swap_amount":true,"min_block_lock":true}],"allowed_collateral_params":[{"auction_size":true,"conversion_factor":false,"debt_limit":true,"denom":false,"liquidation_market_id":false,"liquidation_penalty":false,"liquidation_ratio":false,"prefix":false,"spot_market_id":false,"stability_fee":true,"type":"bnb-a"},{"auction_size":true,"conversion_factor":false,"debt_limit":true,"denom":false,"liquidation_market_id":false,"liquidation_penalty":false,"liquidation_ratio":false,"prefix":false,"spot_market_id":false,"stability_fee":true,"type":"busd-a"},{"auction_size":true,"conversion_factor":false,"debt_limit":true,"denom":false,"liquidation_market_id":false,"liquidation_penalty":false,"liquidation_ratio":false,"prefix":false,"spot_market_id":false,"stability_fee":true,"type":"busd-b"},{"auction_size":true,"conversion_factor":false,"debt_limit":true,"denom":false,"liquidation_market_id":false,"liquidation_penalty":false,"liquidation_ratio":false,"prefix":false,"spot_market_id":false,"stability_fee":true,"type":"btcb-a"},{"auction_size":true,"conversion_factor":false,"debt_limit":true,"denom":false,"liquidation_market_id":false,"liquidation_penalty":false,"liquidation_ratio":false,"prefix":false,"spot_market_id":false,"stability_fee":true,"type":"xrpb-a"}],"allowed_debt_param":{"conversion_factor":false,"debt_floor":true,"denom":false,"reference_asset":false,"savings_rate":true},"allowed_markets":[{"active":true,"base_asset":false,"market_id":"bnb:usd","oracles":false,"quote_asset":false},{"active":true,"base_asset":false,"market_id":"bnb:usd:30","oracles":false,"quote_asset":false},{"active":true,"base_asset":false,"market_id":"btc:usd","oracles":false,"quote_asset":false},{"active":true,"base_asset":false,"market_id":"btc:usd:30","oracles":false,"quote_asset":false},{"active":true,"base_asset":false,"market_id":"xrp:usd","oracles":false,"quote_asset":false},{"active":true,"base_asset":false,"market_id":"xrp:usd:30","oracles":false,"quote_asset":false},{"active":true,"base_asset":false,"market_id":"busd:usd","oracles":false,"quote_asset":false},{"active":true,"base_asset":false,"market_id":"busd:usd:30","oracles":false,"quote_asset":false}],"allowed_params":[{"key":"BidDuration","subspace":"auction"},{"key":"Active","subspace":"harvest"},{"key":"IncrementSurplus","subspace":"auction"},{"key":"Active","subspace":"harvest"},{"key":"IncrementDebt","subspace":"auction"},{"key":"Active","subspace":"harvest"},{"key":"IncrementCollateral","subspace":"auction"},{"key":"Active","subspace":"harvest"},{"key":"AssetParams","subspace":"bep3"},{"key":"Active","subspace":"harvest"},{"key":"GlobalDebtLimit","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"SurplusThreshold","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"SurplusLot","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"DebtThreshold","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"DebtLot","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"DistributionFrequency","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"CollateralParams","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"DebtParam","subspace":"cdp"},{"key":"Active","subspace":"harvest"},{"key":"Active","subspace":"incentive"},{"key":"Active","subspace":"harvest"},{"key":"Active","subspace":"kavadist"},{"key":"Active","subspace":"harvest"},{"key":"Markets","subspace":"pricefeed"},{"key":"Active","subspace":"harvest"}]}},{"type":"kava/TextPermission","value":{}}],"proposal_duration":"604800000000000","vote_threshold":"0.500000000000000000"},{"description":"Kava Safety Committee","id":"2","members":["kava1e0agyg6eug9r62fly9sls77ycjgw8ax6xk73es"],"permissions":[{"type":"kava/SoftwareUpgradePermission","value":{}}],"proposal_duration":"604800000000000","vote_threshold":"0.500000000000000000"}],"next_proposal_id":"40","proposals":[],"votes":[]} diff --git a/x/committee/alias.go b/x/committee/alias.go index 35889c8b..b476f7cc 100644 --- a/x/committee/alias.go +++ b/x/committee/alias.go @@ -54,6 +54,7 @@ var ( GetKeyFromID = types.GetKeyFromID GetVoteKey = types.GetVoteKey NewAllowedCollateralParam = types.NewAllowedCollateralParam + NewAllowedMoneyMarket = types.NewAllowedMoneyMarket NewCommittee = types.NewCommittee NewCommitteeChangeProposal = types.NewCommitteeChangeProposal NewCommitteeDeleteProposal = types.NewCommitteeDeleteProposal @@ -98,6 +99,8 @@ type ( AllowedDebtParam = types.AllowedDebtParam AllowedMarket = types.AllowedMarket AllowedMarkets = types.AllowedMarkets + AllowedMoneyMarket = types.AllowedMoneyMarket + AllowedMoneyMarkets = types.AllowedMoneyMarkets AllowedParam = types.AllowedParam AllowedParams = types.AllowedParams Committee = types.Committee diff --git a/x/committee/legacy/v0_11/types.go b/x/committee/legacy/v0_11/types.go index e2cb58fb..aed9d364 100644 --- a/x/committee/legacy/v0_11/types.go +++ b/x/committee/legacy/v0_11/types.go @@ -484,6 +484,26 @@ type AllowedCollateralParam struct { type AllowedCollateralParams []AllowedCollateralParam +// NewAllowedCollateralParam return a new AllowedCollateralParam +func NewAllowedCollateralParam( + ctype string, denom, liqRatio, debtLimit, + stabilityFee, auctionSize, liquidationPenalty, + prefix, spotMarket, liquidationMarket, conversionFactor bool) AllowedCollateralParam { + return AllowedCollateralParam{ + Type: ctype, + Denom: denom, + LiquidationRatio: liqRatio, + DebtLimit: debtLimit, + StabilityFee: stabilityFee, + AuctionSize: auctionSize, + LiquidationPenalty: liquidationPenalty, + Prefix: prefix, + SpotMarketID: spotMarket, + LiquidationMarketID: liquidationMarket, + ConversionFactor: conversionFactor, + } +} + func (acps AllowedCollateralParams) Allows(current, incoming cdptypes.CollateralParams) bool { allAllowed := true @@ -566,6 +586,7 @@ func (adp AllowedDebtParam) Allows(current, incoming cdptypes.DebtParam) bool { type AllowedAssetParams []AllowedAssetParam +// Allows implement permission interface func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) bool { allAllowed := true @@ -614,19 +635,25 @@ func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) b return allAllowed } +// AllowedAssetParam bep3 asset parameters that can be changed by committee type AllowedAssetParam struct { - Denom string `json:"denom" yaml:"denom"` - CoinID bool `json:"coin_id" yaml:"coin_id"` - Limit bool `json:"limit" yaml:"limit"` - Active bool `json:"active" yaml:"active"` + Denom string `json:"denom" yaml:"denom"` + CoinID bool `json:"coin_id" yaml:"coin_id"` + Limit bool `json:"limit" yaml:"limit"` + Active bool `json:"active" yaml:"active"` + MaxSwapAmount bool `json:"max_swap_amount" yaml:"max_swap_amount"` + MinBlockLock bool `json:"min_block_lock" yaml:"min_block_lock"` } +// Allows bep3 AssetParam parameters than can be changed by committee func (aap AllowedAssetParam) Allows(current, incoming bep3types.AssetParam) bool { allowed := ((aap.Denom == current.Denom) && (aap.Denom == incoming.Denom)) && // require denoms to be all equal ((current.CoinID == incoming.CoinID) || aap.CoinID) && (current.SupplyLimit.Equals(incoming.SupplyLimit) || aap.Limit) && - ((current.Active == incoming.Active) || aap.Active) + ((current.Active == incoming.Active) || aap.Active) && + ((current.MaxSwapAmount.Equal(incoming.MaxSwapAmount)) || aap.MaxSwapAmount) && + ((current.MinBlockLock == incoming.MinBlockLock) || aap.MinBlockLock) return allowed } @@ -796,3 +823,17 @@ func (gs GenesisState) Validate() error { } return nil } + +func RegisterCodec(cdc *codec.Codec) { + + // Proposals + cdc.RegisterInterface((*PubProposal)(nil), nil) + + // Permissions + cdc.RegisterInterface((*Permission)(nil), nil) + cdc.RegisterConcrete(GodPermission{}, "kava/GodPermission", nil) + cdc.RegisterConcrete(SimpleParamChangePermission{}, "kava/SimpleParamChangePermission", nil) + cdc.RegisterConcrete(TextPermission{}, "kava/TextPermission", nil) + cdc.RegisterConcrete(SoftwareUpgradePermission{}, "kava/SoftwareUpgradePermission", nil) + cdc.RegisterConcrete(SubParamChangePermission{}, "kava/SubParamChangePermission", nil) +} diff --git a/x/committee/legacy/v0_13/types.go b/x/committee/legacy/v0_13/types.go index cbbdf196..55dc51f4 100644 --- a/x/committee/legacy/v0_13/types.go +++ b/x/committee/legacy/v0_13/types.go @@ -15,6 +15,7 @@ import ( bep3types "github.com/kava-labs/kava/x/bep3/types" cdptypes "github.com/kava-labs/kava/x/cdp/legacy/v0_11" + "github.com/kava-labs/kava/x/hard" "github.com/kava-labs/kava/x/pricefeed" pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types" ) @@ -564,8 +565,10 @@ func (adp AllowedDebtParam) Allows(current, incoming cdptypes.DebtParam) bool { return allowed } +// AllowedAssetParams slice of AllowedAssetParam type AllowedAssetParams []AllowedAssetParam +// Allows implement permission interface func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) bool { allAllowed := true @@ -614,19 +617,25 @@ func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) b return allAllowed } +// AllowedAssetParam bep3 asset parameters that can be changed by committee type AllowedAssetParam struct { - Denom string `json:"denom" yaml:"denom"` - CoinID bool `json:"coin_id" yaml:"coin_id"` - Limit bool `json:"limit" yaml:"limit"` - Active bool `json:"active" yaml:"active"` + Denom string `json:"denom" yaml:"denom"` + CoinID bool `json:"coin_id" yaml:"coin_id"` + Limit bool `json:"limit" yaml:"limit"` + Active bool `json:"active" yaml:"active"` + MaxSwapAmount bool `json:"max_swap_amount" yaml:"max_swap_amount"` + MinBlockLock bool `json:"min_block_lock" yaml:"min_block_lock"` } +// Allows bep3 AssetParam parameters than can be changed by committee func (aap AllowedAssetParam) Allows(current, incoming bep3types.AssetParam) bool { allowed := ((aap.Denom == current.Denom) && (aap.Denom == incoming.Denom)) && // require denoms to be all equal ((current.CoinID == incoming.CoinID) || aap.CoinID) && (current.SupplyLimit.Equals(incoming.SupplyLimit) || aap.Limit) && - ((current.Active == incoming.Active) || aap.Active) + ((current.Active == incoming.Active) || aap.Active) && + ((current.MaxSwapAmount.Equal(incoming.MaxSwapAmount)) || aap.MaxSwapAmount) && + ((current.MinBlockLock == incoming.MinBlockLock) || aap.MinBlockLock) return allowed } @@ -709,6 +718,75 @@ func addressesEqual(addrs1, addrs2 []sdk.AccAddress) bool { return areEqual } +// AllowedMoneyMarket permission struct for money market parameters (hard module) +type AllowedMoneyMarket struct { + Denom string `json:"denom" yaml:"denom"` + BorrowLimit bool `json:"borrow_limit" yaml:"borrow_limit"` + SpotMarketID bool `json:"spot_market_id" yaml:"spot_market_id"` + ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"` + InterestRateModel bool `json:"interest_rate_model" yaml:"interest_rate_model"` + ReserveFactor bool `json:"reserve_factor" yaml:"reserve_factor"` + AuctionSize bool `json:"auction_size" yaml:"auction_size"` + KeeperRewardPercentage bool `json:"keeper_reward_percentage" yaml:"keeper_reward_percentage"` +} + +// Allows implement permission interface +func (amm AllowedMoneyMarket) Allows(current, incoming hard.MoneyMarket) bool { + allowed := ((amm.Denom == current.Denom) && (amm.Denom == incoming.Denom)) && + ((current.BorrowLimit.Equal(incoming.BorrowLimit)) || amm.BorrowLimit) && + ((current.SpotMarketID == incoming.SpotMarketID) || amm.SpotMarketID) && + ((current.ConversionFactor.Equal(incoming.ConversionFactor)) || amm.ConversionFactor) && + ((current.InterestRateModel.Equal(incoming.InterestRateModel)) || amm.InterestRateModel) && + ((current.KeeperRewardPercentage.Equal(incoming.KeeperRewardPercentage)) || amm.KeeperRewardPercentage) + return allowed +} + +// AllowedMoneyMarkets slice of AllowedMoneyMarket +type AllowedMoneyMarkets []AllowedMoneyMarket + +// Allows implement permission interface +func (amms AllowedMoneyMarkets) Allows(current, incoming hard.MoneyMarkets) bool { + allAllowed := true + + if len(incoming) != len(current) { + return false + } + + for _, incomingMM := range incoming { + var foundAllowedMM bool + var allowedMM AllowedMoneyMarket + + for _, p := range amms { + if p.Denom != incomingMM.Denom { + continue + } + foundAllowedMM = true + allowedMM = p + } + if !foundAllowedMM { + return false + } + + var foundCurrentMM bool + var currentMM hard.MoneyMarket + + for _, p := range current { + if p.Denom != incomingMM.Denom { + continue + } + foundCurrentMM = true + currentMM = p + } + if !foundCurrentMM { + return false + } + allowed := allowedMM.Allows(currentMM, incomingMM) + allAllowed = allAllowed && allowed + } + + return allAllowed +} + // DefaultNextProposalID is the starting poiint for proposal IDs. const DefaultNextProposalID uint64 = 1 diff --git a/x/committee/types/param_permissions_test.go b/x/committee/types/param_permissions_test.go index 627e2b76..e329f199 100644 --- a/x/committee/types/param_permissions_test.go +++ b/x/committee/types/param_permissions_test.go @@ -18,45 +18,9 @@ func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) } func (suite *PermissionsTestSuite) TestAllowedCollateralParams_Allows() { testCPs := cdptypes.CollateralParams{ - { - Type: "bnb-a", - Denom: "bnb", - LiquidationRatio: d("2.0"), - DebtLimit: c("usdx", 1000000000000), - StabilityFee: d("1.000000001547125958"), - LiquidationPenalty: d("0.05"), - AuctionSize: i(100), - Prefix: 0x20, - ConversionFactor: i(6), - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - }, - { - Type: "btc-a", - Denom: "btc", - LiquidationRatio: d("1.5"), - DebtLimit: c("usdx", 1000000000), - StabilityFee: d("1.000000001547125958"), - LiquidationPenalty: d("0.10"), - AuctionSize: i(1000), - Prefix: 0x30, - ConversionFactor: i(8), - SpotMarketID: "btc:usd", - LiquidationMarketID: "btc:usd", - }, - { - Type: "atom-a", - Denom: "atom", - LiquidationRatio: d("2.0"), - DebtLimit: c("usdx", 1000000000), - StabilityFee: d("1.000000001547125958"), - LiquidationPenalty: d("0.07"), - AuctionSize: i(100), - Prefix: 0x40, - ConversionFactor: i(6), - SpotMarketID: "atom:usd", - LiquidationMarketID: "atom:usd", - }, + cdptypes.NewCollateralParam("bnb", "bnb-a", d("2.0"), c("usdx", 1000000000000), d("1.000000001547125958"), i(100), d("0.05"), 0x20, "bnb:usd", "bnb:usd", d("0.01"), i(10), i(6)), + cdptypes.NewCollateralParam("btc", "btc-a", d("1.5"), c("usdx", 1000000000), d("1.000000001547125958"), i(1000), d("0.1"), 0x30, "btc:usd", "btc:usd", d("0.01"), i(10), i(8)), + cdptypes.NewCollateralParam("atom", "atom-a", d("2.0"), c("usdx", 1000000000), d("1.000000001547125958"), i(1000), d("0.07"), 0x40, "atom:usd", "atom:usd", d("0.01"), i(10), i(6)), } updatedTestCPs := make(cdptypes.CollateralParams, len(testCPs)) updatedTestCPs[0] = testCPs[1] @@ -87,17 +51,19 @@ func (suite *PermissionsTestSuite) TestAllowedCollateralParams_Allows() { StabilityFee: true, }, { // allow all fields - Type: "atom-a", - Denom: true, - LiquidationRatio: true, - DebtLimit: true, - StabilityFee: true, - AuctionSize: true, - LiquidationPenalty: true, - Prefix: true, - SpotMarketID: true, - LiquidationMarketID: true, - ConversionFactor: true, + Type: "atom-a", + Denom: true, + LiquidationRatio: true, + DebtLimit: true, + StabilityFee: true, + AuctionSize: true, + LiquidationPenalty: true, + Prefix: true, + SpotMarketID: true, + LiquidationMarketID: true, + ConversionFactor: true, + KeeperRewardPercentage: true, + CheckCollateralizationIndexCount: true, }, }, current: testCPs[:2], @@ -113,17 +79,19 @@ func (suite *PermissionsTestSuite) TestAllowedCollateralParams_Allows() { }, { // allow all fields - Type: "btc-a", - Denom: true, - LiquidationRatio: true, - DebtLimit: true, - StabilityFee: true, - AuctionSize: true, - LiquidationPenalty: true, - Prefix: true, - SpotMarketID: true, - LiquidationMarketID: true, - ConversionFactor: true, + Type: "btc-a", + Denom: true, + LiquidationRatio: true, + DebtLimit: true, + StabilityFee: true, + AuctionSize: true, + LiquidationPenalty: true, + Prefix: true, + SpotMarketID: true, + LiquidationMarketID: true, + ConversionFactor: true, + KeeperRewardPercentage: true, + CheckCollateralizationIndexCount: true, }, }, current: testCPs[:2], @@ -429,19 +397,21 @@ func (suite *PermissionsTestSuite) TestAllowedMarkets_Allows() { } func (suite *PermissionsTestSuite) TestAllowedCollateralParam_Allows() { - testCP := cdptypes.CollateralParam{ - Type: "bnb-a", - Denom: "bnb", - LiquidationRatio: d("1.5"), - DebtLimit: c("usdx", 1000000000000), - StabilityFee: d("1.000000001547125958"), // %5 apr - LiquidationPenalty: d("0.05"), - AuctionSize: i(100), - Prefix: 0x20, - ConversionFactor: i(6), - SpotMarketID: "bnb:usd", - LiquidationMarketID: "bnb:usd", - } + testCP := cdptypes.NewCollateralParam( + "bnb", + "bnb-a", + d("1.5"), + c("usdx", 1000000000000), + d("1.000000001547125958"), // %5 apr + i(10000000000000), + d("0.05"), + 0x20, + "bnb:usd", + "bnb:usd", + d("0.01"), + i(10), + i(8), + ) newMarketIDCP := testCP newMarketIDCP.SpotMarketID = "btc:usd" diff --git a/x/committee/types/permissions.go b/x/committee/types/permissions.go index 2201093f..d5be5b02 100644 --- a/x/committee/types/permissions.go +++ b/x/committee/types/permissions.go @@ -9,6 +9,7 @@ import ( bep3types "github.com/kava-labs/kava/x/bep3/types" cdptypes "github.com/kava-labs/kava/x/cdp/types" + "github.com/kava-labs/kava/x/hard" "github.com/kava-labs/kava/x/pricefeed" pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types" ) @@ -38,8 +39,10 @@ type GodPermission struct{} var _ Permission = GodPermission{} +// Allows implement permission interface func (GodPermission) Allows(sdk.Context, *codec.Codec, ParamKeeper, PubProposal) bool { return true } +// MarshalYAML implement yaml marshalling func (GodPermission) MarshalYAML() (interface{}, error) { valueToMarshal := struct { Type string `yaml:"type"` @@ -60,6 +63,7 @@ type SimpleParamChangePermission struct { var _ Permission = SimpleParamChangePermission{} +// Allows implement permission interface func (perm SimpleParamChangePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { proposal, ok := p.(paramstypes.ParameterChangeProposal) if !ok { @@ -73,6 +77,7 @@ func (perm SimpleParamChangePermission) Allows(_ sdk.Context, _ *codec.Codec, _ return true } +// MarshalYAML implement yaml marshalling func (perm SimpleParamChangePermission) MarshalYAML() (interface{}, error) { valueToMarshal := struct { Type string `yaml:"type"` @@ -84,12 +89,16 @@ func (perm SimpleParamChangePermission) MarshalYAML() (interface{}, error) { return valueToMarshal, nil } +// AllowedParam permission type for module parameter keys type AllowedParam struct { Subspace string `json:"subspace" yaml:"subspace"` Key string `json:"key" yaml:"key"` } + +// AllowedParams slice of AllowedParam type AllowedParams []AllowedParam +// Contains checks if a key is included in param permissions func (allowed AllowedParams) Contains(paramChange paramstypes.ParamChange) bool { for _, p := range allowed { if paramChange.Subspace == p.Subspace && paramChange.Key == p.Key { @@ -108,11 +117,13 @@ type TextPermission struct{} var _ Permission = TextPermission{} +// Allows implement permission interface func (TextPermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { _, ok := p.(govtypes.TextProposal) return ok } +// MarshalYAML implement yaml marshalling func (TextPermission) MarshalYAML() (interface{}, error) { valueToMarshal := struct { Type string `yaml:"type"` @@ -126,15 +137,18 @@ func (TextPermission) MarshalYAML() (interface{}, error) { // SoftwareUpgradePermission // ------------------------------------------ +// SoftwareUpgradePermission permission type for software upgrade proposals type SoftwareUpgradePermission struct{} var _ Permission = SoftwareUpgradePermission{} +// Allows implement permission interface func (SoftwareUpgradePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { _, ok := p.(upgrade.SoftwareUpgradeProposal) return ok } +// MarshalYAML implement yaml marshalling func (SoftwareUpgradePermission) MarshalYAML() (interface{}, error) { valueToMarshal := struct { Type string `yaml:"type"` @@ -148,25 +162,28 @@ func (SoftwareUpgradePermission) MarshalYAML() (interface{}, error) { // SubParamChangePermission // ------------------------------------------ -// ParamChangeProposal only allows changes to certain params +// SubParamChangePermission permission type for allowing changes to specific sub-keys within module parameter keys type SubParamChangePermission struct { AllowedParams AllowedParams `json:"allowed_params" yaml:"allowed_params"` AllowedCollateralParams AllowedCollateralParams `json:"allowed_collateral_params" yaml:"allowed_collateral_params"` AllowedDebtParam AllowedDebtParam `json:"allowed_debt_param" yaml:"allowed_debt_param"` AllowedAssetParams AllowedAssetParams `json:"allowed_asset_params" yaml:"allowed_asset_params"` AllowedMarkets AllowedMarkets `json:"allowed_markets" yaml:"allowed_markets"` + AllowedMoneyMarkets AllowedMoneyMarkets `json:"allowed_money_markets" yaml:"allowed_money_markets"` } var _ Permission = SubParamChangePermission{} +// MarshalYAML implement yaml marshalling func (perm SubParamChangePermission) MarshalYAML() (interface{}, error) { valueToMarshal := struct { - Type string `yaml:"type"` - AllowedParams AllowedParams `yaml:"allowed_params"` - AllowedCollateralParams AllowedCollateralParams `yaml:"allowed_collateral_params"` - AllowedDebtParam AllowedDebtParam `yaml:"allowed_debt_param"` - AllowedAssetParams AllowedAssetParams `yaml:"allowed_asset_params"` - AllowedMarkets AllowedMarkets `yaml:"allowed_markets"` + Type string `yaml:"type" json:"type"` + AllowedParams AllowedParams `yaml:"allowed_params" json:"allowed_params"` + AllowedCollateralParams AllowedCollateralParams `yaml:"allowed_collateral_params" json:"allowed_collateral_params"` + AllowedDebtParam AllowedDebtParam `yaml:"allowed_debt_param" json:"allowed_debt_param"` + AllowedAssetParams AllowedAssetParams `yaml:"allowed_asset_params" json:"allowed_asset_params"` + AllowedMarkets AllowedMarkets `yaml:"allowed_markets" json:"allowed_markets"` + AllowedMoneyMarkets AllowedMoneyMarkets `json:"allowed_money_markets" yaml:"allowed_money_markets"` }{ Type: "param_change_permission", AllowedParams: perm.AllowedParams, @@ -174,10 +191,12 @@ func (perm SubParamChangePermission) MarshalYAML() (interface{}, error) { AllowedDebtParam: perm.AllowedDebtParam, AllowedAssetParams: perm.AllowedAssetParams, AllowedMarkets: perm.AllowedMarkets, + AllowedMoneyMarkets: perm.AllowedMoneyMarkets, } return valueToMarshal, nil } +// Allows implement permission interface func (perm SubParamChangePermission) Allows(ctx sdk.Context, appCdc *codec.Codec, pk ParamKeeper, p PubProposal) bool { // Check pubproposal has correct type proposal, ok := p.(paramstypes.ParameterChangeProposal) @@ -318,11 +337,40 @@ func (perm SubParamChangePermission) Allows(ctx sdk.Context, appCdc *codec.Codec } } + // Check any MoneyMarket changes are allowed + + var foundIncomingMMs bool + var incomingMMs hard.MoneyMarkets + for _, change := range proposal.Changes { + if !(change.Subspace == hard.ModuleName && change.Key == string(hard.KeyMoneyMarkets)) { + continue + } + foundIncomingMMs = true + if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingMMs); err != nil { + return false + } + } + + if foundIncomingMMs { + subspace, found := pk.GetSubspace(hard.ModuleName) + if !found { + return false + } + var currentMMs hard.MoneyMarkets + subspace.Get(ctx, hard.KeyMoneyMarkets, ¤tMMs) + mmChangesAllowed := perm.AllowedMoneyMarkets.Allows(currentMMs, incomingMMs) + if !mmChangesAllowed { + return false + } + } + return true } +// AllowedCollateralParams slice of AllowedCollateralParam type AllowedCollateralParams []AllowedCollateralParam +// Allows determine if collateral params changes are permitted func (acps AllowedCollateralParams) Allows(current, incoming cdptypes.CollateralParams) bool { allAllowed := true @@ -371,40 +419,46 @@ func (acps AllowedCollateralParams) Allows(current, incoming cdptypes.Collateral return allAllowed } +// AllowedCollateralParam permission struct for changes to collateral parameter keys (cdp module) type AllowedCollateralParam struct { - Type string `json:"type" yaml:"type"` - Denom bool `json:"denom" yaml:"denom"` - LiquidationRatio bool `json:"liquidation_ratio" yaml:"liquidation_ratio"` - DebtLimit bool `json:"debt_limit" yaml:"debt_limit"` - StabilityFee bool `json:"stability_fee" yaml:"stability_fee"` - AuctionSize bool `json:"auction_size" yaml:"auction_size"` - LiquidationPenalty bool `json:"liquidation_penalty" yaml:"liquidation_penalty"` - Prefix bool `json:"prefix" yaml:"prefix"` - SpotMarketID bool `json:"spot_market_id" yaml:"spot_market_id"` - LiquidationMarketID bool `json:"liquidation_market_id" yaml:"liquidation_market_id"` - ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"` + Type string `json:"type" yaml:"type"` + Denom bool `json:"denom" yaml:"denom"` + LiquidationRatio bool `json:"liquidation_ratio" yaml:"liquidation_ratio"` + DebtLimit bool `json:"debt_limit" yaml:"debt_limit"` + StabilityFee bool `json:"stability_fee" yaml:"stability_fee"` + AuctionSize bool `json:"auction_size" yaml:"auction_size"` + LiquidationPenalty bool `json:"liquidation_penalty" yaml:"liquidation_penalty"` + Prefix bool `json:"prefix" yaml:"prefix"` + SpotMarketID bool `json:"spot_market_id" yaml:"spot_market_id"` + LiquidationMarketID bool `json:"liquidation_market_id" yaml:"liquidation_market_id"` + ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"` + KeeperRewardPercentage bool `json:"keeper_reward_percentage" yaml:"keeper_reward_percentage"` + CheckCollateralizationIndexCount bool `json:"check_collateralization_index_count" yaml:"check_collateralization_index_count"` } // NewAllowedCollateralParam return a new AllowedCollateralParam func NewAllowedCollateralParam( ctype string, denom, liqRatio, debtLimit, stabilityFee, auctionSize, liquidationPenalty, - prefix, spotMarket, liquidationMarket, conversionFactor bool) AllowedCollateralParam { + prefix, spotMarket, liquidationMarket, conversionFactor, keeperReward, ltvIndexCount bool) AllowedCollateralParam { return AllowedCollateralParam{ - Type: ctype, - Denom: denom, - LiquidationRatio: liqRatio, - DebtLimit: debtLimit, - StabilityFee: stabilityFee, - AuctionSize: auctionSize, - LiquidationPenalty: liquidationPenalty, - Prefix: prefix, - SpotMarketID: spotMarket, - LiquidationMarketID: liquidationMarket, - ConversionFactor: conversionFactor, + Type: ctype, + Denom: denom, + LiquidationRatio: liqRatio, + DebtLimit: debtLimit, + StabilityFee: stabilityFee, + AuctionSize: auctionSize, + LiquidationPenalty: liquidationPenalty, + Prefix: prefix, + SpotMarketID: spotMarket, + LiquidationMarketID: liquidationMarket, + ConversionFactor: conversionFactor, + KeeperRewardPercentage: keeperReward, + CheckCollateralizationIndexCount: ltvIndexCount, } } +// Allows determine if collateral param changes are permitted func (acp AllowedCollateralParam) Allows(current, incoming cdptypes.CollateralParam) bool { allowed := ((acp.Type == current.Type) && (acp.Type == incoming.Type)) && // require collateral types to be all equal (current.Denom == incoming.Denom || acp.Denom) && @@ -416,10 +470,13 @@ func (acp AllowedCollateralParam) Allows(current, incoming cdptypes.CollateralPa ((current.Prefix == incoming.Prefix) || acp.Prefix) && ((current.SpotMarketID == incoming.SpotMarketID) || acp.SpotMarketID) && ((current.LiquidationMarketID == incoming.LiquidationMarketID) || acp.LiquidationMarketID) && + ((current.KeeperRewardPercentage.Equal(incoming.KeeperRewardPercentage)) || acp.KeeperRewardPercentage) && + ((current.CheckCollateralizationIndexCount.Equal(incoming.CheckCollateralizationIndexCount)) || acp.CheckCollateralizationIndexCount) && (current.ConversionFactor.Equal(incoming.ConversionFactor) || acp.ConversionFactor) return allowed } +// AllowedDebtParam permission struct for changes to debt parameter keys (cdp module) type AllowedDebtParam struct { Denom bool `json:"denom" yaml:"denom"` ReferenceAsset bool `json:"reference_asset" yaml:"reference_asset"` @@ -427,6 +484,7 @@ type AllowedDebtParam struct { DebtFloor bool `json:"debt_floor" yaml:"debt_floor"` } +// Allows determines if debt params changes are permitted func (adp AllowedDebtParam) Allows(current, incoming cdptypes.DebtParam) bool { allowed := ((current.Denom == incoming.Denom) || adp.Denom) && ((current.ReferenceAsset == incoming.ReferenceAsset) || adp.ReferenceAsset) && @@ -435,8 +493,10 @@ func (adp AllowedDebtParam) Allows(current, incoming cdptypes.DebtParam) bool { return allowed } +// AllowedAssetParams slice of AllowedAssetParam type AllowedAssetParams []AllowedAssetParam +// Allows determines if asset params changes are permitted func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) bool { allAllowed := true @@ -507,8 +567,10 @@ func (aap AllowedAssetParam) Allows(current, incoming bep3types.AssetParam) bool return allowed } +// AllowedMarkets slice of AllowedMarket type AllowedMarkets []AllowedMarket +// Allows determines if markets params changed are permitted func (ams AllowedMarkets) Allows(current, incoming pricefeedtypes.Markets) bool { allAllowed := true @@ -557,6 +619,7 @@ func (ams AllowedMarkets) Allows(current, incoming pricefeedtypes.Markets) bool return allAllowed } +// AllowedMarket permission struct for market parameters (pricefeed module) type AllowedMarket struct { MarketID string `json:"market_id" yaml:"market_id"` BaseAsset bool `json:"base_asset" yaml:"base_asset"` @@ -565,6 +628,7 @@ type AllowedMarket struct { Active bool `json:"active" yaml:"active"` } +// Allows determines if market param changes are permitted func (am AllowedMarket) Allows(current, incoming pricefeedtypes.Market) bool { allowed := ((am.MarketID == current.MarketID) && (am.MarketID == incoming.MarketID)) && // require denoms to be all equal ((current.BaseAsset == incoming.BaseAsset) || am.BaseAsset) && @@ -585,3 +649,85 @@ func addressesEqual(addrs1, addrs2 []sdk.AccAddress) bool { } return areEqual } + +// AllowedMoneyMarket permission struct for money market parameters (hard module) +type AllowedMoneyMarket struct { + Denom string `json:"denom" yaml:"denom"` + BorrowLimit bool `json:"borrow_limit" yaml:"borrow_limit"` + SpotMarketID bool `json:"spot_market_id" yaml:"spot_market_id"` + ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"` + InterestRateModel bool `json:"interest_rate_model" yaml:"interest_rate_model"` + ReserveFactor bool `json:"reserve_factor" yaml:"reserve_factor"` + KeeperRewardPercentage bool `json:"keeper_reward_percentage" yaml:"keeper_reward_percentage"` +} + +// NewAllowedMoneyMarket returns a new AllowedMoneyMarket +func NewAllowedMoneyMarket(denom string, bl, sm, cf, irm, rf, kr bool) AllowedMoneyMarket { + return AllowedMoneyMarket{ + Denom: denom, + BorrowLimit: bl, + SpotMarketID: sm, + ConversionFactor: cf, + InterestRateModel: irm, + ReserveFactor: rf, + KeeperRewardPercentage: kr, + } +} + +// Allows determines if money market param changes are permitted +func (amm AllowedMoneyMarket) Allows(current, incoming hard.MoneyMarket) bool { + allowed := ((amm.Denom == current.Denom) && (amm.Denom == incoming.Denom)) && + ((current.BorrowLimit.Equal(incoming.BorrowLimit)) || amm.BorrowLimit) && + ((current.SpotMarketID == incoming.SpotMarketID) || amm.SpotMarketID) && + ((current.ConversionFactor.Equal(incoming.ConversionFactor)) || amm.ConversionFactor) && + ((current.InterestRateModel.Equal(incoming.InterestRateModel)) || amm.InterestRateModel) && + ((current.ReserveFactor.Equal(incoming.ReserveFactor)) || amm.ReserveFactor) && + ((current.KeeperRewardPercentage.Equal(incoming.KeeperRewardPercentage)) || amm.KeeperRewardPercentage) + return allowed +} + +// AllowedMoneyMarkets slice of AllowedMoneyMarket +type AllowedMoneyMarkets []AllowedMoneyMarket + +// Allows determins if money market params changes are permitted +func (amms AllowedMoneyMarkets) Allows(current, incoming hard.MoneyMarkets) bool { + allAllowed := true + + if len(incoming) != len(current) { + return false + } + + for _, incomingMM := range incoming { + var foundAllowedMM bool + var allowedMM AllowedMoneyMarket + + for _, p := range amms { + if p.Denom != incomingMM.Denom { + continue + } + foundAllowedMM = true + allowedMM = p + } + if !foundAllowedMM { + return false + } + + var foundCurrentMM bool + var currentMM hard.MoneyMarket + + for _, p := range current { + if p.Denom != incomingMM.Denom { + continue + } + foundCurrentMM = true + currentMM = p + } + if !foundCurrentMM { + return false + } + allowed := allowedMM.Allows(currentMM, incomingMM) + allAllowed = allAllowed && allowed + } + + return allAllowed +}