/
redemption_sweep.go
144 lines (123 loc) · 6.38 KB
/
redemption_sweep.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
package keeper
import (
"fmt"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/gogoproto/proto"
"github.com/Stride-Labs/stride/v21/utils"
recordstypes "github.com/Stride-Labs/stride/v21/x/records/types"
"github.com/Stride-Labs/stride/v21/x/stakeibc/types"
)
// Batch transfers any unbonded tokens from the delegation account to the redemption account
func (k Keeper) SweepAllUnbondedTokensForHostZone(ctx sdk.Context, hostZone types.HostZone, epochUnbondingRecords []recordstypes.EpochUnbondingRecord) (success bool, sweepAmount sdkmath.Int) {
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Sweeping unbonded tokens"))
// Sum up all host zone unbonding records that have finished unbonding
totalAmtTransferToRedemptionAcct := sdkmath.ZeroInt()
epochUnbondingRecordIds := []uint64{}
for _, epochUnbondingRecord := range epochUnbondingRecords {
// Get all the unbondings associated with the epoch + host zone pair
hostZoneUnbonding, found := k.RecordsKeeper.GetHostZoneUnbondingByChainId(ctx, epochUnbondingRecord.EpochNumber, hostZone.ChainId)
if !found {
continue
}
// Get latest blockTime from light client
blockTime, err := k.GetLightClientTimeSafely(ctx, hostZone.ConnectionId)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("\tCould not find blockTime for host zone %s", hostZone.ChainId))
continue
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Epoch %d - Status: %s, Amount: %v, Unbonding Time: %d, Block Time: %d",
epochUnbondingRecord.EpochNumber, hostZoneUnbonding.Status.String(), hostZoneUnbonding.NativeTokenAmount, hostZoneUnbonding.UnbondingTime, blockTime))
// If the unbonding period has elapsed, then we can send the ICA call to sweep this
// hostZone's unbondings to the redemption account (in a batch).
// Verify:
// 1. the unbonding time is set (g.t. 0)
// 2. the unbonding time is less than the current block time
// 3. the host zone is in the EXIT_TRANSFER_QUEUE state, meaning it's ready to be transferred
inTransferQueue := hostZoneUnbonding.Status == recordstypes.HostZoneUnbonding_EXIT_TRANSFER_QUEUE
validUnbondingTime := hostZoneUnbonding.UnbondingTime > 0 && hostZoneUnbonding.UnbondingTime < blockTime
if inTransferQueue && validUnbondingTime {
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, " %v%s included in sweep", hostZoneUnbonding.NativeTokenAmount, hostZoneUnbonding.Denom))
totalAmtTransferToRedemptionAcct = totalAmtTransferToRedemptionAcct.Add(hostZoneUnbonding.NativeTokenAmount)
epochUnbondingRecordIds = append(epochUnbondingRecordIds, epochUnbondingRecord.EpochNumber)
}
}
// If we have any amount to sweep, then we can send the ICA call to sweep them
if totalAmtTransferToRedemptionAcct.LTE(sdkmath.ZeroInt()) {
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "No tokens ready for sweep"))
return true, totalAmtTransferToRedemptionAcct
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Batch transferring %v to host zone", totalAmtTransferToRedemptionAcct))
// Get the delegation account and redemption account
if hostZone.DelegationIcaAddress == "" {
k.Logger(ctx).Error(fmt.Sprintf("Zone %s is missing a delegation address!", hostZone.ChainId))
return false, sdkmath.ZeroInt()
}
if hostZone.RedemptionIcaAddress == "" {
k.Logger(ctx).Error(fmt.Sprintf("Zone %s is missing a redemption address!", hostZone.ChainId))
return false, sdkmath.ZeroInt()
}
// Build transfer message to transfer from the delegation account to redemption account
sweepCoin := sdk.NewCoin(hostZone.HostDenom, totalAmtTransferToRedemptionAcct)
msgs := []proto.Message{
&banktypes.MsgSend{
FromAddress: hostZone.DelegationIcaAddress,
ToAddress: hostZone.RedemptionIcaAddress,
Amount: sdk.NewCoins(sweepCoin),
},
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Preparing MsgSend from Delegation Account to Redemption Account"))
// Store the epoch numbers in the callback to identify the epoch unbonding records
redemptionCallback := types.RedemptionCallback{
HostZoneId: hostZone.ChainId,
EpochUnbondingRecordIds: epochUnbondingRecordIds,
}
marshalledCallbackArgs, err := k.MarshalRedemptionCallbackArgs(ctx, redemptionCallback)
if err != nil {
k.Logger(ctx).Error(err.Error())
return false, sdkmath.ZeroInt()
}
// Send the transfer ICA
_, err = k.SubmitTxsDayEpoch(ctx, hostZone.ConnectionId, msgs, types.ICAAccountType_DELEGATION, ICACallbackID_Redemption, marshalledCallbackArgs)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("Failed to SubmitTxs, transfer to redemption account on %s", hostZone.ChainId))
return false, sdkmath.ZeroInt()
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "ICA MsgSend Successfully Sent"))
// Update the host zone unbonding records to status IN_PROGRESS
err = k.RecordsKeeper.SetHostZoneUnbondingStatus(ctx, hostZone.ChainId, epochUnbondingRecordIds, recordstypes.HostZoneUnbonding_EXIT_TRANSFER_IN_PROGRESS)
if err != nil {
k.Logger(ctx).Error(err.Error())
return false, sdkmath.ZeroInt()
}
return true, totalAmtTransferToRedemptionAcct
}
// Sends all unbonded tokens to the redemption account
// returns:
// - success indicator if all chains succeeded
// - list of successful chains
// - list of tokens swept
// - list of failed chains
func (k Keeper) SweepAllUnbondedTokens(ctx sdk.Context) (success bool, successfulSweeps []string, sweepAmounts []sdkmath.Int, failedSweeps []string) {
// this function returns true if all chains succeeded, false otherwise
// it also returns a list of successful chains (arg 2), tokens swept (arg 3), and failed chains (arg 4)
k.Logger(ctx).Info("Sweeping All Unbonded Tokens...")
success = true
successfulSweeps = []string{}
sweepAmounts = []sdkmath.Int{}
failedSweeps = []string{}
hostZones := k.GetAllActiveHostZone(ctx)
epochUnbondingRecords := k.RecordsKeeper.GetAllEpochUnbondingRecord(ctx)
for _, hostZone := range hostZones {
hostZoneSuccess, sweepAmount := k.SweepAllUnbondedTokensForHostZone(ctx, hostZone, epochUnbondingRecords)
if hostZoneSuccess {
successfulSweeps = append(successfulSweeps, hostZone.ChainId)
sweepAmounts = append(sweepAmounts, sweepAmount)
} else {
success = false
failedSweeps = append(failedSweeps, hostZone.ChainId)
}
}
return success, successfulSweeps, sweepAmounts, failedSweeps
}