github.com/Finschia/finschia-sdk@v0.48.1/x/collection/keeper/msg_server.go (about)

     1  package keeper
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/gogo/protobuf/proto"
     7  
     8  	sdk "github.com/Finschia/finschia-sdk/types"
     9  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    10  	"github.com/Finschia/finschia-sdk/x/collection"
    11  )
    12  
    13  type msgServer struct {
    14  	keeper Keeper
    15  }
    16  
    17  // NewMsgServer returns an implementation of the collection MsgServer interface
    18  // for the provided Keeper.
    19  func NewMsgServer(keeper Keeper) collection.MsgServer {
    20  	return &msgServer{
    21  		keeper: keeper,
    22  	}
    23  }
    24  
    25  var _ collection.MsgServer = (*msgServer)(nil)
    26  
    27  func (s msgServer) SendFT(c context.Context, req *collection.MsgSendFT) (*collection.MsgSendFTResponse, error) {
    28  	ctx := sdk.UnwrapSDKContext(c)
    29  
    30  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
    35  	toAddr := sdk.MustAccAddressFromBech32(req.To)
    36  
    37  	if err := s.keeper.SendCoins(ctx, req.ContractId, fromAddr, toAddr, req.Amount); err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	event := collection.EventSent{
    42  		ContractId: req.ContractId,
    43  		Operator:   req.From,
    44  		From:       req.From,
    45  		To:         req.To,
    46  		Amount:     req.Amount,
    47  	}
    48  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
    49  		panic(err)
    50  	}
    51  
    52  	return &collection.MsgSendFTResponse{}, nil
    53  }
    54  
    55  func (s msgServer) OperatorSendFT(c context.Context, req *collection.MsgOperatorSendFT) (*collection.MsgOperatorSendFTResponse, error) {
    56  	ctx := sdk.UnwrapSDKContext(c)
    57  
    58  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
    63  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
    64  
    65  	if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil {
    66  		return nil, collection.ErrCollectionNotApproved.Wrap(err.Error())
    67  	}
    68  
    69  	toAddr := sdk.MustAccAddressFromBech32(req.To)
    70  
    71  	if err := s.keeper.SendCoins(ctx, req.ContractId, fromAddr, toAddr, req.Amount); err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	event := collection.EventSent{
    76  		ContractId: req.ContractId,
    77  		Operator:   req.Operator,
    78  		From:       req.From,
    79  		To:         req.To,
    80  		Amount:     req.Amount,
    81  	}
    82  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
    83  		panic(err)
    84  	}
    85  
    86  	return &collection.MsgOperatorSendFTResponse{}, nil
    87  }
    88  
    89  func (s msgServer) SendNFT(c context.Context, req *collection.MsgSendNFT) (*collection.MsgSendNFTResponse, error) {
    90  	ctx := sdk.UnwrapSDKContext(c)
    91  
    92  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
    97  
    98  	amount := make([]collection.Coin, len(req.TokenIds))
    99  	for i, id := range req.TokenIds {
   100  		amount[i] = collection.Coin{TokenId: id, Amount: sdk.OneInt()}
   101  
   102  		// legacy
   103  		if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil {
   104  			return nil, err
   105  		}
   106  		if _, err := s.keeper.GetParent(ctx, req.ContractId, id); err == nil {
   107  			return nil, collection.ErrTokenCannotTransferChildToken.Wrap(id)
   108  		}
   109  		if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) {
   110  			return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id)
   111  		}
   112  	}
   113  
   114  	toAddr := sdk.MustAccAddressFromBech32(req.To)
   115  
   116  	if err := s.keeper.SendCoins(ctx, req.ContractId, fromAddr, toAddr, amount); err != nil {
   117  		panic(err)
   118  	}
   119  
   120  	event := collection.EventSent{
   121  		ContractId: req.ContractId,
   122  		Operator:   req.From,
   123  		From:       req.From,
   124  		To:         req.To,
   125  		Amount:     amount,
   126  	}
   127  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   128  		panic(err)
   129  	}
   130  
   131  	return &collection.MsgSendNFTResponse{}, nil
   132  }
   133  
   134  func (s msgServer) OperatorSendNFT(c context.Context, req *collection.MsgOperatorSendNFT) (*collection.MsgOperatorSendNFTResponse, error) {
   135  	ctx := sdk.UnwrapSDKContext(c)
   136  
   137  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
   142  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   143  
   144  	if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil {
   145  		return nil, collection.ErrCollectionNotApproved.Wrap(err.Error())
   146  	}
   147  
   148  	amount := make([]collection.Coin, len(req.TokenIds))
   149  	for i, id := range req.TokenIds {
   150  		amount[i] = collection.Coin{TokenId: id, Amount: sdk.OneInt()}
   151  
   152  		// legacy
   153  		if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil {
   154  			return nil, err
   155  		}
   156  		if _, err := s.keeper.GetParent(ctx, req.ContractId, id); err == nil {
   157  			return nil, collection.ErrTokenCannotTransferChildToken.Wrap(id)
   158  		}
   159  		if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) {
   160  			return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id)
   161  		}
   162  	}
   163  
   164  	toAddr := sdk.MustAccAddressFromBech32(req.To)
   165  
   166  	if err := s.keeper.SendCoins(ctx, req.ContractId, fromAddr, toAddr, amount); err != nil {
   167  		panic(err)
   168  	}
   169  
   170  	event := collection.EventSent{
   171  		ContractId: req.ContractId,
   172  		Operator:   req.Operator,
   173  		From:       req.From,
   174  		To:         req.To,
   175  		Amount:     amount,
   176  	}
   177  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   178  		panic(err)
   179  	}
   180  
   181  	return &collection.MsgOperatorSendNFTResponse{}, nil
   182  }
   183  
   184  func (s msgServer) AuthorizeOperator(c context.Context, req *collection.MsgAuthorizeOperator) (*collection.MsgAuthorizeOperatorResponse, error) {
   185  	ctx := sdk.UnwrapSDKContext(c)
   186  
   187  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	holderAddr := sdk.MustAccAddressFromBech32(req.Holder)
   192  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
   193  
   194  	if err := s.keeper.AuthorizeOperator(ctx, req.ContractId, holderAddr, operatorAddr); err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	event := collection.EventAuthorizedOperator{
   199  		ContractId: req.ContractId,
   200  		Holder:     req.Holder,
   201  		Operator:   req.Operator,
   202  	}
   203  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   204  		panic(err)
   205  	}
   206  
   207  	return &collection.MsgAuthorizeOperatorResponse{}, nil
   208  }
   209  
   210  func (s msgServer) RevokeOperator(c context.Context, req *collection.MsgRevokeOperator) (*collection.MsgRevokeOperatorResponse, error) {
   211  	ctx := sdk.UnwrapSDKContext(c)
   212  
   213  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	holderAddr := sdk.MustAccAddressFromBech32(req.Holder)
   218  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
   219  
   220  	if err := s.keeper.RevokeOperator(ctx, req.ContractId, holderAddr, operatorAddr); err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	event := collection.EventRevokedOperator{
   225  		ContractId: req.ContractId,
   226  		Holder:     req.Holder,
   227  		Operator:   req.Operator,
   228  	}
   229  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   230  		panic(err)
   231  	}
   232  
   233  	return &collection.MsgRevokeOperatorResponse{}, nil
   234  }
   235  
   236  func (s msgServer) CreateContract(c context.Context, req *collection.MsgCreateContract) (*collection.MsgCreateContractResponse, error) {
   237  	ctx := sdk.UnwrapSDKContext(c)
   238  
   239  	contract := collection.Contract{
   240  		Name: req.Name,
   241  		Uri:  req.Uri,
   242  		Meta: req.Meta,
   243  	}
   244  	ownerAddr := sdk.MustAccAddressFromBech32(req.Owner)
   245  
   246  	id := s.keeper.CreateContract(ctx, ownerAddr, contract)
   247  
   248  	return &collection.MsgCreateContractResponse{ContractId: id}, nil
   249  }
   250  
   251  func (s msgServer) IssueFT(c context.Context, req *collection.MsgIssueFT) (*collection.MsgIssueFTResponse, error) {
   252  	ctx := sdk.UnwrapSDKContext(c)
   253  
   254  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	ownerAddr := sdk.MustAccAddressFromBech32(req.Owner)
   259  
   260  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, ownerAddr, collection.PermissionIssue); err != nil {
   261  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   262  	}
   263  
   264  	class := &collection.FTClass{
   265  		Name:     req.Name,
   266  		Meta:     req.Meta,
   267  		Decimals: req.Decimals,
   268  		Mintable: req.Mintable,
   269  	}
   270  	id, err := s.keeper.CreateTokenClass(ctx, req.ContractId, class)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	event := collection.EventCreatedFTClass{
   276  		ContractId: req.ContractId,
   277  		Operator:   req.Owner,
   278  		TokenId:    collection.NewFTID(*id),
   279  		Name:       class.Name,
   280  		Meta:       class.Meta,
   281  		Decimals:   class.Decimals,
   282  		Mintable:   class.Mintable,
   283  	}
   284  
   285  	toAddr := sdk.MustAccAddressFromBech32(req.To)
   286  
   287  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   288  		panic(err)
   289  	}
   290  
   291  	// supply tokens
   292  	if req.Amount.IsPositive() {
   293  		s.keeper.mintFT(ctx, req.ContractId, toAddr, *id, req.Amount)
   294  
   295  		event := collection.EventMintedFT{
   296  			ContractId: req.ContractId,
   297  			Operator:   req.Owner,
   298  			To:         req.To,
   299  			Amount:     collection.NewCoins(collection.NewFTCoin(*id, req.Amount)),
   300  		}
   301  		if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   302  			panic(err)
   303  		}
   304  	}
   305  
   306  	return &collection.MsgIssueFTResponse{TokenId: *id}, nil
   307  }
   308  
   309  func (s msgServer) IssueNFT(c context.Context, req *collection.MsgIssueNFT) (*collection.MsgIssueNFTResponse, error) {
   310  	ctx := sdk.UnwrapSDKContext(c)
   311  
   312  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   313  		return nil, err
   314  	}
   315  
   316  	ownerAddr := sdk.MustAccAddressFromBech32(req.Owner)
   317  
   318  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, ownerAddr, collection.PermissionIssue); err != nil {
   319  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   320  	}
   321  
   322  	class := &collection.NFTClass{
   323  		Name: req.Name,
   324  		Meta: req.Meta,
   325  	}
   326  	id, err := s.keeper.CreateTokenClass(ctx, req.ContractId, class)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  
   331  	event := collection.EventCreatedNFTClass{
   332  		ContractId: req.ContractId,
   333  		Operator:   req.Owner,
   334  		TokenType:  *id,
   335  		Name:       class.Name,
   336  		Meta:       class.Meta,
   337  	}
   338  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   339  		panic(err)
   340  	}
   341  
   342  	for _, permission := range []collection.Permission{
   343  		collection.PermissionMint,
   344  		collection.PermissionBurn,
   345  	} {
   346  		s.keeper.Grant(ctx, req.ContractId, []byte{}, ownerAddr, permission)
   347  	}
   348  
   349  	return &collection.MsgIssueNFTResponse{TokenType: *id}, nil
   350  }
   351  
   352  func (s msgServer) MintFT(c context.Context, req *collection.MsgMintFT) (*collection.MsgMintFTResponse, error) {
   353  	ctx := sdk.UnwrapSDKContext(c)
   354  
   355  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   360  
   361  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, fromAddr, collection.PermissionMint); err != nil {
   362  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   363  	}
   364  
   365  	toAddr := sdk.MustAccAddressFromBech32(req.To)
   366  
   367  	if err := s.keeper.MintFT(ctx, req.ContractId, toAddr, req.Amount); err != nil {
   368  		return nil, err
   369  	}
   370  
   371  	event := collection.EventMintedFT{
   372  		ContractId: req.ContractId,
   373  		Operator:   req.From,
   374  		To:         req.To,
   375  		Amount:     req.Amount,
   376  	}
   377  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   378  		panic(err)
   379  	}
   380  
   381  	return &collection.MsgMintFTResponse{}, nil
   382  }
   383  
   384  func (s msgServer) MintNFT(c context.Context, req *collection.MsgMintNFT) (*collection.MsgMintNFTResponse, error) {
   385  	ctx := sdk.UnwrapSDKContext(c)
   386  
   387  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   388  		return nil, err
   389  	}
   390  
   391  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   392  
   393  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, fromAddr, collection.PermissionMint); err != nil {
   394  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   395  	}
   396  
   397  	toAddr := sdk.MustAccAddressFromBech32(req.To)
   398  
   399  	tokens, err := s.keeper.MintNFT(ctx, req.ContractId, toAddr, req.Params)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	event := collection.EventMintedNFT{
   405  		ContractId: req.ContractId,
   406  		Operator:   req.From,
   407  		To:         req.To,
   408  		Tokens:     tokens,
   409  	}
   410  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   411  		panic(err)
   412  	}
   413  
   414  	tokenIDs := make([]string, 0, len(tokens))
   415  	for _, token := range tokens {
   416  		tokenIDs = append(tokenIDs, token.TokenId)
   417  	}
   418  	return &collection.MsgMintNFTResponse{TokenIds: tokenIDs}, nil
   419  }
   420  
   421  func (s msgServer) BurnFT(c context.Context, req *collection.MsgBurnFT) (*collection.MsgBurnFTResponse, error) {
   422  	ctx := sdk.UnwrapSDKContext(c)
   423  
   424  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   425  		return nil, err
   426  	}
   427  
   428  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   429  
   430  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, fromAddr, collection.PermissionBurn); err != nil {
   431  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   432  	}
   433  
   434  	burnt, err := s.keeper.BurnCoins(ctx, req.ContractId, fromAddr, req.Amount)
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  
   439  	event := collection.EventBurned{
   440  		ContractId: req.ContractId,
   441  		Operator:   req.From,
   442  		From:       req.From,
   443  		Amount:     burnt,
   444  	}
   445  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   446  		panic(err)
   447  	}
   448  
   449  	return &collection.MsgBurnFTResponse{}, nil
   450  }
   451  
   452  func (s msgServer) OperatorBurnFT(c context.Context, req *collection.MsgOperatorBurnFT) (*collection.MsgOperatorBurnFTResponse, error) {
   453  	ctx := sdk.UnwrapSDKContext(c)
   454  
   455  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   460  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
   461  
   462  	if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil {
   463  		return nil, collection.ErrCollectionNotApproved.Wrap(err.Error())
   464  	}
   465  
   466  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, operatorAddr, collection.PermissionBurn); err != nil {
   467  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   468  	}
   469  
   470  	burnt, err := s.keeper.BurnCoins(ctx, req.ContractId, fromAddr, req.Amount)
   471  	if err != nil {
   472  		return nil, err
   473  	}
   474  
   475  	event := collection.EventBurned{
   476  		ContractId: req.ContractId,
   477  		Operator:   req.Operator,
   478  		From:       req.From,
   479  		Amount:     burnt,
   480  	}
   481  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   482  		panic(err)
   483  	}
   484  
   485  	return &collection.MsgOperatorBurnFTResponse{}, nil
   486  }
   487  
   488  func (s msgServer) BurnNFT(c context.Context, req *collection.MsgBurnNFT) (*collection.MsgBurnNFTResponse, error) {
   489  	ctx := sdk.UnwrapSDKContext(c)
   490  
   491  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   492  		return nil, err
   493  	}
   494  
   495  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   496  
   497  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, fromAddr, collection.PermissionBurn); err != nil {
   498  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   499  	}
   500  
   501  	coins := make([]collection.Coin, 0, len(req.TokenIds))
   502  	for _, id := range req.TokenIds {
   503  		coins = append(coins, collection.NewCoin(id, sdk.OneInt()))
   504  
   505  		// legacy
   506  		if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil {
   507  			return nil, err
   508  		}
   509  		if _, err := s.keeper.GetParent(ctx, req.ContractId, id); err == nil {
   510  			return nil, collection.ErrBurnNonRootNFT.Wrap(id)
   511  		}
   512  		if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) {
   513  			return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id)
   514  		}
   515  	}
   516  
   517  	burnt, err := s.keeper.BurnCoins(ctx, req.ContractId, fromAddr, coins)
   518  	if err != nil {
   519  		panic(err)
   520  	}
   521  
   522  	// emit events against all burnt tokens.
   523  	event := collection.EventBurned{
   524  		ContractId: req.ContractId,
   525  		Operator:   req.From,
   526  		From:       req.From,
   527  		Amount:     burnt,
   528  	}
   529  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   530  		panic(err)
   531  	}
   532  
   533  	return &collection.MsgBurnNFTResponse{}, nil
   534  }
   535  
   536  func (s msgServer) OperatorBurnNFT(c context.Context, req *collection.MsgOperatorBurnNFT) (*collection.MsgOperatorBurnNFTResponse, error) {
   537  	ctx := sdk.UnwrapSDKContext(c)
   538  
   539  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   540  		return nil, err
   541  	}
   542  
   543  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   544  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
   545  
   546  	if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil {
   547  		return nil, collection.ErrCollectionNotApproved.Wrap(err.Error())
   548  	}
   549  
   550  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, operatorAddr, collection.PermissionBurn); err != nil {
   551  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   552  	}
   553  
   554  	coins := make([]collection.Coin, 0, len(req.TokenIds))
   555  	for _, id := range req.TokenIds {
   556  		coins = append(coins, collection.NewCoin(id, sdk.OneInt()))
   557  
   558  		// legacy
   559  		if err := s.keeper.hasNFT(ctx, req.ContractId, id); err != nil {
   560  			return nil, err
   561  		}
   562  		if _, err := s.keeper.GetParent(ctx, req.ContractId, id); err == nil {
   563  			return nil, collection.ErrBurnNonRootNFT.Wrap(id)
   564  		}
   565  		if !s.keeper.getOwner(ctx, req.ContractId, id).Equals(fromAddr) {
   566  			return nil, collection.ErrTokenNotOwnedBy.Wrapf("%s does not have %s", fromAddr, id)
   567  		}
   568  	}
   569  
   570  	burnt, err := s.keeper.BurnCoins(ctx, req.ContractId, fromAddr, coins)
   571  	if err != nil {
   572  		panic(err)
   573  	}
   574  
   575  	// emit events against all burnt tokens.
   576  	event := collection.EventBurned{
   577  		ContractId: req.ContractId,
   578  		Operator:   req.Operator,
   579  		From:       req.From,
   580  		Amount:     burnt,
   581  	}
   582  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   583  		panic(err)
   584  	}
   585  
   586  	return &collection.MsgOperatorBurnNFTResponse{}, nil
   587  }
   588  
   589  func (s msgServer) Modify(c context.Context, req *collection.MsgModify) (*collection.MsgModifyResponse, error) {
   590  	ctx := sdk.UnwrapSDKContext(c)
   591  
   592  	collection.UpdateMsgModify(req)
   593  
   594  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   595  		return nil, err
   596  	}
   597  
   598  	operator := sdk.MustAccAddressFromBech32(req.Owner)
   599  
   600  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, operator, collection.PermissionModify); err != nil {
   601  		return nil, collection.ErrTokenNoPermission.Wrap(err.Error())
   602  	}
   603  
   604  	// copied from daphne
   605  	modify := func(tokenType, tokenIndex string) error {
   606  		changes := make([]collection.Attribute, len(req.Changes))
   607  		for i, change := range req.Changes {
   608  			changes[i] = collection.Attribute{
   609  				Key:   change.Key,
   610  				Value: change.Value,
   611  			}
   612  		}
   613  
   614  		classID := tokenType
   615  		tokenID := classID + tokenIndex
   616  		if tokenType != "" {
   617  			if tokenIndex != "" {
   618  				if collection.ValidateNFTID(tokenID) == nil {
   619  					event := collection.EventModifiedNFT{
   620  						ContractId: req.ContractId,
   621  						Operator:   operator.String(),
   622  						TokenId:    tokenID,
   623  						Changes:    changes,
   624  					}
   625  					if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   626  						panic(err)
   627  					}
   628  
   629  					return s.keeper.ModifyNFT(ctx, req.ContractId, tokenID, operator, changes)
   630  				}
   631  
   632  				event := collection.EventModifiedTokenClass{
   633  					ContractId: req.ContractId,
   634  					Operator:   operator.String(),
   635  					TokenType:  classID,
   636  					Changes:    changes,
   637  					TypeName:   proto.MessageName(&collection.FTClass{}),
   638  				}
   639  				if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   640  					panic(err)
   641  				}
   642  
   643  				return s.keeper.ModifyTokenClass(ctx, req.ContractId, classID, operator, changes)
   644  			}
   645  
   646  			event := collection.EventModifiedTokenClass{
   647  				ContractId: req.ContractId,
   648  				Operator:   operator.String(),
   649  				TokenType:  classID,
   650  				Changes:    changes,
   651  				TypeName:   proto.MessageName(&collection.NFTClass{}),
   652  			}
   653  			if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   654  				panic(err)
   655  			}
   656  
   657  			return s.keeper.ModifyTokenClass(ctx, req.ContractId, classID, operator, changes)
   658  		}
   659  		if req.TokenIndex == "" {
   660  			event := collection.EventModifiedContract{
   661  				ContractId: req.ContractId,
   662  				Operator:   operator.String(),
   663  				Changes:    changes,
   664  			}
   665  			if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   666  				panic(err)
   667  			}
   668  
   669  			return s.keeper.ModifyContract(ctx, req.ContractId, operator, changes)
   670  		}
   671  
   672  		panic(sdkerrors.ErrInvalidRequest.Wrap("token index without type"))
   673  	}
   674  
   675  	if err := modify(req.TokenType, req.TokenIndex); err != nil {
   676  		return nil, err
   677  	}
   678  
   679  	return &collection.MsgModifyResponse{}, nil
   680  }
   681  
   682  func (s msgServer) GrantPermission(c context.Context, req *collection.MsgGrantPermission) (*collection.MsgGrantPermissionResponse, error) {
   683  	ctx := sdk.UnwrapSDKContext(c)
   684  
   685  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   686  		return nil, err
   687  	}
   688  
   689  	granter := sdk.MustAccAddressFromBech32(req.From)
   690  	grantee := sdk.MustAccAddressFromBech32(req.To)
   691  	permission := collection.Permission(collection.LegacyPermissionFromString(req.Permission))
   692  
   693  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, granter, permission); err != nil {
   694  		return nil, collection.ErrTokenNoPermission.Wrapf("%s is not authorized for %s", granter, permission)
   695  	}
   696  
   697  	// it emits typed event inside s.keeper.Grant()
   698  	s.keeper.Grant(ctx, req.ContractId, granter, grantee, permission)
   699  
   700  	return &collection.MsgGrantPermissionResponse{}, nil
   701  }
   702  
   703  func (s msgServer) RevokePermission(c context.Context, req *collection.MsgRevokePermission) (*collection.MsgRevokePermissionResponse, error) {
   704  	ctx := sdk.UnwrapSDKContext(c)
   705  
   706  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   707  		return nil, err
   708  	}
   709  
   710  	grantee := sdk.MustAccAddressFromBech32(req.From)
   711  	permission := collection.Permission(collection.LegacyPermissionFromString(req.Permission))
   712  
   713  	if _, err := s.keeper.GetGrant(ctx, req.ContractId, grantee, permission); err != nil {
   714  		return nil, collection.ErrTokenNoPermission.Wrapf("%s is not authorized for %s", grantee, permission)
   715  	}
   716  
   717  	// it emits typed event inside s.keeper.Abandon()
   718  	s.keeper.Abandon(ctx, req.ContractId, grantee, permission)
   719  
   720  	return &collection.MsgRevokePermissionResponse{}, nil
   721  }
   722  
   723  func (s msgServer) Attach(c context.Context, req *collection.MsgAttach) (*collection.MsgAttachResponse, error) {
   724  	ctx := sdk.UnwrapSDKContext(c)
   725  
   726  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   727  		return nil, err
   728  	}
   729  
   730  	event := collection.EventAttached{
   731  		ContractId: req.ContractId,
   732  		Operator:   req.From,
   733  		Holder:     req.From,
   734  		Subject:    req.TokenId,
   735  		Target:     req.ToTokenId,
   736  	}
   737  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   738  		panic(err)
   739  	}
   740  
   741  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   742  
   743  	if err := s.keeper.Attach(ctx, req.ContractId, fromAddr, req.TokenId, req.ToTokenId); err != nil {
   744  		return nil, err
   745  	}
   746  
   747  	return &collection.MsgAttachResponse{}, nil
   748  }
   749  
   750  func (s msgServer) Detach(c context.Context, req *collection.MsgDetach) (*collection.MsgDetachResponse, error) {
   751  	ctx := sdk.UnwrapSDKContext(c)
   752  
   753  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   754  		return nil, err
   755  	}
   756  
   757  	// legacy
   758  	if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil {
   759  		return nil, err
   760  	}
   761  
   762  	// for the additional field of the event
   763  	parent, err := s.keeper.GetParent(ctx, req.ContractId, req.TokenId)
   764  	if err != nil {
   765  		return nil, collection.ErrTokenNotAChild.Wrap(err.Error())
   766  	}
   767  	event := collection.EventDetached{
   768  		ContractId:     req.ContractId,
   769  		Operator:       req.From,
   770  		Holder:         req.From,
   771  		Subject:        req.TokenId,
   772  		PreviousParent: *parent,
   773  	}
   774  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   775  		panic(err)
   776  	}
   777  
   778  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   779  
   780  	if err := s.keeper.Detach(ctx, req.ContractId, fromAddr, req.TokenId); err != nil {
   781  		return nil, err
   782  	}
   783  
   784  	return &collection.MsgDetachResponse{}, nil
   785  }
   786  
   787  func (s msgServer) OperatorAttach(c context.Context, req *collection.MsgOperatorAttach) (*collection.MsgOperatorAttachResponse, error) {
   788  	ctx := sdk.UnwrapSDKContext(c)
   789  
   790  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   791  		return nil, err
   792  	}
   793  
   794  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   795  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
   796  
   797  	if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil {
   798  		return nil, collection.ErrCollectionNotApproved.Wrap(err.Error())
   799  	}
   800  
   801  	event := collection.EventAttached{
   802  		ContractId: req.ContractId,
   803  		Operator:   req.Operator,
   804  		Holder:     req.From,
   805  		Subject:    req.TokenId,
   806  		Target:     req.ToTokenId,
   807  	}
   808  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   809  		panic(err)
   810  	}
   811  
   812  	if err := s.keeper.Attach(ctx, req.ContractId, fromAddr, req.TokenId, req.ToTokenId); err != nil {
   813  		return nil, err
   814  	}
   815  
   816  	return &collection.MsgOperatorAttachResponse{}, nil
   817  }
   818  
   819  func (s msgServer) OperatorDetach(c context.Context, req *collection.MsgOperatorDetach) (*collection.MsgOperatorDetachResponse, error) {
   820  	ctx := sdk.UnwrapSDKContext(c)
   821  
   822  	if err := ValidateLegacyContract(s.keeper, ctx, req.ContractId); err != nil {
   823  		return nil, err
   824  	}
   825  
   826  	fromAddr := sdk.MustAccAddressFromBech32(req.From)
   827  	operatorAddr := sdk.MustAccAddressFromBech32(req.Operator)
   828  
   829  	if _, err := s.keeper.GetAuthorization(ctx, req.ContractId, fromAddr, operatorAddr); err != nil {
   830  		return nil, err
   831  	}
   832  
   833  	// legacy
   834  	if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil {
   835  		return nil, err
   836  	}
   837  
   838  	// for the additional field of the event
   839  	parent, err := s.keeper.GetParent(ctx, req.ContractId, req.TokenId)
   840  	if err != nil {
   841  		return nil, collection.ErrTokenNotAChild.Wrap(err.Error())
   842  	}
   843  	event := collection.EventDetached{
   844  		ContractId:     req.ContractId,
   845  		Operator:       req.Operator,
   846  		Holder:         req.From,
   847  		Subject:        req.TokenId,
   848  		PreviousParent: *parent,
   849  	}
   850  	if err := ctx.EventManager().EmitTypedEvent(&event); err != nil {
   851  		panic(err)
   852  	}
   853  
   854  	if err := s.keeper.Detach(ctx, req.ContractId, fromAddr, req.TokenId); err != nil {
   855  		return nil, err
   856  	}
   857  
   858  	return &collection.MsgOperatorDetachResponse{}, nil
   859  }