github.com/KiraCore/sekai@v0.3.43/x/layer2/keeper/msg_server.go (about)

     1  package keeper
     2  
     3  import (
     4  	"context"
     5  
     6  	govtypes "github.com/KiraCore/sekai/x/gov/types"
     7  	"github.com/KiraCore/sekai/x/layer2/types"
     8  	sdk "github.com/cosmos/cosmos-sdk/types"
     9  )
    10  
    11  type msgServer struct {
    12  	keeper Keeper
    13  }
    14  
    15  // NewMsgServerImpl returns an implementation of the bank MsgServer interface
    16  // for the provided Keeper.
    17  func NewMsgServerImpl(keeper Keeper) types.MsgServer {
    18  	return &msgServer{
    19  		keeper: keeper,
    20  	}
    21  }
    22  
    23  var _ types.MsgServer = msgServer{}
    24  
    25  func (k msgServer) CreateDappProposal(goCtx context.Context, msg *types.MsgCreateDappProposal) (*types.MsgCreateDappProposalResponse, error) {
    26  	ctx := sdk.UnwrapSDKContext(goCtx)
    27  
    28  	properties := k.keeper.gk.GetNetworkProperties(ctx)
    29  	addr := sdk.MustAccAddressFromBech32(msg.Sender)
    30  
    31  	// permission check PermCreateDappProposalWithoutBond
    32  	isAllowed := k.keeper.CheckIfAllowedPermission(ctx, addr, govtypes.PermCreateDappProposalWithoutBond)
    33  	if !isAllowed {
    34  		minDappBond := properties.MinDappBond
    35  		if msg.Bond.Denom != k.keeper.DefaultDenom(ctx) {
    36  			return nil, types.ErrInvalidDappBondDenom
    37  		}
    38  		// check 1% of properties.MinDappBond
    39  		if msg.Bond.Amount.Mul(sdk.NewInt(100)).LT(sdk.NewInt(int64(minDappBond)).Mul(sdk.NewInt(1000_000))) {
    40  			return nil, types.ErrLowAmountToCreateDappProposal
    41  		}
    42  	}
    43  
    44  	// send initial bond to module account
    45  	if msg.Bond.IsPositive() {
    46  		err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, sdk.Coins{msg.Bond})
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  	}
    51  
    52  	dapp := k.keeper.GetDapp(ctx, msg.Dapp.Name)
    53  	if dapp.Name != "" {
    54  		return nil, types.ErrDappAlreadyExists
    55  	}
    56  
    57  	// create dapp object
    58  	msg.Dapp.TotalBond = msg.Bond
    59  	msg.Dapp.CreationTime = uint64(ctx.BlockTime().Unix())
    60  	msg.Dapp.Status = types.Bootstrap
    61  	k.keeper.SetDapp(ctx, msg.Dapp)
    62  	k.keeper.SetUserDappBond(ctx, types.UserDappBond{
    63  		DappName: msg.Dapp.Name,
    64  		User:     msg.Sender,
    65  		Bond:     msg.Bond,
    66  	})
    67  
    68  	return &types.MsgCreateDappProposalResponse{}, nil
    69  }
    70  
    71  func (k msgServer) BondDappProposal(goCtx context.Context, msg *types.MsgBondDappProposal) (*types.MsgBondDappProposalResponse, error) {
    72  	ctx := sdk.UnwrapSDKContext(goCtx)
    73  
    74  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
    75  	if dapp.Name == "" {
    76  		return nil, types.ErrDappDoesNotExist
    77  	}
    78  
    79  	if k.keeper.DefaultDenom(ctx) != msg.Bond.Denom {
    80  		return nil, types.ErrInvalidDappBondDenom
    81  	}
    82  
    83  	// send initial bond to module account
    84  	addr := sdk.MustAccAddressFromBech32(msg.Sender)
    85  	err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, sdk.Coins{msg.Bond})
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	properties := k.keeper.gk.GetNetworkProperties(ctx)
    91  	if dapp.TotalBond.Amount.GTE(sdk.NewInt(int64(properties.MaxDappBond)).Mul(sdk.NewInt(1000_000))) {
    92  		return nil, types.ErrMaxDappBondReached
    93  	}
    94  
    95  	dapp.TotalBond = dapp.TotalBond.Add(msg.Bond)
    96  	k.keeper.SetDapp(ctx, dapp)
    97  
    98  	userDappBond := k.keeper.GetUserDappBond(ctx, msg.DappName, msg.Sender)
    99  	if userDappBond.User != "" {
   100  		userDappBond.Bond = userDappBond.Bond.Add(msg.Bond)
   101  	} else {
   102  		userDappBond = types.UserDappBond{
   103  			User:     msg.Sender,
   104  			DappName: msg.DappName,
   105  			Bond:     msg.Bond,
   106  		}
   107  	}
   108  	k.keeper.SetUserDappBond(ctx, userDappBond)
   109  
   110  	return &types.MsgBondDappProposalResponse{}, nil
   111  }
   112  
   113  func (k msgServer) ReclaimDappBondProposal(goCtx context.Context, msg *types.MsgReclaimDappBondProposal) (*types.MsgReclaimDappBondProposalResponse, error) {
   114  	ctx := sdk.UnwrapSDKContext(goCtx)
   115  
   116  	userDappBond := k.keeper.GetUserDappBond(ctx, msg.DappName, msg.Sender)
   117  	if userDappBond.DappName == "" {
   118  		return nil, types.ErrUserDappBondDoesNotExist
   119  	}
   120  	if userDappBond.Bond.Denom != msg.Bond.Denom {
   121  		return nil, types.ErrInvalidDappBondDenom
   122  	}
   123  	if userDappBond.Bond.Amount.LT(msg.Bond.Amount) {
   124  		return nil, types.ErrNotEnoughUserDappBond
   125  	}
   126  
   127  	userDappBond.Bond.Amount = userDappBond.Bond.Amount.Sub(msg.Bond.Amount)
   128  	k.keeper.SetUserDappBond(ctx, userDappBond)
   129  
   130  	// send tokens back to user
   131  	addr := sdk.MustAccAddressFromBech32(msg.Sender)
   132  	err := k.keeper.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, sdk.Coins{msg.Bond})
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	return &types.MsgReclaimDappBondProposalResponse{}, nil
   138  }
   139  
   140  func (k msgServer) JoinDappVerifierWithBond(goCtx context.Context, msg *types.MsgJoinDappVerifierWithBond) (*types.MsgJoinDappVerifierWithBondResponse, error) {
   141  	ctx := sdk.UnwrapSDKContext(goCtx)
   142  
   143  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
   144  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   145  	if operator.DappName != "" && operator.Verifier {
   146  		return nil, types.ErrAlreadyADappVerifier
   147  	}
   148  
   149  	properties := k.keeper.gk.GetNetworkProperties(ctx)
   150  	verifierBond := properties.DappVerifierBond
   151  	totalSupply := dapp.GetLpTokenSupply()
   152  	dappBondLpToken := dapp.LpToken()
   153  	lpTokenAmount := sdk.NewDecFromInt(totalSupply).Mul(verifierBond).RoundInt()
   154  	verifierBondCoins := sdk.NewCoins(sdk.NewCoin(dappBondLpToken, lpTokenAmount))
   155  	addr := sdk.MustAccAddressFromBech32(msg.Interx)
   156  	if verifierBondCoins.IsAllPositive() {
   157  		err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, verifierBondCoins)
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  	}
   162  
   163  	if operator.DappName == "" {
   164  		operator = types.DappOperator{
   165  			DappName:       msg.DappName,
   166  			Interx:         msg.Interx,
   167  			Operator:       msg.Sender,
   168  			Executor:       false,
   169  			Verifier:       true,
   170  			Status:         types.OperatorActive,
   171  			BondedLpAmount: lpTokenAmount,
   172  		}
   173  	} else {
   174  		operator.Verifier = true
   175  		operator.BondedLpAmount = lpTokenAmount
   176  	}
   177  	k.keeper.SetDappOperator(ctx, operator)
   178  	return &types.MsgJoinDappVerifierWithBondResponse{}, nil
   179  }
   180  
   181  func (k msgServer) ExitDapp(goCtx context.Context, msg *types.MsgExitDapp) (*types.MsgExitDappResponse, error) {
   182  	ctx := sdk.UnwrapSDKContext(goCtx)
   183  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   184  	if operator.DappName == "" {
   185  		return nil, types.ErrNotDappOperator
   186  	}
   187  
   188  	if operator.Status == types.OperatorJailed {
   189  		return nil, types.ErrOperatorJailed
   190  	}
   191  	if operator.Status == types.OperatorExiting {
   192  		return nil, types.ErrOperatorAlreadyExiting
   193  	}
   194  
   195  	operator.Status = types.OperatorExiting
   196  	k.keeper.SetDappOperator(ctx, operator)
   197  
   198  	return &types.MsgExitDappResponse{}, nil
   199  }
   200  
   201  func (k msgServer) PauseDappTx(goCtx context.Context, msg *types.MsgPauseDappTx) (*types.MsgPauseDappTxResponse, error) {
   202  	ctx := sdk.UnwrapSDKContext(goCtx)
   203  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   204  	if operator.DappName == "" {
   205  		return nil, types.ErrNotDappOperator
   206  	}
   207  	if operator.Status != types.OperatorActive {
   208  		return nil, types.ErrDappOperatorNotActive
   209  	}
   210  	operator.Status = types.OperatorPaused
   211  	k.keeper.SetDappOperator(ctx, operator)
   212  
   213  	// TODO: if the validator status changes to paused, inactive or jailed then his executor status for ALL dApps should
   214  	// also change to the same paused, inactive or jailed status so that all other executors can be informed that
   215  	// a specific node operator is not available and will miss his execution round.
   216  
   217  	return &types.MsgPauseDappTxResponse{}, nil
   218  }
   219  
   220  func (k msgServer) UnPauseDappTx(goCtx context.Context, msg *types.MsgUnPauseDappTx) (*types.MsgUnPauseDappTxResponse, error) {
   221  	ctx := sdk.UnwrapSDKContext(goCtx)
   222  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   223  	if operator.DappName == "" {
   224  		return nil, types.ErrNotDappOperator
   225  	}
   226  	if operator.Status != types.OperatorPaused {
   227  		return nil, types.ErrDappOperatorNotPaused
   228  	}
   229  	operator.Status = types.OperatorActive
   230  	k.keeper.SetDappOperator(ctx, operator)
   231  
   232  	return &types.MsgUnPauseDappTxResponse{}, nil
   233  }
   234  
   235  func (k msgServer) ReactivateDappTx(goCtx context.Context, msg *types.MsgReactivateDappTx) (*types.MsgReactivateDappTxResponse, error) {
   236  	ctx := sdk.UnwrapSDKContext(goCtx)
   237  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   238  	if operator.DappName == "" {
   239  		return nil, types.ErrNotDappOperator
   240  	}
   241  	if operator.Status != types.OperatorInactive {
   242  		return nil, types.ErrDappOperatorNotInActive
   243  	}
   244  	operator.Status = types.OperatorActive
   245  	k.keeper.SetDappOperator(ctx, operator)
   246  
   247  	return &types.MsgReactivateDappTxResponse{}, nil
   248  }
   249  
   250  func (k msgServer) ExecuteDappTx(goCtx context.Context, msg *types.MsgExecuteDappTx) (*types.MsgExecuteDappTxResponse, error) {
   251  	ctx := sdk.UnwrapSDKContext(goCtx)
   252  	session := k.keeper.GetDappSession(ctx, msg.DappName)
   253  	if session.DappName == "" {
   254  		return nil, types.ErrNoDappSessionExists
   255  	}
   256  
   257  	if session.NextSession.Leader != msg.Sender {
   258  		return nil, types.ErrNotDappSessionLeader
   259  	}
   260  	session.NextSession.Gateway = msg.Gateway
   261  	session.NextSession.Status = types.SessionOngoing
   262  
   263  	if session.CurrSession == nil {
   264  		session.CurrSession = session.NextSession
   265  		k.keeper.CreateNewSession(ctx, msg.DappName, session.CurrSession.Leader)
   266  	}
   267  
   268  	return &types.MsgExecuteDappTxResponse{}, nil
   269  }
   270  
   271  func (k msgServer) TransitionDappTx(goCtx context.Context, msg *types.MsgTransitionDappTx) (*types.MsgTransitionDappTxResponse, error) {
   272  	ctx := sdk.UnwrapSDKContext(goCtx)
   273  
   274  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
   275  	if dapp.Name != "" {
   276  		return nil, types.ErrDappDoesNotExist
   277  	}
   278  
   279  	if msg.Version != dapp.Version() {
   280  		return nil, types.ErrInvalidDappVersion
   281  	}
   282  
   283  	session := k.keeper.GetDappSession(ctx, msg.DappName)
   284  	if session.DappName == "" {
   285  		return nil, types.ErrDappSessionDoesNotExist
   286  	}
   287  	session.NextSession.StatusHash = msg.StatusHash
   288  	session.NextSession.OnchainMessages = msg.OnchainMessages
   289  	k.keeper.SetDappSession(ctx, session)
   290  
   291  	return &types.MsgTransitionDappTxResponse{}, nil
   292  }
   293  
   294  func (k msgServer) DenounceLeaderTx(goCtx context.Context, msg *types.MsgDenounceLeaderTx) (*types.MsgDenounceLeaderTxResponse, error) {
   295  	ctx := sdk.UnwrapSDKContext(goCtx)
   296  
   297  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   298  	if !operator.Verifier {
   299  		return nil, types.ErrNotDappVerifier
   300  	}
   301  
   302  	session := k.keeper.GetDappSession(ctx, msg.DappName)
   303  	if session.CurrSession == nil || session.CurrSession.StatusHash == "" {
   304  		return nil, types.ErrVerificationNotAllowedOnEmptySession
   305  	}
   306  	if session.CurrSession.Leader == msg.Sender {
   307  		return nil, types.ErrLeaderCannotEvaluateSelfSubmission
   308  	}
   309  
   310  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
   311  	if dapp.Name != "" {
   312  		return nil, types.ErrDappDoesNotExist
   313  	}
   314  
   315  	if msg.Version != dapp.Version() {
   316  		return nil, types.ErrInvalidDappVersion
   317  	}
   318  
   319  	// TODO: update it to be put on session
   320  	k.keeper.SetDappLeaderDenouncement(ctx, types.DappLeaderDenouncement{
   321  		DappName:     msg.DappName,
   322  		Leader:       msg.Leader,
   323  		Sender:       msg.Sender,
   324  		Denouncement: msg.DenounceText,
   325  	})
   326  
   327  	return &types.MsgDenounceLeaderTxResponse{}, nil
   328  }
   329  
   330  func (k msgServer) ApproveDappTransitionTx(goCtx context.Context, msg *types.MsgApproveDappTransitionTx) (*types.MsgApproveDappTransitionTxResponse, error) {
   331  	ctx := sdk.UnwrapSDKContext(goCtx)
   332  
   333  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
   334  	if dapp.Name != "" {
   335  		return nil, types.ErrDappDoesNotExist
   336  	}
   337  
   338  	if msg.Version != dapp.Version() {
   339  		return nil, types.ErrInvalidDappVersion
   340  	}
   341  
   342  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   343  	if !operator.Verifier {
   344  		return nil, types.ErrNotDappVerifier
   345  	}
   346  
   347  	session := k.keeper.GetDappSession(ctx, msg.DappName)
   348  	if session.CurrSession == nil || session.CurrSession.StatusHash == "" {
   349  		return nil, types.ErrVerificationNotAllowedOnEmptySession
   350  	}
   351  	if session.CurrSession.Leader == msg.Sender {
   352  		return nil, types.ErrLeaderCannotEvaluateSelfSubmission
   353  	}
   354  	k.keeper.SetDappSessionApproval(ctx, types.DappSessionApproval{
   355  		DappName:   msg.DappName,
   356  		Approver:   msg.Sender,
   357  		IsApproved: true,
   358  	})
   359  
   360  	// TODO: check after full implementation
   361  	// The current session status can change to `accepted` if and only if 2/3 of executors who are NOT a leader send
   362  	// `approve-dapp-transition-tx` and no verifiers submitted the evidence of faults requesting the application to be halted,
   363  	// additionally the total number of approvals must be no less then `verifiers_min`.
   364  	// It might happen that the application will only have a single executor,
   365  	// meaning that there is always an insufficient number of verifiers to approve the transition.
   366  	// In such cases where only one executor of the dApp exists, the approval of **NO LESS THAN** 2/3 of ALL active verifiers is required
   367  	// for the session state to change into `accepted` (the `verifiers_min` rule also applies).
   368  
   369  	// if more than 2/3 verify, convert to accepted
   370  	verifiers := k.keeper.GetDappVerifiers(ctx, msg.DappName)
   371  	approvals := k.keeper.GetDappSessionApprovals(ctx, msg.DappName)
   372  	if len(verifiers)*2/3 <= len(approvals) {
   373  		session.CurrSession.Status = types.SessionAccepted
   374  		k.keeper.SetDappSession(ctx, session)
   375  		k.keeper.CreateNewSession(ctx, msg.DappName, session.NextSession.Leader)
   376  
   377  		isApprover := make(map[string]bool)
   378  		for _, approval := range approvals {
   379  			isApprover[approval.Approver] = true
   380  		}
   381  
   382  		// dapp operator rank management
   383  		executor := k.keeper.GetDappOperator(ctx, session.DappName, session.CurrSession.Leader)
   384  		k.keeper.HandleSessionParticipation(ctx, executor, true)
   385  		for _, verifier := range verifiers {
   386  			k.keeper.HandleSessionParticipation(ctx, verifier, isApprover[verifier.Operator])
   387  		}
   388  	}
   389  
   390  	return &types.MsgApproveDappTransitionTxResponse{}, nil
   391  }
   392  
   393  func (k msgServer) RejectDappTransitionTx(goCtx context.Context, msg *types.MsgRejectDappTransitionTx) (*types.MsgRejectDappTransitionTxResponse, error) {
   394  	ctx := sdk.UnwrapSDKContext(goCtx)
   395  	operator := k.keeper.GetDappOperator(ctx, msg.DappName, msg.Sender)
   396  	if !operator.Verifier {
   397  		return nil, types.ErrNotDappVerifier
   398  	}
   399  
   400  	session := k.keeper.GetDappSession(ctx, msg.DappName)
   401  	if session.CurrSession.Leader == msg.Sender {
   402  		return nil, types.ErrLeaderCannotEvaluateSelfSubmission
   403  	}
   404  
   405  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
   406  	if dapp.Name != "" {
   407  		return nil, types.ErrDappDoesNotExist
   408  	}
   409  
   410  	if msg.Version != dapp.Version() {
   411  		return nil, types.ErrInvalidDappVersion
   412  	}
   413  
   414  	if session.CurrSession == nil || session.CurrSession.StatusHash == "" {
   415  		return nil, types.ErrVerificationNotAllowedOnEmptySession
   416  	}
   417  
   418  	// halt the session
   419  	session.CurrSession.Status = types.SessionHalted
   420  	k.keeper.SetDappSession(ctx, session)
   421  
   422  	k.keeper.SetDappSessionApproval(ctx, types.DappSessionApproval{
   423  		DappName:   msg.DappName,
   424  		Approver:   msg.Sender,
   425  		IsApproved: true,
   426  	})
   427  
   428  	return &types.MsgRejectDappTransitionTxResponse{}, nil
   429  }
   430  
   431  func (k Keeper) GetCoinsFromBridgeBalance(ctx sdk.Context, balances []types.BridgeBalance) sdk.Coins {
   432  	coins := sdk.Coins{}
   433  	for _, balance := range balances {
   434  		token := k.GetBridgeToken(ctx, balance.BridgeTokenIndex)
   435  		if token.Denom == "" {
   436  			continue
   437  		}
   438  		coins = coins.Add(sdk.NewCoin(token.Denom, balance.Amount))
   439  	}
   440  	return coins
   441  }
   442  
   443  func AddBridgeBalance(balances []types.BridgeBalance, addition []types.BridgeBalance) []types.BridgeBalance {
   444  	indexMap := make(map[uint64]int)
   445  	for i, balance := range balances {
   446  		indexMap[balance.BridgeTokenIndex] = i
   447  	}
   448  
   449  	for _, balance := range addition {
   450  		i, ok := indexMap[balance.BridgeTokenIndex]
   451  		if ok {
   452  			balances[i].Amount = balances[i].Amount.Add(balance.Amount)
   453  		} else {
   454  			balances = append(balances, balance)
   455  		}
   456  	}
   457  	return balances
   458  }
   459  
   460  func SubBridgeBalance(balances []types.BridgeBalance, removal []types.BridgeBalance) ([]types.BridgeBalance, error) {
   461  	indexMap := make(map[uint64]int)
   462  	for i, balance := range balances {
   463  		indexMap[balance.BridgeTokenIndex] = i
   464  	}
   465  
   466  	for _, balance := range removal {
   467  		i, ok := indexMap[balance.BridgeTokenIndex]
   468  		if ok {
   469  			balances[i].Amount = balances[i].Amount.Sub(balance.Amount)
   470  			if balances[i].Amount.IsNegative() {
   471  				return balances, types.ErrNegativeBridgeBalance
   472  			}
   473  		} else {
   474  			return balances, types.ErrNegativeBridgeBalance
   475  		}
   476  	}
   477  	return balances, nil
   478  }
   479  
   480  func (k msgServer) TransferDappTx(goCtx context.Context, msg *types.MsgTransferDappTx) (*types.MsgTransferDappTxResponse, error) {
   481  	ctx := sdk.UnwrapSDKContext(goCtx)
   482  	sender := sdk.MustAccAddressFromBech32(msg.Sender)
   483  	helper := k.keeper.GetBridgeRegistrarHelper(ctx)
   484  	nextXid := helper.NextXam
   485  	for _, xam := range msg.Requests {
   486  		coins := k.keeper.GetCoinsFromBridgeBalance(ctx, xam.Amounts)
   487  		if !coins.Empty() {
   488  			sa := k.keeper.GetBridgeAccount(ctx, xam.SourceAccount)
   489  			if xam.SourceDapp == 0 { // direct deposit from user
   490  				if sa.Address == "" {
   491  					sa.Address = msg.Sender
   492  					sa.Index = helper.NextUser
   493  					sa.DappName = ""
   494  					helper.NextUser += 1
   495  					k.keeper.SetBridgeRegistrarHelper(ctx, helper)
   496  				}
   497  				if sa.Address != msg.Sender {
   498  					return nil, types.ErrInvalidBridgeDepositMessage
   499  				}
   500  				err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, coins)
   501  				if err != nil {
   502  					return nil, err
   503  				}
   504  				sa.Balances = AddBridgeBalance(sa.Balances, xam.Amounts)
   505  				k.keeper.SetBridgeAccount(ctx, sa)
   506  			} else if xam.DestDapp == 0 { // withdrawal to user account
   507  				ba := k.keeper.GetBridgeAccount(ctx, xam.DestBeneficiary)
   508  				if ba.Address != msg.Sender {
   509  					return nil, types.ErrInvalidBridgeWithdrawalMessage
   510  				}
   511  				err := k.keeper.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, coins)
   512  				if err != nil {
   513  					return nil, err
   514  				}
   515  				sa.Balances, err = SubBridgeBalance(sa.Balances, xam.Amounts)
   516  				if err != nil {
   517  					return nil, err
   518  				}
   519  				k.keeper.SetBridgeAccount(ctx, sa)
   520  			} else {
   521  				// check accuracy of source in case of dapp
   522  				sa := k.keeper.GetBridgeAccount(ctx, xam.SourceAccount)
   523  				if sa.Address != msg.Sender {
   524  					return nil, types.ErrInvalidBridgeSourceAccount
   525  				}
   526  			}
   527  		}
   528  		k.keeper.SetXAM(ctx, types.XAM{
   529  			Req: xam,
   530  			Res: types.XAMResponse{
   531  				Xid: nextXid,
   532  				Irc: 0,
   533  				Src: 0,
   534  				Drc: 0,
   535  				Irm: 0,
   536  				Srm: 0,
   537  				Drm: 0,
   538  			},
   539  		})
   540  		nextXid += 1
   541  	}
   542  	helper.NextXam = nextXid
   543  	k.keeper.SetBridgeRegistrarHelper(ctx, helper)
   544  
   545  	return &types.MsgTransferDappTxResponse{}, nil
   546  }
   547  
   548  func (k msgServer) AckTransferDappTx(goCtx context.Context, msg *types.MsgAckTransferDappTx) (*types.MsgAckTransferDappTxResponse, error) {
   549  	ctx := sdk.UnwrapSDKContext(goCtx)
   550  	for _, res := range msg.Responses {
   551  		xam := k.keeper.GetXAM(ctx, res.Xid)
   552  		if xam.Res.Drc != 0 { // response already set
   553  			continue
   554  		}
   555  		xam.Res = res
   556  		k.keeper.SetXAM(ctx, xam)
   557  		// handle token transfer when response is okay
   558  		if res.Drc == 200 { // status okay
   559  			var err error
   560  			sa := k.keeper.GetBridgeAccount(ctx, xam.Req.SourceAccount)
   561  			sa.Balances, err = SubBridgeBalance(sa.Balances, xam.Req.Amounts)
   562  			if err != nil {
   563  				return nil, err
   564  			}
   565  			k.keeper.SetBridgeAccount(ctx, sa)
   566  			da := k.keeper.GetBridgeAccount(ctx, xam.Req.DestBeneficiary)
   567  			da.Balances = AddBridgeBalance(sa.Balances, xam.Req.Amounts)
   568  			if err != nil {
   569  				return nil, err
   570  			}
   571  			k.keeper.SetBridgeAccount(ctx, da)
   572  		}
   573  	}
   574  
   575  	return &types.MsgAckTransferDappTxResponse{}, nil
   576  }
   577  
   578  func (k msgServer) RedeemDappPoolTx(goCtx context.Context, msg *types.MsgRedeemDappPoolTx) (*types.MsgRedeemDappPoolTxResponse, error) {
   579  	ctx := sdk.UnwrapSDKContext(goCtx)
   580  	addr := sdk.MustAccAddressFromBech32(msg.Sender)
   581  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
   582  	if dapp.Name != "" {
   583  		return nil, types.ErrDappDoesNotExist
   584  	}
   585  	lpTokenPrice := k.keeper.LpTokenPrice(ctx, dapp)
   586  	withoutSlippage := sdk.NewDecFromInt(msg.LpToken.Amount).Mul(lpTokenPrice)
   587  
   588  	receiveCoin, err := k.keeper.RedeemDappPoolTx(ctx, addr, dapp, dapp.PoolFee, msg.LpToken)
   589  	if err != nil {
   590  		return nil, err
   591  	}
   592  
   593  	properties := k.keeper.gk.GetNetworkProperties(ctx)
   594  	maxSlippage := msg.Slippage
   595  	if maxSlippage.IsZero() {
   596  		maxSlippage = properties.DappPoolSlippageDefault
   597  	}
   598  	slippage := sdk.OneDec().Sub(sdk.NewDecFromInt(receiveCoin.Amount).Quo(withoutSlippage))
   599  	if slippage.GT(maxSlippage) {
   600  		return nil, types.ErrOperationExceedsSlippage
   601  	}
   602  
   603  	return &types.MsgRedeemDappPoolTxResponse{}, nil
   604  }
   605  
   606  func (k msgServer) SwapDappPoolTx(goCtx context.Context, msg *types.MsgSwapDappPoolTx) (*types.MsgSwapDappPoolTxResponse, error) {
   607  	ctx := sdk.UnwrapSDKContext(goCtx)
   608  
   609  	addr := sdk.MustAccAddressFromBech32(msg.Sender)
   610  	dapp := k.keeper.GetDapp(ctx, msg.DappName)
   611  	if dapp.Name != "" {
   612  		return nil, types.ErrDappDoesNotExist
   613  	}
   614  
   615  	lpTokenPrice := k.keeper.LpTokenPrice(ctx, dapp)
   616  	if lpTokenPrice.IsZero() {
   617  		return nil, types.ErrOperationExceedsSlippage
   618  	}
   619  	withoutSlippage := sdk.NewDecFromInt(msg.Token.Amount).Quo(lpTokenPrice)
   620  
   621  	receiveCoin, err := k.keeper.SwapDappPoolTx(ctx, addr, dapp, dapp.PoolFee, msg.Token)
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  
   626  	properties := k.keeper.gk.GetNetworkProperties(ctx)
   627  	maxSlippage := msg.Slippage
   628  	if maxSlippage.IsZero() {
   629  		maxSlippage = properties.DappPoolSlippageDefault
   630  	}
   631  	slippage := sdk.OneDec().Sub(sdk.NewDecFromInt(receiveCoin.Amount).Quo(withoutSlippage))
   632  	if slippage.GT(maxSlippage) {
   633  		return nil, types.ErrOperationExceedsSlippage
   634  	}
   635  
   636  	return &types.MsgSwapDappPoolTxResponse{}, nil
   637  }
   638  
   639  func (k msgServer) ConvertDappPoolTx(goCtx context.Context, msg *types.MsgConvertDappPoolTx) (*types.MsgConvertDappPoolTxResponse, error) {
   640  	ctx := sdk.UnwrapSDKContext(goCtx)
   641  	addr := sdk.MustAccAddressFromBech32(msg.Sender)
   642  	dapp1 := k.keeper.GetDapp(ctx, msg.DappName)
   643  	if dapp1.Name != "" {
   644  		return nil, types.ErrDappDoesNotExist
   645  	}
   646  
   647  	lpTokenPrice1 := k.keeper.LpTokenPrice(ctx, dapp1)
   648  	if lpTokenPrice1.IsZero() {
   649  		return nil, types.ErrOperationExceedsSlippage
   650  	}
   651  	dapp2 := k.keeper.GetDapp(ctx, msg.TargetDappName)
   652  	if dapp2.Name != "" {
   653  		return nil, types.ErrDappDoesNotExist
   654  	}
   655  
   656  	lpTokenPrice2 := k.keeper.LpTokenPrice(ctx, dapp2)
   657  	if lpTokenPrice2.IsZero() {
   658  		return nil, types.ErrOperationExceedsSlippage
   659  	}
   660  
   661  	withoutSlippage := sdk.NewDecFromInt(msg.LpToken.Amount).Mul(lpTokenPrice1).Quo(lpTokenPrice2)
   662  
   663  	receiveCoin, err := k.keeper.ConvertDappPoolTx(ctx, addr, dapp1, dapp2, msg.LpToken)
   664  	if err != nil {
   665  		return nil, err
   666  	}
   667  
   668  	properties := k.keeper.gk.GetNetworkProperties(ctx)
   669  	maxSlippage := msg.Slippage
   670  	if maxSlippage.IsZero() {
   671  		maxSlippage = properties.DappPoolSlippageDefault
   672  	}
   673  	slippage := sdk.OneDec().Sub(sdk.NewDecFromInt(receiveCoin.Amount).Quo(withoutSlippage))
   674  	if slippage.GT(maxSlippage) {
   675  		return nil, types.ErrOperationExceedsSlippage
   676  	}
   677  
   678  	return &types.MsgConvertDappPoolTxResponse{}, nil
   679  }
   680  
   681  func (k msgServer) MintCreateFtTx(goCtx context.Context, msg *types.MsgMintCreateFtTx) (*types.MsgMintCreateFtTxResponse, error) {
   682  	ctx := sdk.UnwrapSDKContext(goCtx)
   683  	properties := k.keeper.gk.GetNetworkProperties(ctx)
   684  	fee := sdk.NewInt64Coin(k.keeper.DefaultDenom(ctx), int64(properties.MintingFtFee))
   685  	sender := sdk.MustAccAddressFromBech32(msg.Sender)
   686  	err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, sdk.Coins{fee})
   687  	if err != nil {
   688  		return nil, err
   689  	}
   690  
   691  	err = k.keeper.bk.BurnCoins(ctx, types.ModuleName, sdk.Coins{fee})
   692  	if err != nil {
   693  		return nil, err
   694  	}
   695  
   696  	denom := "ku/" + msg.DenomSuffix
   697  
   698  	info := k.keeper.GetTokenInfo(ctx, denom)
   699  	if info.Denom != "" {
   700  		return nil, types.ErrTokenAlreadyRegistered
   701  	}
   702  
   703  	k.keeper.SetTokenInfo(ctx, types.TokenInfo{
   704  		TokenType:   "adr20",
   705  		Denom:       denom,
   706  		Name:        msg.Name,
   707  		Symbol:      msg.Symbol,
   708  		Icon:        msg.Icon,
   709  		Description: msg.Description,
   710  		Website:     msg.Website,
   711  		Social:      msg.Social,
   712  		Decimals:    msg.Decimals,
   713  		Cap:         msg.Cap,
   714  		Supply:      msg.Supply,
   715  		Holders:     msg.Holders,
   716  		Fee:         msg.Fee,
   717  		Owner:       msg.Owner,
   718  		Metadata:    "",
   719  		Hash:        "",
   720  	})
   721  
   722  	return &types.MsgMintCreateFtTxResponse{}, nil
   723  }
   724  
   725  func (k msgServer) MintCreateNftTx(goCtx context.Context, msg *types.MsgMintCreateNftTx) (*types.MsgMintCreateNftTxResponse, error) {
   726  	ctx := sdk.UnwrapSDKContext(goCtx)
   727  	properties := k.keeper.gk.GetNetworkProperties(ctx)
   728  	fee := sdk.NewInt64Coin(k.keeper.DefaultDenom(ctx), int64(properties.MintingFtFee))
   729  	sender := sdk.MustAccAddressFromBech32(msg.Sender)
   730  	err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, sdk.Coins{fee})
   731  	if err != nil {
   732  		return nil, err
   733  	}
   734  
   735  	err = k.keeper.bk.BurnCoins(ctx, types.ModuleName, sdk.Coins{fee})
   736  	if err != nil {
   737  		return nil, err
   738  	}
   739  
   740  	denom := "ku/" + msg.DenomSuffix
   741  	info := k.keeper.GetTokenInfo(ctx, denom)
   742  	if info.Denom != "" {
   743  		return nil, types.ErrTokenAlreadyRegistered
   744  	}
   745  
   746  	k.keeper.SetTokenInfo(ctx, types.TokenInfo{
   747  		TokenType:   "adr43",
   748  		Denom:       denom,
   749  		Name:        msg.Name,
   750  		Symbol:      msg.Symbol,
   751  		Icon:        msg.Icon,
   752  		Description: msg.Description,
   753  		Website:     msg.Website,
   754  		Social:      msg.Social,
   755  		Decimals:    msg.Decimals,
   756  		Cap:         msg.Cap,
   757  		Supply:      msg.Supply,
   758  		Holders:     msg.Holders,
   759  		Fee:         msg.Fee,
   760  		Owner:       msg.Owner,
   761  		Metadata:    msg.Metadata,
   762  		Hash:        msg.Hash,
   763  	})
   764  
   765  	return &types.MsgMintCreateNftTxResponse{}, nil
   766  }
   767  
   768  func (k msgServer) MintIssueTx(goCtx context.Context, msg *types.MsgMintIssueTx) (*types.MsgMintIssueTxResponse, error) {
   769  	ctx := sdk.UnwrapSDKContext(goCtx)
   770  	sender := sdk.MustAccAddressFromBech32(msg.Sender)
   771  	tokenInfo := k.keeper.GetTokenInfo(ctx, msg.Denom)
   772  	if tokenInfo.Denom == "" {
   773  		return nil, types.ErrTokenNotRegistered
   774  	}
   775  
   776  	if msg.Sender != tokenInfo.Owner {
   777  		fee := msg.Amount.Mul(tokenInfo.Fee).Quo(Pow10(tokenInfo.Decimals))
   778  		feeCoins := sdk.Coins{sdk.NewCoin(k.keeper.DefaultDenom(ctx), fee)}
   779  		if fee.IsPositive() {
   780  			if tokenInfo.Owner == "" {
   781  				err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, feeCoins)
   782  				if err != nil {
   783  					return nil, err
   784  				}
   785  			} else {
   786  				owner := sdk.MustAccAddressFromBech32(tokenInfo.Owner)
   787  				err := k.keeper.bk.SendCoins(ctx, sender, owner, feeCoins)
   788  				if err != nil {
   789  					return nil, err
   790  				}
   791  			}
   792  		} else {
   793  			return nil, types.ErrNotAbleToMintCoinsWithoutFee
   794  		}
   795  	}
   796  
   797  	mintCoin := sdk.NewCoin(msg.Denom, msg.Amount)
   798  	err := k.keeper.bk.MintCoins(ctx, types.ModuleName, sdk.Coins{mintCoin})
   799  	if err != nil {
   800  		return nil, err
   801  	}
   802  
   803  	err = k.keeper.bk.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, sdk.Coins{mintCoin})
   804  	if err != nil {
   805  		return nil, err
   806  	}
   807  
   808  	tokenInfo.Supply = tokenInfo.Supply.Add(msg.Amount)
   809  	if tokenInfo.Supply.GT(tokenInfo.Cap) {
   810  		return nil, types.ErrCannotExceedTokenCap
   811  	}
   812  	k.keeper.SetTokenInfo(ctx, tokenInfo)
   813  
   814  	return &types.MsgMintIssueTxResponse{}, nil
   815  }
   816  
   817  func (k msgServer) MintBurnTx(goCtx context.Context, msg *types.MsgMintBurnTx) (*types.MsgMintBurnTxResponse, error) {
   818  	ctx := sdk.UnwrapSDKContext(goCtx)
   819  	sender := sdk.MustAccAddressFromBech32(msg.Sender)
   820  	tokenInfo := k.keeper.GetTokenInfo(ctx, msg.Denom)
   821  	if tokenInfo.Denom == "" {
   822  		return nil, types.ErrTokenNotRegistered
   823  	}
   824  
   825  	burnCoin := sdk.NewCoin(msg.Denom, msg.Amount)
   826  	err := k.keeper.bk.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, sdk.Coins{burnCoin})
   827  	if err != nil {
   828  		return nil, err
   829  	}
   830  
   831  	err = k.keeper.bk.BurnCoins(ctx, types.ModuleName, sdk.Coins{burnCoin})
   832  	if err != nil {
   833  		return nil, err
   834  	}
   835  
   836  	tokenInfo.Supply = tokenInfo.Supply.Sub(msg.Amount)
   837  	k.keeper.SetTokenInfo(ctx, tokenInfo)
   838  
   839  	return &types.MsgMintBurnTxResponse{}, nil
   840  }