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 }