Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add network.max_fee and network.priority_fee #1206

Merged
merged 5 commits into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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