github.com/Finschia/finschia-sdk@v0.49.1/x/auth/ante/fee.go (about)

     1  package ante
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/Finschia/finschia-sdk/types"
     7  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
     8  	"github.com/Finschia/finschia-sdk/x/auth/types"
     9  )
    10  
    11  // MempoolFeeDecorator will check if the transaction's fee is at least as large
    12  // as the local validator's minimum gasFee (defined in validator config).
    13  // If fee is too low, decorator returns error and tx is rejected from mempool.
    14  // Note this only applies when ctx.CheckTx = true
    15  // If fee is high enough or not CheckTx, then call next AnteHandler
    16  // CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator
    17  type MempoolFeeDecorator struct{}
    18  
    19  func NewMempoolFeeDecorator() MempoolFeeDecorator {
    20  	return MempoolFeeDecorator{}
    21  }
    22  
    23  func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
    24  	feeTx, ok := tx.(sdk.FeeTx)
    25  	if !ok {
    26  		return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
    27  	}
    28  
    29  	feeCoins := feeTx.GetFee()
    30  	gas := feeTx.GetGas()
    31  
    32  	// Ensure that the provided fees meet a minimum threshold for the validator,
    33  	// if this is a CheckTx. This is only for local mempool purposes, and thus
    34  	// is only ran on check tx.
    35  	if ctx.IsCheckTx() && !simulate {
    36  		minGasPrices := ctx.MinGasPrices()
    37  		if !minGasPrices.IsZero() {
    38  			requiredFees := make(sdk.Coins, len(minGasPrices))
    39  
    40  			// Determine the required fees by multiplying each required minimum gas
    41  			// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
    42  			glDec := sdk.NewDec(int64(gas))
    43  			for i, gp := range minGasPrices {
    44  				fee := gp.Amount.Mul(glDec)
    45  				requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
    46  			}
    47  
    48  			if !feeCoins.IsAnyGTE(requiredFees) {
    49  				return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
    50  			}
    51  		}
    52  	}
    53  
    54  	return next(ctx, tx, simulate)
    55  }
    56  
    57  // DeductFeeDecorator deducts fees from the first signer of the tx
    58  // If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error
    59  // Call next AnteHandler if fees successfully deducted
    60  // CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator
    61  type DeductFeeDecorator struct {
    62  	ak             AccountKeeper
    63  	bankKeeper     types.BankKeeper
    64  	feegrantKeeper FeegrantKeeper
    65  }
    66  
    67  func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper, fk FeegrantKeeper) DeductFeeDecorator {
    68  	return DeductFeeDecorator{
    69  		ak:             ak,
    70  		bankKeeper:     bk,
    71  		feegrantKeeper: fk,
    72  	}
    73  }
    74  
    75  func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
    76  	feeTx, ok := tx.(sdk.FeeTx)
    77  	if !ok {
    78  		return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
    79  	}
    80  
    81  	if addr := dfd.ak.GetModuleAddress(types.FeeCollectorName); addr.Empty() {
    82  		return ctx, fmt.Errorf("fee collector module account (%s) has not been set", types.FeeCollectorName)
    83  	}
    84  
    85  	fee := feeTx.GetFee()
    86  	feePayer := feeTx.FeePayer()
    87  	feeGranter := feeTx.FeeGranter()
    88  
    89  	deductFeesFrom := feePayer
    90  
    91  	// if feegranter set deduct fee from feegranter account.
    92  	// this works with only when feegrant enabled.
    93  	if feeGranter != nil {
    94  		if dfd.feegrantKeeper == nil {
    95  			return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not enabled")
    96  		} else if !feeGranter.Equals(feePayer) {
    97  			err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs())
    98  			if err != nil {
    99  				return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer)
   100  			}
   101  		}
   102  
   103  		deductFeesFrom = feeGranter
   104  	}
   105  
   106  	deductFeesFromAcc := dfd.ak.GetAccount(ctx, deductFeesFrom)
   107  	if deductFeesFromAcc == nil {
   108  		return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom)
   109  	}
   110  
   111  	// deduct the fees
   112  	if !feeTx.GetFee().IsZero() {
   113  		err = DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, feeTx.GetFee())
   114  		if err != nil {
   115  			return ctx, err
   116  		}
   117  	}
   118  
   119  	events := sdk.Events{
   120  		sdk.NewEvent(
   121  			sdk.EventTypeTx,
   122  			sdk.NewAttribute(sdk.AttributeKeyFee, fee.String()),
   123  			sdk.NewAttribute(sdk.AttributeKeyFeePayer, deductFeesFrom.String()),
   124  		),
   125  	}
   126  	ctx.EventManager().EmitEvents(events)
   127  
   128  	return next(ctx, tx, simulate)
   129  }
   130  
   131  // DeductFees deducts fees from the given account.
   132  func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins) error {
   133  	if !fees.IsValid() {
   134  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
   135  	}
   136  
   137  	err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees)
   138  	if err != nil {
   139  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
   140  	}
   141  
   142  	return nil
   143  }