如何使用 Python 构建 EIP-1559 的 Gas Fee 估算器
2023-09-25 20:23
Tokenview
2023-09-25 20:23
订阅此专栏
收藏此文章

在 EIP-1559 之前,ETH 上的 gas fee 采用的是简单的竞拍模式,出价高者的交易优先被验证。这样的计算模式会导致 gas fee 因为人为因素(出价)而产生剧烈波动。EIP-1559 就是为了解决 gas fee 难以预测以及波动大的问题。


许多应用程序喜欢为用户提供设置自己的汽油费出价的选项,包括“慢速”、“平均”和“快速”选项。在本文中,我们将了解如何使用伦敦分叉后的 API 构建这些选项

01

Gas Fee 的计算公式

  • units of gas used * (base fee + priority fee)

其中:

    • units of gas used  交易花费的 gas 单位数量 ,标准的一次简单 ETH 转账交易需要 21000 单位的 gas;
    • base fee  基础 gas 费,单位是 gwei (1gwei = 0.000000001 ETH);
    • priority fee 优先费 或者说是给矿工的小费 和 base fee 一样,单位为 gwei。

02

Base Fee | 基础费

首先,base fee 是根据上一个区块的 base fee 而来的。由 eth 节点自动完成计算,本质上区别于之前的竞拍模式。粗略来说 上一个区块交易量大,则提高当前区块的 base fee,反之降低。这样的动态调整。也就是说这个 base fee 完全可以精准的计算出来。

EIP-1559 源代码

上述代码基于 golang,我们将他转化为一段简单的 python 代码:

from web3 import Web3
eth_json_rpc_endpoint = "https://services.tokenview.io/vipapi/nodeservice/eth?apikey=xxx"ElasticityMultiplier = 2 # EIP-1559 对区块大小进行了扩容,最大倍数是 2BaseFeeChangeDenominator = 8 # 基本费用在区块之间可以改变的金额
def calc_base_fee_of_next_block(parent_block): parent_gas_target = parent_block.gasLimit // ElasticityMultiplier print('parent_gas_target',parent_gas_target) print('parent_block.gasUsed',parent_block.gasUsed) print('parent_block.baseFeePerGas',parent_block.baseFeePerGas) if parent_block.gasUsed == parent_gas_target: # 如果父块的 gasUsed 与目标相同,则 baseFee 保持不变 return parent_block.baseFeePerGas if parent_block.gasUsed > parent_gas_target: # 如果父块使用的 gas 大于目标值,则 baseFee 应增加 gas_used_delta = parent_block.gasUsed - parent_gas_target x = parent_block.baseFeePerGas * gas_used_delta y = x // parent_gas_target base_fee_delta = max( y // BaseFeeChangeDenominator, 1 ) return parent_block.baseFeePerGas + base_fee_delta else: # 否则,如果父块使用的 gas 小于目标值,则 baseFee 应减少 gas_used_delta = parent_gas_target - parent_block.gasUsed x = parent_block.baseFeePerGas * gas_used_delta y = x // parent_gas_target base_fee_delta = y // BaseFeeChangeDenominator return max( parent_block.baseFeePerGas - base_fee_delta, 0 )
def main(): ethClient = Web3(Web3.HTTPProvider(eth_json_rpc_endpoint)) block_number = ethClient.eth.block_number block = ethClient.eth.get_block(block_number) #计算下个区块的 base fee base_fee_of_next_block = calc_base_fee_of_next_block(block) print(f"Base fee for block {block_number + 1} will be {base_fee_of_next_block}")

03

Priority Fee | 优先费

与基础费不同,优先费是人为设定的值。对于需要在同一区块中优先执行的交易,需要提供更高的小费。要预测优先费,得扫描过去一段时间的区块,看看其他人都在用多少的费用。

   我们将会用到这个 API:eth_feeHistory

from web3 import Web3
eth_json_rpc_endpoint = "https://services.tokenview.io/vipapi/nodeservice/eth?apikey=xxx"ethClient = Web3(Web3.HTTPProvider(eth_json_rpc_endpoint))print(ethClient.eth.fee_history(4,"pending", [25, 50, 75]))

上述调用的意思是,“向我提供从待处理区块开始并向后查看 4 个区块的费用历史信息。对于每个区块,还向我提供该区块中交易的优先费用的第 25、50 和 75 个百分点”。原始结果如下所示:

{  "oldestBlock": 17913327,    //4 个区块中 最老的块高  "reward": [                 //4 个区块 分别列出他们第 25%、50% 和 75% 的优先费 priorityFeePerGas    [39519672,100000000,2000000000],    [100000000,1000000000,3000000000],    [100000000,365064718,1000000000],    100000000,570000000,3000000000]  ],  "baseFeePerGas": [        // 基础费    21121728416,    21666906452,    20742307151,    19782866894,    17762883032  ],  "gasUsedRatio": [         // 区块的填充程度,判断交易量用的    0.6032449666666667,    0.3293066333333333,    0.31497906666666664,    0.09156903333333333  ]}

为了让我们的计算更加容易,让我们编写一段格式化方法,将上述结果按块进行分组:

def format_fee_history(result, include_pending):    block_num = result['oldestBlock']    index = 0    blocks = []    historical_blocks = len(result['reward'])    while block_num < result['oldestBlock'] + historical_blocks:       blocks.append({           'number': block_num,           'baseFeePerGas': float(result['baseFeePerGas'][index]),           'gasUsedRatio': float(result['gasUsedRatio'][index]),           'priorityFeePerGas': [float(x) for x in result['reward'][index]],       })       block_num += 1       index += 1    if include_pending:       blocks.append({           'number': 'pending',           'baseFeePerGas': float(result['baseFeePerGas'][historical_blocks]),           'gasUsedRatio': float('nan'),           'priorityFeePerGas': [],       })    return blocks

接下来我们可以得到这样格式的数据:

blocks = format_fee_history(ethClient.eth.fee_history(4,"latest", [25, 50, 75]),False)print(blocks)
->>[ { number: 17913335, baseFeePerGas: 315777006840, gasUsedRatio: 0.9922326809477219, priorityFeePerGas: [ 34222993160, 34222993160, 63222993160 ] }, { number: 17913336, baseFeePerGas: 354635947504, gasUsedRatio: 0.22772779167798343, priorityFeePerGas: [ 20000000000, 38044555767, 38364052496 ] }, { number: 17913337, baseFeePerGas: 330496570085, gasUsedRatio: 0.8876034775653597, priorityFeePerGas: [ 9503429915, 19503429915, 36503429915 ] }, { number: 17913338, baseFeePerGas: 362521975057, gasUsedRatio: 0.9909446241177369, priorityFeePerGas: [ 18478024943, 37478024943, 81478024943 ] }]

可读性大大提高!其中baseFeePerGas gasUsedRatio是用于计算 base fee 的。我们现在只关注priorityFeePerGas

继续我们的需求,要估算出 priority fee 我们需要确定两个值:

    i.要用多少的历史数据来进行估算;

    ii.希望自己的交易有多大的优先级。

对于 i ,我们将值设为 4 个区块,大概一分钟的时长算是比较合理。

对于 ii ,我们将期望定为25%, 50%, 75%  对应 低,中,高 的小费。

blocks = format_fee_history(ethClient.eth.fee_history(4,"latest", [25, 50, 75]),False)low_percential_priority_fees = [b['priorityFeePerGas'][0] for b in blocks]mid_percential_priority_fees = [b['priorityFeePerGas'][1] for b in blocks]high_percential_priority_fees = [b['priorityFeePerGas'][2] for b in blocks]low_average = sum(low_percential_priority_fees) / len(low_percential_priority_fees) mid_average = sum(mid_percential_priority_fees) / len(mid_percential_priority_fees) high_average = sum(high_percential_priority_fees) / len(high_percential_priority_fees) print(low_average,' | ',mid_average,' | ',high_average)

Tokenview 使用的算法是简单地将过去 4 个历史块的 priorityFeePerGas 求平均值。当然 您如果有更好的估算方式,比如将历史区块数量增加到 20 个块,或者是只考虑最便宜的几个成功交易。您可以构建您自己的 gas fee 估算器。

感谢您从头到尾阅读🎉🎉🎉



认识 Tokenview

Tokenview 旨在成为区块链浏览器的引领者。Tokenview.io 包含 120+ 条公链的链上数据,涵盖了 BTC(UTXO)系列、ETH 系列、TRX、 Layer2 公链、Cosmos 生态、zk-SNARKs、隐私链等链上数据全部由自建公链节点集群提供,包括交易数据、智能合约数据、链上统计数据,Defi,NFT 等,集丰富的区块链数据种类和数量于一体,接口以 Json 展示,结构化易于使用,数据准确度高,经多层校验精准完备。

联系 Tokenview

Website:tokenview.io
Twitter:twitter.com/tokenview2018
Telegram 中文:t.me/tokenview
Telegram英文:t.me/tokenview_en
Discord:discord.gg/9WCeZqdVqu
微信公众号:TokenviewLabs


【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

Tokenview
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开