Fee Market 模块(x/feemarket)实现了 EIP-1559 动态手续费定价,可根据区块利用率调整基础费。与静态 gas 定价相比,这能提供更好的手续费可预测性和网络拥堵管理。

模块概览

用途:为 EVM 交易实现 EIP-1559 动态手续费市场机制 核心功能:
  • 基于区块 gas 使用量动态计算基础费
  • 支持按 gas 计的 EIP-1559 基础费自动调整
  • 支持用于交易排序的优先费(tip)
  • 强制执行最小 gas 价格
  • 可配置区块 gas 限制的弹性
  • 手续费销毁机制(基础费会被销毁)
源代码:x/feemarket 参数默认值:x/feemarket/types/params.go

EIP-1559 概览

EIP-1559 引入了动态手续费结构: 交易手续费组成:
  • 基础费:按算法确定的每单位 gas 手续费(会被销毁)
  • 优先费(Tip):支付给验证者的可选附加手续费
  • 最高费:用户愿意支付的每单位 gas 最大手续费
手续费计算:
effective_gas_price = base_fee + priority_fee
total_fee = gas_used * effective_gas_price
基础费调整:
  • 区块 > 目标 gas:基础费上升(默认设置下每个区块最多上升 12.5%)
  • 区块 < 目标 gas:基础费下降(每个区块最多下降 12.5%)
  • 区块 = 目标 gas:基础费保持不变
优势:
  • 用户手续费更可预测(交易前即可看到基础费)
  • 自动响应拥堵(繁忙时手续费上升,空闲时下降)
  • 降低 MEV(基础费不能被验证者截取)
  • 更好的 UX(钱包可展示可靠的手续费预估)

配置方式

Fee Market 模块在链启动前通过 genesis.json 配置。手续费市场参数提供默认值,通常无需在 local_node.sh 中修改,除非是在测试特定场景。

方法 1:直接编辑 JSON

直接编辑 ~/.evmd/config/genesis.json:
{
  "app_state": {
    "feemarket": {
      "params": {
        "no_base_fee": false,
        "base_fee": "1000000000",
        "base_fee_change_denominator": 8,
        "elasticity_multiplier": 2,
        "enable_height": 0,
        "min_gas_price": "0",
        "min_gas_multiplier": "0.5"
      }
    }
  }
}

方法 2:使用 jq 命令行工具

使用 jq 以编程方式修改 genesis:
GENESIS="$HOME/.evmd/config/genesis.json"
TMP_GENESIS="$HOME/.evmd/config/tmp_genesis.json"


# Disable EIP-1559 (use fixed gas pricing)
jq '.app_state["feemarket"]["params"]["no_base_fee"]=true' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"


# Set initial base fee (1 gwei)
jq '.app_state["feemarket"]["params"]["base_fee"]="1000000000"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"


# Adjust base fee change speed (slower adjustment)
jq '.app_state["feemarket"]["params"]["base_fee_change_denominator"]=50' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"


# Set minimum gas price floor (0.5 gwei)
jq '.app_state["feemarket"]["params"]["min_gas_price"]="500000000"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"

方法 3:运行时配置

在节点启动时,也可以通过 app.toml 影响手续费市场行为:

# In app.toml - sets node-level minimum (separate from consensus param)
minimum-gas-prices = "1000000000atest"  # 1 gwei minimum
注意:app.toml 中的 minimum-gas-prices 是节点级过滤器。genesis 参数属于共识级参数,会在全链范围内生效。

参数

no_base_fee

作用:完全禁用 EIP-1559 动态基础费机制。为 true 时,链只使用 min_gas_price 作为固定的最小手续费。 类型:bool 有效值:
  • false - 启用 EIP-1559 动态基础费(推荐,与 Ethereum 兼容)
  • true - 禁用 EIP-1559,只使用固定 min_gas_price(London 之前的行为)
默认值:false(params.go:21) 配置:

# Using jq - Disable EIP-1559
jq '.app_state["feemarket"]["params"]["no_base_fee"]=true' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "no_base_fee": true
    }
  }
}
影响: 当为 false(启用 EIP-1559)时:
  • 基础费会根据区块利用率动态调整
  • 用户的手续费可预测性更好
  • 自动管理网络拥堵
  • 基础费会被销毁(带来通缩压力)
  • 标准 Ethereum 行为
当为 true(禁用 EIP-1559)时:
  • 使用简单的固定最小 gas 价格
  • 对部分用户而言更容易理解
  • 不会根据拥堵情况动态调整
  • 所有手续费都支付给验证者(不会销毁)
  • London 之前的 Ethereum 行为
使用场景:
  • false - 推荐用于生产链,保持 Ethereum 兼容性
  • true - 适用于私有链或特定场景的简化手续费模型
兼容性说明:设置为 true 会破坏与 Ethereum EIP-1559 交易的兼容性。现代钱包通常预期使用动态手续费。

base_fee

作用:设置创世时每单位 gas 的初始基础费(单位为 wei)。这是 EIP-1559 基础费算法的起点。 类型:string(表示 wei 的十进制数字) 有效值:任意非负的 wei 十进制值 默认值:"1000000000"(1 gwei,params.go:13) 配置:

# Using jq - Set to 10 gwei
jq '.app_state["feemarket"]["params"]["base_fee"]="10000000000"' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "base_fee": "10000000000"
    }
  }
}
常见取值:
  • "100000000"(0.1 gwei)- 对测试网或 L2 而言非常低
  • "1000000000"(1 gwei)- 默认值,适合作为良好的起点
  • "10000000000"(10 gwei)- 适用于预期高需求场景的较高初始手续费
  • "100000000000"(100 gwei)- 适用于已知有需求的主网上线时的很高手续费
影响:
  • 决定链启动时的初始交易成本
  • 创世后会根据区块利用率自动上下调整
  • 应与预期网络使用情况和代币经济模型保持一致
  • 过低:可能导致启动时拥堵
  • 过高:可能抑制早期采用
建议:
  • 测试网:0.1-1 gwei(便于低成本测试)
  • L2/特定应用链:0.1-1 gwei(优化低手续费)
  • 通用型 L1:1-10 gwei(较为均衡)
  • 高需求启动:10-100 gwei(防止垃圾交易)
校验:必须为非负值(params.go:66-68) 相关:可与 enable_height 配合使用,以延迟 EIP-1559 激活

base_fee_change_denominator

作用:控制每个区块基础费变化的速度。它是基础费变化计算中的分母。 类型:uint32 有效值:任意正整数(不能为 0) 默认值:8(标准 EIP-1559,来自 go-ethereum 参数,params.go:51) 配置:

# Using jq - Slower adjustment (±2% per block)
jq '.app_state["feemarket"]["params"]["base_fee_change_denominator"]=50' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "base_fee_change_denominator": 8
    }
  }
}
工作原理: 每个区块允许的基础费最大变化量计算如下:
max_change_per_block = base_fee / base_fee_change_denominator
常见取值:
值每区块最大变化说明
4±25%调整很快(约 3 个区块变化 2 倍)
8±12.5%标准 EIP-1559(约 6 个区块变化 2 倍)
16±6.25%较慢(约 12 个区块变化 2 倍)
50±2%很慢(约 35 个区块变化 2 倍)
100±1%极慢(约 70 个区块变化 2 倍)
影响: 较小的分母(调整更快):
  • 优点:能快速响应突发流量高峰,攻击面更难预测
  • 缺点:手续费波动更大,成本更难预测,可能快速上涨
较大的分母(调整更慢):
  • 优点:手续费更稳定,可预测性更好,用户体验更平滑
  • 缺点:对拥堵的响应更慢,达到均衡所需时间更长
建议:
  • 标准链:8(与 Ethereum 主网一致)
  • 高吞吐链:4-8(更快响应负载变化)
  • 稳定手续费链:16-50(优先保证可预测性)
  • 私有网络:100+(尽量减少手续费波动)
示例场景: 当 denominator=8,且基础费起始为 10 gwei 时:
  • 区块 100% 满载:下一块基础费 = 11.25 gwei(+12.5%)
  • 区块 50% 满载(目标值):下一块基础费 = 10 gwei(不变)
  • 区块 0% 满载:下一块基础费 = 8.75 gwei(-12.5%)
当 denominator=50,相同条件下:
  • 区块 100% 满载:下一块基础费 = 10.2 gwei(+2%)
  • 区块 0% 满载:下一块基础费 = 9.8 gwei(-2%)
校验:不能为 0(params.go:62-64) 相关:与 elasticity_multiplier 一起决定目标 gas 使用量

elasticity_multiplier

作用:将最大区块 gas 限制定义为目标 gas 的倍数。允许区块在高需求期间暂时超过目标值。 类型:uint32 有效值:任意正整数(不能为 0) 默认值:2(标准 EIP-1559,来自 go-ethereum 参数,params.go:52) 配置:

# Using jq - More elastic (3x target)
jq '.app_state["feemarket"]["params"]["elasticity_multiplier"]=3' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "elasticity_multiplier": 2
    }
  }
}
工作原理:
target_gas = block_gas_limit / elasticity_multiplier
max_block_gas = target_gas * elasticity_multiplier
当 elasticity_multiplier = 2 时:
  • 如果区块 gas 限制为 30M gas
  • 每个区块的目标 gas = 15M gas
  • 每个区块的最大 gas = 30M gas(目标值的 2 倍)
基础费调整逻辑:
  • 区块 gas 使用量 > 目标 gas → 基础费上升
  • 区块 gas 使用量 = 目标 gas → 基础费保持不变
  • 区块 gas 使用量 < 目标 gas → 基础费下降
常见取值:
值说明使用场景
1无弹性(硬上限 = 目标值)严格控制吞吐量
2标准 EIP-1559(2 倍突发容量)平衡灵活性(Ethereum 主网)
3更高弹性(3 倍突发容量)负载波动较大的场景
4+很高弹性极强的突发容忍度
影响: 较低的倍数(1-2):
  • 优点:区块大小更可预测,更容易规划资源
  • 缺点:突发容量较小,流量高峰时可能拒绝交易
较高的倍数(3-4+):
  • 优点:更能应对流量高峰,减少交易被拒绝的情况
  • 缺点:区块大小波动更大,需要更高的验证者配置,区块时间更长
multiplier=2 示例:
  • 区块 gas 限制:30M gas
  • 目标值:15M gas
  • 如果区块使用 20M gas(高于目标值):基础费上升
  • 如果区块使用 10M gas(低于目标值):基础费下降
  • 如果区块使用 15M gas(等于目标值):基础费不变
建议:
  • 标准链:2(与 Ethereum 主网一致)
  • 高吞吐链:2-3(处理可变负载)
  • 资源受限链:1-2(需求更可预测)
  • 突发型链:3-4(容纳流量峰值)
校验:不能为 0(params.go:74-76) 相关:会与共识参数 block.max_gas 交互,后者定义了绝对最大值

enable_height

作用:EIP-1559 基础费机制开始生效的区块高度。适用于主网协调升级,在链启动后启用 EIP-1559。 类型:int64 有效值:任意非负整数 默认值:0(从 genesis 开始启用,params.go:19) 配置:

# Using jq - Enable EIP-1559 at block 100000
jq '.app_state["feemarket"]["params"]["enable_height"]=100000' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "enable_height": 0
    }
  }
}
常见取值:
  • 0 - 从 genesis 开始启用(推荐用于新链)
  • 100000 - 在未来指定区块高度启用
  • -1 或非常大的数字 - 实际上等同于禁用(应改用 no_base_fee)
影响: 在 enable_height 之前:
  • 基础费机制未激活
  • 回退为 min_gas_price 行为
  • 交易使用 EIP-1559 之前的 gas 定价方式
在 enable_height 及之后:
  • 基础费机制激活
  • 开始进行动态费用调整
  • 交易必须使用 EIP-1559 费用结构
使用场景: 新链(enable_height=0):
{
  "enable_height": 0,
  "base_fee": "1000000000"
}
  • 从区块 1 起启用 EIP-1559
  • 推荐用于新链
协调升级(enable_height=100000):
{
  "enable_height": 100000,
  "base_fee": "5000000000"
}
  • 链启动时使用固定费用
  • 在区块 100000 切换为动态费用
  • 允许在启用前先进行测试
  • 所有验证者都必须在区块 100000 前准备就绪
建议:
  • 新链:设置为 0(从一开始启用)
  • 现有链:通过治理 + 硬分叉修改(上线后不能再通过 enable_height 实现)
  • 测试:使用特定区块高度测试 fee market 行为
校验:不能为负数(params.go:70-72) 重要:该参数会在每个区块进行检查。在 genesis 中设置它,会影响模块从何时开始计算基础费。

min_gas_price

作用:以原生代币精度设置全局最小 gas price 下限。即使动态基础费下降得更低,也会提供一个绝对最小费用。 类型:string(十进制数值) 有效值:任意非负十进制数值 默认值:"0"(禁用,params.go:17) 配置:

# Using jq - Set 0.5 gwei minimum
jq '.app_state["feemarket"]["params"]["min_gas_price"]="500000000"' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "min_gas_price": "500000000"
    }
  }
}
常见取值:
  • "0" - 不设置最低下限(仅依赖 base fee,默认值)
  • "1000000000" - 1 gwei 最低下限
  • "10000000000" - 10 gwei 最低下限
  • "100000000" - 0.1 gwei 最低下限,适用于低费链
工作原理:
effective_minimum = max(min_gas_price, base_fee * min_gas_multiplier)
if base_fee < effective_minimum:
    use effective_minimum
else:
    use base_fee
影响: 当 min_gas_price = 0(默认)时:
  • 在空闲时段,基础费可以降到任意低
  • 定价完全由市场驱动
  • 在空闲链上可能接近 0
  • 符合以太坊标准行为
当 min_gas_price > 0 时:
  • 防止费用低于下限
  • 在空闲时段防止垃圾交易
  • 即使使用率较低,也能为验证者提供固定收入
  • 如果设置过高,可能抑制使用
使用场景: 公链(min_gas_price=0):
{
  "min_gas_price": "0",
  "min_gas_multiplier": "0.5"
}
  • 纯 EIP-1559,不设置下限
  • 费用完全由供需决定
  • 推荐用于兼容以太坊的场景
防垃圾交易(min_gas_price=1000000000):
{
  "min_gas_price": "1000000000",
  "min_gas_multiplier": "0"
}
  • 1 gwei 绝对最小值
  • 防止费用竞争降到 0
  • 适合希望设置成本下限的链
低费 L2(min_gas_price=100000000):
{
  "min_gas_price": "100000000",
  "min_gas_multiplier": "0"
}
  • 0.1 gwei 最小值
  • 保证交易足够便宜
  • 适用于高吞吐链
建议:
  • 兼容以太坊的链:"0"(不设下限)
  • 侧重抗垃圾交易:"1000000000" - "10000000000"(1-10 gwei)
  • 低费链:"100000000"(0.1 gwei)
  • 私有链:使用更高取值以防止测试垃圾交易
校验:必须为非负数(params.go:89-98) 节点级最小值:请注意,节点也可以在 app.toml 中设置 minimum-gas-prices,它会在共识前于本地过滤交易。

min_gas_multiplier

作用:将 base fee 的某个比例作为有效最小 gas price。它会创建一个随 base fee 变化而缩放的动态最小值。 类型:string(介于 0 和 1 之间的十进制数值) 有效值:介于 0 和 1 之间的十进制数(含边界) 默认值:"0.5"(base fee 的 50%,params.go:15) 配置:

# Using jq - Set to 80% of base fee
jq '.app_state["feemarket"]["params"]["min_gas_multiplier"]="0.8"' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "min_gas_multiplier": "0.5"
    }
  }
}
工作原理:
effective_minimum = max(min_gas_price, base_fee * min_gas_multiplier)
如果 base fee 是 10 gwei,且 min_gas_multiplier 为 0.5:
  • 有效最小值 = 5 gwei
  • 低于 5 gwei 的交易会被拒绝
  • 即使下一个区块的 base fee 降到 8 gwei,最小值也会变为 4 gwei
常见取值:
取值含义使用场景
"0"不使用基于乘数的最小值仅依赖 min_gas_price
"0.5"base fee 的 50%(默认)平衡的保护
"0.8"base fee 的 80%更严格的最小值
"1.0"base fee 的 100%强制按精确 base fee 支付
影响: 较低乘数(0-0.5):
  • 允许交易使用相对 base fee 更低的 gas price
  • 在费用高峰期间对用户更宽松
  • 可能允许部分低费垃圾交易
  • 更利于采用
较高乘数(0.5-1.0):
  • 更严格的最小 gas price 要求
  • 更好的垃圾交易防护
  • 可能会让尝试支付略低于 base fee 的用户感到受限
  • 强制完整执行 EIP-1559 费用支付
示例: 默认值(min_gas_multiplier=0.5):
  • Base fee = 10 gwei → 最小值 = 5 gwei
  • Base fee = 100 gwei → 最小值 = 50 gwei
  • 会随拥堵程度缩放,始终至少接受 base fee 的 50%
严格模式(min_gas_multiplier=1.0):
  • Base fee = 10 gwei → 最小值 = 10 gwei
  • 强制按精确 base fee 支付
  • 当前 base fee 以下的交易一律不接受
宽松模式(min_gas_multiplier=0):
  • 最小值仅由 min_gas_price 决定
  • 如果 min_gas_price=0,则可以接受非常低的费用
  • 垃圾交易风险高
建议:
  • 标准链:"0.5"(默认值,平衡较好)
  • 高价值链:"0.8" - "1.0"(严格执行)
  • 易受垃圾交易影响的链:"0.8" - "1.0"(更好的保护)
  • 低费 L2:"0" - "0.3"(最大化可达性)
校验:必须介于 0 和 1 之间(含边界)(params.go:101-114) 联动关系:它会与 min_gas_price 一起生效。有效最小值取两者中的较大值。

完整配置示例

标准 EIP-1559 配置(推荐)

基于以太坊主网默认值:
{
  "feemarket": {
    "params": {
      "no_base_fee": false,
      "base_fee": "1000000000",
      "base_fee_change_denominator": 8,
      "elasticity_multiplier": 2,
      "enable_height": 0,
      "min_gas_price": "0",
      "min_gas_multiplier": "0.5"
    }
  }
}
使用 jq:
GENESIS="$HOME/.evmd/config/genesis.json"
TMP="$HOME/.evmd/config/tmp_genesis.json"

jq '.app_state["feemarket"]["params"]["no_base_fee"]=false' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["base_fee"]="1000000000"' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["base_fee_change_denominator"]=8' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["elasticity_multiplier"]=2' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["enable_height"]=0' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["min_gas_price"]="0"' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["min_gas_multiplier"]="0.5"' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"

低费 L2 配置

针对低交易成本优化:
{
  "feemarket": {
    "params": {
      "no_base_fee": false,
      "base_fee": "100000000",
      "base_fee_change_denominator": 16,
      "elasticity_multiplier": 3,
      "enable_height": 0,
      "min_gas_price": "100000000",
      "min_gas_multiplier": "0.3"
    }
  }
}

固定费用配置(禁用 EIP-1559)

简单的固定定价模型:
{
  "feemarket": {
    "params": {
      "no_base_fee": true,
      "base_fee": "0",
      "base_fee_change_denominator": 8,
      "elasticity_multiplier": 2,
      "enable_height": 0,
      "min_gas_price": "1000000000",
      "min_gas_multiplier": "0"
    }
  }
}

高稳定性配置

尽量降低费用波动:
{
  "feemarket": {
    "params": {
      "no_base_fee": false,
      "base_fee": "10000000000",
      "base_fee_change_denominator": 50,
      "elasticity_multiplier": 2,
      "enable_height": 0,
      "min_gas_price": "5000000000",
      "min_gas_multiplier": "0.8"
    }
  }
}

运行时配置(app.toml)

除了 genesis 参数外,各节点还可以在 app.toml 中设置最小 gas price:

# app.toml
[json-rpc]

# Node will reject transactions below this price
minimum-gas-prices = "1000000000atest"  # 1 gwei in atest token


# Minimum priority fee for mempool ordering
evm-timeout = "5s"
evm-max-tx-gas-wanted = 0
重要区别:
  • Genesis params(feemarket 模块):共识级,由所有验证者强制执行
  • app.toml params:节点级过滤器,不同节点可以不同
节点级 minimum-gas-prices 必须 >= 共识级最小值,否则该节点将无法产出有效区块。

监控与调整

查询当前 Base Fee


# Query current base fee
evmd query feemarket base-fee


# Query all fee market params
evmd query feemarket params

通过 JSON-RPC


# Get current base fee via eth_gasPrice
curl -X POST http://localhost:8545 -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'


# Get specific block's base fee
curl -X POST http://localhost:8545 -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":1}' \
  | jq '.result.baseFeePerGas'

治理更新

fee market 参数可以在上线后通过治理提案更新:

# Example: Update base_fee_change_denominator to 50 (slower adjustment)
evmd tx gov submit-proposal param-change proposal.json --from validator --chain-id mychain-1


# proposal.json
{
  "title": "Slow Down Base Fee Adjustment",
  "description": "Reduce base fee volatility by setting denominator to 50",
  "changes": [
    {
      "subspace": "feemarket",
      "key": "BaseFeeChangeDenominator",
      "value": "50"
    }
  ],
  "deposit": "10000000atest"
}

常见问题与解决方案

问题:交易因“Gas Price Too Low”失败

原因:交易 gas price < 生效的最低值(base_fee 或 min_gas_price) 解决方案:
  • 检查当前 base fee:evmd query feemarket base-fee
  • 确保钱包使用 EIP-1559 交易类型
  • 设置 maxFeePerGas >= 当前 base fee + 期望的优先费
  • 检查节点在 app.toml 中的 minimum-gas-prices

问题:Base Fee 波动过大

原因:base_fee_change_denominator 过低(调整过快) 解决方案:
  • 提高 base_fee_change_denominator(例如从 8 调整为 50)
  • 上线后修改需要通过治理提案
  • 如果可预测的手续费至关重要,请在 genesis 中提前规划

问题:网络拥堵没有得到响应

原因:base_fee_change_denominator 过高(调整过慢) 解决方案:
  • 降低 base_fee_change_denominator 以加快响应速度
  • 提高 elasticity_multiplier,以便在流量高峰时允许更大的区块
  • 上线后需要通过治理提案进行修改

问题:手续费始终不下降

原因:区块持续高于目标 gas 用量 解决方案:
  • 检查 elasticity_multiplier 是否适合当前流量模式
  • 考虑在共识参数中提高区块 gas 上限
  • 评估链吞吐量是否需要扩容

相关文档


源代码参考


The Fee Market module (x/feemarket) implements EIP-1559 dynamic fee pricing, enabling base fee adjustment based on block utilization. This provides better fee predictability and network congestion management compared to static gas pricing.

Module Overview

Purpose: Implement EIP-1559 dynamic fee market mechanism for EVM transactions Key Functionality:
  • Dynamic base fee calculation based on block gas usage
  • EIP-1559 base fee per gas with automatic adjustment
  • Priority fee (tip) support for transaction ordering
  • Minimum gas price enforcement
  • Configurable elasticity for block gas limits
  • Fee burning mechanism (base fee is burned)
Source Code: x/feemarket Parameter Defaults: x/feemarket/types/params.go

EIP-1559 Overview

EIP-1559 introduces a dynamic fee structure: Transaction Fee Components:
  • Base Fee: Algorithmically determined fee per gas unit (burned)
  • Priority Fee (Tip): Optional additional fee paid to validators
  • Max Fee: Maximum fee per gas the user is willing to pay
Fee Calculation:
effective_gas_price = base_fee + priority_fee
total_fee = gas_used * effective_gas_price
Base Fee Adjustment:
  • Block > target gas: Base fee increases (up to 12.5% per block with default settings)
  • Block < target gas: Base fee decreases (down to 12.5% per block)
  • Block = target gas: Base fee remains constant
Benefits:
  • Predictable fees for users (base fee visible before transaction)
  • Automatic congestion response (fees rise when busy, fall when quiet)
  • MEV reduction (base fee not capturable by validators)
  • Better UX (wallets can show reliable fee estimates)

Configuration Methods

The Fee Market module is configured through genesis.json before chain launch. Fee market parameters have defaults and typically don’t require modification in local_node.sh unless testing specific scenarios.

Method 1: Direct JSON Editing

Edit ~/.evmd/config/genesis.json directly:
{
  "app_state": {
    "feemarket": {
      "params": {
        "no_base_fee": false,
        "base_fee": "1000000000",
        "base_fee_change_denominator": 8,
        "elasticity_multiplier": 2,
        "enable_height": 0,
        "min_gas_price": "0",
        "min_gas_multiplier": "0.5"
      }
    }
  }
}

Method 2: Using jq Command-Line Tool

Programmatically modify genesis using jq:
GENESIS="$HOME/.evmd/config/genesis.json"
TMP_GENESIS="$HOME/.evmd/config/tmp_genesis.json"

# Disable EIP-1559 (use fixed gas pricing)
jq '.app_state["feemarket"]["params"]["no_base_fee"]=true' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"

# Set initial base fee (1 gwei)
jq '.app_state["feemarket"]["params"]["base_fee"]="1000000000"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"

# Adjust base fee change speed (slower adjustment)
jq '.app_state["feemarket"]["params"]["base_fee_change_denominator"]=50' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"

# Set minimum gas price floor (0.5 gwei)
jq '.app_state["feemarket"]["params"]["min_gas_price"]="500000000"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS"

Method 3: Runtime Configuration

Fee market behavior can also be influenced at node startup via app.toml:
# In app.toml - sets node-level minimum (separate from consensus param)
minimum-gas-prices = "1000000000atest"  # 1 gwei minimum
Note: The minimum-gas-prices in app.toml is a node-level filter. Genesis params are consensus-level and apply chain-wide.

Parameters

no_base_fee

What It Does: Completely disables the EIP-1559 dynamic base fee mechanism. When true, the chain uses only min_gas_price as a fixed minimum fee. Type: bool Valid Values:
  • false - Enable EIP-1559 dynamic base fee (recommended, Ethereum-compatible)
  • true - Disable EIP-1559, use fixed min_gas_price only (pre-London behavior)
Default: false (params.go:21) Configuration:
# Using jq - Disable EIP-1559
jq '.app_state["feemarket"]["params"]["no_base_fee"]=true' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "no_base_fee": true
    }
  }
}
Impact: When false (EIP-1559 enabled):
  • Base fee adjusts dynamically based on block utilization
  • Better fee predictability for users
  • Automatic congestion management
  • Base fee is burned (deflationary pressure)
  • Standard Ethereum behavior
When true (EIP-1559 disabled):
  • Simple fixed minimum gas price
  • Easier to understand for some users
  • No dynamic adjustment to congestion
  • All fees go to validators (no burning)
  • Pre-London Ethereum behavior
Use Cases:
  • false - Recommended for production chains, Ethereum compatibility
  • true - Simplified fee model for private chains or specific use cases
Compatibility Note: Setting to true breaks Ethereum EIP-1559 transaction compatibility. Modern wallets expect dynamic fees.

base_fee

What It Does: Sets the initial base fee per gas in wei at genesis. This is the starting point for EIP-1559 base fee algorithm. Type: string (decimal number representing wei) Valid Values: Any non-negative decimal value in wei Default: "1000000000" (1 gwei, params.go:13) Configuration:
# Using jq - Set to 10 gwei
jq '.app_state["feemarket"]["params"]["base_fee"]="10000000000"' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "base_fee": "10000000000"
    }
  }
}
Common Values:
  • "100000000" (0.1 gwei) - Very low for testnets or L2s
  • "1000000000" (1 gwei) - Default, good starting point
  • "10000000000" (10 gwei) - Higher initial fee for expected high demand
  • "100000000000" (100 gwei) - Very high for mainnet launch with known demand
Impact:
  • Sets initial transaction costs at chain launch
  • Adjusts up/down automatically after genesis based on block utilization
  • Should align with expected network usage and token economics
  • Too low: May cause congestion at launch
  • Too high: May deter early adoption
Recommendations:
  • Testnet: 0.1-1 gwei (low cost for testing)
  • L2/App-specific: 0.1-1 gwei (optimize for low fees)
  • General Purpose L1: 1-10 gwei (balanced)
  • High Demand Launch: 10-100 gwei (prevent spam)
Validation: Must be non-negative (params.go:66-68) Related: Works with enable_height to delay EIP-1559 activation

base_fee_change_denominator

What It Does: Controls how quickly the base fee can change per block. Acts as the denominator in the base fee change calculation. Type: uint32 Valid Values: Any positive integer (cannot be 0) Default: 8 (standard EIP-1559, from go-ethereum params, params.go:51) Configuration:
# Using jq - Slower adjustment (±2% per block)
jq '.app_state["feemarket"]["params"]["base_fee_change_denominator"]=50' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "base_fee_change_denominator": 8
    }
  }
}
How It Works: The maximum base fee change per block is calculated as:
max_change_per_block = base_fee / base_fee_change_denominator
Common Values:
ValueMax Change/BlockDescription
4±25%Fast adjustment (2x change in ~3 blocks)
8±12.5%Standard EIP-1559 (2x change in ~6 blocks)
16±6.25%Slower (2x change in ~12 blocks)
50±2%Very slow (2x change in ~35 blocks)
100±1%Extremely slow (2x change in ~70 blocks)
Impact: Lower Denominator (faster adjustment):
  • Pros: Responds quickly to sudden traffic spikes, less predictable attack surface
  • Cons: More volatile fees, harder to predict costs, potential for rapid increases
Higher Denominator (slower adjustment):
  • Pros: More stable fees, better predictability, smoother UX
  • Cons: Slower response to congestion, takes longer to reach equilibrium
Recommendations:
  • Standard chains: 8 (matches Ethereum mainnet)
  • High-throughput chains: 4-8 (faster response to varying load)
  • Stable fee chains: 16-50 (prioritize predictability)
  • Private networks: 100+ (minimal fee variation)
Example Scenarios: With denominator=8, starting from 10 gwei base fee:
  • Block 100% full: Next base fee = 11.25 gwei (+12.5%)
  • Block 50% full (target): Next base fee = 10 gwei (unchanged)
  • Block 0% full: Next base fee = 8.75 gwei (-12.5%)
With denominator=50, same conditions:
  • Block 100% full: Next base fee = 10.2 gwei (+2%)
  • Block 0% full: Next base fee = 9.8 gwei (-2%)
Validation: Cannot be 0 (params.go:62-64) Related: Works with elasticity_multiplier to determine target gas usage

elasticity_multiplier

What It Does: Defines the maximum block gas limit as a multiple of target gas. Allows blocks to temporarily exceed target during high demand. Type: uint32 Valid Values: Any positive integer (cannot be 0) Default: 2 (standard EIP-1559, from go-ethereum params, params.go:52) Configuration:
# Using jq - More elastic (3x target)
jq '.app_state["feemarket"]["params"]["elasticity_multiplier"]=3' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "elasticity_multiplier": 2
    }
  }
}
How It Works:
target_gas = block_gas_limit / elasticity_multiplier
max_block_gas = target_gas * elasticity_multiplier
With elasticity_multiplier = 2:
  • If block gas limit is 30M gas
  • Target gas per block = 15M gas
  • Maximum gas per block = 30M gas (2x target)
Base Fee Adjustment Logic:
  • Block gas used > target gas → Base fee increases
  • Block gas used = target gas → Base fee stays constant
  • Block gas used < target gas → Base fee decreases
Common Values:
ValueDescriptionUse Case
1No elasticity (hard limit = target)Strictly controlled throughput
2Standard EIP-1559 (2x burst capacity)Balanced flexibility (Ethereum mainnet)
3Higher elasticity (3x burst capacity)High-variance workloads
4+Very elasticExtreme burst tolerance
Impact: Lower Multiplier (1-2):
  • Pros: More predictable block sizes, easier to provision resources
  • Cons: Less burst capacity, may reject transactions during spikes
Higher Multiplier (3-4+):
  • Pros: Better handles traffic spikes, fewer transaction rejections
  • Cons: More variable block sizes, requires higher validator specs, longer block times
Example with multiplier=2:
  • Block gas limit: 30M gas
  • Target: 15M gas
  • If block uses 20M gas (above target): Base fee increases
  • If block uses 10M gas (below target): Base fee decreases
  • If block uses 15M gas (at target): Base fee unchanged
Recommendations:
  • Standard chains: 2 (matches Ethereum mainnet)
  • High-throughput chains: 2-3 (handle variable load)
  • Resource-constrained chains: 1-2 (predictable requirements)
  • Burst-heavy chains: 3-4 (accommodate spikes)
Validation: Cannot be 0 (params.go:74-76) Related: Interacts with consensus param block.max_gas which sets the absolute maximum

enable_height

What It Does: Block height at which EIP-1559 base fee mechanism activates. Useful for coordinated mainnet upgrades to enable EIP-1559 after launch. Type: int64 Valid Values: Any non-negative integer Default: 0 (enabled from genesis, params.go:19) Configuration:
# Using jq - Enable EIP-1559 at block 100000
jq '.app_state["feemarket"]["params"]["enable_height"]=100000' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "enable_height": 0
    }
  }
}
Common Values:
  • 0 - Enable from genesis (recommended for new chains)
  • 100000 - Enable at specific future block height
  • -1 or very large number - Effectively disabled (use no_base_fee instead)
Impact: Before enable_height:
  • Base fee mechanism is inactive
  • Falls back to min_gas_price behavior
  • Transactions use pre-EIP-1559 gas pricing
At and after enable_height:
  • Base fee mechanism activates
  • Dynamic fee adjustment begins
  • Transactions must use EIP-1559 fee structure
Use Cases: New Chain (enable_height=0):
{
  "enable_height": 0,
  "base_fee": "1000000000"
}
  • EIP-1559 active from block 1
  • Recommended for new chains
Coordinated Upgrade (enable_height=100000):
{
  "enable_height": 100000,
  "base_fee": "5000000000"
}
  • Chain launches with fixed fees
  • Switches to dynamic fees at block 100000
  • Allows testing before enabling
  • All validators must be ready by block 100000
Recommendations:
  • New chains: Set to 0 (enable from start)
  • Existing chains: Use governance + hard fork to change (cannot be done via enable_height after launch)
  • Testing: Use specific block height to test fee market behavior
Validation: Cannot be negative (params.go:70-72) Important: This parameter is checked at each block. Setting it in genesis affects when the module begins calculating base fees.

min_gas_price

What It Does: Sets a global minimum gas price floor in native token decimals. Provides an absolute minimum fee even if the dynamic base fee drops lower. Type: string (decimal value) Valid Values: Any non-negative decimal value Default: "0" (disabled, params.go:17) Configuration:
# Using jq - Set 0.5 gwei minimum
jq '.app_state["feemarket"]["params"]["min_gas_price"]="500000000"' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "min_gas_price": "500000000"
    }
  }
}
Common Values:
  • "0" - No minimum floor (rely on base fee only, default)
  • "1000000000" - 1 gwei minimum floor
  • "10000000000" - 10 gwei minimum floor
  • "100000000" - 0.1 gwei minimum for low-fee chains
How It Works:
effective_minimum = max(min_gas_price, base_fee * min_gas_multiplier)
if base_fee < effective_minimum:
    use effective_minimum
else:
    use base_fee
Impact: When min_gas_price = 0 (default):
  • Base fee can fall arbitrarily low during quiet periods
  • Purely market-driven pricing
  • May approach 0 on idle chains
  • Standard Ethereum behavior
When min_gas_price > 0:
  • Prevents fees from falling below floor
  • Protects against spam during quiet periods
  • Creates fixed revenue for validators even at low usage
  • May discourage usage if set too high
Use Cases: Public Chain (min_gas_price=0):
{
  "min_gas_price": "0",
  "min_gas_multiplier": "0.5"
}
  • Pure EIP-1559 with no floor
  • Fees determined entirely by supply/demand
  • Recommended for Ethereum compatibility
Spam Protection (min_gas_price=1000000000):
{
  "min_gas_price": "1000000000",
  "min_gas_multiplier": "0"
}
  • 1 gwei absolute minimum
  • Prevents fee races to zero
  • Good for chains that want cost floor
Low-Fee L2 (min_gas_price=100000000):
{
  "min_gas_price": "100000000",
  "min_gas_multiplier": "0"
}
  • 0.1 gwei minimum
  • Cheap transactions guaranteed
  • Suitable for high-throughput chains
Recommendations:
  • Ethereum-compatible chains: "0" (no floor)
  • Anti-spam focus: "1000000000" - "10000000000" (1-10 gwei)
  • Low-fee chains: "100000000" (0.1 gwei)
  • Private chains: Higher values to prevent test spam
Validation: Must be non-negative (params.go:89-98) Node-Level Minimum: Remember that nodes can also set minimum-gas-prices in app.toml, which filters transactions locally before consensus.

min_gas_multiplier

What It Does: Fraction of base fee to use as the effective minimum gas price. Creates a dynamic minimum that scales with base fee. Type: string (decimal value between 0 and 1) Valid Values: Decimal between 0 and 1 (inclusive) Default: "0.5" (50% of base fee, params.go:15) Configuration:
# Using jq - Set to 80% of base fee
jq '.app_state["feemarket"]["params"]["min_gas_multiplier"]="0.8"' genesis.json > tmp.json && mv tmp.json genesis.json
{
  "feemarket": {
    "params": {
      "min_gas_multiplier": "0.5"
    }
  }
}
How It Works:
effective_minimum = max(min_gas_price, base_fee * min_gas_multiplier)
If base fee is 10 gwei and min_gas_multiplier is 0.5:
  • Effective minimum = 5 gwei
  • Transactions below 5 gwei are rejected
  • Even if base fee drops to 8 gwei next block, minimum becomes 4 gwei
Common Values:
ValueMeaningUse Case
"0"No multiplier-based minimumRely only on min_gas_price
"0.5"50% of base fee (default)Balanced protection
"0.8"80% of base feeStricter minimum
"1.0"100% of base feeForce exact base fee payment
Impact: Lower Multiplier (0-0.5):
  • Allows transactions with lower gas prices relative to base fee
  • More forgiving for users during fee spikes
  • May allow some low-fee spam
  • Better for adoption
Higher Multiplier (0.5-1.0):
  • Stricter minimum gas price requirements
  • Better spam protection
  • May frustrate users who try to pay slightly below base fee
  • Forces full EIP-1559 fee payment
Examples: Default (min_gas_multiplier=0.5):
  • Base fee = 10 gwei → Minimum = 5 gwei
  • Base fee = 100 gwei → Minimum = 50 gwei
  • Scales with congestion, always accepts at least 50% of base fee
Strict (min_gas_multiplier=1.0):
  • Base fee = 10 gwei → Minimum = 10 gwei
  • Forces exact base fee payment
  • No transactions accepted below current base fee
Permissive (min_gas_multiplier=0):
  • Minimum determined only by min_gas_price
  • If min_gas_price=0, can accept very low fees
  • High spam risk
Recommendations:
  • Standard chains: "0.5" (default, good balance)
  • High-value chains: "0.8" - "1.0" (strict enforcement)
  • Spam-prone chains: "0.8" - "1.0" (better protection)
  • Low-fee L2s: "0" - "0.3" (maximize accessibility)
Validation: Must be between 0 and 1 inclusive (params.go:101-114) Interaction: Works together with min_gas_price. The effective minimum is the maximum of both values.

Complete Configuration Examples

Based on Ethereum mainnet defaults:
{
  "feemarket": {
    "params": {
      "no_base_fee": false,
      "base_fee": "1000000000",
      "base_fee_change_denominator": 8,
      "elasticity_multiplier": 2,
      "enable_height": 0,
      "min_gas_price": "0",
      "min_gas_multiplier": "0.5"
    }
  }
}
Using jq:
GENESIS="$HOME/.evmd/config/genesis.json"
TMP="$HOME/.evmd/config/tmp_genesis.json"

jq '.app_state["feemarket"]["params"]["no_base_fee"]=false' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["base_fee"]="1000000000"' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["base_fee_change_denominator"]=8' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["elasticity_multiplier"]=2' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["enable_height"]=0' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["min_gas_price"]="0"' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"
jq '.app_state["feemarket"]["params"]["min_gas_multiplier"]="0.5"' "$GENESIS" >"$TMP" && mv "$TMP" "$GENESIS"

Low-Fee L2 Configuration

Optimized for low transaction costs:
{
  "feemarket": {
    "params": {
      "no_base_fee": false,
      "base_fee": "100000000",
      "base_fee_change_denominator": 16,
      "elasticity_multiplier": 3,
      "enable_height": 0,
      "min_gas_price": "100000000",
      "min_gas_multiplier": "0.3"
    }
  }
}

Fixed Fee Configuration (Disabled EIP-1559)

Simple fixed pricing model:
{
  "feemarket": {
    "params": {
      "no_base_fee": true,
      "base_fee": "0",
      "base_fee_change_denominator": 8,
      "elasticity_multiplier": 2,
      "enable_height": 0,
      "min_gas_price": "1000000000",
      "min_gas_multiplier": "0"
    }
  }
}

High-Stability Configuration

Minimal fee volatility:
{
  "feemarket": {
    "params": {
      "no_base_fee": false,
      "base_fee": "10000000000",
      "base_fee_change_denominator": 50,
      "elasticity_multiplier": 2,
      "enable_height": 0,
      "min_gas_price": "5000000000",
      "min_gas_multiplier": "0.8"
    }
  }
}

Runtime Configuration (app.toml)

In addition to genesis parameters, individual nodes can set minimum gas prices in app.toml:
# app.toml
[json-rpc]
# Node will reject transactions below this price
minimum-gas-prices = "1000000000atest"  # 1 gwei in atest token

# Minimum priority fee for mempool ordering
evm-timeout = "5s"
evm-max-tx-gas-wanted = 0
Important Distinction:
  • Genesis params (feemarket module): Consensus-level, enforced by all validators
  • app.toml params: Node-level filter, can vary per node
Node-level minimum-gas-prices must be >= consensus-level minimum or the node will fail to produce valid blocks.

Monitoring and Adjusting

Query Current Base Fee

# Query current base fee
evmd query feemarket base-fee

# Query all fee market params
evmd query feemarket params

Via JSON-RPC

# Get current base fee via eth_gasPrice
curl -X POST http://localhost:8545 -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'

# Get specific block's base fee
curl -X POST http://localhost:8545 -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":1}' \
  | jq '.result.baseFeePerGas'

Governance Updates

Fee market parameters can be updated via governance proposals after launch:
# Example: Update base_fee_change_denominator to 50 (slower adjustment)
evmd tx gov submit-proposal param-change proposal.json --from validator --chain-id mychain-1

# proposal.json
{
  "title": "Slow Down Base Fee Adjustment",
  "description": "Reduce base fee volatility by setting denominator to 50",
  "changes": [
    {
      "subspace": "feemarket",
      "key": "BaseFeeChangeDenominator",
      "value": "50"
    }
  ],
  "deposit": "10000000atest"
}

Common Issues and Solutions

Issue: Transactions Fail with “Gas Price Too Low”

Cause: Transaction gas price < effective minimum (base_fee or min_gas_price) Solution:
  • Check current base fee: evmd query feemarket base-fee
  • Ensure wallet uses EIP-1559 transaction type
  • Set maxFeePerGas >= current base fee + desired priority fee
  • Check node’s minimum-gas-prices in app.toml

Issue: Base Fee Too Volatile

Cause: base_fee_change_denominator too low (fast adjustment) Solution:
  • Increase base_fee_change_denominator (e.g., from 8 to 50)
  • Requires governance proposal to change after launch
  • Plan for this in genesis if predictable fees are critical

Issue: Network Congestion Not Responding

Cause: base_fee_change_denominator too high (slow adjustment) Solution:
  • Decrease base_fee_change_denominator for faster response
  • Increase elasticity_multiplier to allow bigger blocks during spikes
  • Requires governance proposal after launch

Issue: Fees Never Decrease

Cause: Blocks consistently above target gas Solution:
  • Check if elasticity_multiplier is appropriate for traffic pattern
  • Consider increasing block gas limit in consensus params
  • Evaluate if chain throughput needs scaling


Source Code References