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

add inner bounds #938

Merged
merged 14 commits into from
Sep 18, 2023
10 changes: 10 additions & 0 deletions proto/stride/stakeibc/host_zone.proto
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ message HostZone {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
string min_inner_redemption_rate = 28 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
string max_inner_redemption_rate = 29 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
bool lsm_liquid_stake_enabled = 27;
bool halted = 19;
reserved 4, 5, 6, 7, 14, 15, 16;
Expand Down
19 changes: 19 additions & 0 deletions proto/stride/stakeibc/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,27 @@ service Msg {
rpc UpdateValidatorSharesExchRate(MsgUpdateValidatorSharesExchRate)
returns (MsgUpdateValidatorSharesExchRateResponse);
rpc ClearBalance(MsgClearBalance) returns (MsgClearBalanceResponse);
rpc UpdateInnerRedemptionRateBounds(MsgUpdateInnerRedemptionRateBounds)
returns (MsgUpdateInnerRedemptionRateBoundsResponse);
}

message MsgUpdateInnerRedemptionRateBounds {
string creator = 1;
string chain_id = 2;
string min_inner_redemption_rate = 3 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
string max_inner_redemption_rate = 4 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}

message MsgUpdateInnerRedemptionRateBoundsResponse {}

message MsgLiquidStake {
string creator = 1;
string amount = 2 [
Expand Down
1 change: 1 addition & 0 deletions x/stakeibc/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(CmdRestoreInterchainAccount())
cmd.AddCommand(CmdUpdateValidatorSharesExchRate())
cmd.AddCommand(CmdClearBalance())
cmd.AddCommand(CmdUpdateInnerRedemptionRateBounds())

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

import (
"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/v14/x/stakeibc/types"
)

func CmdUpdateInnerRedemptionRateBounds() *cobra.Command {
cmd := &cobra.Command{
Use: "update-tight-bounds [chainid] [min-bound] [max-bound]",
Short: "Broadcast message update-tight-bounds",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) (err error) {
argChainId := args[0]
minRedemptionRateStr := args[1]
maxRedemptionRateStr := args[2]

minInnerRedemptionRate := sdk.ZeroDec()
if minRedemptionRateStr != "" {
minInnerRedemptionRate, err = sdk.NewDecFromStr(minRedemptionRateStr)
if err != nil {
return err
}
}
maxInnerRedemptionRate := sdk.ZeroDec()
if maxRedemptionRateStr != "" {
maxInnerRedemptionRate, err = sdk.NewDecFromStr(maxRedemptionRateStr)
if err != nil {
return err
}
}

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

msg := types.NewMsgUpdateInnerRedemptionRateBounds(
clientCtx.GetFromAddress().String(),
argChainId,
minInnerRedemptionRate,
maxInnerRedemptionRate,
)
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 @@ -58,6 +58,9 @@ func NewMessageHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgUpdateValidatorSharesExchRate:
res, err := msgServer.UpdateValidatorSharesExchRate(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgUpdateInnerRedemptionRateBounds:
res, err := msgServer.UpdateInnerRedemptionRateBounds(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
34 changes: 26 additions & 8 deletions x/stakeibc/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,31 @@

// safety check: ensure the redemption rate is NOT below our min safety threshold && NOT above our max safety threshold on host zone
func (k Keeper) IsRedemptionRateWithinSafetyBounds(ctx sdk.Context, zone types.HostZone) (bool, error) {
// Get the wide bounds
minSafetyThreshold, maxSafetyThreshold := k.GetOuterSafetyBounds(ctx, zone)

redemptionRate := zone.RedemptionRate

if redemptionRate.LT(minSafetyThreshold) || redemptionRate.GT(maxSafetyThreshold) {
errMsg := fmt.Sprintf("IsRedemptionRateWithinSafetyBounds check failed %s is outside safety bounds [%s, %s]", redemptionRate.String(), minSafetyThreshold.String(), maxSafetyThreshold.String())
k.Logger(ctx).Error(errMsg)
return false, errorsmod.Wrapf(types.ErrRedemptionRateOutsideSafetyBounds, errMsg)
}

// Verify the redemption rate is within the inner safety bounds
// The inner safety bounds should always be within the safety bounds, but
// the redundancy above is cheap.
if redemptionRate.LT(zone.MinInnerRedemptionRate) || redemptionRate.GT(zone.MaxInnerRedemptionRate) {
errMsg := fmt.Sprintf("IsRedemptionRateWithinSafetyBounds check failed %s is outside inner safety bounds [%s, %s]", redemptionRate.String(), minSafetyThreshold.String(), maxSafetyThreshold.String())
k.Logger(ctx).Error(errMsg)
return false, errorsmod.Wrapf(types.ErrRedemptionRateOutsideSafetyBounds, errMsg)
}

return true, nil
}

func (k Keeper) GetOuterSafetyBounds(ctx sdk.Context, zone types.HostZone) (sdk.Dec, sdk.Dec) {
sampocs marked this conversation as resolved.
Show resolved Hide resolved
// Fetch the wide bounds
minSafetyThresholdInt := k.GetParam(ctx, types.KeyDefaultMinRedemptionRateThreshold)
minSafetyThreshold := sdk.NewDec(int64(minSafetyThresholdInt)).Quo(sdk.NewDec(100))

Expand All @@ -268,12 +293,5 @@
maxSafetyThreshold = zone.MaxRedemptionRate
}

redemptionRate := zone.RedemptionRate

if redemptionRate.LT(minSafetyThreshold) || redemptionRate.GT(maxSafetyThreshold) {
errMsg := fmt.Sprintf("IsRedemptionRateWithinSafetyBounds check failed %s is outside safety bounds [%s, %s]", redemptionRate.String(), minSafetyThreshold.String(), maxSafetyThreshold.String())
k.Logger(ctx).Error(errMsg)
return false, errorsmod.Wrapf(types.ErrRedemptionRateOutsideSafetyBounds, errMsg)
}
return true, nil
return minSafetyThreshold, maxSafetyThreshold, nil

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / stride amd64 for darwin

too many return values

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / stride amd64 for linux

too many return values

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / golangci-lint

too many return values

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / golangci-lint

too many return values

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / golangci-lint

too many return values

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / golangci-lint

too many return values

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / golangci-lint

too many return values

Check failure on line 296 in x/stakeibc/keeper/keeper.go

View workflow job for this annotation

GitHub Actions / stride arm64 for darwin

too many return values
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package keeper

import (
"context"
"fmt"

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

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

func (k msgServer) UpdateInnerRedemptionRateBounds(goCtx context.Context, msg *types.MsgUpdateInnerRedemptionRateBounds) (*types.MsgUpdateInnerRedemptionRateBoundsResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

// Confirm host zone exists
zone, found := k.GetHostZone(ctx, msg.ChainId)
if !found {
k.Logger(ctx).Error(fmt.Sprintf("Host Zone not found: %s", msg.ChainId))
return nil, types.ErrInvalidHostZone
}

// Get the wide bounds
outerMinSafetyThreshold, outerMaxSafetyThreshold := k.GetOuterSafetyBounds(ctx, zone)

innerMinSafetyThreshold := msg.MinInnerRedemptionRate
innerMaxSafetyThreshold := msg.MaxInnerRedemptionRate

// Confirm the inner bounds are within the outer bounds
if innerMinSafetyThreshold.LT(outerMinSafetyThreshold) {
k.Logger(ctx).Error(fmt.Sprintf("Inner min safety threshold (%s) is less than outer min safety threshold (%s)", innerMinSafetyThreshold, outerMinSafetyThreshold))
return nil, types.ErrInvalidBounds
}

if innerMaxSafetyThreshold.GT(outerMaxSafetyThreshold) {
k.Logger(ctx).Error(fmt.Sprintf("Inner max safety threshold (%s) is greater than outer max safety threshold (%s)", innerMaxSafetyThreshold, outerMaxSafetyThreshold))
return nil, types.ErrInvalidBounds
}

// Confirm the max is greater than the min
if innerMaxSafetyThreshold.LTE(innerMinSafetyThreshold) {
k.Logger(ctx).Error(fmt.Sprintf("Inner max safety threshold (%s) is less than inner min safety threshold (%s)", innerMaxSafetyThreshold, innerMinSafetyThreshold))
return nil, types.ErrInvalidBounds
}

// Set the inner bounds on the host zone
zone.MinInnerRedemptionRate = innerMinSafetyThreshold
zone.MaxInnerRedemptionRate = innerMaxSafetyThreshold
k.SetHostZone(ctx, zone)

return &types.MsgUpdateInnerRedemptionRateBoundsResponse{}, nil
}
2 changes: 2 additions & 0 deletions x/stakeibc/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&ToggleLSMProposal{}, "stakeibc/ToggleLSMProposal", nil)
cdc.RegisterConcrete(&MsgRestoreInterchainAccount{}, "stakeibc/RestoreInterchainAccount", nil)
cdc.RegisterConcrete(&MsgUpdateValidatorSharesExchRate{}, "stakeibc/UpdateValidatorSharesExchRate", nil)
cdc.RegisterConcrete(&MsgUpdateInnerRedemptionRateBounds{}, "stakeibc/UpdateInnerRedemptionRateBounds", nil)
// this line is used by starport scaffolding # 2
}

Expand All @@ -39,6 +40,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
&MsgDeleteValidator{},
&MsgRestoreInterchainAccount{},
&MsgUpdateValidatorSharesExchRate{},
&MsgUpdateInnerRedemptionRateBounds{},
)

registry.RegisterImplementations((*govtypes.Content)(nil),
Expand Down
1 change: 1 addition & 0 deletions x/stakeibc/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ var (
ErrInvalidValidatorDelegationUpdates = errorsmod.Register(ModuleName, 1548, "Invalid validator delegation updates")
ErrLSMLiquidStakeDisabledForHostZone = errorsmod.Register(ModuleName, 1549, "LSM liquid stake is disabled for host zone")
ErrUnableToRemoveValidator = errorsmod.Register(ModuleName, 1550, "Unable to remove validator")
ErrInvalidBounds = errorsmod.Register(ModuleName, 1551, "Invalid safety bounds - inner bounds must be within outer bounds")
)
Loading
Loading