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

     1  package token
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/fibonacci-chain/fbc/x/common"
     7  
     8  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     9  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
    10  	"github.com/fibonacci-chain/fbc/x/common/perf"
    11  	"github.com/fibonacci-chain/fbc/x/common/version"
    12  	"github.com/fibonacci-chain/fbc/x/token/types"
    13  )
    14  
    15  // NewTokenHandler returns a handler for "token" type messages.
    16  func NewTokenHandler(keeper Keeper, protocolVersion version.ProtocolVersionType) sdk.Handler {
    17  	return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
    18  		ctx.SetEventManager(sdk.NewEventManager())
    19  		//logger := ctx.Logger().With("module", "token")
    20  		// NOTE msg already has validate basic run
    21  		var name string
    22  		var handlerFun func() (*sdk.Result, error)
    23  		logger := ctx.Logger().With("module", "token")
    24  		switch msg := msg.(type) {
    25  		case types.MsgTokenIssue:
    26  			name = "handleMsgTokenIssue"
    27  			handlerFun = func() (*sdk.Result, error) {
    28  				return handleMsgTokenIssue(ctx, keeper, msg, logger)
    29  			}
    30  
    31  		case types.MsgTokenBurn:
    32  			name = "handleMsgTokenBurn"
    33  			handlerFun = func() (*sdk.Result, error) {
    34  				return handleMsgTokenBurn(ctx, keeper, msg, logger)
    35  			}
    36  
    37  		case types.MsgTokenMint:
    38  			name = "handleMsgTokenMint"
    39  			handlerFun = func() (*sdk.Result, error) {
    40  				return handleMsgTokenMint(ctx, keeper, msg, logger)
    41  			}
    42  
    43  		case types.MsgMultiSend:
    44  			name = "handleMsgMultiSend"
    45  			handlerFun = func() (*sdk.Result, error) {
    46  				return handleMsgMultiSend(ctx, keeper, msg, logger)
    47  			}
    48  
    49  		case types.MsgSend:
    50  			name = "handleMsgSend"
    51  			handlerFun = func() (*sdk.Result, error) {
    52  				return handleMsgSend(ctx, keeper, msg, logger)
    53  			}
    54  		case types.MsgTransferOwnership:
    55  			name = "handleMsgTransferOwnership"
    56  			handlerFun = func() (*sdk.Result, error) {
    57  				return handleMsgTransferOwnership(ctx, keeper, msg, logger)
    58  			}
    59  		case types.MsgConfirmOwnership:
    60  			name = "handleMsgConfirmOwnership"
    61  			handlerFun = func() (*sdk.Result, error) {
    62  				return handleMsgConfirmOwnership(ctx, keeper, msg, logger)
    63  			}
    64  
    65  		case types.MsgTokenModify:
    66  			name = "handleMsgTokenModify"
    67  			handlerFun = func() (*sdk.Result, error) {
    68  				return handleMsgTokenModify(ctx, keeper, msg, logger)
    69  			}
    70  		case WalletTokenTransfer:
    71  			name = "handleWalletMsgSend"
    72  			handlerFun = func() (*sdk.Result, error) {
    73  				return handleWalletMsgSend(ctx, keeper, MsgSend{
    74  					FromAddress: msg.GetFrom(),
    75  					ToAddress:   msg.GetTo(),
    76  					Amount:      msg.GetAmount(),
    77  				}, logger)
    78  			}
    79  
    80  		default:
    81  			errMsg := fmt.Sprintf("Unrecognized token Msg type: %v", msg.Type())
    82  			return sdk.ErrUnknownRequest(errMsg).Result()
    83  		}
    84  
    85  		seq := perf.GetPerf().OnDeliverTxEnter(ctx, types.ModuleName, name)
    86  		defer perf.GetPerf().OnDeliverTxExit(ctx, types.ModuleName, name, seq)
    87  
    88  		res, err := handlerFun()
    89  		common.SanityCheckHandler(res, err)
    90  		return res, err
    91  	}
    92  }
    93  
    94  func handleMsgTokenIssue(ctx sdk.Context, keeper Keeper, msg types.MsgTokenIssue, logger log.Logger) (*sdk.Result, error) {
    95  	// check upper bound
    96  	totalSupply, err := sdk.NewDecFromStr(msg.TotalSupply)
    97  	if err != nil {
    98  		return types.ErrGetDecimalFromDecimalStringFailed(err.Error()).Result()
    99  	}
   100  	if totalSupply.GT(sdk.NewDec(types.TotalSupplyUpperbound)) {
   101  		return types.ErrAmountBiggerThanTotalSupplyUpperbound().Result()
   102  	}
   103  
   104  	token := types.Token{
   105  		Description:         msg.Description,
   106  		OriginalSymbol:      msg.OriginalSymbol,
   107  		WholeName:           msg.WholeName,
   108  		OriginalTotalSupply: totalSupply,
   109  		Owner:               msg.Owner,
   110  		Mintable:            msg.Mintable,
   111  	}
   112  
   113  	// generate a random symbol
   114  	newName, valid := addTokenSuffix(ctx, keeper, msg.OriginalSymbol)
   115  	if !valid {
   116  		return types.ErrInvalidCoins(msg.OriginalSymbol).Result()
   117  	}
   118  
   119  	token.Symbol = newName
   120  
   121  	coins := sdk.MustParseCoins(token.Symbol, msg.TotalSupply)
   122  	// set supply
   123  	err = keeper.supplyKeeper.MintCoins(ctx, types.ModuleName, coins)
   124  	if err != nil {
   125  		return types.ErrMintCoinsFailed(err.Error()).Result()
   126  	}
   127  
   128  	// send coins to owner
   129  	err = keeper.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, token.Owner, coins)
   130  	if err != nil {
   131  		return types.ErrSendCoinsFromModuleToAccountFailed(err.Error()).Result()
   132  	}
   133  
   134  	// set token info
   135  	keeper.NewToken(ctx, token)
   136  
   137  	// deduction fee
   138  	feeDecCoins := keeper.GetParams(ctx).FeeIssue.ToCoins()
   139  	err = keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, token.Owner, keeper.feeCollectorName, feeDecCoins)
   140  	if err != nil {
   141  		return types.ErrSendCoinsFromAccountToModuleFailed(err.Error()).Result()
   142  	}
   143  
   144  	var name = "handleMsgTokenIssue"
   145  	if logger != nil {
   146  		logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   147  			"                           msg<Description:%s,Symbol:%s,OriginalSymbol:%s,TotalSupply:%s,Owner:%v,Mintable:%v>\n"+
   148  			"                           result<Owner have enough fibos to issue %s>\n",
   149  			ctx.BlockHeight(), name,
   150  			msg.Description, msg.Symbol, msg.OriginalSymbol, msg.TotalSupply, msg.Owner, msg.Mintable,
   151  			token.Symbol))
   152  	}
   153  
   154  	ctx.EventManager().EmitEvent(
   155  		sdk.NewEvent(
   156  			sdk.EventTypeMessage,
   157  			sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
   158  			sdk.NewAttribute(sdk.AttributeKeyFee, keeper.GetParams(ctx).FeeIssue.String()),
   159  			sdk.NewAttribute("symbol", token.Symbol),
   160  		),
   161  	)
   162  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   163  }
   164  
   165  func handleMsgTokenBurn(ctx sdk.Context, keeper Keeper, msg types.MsgTokenBurn, logger log.Logger) (*sdk.Result, error) {
   166  
   167  	token := keeper.GetTokenInfo(ctx, msg.Amount.Denom)
   168  
   169  	// check owner
   170  	if !token.Owner.Equals(msg.Owner) {
   171  		return types.ErrInputOwnerIsNotEqualTokenOwner(msg.Owner).Result()
   172  	}
   173  
   174  	subCoins := msg.Amount.ToCoins()
   175  	// send coins to moduleAcc
   176  	err := keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, msg.Owner, types.ModuleName, subCoins)
   177  	if err != nil {
   178  		return types.ErrSendCoinsFromAccountToModuleFailed(err.Error()).Result()
   179  	}
   180  
   181  	// set supply
   182  	err = keeper.supplyKeeper.BurnCoins(ctx, types.ModuleName, subCoins)
   183  	if err != nil {
   184  		return types.ErrBurnCoinsFailed(err.Error()).Result()
   185  	}
   186  
   187  	// deduction fee
   188  	feeDecCoins := keeper.GetParams(ctx).FeeBurn.ToCoins()
   189  	err = keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, msg.Owner, keeper.feeCollectorName, feeDecCoins)
   190  	if err != nil {
   191  		return types.ErrSendCoinsFromAccountToModuleFailed(feeDecCoins.String()).Result()
   192  	}
   193  
   194  	var name = "handleMsgTokenBurn"
   195  	if logger != nil {
   196  		logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   197  			"                           msg<Owner:%s,Symbol:%s,Amount:%s>\n"+
   198  			"                           result<Owner have enough fibos to burn %s>\n",
   199  			ctx.BlockHeight(), name,
   200  			msg.Owner, msg.Amount.Denom, msg.Amount,
   201  			msg.Amount.Denom))
   202  	}
   203  
   204  	ctx.EventManager().EmitEvent(
   205  		sdk.NewEvent(
   206  			sdk.EventTypeMessage,
   207  			sdk.NewAttribute(sdk.AttributeKeyFee, feeDecCoins.String()),
   208  		),
   209  	)
   210  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   211  }
   212  
   213  func handleMsgTokenMint(ctx sdk.Context, keeper Keeper, msg types.MsgTokenMint, logger log.Logger) (*sdk.Result, error) {
   214  	token := keeper.GetTokenInfo(ctx, msg.Amount.Denom)
   215  	// check owner
   216  	if !token.Owner.Equals(msg.Owner) {
   217  		return types.ErrInputOwnerIsNotEqualTokenOwner(msg.Owner).Result()
   218  	}
   219  
   220  	// check whether token is mintable
   221  	if !token.Mintable {
   222  		return types.ErrTokenIsNotMintable().Result()
   223  	}
   224  
   225  	// check upper bound
   226  	totalSupplyAfterMint := keeper.supplyKeeper.GetSupplyByDenom(ctx, msg.Amount.Denom).Add(msg.Amount.Amount)
   227  	if totalSupplyAfterMint.GT(sdk.NewDec(types.TotalSupplyUpperbound)) {
   228  		return types.ErrCodeTotalsupplyExceedsTheUpperLimit(totalSupplyAfterMint, types.TotalSupplyUpperbound).Result()
   229  	}
   230  
   231  	mintCoins := msg.Amount.ToCoins()
   232  	// set supply
   233  	err := keeper.supplyKeeper.MintCoins(ctx, types.ModuleName, mintCoins)
   234  	if err != nil {
   235  		return types.ErrMintCoinsFailed(err.Error()).Result()
   236  	}
   237  
   238  	// send coins to acc
   239  	err = keeper.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, msg.Owner, mintCoins)
   240  	if err != nil {
   241  		return types.ErrSendCoinsFromModuleToAccountFailed(err.Error()).Result()
   242  	}
   243  
   244  	// deduction fee
   245  	feeDecCoins := keeper.GetParams(ctx).FeeMint.ToCoins()
   246  	err = keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, msg.Owner, keeper.feeCollectorName, feeDecCoins)
   247  	if err != nil {
   248  		return types.ErrSendCoinsFromAccountToModuleFailed(feeDecCoins.String()).Result()
   249  	}
   250  
   251  	name := "handleMsgTokenMint"
   252  	if logger != nil {
   253  		logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   254  			"                           msg<Owner:%s,Symbol:%s,Amount:%s>\n"+
   255  			"                           result<Owner have enough fibos to Mint %s>\n",
   256  			ctx.BlockHeight(), name,
   257  			msg.Owner, msg.Amount.Denom, msg.Amount,
   258  			msg.Amount.Denom))
   259  	}
   260  
   261  	ctx.EventManager().EmitEvent(
   262  		sdk.NewEvent(
   263  			sdk.EventTypeMessage,
   264  			sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
   265  			sdk.NewAttribute(sdk.AttributeKeyFee, feeDecCoins.String()),
   266  		),
   267  	)
   268  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   269  }
   270  
   271  func handleMsgMultiSend(ctx sdk.Context, keeper Keeper, msg types.MsgMultiSend, logger log.Logger) (*sdk.Result, error) {
   272  	if !keeper.bankKeeper.GetSendEnabled(ctx) {
   273  		return types.ErrSendDisabled().Result()
   274  	}
   275  
   276  	var transfers string
   277  	var coinNum int
   278  	for _, transferUnit := range msg.Transfers {
   279  		coinNum += len(transferUnit.Coins)
   280  		err := keeper.SendCoinsFromAccountToAccount(ctx, msg.From, transferUnit.To, transferUnit.Coins)
   281  		if err != nil {
   282  			return types.ErrSendCoinsFromAccountToAccountFailed(err.Error()).Result()
   283  		}
   284  		transfers += fmt.Sprintf("                          msg<To:%s,Coin:%s>\n", transferUnit.To, transferUnit.Coins)
   285  	}
   286  
   287  	name := "handleMsgMultiSend"
   288  	if logger != nil {
   289  		logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   290  			"                           msg<From:%s>\n"+
   291  			transfers+
   292  			"                           result<Owner have enough fibos to send multi txs>\n",
   293  			ctx.BlockHeight(), name,
   294  			msg.From))
   295  	}
   296  
   297  	ctx.EventManager().EmitEvent(
   298  		sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName)),
   299  	)
   300  
   301  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   302  }
   303  
   304  func handleMsgSend(ctx sdk.Context, keeper Keeper, msg types.MsgSend, logger log.Logger) (*sdk.Result, error) {
   305  	if !keeper.bankKeeper.GetSendEnabled(ctx) {
   306  		return types.ErrSendDisabled().Result()
   307  	}
   308  	err := keeper.SendCoinsFromAccountToAccount(ctx, msg.FromAddress, msg.ToAddress, msg.Amount)
   309  	if err != nil {
   310  		return types.ErrSendCoinsFromAccountToAccountFailed(err.Error()).Result()
   311  	}
   312  
   313  	var name = "handleMsgSend"
   314  	if logger != nil {
   315  		logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   316  			"                           msg<From:%s,To:%s,Amount:%s>\n"+
   317  			"                           result<Owner have enough fibos to send a tx>\n",
   318  			ctx.BlockHeight(), name,
   319  			msg.FromAddress, msg.ToAddress, msg.Amount))
   320  	}
   321  
   322  	ctx.EventManager().EmitEvent(
   323  		sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName)),
   324  	)
   325  
   326  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   327  }
   328  
   329  func handleMsgTransferOwnership(ctx sdk.Context, keeper Keeper, msg types.MsgTransferOwnership, logger log.Logger) (*sdk.Result, error) {
   330  	tokenInfo := keeper.GetTokenInfo(ctx, msg.Symbol)
   331  
   332  	if !tokenInfo.Owner.Equals(msg.FromAddress) {
   333  		return types.ErrCodeinputFromAddressIsNotEqualTokenInfoOwner(msg.FromAddress).Result()
   334  	}
   335  
   336  	confirmOwnership, exist := keeper.GetConfirmOwnership(ctx, msg.Symbol)
   337  	if exist && !ctx.BlockTime().After(confirmOwnership.Expire) {
   338  		return types.ErrConfirmOwnershipNotExistOrBlockTimeAfter().Result()
   339  	}
   340  
   341  	if msg.ToAddress.Equals(common.BlackHoleAddress()) { // transfer ownership to black hole
   342  		// first remove it from the raw owner
   343  		keeper.DeleteUserToken(ctx, tokenInfo.Owner, tokenInfo.Symbol)
   344  		tokenInfo.Owner = msg.ToAddress
   345  		keeper.NewToken(ctx, tokenInfo)
   346  	} else {
   347  		// set confirm ownership info
   348  		expireTime := ctx.BlockTime().Add(keeper.GetParams(ctx).OwnershipConfirmWindow)
   349  		confirmOwnership = &types.ConfirmOwnership{
   350  			Symbol:  msg.Symbol,
   351  			Address: msg.ToAddress,
   352  			Expire:  expireTime,
   353  		}
   354  		keeper.SetConfirmOwnership(ctx, confirmOwnership)
   355  	}
   356  	// deduction fee
   357  	feeDecCoins := keeper.GetParams(ctx).FeeChown.ToCoins()
   358  	err := keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, msg.FromAddress, keeper.feeCollectorName, feeDecCoins)
   359  	if err != nil {
   360  		return types.ErrSendCoinsFromAccountToModuleFailed(err.Error()).Result()
   361  	}
   362  
   363  	var name = "handleMsgTransferOwnership"
   364  	if logger != nil {
   365  		logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   366  			"                           msg<From:%s,To:%s,Symbol:%s>\n"+
   367  			"                           result<Owner have enough fibos to transfer the %s>\n",
   368  			ctx.BlockHeight(), name,
   369  			msg.FromAddress, msg.ToAddress, msg.Symbol,
   370  			msg.Symbol))
   371  	}
   372  
   373  	ctx.EventManager().EmitEvent(
   374  		sdk.NewEvent(
   375  			sdk.EventTypeMessage,
   376  			sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
   377  			sdk.NewAttribute(sdk.AttributeKeyFee, keeper.GetParams(ctx).FeeChown.String()),
   378  		),
   379  	)
   380  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   381  }
   382  
   383  func handleMsgConfirmOwnership(ctx sdk.Context, keeper Keeper, msg types.MsgConfirmOwnership, logger log.Logger) (*sdk.Result, error) {
   384  	confirmOwnership, exist := keeper.GetConfirmOwnership(ctx, msg.Symbol)
   385  	if !exist {
   386  		return types.ErrGetConfirmOwnership().Result()
   387  	}
   388  	if ctx.BlockTime().After(confirmOwnership.Expire) {
   389  		// delete ownership confirming information
   390  		keeper.DeleteConfirmOwnership(ctx, confirmOwnership.Symbol)
   391  		return types.ErrConfirmOwnershipNotExistOrBlockTimeAfter().Result()
   392  	}
   393  	if !confirmOwnership.Address.Equals(msg.Address) {
   394  		return types.ErrCodeConfirmOwnershipAddressNotEqualsMsgAddress(msg.Address).Result()
   395  	}
   396  
   397  	tokenInfo := keeper.GetTokenInfo(ctx, msg.Symbol)
   398  	// first remove it from the raw owner
   399  	keeper.DeleteUserToken(ctx, tokenInfo.Owner, tokenInfo.Symbol)
   400  	tokenInfo.Owner = msg.Address
   401  	keeper.NewToken(ctx, tokenInfo)
   402  
   403  	// delete ownership confirming information
   404  	keeper.DeleteConfirmOwnership(ctx, confirmOwnership.Symbol)
   405  
   406  	var name = "handleMsgConfirmOwnership"
   407  	logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   408  		"                           msg<From:%s,Symbol:%s>\n"+
   409  		"                           result<Owner have enough fibos to transfer the %s>\n",
   410  		ctx.BlockHeight(), name, msg.Address, msg.Symbol, msg.Symbol))
   411  
   412  	ctx.EventManager().EmitEvent(
   413  		sdk.NewEvent(
   414  			sdk.EventTypeMessage,
   415  			sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
   416  			sdk.NewAttribute(sdk.AttributeKeyFee, keeper.GetParams(ctx).FeeChown.String()),
   417  		),
   418  	)
   419  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   420  }
   421  
   422  func handleMsgTokenModify(ctx sdk.Context, keeper Keeper, msg types.MsgTokenModify, logger log.Logger) (*sdk.Result, error) {
   423  	token := keeper.GetTokenInfo(ctx, msg.Symbol)
   424  	// check owner
   425  	if !token.Owner.Equals(msg.Owner) {
   426  		return types.ErrInputOwnerIsNotEqualTokenOwner(msg.Owner).Result()
   427  	}
   428  	if !msg.IsWholeNameModified && !msg.IsDescriptionModified {
   429  		return types.ErrWholeNameAndDescriptionIsNotModified().Result()
   430  	}
   431  	// modify
   432  	if msg.IsWholeNameModified {
   433  		token.WholeName = msg.WholeName
   434  	}
   435  	if msg.IsDescriptionModified {
   436  		token.Description = msg.Description
   437  	}
   438  
   439  	keeper.UpdateToken(ctx, token)
   440  
   441  	// deduction fee
   442  	feeDecCoins := keeper.GetParams(ctx).FeeModify.ToCoins()
   443  	err := keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, msg.Owner, keeper.feeCollectorName, feeDecCoins)
   444  	if err != nil {
   445  		return types.ErrSendCoinsFromAccountToModuleFailed(feeDecCoins.String()).Result()
   446  	}
   447  
   448  	name := "handleMsgTokenModify"
   449  	if logger != nil {
   450  		logger.Debug(fmt.Sprintf("BlockHeight<%d>, handler<%s>\n"+
   451  			"                           msg<Owner:%s,Symbol:%s,WholeName:%s,Description:%s>\n"+
   452  			"                           result<Owner have enough fibos to edit %s>\n",
   453  			ctx.BlockHeight(), name,
   454  			msg.Owner, msg.Symbol, msg.WholeName, msg.Description,
   455  			msg.Symbol))
   456  	}
   457  
   458  	ctx.EventManager().EmitEvent(
   459  		sdk.NewEvent(
   460  			sdk.EventTypeMessage,
   461  			sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
   462  			sdk.NewAttribute(sdk.AttributeKeyFee, keeper.GetParams(ctx).FeeModify.String()),
   463  		),
   464  	)
   465  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   466  }