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 }