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  }