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

added alchemy and provider support #1401

Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Force files to be opened as UTF-8
- Added a new solidity compiler setting `use_latest_patch` in brownie-config.yaml to use the latest patch version of a compiler based on the pragma version of the contract.
- Add cli flag `-r` for raising exceptions to the caller instead of doing a system exit.
- Added support for alchemy out of the box with a `WEB3_ALCHEMY_PROJECT_ID` after running `brownie networks set_provider alchemy`.
- Add `override` argument to contract methods which allows changing the state before the call, including overwriting balance, nonce, code, and storage of any address.

### Fixed
Expand Down
90 changes: 88 additions & 2 deletions brownie/_cli/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
import <path> <replace=False> Import network settings
export <path> Export network settings
delete <id> Delete an existing network
update_provider <name> <url> Adds or modifies a new network provider
delete_provider <name> Removes a network provider
set_provider <name> Sets a provider from the list of providers
list_providers List available providers

Options:
--help -h Display this message
Expand All @@ -39,7 +43,7 @@

DEV_REQUIRED = ("id", "host", "cmd", "cmd_settings")
PROD_REQUIRED = ("id", "host", "chainid")
OPTIONAL = ("name", "explorer", "timeout", "multicall2")
OPTIONAL = ("name", "explorer", "timeout", "multicall2", "provider")

DEV_CMD_SETTINGS = (
"port",
Expand Down Expand Up @@ -160,7 +164,6 @@ def _modify(id_, *args):
target = next(i for i in networks["development"] if i["id"] == id_)
else:
target = next(x for i in networks["live"] for x in i["networks"] if x["id"] == id_)

for key, value in args.items():
t = target
if key in DEV_CMD_SETTINGS and is_dev:
Expand Down Expand Up @@ -270,6 +273,77 @@ def _export(path_str):
notify("SUCCESS", f"Network settings exported as '{color('bright magenta')}{path}{color}'")


def _update_provider(name, url):
with _get_data_folder().joinpath("providers-config.yaml").open() as fp:
providers = yaml.safe_load(fp)

providers[name] = {"host": url}

with _get_data_folder().joinpath("providers-config.yaml").open("w") as fp:
yaml.dump(providers, fp)

notify("SUCCESS", f"Provider '{color('bright magenta')}{name}{color}' has been updated")


def _delete_provider(name):
with _get_data_folder().joinpath("providers-config.yaml").open() as fp:
providers = yaml.safe_load(fp)

if name not in providers.keys():
raise ValueError(f"Provider '{color('bright magenta')}{name}{color}' does not exist")

del providers[name]

with _get_data_folder().joinpath("providers-config.yaml").open("w") as fp:
yaml.dump(providers, fp)

notify("SUCCESS", f"Provider '{color('bright magenta')}{name}{color}' has been deleted")


def _set_provider(name):
with _get_data_folder().joinpath("providers-config.yaml").open() as fp:
providers = yaml.safe_load(fp)

if name not in providers.keys():
raise ValueError(f"Provider '{color('bright magenta')}{name}{color}' does not exist")

with _get_data_folder().joinpath("network-config.yaml").open() as fp:
networks = yaml.safe_load(fp)

for blockchain in networks["live"]:
for network in blockchain["networks"]:
if "provider" in network.keys() and network["provider"]:
new_network_name = network["name"].replace(
network["provider"].capitalize(), name.capitalize()
)

new_host = providers[name]["host"].format(network["id"])
_modify(
network["id"],
f"name={new_network_name}",
f"provider={name}",
f"host={new_host}",
)


def _list_providers(verbose=False):
if isinstance(verbose, str):
try:
verbose = eval(verbose.capitalize())
except (NameError, SyntaxError) as e:
print("Please pass 'True' or 'False'.")
raise e

with _get_data_folder().joinpath("providers-config.yaml").open() as fp:
providers = yaml.safe_load(fp)

print("The following providers are declared:")
if verbose:
_print_verbose_providers_description(providers)
else:
_print_simple_providers_description(providers)


def _parse_args(args):
try:
args = dict(i.split("=") for i in args)
Expand All @@ -285,6 +359,18 @@ def _parse_args(args):
return args


def _print_verbose_providers_description(providers):
u = "\u251c"
for provider in providers:
print(f"{color('bright black')} {u}\u2500{color}provider: {provider}:")
print(f"{color('bright black')} {u}\u2500{color} host: {providers[provider]}:")


def _print_simple_providers_description(providers):
u = "\u251c"
print(f"{color('bright black')} {u}\u2500{color}{providers.keys()}:")


def _print_simple_network_description(network_dict, is_last):
u = "\u2514" if is_last else "\u251c"
print(
Expand Down
6 changes: 6 additions & 0 deletions brownie/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,12 @@ def _make_data_folders(data_folder: Path) -> None:
data_folder.joinpath("network-config.yaml"),
)

if not data_folder.joinpath("providers-config.yaml").exists():
shutil.copyfile(
BROWNIE_FOLDER.joinpath("data/providers-config.yaml"),
data_folder.joinpath("providers-config.yaml"),
)


warnings.filterwarnings("once", category=DeprecationWarning, module="brownie")

Expand Down
7 changes: 6 additions & 1 deletion brownie/data/network-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,35 @@ live:
host: https://mainnet.infura.io/v3/$WEB3_INFURA_PROJECT_ID
explorer: https://api.etherscan.io/api
multicall2: "0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696"
provider: infura
- name: Ropsten (Infura)
chainid: 3
id: ropsten
host: https://ropsten.infura.io/v3/$WEB3_INFURA_PROJECT_ID
explorer: https://api-ropsten.etherscan.io/api
multicall2: "0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696"
provider: infura
- name: Rinkeby (Infura)
chainid: 4
id: rinkeby
host: https://rinkeby.infura.io/v3/$WEB3_INFURA_PROJECT_ID
explorer: https://api-rinkeby.etherscan.io/api
multicall2: "0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696"
provider: infura
- name: Goerli (Infura)
chainid: 5
id: goerli
host: https://goerli.infura.io/v3/$WEB3_INFURA_PROJECT_ID
explorer: https://api-goerli.etherscan.io/api
multicall2: "0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696"
provider: infura
- name: Kovan (Infura)
chainid: 42
id: kovan
host: https://kovan.infura.io/v3/$WEB3_INFURA_PROJECT_ID
explorer: https://api-kovan.etherscan.io/api
multicall2: "0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696"
provider: infura
- name: Ethereum Classic
networks:
- name: Mainnet
Expand All @@ -54,7 +59,7 @@ live:
- name: Avalanche
networks:
- chainid: 43114
explorer: https://api.snowtrace.io/api
explorer: https://api.snowtrace.io/api
host: https://api.avax.network/ext/bc/C/rpc
id: avax-main
name: Mainnet
Expand Down
4 changes: 4 additions & 0 deletions brownie/data/providers-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
infura:
host: https://{}.infura.io/v3/$WEB3_INFURA_PROJECT_ID
alchemy:
host: https://eth-{}.alchemyapi.io/v2/$WEB3_ALCHEMY_PROJECT_ID
82 changes: 77 additions & 5 deletions docs/network-management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,16 +187,68 @@ Clients such as `Geth <https://geth.ethereum.org/>`_ or `Parity <https://www.par

If you wish to learn more about running a node, ethereum.org provides a `list of resources <https://ethereum.org/en/developers/docs/nodes-and-clients/>`_ that you can use to get started.

Using a Hosted Node
*******************
Using a Hosted Node / Providers
========================================

Services such as `Infura <https://infura.io>`_ provide public access to Ethereum nodes. This is a much simpler option than running your own, but it is not without limitations:
Services such as `Alchemy <https://www.alchemy.com>`_ and `Infura <https://infura.io>`_ provide public access to Ethereum nodes. This is a much simpler option than running your own, but it is not without limitations:

1. Some RPC endpoints may be unavailable. In particular, Infura does not provide access to the `debug_traceTransaction <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction>`_ method. For this reason, Brownie's :ref:`debugging tools<debug>` will not work when connected via Infura.
2. Hosted nodes do not provide access to accounts - this would be a major security hazard! You will have to manually unlock your own :ref:`local account<local-accounts>` before you can make a transaction.

Using Infura
^^^^^^^^^^^^
Brownie allows you to bulk modify the provider you use by setting the `provider` field in the networks, and the associated provider.

::

$ brownie networks providers_list

Brownie v1.17.2 - Python development framework for Ethereum

The following providers are declared:
├─dict_keys(['infura', 'alchemy']):

or

::

$ brownie networks providers_list True

Brownie v1.17.2 - Python development framework for Ethereum

The following providers are declared:
├─provider: infura:
├─ host: {'host': 'https://{}.infura.io/v3/$WEB3_INFURA_PROJECT_ID'}:
├─provider: alchemy:
├─ host: {'host': 'https://eth-{}.alchemyapi.io/v2/$WEB3_ALCHEMY_PROJECT_ID'}:


Any `network` that has a `provider` set will be able to be swapped to the format of another provider. For example, to swap all provider-based networks to Alchemy, run:

::

$ brownie networks set_provider alchemy


And it'll print out all the valid networks to swap to the Alchemy format. If you don't have a `provider` set, you can set one with:

::

$ brownie networks modify mainnet provider=alchemy


Adding Providers
----------------

To add or update a provider, run:

::

$ brownie networks update_provider alchemy https://eth-{}.alchemyapi.io/v2/$WEB3_ALCHEMY_PROJECT_ID


This URL will allow brownie to swap out the {} with whatever network it's on, and you'll set a `WEB3_ALCHEMY_PROJECT_ID` environment variable as your Alchemy key.

Using Infura
************

To Infura you need to `register for an account <https://infura.io/register>`_. Once you have signed up, login and create a new project. You will be provided with a project ID, as well as API URLs that can be leveraged to access the network.

Expand All @@ -206,6 +258,26 @@ To connect to Infura using Brownie, store your project ID as an environment vari

$ export WEB3_INFURA_PROJECT_ID=YourProjectID

Or adding `export WEB3_INFURA_PROJECT_ID=YourProjectID` to your `.env` and adding `dotenv: .env` to your `brownie-config.yaml`.


Using Alchemy
*************

To Alchemy you need to `signup for an account <https://auth.alchemyapi.io/signup>`_. Once you have signed up, login and create a new project. You will be provided with a URL that can be leveraged to access the network.

Hit the `view key` button, and you'll be given a URL. You can just use the section after the last slash as your `WEB3_ALCHEMY_PROJECT_ID`. For example if your full key is: `https://eth-mainnet.alchemyapi.io/v2/1234`, your `WEB3_ALCHEMY_PROJECT_ID` would be `1234`.
Note, this only works well with ETH networks at the moment, but you can modify your providers list at any time.

You can set your `WEB3_ALCHEMY_PROJECT_ID` with the following command
::

$ export WEB3_ALCHEMY_PROJECT_ID=YourProjectID

Or adding `export WEB3_ALCHEMY_PROJECT_ID=YourProjectID` to your `.env` and adding `dotenv: .env` to your `brownie-config.yaml`.

To connect with other non-ethereum networks through alchemy, you'll have to follow the normal network adding process.

.. _network-management-fork:

Using a Forked Development Network
Expand Down
10 changes: 5 additions & 5 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ certifi==2021.10.8
# requests
cffi==1.15.0
# via cryptography
charset-normalizer==2.0.7
charset-normalizer==2.0.9
# via
# -c requirements.txt
# requests
Expand Down Expand Up @@ -61,7 +61,7 @@ idna==3.3
# requests
imagesize==1.2.0
# via sphinx
importlib-metadata==4.8.1
importlib-metadata==4.8.2
# via
# keyring
# twine
Expand Down Expand Up @@ -89,7 +89,7 @@ mypy-extensions==0.4.3
# via
# -c requirements.txt
# mypy
packaging==21.0
packaging==21.3
# via
# -c requirements.txt
# bleach
Expand All @@ -111,7 +111,7 @@ pluggy==1.0.0
# -c requirements.txt
# pytest
# tox
py==1.10.0
py==1.11.0
# via
# -c requirements.txt
# pytest
Expand All @@ -127,7 +127,7 @@ pygments==2.10.0
# -c requirements.txt
# readme-renderer
# sphinx
pyparsing==3.0.3
pyparsing==3.0.6
# via
# -c requirements.txt
# packaging
Expand Down
Loading