diff --git a/app/app.go b/app/app.go index a2b66870abf09cda1637ad60f0e2349794e851fa..55665cf147f6ea41ba0b37574c415c3dc13915ed 100644 --- a/app/app.go +++ b/app/app.go @@ -339,6 +339,9 @@ func NewOsmosisApp( distrParams.BaseProposerReward = sdk.ZeroDec() distrParams.BonusProposerReward = sdk.ZeroDec() app.DistrKeeper.SetParams(ctx, distrParams) + + // configure upgrade for gamm module's pool creation fee param add + app.GAMMKeeper.SetParams(ctx, gammtypes.NewParams(sdk.Coins{sdk.NewInt64Coin("uosmo", 1000000000)})) // 1000 OSMO }) // Create IBC Keeper @@ -373,7 +376,7 @@ func NewOsmosisApp( app.StakingKeeper = *stakingKeeper.SetHooks( stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks(), app.ClaimKeeper.Hooks()), ) - gammKeeper := gammkeeper.NewKeeper(appCodec, keys[gammtypes.StoreKey], app.AccountKeeper, app.BankKeeper) + gammKeeper := gammkeeper.NewKeeper(appCodec, keys[gammtypes.StoreKey], app.GetSubspace(gammtypes.ModuleName), app.AccountKeeper, app.BankKeeper, app.DistrKeeper) lockupKeeper := lockupkeeper.NewKeeper(appCodec, keys[lockuptypes.StoreKey], app.AccountKeeper, app.BankKeeper) epochsKeeper := epochskeeper.NewKeeper(appCodec, keys[epochstypes.StoreKey]) incentivesKeeper := incentiveskeeper.NewKeeper(appCodec, keys[incentivestypes.StoreKey], app.GetSubspace(incentivestypes.ModuleName), app.AccountKeeper, app.BankKeeper, *lockupKeeper, epochsKeeper, app.StakingKeeper) @@ -517,6 +520,7 @@ func NewOsmosisApp( claimtypes.ModuleName, incentivestypes.ModuleName, epochstypes.ModuleName, + gammtypes.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) @@ -770,6 +774,7 @@ func initParamsKeeper(appCodec codec.BinaryMarshaler, legacyAmino *codec.LegacyA paramsKeeper.Subspace(ibchost.ModuleName) paramsKeeper.Subspace(incentivestypes.ModuleName) paramsKeeper.Subspace(poolincentivestypes.ModuleName) + paramsKeeper.Subspace(gammtypes.ModuleName) return paramsKeeper } diff --git a/osmomath/math.go b/osmomath/math.go new file mode 100644 index 0000000000000000000000000000000000000000..1889531af8116a442288eaeddeb522dadda9a513 --- /dev/null +++ b/osmomath/math.go @@ -0,0 +1,156 @@ +package osmomath + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Don't EVER change after initializing +// TODO: Analyze choice here +var powPrecision, _ = sdk.NewDecFromStr("0.00000001") + +// Singletons +var zero sdk.Dec = sdk.ZeroDec() +var one_half sdk.Dec = sdk.MustNewDecFromStr("0.5") +var one sdk.Dec = sdk.OneDec() +var two sdk.Dec = sdk.MustNewDecFromStr("2") + + +/*********************************************************/ + +// AbsDifferenceWithSign returns | a - b |, (a - b).sign() +// a is mutated and returned +func AbsDifferenceWithSign(a, b sdk.Dec) (sdk.Dec, bool) { + if a.GTE(b) { + return a.SubMut(b), false + } else { + return a.NegMut().AddMut(b), true + } +} + +// func largeBasePow(base sdk.Dec, exp sdk.Dec) sdk.Dec { +// // pow requires the base to be <= 2 +// } + +// Pow computes base^(exp) +// However since the exponent is not an integer, we must do an approximation algorithm. +// TODO: In the future, lets add some optimized routines for common exponents, e.g. for common wIn / wOut ratios +// Many simple exponents like 2:1 pools +func Pow(base sdk.Dec, exp sdk.Dec) sdk.Dec { + // Exponentiation of a negative base with an arbitrary real exponent is not closed within the reals. + // You can see this by recalling that `i = (-1)^(.5)`. We have to go to complex numbers to define this. + // (And would have to implement complex logarithms) + // We don't have a need for negative bases, so we don't include any such logic. + if !base.IsPositive() { + panic(fmt.Errorf("base must be greater than 0")) + } + // TODO: Remove this if we want to generalize the function, + // we can adjust the algorithm in this setting. + if base.GTE(two) { + panic(fmt.Errorf("base must be lesser than two")) + } + + // We will use an approximation algorithm to compute the power. + // Since computing an integer power is easy, we split up the exponent into + // an integer component and a fractional component. + integer := exp.TruncateDec() + fractional := exp.Sub(integer) + + integerPow := base.Power(uint64(integer.TruncateInt64())) + + if fractional.IsZero() { + return integerPow + } + + fractionalPow := PowApprox(base, fractional, powPrecision) + + return integerPow.Mul(fractionalPow) +} + +// Contract: 0 < base <= 2 +// 0 < exp < 1 +func PowApprox(base sdk.Dec, exp sdk.Dec, precision sdk.Dec) sdk.Dec { + if exp.IsZero() { + return sdk.ZeroDec() + } + + // Common case optimization + // Optimize for it being equal to one-half + if exp.Equal(one_half) { + output, err := base.ApproxSqrt() + if err != nil { + panic(err) + } + return output + } + // TODO: Make an approx-equal function, and then check if exp * 3 = 1, and do a check accordingly + + // We compute this via taking the maclaurin series of (1 + x)^a + // where x = base - 1. + // The maclaurin series of (1 + x)^a = sum_{k=0}^{infty} binom(a, k) x^k + // Binom(a, k) takes the natural continuation on the first parameter, namely that + // Binom(a, k) = N/D, where D = k!, and N = a(a-1)(a-2)...(a-k+1) + // Next we show that the absolute value of each term is less than the last term. + // Note that the change in term n's value vs term n + 1 is a multiplicative factor of + // v_n = x(a - n) / (n+1) + // So if |v_n| < 1, we know that each term has a lesser impact on the result than the last. + // For our bounds on |x| < 1, |a| < 1, + // it suffices to see for what n is |v_n| < 1, + // in the worst parameterization of x = 1, a = -1. + // v_n = |(-1 + epsilon - n) / (n+1)| + // So |v_n| is always less than 1, as n ranges over the integers. + // + // Note that term_n of the expansion is 1 * prod_{i=0}^{n-1} v_i + // The error if we stop the expansion at term_n is: + // error_n = sum_{k=n+1}^{infty} term_k + // At this point we further restrict a >= 0, so 0 <= a < 1. + // Now we take the _INCORRECT_ assumption that if term_n < p, then + // error_n < p. + // This assumption is obviously wrong. + // However our usages of this function don't use the full domain. + // With a > 0, |x| << 1, and p sufficiently low, perhaps this actually is true. + + // TODO: Check with our parameterization + // TODO: If theres a bug, balancer is also wrong here :thonk: + + base = base.Clone() + x, xneg := AbsDifferenceWithSign(base, one) + term := sdk.OneDec() + sum := sdk.OneDec() + negative := false + + a := exp.Clone() + bigK := sdk.NewDec(0) + // TODO: Document this computation via taylor expansion + for i := int64(1); term.GTE(precision); i++ { + // At each iteration, we need two values, i and i-1. + // To avoid expensive big.Int allocation, we reuse bigK variable. + // On this line, bigK == i-1. + c, cneg := AbsDifferenceWithSign(a, bigK) + // On this line, bigK == i. + bigK.Set(sdk.NewDec(i)) // TODO: O(n) bigint allocation happens + term.MulMut(c).MulMut(x).QuoMut(bigK) + + // a is mutated on absDifferenceWithSign, reset + a.Set(exp) + + if term.IsZero() { + break + } + if xneg { + negative = !negative + } + + if cneg { + negative = !negative + } + + if negative { + sum.SubMut(term) + } else { + sum.AddMut(term) + } + } + return sum +} diff --git a/osmomath/math_test.go b/osmomath/math_test.go new file mode 100644 index 0000000000000000000000000000000000000000..096a685a20464f0f454a93e5f97a3d4b7b9c5a9b --- /dev/null +++ b/osmomath/math_test.go @@ -0,0 +1,59 @@ +package osmomath + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/stretchr/testify/require" +) + +func TestAbsDifferenceWithSign(t *testing.T) { + decA, err := sdk.NewDecFromStr("3.2") + require.NoError(t, err) + decB, err := sdk.NewDecFromStr("4.3432389") + require.NoError(t, err) + + s, b := AbsDifferenceWithSign(decA, decB) + require.True(t, b) + + expectedDec, err := sdk.NewDecFromStr("1.1432389") + require.NoError(t, err) + require.Equal(t, expectedDec, s) +} + +func TestPowApprox(t *testing.T) { + base, err := sdk.NewDecFromStr("0.8") + require.NoError(t, err) + exp, err := sdk.NewDecFromStr("0.32") + require.NoError(t, err) + + s := PowApprox(base, exp, powPrecision) + expectedDec, err := sdk.NewDecFromStr("0.93108385") + require.NoError(t, err) + + require.True( + t, + expectedDec.Sub(s).Abs().LTE(powPrecision), + "expected value & actual value's difference should less than precision", + ) +} + +func TestPow(t *testing.T) { + base, err := sdk.NewDecFromStr("1.68") + require.NoError(t, err) + exp, err := sdk.NewDecFromStr("0.32") + require.NoError(t, err) + + s := Pow(base, exp) + expectedDec, err := sdk.NewDecFromStr("1.18058965") + require.NoError(t, err) + + require.True( + t, + expectedDec.Sub(s).Abs().LTE(powPrecision), + "expected value & actual value's difference should less than precision", + ) +} + + diff --git a/x/gamm/keeper/pow_bench_test.go b/osmomath/pow_bench_test.go similarity index 95% rename from x/gamm/keeper/pow_bench_test.go rename to osmomath/pow_bench_test.go index 0af73bb365c1a5d8a47cdc04d1b03904ea50bca9..51c4095ccda9e15c4fac116b4dc31208bdb17bfc 100644 --- a/x/gamm/keeper/pow_bench_test.go +++ b/osmomath/pow_bench_test.go @@ -1,4 +1,4 @@ -package keeper +package osmomath import ( "testing" @@ -56,7 +56,7 @@ func BenchmarkPow(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range tests { - pow(test.base, test.exp) + Pow(test.base, test.exp) } } } @@ -80,7 +80,7 @@ func BenchmarkSqrtPow(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range tests { - pow(test.base, one_half) + Pow(test.base, one_half) } } } diff --git a/proto/osmosis/gamm/v1beta1/genesis.proto b/proto/osmosis/gamm/v1beta1/genesis.proto index 8f682fa187ea4d20e99fe7764e2dcacec856e259..1e815bcd64c5bca5d1ee6974c04a32c81a32a64b 100644 --- a/proto/osmosis/gamm/v1beta1/genesis.proto +++ b/proto/osmosis/gamm/v1beta1/genesis.proto @@ -4,6 +4,17 @@ package osmosis.gamm; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// Params holds parameters for the incentives module +message Params { + repeated cosmos.base.v1beta1.Coin pool_creation_fee = 1 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"pool_creation_fee\"", + (gogoproto.nullable) = false + ]; +} + option go_package = "github.com/osmosis-labs/osmosis/x/gamm/types"; @@ -12,4 +23,5 @@ message GenesisState { repeated google.protobuf.Any pools = 1 [ (cosmos_proto.accepts_interface) = "PoolI" ]; uint64 next_pool_number = 2; + Params params = 3 [ (gogoproto.nullable) = false ]; } diff --git a/x/epochs/genesis_test.go b/x/epochs/genesis_test.go index 050b6ec21d0d247ff7bd2e8bc3e85bd57de43e0d..60673004cd2e9149ddc37d300bb93aa388915f02 100644 --- a/x/epochs/genesis_test.go +++ b/x/epochs/genesis_test.go @@ -51,7 +51,8 @@ func TestEpochsInitGenesis(t *testing.T) { ctx = ctx.WithBlockHeight(1) ctx = ctx.WithBlockTime(now) - epochs.InitGenesis(ctx, app.EpochsKeeper, types.GenesisState{ + //test genesisState validation + genesisState := types.GenesisState{ Epochs: []types.EpochInfo{ { Identifier: "monthly", @@ -62,9 +63,34 @@ func TestEpochsInitGenesis(t *testing.T) { EpochCountingStarted: true, CurrentEpochEnded: true, }, + { + Identifier: "monthly", + StartTime: time.Time{}, + Duration: time.Hour * 24, + CurrentEpoch: 0, + CurrentEpochStartTime: time.Time{}, + EpochCountingStarted: true, + CurrentEpochEnded: true, + }, }, - }) + } + require.EqualError(t, genesisState.Validate(), "epoch identifier should be unique") + + genesisState = types.GenesisState{ + Epochs: []types.EpochInfo{ + { + Identifier: "monthly", + StartTime: time.Time{}, + Duration: time.Hour * 24, + CurrentEpoch: 0, + CurrentEpochStartTime: time.Time{}, + EpochCountingStarted: true, + CurrentEpochEnded: true, + }, + }, + } + epochs.InitGenesis(ctx, app.EpochsKeeper, genesisState) epochInfo := app.EpochsKeeper.GetEpochInfo(ctx, "monthly") require.Equal(t, epochInfo.Identifier, "monthly") require.Equal(t, epochInfo.StartTime.UTC().String(), now.UTC().String()) diff --git a/x/epochs/types/genesis.go b/x/epochs/types/genesis.go index f0a9bfdbbd490276dd2672bc2d8094beda9a4bdf..3387ecac3c5e88c4418afc900496cd9c55aeb37d 100644 --- a/x/epochs/types/genesis.go +++ b/x/epochs/types/genesis.go @@ -41,13 +41,18 @@ func DefaultGenesis() *GenesisState { // failure. func (gs GenesisState) Validate() error { // TODO: Epochs identifiers should be unique + epochIdentifiers := map[string]bool{} for _, epoch := range gs.Epochs { if epoch.Identifier == "" { return errors.New("epoch identifier should NOT be empty") } + if epochIdentifiers[epoch.Identifier] { + return errors.New("epoch identifier should be unique") + } if epoch.Duration == 0 { return errors.New("epoch duration should NOT be 0") } + epochIdentifiers[epoch.Identifier] = true } return nil } diff --git a/x/epochs/types/identifier.go b/x/epochs/types/identifier.go new file mode 100644 index 0000000000000000000000000000000000000000..9e6a15d85861a35469428f63042cd76f9c755631 --- /dev/null +++ b/x/epochs/types/identifier.go @@ -0,0 +1,22 @@ +package types + +import ( + "fmt" +) + +func ValidateEpochIdentifierInterface(i interface{}) error { + v, ok := i.(string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + ValidateEpochIdentifierString(v) + + return nil +} + +func ValidateEpochIdentifierString(s string) error { + if s == "" { + return fmt.Errorf("empty distribution epoch identifier: %+v", s) + } + return nil +} diff --git a/x/gamm/client/cli/cli_test.go b/x/gamm/client/cli/cli_test.go index 974532e24b338ff94ea2a766420f2bf8c6f74a2f..7011ea0f270678692c73b4abf6b9f1833425faee 100644 --- a/x/gamm/client/cli/cli_test.go +++ b/x/gamm/client/cli/cli_test.go @@ -19,7 +19,7 @@ import ( "github.com/osmosis-labs/osmosis/x/gamm/client/cli" gammtestutil "github.com/osmosis-labs/osmosis/x/gamm/client/testutil" "github.com/osmosis-labs/osmosis/x/gamm/types" - + gammtypes "github.com/osmosis-labs/osmosis/x/gamm/types" tmcli "github.com/tendermint/tendermint/libs/cli" ) @@ -35,6 +35,16 @@ func (s *IntegrationTestSuite) SetupSuite() { s.cfg = app.DefaultConfig() + encCfg := app.MakeEncodingConfig() + + // modification to pay fee with test bond denom "stake" + genesisState := app.ModuleBasics.DefaultGenesis(encCfg.Marshaler) + gammGen := gammtypes.DefaultGenesis() + gammGen.Params.PoolCreationFee = sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 1000000)} + gammGenJson := encCfg.Marshaler.MustMarshalJSON(gammGen) + genesisState[gammtypes.ModuleName] = gammGenJson + s.cfg.GenesisState = genesisState + s.network = network.New(s.T(), s.cfg) _, err := s.network.WaitForHeight(1) @@ -67,7 +77,7 @@ func (s *IntegrationTestSuite) TestNewCreatePoolCmd() { val.ClientCtx, val.Address, newAddr, - sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 20000), sdk.NewInt64Coin("node0token", 20000)), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 200000000), sdk.NewInt64Coin("node0token", 20000)), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), ) diff --git a/x/gamm/client/cli/query.go b/x/gamm/client/cli/query.go index 6a35870766d6e1c5378d762238067813da35010b..15a2e899892e0022423c9d2ce0325e4c649fcf9b 100644 --- a/x/gamm/client/cli/query.go +++ b/x/gamm/client/cli/query.go @@ -119,8 +119,13 @@ $ %s query gamm pools } queryClient := types.NewQueryClient(clientCtx) + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + res, err := queryClient.Pools(cmd.Context(), &types.QueryPoolsRequest{ - Pagination: nil, + Pagination: pageReq, }) if err != nil { return err @@ -131,6 +136,7 @@ $ %s query gamm pools } flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "pools") return cmd } diff --git a/x/gamm/genesis.go b/x/gamm/genesis.go index 37dcea8d5e31d293f0d098b997fe5ff38e1a9931..9345a372af4477ba2a2da1056c7e4c48c6636da1 100644 --- a/x/gamm/genesis.go +++ b/x/gamm/genesis.go @@ -12,6 +12,7 @@ import ( // InitGenesis initializes the capability module's state from a provided genesis // state. func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState, unpacker codectypes.AnyUnpacker) { + k.SetParams(ctx, genState.Params) k.SetNextPoolNumber(ctx, genState.NextPoolNumber) liquidity := sdk.Coins{} @@ -56,5 +57,6 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { return &types.GenesisState{ NextPoolNumber: k.GetNextPoolNumber(ctx), Pools: poolAnys, + Params: k.GetParams(ctx), } } diff --git a/x/gamm/genesis_test.go b/x/gamm/genesis_test.go index eac860593ee442f9ee83924a6f6fcf31f598de4b..e394b1991d2b508c04b1f2fe96b7cf3feb7c6011 100644 --- a/x/gamm/genesis_test.go +++ b/x/gamm/genesis_test.go @@ -6,6 +6,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" simapp "github.com/osmosis-labs/osmosis/app" + appparams "github.com/osmosis-labs/osmosis/app/params" "github.com/osmosis-labs/osmosis/x/gamm" "github.com/osmosis-labs/osmosis/x/gamm/types" "github.com/stretchr/testify/assert" @@ -42,6 +43,9 @@ func TestGammInitGenesis(t *testing.T) { gamm.InitGenesis(ctx, app.GAMMKeeper, types.GenesisState{ Pools: []*codectypes.Any{any}, NextPoolNumber: 2, + Params: types.Params{ + PoolCreationFee: sdk.Coins{sdk.NewInt64Coin(appparams.BaseCoinUnit, 1000_000_000)}, + }, }, app.AppCodec()) require.Equal(t, app.GAMMKeeper.GetNextPoolNumber(ctx), uint64(2)) @@ -68,6 +72,7 @@ func TestGammExportGenesis(t *testing.T) { acc1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address().Bytes()) app.BankKeeper.SetBalances(ctx, acc1, sdk.Coins{ + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewInt64Coin("foo", 100000), sdk.NewInt64Coin("bar", 100000), }) @@ -110,6 +115,7 @@ func TestMarshalUnmarshalGenesis(t *testing.T) { am := gamm.NewAppModule(appCodec, app.GAMMKeeper, app.AccountKeeper, app.BankKeeper) acc1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address().Bytes()) app.BankKeeper.SetBalances(ctx, acc1, sdk.Coins{ + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewInt64Coin("foo", 100000), sdk.NewInt64Coin("bar", 100000), }) diff --git a/x/gamm/keeper/invariants.go b/x/gamm/keeper/invariants.go index 012d58b8c666fad806474fc3553ba5935d60de22..e8168964da4b752187125bf6c3aef4e79b4f685c 100644 --- a/x/gamm/keeper/invariants.go +++ b/x/gamm/keeper/invariants.go @@ -6,6 +6,8 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/osmosis/osmomath" "github.com/osmosis-labs/osmosis/x/gamm/types" ) @@ -88,9 +90,9 @@ func PoolTotalWeightInvariant(keeper Keeper, bk types.BankKeeper) sdk.Invariant func genericPow(base, exp sdk.Dec) sdk.Dec { if !base.GTE(sdk.NewDec(2)) { - return pow(base, exp) + return osmomath.Pow(base, exp) } - return powApprox(sdk.OneDec().Quo(base), exp.Neg(), powPrecision) + return osmomath.PowApprox(sdk.OneDec().Quo(base), exp.Neg(), powPrecision) } // constantChange returns the multiplicative factor difference in the pool constant, between two different pools. diff --git a/x/gamm/keeper/keeper.go b/x/gamm/keeper/keeper.go index fef062d463ccfc45ab337e0952a52347c6201ecd..dfc5d2b6b8fc8164f0cd8a9a86f681a2013211dd 100644 --- a/x/gamm/keeper/keeper.go +++ b/x/gamm/keeper/keeper.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/osmosis-labs/osmosis/x/gamm/types" ) @@ -24,14 +25,16 @@ type Keeper struct { storeKey sdk.StoreKey cdc codec.BinaryMarshaler - hooks types.GammHooks + paramSpace paramtypes.Subspace + hooks types.GammHooks // keepers accountKeeper types.AccountKeeper bankKeeper types.BankKeeper + distrKeeper types.DistrKeeper } -func NewKeeper(cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper) Keeper { +func NewKeeper(cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, distrKeeper types.DistrKeeper) Keeper { // Ensure that the module account are set. moduleAddr, perms := accountKeeper.GetModuleAddressAndPermissions(types.ModuleName) if moduleAddr == nil { @@ -43,12 +46,17 @@ func NewKeeper(cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, accountKeeper t if !permContains(perms, authtypes.Burner) { panic(fmt.Sprintf("%s module account should have the burner permission", types.ModuleName)) } + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } return Keeper{ - storeKey: storeKey, - cdc: cdc, + storeKey: storeKey, + cdc: cdc, + paramSpace: paramSpace, // keepers accountKeeper: accountKeeper, bankKeeper: bankKeeper, + distrKeeper: distrKeeper, } } diff --git a/x/gamm/keeper/keeper_test.go b/x/gamm/keeper/keeper_test.go index 087382cda4bb6851903386ba9728d38c56e8604d..3fcd6c46fea1d2d81c24b613b06b3ffb9ea6c87b 100644 --- a/x/gamm/keeper/keeper_test.go +++ b/x/gamm/keeper/keeper_test.go @@ -50,6 +50,7 @@ func (suite *KeeperTestSuite) preparePoolWithPoolParams(poolParams types.PoolPar suite.ctx, acc, sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)), sdk.NewCoin("baz", sdk.NewInt(10000000)), diff --git a/x/gamm/keeper/math.go b/x/gamm/keeper/math.go index 83e1afa74ec23f4b43105b6ca54959f0901f5efc..2132eca29ac67b65061ac308c832ef77fdee401e 100644 --- a/x/gamm/keeper/math.go +++ b/x/gamm/keeper/math.go @@ -1,7 +1,7 @@ package keeper import ( - "fmt" + "github.com/osmosis-labs/osmosis/osmomath" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -67,7 +67,7 @@ func calcOutGivenIn( adjustedIn := sdk.OneDec().Sub(swapFee) adjustedIn = tokenAmountIn.Mul(adjustedIn) y := tokenBalanceIn.Quo(tokenBalanceIn.Add(adjustedIn)) - foo := pow(y, weightRatio) + foo := osmomath.Pow(y, weightRatio) bar := sdk.OneDec().Sub(foo) return tokenBalanceOut.Mul(bar) } @@ -84,7 +84,7 @@ func calcInGivenOut( weightRatio := tokenWeightOut.Quo(tokenWeightIn) diff := tokenBalanceOut.Sub(tokenAmountOut) y := tokenBalanceOut.Quo(diff) - foo := pow(y, weightRatio) + foo := osmomath.Pow(y, weightRatio) foo = foo.Sub(one) tokenAmountIn := sdk.OneDec().Sub(swapFee) return (tokenBalanceIn.Mul(foo)).Quo(tokenAmountIn) @@ -108,7 +108,7 @@ func calcPoolOutGivenSingleIn( tokenInRatio := newTokenBalanceIn.Quo(tokenBalanceIn) // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply; - poolRatio := pow(tokenInRatio, normalizedWeight) + poolRatio := osmomath.Pow(tokenInRatio, normalizedWeight) newPoolSupply := poolRatio.Mul(poolSupply) return newPoolSupply.Sub(poolSupply) } @@ -128,7 +128,7 @@ func calcSingleInGivenPoolOut( //uint newBalTi = poolRatio^(1/weightTi) * balTi; boo := sdk.OneDec().Quo(normalizedWeight) - tokenInRatio := pow(poolRatio, boo) + tokenInRatio := osmomath.Pow(poolRatio, boo) newTokenBalanceIn := tokenInRatio.Mul(tokenBalanceIn) tokenAmountInAfterFee := newTokenBalanceIn.Sub(tokenBalanceIn) // Do reverse order of fees charged in joinswap_ExternAmountIn, this way @@ -157,7 +157,7 @@ func calcSingleOutGivenPoolIn( // newBalTo = poolRatio^(1/weightTo) * balTo; - tokenOutRatio := pow(poolRatio, sdk.OneDec().Quo(normalizedWeight)) + tokenOutRatio := osmomath.Pow(poolRatio, sdk.OneDec().Quo(normalizedWeight)) newTokenBalanceOut := tokenOutRatio.Mul(tokenBalanceOut) tokenAmountOutBeforeSwapFee := tokenBalanceOut.Sub(newTokenBalanceOut) @@ -190,7 +190,7 @@ func calcPoolInGivenSingleOut( tokenOutRatio := newTokenBalanceOut.Quo(tokenBalanceOut) //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply; - poolRatio := pow(tokenOutRatio, normalizedWeight) + poolRatio := osmomath.Pow(tokenOutRatio, normalizedWeight) newPoolSupply := poolRatio.Mul(poolSupply) poolAmountInAfterExitFee := poolSupply.Sub(newPoolSupply) @@ -198,141 +198,3 @@ func calcPoolInGivenSingleOut( // pAi = pAiAfterExitFee/(1-exitFee) return poolAmountInAfterExitFee.Quo(sdk.OneDec().Sub(exitFee)) } - -/*********************************************************/ - -// absDifferenceWithSign returns | a - b |, (a - b).sign() -// a is mutated and returned -func absDifferenceWithSign(a, b sdk.Dec) (sdk.Dec, bool) { - if a.GTE(b) { - return a.SubMut(b), false - } else { - return a.NegMut().AddMut(b), true - } -} - -// func largeBasePow(base sdk.Dec, exp sdk.Dec) sdk.Dec { -// // pow requires the base to be <= 2 -// } - -// pow computes base^(exp) -// However since the exponent is not an integer, we must do an approximation algorithm. -// TODO: In the future, lets add some optimized routines for common exponents, e.g. for common wIn / wOut ratios -// Many simple exponents like 2:1 pools -func pow(base sdk.Dec, exp sdk.Dec) sdk.Dec { - // Exponentiation of a negative base with an arbitrary real exponent is not closed within the reals. - // You can see this by recalling that `i = (-1)^(.5)`. We have to go to complex numbers to define this. - // (And would have to implement complex logarithms) - // We don't have a need for negative bases, so we don't include any such logic. - if !base.IsPositive() { - panic(fmt.Errorf("base must be greater than 0")) - } - // TODO: Remove this if we want to generalize the function, - // we can adjust the algorithm in this setting. - if base.GTE(two) { - panic(fmt.Errorf("base must be lesser than two")) - } - - // We will use an approximation algorithm to compute the power. - // Since computing an integer power is easy, we split up the exponent into - // an integer component and a fractional component. - integer := exp.TruncateDec() - fractional := exp.Sub(integer) - - integerPow := base.Power(uint64(integer.TruncateInt64())) - - if fractional.IsZero() { - return integerPow - } - - fractionalPow := powApprox(base, fractional, powPrecision) - - return integerPow.Mul(fractionalPow) -} - -// Contract: 0 < base <= 2 -// 0 < exp < 1 -func powApprox(base sdk.Dec, exp sdk.Dec, precision sdk.Dec) sdk.Dec { - if exp.IsZero() { - return sdk.ZeroDec() - } - - // Common case optimization - // Optimize for it being equal to one-half - if exp.Equal(one_half) { - output, err := base.ApproxSqrt() - if err != nil { - panic(err) - } - return output - } - // TODO: Make an approx-equal function, and then check if exp * 3 = 1, and do a check accordingly - - // We compute this via taking the maclaurin series of (1 + x)^a - // where x = base - 1. - // The maclaurin series of (1 + x)^a = sum_{k=0}^{infty} binom(a, k) x^k - // Binom(a, k) takes the natural continuation on the first parameter, namely that - // Binom(a, k) = N/D, where D = k!, and N = a(a-1)(a-2)...(a-k+1) - // Next we show that the absolute value of each term is less than the last term. - // Note that the change in term n's value vs term n + 1 is a multiplicative factor of - // v_n = x(a - n) / (n+1) - // So if |v_n| < 1, we know that each term has a lesser impact on the result than the last. - // For our bounds on |x| < 1, |a| < 1, - // it suffices to see for what n is |v_n| < 1, - // in the worst parameterization of x = 1, a = -1. - // v_n = |(-1 + epsilon - n) / (n+1)| - // So |v_n| is always less than 1, as n ranges over the integers. - // - // Note that term_n of the expansion is 1 * prod_{i=0}^{n-1} v_i - // The error if we stop the expansion at term_n is: - // error_n = sum_{k=n+1}^{infty} term_k - // At this point we further restrict a >= 0, so 0 <= a < 1. - // Now we take the _INCORRECT_ assumption that if term_n < p, then - // error_n < p. - // This assumption is obviously wrong. - // However our usages of this function don't use the full domain. - // With a > 0, |x| << 1, and p sufficiently low, perhaps this actually is true. - - // TODO: Check with our parameterization - // TODO: If theres a bug, balancer is also wrong here :thonk: - - base = base.Clone() - x, xneg := absDifferenceWithSign(base, one) - term := sdk.OneDec() - sum := sdk.OneDec() - negative := false - - a := exp.Clone() - bigK := sdk.NewDec(0) - // TODO: Document this computation via taylor expansion - for i := int64(1); term.GTE(precision); i++ { - // At each iteration, we need two values, i and i-1. - // To avoid expensive big.Int allocation, we reuse bigK variable. - // On this line, bigK == i-1. - c, cneg := absDifferenceWithSign(a, bigK) - // On this line, bigK == i. - bigK.Set(sdk.NewDec(i)) // TODO: O(n) bigint allocation happens - term.MulMut(c).MulMut(x).QuoMut(bigK) - - // a is mutated on absDifferenceWithSign, reset - a.Set(exp) - - if term.IsZero() { - break - } - if xneg { - negative = !negative - } - - if cneg { - negative = !negative - } - - if negative { - sum.SubMut(term) - } else { - sum.AddMut(term) - } - } - return sum -} diff --git a/x/gamm/keeper/math_test.go b/x/gamm/keeper/math_test.go index 7660beb69f3078294178155e240d45dab7c91541..0c400c05351f2fac0cb7daf3fb6db3e1199c7ee1 100644 --- a/x/gamm/keeper/math_test.go +++ b/x/gamm/keeper/math_test.go @@ -8,54 +8,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestAbsDifferenceWithSign(t *testing.T) { - decA, err := sdk.NewDecFromStr("3.2") - require.NoError(t, err) - decB, err := sdk.NewDecFromStr("4.3432389") - require.NoError(t, err) - - s, b := absDifferenceWithSign(decA, decB) - require.True(t, b) - - expectedDec, err := sdk.NewDecFromStr("1.1432389") - require.NoError(t, err) - require.Equal(t, expectedDec, s) -} - -func TestPowApprox(t *testing.T) { - base, err := sdk.NewDecFromStr("0.8") - require.NoError(t, err) - exp, err := sdk.NewDecFromStr("0.32") - require.NoError(t, err) - - s := powApprox(base, exp, powPrecision) - expectedDec, err := sdk.NewDecFromStr("0.93108385") - require.NoError(t, err) - - require.True( - t, - expectedDec.Sub(s).Abs().LTE(powPrecision), - "expected value & actual value's difference should less than precision", - ) -} - -func TestPow(t *testing.T) { - base, err := sdk.NewDecFromStr("1.68") - require.NoError(t, err) - exp, err := sdk.NewDecFromStr("0.32") - require.NoError(t, err) - - s := pow(base, exp) - expectedDec, err := sdk.NewDecFromStr("1.18058965") - require.NoError(t, err) - - require.True( - t, - expectedDec.Sub(s).Abs().LTE(powPrecision), - "expected value & actual value's difference should less than precision", - ) -} - func TestCalcSpotPrice(t *testing.T) { // TODO: Change test to be table driven tokenBalanceIn, err := sdk.NewDecFromStr("100") diff --git a/x/gamm/keeper/params.go b/x/gamm/keeper/params.go new file mode 100644 index 0000000000000000000000000000000000000000..0c633bbc056f0f556cb4367959d0159303f72a9d --- /dev/null +++ b/x/gamm/keeper/params.go @@ -0,0 +1,17 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/osmosis-labs/osmosis/x/gamm/types" +) + +// GetParams returns the total set params +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the total set of params +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} diff --git a/x/gamm/keeper/pool_service.go b/x/gamm/keeper/pool_service.go index 0eff1a8664fd2d235d38074b3378dcca13f87a6d..70f7f1a36330179591c766d485b3e3537224726e 100644 --- a/x/gamm/keeper/pool_service.go +++ b/x/gamm/keeper/pool_service.go @@ -28,6 +28,13 @@ func (k Keeper) CreatePool( ) } + // send pool creation fee to community pool + params := k.GetParams(ctx) + err := k.distrKeeper.FundCommunityPool(ctx, params.PoolCreationFee, sender) + if err != nil { + return 0, err + } + pool, err := k.newPool(ctx, poolParams, poolAssets, futurePoolGovernor) if err != nil { return 0, err diff --git a/x/gamm/keeper/pool_service_test.go b/x/gamm/keeper/pool_service_test.go index c5a523fa89be13a39a084d8e441bd689ea89d6fd..10447922dfb36736b25d337dbe73b3ec0cf7a089 100644 --- a/x/gamm/keeper/pool_service_test.go +++ b/x/gamm/keeper/pool_service_test.go @@ -19,6 +19,13 @@ var ( ) func (suite *KeeperTestSuite) TestCreatePool() { + params := suite.app.GAMMKeeper.GetParams(suite.ctx) + + poolCreationFeeDecCoins := sdk.DecCoins{} + for _, coin := range params.PoolCreationFee { + poolCreationFeeDecCoins = poolCreationFeeDecCoins.Add(sdk.NewDecCoin(coin.Denom, coin.Amount)) + } + func() { keeper := suite.app.GAMMKeeper @@ -41,6 +48,8 @@ func (suite *KeeperTestSuite) TestCreatePool() { }{{ fn: func() { keeper := suite.app.GAMMKeeper + prevFeePool := suite.app.DistrKeeper.GetFeePoolCommunityCoins(suite.ctx) + prevAcc1Bal := suite.app.BankKeeper.GetAllBalances(suite.ctx, acc1) poolId, err := keeper.CreatePool(suite.ctx, acc1, types.PoolParams{ SwapFee: sdk.NewDecWithPrec(1, 2), ExitFee: sdk.NewDecWithPrec(1, 2), @@ -59,6 +68,20 @@ func (suite *KeeperTestSuite) TestCreatePool() { fmt.Sprintf("share token should be minted as %s initially", types.InitPoolSharesSupply.String()), ) + // check fee is correctly sent to community pool + feePool := suite.app.DistrKeeper.GetFeePoolCommunityCoins(suite.ctx) + suite.Require().Equal(feePool, prevFeePool.Add(poolCreationFeeDecCoins...)) + + // check account's balance is correctly reduced + acc1Bal := suite.app.BankKeeper.GetAllBalances(suite.ctx, acc1) + suite.Require().Equal(acc1Bal.String(), + prevAcc1Bal.Sub(params.PoolCreationFee). + Sub(sdk.Coins{ + sdk.NewCoin("bar", sdk.NewInt(10000)), + sdk.NewCoin("foo", sdk.NewInt(10000)), + }).Add(sdk.NewCoin(types.GetPoolShareDenom(pool.GetId()), types.InitPoolSharesSupply)).String(), + ) + liquidity := suite.app.GAMMKeeper.GetTotalLiquidity(suite.ctx) suite.Require().Equal("10000bar,10000foo", liquidity.String()) }, @@ -183,6 +206,48 @@ func (suite *KeeperTestSuite) TestCreatePool() { }}, defaultFutureGovernor) suite.Require().Error(err, "can't create the pool with duplicated PoolAssets") }, + }, { + fn: func() { + keeper := suite.app.GAMMKeeper + keeper.SetParams(suite.ctx, types.Params{ + PoolCreationFee: sdk.Coins{}, + }) + _, err := keeper.CreatePool(suite.ctx, acc1, types.PoolParams{ + SwapFee: sdk.NewDecWithPrec(1, 2), + ExitFee: sdk.NewDecWithPrec(1, 2), + }, []types.PoolAsset{{ + Weight: sdk.NewInt(100), + Token: sdk.NewCoin("foo", sdk.NewInt(10000)), + }, { + Weight: sdk.NewInt(100), + Token: sdk.NewCoin("bar", sdk.NewInt(10000)), + }}, defaultFutureGovernor) + suite.Require().NoError(err) + pools, err := keeper.GetPools(suite.ctx) + suite.Require().Len(pools, 1) + suite.Require().NoError(err) + }, + }, { + fn: func() { + keeper := suite.app.GAMMKeeper + keeper.SetParams(suite.ctx, types.Params{ + PoolCreationFee: nil, + }) + _, err := keeper.CreatePool(suite.ctx, acc1, types.PoolParams{ + SwapFee: sdk.NewDecWithPrec(1, 2), + ExitFee: sdk.NewDecWithPrec(1, 2), + }, []types.PoolAsset{{ + Weight: sdk.NewInt(100), + Token: sdk.NewCoin("foo", sdk.NewInt(10000)), + }, { + Weight: sdk.NewInt(100), + Token: sdk.NewCoin("bar", sdk.NewInt(10000)), + }}, defaultFutureGovernor) + suite.Require().NoError(err) + pools, err := keeper.GetPools(suite.ctx) + suite.Require().Len(pools, 1) + suite.Require().NoError(err) + }, }} for _, test := range tests { @@ -194,6 +259,7 @@ func (suite *KeeperTestSuite) TestCreatePool() { suite.ctx, acc, sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)), sdk.NewCoin("baz", sdk.NewInt(10000000)), @@ -281,6 +347,7 @@ func (suite *KeeperTestSuite) TestJoinPool() { suite.ctx, acc, sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)), sdk.NewCoin("baz", sdk.NewInt(10000000)), @@ -389,6 +456,7 @@ func (suite *KeeperTestSuite) TestExitPool() { suite.ctx, acc, sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)), sdk.NewCoin("baz", sdk.NewInt(10000000)), @@ -436,6 +504,7 @@ func (suite *KeeperTestSuite) TestActivePool() { suite.ctx, acc, sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)), sdk.NewCoin("baz", sdk.NewInt(10000000)), diff --git a/x/gamm/keeper/swap_test.go b/x/gamm/keeper/swap_test.go index aa3a4423d3609024f0bb2ec56d53f45fdc6bdc23..8fa6e880dc987b230184239b135fb6f6696ce00d 100644 --- a/x/gamm/keeper/swap_test.go +++ b/x/gamm/keeper/swap_test.go @@ -218,6 +218,7 @@ func (suite *KeeperTestSuite) TestActivePoolSwap() { suite.ctx, acc, sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)), sdk.NewCoin("baz", sdk.NewInt(10000000)), diff --git a/x/gamm/module.go b/x/gamm/module.go index f3cfb208d434a349642e3f5793ec81c7f841a87f..ff134856c868a558a48cc4539cd39c19bf3c5065 100644 --- a/x/gamm/module.go +++ b/x/gamm/module.go @@ -3,6 +3,7 @@ package gamm import ( "context" "encoding/json" + "fmt" "math/rand" "github.com/cosmos/cosmos-sdk/client" @@ -45,12 +46,16 @@ func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { // DefaultGenesis returns default genesis state as raw bytes for the gamm // module. func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { - return nil + return cdc.MustMarshalJSON(types.DefaultGenesis()) } // ValidateGenesis performs genesis state validation for the gamm module. func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { - return nil + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() } //--------------------------------------- diff --git a/x/gamm/simulation/operations.go b/x/gamm/simulation/operations.go index 63c34f5deb80d4b7365b2506f48e0ddc6c7cb24f..3745a606e2d7bfd75cf8223d82e963093696afd3 100644 --- a/x/gamm/simulation/operations.go +++ b/x/gamm/simulation/operations.go @@ -154,6 +154,12 @@ func SimulateMsgCreatePool(ak stakingTypes.AccountKeeper, bk stakingTypes.BankKe for i := range balances { denoms[i] = balances[i].Denom } + + // set the pool params to set the pool creation fee to dust amount of denom + k.SetParams(ctx, types.Params{ + PoolCreationFee: sdk.Coins{sdk.NewInt64Coin(denoms[0], 1)}, + }) + // futurePoolGovernor := genFuturePoolGovernor(r, simAccount.Address, denoms) msg := types.MsgCreatePool{ Sender: simAccount.Address.String(), @@ -184,7 +190,7 @@ func SimulateMsgSwapExactAmountIn(ak stakingTypes.AccountKeeper, bk stakingTypes coin := simCoins[r.Intn(len(simCoins))] // Use under 0.5% of the account balance - // TODO: Make like a 33% probability of using a ton of balance + // TODO: Make like a 33% probability of using a ton of balance amt, _ := simtypes.RandPositiveInt(r, coin.Amount.QuoRaw(200)) tokenIn := sdk.Coin{ diff --git a/x/gamm/spec/04_params.md b/x/gamm/spec/04_params.md new file mode 100644 index 0000000000000000000000000000000000000000..4bde2ebb07bdfb936154beeffdbff0db2742bab7 --- /dev/null +++ b/x/gamm/spec/04_params.md @@ -0,0 +1,14 @@ +<!-- +order: 4 +--> + +# Parameters + +The gamm module contains the following parameters: + +| Key | Type | Example | +| --------------- | --------- | ----------------- | +| PoolCreationFee | sdk.Coins | "1000000000uosmo" | + +Note: +PoolCreationFee is the amount of coins paid to community pool at the time of pool creation which is introduced to prevent spam pool creation. diff --git a/x/gamm/types/expected_keepers.go b/x/gamm/types/expected_keepers.go index e9d8238cb38825b655cbfdfa0049f2b181a8e205..f07b8da1222637b14bcc51d77a8a3c1bbded4bbe 100644 --- a/x/gamm/types/expected_keepers.go +++ b/x/gamm/types/expected_keepers.go @@ -56,3 +56,8 @@ type BankKeeper interface { UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error } + +// DistrKeeper defines the contract needed to be fulfilled for distribution keeper +type DistrKeeper interface { + FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error +} diff --git a/x/gamm/types/genesis.go b/x/gamm/types/genesis.go new file mode 100644 index 0000000000000000000000000000000000000000..47b2102730e6fb9f35d9aadc94ccb10ad7d022fe --- /dev/null +++ b/x/gamm/types/genesis.go @@ -0,0 +1,23 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" +) + +// DefaultGenesis creates a default GenesisState object +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Pools: []*codectypes.Any{}, + NextPoolNumber: 1, + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + if err := gs.Params.Validate(); err != nil { + return err + } + return nil +} diff --git a/x/gamm/types/genesis.pb.go b/x/gamm/types/genesis.pb.go index 4022360b6bf763be9dcade0429458a82f3ee5529..66dd5a3348d943804fd7c90bcce1d77aef1d06c0 100644 --- a/x/gamm/types/genesis.pb.go +++ b/x/gamm/types/genesis.pb.go @@ -5,7 +5,9 @@ package types import ( fmt "fmt" - types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" _ "github.com/regen-network/cosmos-proto" @@ -25,17 +27,63 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// Params holds parameters for the incentives module +type Params struct { + PoolCreationFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=pool_creation_fee,json=poolCreationFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"pool_creation_fee" yaml:"pool_creation_fee"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_5a324eb7f1dd793e, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetPoolCreationFee() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.PoolCreationFee + } + return nil +} + // GenesisState defines the gamm module's genesis state. type GenesisState struct { - Pools []*types.Any `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"` - NextPoolNumber uint64 `protobuf:"varint,2,opt,name=next_pool_number,json=nextPoolNumber,proto3" json:"next_pool_number,omitempty"` + Pools []*types1.Any `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"` + NextPoolNumber uint64 `protobuf:"varint,2,opt,name=next_pool_number,json=nextPoolNumber,proto3" json:"next_pool_number,omitempty"` + Params Params `protobuf:"bytes,3,opt,name=params,proto3" json:"params"` } func (m *GenesisState) Reset() { *m = GenesisState{} } func (m *GenesisState) String() string { return proto.CompactTextString(m) } func (*GenesisState) ProtoMessage() {} func (*GenesisState) Descriptor() ([]byte, []int) { - return fileDescriptor_5a324eb7f1dd793e, []int{0} + return fileDescriptor_5a324eb7f1dd793e, []int{1} } func (m *GenesisState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -64,7 +112,7 @@ func (m *GenesisState) XXX_DiscardUnknown() { var xxx_messageInfo_GenesisState proto.InternalMessageInfo -func (m *GenesisState) GetPools() []*types.Any { +func (m *GenesisState) GetPools() []*types1.Any { if m != nil { return m.Pools } @@ -78,7 +126,15 @@ func (m *GenesisState) GetNextPoolNumber() uint64 { return 0 } +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + func init() { + proto.RegisterType((*Params)(nil), "osmosis.gamm.Params") proto.RegisterType((*GenesisState)(nil), "osmosis.gamm.GenesisState") } @@ -87,24 +143,69 @@ func init() { } var fileDescriptor_5a324eb7f1dd793e = []byte{ - // 259 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xca, 0x2f, 0xce, 0xcd, - 0x2f, 0xce, 0x2c, 0xd6, 0x4f, 0x4f, 0xcc, 0xcd, 0xd5, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, - 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, - 0x81, 0xaa, 0xd1, 0x03, 0xa9, 0x91, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x4b, 0xe8, 0x83, 0x58, - 0x10, 0x35, 0x52, 0x92, 0xe9, 0xf9, 0xf9, 0xe9, 0x39, 0xa9, 0xfa, 0x60, 0x5e, 0x52, 0x69, 0x9a, - 0x7e, 0x62, 0x5e, 0x25, 0x4c, 0x2a, 0x19, 0xac, 0x3f, 0x1e, 0xa2, 0x07, 0xc2, 0x81, 0x48, 0x29, - 0xe5, 0x73, 0xf1, 0xb8, 0x43, 0xac, 0x0a, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0x32, 0xe5, 0x62, 0x2d, - 0xc8, 0xcf, 0xcf, 0x29, 0x96, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x12, 0xd1, 0x83, 0x98, 0xaa, - 0x07, 0x33, 0x55, 0xcf, 0x31, 0xaf, 0xd2, 0x89, 0xf3, 0xd4, 0x16, 0x5d, 0xd6, 0x80, 0xfc, 0xfc, - 0x1c, 0xcf, 0x20, 0x88, 0x6a, 0x21, 0x0d, 0x2e, 0x81, 0xbc, 0xd4, 0x8a, 0x92, 0x78, 0x10, 0x2f, - 0x3e, 0xaf, 0x34, 0x37, 0x29, 0xb5, 0x48, 0x82, 0x49, 0x81, 0x51, 0x83, 0x25, 0x88, 0x0f, 0x24, - 0x0e, 0x52, 0xeb, 0x07, 0x16, 0x75, 0x72, 0x3b, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, - 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, - 0x86, 0x28, 0x9d, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0x7f, - 0x75, 0x73, 0x12, 0x93, 0x8a, 0x61, 0x1c, 0xfd, 0x0a, 0x48, 0x10, 0x95, 0x54, 0x16, 0xa4, 0x16, - 0x27, 0xb1, 0x81, 0x5d, 0x64, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x16, 0xaa, 0x73, 0x98, 0x3f, - 0x01, 0x00, 0x00, + // 390 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x52, 0x3d, 0xeb, 0xd3, 0x40, + 0x18, 0xcf, 0xf9, 0xff, 0xb7, 0x60, 0x2c, 0xbe, 0x84, 0x0e, 0x69, 0x87, 0xb4, 0x64, 0xca, 0x60, + 0xef, 0x68, 0xc5, 0xc5, 0xcd, 0x14, 0x2a, 0x82, 0x48, 0x89, 0x9b, 0x4b, 0xb8, 0xc4, 0x6b, 0x0c, + 0x26, 0xf7, 0x84, 0xdc, 0x55, 0x9a, 0x6f, 0x21, 0xb8, 0xbb, 0xb8, 0x39, 0xfb, 0x21, 0x8a, 0x53, + 0x47, 0xa7, 0x2a, 0xed, 0x37, 0xf0, 0x13, 0xc8, 0xbd, 0x54, 0x0a, 0x4e, 0xc9, 0xef, 0x7e, 0x2f, + 0xf7, 0x3c, 0xbf, 0xc4, 0x0d, 0x41, 0xd4, 0x20, 0x4a, 0x41, 0x0a, 0x5a, 0xd7, 0xe4, 0xe3, 0x3c, + 0x63, 0x92, 0xce, 0x49, 0xc1, 0x38, 0x13, 0xa5, 0xc0, 0x4d, 0x0b, 0x12, 0xbc, 0x81, 0xd5, 0x60, + 0xa5, 0x19, 0x0f, 0x0b, 0x28, 0x40, 0x13, 0x44, 0xbd, 0x19, 0xcd, 0x78, 0x54, 0x00, 0x14, 0x15, + 0x23, 0x1a, 0x65, 0xdb, 0x0d, 0xa1, 0xbc, 0xbb, 0x50, 0xb9, 0xf6, 0xa7, 0xc6, 0x63, 0x80, 0xa5, + 0x02, 0x83, 0x48, 0x46, 0x05, 0xfb, 0x77, 0x79, 0x0e, 0x25, 0x37, 0x7c, 0xf8, 0x05, 0xb9, 0xfd, + 0x35, 0x6d, 0x69, 0x2d, 0xbc, 0xcf, 0xc8, 0x7d, 0xd4, 0x00, 0x54, 0x69, 0xde, 0x32, 0x2a, 0x4b, + 0xe0, 0xe9, 0x86, 0x31, 0x1f, 0x4d, 0x6f, 0xa2, 0x7b, 0x8b, 0x11, 0xb6, 0xa9, 0x2a, 0x07, 0xdb, + 0x1c, 0xbc, 0x84, 0x92, 0xc7, 0xaf, 0xf6, 0xc7, 0x89, 0xf3, 0xe7, 0x38, 0xf1, 0x3b, 0x5a, 0x57, + 0xcf, 0xc2, 0xff, 0x12, 0xc2, 0x6f, 0xbf, 0x26, 0x51, 0x51, 0xca, 0xf7, 0xdb, 0x0c, 0xe7, 0x50, + 0xdb, 0xf1, 0xec, 0x63, 0x26, 0xde, 0x7d, 0x20, 0xb2, 0x6b, 0x98, 0xd0, 0x61, 0x22, 0x79, 0xa0, + 0xfc, 0x4b, 0x6b, 0x5f, 0x31, 0x16, 0x7e, 0x45, 0xee, 0xe0, 0x85, 0x29, 0xeb, 0x8d, 0xa4, 0x92, + 0x79, 0x4f, 0xdd, 0x9e, 0xd2, 0x08, 0x3b, 0xd9, 0x10, 0x9b, 0x5e, 0xf0, 0xa5, 0x17, 0xfc, 0x9c, + 0x77, 0xf1, 0xdd, 0x1f, 0xdf, 0x67, 0xbd, 0x35, 0x40, 0xf5, 0x32, 0x31, 0x6a, 0x2f, 0x72, 0x1f, + 0x72, 0xb6, 0x93, 0xa9, 0x9e, 0x8f, 0x6f, 0xeb, 0x8c, 0xb5, 0xfe, 0x9d, 0x29, 0x8a, 0x6e, 0x93, + 0xfb, 0xea, 0x5c, 0x69, 0x5f, 0xeb, 0x53, 0x6f, 0xe1, 0xf6, 0x1b, 0xdd, 0x88, 0x7f, 0x33, 0x45, + 0xfa, 0x86, 0xeb, 0xaf, 0x83, 0x4d, 0x5b, 0xf1, 0xad, 0x5a, 0x3b, 0xb1, 0xca, 0x78, 0xb5, 0x3f, + 0x05, 0xe8, 0x70, 0x0a, 0xd0, 0xef, 0x53, 0x80, 0x3e, 0x9d, 0x03, 0xe7, 0x70, 0x0e, 0x9c, 0x9f, + 0xe7, 0xc0, 0x79, 0xfb, 0xf8, 0x6a, 0x75, 0x9b, 0x33, 0xab, 0x68, 0x26, 0x2e, 0x80, 0xec, 0xcc, + 0x8f, 0xa1, 0x4b, 0xc8, 0xfa, 0x7a, 0x8b, 0x27, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x6a, + 0x74, 0x74, 0x35, 0x02, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PoolCreationFee) > 0 { + for iNdEx := len(m.PoolCreationFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PoolCreationFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -127,6 +228,16 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a if m.NextPoolNumber != 0 { i = encodeVarintGenesis(dAtA, i, uint64(m.NextPoolNumber)) i-- @@ -160,6 +271,21 @@ func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.PoolCreationFee) > 0 { + for _, e := range m.PoolCreationFee { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + func (m *GenesisState) Size() (n int) { if m == nil { return 0 @@ -175,6 +301,8 @@ func (m *GenesisState) Size() (n int) { if m.NextPoolNumber != 0 { n += 1 + sovGenesis(uint64(m.NextPoolNumber)) } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -184,6 +312,93 @@ func sovGenesis(x uint64) (n int) { func sozGenesis(x uint64) (n int) { return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PoolCreationFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PoolCreationFee = append(m.PoolCreationFee, types.Coin{}) + if err := m.PoolCreationFee[len(m.PoolCreationFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *GenesisState) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -242,7 +457,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Pools = append(m.Pools, &types.Any{}) + m.Pools = append(m.Pools, &types1.Any{}) if err := m.Pools[len(m.Pools)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -266,6 +481,39 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/gamm/types/params.go b/x/gamm/types/params.go new file mode 100644 index 0000000000000000000000000000000000000000..59aeef798e320c53b66a31a1935b12446a5cc896 --- /dev/null +++ b/x/gamm/types/params.go @@ -0,0 +1,62 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + appparams "github.com/osmosis-labs/osmosis/app/params" +) + +// Parameter store keys +var ( + KeyPoolCreationFee = []byte("PoolCreationFee") +) + +// ParamTable for gamm module. +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +func NewParams(poolCreationFee sdk.Coins) Params { + return Params{ + PoolCreationFee: poolCreationFee, + } +} + +// default gamm module parameters +func DefaultParams() Params { + return Params{ + PoolCreationFee: sdk.Coins{sdk.NewInt64Coin(appparams.BaseCoinUnit, 1000_000_000)}, // 1000 OSMO + } +} + +// validate params +func (p Params) Validate() error { + if err := validatePoolCreationFee(p.PoolCreationFee); err != nil { + return err + } + + return nil + +} + +// Implements params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyPoolCreationFee, &p.PoolCreationFee, validatePoolCreationFee), + } +} + +func validatePoolCreationFee(i interface{}) error { + v, ok := i.(sdk.Coins) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.Validate() != nil { + return fmt.Errorf("invalid pool creation fee: %+v", i) + } + + return nil +} diff --git a/x/incentives/genesis_test.go b/x/incentives/genesis_test.go index 96d4ad47235b44f244c93b6ebec4f971ef1c6c9d..6a92f10d0341934e5e7e64085e82e89133142055 100644 --- a/x/incentives/genesis_test.go +++ b/x/incentives/genesis_test.go @@ -53,6 +53,9 @@ func TestIncentivesInitGenesis(t *testing.T) { app := simapp.Setup(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + validateGenesis := types.DefaultGenesis().Params.Validate() + require.NoError(t, validateGenesis) + coins := sdk.Coins{sdk.NewInt64Coin("stake", 10000)} startTime := time.Now() distrTo := lockuptypes.QueryCondition{ @@ -87,4 +90,5 @@ func TestIncentivesInitGenesis(t *testing.T) { gauges := app.IncentivesKeeper.GetGauges(ctx) require.Len(t, gauges, 1) require.Equal(t, gauges[0], gauge) + } diff --git a/x/incentives/types/params.go b/x/incentives/types/params.go index e521860dd5e681b472a836cd1e45a103f1604544..bbbd4027b7129b508607b670266fafffc7d68e0a 100644 --- a/x/incentives/types/params.go +++ b/x/incentives/types/params.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + epochtypes "github.com/osmosis-labs/osmosis/x/epochs/types" ) // Parameter store keys @@ -35,7 +36,7 @@ func DefaultParams() Params { // validate params func (p Params) Validate() error { - if err := validateDistrEpochIdentifier(p.DistrEpochIdentifier); err != nil { + if err := epochtypes.ValidateEpochIdentifierInterface(p.DistrEpochIdentifier); err != nil { return err } if err := validateMinAutostakingRate(p.MinAutostakingRate); err != nil { @@ -43,7 +44,6 @@ func (p Params) Validate() error { } return nil - } // Implements params.ParamSet @@ -63,7 +63,6 @@ func validateDistrEpochIdentifier(i interface{}) error { if v == "" { return fmt.Errorf("empty distribution epoch identifier: %+v", i) } - return nil } diff --git a/x/mint/genesis_test.go b/x/mint/genesis_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2b4bafe61f8c3aa9069cb34ad5594ba55f85cee1 --- /dev/null +++ b/x/mint/genesis_test.go @@ -0,0 +1,27 @@ +package mint_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + simapp "github.com/osmosis-labs/osmosis/app" + "github.com/osmosis-labs/osmosis/x/mint/types" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +func TestMintInitGenesis(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + validateGenesis := types.ValidateGenesis(*types.DefaultGenesisState()) + require.NoError(t, validateGenesis) + + developerAccount := app.AccountKeeper.GetModuleAddress(types.DeveloperVestingModuleAcctName) + initialVestingCoins := app.BankKeeper.GetBalance(ctx, developerAccount, sdk.DefaultBondDenom) + + expectedVestingCoins, ok := sdk.NewIntFromString("225000000000000") + require.True(t, ok) + require.Equal(t, expectedVestingCoins, initialVestingCoins.Amount) + require.Equal(t, int64(0), app.MintKeeper.GetLastHalvenEpochNum(ctx)) +} diff --git a/x/mint/types/params.go b/x/mint/types/params.go index 388945ada5862923631617519f5e5aca7a33e3f2..9abec74b04240af4cf7ab485bcd9f07b2f5e5a57 100644 --- a/x/mint/types/params.go +++ b/x/mint/types/params.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + epochtypes "github.com/osmosis-labs/osmosis/x/epochs/types" yaml "gopkg.in/yaml.v2" ) @@ -72,7 +73,7 @@ func (p Params) Validate() error { if err := validateGenesisEpochProvisions(p.GenesisEpochProvisions); err != nil { return err } - if err := validateEpochIdentifier(p.EpochIdentifier); err != nil { + if err := epochtypes.ValidateEpochIdentifierInterface(p.EpochIdentifier); err != nil { return err } if err := validateReductionPeriodInEpochs(p.ReductionPeriodInEpochs); err != nil { @@ -105,7 +106,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ paramtypes.NewParamSetPair(KeyMintDenom, &p.MintDenom, validateMintDenom), paramtypes.NewParamSetPair(KeyGenesisEpochProvisions, &p.GenesisEpochProvisions, validateGenesisEpochProvisions), - paramtypes.NewParamSetPair(KeyEpochIdentifier, &p.EpochIdentifier, validateEpochIdentifier), + paramtypes.NewParamSetPair(KeyEpochIdentifier, &p.EpochIdentifier, epochtypes.ValidateEpochIdentifierInterface), paramtypes.NewParamSetPair(KeyReductionPeriodInEpochs, &p.ReductionPeriodInEpochs, validateReductionPeriodInEpochs), paramtypes.NewParamSetPair(KeyReductionFactor, &p.ReductionFactor, validateReductionFactor), paramtypes.NewParamSetPair(KeyPoolAllocationRatio, &p.DistributionProportions, validateDistributionProportions), @@ -143,19 +144,6 @@ func validateGenesisEpochProvisions(i interface{}) error { return nil } -func validateEpochIdentifier(i interface{}) error { - v, ok := i.(string) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v == "" { - return fmt.Errorf("empty distribution epoch identifier: %+v", i) - } - - return nil -} - func validateReductionPeriodInEpochs(i interface{}) error { v, ok := i.(int64) if !ok { diff --git a/x/pool-incentives/client/cli/query.go b/x/pool-incentives/client/cli/query.go index d91d16d784ea294fb777c7bd948bdde84c218996..a0cb6c049f7a0e7b2eaca2d8bee75e09d334ff4c 100644 --- a/x/pool-incentives/client/cli/query.go +++ b/x/pool-incentives/client/cli/query.go @@ -25,6 +25,10 @@ func GetQueryCmd(queryRoute string) *cobra.Command { cmd.AddCommand( GetCmdGaugeIds(), + GetCmdDistrInfo(), + GetCmdParams(), + GetCmdLockableDurations(), + GetCmdIncentivizedPools(), ) return cmd @@ -73,7 +77,7 @@ $ %s query pool-incentives gauge-ids 1 return cmd } -// GetCmdDistrInfo takes the pool id and returns the matching gauge ids and durations +// GetCmdDistrInfo takes the pool id and returns the matching gauge ids and weights func GetCmdDistrInfo() *cobra.Command { cmd := &cobra.Command{ Use: "distr-info", diff --git a/x/pool-incentives/keeper/keeper_test.go b/x/pool-incentives/keeper/keeper_test.go index d32789e84120619f02de2aef9ec9b8634a1c988d..bdf3229aeb0e8739f07b64c270b29c84cb4722a8 100644 --- a/x/pool-incentives/keeper/keeper_test.go +++ b/x/pool-incentives/keeper/keeper_test.go @@ -52,6 +52,7 @@ func (suite *KeeperTestSuite) preparePoolWithPoolParams(poolParams gammtypes.Poo suite.ctx, acc, sdk.NewCoins( + sdk.NewCoin("uosmo", sdk.NewInt(10000000000)), sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)), sdk.NewCoin("baz", sdk.NewInt(10000000)),