Skip to content

Commit

Permalink
feat: add SetScalingFactorController gov prop (#5937)
Browse files Browse the repository at this point in the history
* added SetScalingFactorControllerProposal to protos

* implemented gov content type

* added setStableSwapScalingFactorController keeper

* added unit test for setStableSwapScalingFactorController

* wired up handler

* added changelog entry

* Update x/gamm/handler.go

Co-authored-by: Adam Tucker <[email protected]>

* added cli command

* shortened message name for ledger support

---------

Co-authored-by: Adam Tucker <[email protected]>
  • Loading branch information
sampocs and czarcas7ic authored Aug 5, 2023
1 parent 7bbcc9f commit e5b8273
Show file tree
Hide file tree
Showing 16 changed files with 688 additions and 55 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#5883](https://github.com/osmosis-labs/osmosis/pull/5883) feat: Uninitialize empty ticks
* [#5874](https://github.com/osmosis-labs/osmosis/pull/5874) Remove Partial Migration from superfluid migration to CL
* [#5901](https://github.com/osmosis-labs/osmosis/pull/5901) Adding support for CW pools in ProtoRev
* [#5937](https://github.com/osmosis-labs/osmosis/pull/5937) feat: add SetScalingFactorController gov prop

### BugFix

Expand Down
2 changes: 1 addition & 1 deletion app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
AddRoute(txfeestypes.RouterKey, txfees.NewUpdateFeeTokenProposalHandler(*appKeepers.TxFeesKeeper)).
AddRoute(superfluidtypes.RouterKey, superfluid.NewSuperfluidProposalHandler(*appKeepers.SuperfluidKeeper, *appKeepers.EpochsKeeper, *appKeepers.GAMMKeeper)).
AddRoute(protorevtypes.RouterKey, protorev.NewProtoRevProposalHandler(*appKeepers.ProtoRevKeeper)).
AddRoute(gammtypes.RouterKey, gamm.NewMigrationRecordHandler(*appKeepers.GAMMKeeper)).
AddRoute(gammtypes.RouterKey, gamm.NewGammProposalHandler(*appKeepers.GAMMKeeper)).
AddRoute(concentratedliquiditytypes.RouterKey, concentratedliquidity.NewConcentratedLiquidityProposalHandler(*appKeepers.ConcentratedLiquidityKeeper)).
AddRoute(cosmwasmpooltypes.RouterKey, cosmwasmpool.NewCosmWasmPoolProposalHandler(*appKeepers.CosmwasmPoolKeeper))

Expand Down
1 change: 1 addition & 0 deletions app/keepers/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ var AppModuleBasics = []module.AppModuleBasic{
gammclient.ReplaceMigrationRecordsProposalHandler,
gammclient.UpdateMigrationRecordsProposalHandler,
gammclient.CreateCLPoolAndLinkToCFMMProposalHandler,
gammclient.SetScalingFactorControllerProposalHandler,
clclient.CreateConcentratedLiquidityPoolProposalHandler,
clclient.TickSpacingDecreaseProposalHandler,
cwpoolclient.UploadCodeIdAndWhitelistProposalHandler,
Expand Down
16 changes: 15 additions & 1 deletion proto/osmosis/gamm/v1beta1/gov.proto
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ message UpdateMigrationRecordsProposal {
repeated BalancerToConcentratedPoolLink records = 3
[ (gogoproto.nullable) = false ];
}

message PoolRecordWithCFMMLink {
option (gogoproto.equal) = true;

Expand Down Expand Up @@ -96,4 +95,19 @@ message CreateConcentratedLiquidityPoolsAndLinktoCFMMProposal {
(gogoproto.moretags) = "yaml:\"create_cl_pool_and_link_to_cfmm\"",
(gogoproto.nullable) = false
];
}

// SetScalingFactorControllerProposal is a gov Content type for updating the
// scaling factor controller address of a stableswap pool
message SetScalingFactorControllerProposal {
option (gogoproto.equal) = true;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
option (amino.name) = "osmosis/SetScalingFactorControllerProposal";
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";

string title = 1;
string description = 2;
uint64 pool_id = 3;
string controller_address = 4;
}
3 changes: 2 additions & 1 deletion x/gamm/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const (
// Will be parsed to []string.
FlagSwapRouteDenoms = "swap-route-denoms"
// FlagScalingFactors represents the flag name for the scaling factors.
FlagScalingFactors = "scaling-factors"
FlagScalingFactors = "scaling-factors"
FlagScalingFactorControllerAddress = "scaling-factor-controller-address"

FlagMigrationRecords = "migration-records"

Expand Down
123 changes: 123 additions & 0 deletions x/gamm/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cli

import (
"encoding/json"
"errors"
"fmt"
"os"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -346,6 +348,78 @@ func NewCmdSubmitCreateCLPoolAndLinkToCFMMProposal() *cobra.Command {
return cmd
}

// NewCmdSubmitSetScalingFactorControllerProposal implements a command handler for the set scaling factor controller proposal
func NewCmdSubmitSetScalingFactorControllerProposal() *cobra.Command {
cmd := &cobra.Command{
Use: "set-scaling-factor-controller-proposal [flags]",
Args: cobra.ExactArgs(0),
Short: "Submit a set scaling factor controller proposal",
Long: strings.TrimSpace(`Submit a set scaling factor controller proposal.
Sample proposal file:
{
"title": "Set Scaling Factor Controller Proposal",
"description": "Change scaling factor controller address from osmoXXX to osmoYYY"
"pool-id": 1,
"controller-address": "osmoYYY"
}
>>> osmosisd tx gov submit-proposal set-scaling-factor-controller-proposal \
--proposal proposal.json \
--deposit 1600000000uosmo \
Sample proposal with flags
>>> osmosisd tx gov submit-proposal set-scaling-factor-controller-proposal \
--title "Set Scaling Factor Controller Proposal" \
--description "Change scaling factor controller address from osmoXXX to osmoYYY"
--deposit 1600000000uosmo
--pool-id 1
--controller-address osmoYYY
`),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
content, err := parseSetScalingFactorControllerArgsToContent(cmd)
if err != nil {
return err
}

from := clientCtx.GetFromAddress()

depositStr, err := cmd.Flags().GetString(govcli.FlagDeposit)
if err != nil {
return err
}
deposit, err := sdk.ParseCoinsNormalized(depositStr)
if err != nil {
return err
}

msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from)
if err != nil {
return err
}

if err = msg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

cmd.Flags().String(govcli.FlagTitle, "", "title of proposal")
cmd.Flags().String(govcli.FlagDescription, "", "description of proposal")
cmd.Flags().String(govcli.FlagDeposit, "", "deposit of proposal")
cmd.Flags().Uint64(FlagPoolId, 0, "stableswap pool-id")
cmd.Flags().String(FlagScalingFactorControllerAddress, "", "target scaling factor controller address")
cmd.Flags().Bool(govcli.FlagIsExpedited, false, "If true, makes the proposal an expedited one")
cmd.Flags().String(govcli.FlagProposal, "", "Proposal file path (if this path is given, other proposal flags are ignored)")

return cmd
}

func BuildCreatePoolCmd(clientCtx client.Context, args []string, fs *flag.FlagSet) (sdk.Msg, error) {
poolType, err := fs.GetString(FlagPoolType)
if err != nil {
Expand Down Expand Up @@ -846,3 +920,52 @@ func parsePoolRecordsWithCFMMLink(cmd *cobra.Command) ([]types.PoolRecordWithCFM

return finalPoolRecords, nil
}

func parseSetScalingFactorControllerArgsToContent(cmd *cobra.Command) (govtypes.Content, error) {
proposalFile, err := cmd.Flags().GetString(govcli.FlagProposal)
if err != nil {
return nil, err
}

if proposalFile != "" {
contents, err := os.ReadFile(proposalFile)
if err != nil {
return nil, err
}

var proposal types.SetScalingFactorControllerProposal
if err := json.Unmarshal(contents, &proposal); err != nil {
return nil, err
}
return &proposal, nil
}

title, err := cmd.Flags().GetString(govcli.FlagTitle)
if err != nil {
return nil, err
}

description, err := cmd.Flags().GetString(govcli.FlagDescription)
if err != nil {
return nil, err
}

poolId, err := cmd.Flags().GetUint64(FlagPoolId)
if err != nil {
return nil, err
}

controllerAddress, err := cmd.Flags().GetString(FlagScalingFactorControllerAddress)
if err != nil {
return nil, err
}

content := &types.SetScalingFactorControllerProposal{
Title: title,
Description: description,
PoolId: poolId,
ControllerAddress: controllerAddress,
}

return content, nil
}
7 changes: 4 additions & 3 deletions x/gamm/client/proposal_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
)

var (
ReplaceMigrationRecordsProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitReplaceMigrationRecordsProposal, rest.ProposalReplaceMigrationRecordsRESTHandler)
UpdateMigrationRecordsProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpdateMigrationRecordsProposal, rest.ProposalUpdateMigrationRecordsRESTHandler)
CreateCLPoolAndLinkToCFMMProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitCreateCLPoolAndLinkToCFMMProposal, rest.ProposalCreateConcentratedLiquidityPoolAndLinkToCFMMHandler)
ReplaceMigrationRecordsProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitReplaceMigrationRecordsProposal, rest.ProposalReplaceMigrationRecordsRESTHandler)
UpdateMigrationRecordsProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpdateMigrationRecordsProposal, rest.ProposalUpdateMigrationRecordsRESTHandler)
CreateCLPoolAndLinkToCFMMProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitCreateCLPoolAndLinkToCFMMProposal, rest.ProposalCreateConcentratedLiquidityPoolAndLinkToCFMMHandler)
SetScalingFactorControllerProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitSetScalingFactorControllerProposal, rest.ProposalSetScalingFactorController)
)
7 changes: 7 additions & 0 deletions x/gamm/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ func ProposalCreateConcentratedLiquidityPoolAndLinkToCFMMHandler(clientCtx clien
}
}

func ProposalSetScalingFactorController(clientCtx client.Context) govrest.ProposalRESTHandler {
return govrest.ProposalRESTHandler{
SubRoute: "set-scaling-factor-controller",
Handler: emptyHandler(clientCtx),
}
}

func emptyHandler(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
}
Expand Down
12 changes: 10 additions & 2 deletions x/gamm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"github.com/osmosis-labs/osmosis/v17/x/gamm/types"
)

// NewMigrationRecordHandler is a handler for governance proposals on new migration records.
func NewMigrationRecordHandler(k keeper.Keeper) govtypes.Handler {
// NewGammProposalHandler is a handler for governance proposals for the GAMM module.
func NewGammProposalHandler(k keeper.Keeper) govtypes.Handler {
return func(ctx sdk.Context, content govtypes.Content) error {
switch c := content.(type) {
case *types.UpdateMigrationRecordsProposal:
Expand All @@ -21,6 +21,8 @@ func NewMigrationRecordHandler(k keeper.Keeper) govtypes.Handler {
return handleReplaceMigrationRecordsProposal(ctx, k, c)
case *types.CreateConcentratedLiquidityPoolsAndLinktoCFMMProposal:
return handleCreatingCLPoolAndLinkToCFMMProposal(ctx, k, c)
case *types.SetScalingFactorControllerProposal:
return handleSetScalingFactorControllerProposal(ctx, k, c)

default:
return errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized migration record proposal content type: %T", c)
Expand All @@ -47,3 +49,9 @@ func handleCreatingCLPoolAndLinkToCFMMProposal(ctx sdk.Context, k keeper.Keeper,
}
return nil
}

// handleSetScalingFactorControllerProposal is a handler for gov proposals to set a stableswap pool's
// scaling factor controller address
func handleSetScalingFactorControllerProposal(ctx sdk.Context, k keeper.Keeper, p *types.SetScalingFactorControllerProposal) error {
return k.HandleSetScalingFactorControllerProposal(ctx, p)
}
4 changes: 4 additions & 0 deletions x/gamm/keeper/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func (k Keeper) SetStableSwapScalingFactors(ctx sdk.Context, poolId uint64, scal
return k.setStableSwapScalingFactors(ctx, poolId, scalingFactors, sender)
}

func (k Keeper) SetStableSwapScalingFactorController(ctx sdk.Context, poolId uint64, controllerAddress string) error {
return k.setStableSwapScalingFactorController(ctx, poolId, controllerAddress)
}

func AsCFMMPool(pool poolmanagertypes.PoolI) (types.CFMMPoolI, error) {
return asCFMMPool(pool)
}
Expand Down
4 changes: 4 additions & 0 deletions x/gamm/keeper/gov.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ func (k Keeper) HandleReplaceMigrationRecordsProposal(ctx sdk.Context, p *types.
func (k Keeper) HandleUpdateMigrationRecordsProposal(ctx sdk.Context, p *types.UpdateMigrationRecordsProposal) error {
return k.UpdateMigrationRecords(ctx, p.Records)
}

func (k Keeper) HandleSetScalingFactorControllerProposal(ctx sdk.Context, p *types.SetScalingFactorControllerProposal) error {
return k.setStableSwapScalingFactorController(ctx, p.PoolId, p.ControllerAddress)
}
17 changes: 17 additions & 0 deletions x/gamm/keeper/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,23 @@ func (k Keeper) setStableSwapScalingFactors(ctx sdk.Context, poolId uint64, scal
return k.setPool(ctx, stableswapPool)
}

// setStableSwapScalingFactorController updates the scaling factor controller address for a stable swap pool
// errors if the pool does not exist or is not a stable swap pool
func (k Keeper) setStableSwapScalingFactorController(ctx sdk.Context, poolId uint64, controllerAddress string) error {
pool, err := k.GetPoolAndPoke(ctx, poolId)
if err != nil {
return err
}
stableswapPool, ok := pool.(*stableswap.Pool)
if !ok {
return fmt.Errorf("pool id %d is not of type stableswap pool", poolId)
}

stableswapPool.ScalingFactorController = controllerAddress

return k.setPool(ctx, stableswapPool)
}

// asCFMMPool converts PoolI to CFMMPoolI by casting the input.
// Returns the pool of the CFMMPoolI or error if the given pool does not implement
// CFMMPoolI.
Expand Down
77 changes: 77 additions & 0 deletions x/gamm/keeper/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,83 @@ func (s *KeeperTestSuite) TestSetStableSwapScalingFactors() {
}
}

func (s *KeeperTestSuite) TestSetStableSwapScalingFactorController() {
initialControllerAddr := s.TestAccs[0].String()
updatedControllerAddr := s.TestAccs[1].String()

testcases := []struct {
name string
poolId uint64
expError error
isStableSwapPool bool
}{
{
name: "Error: Pool does not exist",
poolId: 2,
expError: types.PoolDoesNotExistError{PoolId: defaultPoolId + 1},
isStableSwapPool: false,
},
{
name: "Error: Pool id is not of type stableswap pool",
poolId: 1,
expError: fmt.Errorf("pool id 1 is not of type stableswap pool"),
isStableSwapPool: false,
},
{
name: "Valid case",
poolId: 1,
isStableSwapPool: true,
},
}
for _, tc := range testcases {
s.Run(tc.name, func() {
s.SetupTest()
if tc.isStableSwapPool == true {
poolId := s.prepareCustomStableswapPool(
defaultAcctFunds,
stableswap.PoolParams{
SwapFee: defaultSpreadFactor,
ExitFee: defaultZeroExitFee,
},
sdk.NewCoins(sdk.NewCoin(defaultAcctFunds[0].Denom, defaultAcctFunds[0].Amount.QuoRaw(2)), sdk.NewCoin(defaultAcctFunds[1].Denom, defaultAcctFunds[1].Amount.QuoRaw(2))),
[]uint64{1, 1},
)
pool, _ := s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, poolId)
stableswapPool, _ := pool.(*stableswap.Pool)
stableswapPool.ScalingFactorController = initialControllerAddr
err := s.App.GAMMKeeper.SetPool(s.Ctx, stableswapPool)
s.Require().NoError(err)

// attempt to adjust the scaling factor from the new address - it should fail
err = s.App.GAMMKeeper.SetStableSwapScalingFactors(s.Ctx, tc.poolId, []uint64{1, 2}, updatedControllerAddr)
s.Require().ErrorIs(err, types.ErrNotScalingFactorGovernor)
} else {
s.prepareCustomBalancerPool(
defaultAcctFunds,
defaultPoolAssets,
defaultPoolParams)
}

err := s.App.GAMMKeeper.SetStableSwapScalingFactorController(s.Ctx, tc.poolId, updatedControllerAddr)
if tc.expError != nil {
s.Require().Error(err)
s.Require().EqualError(err, tc.expError.Error())
} else {
s.Require().NoError(err)

// confirm the scaling factor controller has been updated
pool, _ := s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, tc.poolId)
stableswapPool, _ := pool.(*stableswap.Pool)
s.Require().Equal(updatedControllerAddr, stableswapPool.ScalingFactorController)

// confirm the new controller can update the scaling factor
err = s.App.GAMMKeeper.SetStableSwapScalingFactors(s.Ctx, tc.poolId, []uint64{1, 2}, updatedControllerAddr)
s.Require().NoError(err)
}
})
}
}

func (suite *KeeperTestSuite) TestGetMaximalNoSwapLPAmount() {
tests := map[string]struct {
poolId uint64
Expand Down
Loading

0 comments on commit e5b8273

Please sign in to comment.