forked from evmos/ethermint
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathfee_checker.go
159 lines (131 loc) · 5.49 KB
/
fee_checker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2021 Evmos Foundation
// This file is part of Evmos' Ethermint library.
//
// The Ethermint library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Ethermint library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE
package ante
import (
"fmt"
"math"
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"
ethermint "github.com/evmos/ethermint/types"
"github.com/evmos/ethermint/x/evm/types"
)
// NewDynamicFeeChecker returns a `TxFeeChecker` that applies a dynamic fee to
// Cosmos txs using the EIP-1559 fee market logic.
// This can be called in both CheckTx and deliverTx modes.
// a) feeCap = tx.fees / tx.gas
// b) tipFeeCap = tx.MaxPriorityPrice (default) or MaxInt64
// - when `ExtensionOptionDynamicFeeTx` is omitted, `tipFeeCap` defaults to `MaxInt64`.
// - when london hardfork is not enabled, it fallbacks to SDK default behavior (validator min-gas-prices).
// - Tx priority is set to `effectiveGasPrice / DefaultPriorityReduction`.
func NewDynamicFeeChecker(k DynamicFeeEVMKeeper) authante.TxFeeChecker {
return func(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return nil, 0, fmt.Errorf("tx must be a FeeTx")
}
if ctx.BlockHeight() == 0 {
// genesis transactions: fallback to min-gas-price logic
return checkTxFeeWithValidatorMinGasPrices(ctx, feeTx)
}
params := k.GetParams(ctx)
denom := params.EvmDenom
ethCfg := params.ChainConfig.EthereumConfig(k.ChainID())
baseFee := k.GetBaseFee(ctx, ethCfg)
if baseFee == nil {
// london hardfork is not enabled: fallback to min-gas-prices logic
return checkTxFeeWithValidatorMinGasPrices(ctx, feeTx)
}
// default to `MaxInt64` when there's no extension option.
maxPriorityPrice := sdkmath.NewInt(math.MaxInt64)
// get the priority tip cap from the extension option.
if hasExtOptsTx, ok := tx.(authante.HasExtensionOptionsTx); ok {
for _, opt := range hasExtOptsTx.GetExtensionOptions() {
if extOpt, ok := opt.GetCachedValue().(*ethermint.ExtensionOptionDynamicFeeTx); ok {
maxPriorityPrice = extOpt.MaxPriorityPrice
break
}
}
}
gas := feeTx.GetGas()
feeCoins := feeTx.GetFee()
fee := feeCoins.AmountOfNoDenomValidation(denom)
feeCap := fee.Quo(sdkmath.NewIntFromUint64(gas))
baseFeeInt := sdkmath.NewIntFromBigInt(baseFee)
if feeCap.LT(baseFeeInt) {
return nil, 0, errorsmod.Wrapf(errortypes.ErrInsufficientFee, "insufficient gas prices; got: %s required: %s", feeCap, baseFeeInt)
}
// calculate the effective gas price using the EIP-1559 logic.
effectivePrice := sdkmath.NewIntFromBigInt(types.EffectiveGasPrice(baseFeeInt.BigInt(), feeCap.BigInt(), maxPriorityPrice.BigInt()))
// NOTE: create a new coins slice without having to validate the denom
effectiveFee := sdk.Coins{
{
Denom: denom,
Amount: effectivePrice.Mul(sdkmath.NewIntFromUint64(gas)),
},
}
bigPriority := effectivePrice.Sub(baseFeeInt).Quo(types.DefaultPriorityReduction)
priority := int64(math.MaxInt64)
if bigPriority.IsInt64() {
priority = bigPriority.Int64()
}
return effectiveFee, priority, nil
}
}
// checkTxFeeWithValidatorMinGasPrices implements the default fee logic, where the minimum price per
// unit of gas is fixed and set by each validator, and the tx priority is computed from the gas price.
func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.FeeTx) (sdk.Coins, int64, error) {
feeCoins := tx.GetFee()
gas := tx.GetGas()
minGasPrices := ctx.MinGasPrices()
// Ensure that the provided fees meet a minimum threshold for the validator,
// if this is a CheckTx. This is only for local mempool purposes, and thus
// is only ran on check tx.
if ctx.IsCheckTx() && !minGasPrices.IsZero() {
requiredFees := make(sdk.Coins, len(minGasPrices))
// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
glDec := sdkmath.LegacyNewDec(int64(gas))
for i, gp := range minGasPrices {
fee := gp.Amount.Mul(glDec)
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
}
if !feeCoins.IsAnyGTE(requiredFees) {
return nil, 0, errorsmod.Wrapf(errortypes.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
}
}
priority := getTxPriority(feeCoins, int64(gas))
return feeCoins, priority, nil
}
// getTxPriority returns a naive tx priority based on the amount of the smallest denomination of the gas price
// provided in a transaction.
func getTxPriority(fees sdk.Coins, gas int64) int64 {
var priority int64
for _, fee := range fees {
gasPrice := fee.Amount.QuoRaw(gas)
amt := gasPrice.Quo(types.DefaultPriorityReduction)
p := int64(math.MaxInt64)
if amt.IsInt64() {
p = amt.Int64()
}
if priority == 0 || p < priority {
priority = p
}
}
return priority
}