github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/ante/fee.go (about)

     1  package ante
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	errorsmod "cosmossdk.io/errors"
     8  
     9  	sdk "github.com/cosmos/cosmos-sdk/types"
    10  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    11  	"github.com/cosmos/cosmos-sdk/x/auth/types"
    12  )
    13  
    14  // TxFeeChecker check if the provided fee is enough and returns the effective fee and tx priority,
    15  // the effective fee should be deducted later, and the priority should be returned in abci response.
    16  type TxFeeChecker func(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error)
    17  
    18  // DeductFeeDecorator deducts fees from the fee payer. The fee payer is the fee granter (if specified) or first signer of the tx.
    19  // If the fee payer does not have the funds to pay for the fees, return an InsufficientFunds error.
    20  // Call next AnteHandler if fees successfully deducted.
    21  // CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator
    22  type DeductFeeDecorator struct {
    23  	accountKeeper  AccountKeeper
    24  	bankKeeper     types.BankKeeper
    25  	feegrantKeeper FeegrantKeeper
    26  	txFeeChecker   TxFeeChecker
    27  }
    28  
    29  func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper, fk FeegrantKeeper, tfc TxFeeChecker) DeductFeeDecorator {
    30  	if tfc == nil {
    31  		tfc = checkTxFeeWithValidatorMinGasPrices
    32  	}
    33  
    34  	return DeductFeeDecorator{
    35  		accountKeeper:  ak,
    36  		bankKeeper:     bk,
    37  		feegrantKeeper: fk,
    38  		txFeeChecker:   tfc,
    39  	}
    40  }
    41  
    42  func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
    43  	feeTx, ok := tx.(sdk.FeeTx)
    44  	if !ok {
    45  		return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
    46  	}
    47  
    48  	if !simulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 {
    49  		return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas")
    50  	}
    51  
    52  	var (
    53  		priority int64
    54  		err      error
    55  	)
    56  
    57  	fee := feeTx.GetFee()
    58  	if !simulate {
    59  		fee, priority, err = dfd.txFeeChecker(ctx, tx)
    60  		if err != nil {
    61  			return ctx, err
    62  		}
    63  	}
    64  	if err := dfd.checkDeductFee(ctx, tx, fee); err != nil {
    65  		return ctx, err
    66  	}
    67  
    68  	newCtx := ctx.WithPriority(priority)
    69  
    70  	return next(newCtx, tx, simulate)
    71  }
    72  
    73  func (dfd DeductFeeDecorator) checkDeductFee(ctx sdk.Context, sdkTx sdk.Tx, fee sdk.Coins) error {
    74  	feeTx, ok := sdkTx.(sdk.FeeTx)
    75  	if !ok {
    76  		return errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
    77  	}
    78  
    79  	if addr := dfd.accountKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil {
    80  		return fmt.Errorf("fee collector module account (%s) has not been set", types.FeeCollectorName)
    81  	}
    82  
    83  	feePayer := feeTx.FeePayer()
    84  	feeGranter := feeTx.FeeGranter()
    85  	deductFeesFrom := feePayer
    86  
    87  	// if feegranter set deduct fee from feegranter account.
    88  	// this works with only when feegrant enabled.
    89  	if feeGranter != nil {
    90  		feeGranterAddr := sdk.AccAddress(feeGranter)
    91  
    92  		if dfd.feegrantKeeper == nil {
    93  			return sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled")
    94  		} else if !bytes.Equal(feeGranterAddr, feePayer) {
    95  			err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranterAddr, feePayer, fee, sdkTx.GetMsgs())
    96  			if err != nil {
    97  				return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer)
    98  			}
    99  		}
   100  
   101  		deductFeesFrom = feeGranterAddr
   102  	}
   103  
   104  	deductFeesFromAcc := dfd.accountKeeper.GetAccount(ctx, deductFeesFrom)
   105  	if deductFeesFromAcc == nil {
   106  		return sdkerrors.ErrUnknownAddress.Wrapf("fee payer address: %s does not exist", deductFeesFrom)
   107  	}
   108  
   109  	// deduct the fees
   110  	if !fee.IsZero() {
   111  		err := DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, fee)
   112  		if err != nil {
   113  			return err
   114  		}
   115  	}
   116  
   117  	events := sdk.Events{
   118  		sdk.NewEvent(
   119  			sdk.EventTypeTx,
   120  			sdk.NewAttribute(sdk.AttributeKeyFee, fee.String()),
   121  			sdk.NewAttribute(sdk.AttributeKeyFeePayer, sdk.AccAddress(deductFeesFrom).String()),
   122  		),
   123  	}
   124  	ctx.EventManager().EmitEvents(events)
   125  
   126  	return nil
   127  }
   128  
   129  // DeductFees deducts fees from the given account.
   130  func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc sdk.AccountI, fees sdk.Coins) error {
   131  	if !fees.IsValid() {
   132  		return errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
   133  	}
   134  
   135  	err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees)
   136  	if err != nil {
   137  		return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
   138  	}
   139  
   140  	return nil
   141  }