Source code for eth_defi.gas
"""Gas price strategies.
`Web3.py no longer support gas price strategies post London hard work <https://web3py.readthedocs.io/en/stable/gas_price.html>`_.
"""
import enum
from dataclasses import dataclass
from typing import Optional
from web3 import Web3
from web3.gas_strategies.rpc import rpc_gas_price_strategy
[docs]class GasPriceMethod(enum.Enum):
"""What method we did use for setting the gas price."""
#: Legacy chains
legacy = "legacy"
#: Post London hard work
london = "london"
[docs]@dataclass
class GasPriceSuggestion:
"""Gas price details.
Capture the necessary information for the gas price to used during the transaction building.
- EIP-1559 London hard fork chains (Ethereumm mainnet)
- Legacy EVM: Polygon, BNB Chain
"""
#: How the gas price was determined
method: GasPriceMethod
#: Non London hard fork chains
legacy_gas_price: Optional[int] = None
#: London hard fork chains
base_fee: Optional[int] = None
#: London hard fork chains
max_priority_fee_per_gas: Optional[int] = None
#: London hard fork chains
max_fee_per_gas: Optional[int] = None
def __repr__(self):
return f"<Gas pricing method:{self.method.name} base:{self.base_fee} priority:{self.max_priority_fee_per_gas} max:{self.max_fee_per_gas} legacy:{self.legacy_gas_price}>"
[docs] def get_tx_gas_params(self) -> dict:
"""Get gas params as they are applied to ContractFunction.build_transaction()"""
if self.base_fee is not None:
return {"maxPriorityFeePerGas": self.max_priority_fee_per_gas, "maxFeePerGas": self.max_fee_per_gas}
else:
return {"gasPrice": self.legacy_gas_price}
[docs]def estimate_gas_fees(web3: Web3) -> GasPriceSuggestion:
"""Get a good gas price for a transaction.
TODO: This is non-optimal, first draft implementation.
"""
last_block = web3.eth.get_block("latest")
base_fee = last_block.get("baseFeePerGas")
if base_fee is not None:
# London gas strategy
# see https://github.com/ethereum/web3.py/blob/c70f7fbe1cfa98b1ce8597a08c99e05759a9667b/web3/_utils/transactions.py#L57
# see https://github.com/ethereum/web3.py/blob/36adb16c68f570c343d01ecc8d0096cbac814172/web3/middleware/gas_price_strategy.py#L57
base_fee = base_fee
max_fee_per_gas = web3.eth.max_priority_fee + (2 * base_fee)
if web3.eth.chain_id == 137:
# polygon now has a minimum gas fee of 30 gwei to avoid spam
max_priority_fee_per_gas = max(30_000_000_000, web3.eth.max_priority_fee)
else:
max_priority_fee_per_gas = web3.eth.max_priority_fee
# https://github.com/ethereum/go-ethereum/blob/2e478aab98c13577c66b4531ba240a601dbc1516/core/error.go#L87
if max_priority_fee_per_gas > max_fee_per_gas:
max_fee_per_gas = max_priority_fee_per_gas
return GasPriceSuggestion(method=GasPriceMethod.london, base_fee=base_fee, max_priority_fee_per_gas=max_priority_fee_per_gas, max_fee_per_gas=max_fee_per_gas)
else:
# Legacy gas strategy
return GasPriceSuggestion(method=GasPriceMethod.legacy, legacy_gas_price=web3.eth.generate_gas_price())
[docs]def apply_gas(tx: dict, suggestion: GasPriceSuggestion) -> dict:
"""Apply gas fees to a raw transaction dict.
Example:
.. code-block::
from web3 import Web3
from web3._utils.transactions import fill_nonce
from eth_account.signers.local import LocalAccount
web3: Web3
hot_wallet: LocalAccount
# Move 10 tokens from deployer to user1
tx = token.functions.transfer(hot_wallet.address, 10 * 10**18).build_transaction({
"from": hot_wallet.address,
'chainId': web3.eth.chain_id,
"gas": 150_000, # 150k gas should be more than enough for ERC20.transfer()
})
tx = fill_nonce(web3, tx)
gas_fees = estimate_gas_fees(web3)
apply_gas(tx, gas_fees)
signed = hot_wallet.sign_transaction(tx)
tx_hash = web3.eth.send_raw_transaction(signed.rawTransaction)
receipt = web3.eth.get_transaction_receipt(tx_hash)
:return:
Mutated dict
"""
if suggestion.method == GasPriceMethod.london:
tx["maxFeePerGas"] = suggestion.max_fee_per_gas
tx["maxPriorityFeePerGas"] = suggestion.max_priority_fee_per_gas
else:
tx["gasPrice"] = suggestion.legacy_gas_price
return tx
[docs]def node_default_gas_price_strategy(web3: Web3, transaction_params: dict) -> int:
"""Gas price strategy for blockchains not supporting dynamic gas fees.
This gas price strategy will query the JSON-RPC for the suggested flat fee.
It works on chains that do not support EIP-1559 London hardfork style
base fee + max fee dynamic pricing.
These include
- BNB Chain
Example:
.. code-block::
from eth_defi.gas import node_default_gas_price_strategy
web3.eth.set_gas_price_strategy(node_default_gas_price_strategy)
For more information see
- https://web3py.readthedocs.io/en/stable/gas_price.html
- https://www.blockchain-council.org/ethereum/eip-1559/
"""
node_default_price = rpc_gas_price_strategy(web3)
return node_default_price