-
Notifications
You must be signed in to change notification settings - Fork 194
/
delegation.go
192 lines (162 loc) · 7.55 KB
/
delegation.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
package keeper
import (
"fmt"
errorsmod "cosmossdk.io/errors"
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/cosmos/gogoproto/proto"
"github.com/spf13/cast"
icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"
"github.com/Stride-Labs/stride/v19/utils"
recordstypes "github.com/Stride-Labs/stride/v19/x/records/types"
"github.com/Stride-Labs/stride/v19/x/stakeibc/types"
)
func (k Keeper) DelegateOnHost(ctx sdk.Context, hostZone types.HostZone, amt sdk.Coin, depositRecord recordstypes.DepositRecord) error {
// TODO: Remove this block and use connection-id from host zone
// the relevant ICA is the delegate account
owner := types.FormatHostZoneICAOwner(hostZone.ChainId, types.ICAAccountType_DELEGATION)
portID, err := icatypes.NewControllerPortID(owner)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "%s has no associated portId", owner)
}
connectionId, found := k.GetConnectionIdFromICAPortId(ctx, portID)
if !found {
return errorsmod.Wrapf(types.ErrICAAccountNotFound, "unable to find ICA connection Id for port %s", portID)
}
// Fetch the relevant ICA
if hostZone.DelegationIcaAddress == "" {
return errorsmod.Wrapf(types.ErrICAAccountNotFound, "no delegation account found for %s", hostZone.ChainId)
}
// Construct the transaction
targetDelegatedAmts, err := k.GetTargetValAmtsForHostZone(ctx, hostZone, amt.Amount)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("Error getting target delegation amounts for host zone %s", hostZone.ChainId))
return err
}
var splitDelegations []*types.SplitDelegation
var msgs []proto.Message
for _, validator := range hostZone.Validators {
relativeAmount, ok := targetDelegatedAmts[validator.Address]
if !ok || !relativeAmount.IsPositive() {
continue
}
msgs = append(msgs, &stakingtypes.MsgDelegate{
DelegatorAddress: hostZone.DelegationIcaAddress,
ValidatorAddress: validator.Address,
Amount: sdk.NewCoin(amt.Denom, relativeAmount),
})
splitDelegations = append(splitDelegations, &types.SplitDelegation{
Validator: validator.Address,
Amount: relativeAmount,
})
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Preparing MsgDelegates from the delegation account to each validator"))
if len(msgs) == 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Target delegation amount was 0 for each validator")
}
// add callback data
delegateCallback := types.DelegateCallback{
HostZoneId: hostZone.ChainId,
DepositRecordId: depositRecord.Id,
SplitDelegations: splitDelegations,
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Marshalling DelegateCallback args: %+v", delegateCallback))
marshalledCallbackArgs, err := k.MarshalDelegateCallbackArgs(ctx, delegateCallback)
if err != nil {
return err
}
// Send the transaction through SubmitTx
_, err = k.SubmitTxsStrideEpoch(ctx, connectionId, msgs, types.ICAAccountType_DELEGATION, ICACallbackID_Delegate, marshalledCallbackArgs)
if err != nil {
return errorsmod.Wrapf(err, "Failed to SubmitTxs for connectionId %s on %s. Messages: %s", connectionId, hostZone.ChainId, msgs)
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "ICA MsgDelegates Successfully Sent"))
// flag the delegation change in progress on each validator
for _, splitDelegation := range splitDelegations {
if err := k.IncrementValidatorDelegationChangesInProgress(&hostZone, splitDelegation.Validator); err != nil {
return err
}
}
k.SetHostZone(ctx, hostZone)
// update the record state to DELEGATION_IN_PROGRESS
depositRecord.Status = recordstypes.DepositRecord_DELEGATION_IN_PROGRESS
k.RecordsKeeper.SetDepositRecord(ctx, depositRecord)
return nil
}
// Iterate each deposit record marked DELEGATION_QUEUE and use the delegation ICA to delegate on the host zone
func (k Keeper) StakeExistingDepositsOnHostZones(ctx sdk.Context, epochNumber uint64, depositRecords []recordstypes.DepositRecord) {
k.Logger(ctx).Info("Staking deposit records...")
stakeDepositRecords := utils.FilterDepositRecords(depositRecords, func(record recordstypes.DepositRecord) (condition bool) {
isStakeRecord := record.Status == recordstypes.DepositRecord_DELEGATION_QUEUE
isBeforeCurrentEpoch := record.DepositEpochNumber < epochNumber
return isStakeRecord && isBeforeCurrentEpoch
})
if len(stakeDepositRecords) == 0 {
k.Logger(ctx).Info("No deposit records in state DELEGATION_QUEUE")
return
}
// limit the number of staking deposits to process per epoch
maxDepositRecordsToStake := utils.Min(len(stakeDepositRecords), cast.ToInt(k.GetParam(ctx, types.KeyMaxStakeICACallsPerEpoch)))
if maxDepositRecordsToStake < len(stakeDepositRecords) {
k.Logger(ctx).Info(fmt.Sprintf(" MaxStakeICACallsPerEpoch limit reached - Only staking %d out of %d deposit records", maxDepositRecordsToStake, len(stakeDepositRecords)))
}
for _, depositRecord := range stakeDepositRecords[:maxDepositRecordsToStake] {
if depositRecord.Amount.IsZero() {
continue
}
k.Logger(ctx).Info(utils.LogWithHostZone(depositRecord.HostZoneId,
"Processing deposit record %d: %v%s", depositRecord.Id, depositRecord.Amount, depositRecord.Denom))
hostZone, hostZoneFound := k.GetHostZone(ctx, depositRecord.HostZoneId)
if !hostZoneFound {
k.Logger(ctx).Error(fmt.Sprintf("[StakeExistingDepositsOnHostZones] Host zone not found for deposit record {%d}", depositRecord.Id))
continue
}
if hostZone.Halted {
k.Logger(ctx).Error(fmt.Sprintf("[StakeExistingDepositsOnHostZones] Host zone halted for deposit record {%d}", depositRecord.Id))
continue
}
if hostZone.DelegationIcaAddress == "" {
k.Logger(ctx).Error(fmt.Sprintf("[StakeExistingDepositsOnHostZones] Zone %s is missing a delegation address!", hostZone.ChainId))
continue
}
k.Logger(ctx).Info(utils.LogWithHostZone(depositRecord.HostZoneId, "Staking %v%s", depositRecord.Amount, hostZone.HostDenom))
stakeAmount := sdk.NewCoin(hostZone.HostDenom, depositRecord.Amount)
err := k.DelegateOnHost(ctx, hostZone, stakeAmount, depositRecord)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("Did not stake %s on %s | err: %s", stakeAmount.String(), hostZone.ChainId, err.Error()))
continue
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Successfully submitted stake"))
ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute("hostZone", hostZone.ChainId),
sdk.NewAttribute("newAmountStaked", depositRecord.Amount.String()),
),
)
}
}
// Delegates accrued staking rewards for reinvestment
func (k Keeper) ReinvestRewards(ctx sdk.Context) {
k.Logger(ctx).Info("Reinvesting tokens...")
for _, hostZone := range k.GetAllActiveHostZone(ctx) {
// only process host zones once withdrawal accounts are registered
if hostZone.WithdrawalIcaAddress == "" {
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Withdrawal account not registered for host zone"))
continue
}
// read clock time on host zone
blockTime, err := k.GetLightClientTimeSafely(ctx, hostZone.ConnectionId)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("Could not find blockTime for host zone %s, err: %s", hostZone.ConnectionId, err.Error()))
continue
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "BlockTime for host zone: %d", blockTime))
err = k.UpdateWithdrawalBalance(ctx, hostZone)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("Error updating withdrawal balance for host zone %s: %s", hostZone.ConnectionId, err.Error()))
continue
}
}
}