github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/auth/ante/fee.go (about) 1 package ante 2 3 import ( 4 "fmt" 5 6 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 7 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported" 8 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/keeper" 9 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types" 10 11 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 12 ) 13 14 var ( 15 _ FeeTx = (*types.StdTx)(nil) // assert StdTx implements FeeTx 16 ) 17 18 // FeeTx defines the interface to be implemented by Tx to use the FeeDecorators 19 type FeeTx interface { 20 sdk.Tx 21 GetGas() uint64 22 GetFee() sdk.Coins 23 FeePayer(ctx sdk.Context) sdk.AccAddress 24 } 25 26 // MempoolFeeDecorator will check if the transaction's fee is at least as large 27 // as the local validator's minimum gasFee (defined in validator config). 28 // If fee is too low, decorator returns error and tx is rejected from mempool. 29 // Note this only applies when ctx.CheckTx = true 30 // If fee is high enough or not CheckTx, then call next AnteHandler 31 // CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator 32 type MempoolFeeDecorator struct{} 33 34 func NewMempoolFeeDecorator() MempoolFeeDecorator { 35 return MempoolFeeDecorator{} 36 } 37 38 func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 39 feeTx, ok := tx.(FeeTx) 40 if !ok { 41 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") 42 } 43 feeCoins := feeTx.GetFee() 44 gas := feeTx.GetGas() 45 46 // Ensure that the provided fees meet a minimum threshold for the validator, 47 // if this is a CheckTx. This is only for local mempool purposes, and thus 48 // is only ran on check tx. 49 if ctx.IsCheckTx() && !simulate { 50 minGasPrices := ctx.MinGasPrices() 51 if !minGasPrices.IsZero() { 52 requiredFees := make(sdk.Coins, len(minGasPrices)) 53 54 // Determine the required fees by multiplying each required minimum gas 55 // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). 56 glDec := sdk.NewDec(int64(gas)) 57 for i, gp := range minGasPrices { 58 fee := gp.Amount.Mul(glDec) 59 requiredFees[i] = sdk.NewDecCoinFromDec(gp.Denom, fee) 60 } 61 62 if !feeCoins.IsAnyGTE(requiredFees) { 63 return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees) 64 } 65 } 66 } 67 68 return next(ctx, tx, simulate) 69 } 70 71 // DeductFeeDecorator deducts fees from the first signer of the tx 72 // If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error 73 // Call next AnteHandler if fees successfully deducted 74 // CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator 75 type DeductFeeDecorator struct { 76 ak keeper.AccountKeeper 77 supplyKeeper types.SupplyKeeper 78 } 79 80 func NewDeductFeeDecorator(ak keeper.AccountKeeper, sk types.SupplyKeeper) DeductFeeDecorator { 81 return DeductFeeDecorator{ 82 ak: ak, 83 supplyKeeper: sk, 84 } 85 } 86 87 func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 88 feeTx, ok := tx.(FeeTx) 89 if !ok { 90 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") 91 } 92 93 if addr := dfd.supplyKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil { 94 panic(fmt.Sprintf("%s module account has not been set", types.FeeCollectorName)) 95 } 96 97 feePayer := feeTx.FeePayer(ctx) 98 feePayerAcc := dfd.ak.GetAccount(ctx, feePayer) 99 100 if feePayerAcc == nil { 101 return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) 102 } 103 104 // deduct the fees 105 if !feeTx.GetFee().IsZero() { 106 err = DeductFees(dfd.supplyKeeper, ctx, feePayerAcc, feeTx.GetFee()) 107 if err != nil { 108 return ctx, err 109 } 110 } 111 112 return next(ctx, tx, simulate) 113 } 114 115 // DeductFees deducts fees from the given account. 116 // 117 // NOTE: We could use the BankKeeper (in addition to the AccountKeeper, because 118 // the BankKeeper doesn't give us accounts), but it seems easier to do this. 119 func DeductFees(supplyKeeper types.SupplyKeeper, ctx sdk.Context, acc exported.Account, fees sdk.Coins) error { 120 blockTime := ctx.BlockTime() 121 coins := acc.GetCoins() 122 123 if !fees.IsValid() { 124 return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) 125 } 126 127 // verify the account has enough funds to pay for fees 128 _, hasNeg := coins.SafeSub(fees) 129 if hasNeg { 130 return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, 131 "insufficient funds to pay for fees; %s < %s", coins, fees) 132 } 133 134 // Validate the account has enough "spendable" coins as this will cover cases 135 // such as vesting accounts. 136 spendableCoins := acc.SpendableCoins(blockTime) 137 if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg { 138 return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, 139 "insufficient funds to pay for fees; %s < %s", spendableCoins, fees) 140 } 141 142 err := supplyKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees) 143 if err != nil { 144 return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) 145 } 146 147 return nil 148 }