在EIP-1559之前,ETH上的 gas fee 采用的是简单的竞拍模式,出价高者的交易优先被验证。这样的计算模式会导致 gas fee 因为人为因素(出价)而产生剧烈波动。EIP-1559 就是为了解决 gas fee 难以预测以及波动大的问题。
许多应用程序喜欢为用户提供设置自己的汽油费出价的选项,包括“慢速”、“平均”和“快速”选项。在本文中,我们将了解如何使用伦敦分叉后的 API 构建这些选项。
首先,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 The block size has been expanded, the maximum multiple is 2 BaseFeeChangeDenominator = 8 # The amount the base fee can change between blocks 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: # parent block's gasUsed is the same as the target, baseFee remains unchanged return parent_block.baseFeePerGas if parent_block.gasUsed > parent_gas_target: # parent block uses gas greater than the target value, baseFee increase 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: # the gas used by the parent block is less than the target value, baseFee reduce 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_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}")
与基础费不同,优先费是人为设定的值。对于需要在同一区块中优先执行的交易,需要提供更高的小费。要预测优先费,得扫描过去一段时间的区块,看看其他人都在用多少的费用
我们将会用到这个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, "reward": [ [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 估算器
感谢您从头到尾阅读 🎉🎉🎉