Skip to content

Commit

Permalink
add inner bounds (#938)
Browse files Browse the repository at this point in the history
**summary**
add inner bounds

**tests**
- unittests
- dockernet
    - verify bounds are set upon registration
    - verify hz halts if the inner bound is crossed

```
➜  stride git:(tight-bounds) ✗ stridedl q stakeibc show-host-zone GAIA       
host_zone:
...
  last_redemption_rate: "1.000000000000000000"
  max_inner_redemption_rate: "1.500000000000000000"
  max_redemption_rate: "1.500000000000000000"
  min_inner_redemption_rate: "0.900000000000000000"
  min_redemption_rate: "0.900000000000000000"
  redemption_rate: "1.000000000000000000"
```

update bounds
```
$STRIDE_MAIN_CMD tx stakeibc set-redemption-rate-bounds GAIA 1 1.4 --from $STRIDE_ADMIN_ACCT -y | TRIM_TX
```
and query them
```
  max_inner_redemption_rate: "1.400000000000000000"
  max_redemption_rate: "1.500000000000000000"
  min_inner_redemption_rate: "1.000000000000000000"
  min_redemption_rate: "0.900000000000000000"
```

try setting from the wrong account
```
$STRIDE_MAIN_CMD tx stakeibc set-redemption-rate-bounds GAIA 1.1 1.3 --from val1 -y | TRIM_TX
```
and the error
```
➜  stride git:(tight-bounds) ✗ bash dockernet/scripts/set_bound.sh
Error: invalid creator address (stride1uk4ze0x4nvh4fk0xm4jdud58eqn4yxhrt52vv7): invalid address
Usage:
  strided tx stakeibc set-redemption-rate-bounds [chainid] [min-bound] [max-bound] [flags]

Flags:
```

Set tight bound and observe halt
```
$STRIDE_MAIN_CMD tx stakeibc set-redemption-rate-bounds GAIA 1 1.000001 --from $STRIDE_ADMIN_ACCT -y | TRIM_TX
```

halt
```
dockernet-stride1-1  | 2:11AM ERR IsRedemptionRateWithinSafetyBounds check failed 1.001028241739306108 is outside inner safety bounds [1.000000000000000000, 1.000001000000000000] module=x/stakeibc
dockernet-stride1-1  | 2:11AM ERR [INVARIANT BROKEN!!!] GAIA's RR is 1.001028241739306108. ERR: IsRedemptionRateWithinSafetyBounds check failed 1.001028241739306108 is outside inner safety bounds [1.000000000000000000, 1.000001000000000000]: redemption rate outside safety bounds module=x/stakeibc

```
  • Loading branch information
asalzmann authored Sep 18, 2023
1 parent b4719ff commit ce7fb02
Show file tree
Hide file tree
Showing 16 changed files with 1,108 additions and 181 deletions.
11 changes: 11 additions & 0 deletions dockernet/scripts/set_bound.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
### IBC TRANSFER
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
source ${SCRIPT_DIR}/../config.sh

## Set the redemption rate bounds
# Set bounds from the correct account (success)
# $STRIDE_MAIN_CMD tx stakeibc set-redemption-rate-bounds GAIA 1 1.4 --from $STRIDE_ADMIN_ACCT -y | TRIM_TX
# Set bounds from the wrong account (fail)
# $STRIDE_MAIN_CMD tx stakeibc set-redemption-rate-bounds GAIA 1.1 1.3 --from val1 -y | TRIM_TX
# Set tight bound and observe halt
$STRIDE_MAIN_CMD tx stakeibc set-redemption-rate-bounds GAIA 1 1.000001 --from $STRIDE_ADMIN_ACCT -y | TRIM_TX
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
}
44 changes: 44 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,44 @@
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: "set-redemption-rate-bounds [chainid] [min-bound] [max-bound]",
Short: "Broadcast message set-redemption-rate-bounds",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) (err error) {
argChainId := args[0]
minInnerRedemptionRate := sdk.MustNewDecFromStr(args[1])
maxInnerRedemptionRate := sdk.MustNewDecFromStr(args[2])

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
2 changes: 2 additions & 0 deletions x/stakeibc/keeper/host_zone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ func createNHostZone(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Host
items[i].LastRedemptionRate = sdk.NewDec(1)
items[i].MinRedemptionRate = sdk.NewDecWithPrec(5, 1)
items[i].MaxRedemptionRate = sdk.NewDecWithPrec(15, 1)
items[i].MinInnerRedemptionRate = sdk.NewDecWithPrec(5, 1)
items[i].MaxInnerRedemptionRate = sdk.NewDecWithPrec(15, 1)
items[i].TotalDelegations = sdkmath.ZeroInt()
keeper.SetHostZone(ctx, items[i])
}
Expand Down
46 changes: 40 additions & 6 deletions x/stakeibc/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,33 @@ func (k Keeper) GetICATimeoutNanos(ctx sdk.Context, epochType string) (uint64, e

// 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.
// There is also one scenario where the outer bounds go within the inner bounds - if they're updated as part of a param change proposal.
minInnerSafetyThreshold, maxInnerSafetyThreshold := k.GetInnerSafetyBounds(ctx, zone)
if redemptionRate.LT(minInnerSafetyThreshold) || redemptionRate.GT(maxInnerSafetyThreshold) {
errMsg := fmt.Sprintf("IsRedemptionRateWithinSafetyBounds check failed %s is outside inner safety bounds [%s, %s]", redemptionRate.String(), minInnerSafetyThreshold.String(), maxInnerSafetyThreshold.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) {
// Fetch the wide bounds
minSafetyThresholdInt := k.GetParam(ctx, types.KeyDefaultMinRedemptionRateThreshold)
minSafetyThreshold := sdk.NewDec(int64(minSafetyThresholdInt)).Quo(sdk.NewDec(100))

Expand All @@ -268,12 +295,19 @@ func (k Keeper) IsRedemptionRateWithinSafetyBounds(ctx sdk.Context, zone types.H
maxSafetyThreshold = zone.MaxRedemptionRate
}

redemptionRate := zone.RedemptionRate
return minSafetyThreshold, maxSafetyThreshold
}

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)
func (k Keeper) GetInnerSafetyBounds(ctx sdk.Context, zone types.HostZone) (sdk.Dec, sdk.Dec) {
// Fetch the inner bounds
minSafetyThreshold, maxSafetyThreshold := k.GetOuterSafetyBounds(ctx, zone)

if !zone.MinInnerRedemptionRate.IsNil() && zone.MinInnerRedemptionRate.IsPositive() && zone.MinInnerRedemptionRate.GT(minSafetyThreshold) {
minSafetyThreshold = zone.MinInnerRedemptionRate
}
return true, nil
if !zone.MaxInnerRedemptionRate.IsNil() && zone.MaxInnerRedemptionRate.IsPositive() && zone.MaxInnerRedemptionRate.LT(maxSafetyThreshold) {
maxSafetyThreshold = zone.MaxInnerRedemptionRate
}

return minSafetyThreshold, maxSafetyThreshold
}
17 changes: 10 additions & 7 deletions x/stakeibc/keeper/msg_server_register_host_zone.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,16 @@ func (k msgServer) RegisterHostZone(goCtx context.Context, msg *types.MsgRegiste
HostDenom: msg.HostDenom,
TransferChannelId: msg.TransferChannelId,
// Start sharesToTokens rate at 1 upon registration
RedemptionRate: sdk.NewDec(1),
LastRedemptionRate: sdk.NewDec(1),
UnbondingPeriod: msg.UnbondingPeriod,
DepositAddress: depositAddress.String(),
MinRedemptionRate: msg.MinRedemptionRate,
MaxRedemptionRate: msg.MaxRedemptionRate,
LsmLiquidStakeEnabled: msg.LsmLiquidStakeEnabled,
RedemptionRate: sdk.NewDec(1),
LastRedemptionRate: sdk.NewDec(1),
UnbondingPeriod: msg.UnbondingPeriod,
DepositAddress: depositAddress.String(),
MinRedemptionRate: msg.MinRedemptionRate,
MaxRedemptionRate: msg.MaxRedemptionRate,
// Default the inner bounds to the outer bounds
MinInnerRedemptionRate: msg.MinRedemptionRate,
MaxInnerRedemptionRate: msg.MaxRedemptionRate,
LsmLiquidStakeEnabled: msg.LsmLiquidStakeEnabled,
}
// write the zone back to the store
k.SetHostZone(ctx, zone)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package keeper

import (
"context"
"fmt"

errorsmod "cosmossdk.io/errors"
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) {
errMsg := fmt.Sprintf("inner min safety threshold (%s) is less than outer min safety threshold (%s)", innerMinSafetyThreshold, outerMinSafetyThreshold)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidBounds, errMsg)
}

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

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

return &types.MsgUpdateInnerRedemptionRateBoundsResponse{}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package keeper_test

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
_ "github.com/stretchr/testify/suite"

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

type UpdateInnerRedemptionRateBoundsTestCase struct {
validMsg stakeibctypes.MsgUpdateInnerRedemptionRateBounds
zone stakeibctypes.HostZone
}

func (s *KeeperTestSuite) SetupUpdateInnerRedemptionRateBounds() UpdateInnerRedemptionRateBoundsTestCase {
// Register a host zone
hostZone := stakeibctypes.HostZone{
ChainId: HostChainId,
HostDenom: Atom,
IbcDenom: IbcAtom,
RedemptionRate: sdk.NewDec(1.0),
MinRedemptionRate: sdk.NewDec(9).Quo(sdk.NewDec(10)),
MaxRedemptionRate: sdk.NewDec(15).Quo(sdk.NewDec(10)),
}

s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)

defaultMsg := stakeibctypes.MsgUpdateInnerRedemptionRateBounds{
// TODO: does this need to be the admin address?
Creator: s.TestAccs[0].String(),
ChainId: HostChainId,
MinInnerRedemptionRate: sdk.NewDec(1),
MaxInnerRedemptionRate: sdk.NewDec(11).Quo(sdk.NewDec(10)),
}

return UpdateInnerRedemptionRateBoundsTestCase{
validMsg: defaultMsg,
zone: hostZone,
}
}

// Verify that bounds can be set successfully
func (s *KeeperTestSuite) TestUpdateInnerRedemptionRateBounds_Success() {
tc := s.SetupUpdateInnerRedemptionRateBounds()

// Set the inner bounds on the host zone
_, err := s.GetMsgServer().UpdateInnerRedemptionRateBounds(s.Ctx, &tc.validMsg)
s.Require().NoError(err, "should not throw an error")

// Confirm the inner bounds were set
zone, found := s.App.StakeibcKeeper.GetHostZone(s.Ctx, HostChainId)
s.Require().True(found, "host zone should be in the store")
s.Require().Equal(tc.validMsg.MinInnerRedemptionRate, zone.MinInnerRedemptionRate, "min inner redemption rate should be set")
s.Require().Equal(tc.validMsg.MaxInnerRedemptionRate, zone.MaxInnerRedemptionRate, "max inner redemption rate should be set")
}

// Setting inner bounds outside of outer bounds should throw an error
func (s *KeeperTestSuite) TestUpdateInnerRedemptionRateBounds_OutOfBounds() {
tc := s.SetupUpdateInnerRedemptionRateBounds()

// Set the min inner bound to be less than the min outer bound
tc.validMsg.MinInnerRedemptionRate = sdk.NewDec(0)

// Set the inner bounds on the host zone
_, err := s.GetMsgServer().UpdateInnerRedemptionRateBounds(s.Ctx, &tc.validMsg)
// verify it throws an error
errMsg := fmt.Sprintf("inner min safety threshold (%s) is less than outer min safety threshold (%s)", tc.validMsg.MinInnerRedemptionRate, sdk.NewDec(9).Quo(sdk.NewDec(10)))
s.Require().ErrorContains(err, errMsg)

// Set the min inner bound to be valid, but the max inner bound to be greater than the max outer bound
tc.validMsg.MinInnerRedemptionRate = sdk.NewDec(1)
tc.validMsg.MaxInnerRedemptionRate = sdk.NewDec(3)
// Set the inner bounds on the host zone
_, err = s.GetMsgServer().UpdateInnerRedemptionRateBounds(s.Ctx, &tc.validMsg)
// verify it throws an error
errMsg = fmt.Sprintf("inner max safety threshold (%s) is greater than outer max safety threshold (%s)", tc.validMsg.MaxInnerRedemptionRate, sdk.NewDec(15).Quo(sdk.NewDec(10)))
s.Require().ErrorContains(err, errMsg)
}

// Validate basic tests
func (s *KeeperTestSuite) TestUpdateInnerRedemptionRateBounds_InvalidMsg() {
tc := s.SetupUpdateInnerRedemptionRateBounds()

// Set the min inner bound to be greater than than the max inner bound
invalidMsg := tc.validMsg
invalidMsg.MinInnerRedemptionRate = sdk.NewDec(2)

err := invalidMsg.ValidateBasic()

// Verify the error
errMsg := fmt.Sprintf("Inner max safety threshold (%s) is less than inner min safety threshold (%s)", invalidMsg.MaxInnerRedemptionRate, invalidMsg.MinInnerRedemptionRate)
s.Require().ErrorContains(err, errMsg)
}

// Verify that if inner bounds end up outside of outer bounds (somehow), the outer bounds are returned
func (s *KeeperTestSuite) TestGetInnerSafetyBounds() {
tc := s.SetupUpdateInnerRedemptionRateBounds()

// Set the inner bounds outside the outer bounds on the host zone directly
tc.zone.MinInnerRedemptionRate = sdk.NewDec(0)
tc.zone.MaxInnerRedemptionRate = sdk.NewDec(3)
// Set the host zone
s.App.StakeibcKeeper.SetHostZone(s.Ctx, tc.zone)

// Get the inner bounds and verify the outer bounds are used
innerMinSafetyThreshold, innerMaxSafetyThreshold := s.App.StakeibcKeeper.GetInnerSafetyBounds(s.Ctx, tc.zone)
s.Require().Equal(tc.zone.MinRedemptionRate, innerMinSafetyThreshold, "min inner redemption rate should be set")
s.Require().Equal(tc.zone.MaxRedemptionRate, innerMaxSafetyThreshold, "max inner redemption rate should be set")
}
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

0 comments on commit ce7fb02

Please sign in to comment.