mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 18:15:19 +00:00
ceaed3f0e1
* proto types * proto generated types * liquidstaking top level files: module, genesis * liquidstaking types * liquidstaking keeper * liquidstaking client/cli * add liquidstaking to app, simapp * implement mint derivative * set up liquidstaking keeper test suite * test mint derivative * rename module to liquid * rename proto types, app.go to liquid * use sdk.Coin instead of shares * mint liquid tokens to delegator * burn derivative tokens to receive delegation * use conversion method instead of type cast * simplify delegation transfer logic * broaden delegation transfer tests * simplify transfer delegation method This removes a source of rounding errors * move derivative denom to keeper config * check for invalid coins in msg validation * block 0share transfers to avoid handling edge case * refactor MintDerivative to test calculations * simplify burn method so shares and tokens equal * convert TransferShares back to old design this makes handling vesting tokens easier * fix missed merge conflict * remove deprecated constants * tidy up msg.go * add msg tests * remove unused store key * fix msg event sender * remove unused params * tidy up documentation and errors * remove unused mocks * remove unused keepers from AppModule * tidy up msg return values keeper return values to be used in router msgs * reinstate unintentionally removed interface check * catch invalid input for MnitDerivative clear up test TODOs * clear up InitGenesis TODO * Update x/liquid/client/cli/tx.go Co-authored-by: Derrick Lee <derrick@dlee.dev> * Update x/liquid/client/cli/tx.go Co-authored-by: Derrick Lee <derrick@dlee.dev> * show error logs in devnet * unblock mod account so it can receive dist rewards * catch zero amout msgs early * minor cli fixes Co-authored-by: rhuairahrighairigh <ruaridh.odonnell@gmail.com> Co-authored-by: Ruaridh <rhuairahrighairidh@users.noreply.github.com> Co-authored-by: Derrick Lee <derrick@dlee.dev>
110 lines
4.5 KiB
Go
110 lines
4.5 KiB
Go
package keeper
|
|
|
|
import (
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
|
|
"github.com/kava-labs/kava/x/liquid/types"
|
|
)
|
|
|
|
// TransferDelegation moves some delegation shares between addresses, while keeping the same validator.
|
|
//
|
|
// Internally shares are unbonded, tokens moved then bonded again. This limits only vested tokens from being transferred.
|
|
// The sending delegation must not have any active redelegations.
|
|
// A validator cannot reduce self delegated shares below its min self delegation.
|
|
// Attempting to transfer zero shares will error.
|
|
func (k Keeper) TransferDelegation(ctx sdk.Context, valAddr sdk.ValAddress, fromDelegator, toDelegator sdk.AccAddress, shares sdk.Dec) (sdk.Dec, error) {
|
|
// Redelegations link a delegation to it's previous validator so slashes are propagated to the new validator.
|
|
// If the delegation is transferred to a new owner, the redelegation object must be updated.
|
|
// For expediency all transfers with redelegations are blocked.
|
|
if k.stakingKeeper.HasReceivingRedelegation(ctx, fromDelegator, valAddr) {
|
|
return sdk.Dec{}, types.ErrRedelegationsNotCompleted
|
|
}
|
|
|
|
if shares.IsNil() || shares.LT(sdk.ZeroDec()) {
|
|
return sdk.Dec{}, sdkerrors.Wrap(types.ErrUntransferableShares, "nil or negative shares")
|
|
}
|
|
if shares.Equal(sdk.ZeroDec()) {
|
|
// Block 0 transfers to reduce edge cases.
|
|
return sdk.Dec{}, sdkerrors.Wrap(types.ErrUntransferableShares, "zero shares")
|
|
}
|
|
|
|
fromDelegation, found := k.stakingKeeper.GetDelegation(ctx, fromDelegator, valAddr)
|
|
if !found {
|
|
return sdk.Dec{}, types.ErrNoDelegatorForAddress
|
|
}
|
|
validator, found := k.stakingKeeper.GetValidator(ctx, valAddr)
|
|
if !found {
|
|
return sdk.Dec{}, types.ErrNoValidatorFound
|
|
}
|
|
// Prevent validators from reducing their self delegation below the min.
|
|
isValidatorOperator := fromDelegator.Equals(valAddr)
|
|
if isValidatorOperator {
|
|
if isBelowMinSelfDelegation(validator, fromDelegation.Shares.Sub(shares)) {
|
|
return sdk.Dec{}, types.ErrSelfDelegationBelowMinimum
|
|
}
|
|
}
|
|
|
|
returnAmount, err := k.fastUndelegate(ctx, valAddr, fromDelegator, shares)
|
|
if err != nil {
|
|
return sdk.Dec{}, err
|
|
}
|
|
returnCoins := sdk.NewCoins(sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), returnAmount))
|
|
|
|
if err := k.bankKeeper.SendCoins(ctx, fromDelegator, toDelegator, returnCoins); err != nil {
|
|
return sdk.Dec{}, err
|
|
}
|
|
receivedShares, err := k.delegateFromAccount(ctx, valAddr, toDelegator, returnAmount)
|
|
if err != nil {
|
|
return sdk.Dec{}, err
|
|
}
|
|
|
|
return receivedShares, nil
|
|
}
|
|
|
|
// isBelowMinSelfDelegation check if the supplied shares, converted to tokens, are under the validator's min_self_delegation.
|
|
func isBelowMinSelfDelegation(validator stakingtypes.ValidatorI, shares sdk.Dec) bool {
|
|
return validator.TokensFromShares(shares).TruncateInt().LT(validator.GetMinSelfDelegation())
|
|
}
|
|
|
|
// fastUndelegate undelegates shares from a validator skipping the unbonding period and not creating any unbonding delegations.
|
|
func (k Keeper) fastUndelegate(ctx sdk.Context, valAddr sdk.ValAddress, delegator sdk.AccAddress, shares sdk.Dec) (sdk.Int, error) {
|
|
validator, found := k.stakingKeeper.GetValidator(ctx, valAddr)
|
|
if !found {
|
|
return sdk.Int{}, types.ErrNoDelegatorForAddress
|
|
}
|
|
|
|
returnAmount, err := k.stakingKeeper.Unbond(ctx, delegator, valAddr, shares)
|
|
if err != nil {
|
|
return sdk.Int{}, err
|
|
}
|
|
returnCoins := sdk.NewCoins(sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), returnAmount))
|
|
|
|
// transfer the validator tokens to the not bonded pool
|
|
if validator.IsBonded() {
|
|
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, stakingtypes.BondedPoolName, stakingtypes.NotBondedPoolName, returnCoins); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
if err := k.bankKeeper.UndelegateCoinsFromModuleToAccount(ctx, stakingtypes.NotBondedPoolName, delegator, returnCoins); err != nil {
|
|
return sdk.Int{}, err
|
|
}
|
|
return returnAmount, nil
|
|
}
|
|
|
|
// delegateFromAccount delegates to a validator from an account (vs redelegating from an existing delegation)
|
|
func (k Keeper) delegateFromAccount(ctx sdk.Context, valAddr sdk.ValAddress, delegator sdk.AccAddress, amount sdk.Int) (sdk.Dec, error) {
|
|
validator, found := k.stakingKeeper.GetValidator(ctx, valAddr)
|
|
if !found {
|
|
return sdk.Dec{}, types.ErrNoValidatorFound
|
|
}
|
|
// source tokens are from an account, so subtractAccount true and tokenSrc unbonded
|
|
newShares, err := k.stakingKeeper.Delegate(ctx, delegator, amount, stakingtypes.Unbonded, validator, true)
|
|
if err != nil {
|
|
return sdk.Dec{}, err
|
|
}
|
|
return newShares, nil
|
|
}
|