Skip to content

Commit

Permalink
added register rebate tx (#1138)
Browse files Browse the repository at this point in the history
  • Loading branch information
sampocs authored Mar 19, 2024
1 parent 1e04151 commit d0b9578
Show file tree
Hide file tree
Showing 10 changed files with 1,015 additions and 134 deletions.
26 changes: 26 additions & 0 deletions proto/stride/stakeibc/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ service Msg {
returns (MsgDeleteTradeRouteResponse);
rpc UpdateTradeRoute(MsgUpdateTradeRoute)
returns (MsgUpdateTradeRouteResponse);
rpc SetCommunityPoolRebate(MsgSetCommunityPoolRebate)
returns (MsgSetCommunityPoolRebateResponse);
}

message MsgUpdateInnerRedemptionRateBounds {
Expand Down Expand Up @@ -314,3 +316,27 @@ message MsgUpdateTradeRoute {
];
}
message MsgUpdateTradeRouteResponse {}

// Registers or updates a community pool rebate by specifying the amount liquid
// staked
message MsgSetCommunityPoolRebate {
option (cosmos.msg.v1.signer) = "creator";
option (amino.name) = "stride/x/stakeibc/MsgSetCommunityPoolRebate";

// Message signer (admin only)
string creator = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// Chain id of the chain whose community pool has a liquid staking rebate
// arrangement with stride
string chain_id = 2;
// Rebate percentage represented as a decimal (e.g. 0.2 for 20%)
string rebate_rate = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// Number of native tokens staked by the community pool
string liquid_staked_amount = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}
message MsgSetCommunityPoolRebateResponse {}
1 change: 1 addition & 0 deletions x/stakeibc/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(CmdClearBalance())
cmd.AddCommand(CmdUpdateInnerRedemptionRateBounds())
cmd.AddCommand(CmdResumeHostZone())
cmd.AddCommand(CmdSetCommunityPoolRebate())

return cmd
}
61 changes: 61 additions & 0 deletions x/stakeibc/client/cli/tx_set_community_pool_rebate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package cli

import (
"errors"
"fmt"
"strings"

sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"

"github.com/Stride-Labs/stride/v19/x/stakeibc/types"
)

func CmdSetCommunityPoolRebate() *cobra.Command {
cmd := &cobra.Command{
Use: "set-rebate [chain-id] [rebate-rate] [liquid-staked-amount]",
Short: "Registers or updates a community pool rebate",
Long: strings.TrimSpace(`Registers a community pool rebate by specifying the rebate percentage (as a decimal)
and the amount liquid staked.
E.g. to specify a 20% rebate, the rebate rate should be 0.2
If a 0.0 rebate or 0 token liquid stake is specified, the rebate will be deleted.
`),
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) (err error) {
chainId := args[0]
rebatePercentage, err := sdk.NewDecFromStr(args[1])
if err != nil {
return fmt.Errorf("unable to parse rebate percentage: %s", err.Error())
}
liquidStakeAmount, ok := sdkmath.NewIntFromString(args[2])
if !ok {
return errors.New("unable to parse liquid stake amount")
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

msg := types.NewMsgSetCommunityPoolRebate(
clientCtx.GetFromAddress().String(),
chainId,
rebatePercentage,
liquidStakeAmount,
)
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
3 changes: 3 additions & 0 deletions x/stakeibc/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ func NewMessageHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgUpdateTradeRoute:
res, err := msgServer.UpdateTradeRoute(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSetCommunityPoolRebate:
res, err := msgServer.SetCommunityPoolRebate(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
default:
errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg)
return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
Expand Down
28 changes: 28 additions & 0 deletions x/stakeibc/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1103,3 +1103,31 @@ func (k msgServer) ResumeHostZone(goCtx context.Context, msg *types.MsgResumeHos

return &types.MsgResumeHostZoneResponse{}, nil
}

// Registers or updates a community pool rebate, configuring the rebate percentage and liquid stake amount
func (k msgServer) SetCommunityPoolRebate(
goCtx context.Context,
msg *types.MsgSetCommunityPoolRebate,
) (*types.MsgSetCommunityPoolRebateResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

hostZone, found := k.GetHostZone(ctx, msg.ChainId)
if !found {
return nil, types.ErrHostZoneNotFound.Wrapf("host zone %s not found", msg.ChainId)
}

// If a zero rebate is specified, set the rebate to nil
// Otherwise, update the struct
if msg.LiquidStakedAmount.IsZero() || msg.RebateRate.IsZero() {
hostZone.CommunityPoolRebate = nil
} else {
hostZone.CommunityPoolRebate = &types.CommunityPoolRebate{
LiquidStakeAmount: msg.LiquidStakedAmount,
RebatePercentage: msg.RebateRate,
}
}

k.SetHostZone(ctx, hostZone)

return &types.MsgSetCommunityPoolRebateResponse{}, nil
}
45 changes: 45 additions & 0 deletions x/stakeibc/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2575,3 +2575,48 @@ func (s *KeeperTestSuite) TestResumeHostZone_UnhaltedZones() {
expectedErrorMsg := fmt.Sprintf("invalid chain id, zone for %s not halted: host zone is not halted", HostChainId)
s.Require().Equal(expectedErrorMsg, err.Error(), "should return correct error msg")
}

// ----------------------------------------------------
// SetCommunityPoolRebate
// ----------------------------------------------------

func (s *KeeperTestSuite) TestSetCommunityPoolRebate() {
rebateInfo := types.CommunityPoolRebate{
LiquidStakeAmount: sdk.NewInt(1000),
RebatePercentage: sdk.MustNewDecFromStr("0.5"),
}

// Set host zone with no rebate
hostZone := types.HostZone{
ChainId: HostChainId,
}
s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)

// Submit a message to create the rebate
msg := types.MsgSetCommunityPoolRebate{
ChainId: HostChainId,
RebateRate: rebateInfo.RebatePercentage,
LiquidStakedAmount: rebateInfo.LiquidStakeAmount,
}
_, err := s.GetMsgServer().SetCommunityPoolRebate(s.Ctx, &msg)
s.Require().NoError(err, "no error expected when registering rebate")

// Confirm the rebate was updated
actualHostZone := s.MustGetHostZone(HostChainId)
s.Require().Equal(rebateInfo, *actualHostZone.CommunityPoolRebate, "rebate was updated on host zone")

// Submit a 0 LS amount which should delete the rebate
removeMsg := types.MsgSetCommunityPoolRebate{
ChainId: HostChainId,
LiquidStakedAmount: sdk.ZeroInt(),
}
_, err = s.GetMsgServer().SetCommunityPoolRebate(s.Ctx, &removeMsg)
s.Require().NoError(err, "no error expected when registering 0 rebate")

actualHostZone = s.MustGetHostZone(HostChainId)
s.Require().Nil(actualHostZone.CommunityPoolRebate, "rebate was removed from host zone")

// Confirm a message with an invalid chain ID would cause an error
_, err = s.GetMsgServer().SetCommunityPoolRebate(s.Ctx, &types.MsgSetCommunityPoolRebate{ChainId: "invalid"})
s.Require().ErrorContains(err, "host zone not found")
}
5 changes: 2 additions & 3 deletions x/stakeibc/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgCalibrateDelegation{}, "stakeibc/CalibrateDelegation", nil)
cdc.RegisterConcrete(&MsgUpdateInnerRedemptionRateBounds{}, "stakeibc/UpdateInnerRedemptionRateBounds", nil)
cdc.RegisterConcrete(&MsgResumeHostZone{}, "stakeibc/ResumeHostZone", nil)
// this line is used by starport scaffolding # 2
cdc.RegisterConcrete(&MsgSetCommunityPoolRebate{}, "stakeibc/SetCommunityPoolRebate", nil)
}

func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
Expand All @@ -45,15 +45,14 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
&MsgCalibrateDelegation{},
&MsgUpdateInnerRedemptionRateBounds{},
&MsgResumeHostZone{},
&MsgSetCommunityPoolRebate{},
)

registry.RegisterImplementations((*govtypes.Content)(nil),
&AddValidatorsProposal{},
&ToggleLSMProposal{},
)

// this line is used by starport scaffolding # 3

msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}

Expand Down
73 changes: 73 additions & 0 deletions x/stakeibc/types/message_set_community_pool_rebate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package types

import (
"errors"

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

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/Stride-Labs/stride/v19/utils"
)

const TypeMsgSetCommunityPoolRebate = "register_community_pool_rebate"

var _ sdk.Msg = &MsgSetCommunityPoolRebate{}

func NewMsgSetCommunityPoolRebate(
creator string,
chainId string,
rebateRate sdk.Dec,
liquidStakedAmount sdkmath.Int,
) *MsgSetCommunityPoolRebate {
return &MsgSetCommunityPoolRebate{
Creator: creator,
ChainId: chainId,
RebateRate: rebateRate,
LiquidStakedAmount: liquidStakedAmount,
}
}

func (msg *MsgSetCommunityPoolRebate) Route() string {
return RouterKey
}

func (msg *MsgSetCommunityPoolRebate) Type() string {
return TypeMsgSetCommunityPoolRebate
}

func (msg *MsgSetCommunityPoolRebate) GetSigners() []sdk.AccAddress {
creator, err := sdk.AccAddressFromBech32(msg.Creator)
if err != nil {
panic(err)
}
return []sdk.AccAddress{creator}
}

func (msg *MsgSetCommunityPoolRebate) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz)
}

func (msg *MsgSetCommunityPoolRebate) ValidateBasic() error {
_, err := sdk.AccAddressFromBech32(msg.Creator)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err)
}
if err := utils.ValidateAdminAddress(msg.Creator); err != nil {
return err
}
if msg.ChainId == "" {
return errors.New("chain ID must be specified")
}
if msg.RebateRate.IsNil() || msg.RebateRate.LT(sdk.ZeroDec()) || msg.RebateRate.GT(sdk.OneDec()) {
return errors.New("invalid rebate rate, must be a decimal between 0 and 1 (inclusive)")
}
if msg.LiquidStakedAmount.IsNil() || msg.LiquidStakedAmount.LT(sdkmath.ZeroInt()) {
return errors.New("invalid liquid stake amount, must be greater than or equal to zero")
}

return nil
}
Loading

0 comments on commit d0b9578

Please sign in to comment.