github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/farm/handler_lock.go (about)

     1  package farm
     2  
     3  import (
     4  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     5  	"github.com/fibonacci-chain/fbc/x/farm/keeper"
     6  	"github.com/fibonacci-chain/fbc/x/farm/types"
     7  )
     8  
     9  func handleMsgLock(ctx sdk.Context, k keeper.Keeper, msg types.MsgLock) (*sdk.Result, error) {
    10  	// 1.1 Get farm pool
    11  	pool, found := k.GetFarmPool(ctx, msg.PoolName)
    12  	if !found {
    13  		return types.ErrNoFarmPoolFound(msg.PoolName).Result()
    14  	}
    15  	if pool.MinLockAmount.Denom != msg.Amount.Denom {
    16  		return types.ErrInvalidDenom(pool.MinLockAmount.Denom, msg.Amount.Denom).Result()
    17  	}
    18  
    19  	// 1.2. check min lock amount
    20  	hasLocked := k.HasLockInfo(ctx, msg.Address, msg.PoolName)
    21  	if !hasLocked && msg.Amount.Amount.LT(pool.MinLockAmount.Amount) {
    22  		return types.ErrLockAmountBelowMinimum(pool.MinLockAmount.Amount, msg.Amount.Amount).Result()
    23  	}
    24  
    25  	// 2. Calculate how many provided token & native token could be yielded in current period
    26  	updatedPool, yieldedTokens := k.CalculateAmountYieldedBetween(ctx, pool)
    27  
    28  	// 3. Lock info
    29  	var rewards sdk.SysCoins
    30  	if hasLocked {
    31  		// If it exists, withdraw money
    32  		var err error
    33  		rewards, err = k.WithdrawRewards(ctx, pool.Name, pool.TotalValueLocked, yieldedTokens, msg.Address)
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  		if updatedPool.TotalAccumulatedRewards.IsAllLT(rewards) {
    38  			panic("should not happen")
    39  		}
    40  		updatedPool.TotalAccumulatedRewards = updatedPool.TotalAccumulatedRewards.Sub(rewards)
    41  
    42  	} else {
    43  		// If it doesn't exist, only increase period
    44  		k.IncrementPoolPeriod(ctx, pool.Name, pool.TotalValueLocked, yieldedTokens)
    45  
    46  		// Create new lock info
    47  		lockInfo := types.NewLockInfo(
    48  			msg.Address, pool.Name, sdk.NewDecCoinFromDec(pool.MinLockAmount.Denom, sdk.ZeroDec()),
    49  			ctx.BlockHeight(), 0,
    50  		)
    51  		k.SetLockInfo(ctx, lockInfo)
    52  		k.SetAddressInFarmPool(ctx, msg.PoolName, msg.Address)
    53  	}
    54  
    55  	// 4. Update lock info
    56  	k.UpdateLockInfo(ctx, msg.Address, msg.PoolName, msg.Amount.Amount)
    57  
    58  	// 5. Send the locked-tokens from its own account to farm module account
    59  	if err := k.SupplyKeeper().SendCoinsFromAccountToModule(
    60  		ctx, msg.Address, ModuleName, msg.Amount.ToCoins(),
    61  	); err != nil {
    62  		return nil, types.ErrSendCoinsFromAccountToModuleFailed(err.Error())
    63  	}
    64  
    65  	// 6. Update farm pool
    66  	updatedPool.TotalValueLocked = updatedPool.TotalValueLocked.Add(msg.Amount)
    67  	k.SetFarmPool(ctx, updatedPool)
    68  
    69  	// 7. notify backend
    70  	if hasLocked {
    71  		k.OnClaim(ctx, msg.Address, pool.Name, rewards)
    72  	}
    73  
    74  	ctx.EventManager().EmitEvent(sdk.NewEvent(
    75  		types.EventTypeLock,
    76  		sdk.NewAttribute(types.AttributeKeyAddress, msg.Address.String()),
    77  		sdk.NewAttribute(types.AttributeKeyPool, msg.PoolName),
    78  		sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
    79  	))
    80  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
    81  }
    82  
    83  func handleMsgUnlock(ctx sdk.Context, k keeper.Keeper, msg types.MsgUnlock) (*sdk.Result, error) {
    84  	// 1.1 Check if there are enough tokens to unlock
    85  	lockInfo, found := k.GetLockInfo(ctx, msg.Address, msg.PoolName)
    86  	if !found {
    87  		return types.ErrNoLockInfoFound(msg.Address.String(), msg.PoolName).Result()
    88  	}
    89  
    90  	if lockInfo.Amount.Denom != msg.Amount.Denom {
    91  		return types.ErrInvalidDenom(lockInfo.Amount.Denom, msg.Amount.Denom).Result()
    92  	}
    93  
    94  	if lockInfo.Amount.IsLT(msg.Amount) {
    95  		return types.ErrInsufficientAmount(lockInfo.Amount.String(), msg.Amount.String()).Result()
    96  	}
    97  
    98  	// 1.2 Get the pool info
    99  	pool, poolFound := k.GetFarmPool(ctx, msg.PoolName)
   100  	if !poolFound {
   101  		return types.ErrNoFarmPoolFound(msg.PoolName).Result()
   102  	}
   103  	if pool.MinLockAmount.Denom != msg.Amount.Denom {
   104  		return types.ErrInvalidDenom(pool.MinLockAmount.Denom, msg.Amount.Denom).Result()
   105  	}
   106  	remainAmount := lockInfo.Amount.Amount.Sub(msg.Amount.Amount)
   107  	if !remainAmount.IsZero() && remainAmount.LT(pool.MinLockAmount.Amount) {
   108  		return types.ErrLockAmountBelowMinimum(pool.MinLockAmount.Amount, remainAmount).Result()
   109  	}
   110  
   111  	// 2. Calculate how many provided token & native token could be yielded in current period
   112  	updatedPool, yieldedTokens := k.CalculateAmountYieldedBetween(ctx, pool)
   113  
   114  	// 3. Withdraw money
   115  	rewards, err := k.WithdrawRewards(ctx, pool.Name, pool.TotalValueLocked, yieldedTokens, msg.Address)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	// 4. Update the lock info
   121  	k.UpdateLockInfo(ctx, msg.Address, msg.PoolName, msg.Amount.Amount.Neg())
   122  
   123  	// 5. Send the locked-tokens from farm module account to its own account
   124  	if err = k.SupplyKeeper().SendCoinsFromModuleToAccount(ctx, ModuleName, msg.Address, msg.Amount.ToCoins()); err != nil {
   125  		return nil, types.ErrSendCoinsFromModuleToAccountFailed(err.Error())
   126  	}
   127  
   128  	// 6. Update farm pool
   129  	updatedPool.TotalValueLocked = updatedPool.TotalValueLocked.Sub(msg.Amount)
   130  	if updatedPool.TotalAccumulatedRewards.IsAllLT(rewards) {
   131  		panic("should not happen")
   132  	}
   133  	updatedPool.TotalAccumulatedRewards = updatedPool.TotalAccumulatedRewards.Sub(rewards)
   134  	k.SetFarmPool(ctx, updatedPool)
   135  
   136  	// 7. notify backend
   137  	k.OnClaim(ctx, msg.Address, pool.Name, rewards)
   138  
   139  	ctx.EventManager().EmitEvent(sdk.NewEvent(
   140  		types.EventTypeUnlock,
   141  		sdk.NewAttribute(types.AttributeKeyAddress, msg.Address.String()),
   142  		sdk.NewAttribute(types.AttributeKeyPool, msg.PoolName),
   143  		sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
   144  	))
   145  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   146  }