Skip to content

Commit

Permalink
Protorev: Post Handler + Proposal Handlers + Profit Sharing (#3806)
Browse files Browse the repository at this point in the history
* new stores for admin and developer functionality + epoch fully implemented

* testing for new store setters and getters + small update to stores

* adding proposal handlers for protorev + test suite

* implementing the developer fee split + testing

* init for post handler

testing updates

nit

* off by one edge case for fee splitting

* Add posthandler functionality and tests

Remove checkTx skip

linting update after posthandler addition

* rebasing and updating routes to swaprouter types

* rebase, logging, clean up of proposal cli

* constraining protorev by # routes traversed

* nit

* point system for route building

* nit + posthandler checkTx update

* configurable route weights

* rebase

* PR updates, moving constants to new file

* rebase

* additional comments for execution bounding

Co-authored-by: Jeremy Liu <[email protected]>
  • Loading branch information
davidterpay and NotJeremyLiu authored Jan 11, 2023
1 parent c3580f6 commit 6d668bf
Show file tree
Hide file tree
Showing 30 changed files with 2,341 additions and 222 deletions.
3 changes: 1 addition & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,7 @@ func NewOsmosisApp(
app.IBCKeeper,
),
)
// Uncomment to enable postHandlers:
// app.SetPostHandler(NewTxPostHandler())
app.SetPostHandler(NewPostHandler(app.ProtoRevKeeper))
app.SetEndBlocker(app.EndBlocker)

// Register snapshot extensions to enable state-sync for wasm.
Expand Down
4 changes: 3 additions & 1 deletion app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
ibcratelimittypes "github.com/osmosis-labs/osmosis/v14/x/ibc-rate-limit/types"
"github.com/osmosis-labs/osmosis/v14/x/poolmanager"
poolmanagertypes "github.com/osmosis-labs/osmosis/v14/x/poolmanager/types"
"github.com/osmosis-labs/osmosis/v14/x/protorev"
ibchooks "github.com/osmosis-labs/osmosis/x/ibc-hooks"
ibchookskeeper "github.com/osmosis-labs/osmosis/x/ibc-hooks/keeper"
ibchookstypes "github.com/osmosis-labs/osmosis/x/ibc-hooks/types"
Expand Down Expand Up @@ -425,7 +426,8 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
AddRoute(ibchost.RouterKey, ibcclient.NewClientProposalHandler(appKeepers.IBCKeeper.ClientKeeper)).
AddRoute(poolincentivestypes.RouterKey, poolincentives.NewPoolIncentivesProposalHandler(*appKeepers.PoolIncentivesKeeper)).
AddRoute(txfeestypes.RouterKey, txfees.NewUpdateFeeTokenProposalHandler(*appKeepers.TxFeesKeeper)).
AddRoute(superfluidtypes.RouterKey, superfluid.NewSuperfluidProposalHandler(*appKeepers.SuperfluidKeeper, *appKeepers.EpochsKeeper, *appKeepers.GAMMKeeper))
AddRoute(superfluidtypes.RouterKey, superfluid.NewSuperfluidProposalHandler(*appKeepers.SuperfluidKeeper, *appKeepers.EpochsKeeper, *appKeepers.GAMMKeeper)).
AddRoute(protorevtypes.RouterKey, protorev.NewProtoRevProposalHandler(*appKeepers.ProtoRevKeeper))

// The gov proposal types can be individually enabled
if len(wasmEnabledProposals) != 0 {
Expand Down
12 changes: 12 additions & 0 deletions app/posthandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app

import (
sdk "github.com/cosmos/cosmos-sdk/types"

protorevkeeper "github.com/osmosis-labs/osmosis/v14/x/protorev/keeper"
)

func NewPostHandler(protoRevKeeper *protorevkeeper.Keeper) sdk.AnteHandler {
protoRevDecorator := protorevkeeper.NewProtoRevDecorator(*protoRevKeeper)
return sdk.ChainAnteDecorators(protoRevDecorator)
}
9 changes: 0 additions & 9 deletions app/tx_post_handler.go

This file was deleted.

11 changes: 11 additions & 0 deletions proto/osmosis/protorev/v1beta1/protorev.proto
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,15 @@ message PoolStatistics {
];
// pool_id is the id of the pool
uint64 pool_id = 3;
}

// RouteWeights contains the weights of all of the different route types. Routes
// are broken up into different types based on the pool that is sandwiched in
// between the arbitrage route. This distinction is made and necessary because
// the execution time ranges fairly between the different route types.
message RouteWeights {
// The weight of a route that includes a stableswap pool
uint64 stable_weight = 1;
// The weight of a route that includes a balancer pool
uint64 balancer_weight = 2;
}
133 changes: 133 additions & 0 deletions x/protorev/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package cli

import (
"fmt"
"strconv"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/gov/client/cli"
"github.com/spf13/cobra"

govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"

"github.com/osmosis-labs/osmosis/osmoutils/osmocli"
"github.com/osmosis-labs/osmosis/v14/x/protorev/types"

sdk "github.com/cosmos/cosmos-sdk/types"
)

func NewCmdTx() *cobra.Command {
txCmd := osmocli.TxIndexCmd(types.ModuleName)
txCmd.AddCommand(
CmdSetProtoRevAdminAccountProposal(),
CmdSetProtoRevEnabledProposal(),
)
return txCmd
}

// CmdSetProtoRevAdminAccountProposal implements the command to submit a SetProtoRevAdminAccountProposal
func CmdSetProtoRevAdminAccountProposal() *cobra.Command {
cmd := &cobra.Command{
Use: "set-protorev-admin-account-proposal [sdk.AccAddress]",
Args: cobra.ExactArgs(1),
Short: "submit a set protorev admin account proposal to set the admin account for x/protorev",
Example: fmt.Sprintf(`$ %s tx protorev set-protorev-admin-account osmo123... --from mykey`, version.AppName),
RunE: func(cmd *cobra.Command, args []string) error {
createContent := func(title string, description string, args ...string) (govtypes.Content, error) {
return types.NewSetProtoRevAdminAccountProposal(title, description, args[0]), nil
}

return ProposalExecute(cmd, args, createContent)
},
}

cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "deposit of proposal")
flags.AddTxFlagsToCmd(cmd)
_ = cmd.MarkFlagRequired(cli.FlagTitle)
_ = cmd.MarkFlagRequired(cli.FlagDescription)

return cmd
}

// CmdSetProtoRevEnabledProposal implements the command to submit a SetProtoRevEnabledProposal
func CmdSetProtoRevEnabledProposal() *cobra.Command {
cmd := &cobra.Command{
Use: "set-protorev-enabled-proposal [boolean]",
Args: cobra.ExactArgs(1),
Short: "submit a set protorev enabled proposal to enable or disable the protocol",
Example: fmt.Sprintf(`$ %s tx protorev set-protorev-enabled true --from mykey`, version.AppName),
RunE: func(cmd *cobra.Command, args []string) error {
createContent := func(title string, description string, args ...string) (govtypes.Content, error) {
res, err := strconv.ParseBool(args[0])
if err != nil {
return nil, err
}

content := types.NewSetProtoRevEnabledProposal(title, description, res)
return content, nil
}

return ProposalExecute(cmd, args, createContent)
},
}

cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
cmd.Flags().String(cli.FlagDeposit, "", "deposit of proposal")
flags.AddTxFlagsToCmd(cmd)
_ = cmd.MarkFlagRequired(cli.FlagTitle)
_ = cmd.MarkFlagRequired(cli.FlagDescription)

return cmd
}

// ProposalExecute is a helper function to execute a proposal command. It takes in a function to create the proposal content.
func ProposalExecute(cmd *cobra.Command, args []string, createContent func(title string, description string, args ...string) (govtypes.Content, error)) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

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

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

depositStr, err := cmd.Flags().GetString(cli.FlagDeposit)
if err != nil {
return err
}

deposit, err := sdk.ParseCoinsNormalized(depositStr)
if err != nil {
return err
}

from := clientCtx.GetFromAddress()

content, err := createContent(title, description, args...)
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)
}
28 changes: 27 additions & 1 deletion x/protorev/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,34 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState)
panic(err)
}

// Init module parameters
// Init module state
k.SetParams(ctx, genState.Params)
k.SetProtoRevEnabled(ctx, genState.Params.Enabled)
k.SetDaysSinceModuleGenesis(ctx, 0)
k.SetLatestBlockHeight(ctx, uint64(ctx.BlockHeight()))
k.SetRouteCountForBlock(ctx, 0)

// Configure max routes per block. This roughly correlates to the ms of execution time protorev will
// take per block
if err := k.SetMaxRoutesPerBlock(ctx, 100); err != nil {
panic(err)
}

// Configure max routes per tx. This roughly correlates to the ms of execution time protorev will take
// per tx
if err := k.SetMaxRoutesPerTx(ctx, 6); err != nil {
panic(err)
}

// Configure the route weights for genesis. This roughly correlates to the ms of execution time
// by route type
routeWeights := types.RouteWeights{
StableWeight: 5, // it takes around 5 ms to execute a stable swap route
BalancerWeight: 2, // it takes around 2 ms to execute a balancer swap route
}
if err := k.SetRouteWeights(ctx, routeWeights); err != nil {
panic(err)
}

// Update the pools on genesis
if err := k.UpdatePools(ctx); err != nil {
Expand Down
61 changes: 61 additions & 0 deletions x/protorev/keeper/developer_fees.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/osmosis-labs/osmosis/v14/x/protorev/types"
)

// SendDeveloperFeesToDeveloperAccount sends the developer fees from the module account to the developer account
func (k Keeper) SendDeveloperFeesToDeveloperAccount(ctx sdk.Context) error {
// Developer account must be set in order to be able to withdraw developer fees
developerAccount, err := k.GetDeveloperAccount(ctx)
if err != nil {
return err
}

coins := k.GetAllDeveloperFees(ctx)

for _, coin := range coins {
// Send the coins to the developer account
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, developerAccount, sdk.NewCoins(coin)); err != nil {
return err
}

// Reset the developer fees for the coin
k.DeleteDeveloperFees(ctx, coin.Denom)
}

return nil
}

// UpdateDeveloperFees updates the fees that developers can withdraw from the module account
func (k Keeper) UpdateDeveloperFees(ctx sdk.Context, denom string, profit sdk.Int) error {
daysSinceGenesis, err := k.GetDaysSinceModuleGenesis(ctx)
if err != nil {
return err
}

// Calculate the developer fee
if daysSinceGenesis < types.Phase1Length {
profit = profit.MulRaw(types.ProfitSplitPhase1).QuoRaw(100)
} else if daysSinceGenesis < types.Phase2Length {
profit = profit.MulRaw(types.ProfitSplitPhase2).QuoRaw(100)
} else {
profit = profit.MulRaw(types.ProfitSplitPhase3).QuoRaw(100)
}

// Get the developer fees for the denom, if not there then set it to 0 and initialize it
currentDeveloperFee, err := k.GetDeveloperFees(ctx, denom)
if err != nil {
currentDeveloperFee = sdk.NewCoin(denom, sdk.ZeroInt())
}
currentDeveloperFee.Amount = currentDeveloperFee.Amount.Add(profit)

// Set the developer fees for the denom
if err = k.SetDeveloperFees(ctx, currentDeveloperFee); err != nil {
return err
}

return nil
}
Loading

0 comments on commit 6d668bf

Please sign in to comment.