package keeper
import (
"math/big"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
// DeductTxCostsFromUserBalance deducts the fees from the user balance. Returns an
// error if the specified sender address does not exist or the account balance is not sufficient.
func (k *Keeper) DeductTxCostsFromUserBalance(
ctx sdk.Context,
fees sdk.Coins,
from common.Address,
) error {
// fetch sender account
signerAcc, err := authante.GetSignerAcc(ctx, k.accountKeeper, from.Bytes())
if err != nil {
return errorsmod.Wrapf(err, "account not found for sender %s", from)
}
// deduct the full gas cost from the user balance
if err := authante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil {
return errorsmod.Wrapf(err, "failed to deduct full gas cost %s from the user %s balance", fees, from)
return nil
// VerifyFee is used to return the fee for the given transaction data in sdk.Coins. It checks that the
// gas limit is not reached, the gas limit is higher than the intrinsic gas and that the
// base fee is higher than the gas fee cap.
func VerifyFee(
txData evmtypes.TxData,
denom string,
baseFee *big.Int,
homestead, istanbul, isCheckTx bool,
) (sdk.Coins, error) {
isContractCreation := txData.GetTo() == nil
gasLimit := txData.GetGas()
var accessList ethtypes.AccessList
if txData.GetAccessList() != nil {
accessList = txData.GetAccessList()
intrinsicGas, err := core.IntrinsicGas(txData.GetData(), accessList, isContractCreation, homestead, istanbul)
return nil, errorsmod.Wrapf(
err,
"failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t",
isContractCreation, homestead, istanbul,
// intrinsic gas verification during CheckTx
if isCheckTx && gasLimit < intrinsicGas {
errortypes.ErrOutOfGas,
"gas limit too low: %d (gas limit) < %d (intrinsic gas)", gasLimit, intrinsicGas,
if baseFee != nil && txData.GetGasFeeCap().Cmp(baseFee) < 0 {
return nil, errorsmod.Wrapf(errortypes.ErrInsufficientFee,
"the tx gasfeecap is lower than the tx baseFee: %s (gasfeecap), %s (basefee) ",
txData.GetGasFeeCap(),
baseFee)
feeAmt := txData.EffectiveFee(baseFee)
if feeAmt.Sign() == 0 {
// zero fee, no need to deduct
return sdk.Coins{}, nil
return sdk.Coins{{Denom: denom, Amount: sdkmath.NewIntFromBigInt(feeAmt)}}, nil
// CheckSenderBalance validates that the tx cost value is positive and that the
// sender has enough funds to pay for the fees and value of the transaction.
func CheckSenderBalance(
balance sdkmath.Int,
cost := txData.Cost()
if cost.Sign() < 0 {
return errorsmod.Wrapf(
errortypes.ErrInvalidCoins,
"tx cost (%s) is negative and invalid", cost,
if balance.IsNegative() || balance.BigInt().Cmp(cost) < 0 {
errortypes.ErrInsufficientFunds,
"sender balance < tx cost (%s < %s)", balance, txData.Cost(),