-
Notifications
You must be signed in to change notification settings - Fork 8
/
stake.go
117 lines (101 loc) · 4.02 KB
/
stake.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package keeper
import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
types "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types"
)
// Delegate mints new "virtual" bonding tokens and delegates them to the given validator.
// The amount minted is removed from the SupplyOffset (so that it will become negative), when supported.
// Authorization of the actor should be handled before entering this method.
func (k Keeper) Delegate(pCtx sdk.Context, actor sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Coin) (sdk.Dec, error) {
if amt.Amount.IsNil() || amt.Amount.IsZero() || amt.Amount.IsNegative() {
return sdk.ZeroDec(), errors.ErrInvalidRequest.Wrap("amount")
}
// Ensure staking constraints
bondDenom := k.Staking.BondDenom(pCtx)
if amt.Denom != bondDenom {
return sdk.ZeroDec(), errors.ErrInvalidRequest.Wrapf("invalid coin denomination: got %s, expected %s", amt.Denom, bondDenom)
}
validator, found := k.Staking.GetValidator(pCtx, valAddr)
if !found {
return sdk.ZeroDec(), stakingtypes.ErrNoValidatorFound
}
// Ensure MS constraints:
newTotalDelegatedAmount := k.GetTotalDelegated(pCtx, actor).Add(amt)
max := k.GetMaxCapLimit(pCtx, actor)
if max.IsLT(newTotalDelegatedAmount) {
return sdk.ZeroDec(), types.ErrMaxCapExceeded.Wrapf("%s exceeds %s", newTotalDelegatedAmount, max)
}
cacheCtx, done := pCtx.CacheContext() // work in a cached store as osmosis (safety net?)
// mint tokens as virtual coins that do not count to the total supply
coins := sdk.NewCoins(amt)
err := k.bank.MintCoins(cacheCtx, types.ModuleName, coins)
if err != nil {
return sdk.ZeroDec(), err
}
k.bank.AddSupplyOffset(cacheCtx, bondDenom, amt.Amount.Neg())
err = k.bank.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, actor, coins)
if err != nil {
return sdk.ZeroDec(), err
}
// delegate virtual coins to the validator
newShares, err := k.Staking.Delegate(
cacheCtx,
actor,
amt.Amount,
stakingtypes.Unbonded,
validator,
true,
)
// and update our records
k.setTotalDelegated(cacheCtx, actor, newTotalDelegatedAmount)
done()
return newShares, err
}
// Undelegate executes an instant undelegate and burns the released virtual staking tokens.
// The amount burned is added to the (negative) SupplyOffset, when supported.
// Authorization of the actor should be handled before entering this method.
func (k Keeper) Undelegate(pCtx sdk.Context, actor sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Coin) error {
if amt.Amount.IsNil() || amt.Amount.IsZero() || amt.Amount.IsNegative() {
return errors.ErrInvalidRequest.Wrap("amount")
}
// Ensure staking constraints
bondDenom := k.Staking.BondDenom(pCtx)
if amt.Denom != bondDenom {
return errors.ErrInvalidRequest.Wrapf("invalid coin denomination: got %s, expected %s", amt.Denom, bondDenom)
}
cacheCtx, done := pCtx.CacheContext() // work in a cached store (safety net?)
totalDelegatedAmount := k.GetTotalDelegated(cacheCtx, actor)
if totalDelegatedAmount.IsLT(amt) {
return errors.ErrInvalidRequest.Wrap("amount exceeds total delegated")
}
shares, err := k.Staking.ValidateUnbondAmount(cacheCtx, actor, valAddr, amt.Amount)
if err == stakingtypes.ErrNoDelegation {
return nil
} else if err != nil {
return err
}
undelegatedCoins, err := k.Staking.InstantUndelegate(cacheCtx, actor, valAddr, shares)
if err != nil {
return err
}
err = k.bank.SendCoinsFromAccountToModule(cacheCtx, actor, types.ModuleName, undelegatedCoins)
if err != nil {
return err
}
err = k.bank.BurnCoins(cacheCtx, types.ModuleName, undelegatedCoins)
if err != nil {
return err
}
unbondedAmount := sdk.NewCoin(bondDenom, undelegatedCoins.AmountOf(bondDenom))
k.bank.AddSupplyOffset(cacheCtx, bondDenom, unbondedAmount.Amount)
newDelegatedAmt := totalDelegatedAmount.Sub(unbondedAmount)
if newDelegatedAmt.IsNegative() {
newDelegatedAmt = sdk.NewCoin(bondDenom, math.ZeroInt())
}
k.setTotalDelegated(cacheCtx, actor, newDelegatedAmt)
done()
return nil
}