/
upgrades.go
224 lines (188 loc) · 8.14 KB
/
upgrades.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package v18
import (
"fmt"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
recordskeeper "github.com/Stride-Labs/stride/v19/x/records/keeper"
recordtypes "github.com/Stride-Labs/stride/v19/x/records/types"
stakeibckeeper "github.com/Stride-Labs/stride/v19/x/stakeibc/keeper"
"github.com/Stride-Labs/stride/v19/x/stakeibc/types"
stakeibctypes "github.com/Stride-Labs/stride/v19/x/stakeibc/types"
)
// CreateUpgradeHandler creates an SDK upgrade handler for v18
func CreateUpgradeHandler(
mm *module.Manager,
configurator module.Configurator,
bankKeeper bankkeeper.Keeper,
govKeeper govkeeper.Keeper,
recordsKeeper recordskeeper.Keeper,
stakeibcKeeper stakeibckeeper.Keeper,
) upgradetypes.UpgradeHandler {
return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
ctx.Logger().Info("Starting upgrade v18...")
ctx.Logger().Info("Updating redemption rate bounds...")
UpdateRedemptionRateBounds(ctx, stakeibcKeeper)
ctx.Logger().Info("Resetting delegation changes in progress...")
if err := DecrementTerraDelegationChangesInProgress(ctx, stakeibcKeeper); err != nil {
return vm, errorsmod.Wrapf(err, "unable to reset delegation changes in progress")
}
ctx.Logger().Info("Updating unbonding records...")
err := UpdateUnbondingRecords(
ctx,
stakeibcKeeper,
recordsKeeper,
StartingEstimateEpoch,
RedemptionRatesBeforeProp,
RedemptionRatesAtTimeOfProp,
)
if err != nil {
return vm, errorsmod.Wrapf(err, "unable to update unbonding records")
}
ctx.Logger().Info(fmt.Sprintf("Checking on prop %d status...", Prop228ProposalId))
if err := ExecuteProp228IfPassed(ctx, bankKeeper, govKeeper); err != nil {
ctx.Logger().Error(fmt.Sprintf("Failed to check on or execute prop %d: %s",
Prop228ProposalId, err.Error()))
}
return mm.RunMigrations(ctx, configurator, vm)
}
}
// Updates the outer redemption rate bounds
func UpdateRedemptionRateBounds(ctx sdk.Context, k stakeibckeeper.Keeper) {
for _, hostZone := range k.GetAllHostZone(ctx) {
// Give osmosis a bit more slack since OSMO stakers collect real yield
outerAdjustment := RedemptionRateOuterMaxAdjustment
if hostZone.ChainId == OsmosisChainId {
outerAdjustment = outerAdjustment.Add(OsmosisRedemptionRateBuffer)
}
outerMinDelta := hostZone.RedemptionRate.Mul(RedemptionRateOuterMinAdjustment)
outerMaxDelta := hostZone.RedemptionRate.Mul(outerAdjustment)
outerMin := hostZone.RedemptionRate.Sub(outerMinDelta)
outerMax := hostZone.RedemptionRate.Add(outerMaxDelta)
hostZone.MinRedemptionRate = outerMin
hostZone.MaxRedemptionRate = outerMax
k.SetHostZone(ctx, hostZone)
}
}
// Decrement DelegationChangesInProgress on Terra vals by 3
// - Fetches terra host zone
// - Loops validators
// - Decrements each validator's DelegationChangeInProgress by 3
func DecrementTerraDelegationChangesInProgress(
ctx sdk.Context,
sk stakeibckeeper.Keeper,
) error {
// grab the terra host zone
hostZone, found := sk.GetHostZone(ctx, TerraChainId)
if !found {
return types.ErrHostZoneNotFound.Wrapf("failed to fetch %s", TerraChainId)
}
// iterate the validators
for _, val := range hostZone.Validators {
// subtract 3, flooring at 0
if val.DelegationChangesInProgress < 3 {
val.DelegationChangesInProgress = 0
} else {
val.DelegationChangesInProgress = val.DelegationChangesInProgress - 3
}
}
// set the host zone
sk.SetHostZone(ctx, hostZone)
return nil
}
// Modify HostZoneUnbonding and UserRedemptionRecords NativeTokenAmount to reflect new data structs
func UpdateUnbondingRecords(
ctx sdk.Context,
sk stakeibckeeper.Keeper,
rk recordskeeper.Keeper,
startingEstimateEpoch uint64,
redemptionRatesBeforeProp map[string]map[uint64]sdk.Dec,
redemptionRatesAtTimeOfProp map[string]sdk.Dec,
) error {
// loop over host zone unbonding records
for _, epochUnbondingRecord := range rk.GetAllEpochUnbondingRecord(ctx) {
for _, hostZoneUnbonding := range epochUnbondingRecord.HostZoneUnbondings {
epochNumber := epochUnbondingRecord.EpochNumber
chainId := hostZoneUnbonding.HostZoneId
// we can ignore any record that's not currently unbonding
if hostZoneUnbonding.Status != recordtypes.HostZoneUnbonding_EXIT_TRANSFER_QUEUE {
continue
}
// Grab the redemption rates from before the prop was posted, for a given chain
// across all the epochs that unbonded
hostZoneRRBeforeProp, ok := redemptionRatesBeforeProp[hostZoneUnbonding.HostZoneId]
if !ok {
ctx.Logger().Error(fmt.Sprintf("Host zone %s not included in redemption rate mapping", chainId))
continue
}
// Grab the redemption rate for this specific epoch
// If it's not found, that means the unbonding for this epoch occurred after the prop was live
recordRedemptionRate, recordUnbondedBeforeProp := hostZoneRRBeforeProp[epochUnbondingRecord.EpochNumber]
if !recordUnbondedBeforeProp && (epochNumber < startingEstimateEpoch) {
ctx.Logger().Info(fmt.Sprintf("Skipping unbonding record adjustment for chain %s epoch %d",
chainId, epochNumber))
continue
}
// If we don't have the redemption rate, estimate it
if !recordUnbondedBeforeProp {
hostZone, found := sk.GetHostZone(ctx, hostZoneUnbonding.HostZoneId)
if !found {
return errorsmod.Wrapf(stakeibctypes.ErrHostZoneNotFound,
"unable to find host zone with chain-id %s", hostZoneUnbonding.HostZoneId)
}
redemptionRateAtTimeOfProp := redemptionRatesAtTimeOfProp[hostZoneUnbonding.HostZoneId]
redemptionRateDuringUpgrade := hostZone.RedemptionRate
recordRedemptionRate = redemptionRateAtTimeOfProp.Add(redemptionRateDuringUpgrade).Quo(sdk.NewDec(2))
}
// now update all userRedemptionRecords by using the redemption rate to set the native token amount
totalNativeAmount := sdkmath.ZeroInt()
for _, userRedemptionRecordId := range hostZoneUnbonding.UserRedemptionRecords {
userRedemptionRecord, found := rk.GetUserRedemptionRecord(ctx, userRedemptionRecordId)
if !found {
return errorsmod.Wrapf(recordtypes.ErrHostUnbondingRecordNotFound,
"unable to find user redemption record with id %s", userRedemptionRecordId)
}
userNativeAmount := sdk.NewDecFromInt(userRedemptionRecord.StTokenAmount).Mul(recordRedemptionRate).TruncateInt()
totalNativeAmount = totalNativeAmount.Add(userNativeAmount)
userRedemptionRecord.NativeTokenAmount = userNativeAmount
rk.SetUserRedemptionRecord(ctx, userRedemptionRecord)
}
// finally, update the hostZoneUnbonding record
hostZoneUnbonding.NativeTokenAmount = totalNativeAmount
if err := rk.SetHostZoneUnbondingRecord(ctx, epochNumber, chainId, *hostZoneUnbonding); err != nil {
return errorsmod.Wrapf(err, "unable to set host zone unbonding for %s and epoch %d",
hostZoneUnbonding.HostZoneId, epochUnbondingRecord.EpochNumber)
}
}
}
return nil
}
// Executes the bank send for prop 228 if it passed
func ExecuteProp228IfPassed(ctx sdk.Context, bk bankkeeper.Keeper, gk govkeeper.Keeper) error {
// Grab proposal from gov store
proposal, found := gk.GetProposal(ctx, Prop228ProposalId)
if !found {
return fmt.Errorf("Prop %d not found", Prop228ProposalId)
}
// Check if it passed - if it didn't do nothing
if proposal.Status != govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED {
ctx.Logger().Info(fmt.Sprintf("Prop %d did not pass", Prop228ProposalId))
return nil
}
ctx.Logger().Info(fmt.Sprintf("Prop %d passed - executing corresponding bank send", Prop228ProposalId))
// Transfer from incentive program address to F4
fromAddress, err := sdk.AccAddressFromBech32(IncentiveProgramAddress)
if err != nil {
return errorsmod.Wrap(err, "invalid prop sender address")
}
toAddress, err := sdk.AccAddressFromBech32(StrideFoundationAddress_F4)
if err != nil {
return errorsmod.Wrap(err, "invalid prop recipient address")
}
return bk.SendCoins(ctx, fromAddress, toAddress, sdk.NewCoins(sdk.NewCoin(Strd, Prop228SendAmount)))
}