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 }