Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions proto/multistaking/v1/proposals.proto
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,16 @@ message AddMultiStakingEVMCoinProposal {
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec"
];
}

// RemoveMultiStakingCoinProposal is a gov v1beta1 Content type to remove an
// token as a bond token
message RemoveMultiStakingCoinProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";
string title = 1;
string description = 2;
string denom = 3;
}
51 changes: 51 additions & 0 deletions x/multi-staking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,54 @@ func (k Keeper) AdjustCancelUnbondingAmount(ctx sdk.Context, delAcc sdk.AccAddre

return math.MinInt(totalUnbondingAmount, amount), nil
}

func (k Keeper) Undelegate(ctx sdk.Context, msg *stakingtypes.MsgUndelegate) (*stakingtypes.MsgUndelegateResponse, error) {
multiStakerAddr, valAcc, err := types.AccAddrAndValAddrFromStrings(msg.DelegatorAddress, msg.ValidatorAddress)
if err != nil {
return nil, err
}

if !k.isValMultiStakingCoin(ctx, valAcc, msg.Amount) {
return nil, fmt.Errorf("not allowed coin")
}

lockID := types.MultiStakingLockID(msg.DelegatorAddress, msg.ValidatorAddress)
lock, found := k.GetMultiStakingLock(ctx, lockID)
if !found {
return nil, fmt.Errorf("can't find multi staking lock")
}

multiStakingCoin := lock.MultiStakingCoin(msg.Amount.Amount)
err = lock.RemoveCoinFromMultiStakingLock(multiStakingCoin)
if err != nil {
return nil, err
}
k.SetMultiStakingLock(ctx, lock)

unbondAmount := multiStakingCoin.BondValue()
unbondAmount, err = k.AdjustUnbondAmount(ctx, multiStakerAddr, valAcc, unbondAmount)
if err != nil {
return nil, err
}

bondDenom, err := k.stakingKeeper.BondDenom(ctx)
if err != nil {
return nil, err
}
unbondCoin := sdk.NewCoin(bondDenom, unbondAmount)

k.SetMultiStakingUnlockEntry(ctx, types.MultiStakingUnlockID(msg.DelegatorAddress, msg.ValidatorAddress), multiStakingCoin)

// Create a stakingMsgServer instance to handle the undelegation with all proper events and telemetry
stakingMsgServer := stakingkeeper.NewMsgServerImpl(k.stakingKeeper)

// Prepare the message with the adjusted unbond amount
sdkMsg := &stakingtypes.MsgUndelegate{
DelegatorAddress: msg.DelegatorAddress,
ValidatorAddress: msg.ValidatorAddress,
Amount: unbondCoin,
}

// Call the staking MsgServer to handle undelegation with all side effects
return stakingMsgServer.Undelegate(ctx, sdkMsg)
}
46 changes: 2 additions & 44 deletions x/multi-staking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,50 +195,8 @@ func (k msgServer) BeginRedelegate(goCtx context.Context, msg *stakingtypes.MsgB
// Undelegate defines a method for performing an undelegation from a delegate and a validator
func (k msgServer) Undelegate(goCtx context.Context, msg *stakingtypes.MsgUndelegate) (*stakingtypes.MsgUndelegateResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

multiStakerAddr, valAcc, err := types.AccAddrAndValAddrFromStrings(msg.DelegatorAddress, msg.ValidatorAddress)
if err != nil {
return nil, err
}

if !k.keeper.isValMultiStakingCoin(ctx, valAcc, msg.Amount) {
return nil, fmt.Errorf("not allowed coin")
}

lockID := types.MultiStakingLockID(msg.DelegatorAddress, msg.ValidatorAddress)
lock, found := k.keeper.GetMultiStakingLock(ctx, lockID)
if !found {
return nil, fmt.Errorf("can't find multi staking lock")
}

multiStakingCoin := lock.MultiStakingCoin(msg.Amount.Amount)
err = lock.RemoveCoinFromMultiStakingLock(multiStakingCoin)
if err != nil {
return nil, err
}
k.keeper.SetMultiStakingLock(ctx, lock)

unbondAmount := multiStakingCoin.BondValue()
unbondAmount, err = k.keeper.AdjustUnbondAmount(ctx, multiStakerAddr, valAcc, unbondAmount)
if err != nil {
return nil, err
}

bondDenom, err := k.keeper.stakingKeeper.BondDenom(ctx)
if err != nil {
return nil, err
}
unbondCoin := sdk.NewCoin(bondDenom, unbondAmount)

sdkMsg := &stakingtypes.MsgUndelegate{
DelegatorAddress: msg.DelegatorAddress,
ValidatorAddress: msg.ValidatorAddress,
Amount: unbondCoin, // replace with unbondCoin
}

k.keeper.SetMultiStakingUnlockEntry(ctx, types.MultiStakingUnlockID(msg.DelegatorAddress, msg.ValidatorAddress), multiStakingCoin)

return k.stakingMsgServer.Undelegate(ctx, sdkMsg)
// Delegate to the Keeper method to avoid duplication
return k.keeper.Undelegate(ctx, msg)
}

// CancelUnbondingDelegation defines a method for canceling the unbonding delegation
Expand Down
48 changes: 47 additions & 1 deletion x/multi-staking/keeper/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

// AddMultiStakingCoinProposal handles the proposals to add a new bond token
Expand Down Expand Up @@ -85,7 +86,7 @@ func (k Keeper) BondWeightProposal(
}

bondWeight := *p.UpdatedBondWeight
if bondWeight.LT(math.LegacyZeroDec()) {
if bondWeight.LTE(math.LegacyZeroDec()) {
return fmt.Errorf("Error MultiStakingCoin BondWeight %s invalid", bondWeight) //nolint:stylecheck
}

Expand All @@ -100,3 +101,48 @@ func (k Keeper) BondWeightProposal(
)
return nil
}

// RemoveMultiStakingCoinProposal handles the proposals to remove a bond token
// We will force undelegate all the delegation of the removed bond token
// Remove bond token from store
func (k Keeper) RemoveMultiStakingCoinProposal(
ctx sdk.Context,
p *types.RemoveMultiStakingCoinProposal,
) error {
_, found := k.GetBondWeight(ctx, p.Denom)
if !found {
return fmt.Errorf("Error MultiStakingCoin %s not found", p.Denom) //nolint:stylecheck
}
Comment on lines +112 to +115

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If bond weight is 0 before this happens, would this work or throw an error here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don’t accept zero weight anymore

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should still be able to handle that scenario though

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont get it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what u think we should do with zero weight then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anw the scenario that weight is zero can not happen since we block it in AddMultiStakingCoinProposal and UpdateBondWeightProposal also.
There must be 2 state of a denom, non-zero or be removed.


var ubdErr error
k.MultiStakingLockIterator(ctx, func(stakingLock types.MultiStakingLock) bool {
if stakingLock.LockedCoin.Denom != p.Denom {
return false
}
// Call the Keeper method directly instead of going through MsgServer
_, err := k.Undelegate(ctx, &stakingtypes.MsgUndelegate{
DelegatorAddress: stakingLock.LockID.MultiStakerAddr,
ValidatorAddress: stakingLock.LockID.ValAddr,
Amount: stakingLock.LockedCoin.ToCoin(),
})
if err != nil {
ubdErr = err
return true
}

return false
})
if ubdErr != nil {
return ubdErr
}

k.RemoveBondWeight(ctx, p.Denom)

ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeRemoveMultiStakingCoin,
sdk.NewAttribute(types.AttributeKeyDenom, p.Denom),
),
)
Comment thread
facs95 marked this conversation as resolved.
return nil
}
4 changes: 2 additions & 2 deletions x/multi-staking/keeper/proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (suite *KeeperTestSuite) TestUpdateBondWeightProposal() {
shouldErr: true,
},
{
desc: "Accept zero value",
desc: "Not accept zero value",
malleate: func(p *types.UpdateBondWeightProposal) {
oldBondWeight := math.LegacyNewDec(1)
suite.msKeeper.SetBondWeight(suite.ctx, p.Denom, oldBondWeight)
Expand All @@ -125,7 +125,7 @@ func (suite *KeeperTestSuite) TestUpdateBondWeightProposal() {
Denom: "stake1",
UpdatedBondWeight: &zeroWeight,
},
shouldErr: false,
shouldErr: true,
},
} {
tc := tc
Expand Down
2 changes: 2 additions & 0 deletions x/multi-staking/proposal_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func NewMultiStakingProposalHandler(k *keeper.Keeper) govv1beta1.Handler {
return k.AddMultiStakingEVMCoinProposal(ctx, c)
case *types.UpdateBondWeightProposal:
return k.BondWeightProposal(ctx, c)
case *types.RemoveMultiStakingCoinProposal:
return k.RemoveMultiStakingCoinProposal(ctx, c)
default:
return sdkerrors.Wrapf(errortypes.ErrUnknownRequest, "unrecognized %s proposal content type: %T", types.ModuleName, c)
}
Expand Down
6 changes: 5 additions & 1 deletion x/multi-staking/spec/02_gov.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ The proposal performs the following actions:

### Change Bond Token Weight Proposals

We can alter the `BondWeight` of a `multistaking coin` by submiting a `UpdateBondWeightProposal`. This proposal requires specifying `denom` of the `multistaking coin` and the new `BondWeight`, if the proposal is passed the specified `multistaking coin` have its `BondWeight` changed to new value that decleared by the proposal.
We can alter the `BondWeight` of a `multistaking coin` by submiting a `UpdateBondWeightProposal`. This proposal requires specifying `denom` of the `multistaking coin` and the new `BondWeight`, if the proposal is passed the specified `multistaking coin` have its `BondWeight` changed to new value that decleared by the proposal.

### Remove Bond Token Proposals (for cosmos base coin)

We can remove a `multistaking coin` boned token by submiting an `RemoveMultiStakingCoinProposal`. In this proposal, we specify the token's `denom`. If the proposal passes, we will force undelegate all the delegation of the removed bond token and remove the bond token from store.
4 changes: 4 additions & 0 deletions x/multi-staking/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&AddMultiStakingCoinProposal{}, "multistaking/AddMultiStakingCoinProposal", nil)
cdc.RegisterConcrete(&UpdateBondWeightProposal{}, "multistaking/UpdateBondWeightProposal", nil)
cdc.RegisterConcrete(&AddMultiStakingEVMCoinProposal{}, "multistaking/AddMultiStakingEVMCoinProposal", nil)
cdc.RegisterConcrete(&RemoveMultiStakingCoinProposal{}, "multistaking/RemoveMultiStakingCoinProposal", nil)

// this line is used by starport scaffolding # 2
}

Expand All @@ -24,12 +26,14 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
&AddMultiStakingCoinProposal{},
&UpdateBondWeightProposal{},
&AddMultiStakingEVMCoinProposal{},
&RemoveMultiStakingCoinProposal{},
)
registry.RegisterImplementations(
(*v1beta1types.Content)(nil),
&AddMultiStakingCoinProposal{},
&UpdateBondWeightProposal{},
&AddMultiStakingEVMCoinProposal{},
&RemoveMultiStakingCoinProposal{},
)

msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
Expand Down
5 changes: 3 additions & 2 deletions x/multi-staking/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package types

// x/multistaking module event types
const (
EventTypeAddMultiStakingCoin = "add_multi_staking_coin"
EventTypeUpdateBondWeight = "update_bond_weight"
EventTypeAddMultiStakingCoin = "add_multi_staking_coin"
EventTypeRemoveMultiStakingCoin = "remove_multi_staking_coin"
EventTypeUpdateBondWeight = "update_bond_weight"

AttributeKeyDenom = "denom"
AttributeKeyBondWeight = "bond_weight"
Expand Down
45 changes: 44 additions & 1 deletion x/multi-staking/types/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
ProposalTypeAddMultiStakingCoin string = "AddMultiStakingCoin"
ProposalTypeUpdateBondWeight string = "UpdateBondWeight"
ProposalTypeAddMultiStakingEVMCoin string = "AddMultiStakingEVMCoin"
ProposalTypeRemoveMultiStakingCoin string = "RemoveMultiStakingCoin"
)

// Assert module proposals implement govtypes.Content at compile-time
Expand Down Expand Up @@ -118,7 +119,7 @@ func (cbtp *UpdateBondWeightProposal) ValidateBasic() error {
return sdkerrors.Wrap(ErrInvalidUpdateBondWeightProposal, "proposal bond token cannot be blank")
}

if cbtp.UpdatedBondWeight.LT(math.LegacyZeroDec()) {
if cbtp.UpdatedBondWeight.LTE(math.LegacyZeroDec()) {
return sdkerrors.Wrap(ErrInvalidUpdateBondWeightProposal, "proposal bond token weight must be positive")
}

Expand Down Expand Up @@ -171,3 +172,45 @@ func (abtp *AddMultiStakingEVMCoinProposal) ValidateBasic() error {
func (abtp AddMultiStakingEVMCoinProposal) String() string {
return fmt.Sprintf("AddMultiStakingEVMCoinProposal: Title: %s Description: %s Denom: %s TokenWeight: %s", abtp.Title, abtp.Description, abtp.ContractAddress, abtp.BondWeight)
}

// NewRemoveMultiStakingCoinProposal returns new instance of RemoveMultiStakingCoinProposal
func NewRemoveMultiStakingCoinProposal(title, description, denom string) govv1beta1.Content {
return &RemoveMultiStakingCoinProposal{
Title: title,
Description: description,
Denom: denom,
}
}

// GetTitle returns the title of a AddMultiStakingCoinProposal
func (abtp *RemoveMultiStakingCoinProposal) GetTitle() string { return abtp.Title }

// GetDescription returns the description of a AddMultiStakingCoinProposal
func (abtp *RemoveMultiStakingCoinProposal) GetDescription() string { return abtp.Description }

// ProposalRoute returns router key for a AddMultiStakingCoinProposal
func (*RemoveMultiStakingCoinProposal) ProposalRoute() string { return RouterKey }

// ProposalType returns proposal type for a AddMultiStakingCoinProposal
func (*RemoveMultiStakingCoinProposal) ProposalType() string {
return ProposalTypeAddMultiStakingCoin
}

// ValidateBasic runs basic stateless validity checks
func (abtp *RemoveMultiStakingCoinProposal) ValidateBasic() error {
err := govv1beta1.ValidateAbstract(abtp)
if err != nil {
return err
}

if abtp.Denom == "" {
return fmt.Errorf("proposal bond token cannot be blank")
}

return nil
}

// String implements the Stringer interface.
func (abtp RemoveMultiStakingCoinProposal) String() string {
return fmt.Sprintf("RemoveMultiStakingCoinProposal: Title: %s Description: %s Denom: %s", abtp.Title, abtp.Description, abtp.Denom)
}
2 changes: 1 addition & 1 deletion x/multi-staking/types/proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ func (suite *ProposalTestSuite) TestUpdateBondWeightProposal() {
}{
// Valid tests
{msg: "Change bond token weight", title: "test", description: "test desc", denom: "token", bondWeight: math.LegacyOneDec(), expectPass: true},
{msg: "Change bond token weight - zero weight", title: "test", description: "test desc", denom: "token", bondWeight: math.LegacyZeroDec(), expectPass: true},

// Invalid tests
{msg: "Change bond token weight - zero weight", title: "test", description: "test desc", denom: "token", bondWeight: math.LegacyZeroDec(), expectPass: false},
{msg: "Change bond token weight - invalid token", title: "test", description: "test desc", denom: "", bondWeight: math.LegacyOneDec(), expectPass: false},
{msg: "Change bond token weight - negative weight", title: "test", description: "test desc", denom: "token", bondWeight: math.LegacyMustNewDecFromStr("-1"), expectPass: false},
}
Expand Down
Loading
Loading