Skip to content

Commit

Permalink
add msg redeem deprecated
Browse files Browse the repository at this point in the history
  • Loading branch information
puneet2019 committed Dec 11, 2024
1 parent b73d491 commit c980337
Show file tree
Hide file tree
Showing 9 changed files with 760 additions and 76 deletions.
15 changes: 14 additions & 1 deletion proto/pstake/liquidstakeibc/v1beta1/msgs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ service Msg {
rpc Redeem(MsgRedeem) returns (MsgRedeemResponse) {
option (google.api.http).post = "/pstake/liquidstakeibc/v1beta1/Redeem";
}

rpc RedeemDeprecated(MsgRedeemDeprecated) returns (MsgRedeemDeprecatedResponse) {
option (google.api.http).post = "/pstake/liquidstakeibc/v1beta1/RedeemDeprecated";
}
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
}

Expand Down Expand Up @@ -159,3 +161,14 @@ message MsgUpdateParams {
}

message MsgUpdateParamsResponse {}

message MsgRedeemDeprecated {
option (cosmos.msg.v1.signer) = "delegator_address";
option (amino.name) = "pstake/MsgRedeemDepracated";

string delegator_address = 1
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
cosmos.base.v1beta1.Coin amount = 2 [ (gogoproto.nullable) = false ];
}

message MsgRedeemDeprecatedResponse {}
35 changes: 35 additions & 0 deletions x/liquidstakeibc/client/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func NewTxCmd() *cobra.Command {
NewLiquidUnstakeCmd(),
NewRedeemCmd(),
NewUpdateParamsCmd(),
NewRedeemDeprecatedCmd(),
)

return txCmd
Expand Down Expand Up @@ -363,3 +364,37 @@ Params file contents:

return cmd
}

func NewRedeemDeprecatedCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "redeem-deprecated [amount]",
Short: `Instantly redeem stk tokens from a deprecated host chain`,
Long: strings.TrimSpace(
fmt.Sprintf(
`Submit a redeem-deprecated transaction: $ %s tx liquidstakeibc redeem-deprecated 50000000stk/uatom`,
version.AppName,
),
),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientctx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

amount, err := sdk.ParseCoinNormalized(args[0])
if err != nil {
return err
}

delegatorAddress := clientctx.GetFromAddress()
msg := types.NewMsgRedeemDeprecated(amount, delegatorAddress)

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

flags.AddTxFlagsToCmd(cmd)

return cmd
}
1 change: 0 additions & 1 deletion x/liquidstakeibc/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) {
if hc.Active {
// attempt to delegate
k.DoDelegate(ctx, hc)

}

// attempt to recreate closed ICA channels
Expand Down
120 changes: 119 additions & 1 deletion x/liquidstakeibc/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/cosmos/gogoproto/proto"
"strconv"
"strings"

Expand All @@ -14,6 +13,7 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/cosmos/gogoproto/proto"
transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"

"github.com/persistenceOne/pstake-native/v2/x/liquidstakeibc/types"
Expand Down Expand Up @@ -1063,3 +1063,121 @@ func (k msgServer) validateLiquidStakeLSMDeposit(

return hc, validator, &denomTrace, nil
}

// Redeem defines a method for deprecated redeem liquid staked tokens
func (k msgServer) RedeemDeprecated(
goCtx context.Context,
msg *types.MsgRedeemDeprecated,
) (*types.MsgRedeemDeprecatedResponse, error) {
ctx := sdktypes.UnwrapSDKContext(goCtx)

// parse the chain host denom from the stk denom
hostDenom, found := types.MintDenomToHostDenom(msg.Amount.Denom)
if !found {
return nil, errorsmod.Wrapf(types.ErrInvalidHostChain,
"could not parse chain host denom from %s",
msg.Amount.Denom,
)
}

// get the host chain we need to unstake from
hc, found := k.GetHostChainFromHostDenom(ctx, hostDenom)
if !found {
return nil, errorsmod.Wrapf(types.ErrInvalidHostChain,
"host chain with host denom %s not registered",
hostDenom,
)
}

if hc.Active {
return nil, types.ErrHostChainActive
}

// check the msg amount denom is the host chain mint denom
if msg.Amount.Denom != hc.MintDenom() {
return nil, errorsmod.Wrapf(
types.ErrInvalidDenom,
"expected %s, got %s",
hc.MintDenom(),
msg.Amount.Denom,
)
}

// get the redeem address
redeemAddress, err := sdktypes.AccAddressFromBech32(msg.DelegatorAddress)
if err != nil {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "got error : %s", err)
}

// send the redeem amount to the module account
err = k.bankKeeper.SendCoinsFromAccountToModule(
ctx,
redeemAddress,
types.ModuleName,
sdktypes.NewCoins(msg.Amount))
if err != nil {
return nil, errorsmod.Wrapf(
types.ErrMintFailed,
"failed to send deprecated redeemed coins from account %s to module %s: %s",
redeemAddress.String(),
types.ModuleName,
err.Error(),
)
}

// amount of tokens to be redeemed
stkAmount := msg.Amount
redeemAmount := sdktypes.NewDecCoinFromCoin(stkAmount).Amount.Quo(hc.CValue)
redeemToken, _ := sdktypes.NewDecCoinFromDec(hc.IBCDenom(), redeemAmount).TruncateDecimal()

// send the deprecated redeemed token from module to the account,
// this will error out if there are insufficient redeemTokens
err = k.bankKeeper.SendCoinsFromModuleToAccount(
ctx,
types.UndelegationModuleAccount,
redeemAddress,
sdktypes.NewCoins(redeemToken),
)
if err != nil {
return nil, errorsmod.Wrapf(
types.ErrRedeemFailed,
"failed to send deprecated redeemed coins from module %s to account %s: %s",
types.UndelegationModuleAccount,
redeemAddress.String(),
err.Error(),
)
}

// burn the stk tokens
err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdktypes.NewCoins(stkAmount))
if err != nil {
return nil, errorsmod.Wrapf(
types.ErrBurnFailed,
"failed to burn deprecated redeemed coins on module %s: %s",
types.ModuleName,
err.Error(),
)
}

ctx.EventManager().EmitEvents(sdktypes.Events{
sdktypes.NewEvent(
types.EventTypeRedeemDeprecated,
sdktypes.NewAttribute(types.AttributeChainID, hc.ChainId),
sdktypes.NewAttribute(types.AttributeDelegatorAddress, redeemAddress.String()),
sdktypes.NewAttribute(types.AttributeInputAmount,
sdktypes.NewCoin(hc.MintDenom(), msg.Amount.Amount).String()),
sdktypes.NewAttribute(types.AttributeOutputAmount,
sdktypes.NewCoin(hc.HostDenom, redeemToken.Amount).String()),
),
sdktypes.NewEvent(
sdktypes.EventTypeMessage,
sdktypes.NewAttribute(sdktypes.AttributeKeyModule, types.AttributeValueCategory),
sdktypes.NewAttribute(sdktypes.AttributeKeySender, msg.DelegatorAddress),
),
},
)

telemetry.IncrCounter(float32(1), hc.ChainId, "redeem_deprecated")

return &types.MsgRedeemDeprecatedResponse{}, nil
}
1 change: 1 addition & 0 deletions x/liquidstakeibc/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ var (
ErrLSMDepositProcessing = errorsmod.Register(ModuleName, 2020, "already processing LSM deposit")
ErrLSMValidatorInvalidState = errorsmod.Register(ModuleName, 2021, "validator invalid state")
ErrInsufficientDeposits = errorsmod.Register(ModuleName, 2022, "insufficient deposits")
ErrHostChainActive = errorsmod.Register(ModuleName, 2023, "host chain is active")
)
1 change: 1 addition & 0 deletions x/liquidstakeibc/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const (
EventTypeLiquidStakeLSM = "liquid_stake_lsm"
EventTypeLiquidUnstake = "liquid_unstake"
EventTypeRedeem = "redeem"
EventTypeRedeemDeprecated = "redeem_deprecated"
EventTypePacket = "ics27_packet"
EventTypeTimeout = "timeout"
EventTypeSlashing = "validator_slash"
Expand Down
52 changes: 52 additions & 0 deletions x/liquidstakeibc/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
MsgTypeLiquidUnstake string = "msg_liquid_unstake"
MsgTypeRedeem string = "msg_redeem"
MsgTypeUpdateParams string = "msg_update_params"
MsgTypeRedeemDeprecated string = "msg_redeem_deprecated"
)

var (
Expand All @@ -33,6 +34,7 @@ var (
_ sdk.Msg = &MsgLiquidUnstake{}
_ sdk.Msg = &MsgRedeem{}
_ sdk.Msg = &MsgLiquidStakeLSM{}
_ sdk.Msg = &MsgRedeemDeprecated{}
)

func NewMsgRegisterHostChain(
Expand Down Expand Up @@ -676,3 +678,53 @@ func (m *MsgUpdateParams) ValidateBasic() error {
}
return nil
}

func NewMsgRedeemDeprecated(amount sdk.Coin, address sdk.AccAddress) *MsgRedeemDeprecated {
return &MsgRedeemDeprecated{
DelegatorAddress: address.String(),
Amount: amount,
}
}

func (m *MsgRedeemDeprecated) Route() string {
return RouterKey
}

// Type should return the action
func (m *MsgRedeemDeprecated) Type() string {
return MsgTypeRedeemDeprecated
}

// GetSignBytes encodes the message for signing
func (m *MsgRedeemDeprecated) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(m))
}

// GetSigners defines whose signature is required
func (m *MsgRedeemDeprecated) GetSigners() []sdk.AccAddress {
acc, err := sdk.AccAddressFromBech32(m.DelegatorAddress)
if err != nil {
panic(err)
}
return []sdk.AccAddress{acc}
}

// ValidateBasic performs stateless checks
func (m *MsgRedeemDeprecated) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(m.DelegatorAddress); err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, m.DelegatorAddress)
}

if !m.Amount.IsValid() {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
}

if !m.Amount.IsPositive() {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
}

if !IsLiquidStakingDenom(m.Amount.Denom) {
return sdkerrors.ErrInvalidCoins.Wrapf("invalid denom, required stk/{host-denom} got %s", m.Amount.Denom)
}
return nil
}
Loading

0 comments on commit c980337

Please sign in to comment.