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 }