-
Notifications
You must be signed in to change notification settings - Fork 201
/
deposit_records.go
172 lines (142 loc) · 7.59 KB
/
deposit_records.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
package keeper
import (
"fmt"
sdkmath "cosmossdk.io/math"
ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
sdk "github.com/cosmos/cosmos-sdk/types"
ibctypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
"github.com/spf13/cast"
"github.com/Stride-Labs/stride/v18/utils"
recordstypes "github.com/Stride-Labs/stride/v18/x/records/types"
"github.com/Stride-Labs/stride/v18/x/stakeibc/types"
)
// Create a new deposit record for each host zone for the given epoch
func (k Keeper) CreateDepositRecordsForEpoch(ctx sdk.Context, epochNumber uint64) {
k.Logger(ctx).Info(fmt.Sprintf("Creating Deposit Records for Epoch %d", epochNumber))
for _, hostZone := range k.GetAllActiveHostZone(ctx) {
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Creating Deposit Record"))
depositRecord := recordstypes.DepositRecord{
Amount: sdkmath.ZeroInt(),
Denom: hostZone.HostDenom,
HostZoneId: hostZone.ChainId,
Status: recordstypes.DepositRecord_TRANSFER_QUEUE,
DepositEpochNumber: epochNumber,
}
k.RecordsKeeper.AppendDepositRecord(ctx, depositRecord)
}
}
// Iterate each deposit record marked TRANSFER_QUEUE and IBC transfer tokens from the Stride controller account to the delegation ICAs on each host zone
func (k Keeper) TransferExistingDepositsToHostZones(ctx sdk.Context, epochNumber uint64, depositRecords []recordstypes.DepositRecord) {
k.Logger(ctx).Info("Transfering deposit records...")
transferDepositRecords := utils.FilterDepositRecords(depositRecords, func(record recordstypes.DepositRecord) (condition bool) {
isTransferRecord := record.Status == recordstypes.DepositRecord_TRANSFER_QUEUE
isBeforeCurrentEpoch := record.DepositEpochNumber < epochNumber
return isTransferRecord && isBeforeCurrentEpoch
})
ibcTransferTimeoutNanos := k.GetParam(ctx, types.KeyIBCTransferTimeoutNanos)
for _, depositRecord := range transferDepositRecords {
k.Logger(ctx).Info(utils.LogWithHostZone(depositRecord.HostZoneId,
"Processing deposit record %d: %v%s", depositRecord.Id, depositRecord.Amount, depositRecord.Denom))
// if a TRANSFER_QUEUE record has 0 balance and was created in the previous epoch, it's safe to remove since it will never be updated or used
if depositRecord.Amount.LTE(sdkmath.ZeroInt()) && depositRecord.DepositEpochNumber < epochNumber {
k.Logger(ctx).Info(utils.LogWithHostZone(depositRecord.HostZoneId, "Empty deposit record - Removing."))
k.RecordsKeeper.RemoveDepositRecord(ctx, depositRecord.Id)
continue
}
hostZone, hostZoneFound := k.GetHostZone(ctx, depositRecord.HostZoneId)
if !hostZoneFound {
k.Logger(ctx).Error(fmt.Sprintf("[TransferExistingDepositsToHostZones] Host zone not found for deposit record id %d", depositRecord.Id))
continue
}
if hostZone.Halted {
k.Logger(ctx).Error(fmt.Sprintf("[TransferExistingDepositsToHostZones] Host zone halted for deposit record id %d", depositRecord.Id))
continue
}
if hostZone.DelegationIcaAddress == "" {
k.Logger(ctx).Error(fmt.Sprintf("[TransferExistingDepositsToHostZones] Zone %s is missing a delegation address!", hostZone.ChainId))
continue
}
k.Logger(ctx).Info(utils.LogWithHostZone(depositRecord.HostZoneId, "Transferring %v%s", depositRecord.Amount, hostZone.HostDenom))
transferCoin := sdk.NewCoin(hostZone.IbcDenom, depositRecord.Amount)
// timeout 30 min in the future
// NOTE: this assumes no clock drift between chains, which tendermint guarantees
// if we onboard non-tendermint chains, we need to use the time on the host chain to
// calculate the timeout
// https://github.com/cometbft/cometbft/blob/v0.34.x/spec/consensus/bft-time.md
timeoutTimestamp := uint64(ctx.BlockTime().UnixNano()) + ibcTransferTimeoutNanos
msg := ibctypes.NewMsgTransfer(
ibctransfertypes.PortID,
hostZone.TransferChannelId,
transferCoin,
hostZone.DepositAddress,
hostZone.DelegationIcaAddress,
clienttypes.Height{},
timeoutTimestamp,
"",
)
k.Logger(ctx).Info(utils.LogWithHostZone(depositRecord.HostZoneId, "Transfer Msg: %+v", msg))
// transfer the deposit record and update its status to TRANSFER_IN_PROGRESS
err := k.RecordsKeeper.IBCTransferNativeTokens(ctx, msg, depositRecord)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("[TransferExistingDepositsToHostZones] Failed to initiate IBC transfer to host zone, HostZone: %v, Channel: %v, Amount: %v, ModuleAddress: %v, DelegateAddress: %v, Timeout: %v",
hostZone.ChainId, hostZone.TransferChannelId, transferCoin, hostZone.DepositAddress, hostZone.DelegationIcaAddress, timeoutTimestamp))
k.Logger(ctx).Error(fmt.Sprintf("[TransferExistingDepositsToHostZones] err {%s}", err.Error()))
continue
}
k.Logger(ctx).Info(utils.LogWithHostZone(hostZone.ChainId, "Successfully submitted transfer"))
}
}
// 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()),
),
)
}
}