github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/staking/keeper/delegation.go (about) 1 package keeper 2 3 import ( 4 "time" 5 6 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 7 "github.com/fibonacci-chain/fbc/x/staking/types" 8 ) 9 10 // UpdateProxy updates the shares by the total delegated and self delegated tokens of a proxy 11 func (k Keeper) UpdateProxy(ctx sdk.Context, delegator types.Delegator, tokens sdk.Dec) (err error) { 12 if !delegator.HasProxy() { 13 return nil 14 } 15 // delegator has bound a proxy, need update proxy's shares 16 if proxy, found := k.GetDelegator(ctx, delegator.ProxyAddress); found { 17 // tokens might be negative 18 proxy.TotalDelegatedTokens = proxy.TotalDelegatedTokens.Add(tokens) 19 if proxy.TotalDelegatedTokens.LT(sdk.ZeroDec()) { 20 return types.ErrInvalidProxyUpdating() 21 } 22 23 finalTokens := proxy.TotalDelegatedTokens.Add(proxy.Tokens) 24 k.SetDelegator(ctx, proxy) 25 return k.UpdateShares(ctx, proxy.DelegatorAddress, finalTokens) 26 } 27 return sdk.ErrInvalidAddress(delegator.ProxyAddress.String()) 28 } 29 30 // Delegate handles the process of delegating 31 func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, token sdk.SysCoin) error { 32 33 delQuantity, minDelLimit := token.Amount, k.ParamsMinDelegation(ctx) 34 if delQuantity.LT(minDelLimit) { 35 return types.ErrInsufficientQuantity(delQuantity.String(), minDelLimit.String()) 36 } 37 38 // 1.transfer account's fibo into bondPool 39 coins := sdk.SysCoins{token} 40 if err := k.supplyKeeper.DelegateCoinsFromAccountToModule(ctx, delAddr, types.BondedPoolName, coins); err != nil { 41 return err 42 } 43 44 // 2.get 45 delegator, found := k.GetDelegator(ctx, delAddr) 46 if !found { 47 delegator = types.NewDelegator(delAddr) 48 } 49 50 // 3.update delegator 51 delegator.Tokens = delegator.Tokens.Add(delQuantity) 52 k.SetDelegator(ctx, delegator) 53 54 if delegator.HasProxy() { 55 //delegator have bound with some proxy, need update proxy's shares 56 return k.UpdateProxy(ctx, delegator, delQuantity) 57 58 } 59 // 4.update shares when delAddr has added already 60 finalTokens := delegator.Tokens 61 // finalTokens should add TotalDelegatedTokens when delegator is proxy 62 if delegator.IsProxy { 63 finalTokens = finalTokens.Add(delegator.TotalDelegatedTokens) 64 } 65 return k.UpdateShares(ctx, delegator.DelegatorAddress, finalTokens) 66 } 67 68 // Withdraw handles the process of withdrawing token from deposit account 69 func (k Keeper) Withdraw(ctx sdk.Context, delAddr sdk.AccAddress, token sdk.SysCoin) (time.Time, error) { 70 delegator, found := k.GetDelegator(ctx, delAddr) 71 if !found { 72 return time.Time{}, types.ErrNoDelegationToAddShares(delAddr.String()) 73 } 74 quantity, minDelLimit := token.Amount, k.ParamsMinDelegation(ctx) 75 if quantity.LT(minDelLimit) { 76 return time.Time{}, types.ErrInsufficientQuantity(quantity.String(), minDelLimit.String()) 77 } else if delegator.Tokens.LT(quantity) { 78 return time.Time{}, types.ErrInsufficientDelegation(quantity.String(), delegator.Tokens.String()) 79 } 80 81 // proxy has to unreg before withdrawing total tokens 82 leftTokens := delegator.Tokens.Sub(quantity) 83 if delegator.IsProxy && leftTokens.IsZero() { 84 return time.Time{}, types.ErrInvalidProxyWithdrawTotal(delAddr.String()) 85 } 86 87 // 1.some fibo transfer bondPool into unbondPool 88 k.bondedTokensToNotBonded(ctx, token) 89 90 // 2.delete delegator in store, or set back 91 if delegator.HasProxy() { 92 if sdkErr := k.UpdateProxy(ctx, delegator, quantity.Mul(sdk.NewDec(-1))); sdkErr != nil { 93 return time.Time{}, sdkErr 94 } 95 } 96 if leftTokens.IsZero() { 97 // withdraw all shares 98 lastVals, lastShares := k.GetLastValsAddedSharesExisted(ctx, delAddr) 99 k.WithdrawLastShares(ctx, delAddr, lastVals, lastShares) 100 if delegator.HasProxy() { 101 k.SetProxyBinding(ctx, delegator.ProxyAddress, delAddr, true) 102 } 103 k.DeleteDelegator(ctx, delAddr) 104 } else { 105 delegator.Tokens = leftTokens 106 k.SetDelegator(ctx, delegator) 107 if !delegator.HasProxy() { 108 finalTokens := delegator.Tokens 109 // finalTokens should add TotalDelegatedTokens when delegator is proxy 110 if delegator.IsProxy { 111 finalTokens = finalTokens.Add(delegator.TotalDelegatedTokens) 112 } 113 if err := k.UpdateShares(ctx, delegator.DelegatorAddress, finalTokens); err != nil { 114 return time.Time{}, err 115 } 116 } 117 } 118 119 // 3.set undelegation and into store 120 completionTime := ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx)) 121 undelegation, found := k.GetUndelegating(ctx, delAddr) 122 if !found { 123 undelegation = types.NewUndelegationInfo(delAddr, quantity, completionTime) 124 } else { 125 k.DeleteAddrByTimeKey(ctx, undelegation.CompletionTime, delAddr) 126 undelegation.Quantity = undelegation.Quantity.Add(quantity) 127 undelegation.CompletionTime = completionTime 128 } 129 k.SetUndelegating(ctx, undelegation) 130 k.SetAddrByTimeKeyWithNilValue(ctx, completionTime, delAddr) 131 132 return completionTime, nil 133 } 134 135 // GetUndelegating gets UndelegationInfo entity from store 136 func (k Keeper) GetUndelegating(ctx sdk.Context, delAddr sdk.AccAddress) (undelegationInfo types.UndelegationInfo, 137 found bool) { 138 bytes := ctx.KVStore(k.storeKey).Get(types.GetUndelegationInfoKey(delAddr)) 139 if bytes == nil { 140 return undelegationInfo, false 141 } 142 143 undelegationInfo = types.MustUnMarshalUndelegationInfo(k.cdcMarshl.GetCdc(), bytes) 144 return undelegationInfo, true 145 } 146 147 // SetUndelegating sets UndelegationInfo entity to store 148 func (k Keeper) SetUndelegating(ctx sdk.Context, undelegationInfo types.UndelegationInfo) { 149 key := types.GetUndelegationInfoKey(undelegationInfo.DelegatorAddress) 150 bytes := k.cdcMarshl.GetCdc().MustMarshalBinaryLengthPrefixed(undelegationInfo) 151 ctx.KVStore(k.storeKey).Set(key, bytes) 152 } 153 154 // DeleteUndelegating deletes UndelegationInfo from store 155 func (k Keeper) DeleteUndelegating(ctx sdk.Context, delAddr sdk.AccAddress) { 156 ctx.KVStore(k.storeKey).Delete(types.GetUndelegationInfoKey(delAddr)) 157 } 158 159 // CompleteUndelegation handles the final process when the undelegation is completed 160 func (k Keeper) CompleteUndelegation(ctx sdk.Context, delAddr sdk.AccAddress) (sdk.Dec, error) { 161 ud, found := k.GetUndelegating(ctx, delAddr) 162 if !found { 163 return sdk.NewDec(0), types.ErrNotInDelegating(delAddr.String()) 164 } 165 166 coin := sdk.SysCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, ud.Quantity)} 167 168 err := k.supplyKeeper.UndelegateCoinsFromModuleToAccount(ctx, types.NotBondedPoolName, ud.DelegatorAddress, coin) 169 if err != nil { 170 return sdk.NewDec(0), err 171 } 172 173 k.DeleteUndelegating(ctx, delAddr) 174 return ud.Quantity, nil 175 } 176 177 // IterateUndelegationInfo iterates through all of the undelegation info 178 func (k Keeper) IterateUndelegationInfo(ctx sdk.Context, 179 fn func(index int64, undelegationInfo types.UndelegationInfo) (stop bool)) { 180 store := ctx.KVStore(k.storeKey) 181 iterator := sdk.KVStorePrefixIterator(store, types.UnDelegationInfoKey) 182 defer iterator.Close() 183 184 for i := int64(0); iterator.Valid(); iterator.Next() { 185 var undelegationInfo types.UndelegationInfo 186 k.cdcMarshl.GetCdc().MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &undelegationInfo) 187 if stop := fn(i, undelegationInfo); stop { 188 break 189 } 190 i++ 191 } 192 } 193 194 // SetAddrByTimeKeyWithNilValue sets the time+delAddr key into store with an empty value 195 func (k Keeper) SetAddrByTimeKeyWithNilValue(ctx sdk.Context, timestamp time.Time, delAddr sdk.AccAddress) { 196 ctx.KVStore(k.storeKey).Set(types.GetCompleteTimeWithAddrKey(timestamp, delAddr), []byte{}) 197 } 198 199 // DeleteAddrByTimeKey deletes the time+delAddr key from store 200 func (k Keeper) DeleteAddrByTimeKey(ctx sdk.Context, timestamp time.Time, delAddr sdk.AccAddress) { 201 ctx.KVStore(k.storeKey).Delete(types.GetCompleteTimeWithAddrKey(timestamp, delAddr)) 202 } 203 204 // IterateKeysBeforeCurrentTime iterates for all keys of (time+delAddr) from time 0 until the current Blockheader time 205 func (k Keeper) IterateKeysBeforeCurrentTime(ctx sdk.Context, currentTime time.Time, 206 fn func(index int64, key []byte) (stop bool)) { 207 208 timeKeyIterator := k.getAddrByTimeKeyIterator(ctx, currentTime) 209 defer timeKeyIterator.Close() 210 211 for i := int64(0); timeKeyIterator.Valid(); timeKeyIterator.Next() { 212 key := timeKeyIterator.Key() 213 if stop := fn(i, key); stop { 214 break 215 } 216 i++ 217 } 218 } 219 220 // getAddrByTimeKeyIterator gets the iterator of keys from time 0 until endTime 221 func (k Keeper) getAddrByTimeKeyIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { 222 store := ctx.KVStore(k.storeKey) 223 key := types.GetCompleteTimeKey(endTime) 224 return store.Iterator(types.UnDelegateQueueKey, sdk.PrefixEndBytes(key)) 225 }