Skip to content

Commit

Permalink
Merge pull request #1206 from eth-brownie/feat/eip1559-defaults
Browse files Browse the repository at this point in the history
Add `network.max_fee` and `network.priority_fee`
  • Loading branch information
iamdefinitelyahuman authored Aug 16, 2021
2 parents eb0dd3c + 11a30e0 commit 99382bb
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 62 deletions.
4 changes: 4 additions & 0 deletions brownie/data/default-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ networks:
gas_limit: max
gas_buffer: 1
gas_price: 0
max_fee: null
priority_fee: null
reverting_tx_gas_limit: max
default_contract_owner: true
cmd_settings: null
live:
gas_limit: auto
gas_buffer: 1.1
gas_price: auto
max_fee: null
priority_fee: null
reverting_tx_gas_limit: false
default_contract_owner: false

Expand Down
11 changes: 10 additions & 1 deletion brownie/network/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@


from .account import Accounts
from .main import connect, disconnect, gas_limit, gas_price, is_connected, show_active # NOQA 401
from .main import ( # NOQA 401
connect,
disconnect,
gas_limit,
gas_price,
is_connected,
max_fee,
priority_fee,
show_active,
)
from .rpc import Rpc
from .state import Chain, TxHistory
from .web3 import web3
Expand Down
11 changes: 11 additions & 0 deletions brownie/network/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,18 @@ def _make_transaction(
if silent is None:
silent = bool(CONFIG.mode == "test" or CONFIG.argv["silent"])

if gas_price is None:
# if gas price is not explicitly set, load the default max fee and priority fee
if max_fee is None:
max_fee = CONFIG.active_network["settings"]["max_fee"] or None
if priority_fee is None:
priority_fee = CONFIG.active_network["settings"]["priority_fee"] or None

if priority_fee == "auto":
priority_fee = Chain().priority_fee

try:
# if max fee and priority fee are not set, use gas price
if max_fee is None and priority_fee is None:
gas_price, gas_strategy, gas_iter = self._gas_price(gas_price)
else:
Expand Down
45 changes: 45 additions & 0 deletions brownie/network/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,48 @@ def gas_buffer(*args: Tuple[float, None]) -> Union[float, None]:
else:
raise TypeError("Invalid gas buffer - must be given as a float, int or None")
return CONFIG.active_network["settings"]["gas_buffer"]


def max_fee(*args: Tuple[Union[int, str, bool, None]]) -> Union[int, bool]:
"""
Gets and optionally sets the default max fee per gas.
* If a Wei value is given, this will be the default max fee.
* If set to None or False, transactions will default to using gas price.
"""
if not is_connected():
raise ConnectionError("Not connected to any network")
if args:
if args[0] in (None, False):
CONFIG.active_network["settings"]["max_fee"] = None
else:
try:
price = Wei(args[0])
except ValueError:
raise TypeError(f"Invalid max fee '{args[0]}'")
CONFIG.active_network["settings"]["max_fee"] = price
return CONFIG.active_network["settings"]["max_fee"]


def priority_fee(*args: Tuple[Union[int, str, bool, None]]) -> Union[int, bool]:
"""
Gets and optionally sets the default max priority fee per gas.
* If set to 'auto', the fee is set using `eth_maxPriorityFeePerGas`.
* If a Wei value is given, this will be the default max fee.
* If set to None or False, transactions will default to using gas price.
"""
if not is_connected():
raise ConnectionError("Not connected to any network")
if args:
if args[0] in (None, False):
CONFIG.active_network["settings"]["priority_fee"] = None
elif args[0] == "auto":
CONFIG.active_network["settings"]["priority_fee"] = "auto"
else:
try:
price = Wei(args[0])
except ValueError:
raise TypeError(f"Invalid priority fee '{args[0]}'")
CONFIG.active_network["settings"]["priority_fee"] = price
return CONFIG.active_network["settings"]["priority_fee"]
43 changes: 39 additions & 4 deletions docs/api-network.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ The ``main`` module contains methods for conncting to or disconnecting from the

* If no argument is given, the current default is displayed.
* If an integer value is given, this will be the default gas limit.
* If set to ``auto``, the gas limit is determined automatically via :meth:`web3.eth.estimate_gas <web3.eth.Eth.estimateGas>`.
* If set to ``"auto"``, the gas limit is determined automatically via :meth:`web3.eth.estimate_gas <web3.eth.Eth.estimate_gas>`.

Returns ``False`` if the gas limit is set automatically, or an ``int`` if it is set to a fixed value.

Expand Down Expand Up @@ -99,7 +99,7 @@ The ``main`` module contains methods for conncting to or disconnecting from the
Gets and optionally sets the default gas price.

* If an integer value is given, this will be the default gas price.
* If set to ``auto``, the gas price is determined automatically via :attr:`web3.eth.gas_price <web3.eth.Eth.gasPrice>`.
* If set to ``"auto"``, the gas price is determined automatically via :attr:`web3.eth.gas_price <web3.eth.Eth.gas_price>`.

Returns ``False`` if the gas price is set automatically, or an ``int`` if it is set to a fixed value.

Expand All @@ -115,6 +115,41 @@ The ``main`` module contains methods for conncting to or disconnecting from the
>>> network.gas_price("auto")
False
.. py:method:: main.max_fee(*args)
Gets and optionally sets the default max fee per gas.

* If an integer value is given, this will be the default max fee.
* If set to ``None`` or ``False``, transactions will instead default to using a legacy-style ``gas_price``.

.. code-block:: python
>>> from brownie import network
>>> network.max_fee()
None
>>> network.max_fee(10000000000)
10000000000
>>> network.max_fee("45 gwei")
45000000000
.. py:method:: main.priority_fee(*args)
Gets and optionally sets the default max priority fee per gas.

* If an integer value is given, this will be the default priority fee.
* If set to ``"auto"``, the fee is determined automatically via :attr:`web3.eth.max_priority_fee <web3.eth.Eth.max_priority_fee>`.
* If set to ``None`` or ``False``, transactions will instead default to using a legacy-style ``gas_price``.

.. code-block:: python
>>> from brownie import network
>>> network.priority_fee()
None
>>> network.priority_fee(4000000000)
4000000000
>>> network.priority_fee("2 gwei")
2000000000
``brownie.network.account``
===========================

Expand Down Expand Up @@ -1690,7 +1725,7 @@ Multicall
2. Auto-deployment on development networks (on first use).
3. Uses ``multicall2`` key in network-config as pre-defined multicall contract address
4. Can specify/modify block number to make calls at particular block heights
5. Calls which fail return ``None`` instad of causing all calls to fail
5. Calls which fail return ``None`` instad of causing all calls to fail

.. code-block:: python
Expand Down Expand Up @@ -1725,7 +1760,7 @@ Multicall Attributes
.. note::

``Multicall`` relies on an instance of ``Multicall2`` being available for aggregating results. If you set the block_height before the ``Multicall2`` instance you are using was deployed a ``ContractNotFound`` error will be raised.

.. code-block:: python
>>> with brownie.multicall(block_identifier=12733683):
Expand Down
14 changes: 14 additions & 0 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ Networks
gas_limit: max
gas_buffer: 1
gas_price: 0
max_fee: null
priority_fee: null
reverting_tx_gas_limit: max
default_contract_owner: true
cmd_settings:
Expand Down Expand Up @@ -157,6 +159,18 @@ Networks

live default: ``auto``

.. py:attribute:: max_fee
The default max fee per gas for all transactions. If set to ``null``, transactions will default to legacy-style (using ``gas_price``).

default: ``null``

.. py:attribute:: priority_fee
The default max priority fee per gas for all transactions. If set to ``null``, transactions will default to legacy-style (using ``gas_price``).

default: ``null``

.. py:attribute:: default_contract_owner
If ``false``, deployed contracts will not remember the account that they were created by. Every transaction will require a ``from`` kwarg.
Expand Down
98 changes: 56 additions & 42 deletions docs/core-gas.rst
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
.. _core-accounts:
.. _core-gas:

==============================
Setting Transaction Gas Prices
==============================

========================
Dynamic Fee Transactions
========================

If the network you are interacting with implements EIP-1559, you can use a better fee model when sending transactions. Instead of specifying ``gas_price``, you specify ``priority_fee`` and an optional ``max_fee``.

* ``priority_fee`` detrmines ``maxPriorityFeePerGas``, which is tipped to the miner. The recommended priority fee can be read from ``chain.priority_fee``.
* ``priority_fee`` determines ``maxPriorityFeePerGas``, which is tipped to the miner. The recommended priority fee can be read from ``chain.priority_fee``.

* ``max_fee`` determines ``maxFeePerGas``, which includes the base fee, which is the same for all transactions in the block and is burned, and your priority fee. The current base fee can be read from ``chain.base_fee``.

Brownie uses ``base_fee * 2 + priority_fee`` as ``max_fee`` if you only specify the priority fee.

.. code-block:: python
.. code-block:: python
>>> accounts[0].transfer(accounts[1], priority_fee='2 gwei')
Transaction sent: 0x090755e0b75648d12b1ada31fa5957a07aadcbe8a34b8f9af59098f1890d1063
Max fee: 4.0 gwei Priority fee: 2.0 gwei Gas limit: 30000000 Nonce: 0
Transaction confirmed Block: 1 Gas used: 21000 (0.07%) Gas price: 2.875 gwei
>>> accounts[0].transfer(accounts[1], priority_fee="2 gwei")
Transaction sent: 0x090755e0b75648d12b1ada31fa5957a07aadcbe8a34b8f9af59098f1890d1063
Max fee: 4.0 gwei Priority fee: 2.0 gwei Gas limit: 30000000 Nonce: 0
Transaction confirmed Block: 1 Gas used: 21000 (0.07%) Gas price: 2.875 gwei
Dynamic fee transactions do not support (and arguably don't need) gas strategies. The section below only applies to legacy transactions which use ``gas_price``.
Dynamic fee transactions do not support (and arguably don't need) gas strategies. The section below only applies to legacy transactions which use ``gas_price``.

Setting Default Dynamic Fees
----------------------------

You can use :func:`network.priority_fee <main.max_fee>` to set a default priority fee for all transactions:

.. code-block:: python
>>> from brownie.network import gas_price
>>> priority_fee("2 gwei")
Setting the default to ``"auto"`` will dynamically determine the priority fee using :attr:`web3.eth.max_priority_fee <web3.eth.Eth.max_priority_fee>`. Seting to ``None`` will return to legacy-style transactions.

==============
Gas Strategies
==============

Expand All @@ -34,37 +48,37 @@ Gas strategies come in three basic types:
* **Time** strategies provide an initial price, and optionally replace pending transactions based on the amount of time that has passed since the first transaction was broadcast.

Using a Gas Strategy
====================
--------------------

To use a gas strategy, first import it from ``brownie.network.gas.strategies``:

.. code-block:: python
.. code-block:: python
>>> from brownie.network.gas.strategies import GasNowStrategy
>>> gas_strategy = GasNowStrategy("fast")
>>> from brownie.network.gas.strategies import GasNowStrategy
>>> gas_strategy = GasNowStrategy("fast")
You can then provide the object in the ``gas_price`` field when making a transaction:

.. code-block:: python
.. code-block:: python
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
When the strategy replaces a pending transaction, the returned :func:`TransactionReceipt <brownie.network.transaction.TransactionReceipt>` object will be for the transaction that confirms.

During :ref:`non-blocking transactions <core-accounts-non-blocking>`, all pending transactions are available within the :func:`history <brownie.network.state.TxHistory>` object. As soon as one transaction confirms, the remaining dropped transactions are removed.

Setting a Default Gas Strategy
==============================
------------------------------

You can use :func:`network.gas_price <main.gas_price>` to set a gas strategy as the default for all transactions:

.. code-block:: python
.. code-block:: python
>>> from brownie.network import gas_price
>>> gas_price(gas_strategy)
>>> from brownie.network import gas_price
>>> gas_price(gas_strategy)
Available Gas Strategies
========================
------------------------

.. py:class:: brownie.network.gas.strategies.LinearScalingStrategy(initial_gas_price, max_gas_price, increment=1.125, time_duration=30)
Expand All @@ -75,12 +89,12 @@ Available Gas Strategies
* ``increment``: Multiplier applied to the previous gas price in order to determine the new gas price
* ``time_duration``: Number of seconds between transactions

.. code-block:: python
.. code-block:: python
>>> from brownie.network.gas.strategies import LinearScalingStrategy
>>> gas_strategy = LinearScalingStrategy("10 gwei", "50 gwei", 1.1)
>>> from brownie.network.gas.strategies import LinearScalingStrategy
>>> gas_strategy = LinearScalingStrategy("10 gwei", "50 gwei", 1.1)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
.. py:class:: brownie.network.gas.strategies.ExponentialScalingStrategy(initial_gas_price, max_gas_price, time_duration=30)
Expand All @@ -92,25 +106,25 @@ Available Gas Strategies
* ``max_gas_price``: The maximum gas price to use
* ``time_duration``: Number of seconds between transactions

.. code-block:: python
.. code-block:: python
>>> from brownie.network.gas.strategies import ExponentialScalingStrategy
>>> gas_strategy = ExponentialScalingStrategy("10 gwei", "50 gwei")
>>> from brownie.network.gas.strategies import ExponentialScalingStrategy
>>> gas_strategy = ExponentialScalingStrategy("10 gwei", "50 gwei")
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
.. py:class:: brownie.network.gas.strategies.GasNowStrategy(speed="fast")
Simple gas strategy for determing a price using the `GasNow <https://www.gasnow.org/>`_ API.

* ``speed``: The gas price to use based on the API call. Options are rapid, fast, standard and slow.

.. code-block:: python
.. code-block:: python
>>> from brownie.network.gas.strategies import GasNowStrategy
>>> gas_strategy = GasNowStrategy("fast")
>>> from brownie.network.gas.strategies import GasNowStrategy
>>> gas_strategy = GasNowStrategy("fast")
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
.. py:class:: brownie.network.gas.strategies.GasNowScalingStrategy(initial_speed="standard", max_speed="rapid", increment=1.125, block_duration=2)
Expand All @@ -121,12 +135,12 @@ Available Gas Strategies
* ``increment``: A multiplier applied to the most recently used gas price in order to determine the new gas price. If the incremented value is less than or equal to the current ``max_speed`` rate, a new transaction is broadcasted. If the current rate for ``initial_speed`` is greater than the incremented rate, it is used instead.
* ``block_duration``: The number of blocks to wait between broadcasting new transactions.

.. code-block:: python
.. code-block:: python
>>> from brownie.network.gas.strategies import GasNowScalingStrategy
>>> gas_strategy = GasNowScalingStrategy("fast", increment=1.2)
>>> from brownie.network.gas.strategies import GasNowScalingStrategy
>>> gas_strategy = GasNowScalingStrategy("fast", increment=1.2)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
.. py:class:: brownie.network.gas.strategies.GethMempoolStrategy(position=500, graphql_endpoint=None, block_duration=2)
Expand All @@ -139,14 +153,14 @@ Available Gas Strategies
* A position of 200 or less usually places a transaction within the mining block.
* A position of 500 usually places a transaction within the 2nd pending block.

.. code-block:: python
.. code-block:: python
>>> from brownie.network.gas.strategies import GethMempoolStrategy
>>> gas_strategy = GethMempoolStrategy(200)
>>> from brownie.network.gas.strategies import GethMempoolStrategy
>>> gas_strategy = GethMempoolStrategy(200)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
>>> accounts[0].transfer(accounts[1], "1 ether", gas_price=gas_strategy)
Building your own Gas Strategy
==============================
------------------------------

To implement your own gas strategy you must subclass from one of the :ref:`gas strategy abstract base classes <api-network-gas-abc>`.
Loading

0 comments on commit 99382bb

Please sign in to comment.