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

     1  package farm
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/fibonacci-chain/fbc/x/common"
     8  
     9  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    10  
    11  	"github.com/fibonacci-chain/fbc/x/common/perf"
    12  	"github.com/fibonacci-chain/fbc/x/farm/keeper"
    13  	"github.com/fibonacci-chain/fbc/x/farm/types"
    14  )
    15  
    16  var destroyPoolHandler func(ctx sdk.Context, k keeper.Keeper, msg types.MsgDestroyPool) (*sdk.Result, error) = handleMsgDestroyPool
    17  
    18  // NewHandler creates an sdk.Handler for all the farm type messages
    19  func NewHandler(k keeper.Keeper) sdk.Handler {
    20  	return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
    21  		ctx.SetEventManager(sdk.NewEventManager())
    22  		var handlerFun func() (*sdk.Result, error)
    23  		var name string
    24  		switch msg := msg.(type) {
    25  		case types.MsgCreatePool:
    26  			name = "handleMsgCreatePool"
    27  			handlerFun = func() (*sdk.Result, error) {
    28  				return handleMsgCreatePool(ctx, k, msg)
    29  			}
    30  		case types.MsgDestroyPool:
    31  			name = "handleMsgDestroyPool"
    32  			handlerFun = func() (*sdk.Result, error) {
    33  				return destroyPoolHandler(ctx, k, msg)
    34  			}
    35  		case types.MsgProvide:
    36  			name = "handleMsgProvide"
    37  			handlerFun = func() (*sdk.Result, error) {
    38  				return handleMsgProvide(ctx, k, msg)
    39  			}
    40  		case types.MsgLock:
    41  			name = "handleMsgLock"
    42  			handlerFun = func() (*sdk.Result, error) {
    43  				return handleMsgLock(ctx, k, msg)
    44  			}
    45  		case types.MsgUnlock:
    46  			name = "handleMsgUnlock"
    47  			handlerFun = func() (*sdk.Result, error) {
    48  				return handleMsgUnlock(ctx, k, msg)
    49  			}
    50  		case types.MsgClaim:
    51  			name = "handleMsgClaim"
    52  			handlerFun = func() (*sdk.Result, error) {
    53  				return handleMsgClaim(ctx, k, msg)
    54  			}
    55  		default:
    56  			errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg)
    57  			return types.ErrUnknownFarmMsgType(errMsg).Result()
    58  		}
    59  
    60  		seq := perf.GetPerf().OnDeliverTxEnter(ctx, types.ModuleName, name)
    61  		defer perf.GetPerf().OnDeliverTxExit(ctx, types.ModuleName, name, seq)
    62  
    63  		res, err := handlerFun()
    64  		common.SanityCheckHandler(res, err)
    65  		return res, err
    66  	}
    67  }
    68  
    69  func handleMsgProvide(ctx sdk.Context, k keeper.Keeper, msg types.MsgProvide) (*sdk.Result, error) {
    70  	// 0. Check if the start_height_to_yield is more than current height
    71  	if msg.StartHeightToYield <= ctx.BlockHeight() {
    72  		return types.ErrInvalidStartHeight().Result()
    73  	}
    74  
    75  	// 1.1 Check farm pool
    76  	pool, found := k.GetFarmPool(ctx, msg.PoolName)
    77  	if !found {
    78  		return types.ErrNoFarmPoolFound(msg.PoolName).Result()
    79  	}
    80  
    81  	// 1.2 Check owner
    82  	if !pool.Owner.Equals(msg.Address) {
    83  		return types.ErrInvalidPoolOwner(msg.Address.String(), msg.PoolName).Result()
    84  	}
    85  
    86  	// 1.3 Check if the provided coin denom is the same as the locked coin name
    87  	if len(pool.YieldedTokenInfos) != 1 {
    88  		panic(fmt.Sprintf("The YieldedTokenInfos length is %d, which should be 1 in current code version",
    89  			len(pool.YieldedTokenInfos)))
    90  	}
    91  	if pool.YieldedTokenInfos[0].RemainingAmount.Denom != msg.Amount.Denom {
    92  		return types.ErrInvalidDenom(pool.YieldedTokenInfos[0].RemainingAmount.Denom, msg.Amount.Denom).Result()
    93  	}
    94  
    95  	// 2.1 Calculate how many provided token & native token could be yielded in current period
    96  	updatedPool, yieldedTokens := k.CalculateAmountYieldedBetween(ctx, pool)
    97  
    98  	// 2.2 Check if remaining amount is already zero
    99  	remainingAmount := updatedPool.YieldedTokenInfos[0].RemainingAmount
   100  	if !remainingAmount.IsZero() {
   101  		return types.ErrRemainingAmountNotZero(remainingAmount.String()).Result()
   102  	}
   103  
   104  	// 3. Terminate pool current period
   105  	k.IncrementPoolPeriod(ctx, pool.Name, pool.TotalValueLocked, yieldedTokens)
   106  
   107  	// 4. Transfer coin to farm module account
   108  	if err := k.SupplyKeeper().SendCoinsFromAccountToModule(
   109  		ctx, msg.Address, YieldFarmingAccount, msg.Amount.ToCoins(),
   110  	); err != nil {
   111  		return types.ErrSendCoinsFromAccountToModuleFailed(err.Error()).Result()
   112  	}
   113  
   114  	// 5. init a new yielded_token_info struct, then set it into store
   115  	updatedPool.YieldedTokenInfos[0] = types.NewYieldedTokenInfo(
   116  		msg.Amount, msg.StartHeightToYield, msg.AmountYieldedPerBlock,
   117  	)
   118  	k.SetFarmPool(ctx, updatedPool)
   119  
   120  	ctx.EventManager().EmitEvent(sdk.NewEvent(
   121  		types.EventTypeProvide,
   122  		sdk.NewAttribute(types.AttributeKeyAddress, msg.Address.String()),
   123  		sdk.NewAttribute(types.AttributeKeyPool, msg.PoolName),
   124  		sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
   125  		sdk.NewAttribute(types.AttributeKeyStartHeightToYield, strconv.FormatInt(msg.StartHeightToYield, 10)),
   126  		sdk.NewAttribute(types.AttributeKeyAmountYieldPerBlock, msg.AmountYieldedPerBlock.String()),
   127  	))
   128  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   129  }
   130  
   131  func handleMsgClaim(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaim) (*sdk.Result, error) {
   132  	// 1. Get the pool info
   133  	pool, poolFound := k.GetFarmPool(ctx, msg.PoolName)
   134  	if !poolFound {
   135  		return types.ErrNoFarmPoolFound(msg.PoolName).Result()
   136  	}
   137  
   138  	// 2. Calculate how many provided token & native token could be yielded in current period
   139  	updatedPool, yieldedTokens := k.CalculateAmountYieldedBetween(ctx, pool)
   140  
   141  	// 3. Withdraw rewards
   142  	rewards, err := k.WithdrawRewards(ctx, pool.Name, pool.TotalValueLocked, yieldedTokens, msg.Address)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	// 4. Update the lock_info data
   148  	k.UpdateLockInfo(ctx, msg.Address, pool.Name, sdk.ZeroDec())
   149  
   150  	// 5. Update farm pool
   151  	if updatedPool.TotalAccumulatedRewards.IsAllLT(rewards) {
   152  		panic("should not happen")
   153  	}
   154  	updatedPool.TotalAccumulatedRewards = updatedPool.TotalAccumulatedRewards.Sub(rewards)
   155  	k.SetFarmPool(ctx, updatedPool)
   156  
   157  	// 6. notify backend
   158  	k.OnClaim(ctx, msg.Address, pool.Name, rewards)
   159  
   160  	ctx.EventManager().EmitEvent(sdk.NewEvent(
   161  		types.EventTypeClaim,
   162  		sdk.NewAttribute(types.AttributeKeyAddress, msg.Address.String()),
   163  		sdk.NewAttribute(types.AttributeKeyPool, msg.PoolName),
   164  	))
   165  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   166  }