Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moves ValidatePermissionlessPoolCreationEnabled out of poolmanager module #6421

Merged
merged 13 commits into from
Sep 24, 2023
7 changes: 7 additions & 0 deletions proto/osmosis/concentrated-liquidity/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@ message Params {
bool is_permissionless_pool_creation_enabled = 6
[ (gogoproto.moretags) =
"yaml:\"is_permissionless_pool_creation_enabled\"" ];

// unrestricted_pool_creator_whitelist is a list of addresses that are
// allowed to bypass restrictions on permissionless supercharged pool
// creation, like pool_creation_enabled, restricted quote assets, no
// double creation of pools, etc.
repeated string unrestricted_pool_creator_whitelist = 7
[ (gogoproto.moretags) = "yaml:\"unrestricted_pool_creator_whitelist\"" ];
}
14 changes: 0 additions & 14 deletions tests/mocks/pool_module.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions x/concentrated-liquidity/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,8 @@ func (k *Keeper) SetListeners(listeners types.ConcentratedLiquidityListeners) *K

// ValidatePermissionlessPoolCreationEnabled returns nil if permissionless pool creation in the module is enabled.
// Otherwise, returns an error.
func (k Keeper) ValidatePermissionlessPoolCreationEnabled(ctx sdk.Context) error {
if !k.GetParams(ctx).IsPermissionlessPoolCreationEnabled {
return types.ErrPermissionlessPoolCreationDisabled
}
return nil
func (k Keeper) ValidatePermissionlessPoolCreationEnabled(ctx sdk.Context) bool {
return !k.GetParams(ctx).IsPermissionlessPoolCreationEnabled
sunnya97 marked this conversation as resolved.
Show resolved Hide resolved
}

// GetAuthorizedQuoteDenoms gets the authorized quote denoms from the poolmanager keeper.
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,15 +433,15 @@ func (s *KeeperTestSuite) AddBlockTime(timeToAdd time.Duration) {
func (s *KeeperTestSuite) TestValidatePermissionlessPoolCreationEnabled() {
// Normally, by default, permissionless pool creation is disabled.
// SetupTest, however, calls SetupConcentratedLiquidityDenomsAndPoolCreation which enables permissionless pool creation.
sunnya97 marked this conversation as resolved.
Show resolved Hide resolved
s.Require().NoError(s.App.ConcentratedLiquidityKeeper.ValidatePermissionlessPoolCreationEnabled(s.Ctx))
s.Require().True(s.App.ConcentratedLiquidityKeeper.ValidatePermissionlessPoolCreationEnabled(s.Ctx))

// Disable permissionless pool creation.
defaultParams := types.DefaultParams()
defaultParams.IsPermissionlessPoolCreationEnabled = false
s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, defaultParams)

// Validate that permissionless pool creation is disabled.
s.Require().Error(s.App.ConcentratedLiquidityKeeper.ValidatePermissionlessPoolCreationEnabled(s.Ctx))
s.Require().False(s.App.ConcentratedLiquidityKeeper.ValidatePermissionlessPoolCreationEnabled(s.Ctx))
}

func (s *KeeperTestSuite) runMultipleAuthorizedUptimes(tests func()) {
Expand Down
37 changes: 31 additions & 6 deletions x/concentrated-liquidity/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,41 @@ func (k Keeper) InitializePool(ctx sdk.Context, poolI poolmanagertypes.PoolI, cr
quoteAsset := concentratedPool.GetToken1()
poolManagerParams := k.poolmanagerKeeper.GetParams(ctx)

if !k.validateTickSpacing(params, tickSpacing) {
return types.UnauthorizedTickSpacingError{ProvidedTickSpacing: tickSpacing, AuthorizedTickSpacings: params.AuthorizedTickSpacing}
bypassRestrictions := false

poolmanagerModuleAcc := k.accountKeeper.GetModuleAccount(ctx, poolmanagertypes.ModuleName).GetAddress()

// allow pool mananger module account to bypass restrictions (i.e. gov prop)
if creatorAddress.Equals(poolmanagerModuleAcc) {
bypassRestrictions = true
}

if !k.validateSpreadFactor(params, spreadFactor) {
return types.UnauthorizedSpreadFactorError{ProvidedSpreadFactor: spreadFactor, AuthorizedSpreadFactors: params.AuthorizedSpreadFactors}
// allow whitelisted pool creators to bypass restrictions
if !bypassRestrictions {
for _, addr := range params.UnrestrictedPoolCreatorWhitelist {
// okay to use MustAccAddressFromBech32 because already validated in params
if sdk.MustAccAddressFromBech32(addr).Equals(creatorAddress) {
bypassRestrictions = true
}
}
}

if !validateAuthorizedQuoteDenoms(quoteAsset, poolManagerParams.AuthorizedQuoteDenoms) {
return types.UnauthorizedQuoteDenomError{ProvidedQuoteDenom: quoteAsset, AuthorizedQuoteDenoms: poolManagerParams.AuthorizedQuoteDenoms}
if !bypassRestrictions {
if !k.ValidatePermissionlessPoolCreationEnabled(ctx) {
return types.ErrPermissionlessPoolCreationDisabled
}

if !k.validateTickSpacing(params, tickSpacing) {
return types.UnauthorizedTickSpacingError{ProvidedTickSpacing: tickSpacing, AuthorizedTickSpacings: params.AuthorizedTickSpacing}
}

if !k.validateSpreadFactor(params, spreadFactor) {
return types.UnauthorizedSpreadFactorError{ProvidedSpreadFactor: spreadFactor, AuthorizedSpreadFactors: params.AuthorizedSpreadFactors}
}

if !validateAuthorizedQuoteDenoms(quoteAsset, poolManagerParams.AuthorizedQuoteDenoms) {
return types.UnauthorizedQuoteDenomError{ProvidedQuoteDenom: quoteAsset, AuthorizedQuoteDenoms: poolManagerParams.AuthorizedQuoteDenoms}
}
}

if err := k.createSpreadRewardAccumulator(ctx, poolId); err != nil {
Expand Down
63 changes: 58 additions & 5 deletions x/concentrated-liquidity/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,42 @@ func (s *KeeperTestSuite) TestInitializePool() {

validCreatorAddress := s.TestAccs[0]

poolmanagerModuleAccount := s.App.AccountKeeper.GetModuleAccount(s.Ctx, poolmanagertypes.ModuleName).GetAddress()

tests := []struct {
name string
poolI poolmanagertypes.PoolI
authorizedDenomsOverwrite []string
creatorAddress sdk.AccAddress
expectedErr error
name string
poolI poolmanagertypes.PoolI
authorizedDenomsOverwrite []string
unrestrictedPoolCreatorWhitelist []string
permissionlessPoolCreationDisabled bool
creatorAddress sdk.AccAddress
expectedErr error
}{
{
name: "Happy path",
poolI: validPoolI,
creatorAddress: validCreatorAddress,
},
{
name: "Permissionless pool creation disabled",
poolI: validPoolI,
permissionlessPoolCreationDisabled: true,
creatorAddress: validCreatorAddress,
expectedErr: types.ErrPermissionlessPoolCreationDisabled,
},
{
name: "bypass disabled permissionless pool creation check because poolmanager module account",
poolI: validPoolI,
permissionlessPoolCreationDisabled: true,
creatorAddress: poolmanagerModuleAccount,
},
{
name: "bypass disabled permissionless pool creation check because of whitelisted bypass",
poolI: validPoolI,
permissionlessPoolCreationDisabled: true,
creatorAddress: validCreatorAddress,
unrestrictedPoolCreatorWhitelist: []string{validCreatorAddress.String()},
},
{
name: "Wrong pool type: empty pool interface that doesn't implement ConcentratedPoolExtension",
poolI: invalidPoolI,
Expand Down Expand Up @@ -79,18 +103,47 @@ func (s *KeeperTestSuite) TestInitializePool() {
creatorAddress: validCreatorAddress,
expectedErr: types.UnauthorizedQuoteDenomError{ProvidedQuoteDenom: USDC, AuthorizedQuoteDenoms: []string{"otherDenom"}},
},
{
name: "bypass unauthorized quote denom check because poolmanager module account",
poolI: validPoolI,
authorizedDenomsOverwrite: []string{"otherDenom"},
// despite the quote denom not being authorized, will still
// pass because its coming from the poolmanager module account
creatorAddress: poolmanagerModuleAccount,
},
{
name: "bypass unauthorized quote denom check because of whitelisted bypass",
poolI: validPoolI,
authorizedDenomsOverwrite: []string{"otherDenom"},
// despite the quote denom not being authorized, will still
// pass because its coming from a whitelisted pool creator
unrestrictedPoolCreatorWhitelist: []string{validCreatorAddress.String()},
creatorAddress: validCreatorAddress,
},
}

for _, test := range tests {
s.Run(test.name, func() {
s.SetupTest()

if test.permissionlessPoolCreationDisabled {
params := s.App.ConcentratedLiquidityKeeper.GetParams(s.Ctx)
params.IsPermissionlessPoolCreationEnabled = false
s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, params)
}

if len(test.authorizedDenomsOverwrite) > 0 {
params := s.App.PoolManagerKeeper.GetParams(s.Ctx)
params.AuthorizedQuoteDenoms = test.authorizedDenomsOverwrite
s.App.PoolManagerKeeper.SetParams(s.Ctx, params)
}

if len(test.unrestrictedPoolCreatorWhitelist) > 0 {
params := s.App.ConcentratedLiquidityKeeper.GetParams(s.Ctx)
params.UnrestrictedPoolCreatorWhitelist = test.unrestrictedPoolCreatorWhitelist
s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, params)
}

s.setListenerMockOnConcentratedLiquidityKeeper()

// Method under test.
Expand Down
3 changes: 2 additions & 1 deletion x/concentrated-liquidity/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@ var (
}
DefaultBalancerSharesDiscount = osmomath.MustNewDecFromStr("0.05")
// By default, we only authorize one nanosecond (one block) uptime as an option
DefaultAuthorizedUptimes = []time.Duration{time.Nanosecond}
DefaultAuthorizedUptimes = []time.Duration{time.Nanosecond}
DefaultUnrestrictedPoolCreatorWhitelist = []string{}
)
33 changes: 32 additions & 1 deletion x/concentrated-liquidity/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var (
KeyAuthorizedQuoteDenoms = []byte("AuthorizedQuoteDenoms")
KeyAuthorizedUptimes = []byte("AuthorizedUptimes")
KeyIsPermisionlessPoolCreationEnabled = []byte("IsPermisionlessPoolCreationEnabled")
KeyUnrestrictedPoolCreatorWhitelist = []byte("UnrestrictedPoolCreatorWhitelist")

_ paramtypes.ParamSet = &Params{}
)
Expand All @@ -27,14 +28,15 @@ func ParamKeyTable() paramtypes.KeyTable {
return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
}

func NewParams(authorizedTickSpacing []uint64, authorizedSpreadFactors []osmomath.Dec, discountRate osmomath.Dec, authorizedQuoteDenoms []string, authorizedUptimes []time.Duration, isPermissionlessPoolCreationEnabled bool) Params {
func NewParams(authorizedTickSpacing []uint64, authorizedSpreadFactors []osmomath.Dec, discountRate osmomath.Dec, authorizedQuoteDenoms []string, authorizedUptimes []time.Duration, isPermissionlessPoolCreationEnabled bool, unrestrictedPoolCreatorWhitelist []string) Params {
return Params{
AuthorizedTickSpacing: authorizedTickSpacing,
AuthorizedSpreadFactors: authorizedSpreadFactors,
AuthorizedQuoteDenoms: authorizedQuoteDenoms,
BalancerSharesRewardDiscount: discountRate,
AuthorizedUptimes: authorizedUptimes,
IsPermissionlessPoolCreationEnabled: isPermissionlessPoolCreationEnabled,
UnrestrictedPoolCreatorWhitelist: unrestrictedPoolCreatorWhitelist,
}
}

Expand All @@ -52,6 +54,7 @@ func DefaultParams() Params {
BalancerSharesRewardDiscount: DefaultBalancerSharesDiscount,
AuthorizedUptimes: DefaultAuthorizedUptimes,
IsPermissionlessPoolCreationEnabled: false,
UnrestrictedPoolCreatorWhitelist: DefaultUnrestrictedPoolCreatorWhitelist,
}
}

Expand All @@ -75,6 +78,9 @@ func (p Params) Validate() error {
if err := validateAuthorizedUptimes(p.AuthorizedUptimes); err != nil {
return err
}
if err := validateUnrestrictedPoolCreatorWhitelist(p.UnrestrictedPoolCreatorWhitelist); err != nil {
return err
}
return nil
}

Expand All @@ -87,6 +93,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
paramtypes.NewParamSetPair(KeyIsPermisionlessPoolCreationEnabled, &p.IsPermissionlessPoolCreationEnabled, validateIsPermissionLessPoolCreationEnabled),
paramtypes.NewParamSetPair(KeyDiscountRate, &p.BalancerSharesRewardDiscount, validateBalancerSharesDiscount),
paramtypes.NewParamSetPair(KeyAuthorizedUptimes, &p.AuthorizedUptimes, validateAuthorizedUptimes),
paramtypes.NewParamSetPair(KeyUnrestrictedPoolCreatorWhitelist, &p.UnrestrictedPoolCreatorWhitelist, validateUnrestrictedPoolCreatorWhitelist),
}
}

Expand Down Expand Up @@ -235,3 +242,27 @@ func validateAuthorizedUptimes(i interface{}) error {

return nil
}

// validateUnrestrictedPoolCreatorWhitelist validates a slice of addresses
// that are allowed to bypass the restrictions on permissionless pool creation
//
// Parameters:
// - i: The parameter to validate.
//
// Returns:
// - An error if any of the strings are not addresses
func validateUnrestrictedPoolCreatorWhitelist(i interface{}) error {
whitelist, ok := i.([]string)

if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}

for _, a := range whitelist {
if _, err := sdk.AccAddressFromBech32(a); err != nil {
return fmt.Errorf("invalid address")
}
}

return nil
}
Loading