2022-05-31 16:28:46 +00:00
package ante
import (
2022-06-06 08:15:10 +00:00
"math/big"
2022-11-25 08:24:55 +00:00
errorsmod "cosmossdk.io/errors"
2022-05-31 16:28:46 +00:00
sdk "github.com/cosmos/cosmos-sdk/types"
2022-11-25 08:24:55 +00:00
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
2022-05-31 16:28:46 +00:00
ethtypes "github.com/ethereum/go-ethereum/core/types"
2022-06-19 09:43:41 +00:00
evmtypes "github.com/evmos/ethermint/x/evm/types"
2022-05-31 16:28:46 +00:00
)
// MinGasPriceDecorator will check if the transaction's fee is at least as large
// as the MinGasPrices param. If fee is too low, decorator returns error and tx
// is rejected. This applies for both CheckTx and DeliverTx
// If fee is high enough, then call next AnteHandler
// CONTRACT: Tx must implement FeeTx to use MinGasPriceDecorator
type MinGasPriceDecorator struct {
feesKeeper FeeMarketKeeper
evmKeeper EVMKeeper
}
2022-11-25 08:24:55 +00:00
// EthMinGasPriceDecorator will check if the transaction's fee is at least as large
// as the MinGasPrices param. If fee is too low, decorator returns error and tx
// is rejected. This applies to both CheckTx and DeliverTx and regardless
// if London hard fork or fee market params (EIP-1559) are enabled.
// If fee is high enough, then call next AnteHandler
type EthMinGasPriceDecorator struct {
feesKeeper FeeMarketKeeper
evmKeeper EVMKeeper
}
// EthMempoolFeeDecorator will check if the transaction's effective fee is at least as large
// as the local validator's minimum gasFee (defined in validator config).
// If fee is too low, decorator returns error and tx is rejected from mempool.
// Note this only applies when ctx.CheckTx = true
// If fee is high enough or not CheckTx, then call next AnteHandler
// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator
type EthMempoolFeeDecorator struct {
evmKeeper EVMKeeper
}
// NewMinGasPriceDecorator creates a new MinGasPriceDecorator instance used only for
// Cosmos transactions.
2022-05-31 16:28:46 +00:00
func NewMinGasPriceDecorator ( fk FeeMarketKeeper , ek EVMKeeper ) MinGasPriceDecorator {
return MinGasPriceDecorator { feesKeeper : fk , evmKeeper : ek }
}
2022-11-25 08:24:55 +00:00
// NewEthMinGasPriceDecorator creates a new MinGasPriceDecorator instance used only for
// Ethereum transactions.
func NewEthMinGasPriceDecorator ( fk FeeMarketKeeper , ek EVMKeeper ) EthMinGasPriceDecorator {
return EthMinGasPriceDecorator { feesKeeper : fk , evmKeeper : ek }
}
// NewEthMempoolFeeDecorator creates a new NewEthMempoolFeeDecorator instance used only for
// Ethereum transactions.
func NewEthMempoolFeeDecorator ( ek EVMKeeper ) EthMempoolFeeDecorator {
return EthMempoolFeeDecorator {
evmKeeper : ek ,
}
}
2022-05-31 16:28:46 +00:00
func ( mpd MinGasPriceDecorator ) AnteHandle ( ctx sdk . Context , tx sdk . Tx , simulate bool , next sdk . AnteHandler ) ( newCtx sdk . Context , err error ) {
feeTx , ok := tx . ( sdk . FeeTx )
if ! ok {
2022-11-25 08:24:55 +00:00
return ctx , errorsmod . Wrapf ( errortypes . ErrInvalidType , "invalid transaction type %T, expected sdk.FeeTx" , tx )
2022-05-31 16:28:46 +00:00
}
2022-06-06 08:15:10 +00:00
minGasPrice := mpd . feesKeeper . GetParams ( ctx ) . MinGasPrice
2022-10-05 23:25:23 +00:00
// Short-circuit if min gas price is 0 or if simulating
if minGasPrice . IsZero ( ) || simulate {
2022-06-06 08:15:10 +00:00
return next ( ctx , tx , simulate )
}
2022-10-19 16:21:59 +00:00
evmDenom := mpd . evmKeeper . GetEVMDenom ( ctx )
2022-06-06 08:15:10 +00:00
minGasPrices := sdk . DecCoins {
{
2022-10-19 16:21:59 +00:00
Denom : evmDenom ,
2022-06-06 08:15:10 +00:00
Amount : minGasPrice ,
} ,
}
2022-05-31 16:28:46 +00:00
feeCoins := feeTx . GetFee ( )
gas := feeTx . GetGas ( )
2022-06-06 08:15:10 +00:00
requiredFees := make ( sdk . Coins , 0 )
2022-05-31 16:28:46 +00:00
2022-06-06 08:15:10 +00:00
// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
gasLimit := sdk . NewDecFromBigInt ( new ( big . Int ) . SetUint64 ( gas ) )
2022-05-31 16:28:46 +00:00
2022-06-06 08:15:10 +00:00
for _ , gp := range minGasPrices {
fee := gp . Amount . Mul ( gasLimit ) . Ceil ( ) . RoundInt ( )
if fee . IsPositive ( ) {
requiredFees = requiredFees . Add ( sdk . Coin { Denom : gp . Denom , Amount : fee } )
2022-05-31 16:28:46 +00:00
}
}
2022-06-06 08:15:10 +00:00
if ! feeCoins . IsAnyGTE ( requiredFees ) {
2022-11-25 08:24:55 +00:00
return ctx , errorsmod . Wrapf ( errortypes . ErrInsufficientFee ,
2022-08-26 10:30:55 +00:00
"provided fee < minimum global fee (%s < %s). Please increase the gas price." ,
feeCoins ,
requiredFees )
2022-06-06 08:15:10 +00:00
}
2022-05-31 16:28:46 +00:00
return next ( ctx , tx , simulate )
}
2022-11-25 08:24:55 +00:00
// AnteHandle ensures that the that the effective fee from the transaction is greater than the
// minimum global fee, which is defined by the MinGasPrice (parameter) * GasLimit (tx argument).
2022-05-31 16:28:46 +00:00
func ( empd EthMinGasPriceDecorator ) AnteHandle ( ctx sdk . Context , tx sdk . Tx , simulate bool , next sdk . AnteHandler ) ( newCtx sdk . Context , err error ) {
minGasPrice := empd . feesKeeper . GetParams ( ctx ) . MinGasPrice
2022-06-06 08:15:10 +00:00
// short-circuit if min gas price is 0
if minGasPrice . IsZero ( ) {
return next ( ctx , tx , simulate )
}
2022-10-19 16:21:59 +00:00
chainCfg := empd . evmKeeper . GetChainConfig ( ctx )
ethCfg := chainCfg . EthereumConfig ( empd . evmKeeper . ChainID ( ) )
2022-06-06 08:15:10 +00:00
baseFee := empd . evmKeeper . GetBaseFee ( ctx , ethCfg )
for _ , msg := range tx . GetMsgs ( ) {
ethMsg , ok := msg . ( * evmtypes . MsgEthereumTx )
if ! ok {
2022-11-25 08:24:55 +00:00
return ctx , errorsmod . Wrapf (
errortypes . ErrUnknownRequest ,
2022-06-06 08:15:10 +00:00
"invalid message type %T, expected %T" ,
msg , ( * evmtypes . MsgEthereumTx ) ( nil ) ,
)
}
feeAmt := ethMsg . GetFee ( )
// For dynamic transactions, GetFee() uses the GasFeeCap value, which
// is the maximum gas price that the signer can pay. In practice, the
// signer can pay less, if the block's BaseFee is lower. So, in this case,
// we use the EffectiveFee. If the feemarket formula results in a BaseFee
// that lowers EffectivePrice until it is < MinGasPrices, the users must
// increase the GasTipCap (priority fee) until EffectivePrice > MinGasPrices.
// Transactions with MinGasPrices * gasUsed < tx fees < EffectiveFee are rejected
// by the feemarket AnteHandle
txData , err := evmtypes . UnpackTxData ( ethMsg . Data )
if err != nil {
2022-11-25 08:24:55 +00:00
return ctx , errorsmod . Wrapf ( err , "failed to unpack tx data %s" , ethMsg . Hash )
2022-06-06 08:15:10 +00:00
}
if txData . TxType ( ) != ethtypes . LegacyTxType {
feeAmt = ethMsg . GetEffectiveFee ( baseFee )
}
gasLimit := sdk . NewDecFromBigInt ( new ( big . Int ) . SetUint64 ( ethMsg . GetGas ( ) ) )
2022-06-22 10:57:16 +00:00
2022-06-06 08:15:10 +00:00
requiredFee := minGasPrice . Mul ( gasLimit )
fee := sdk . NewDecFromBigInt ( feeAmt )
if fee . LT ( requiredFee ) {
2022-11-25 08:24:55 +00:00
return ctx , errorsmod . Wrapf (
errortypes . ErrInsufficientFee ,
2022-08-26 10:30:55 +00:00
"provided fee < minimum global fee (%d < %d). Please increase the priority tip (for EIP-1559 txs) or the gas prices (for access list or legacy txs)" , //nolint:lll
2022-06-22 10:57:16 +00:00
fee . TruncateInt ( ) . Int64 ( ) , requiredFee . TruncateInt ( ) . Int64 ( ) ,
2022-06-06 08:15:10 +00:00
)
2022-05-31 16:28:46 +00:00
}
}
return next ( ctx , tx , simulate )
}
2022-11-25 08:24:55 +00:00
// AnteHandle ensures that the provided fees meet a minimum threshold for the validator.
// This check only for local mempool purposes, and thus it is only run on (Re)CheckTx.
// The logic is also skipped if the London hard fork and EIP-1559 are enabled.
func ( mfd EthMempoolFeeDecorator ) AnteHandle ( ctx sdk . Context , tx sdk . Tx , simulate bool , next sdk . AnteHandler ) ( newCtx sdk . Context , err error ) {
if ! ctx . IsCheckTx ( ) || simulate {
return next ( ctx , tx , simulate )
}
chainCfg := mfd . evmKeeper . GetChainConfig ( ctx )
ethCfg := chainCfg . EthereumConfig ( mfd . evmKeeper . ChainID ( ) )
baseFee := mfd . evmKeeper . GetBaseFee ( ctx , ethCfg )
// skip check as the London hard fork and EIP-1559 are enabled
if baseFee != nil {
return next ( ctx , tx , simulate )
}
evmDenom := mfd . evmKeeper . GetEVMDenom ( ctx )
minGasPrice := ctx . MinGasPrices ( ) . AmountOf ( evmDenom )
for _ , msg := range tx . GetMsgs ( ) {
ethMsg , ok := msg . ( * evmtypes . MsgEthereumTx )
if ! ok {
return ctx , errorsmod . Wrapf ( errortypes . ErrUnknownRequest , "invalid message type %T, expected %T" , msg , ( * evmtypes . MsgEthereumTx ) ( nil ) )
}
fee := sdk . NewDecFromBigInt ( ethMsg . GetFee ( ) )
gasLimit := sdk . NewDecFromBigInt ( new ( big . Int ) . SetUint64 ( ethMsg . GetGas ( ) ) )
requiredFee := minGasPrice . Mul ( gasLimit )
if fee . LT ( requiredFee ) {
return ctx , errorsmod . Wrapf (
errortypes . ErrInsufficientFee ,
"insufficient fee; got: %s required: %s" ,
fee , requiredFee ,
)
}
}
return next ( ctx , tx , simulate )
}