From 22eb591e586f2e0ae0388f16c3c40406d36c9b70 Mon Sep 17 00:00:00 2001 From: Andre Miras Date: Fri, 8 Nov 2019 20:23:42 +0100 Subject: [PATCH 1/3] Adds CHANGELOG.md to readthedocs --- docs/changelog.rst | 5 +++++ docs/index.rst | 1 + 2 files changed, 6 insertions(+) create mode 100644 docs/changelog.rst diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..8e1bc1d --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,5 @@ +CHANGELOG +========= + +.. mdinclude:: ../CHANGELOG.md + diff --git a/docs/index.rst b/docs/index.rst index e4e3ed0..4dc1037 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ Welcome to pyetheroll's documentation! readme.rst Examples.md Release.md + changelog.rst Indices and tables From af8fd4993894a2ac1f6bb1a6158cbe3c41364c7b Mon Sep 17 00:00:00 2001 From: Andre Miras Date: Sat, 21 Mar 2020 18:58:00 +0100 Subject: [PATCH 2/3] Moves Etherscan API key to env var Makes the Etherscan API key name explicit as we'll now also have Infura one. Also moves it to an environment variable for consistency with web3 and Infura. Also adds a documentation note on the API keys, fixes #9 --- CHANGELOG.md | 4 ++++ README.md | 5 +++++ pyetheroll/constants.py | 3 ++- pyetheroll/etheroll.py | 31 ++++++++++++------------------ pyetheroll/transaction_debugger.py | 16 +++++++++------ pyetheroll/utils.py | 17 +++++++++++++++- tests/test_etheroll.py | 4 ++-- 7 files changed, 51 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 030e2ed..e441974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [Unreleased] + + - Move API keys to env var, refs #9 + ## [20191108] - Fix Ropsten 403 errors diff --git a/README.md b/README.md index 17700d9..4e9ad8b 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,11 @@ etheroll = Etheroll(chain_id, contract_address) Find out more in [docs/Examples.md](docs/Examples.md). +## API keys +Both Etherscan and Infura require API keys which are retrieved from the following environment variables: +- `ETHERSCAN_API_KEY` (consumed by pyetheroll directly) +- `WEB3_INFURA_PROJECT_ID` (consumed by [web3.py](https://github.com/ethereum/web3.py)) + ## Install [Latest stable release](https://github.com/AndreMiras/pyetheroll/tree/master): diff --git a/pyetheroll/constants.py b/pyetheroll/constants.py index 4217948..48b3a14 100644 --- a/pyetheroll/constants.py +++ b/pyetheroll/constants.py @@ -3,7 +3,8 @@ ROUND_DIGITS = 2 DEFAULT_GAS_PRICE_GWEI = 4 DEFAULT_GAS_PRICE_WEI = int(DEFAULT_GAS_PRICE_GWEI * 1e9) -DEFAULT_API_KEY_TOKEN = "YourApiKeyToken" +DEFAULT_ETHERSCAN_API_KEY = "YourApiKeyToken" +DEFAULT_INFURA_PROJECT_ID = "7c841c560b1e4660a9683507cb27b2f8" class ChainID(Enum): diff --git a/pyetheroll/etheroll.py b/pyetheroll/etheroll.py index f7e10f2..27fc9db 100644 --- a/pyetheroll/etheroll.py +++ b/pyetheroll/etheroll.py @@ -13,12 +13,7 @@ from web3 import Web3 from web3.contract import Contract -from pyetheroll.constants import ( - DEFAULT_API_KEY_TOKEN, - DEFAULT_GAS_PRICE_WEI, - ROUND_DIGITS, - ChainID, -) +from pyetheroll.constants import DEFAULT_GAS_PRICE_WEI, ROUND_DIGITS, ChainID from pyetheroll.etherscan_utils import ( ChainEtherscanAccountFactory, ChainEtherscanContractFactory, @@ -27,7 +22,7 @@ HTTPProviderFactory, TransactionDebugger, ) -from pyetheroll.utils import timestamp2datetime +from pyetheroll.utils import get_etherscan_api_key, timestamp2datetime REQUESTS_CACHE_PARAMS = { "cache_name": "requests_cache", @@ -83,7 +78,6 @@ class Etheroll: def __init__( self, - api_key: str = DEFAULT_API_KEY_TOKEN, chain_id: ChainID = ChainID.MAINNET, contract_address: str = None, ): @@ -96,7 +90,7 @@ def __init__( # self.provider = EthereumTesterProvider(ethereum_tester) self.provider = HTTPProviderFactory.create(self.chain_id) self.web3 = Web3(self.provider) - self.etherscan_api_key = api_key + self.etherscan_api_key = get_etherscan_api_key() ChainEtherscanContract = ChainEtherscanContractFactory.create( self.chain_id ) @@ -111,7 +105,8 @@ def __init__( address=self.contract_address, api_key=self.etherscan_api_key ) self.etherscan_contract_api.http.headers = update_user_agent( - self.etherscan_contract_api.http.headers) + self.etherscan_contract_api.http.headers + ) self.contract_abi = json.loads( self.etherscan_contract_api.get_abi() ) @@ -130,10 +125,7 @@ def __init__( @classmethod def get_or_create( - cls, - api_key: str = DEFAULT_API_KEY_TOKEN, - chain_id: ChainID = ChainID.MAINNET, - contract_address: str = None, + cls, chain_id: ChainID = ChainID.MAINNET, contract_address: str = None, ): """ Gets or creates the Etheroll object. @@ -141,11 +133,10 @@ def get_or_create( """ contract_address = contract_address or cls.CONTRACT_ADDRESSES[chain_id] if cls._etheroll is None or ( - cls._etheroll.etherscan_api_key, cls._etheroll.chain_id, cls._etheroll.contract_address, - ) != (api_key, chain_id, contract_address): - cls._etheroll = cls(api_key, chain_id, contract_address) + ) != (chain_id, contract_address): + cls._etheroll = cls(chain_id, contract_address) return cls._etheroll def definitions(self, contract_abi, typ): @@ -284,7 +275,8 @@ def get_transaction_page( address=address, api_key=self.etherscan_api_key ) etherscan_account_api.http.headers = update_user_agent( - etherscan_account_api.http.headers) + etherscan_account_api.http.headers + ) sort = "desc" try: transactions = etherscan_account_api.get_transaction_page( @@ -612,7 +604,8 @@ def get_balance(self, address): address=address, api_key=self.etherscan_api_key ) etherscan_account_api.http.headers = update_user_agent( - etherscan_account_api.http.headers) + etherscan_account_api.http.headers + ) balance_wei = int(etherscan_account_api.get_balance()) balance_eth = round(balance_wei / 1e18, ROUND_DIGITS) return balance_eth diff --git a/pyetheroll/transaction_debugger.py b/pyetheroll/transaction_debugger.py index c23abd9..d885257 100644 --- a/pyetheroll/transaction_debugger.py +++ b/pyetheroll/transaction_debugger.py @@ -4,8 +4,9 @@ from eth_utils import decode_hex, function_abi_to_4byte_selector from web3 import HTTPProvider, Web3 -from pyetheroll.constants import DEFAULT_API_KEY_TOKEN, ChainID +from pyetheroll.constants import ChainID from pyetheroll.etherscan_utils import ChainEtherscanContractFactory +from pyetheroll.utils import get_etherscan_api_key, get_infura_project_id def decode_contract_call(contract_abi: list, call_data: str): @@ -30,10 +31,14 @@ class HTTPProviderFactory: # ChainID.MAINNET: 'https://api.myetherapi.com/eth', # ChainID.MAINNET: 'https://api.infura.io/v1/jsonrpc/mainnet', # ChainID.MAINNET: 'https://api.mycryptoapi.com/eth', - ChainID.MAINNET: "https://mainnet.infura.io", + ChainID.MAINNET: ( + f"https://mainnet.infura.io/v3/{get_infura_project_id()}" + ), # ChainID.ROPSTEN: 'https://api.myetherapi.com/rop', # ChainID.ROPSTEN: 'https://api.infura.io/v1/jsonrpc/ropsten', - ChainID.ROPSTEN: "https://ropsten.infura.io", + ChainID.ROPSTEN: ( + f"https://ropsten.infura.io/v3/{get_infura_project_id()}" + ), } @classmethod @@ -48,13 +53,12 @@ def __init__(self, contract_abi): self.methods_infos = None @staticmethod - def get_contract_abi( - chain_id, contract_address, api_key: str = DEFAULT_API_KEY_TOKEN - ) -> dict: + def get_contract_abi(chain_id, contract_address) -> dict: """ Given a contract address returns the contract ABI from Etherscan, refs #2 """ + api_key = get_etherscan_api_key() ChainEtherscanContract = ChainEtherscanContractFactory.create(chain_id) api = ChainEtherscanContract(address=contract_address, api_key=api_key) json_abi = api.get_abi() diff --git a/pyetheroll/utils.py b/pyetheroll/utils.py index 63b62f0..6ede391 100644 --- a/pyetheroll/utils.py +++ b/pyetheroll/utils.py @@ -1,6 +1,11 @@ +import os from datetime import datetime -from pyetheroll.constants import ROUND_DIGITS +from pyetheroll.constants import ( + DEFAULT_ETHERSCAN_API_KEY, + DEFAULT_INFURA_PROJECT_ID, + ROUND_DIGITS, +) class EtherollUtils: @@ -34,3 +39,13 @@ def timestamp2datetime(timestamp: str) -> datetime: base = 16 date_time = datetime.utcfromtimestamp(int(timestamp, base)) return date_time + + +def get_etherscan_api_key(): + """Returns ETHERSCAN_API_KEY from environment variable.""" + return os.environ.get("ETHERSCAN_API_KEY", DEFAULT_ETHERSCAN_API_KEY) + + +def get_infura_project_id(): + """Returns WEB3_INFURA_PROJECT_ID from environment variable.""" + return os.environ.get("WEB3_INFURA_PROJECT_ID", DEFAULT_INFURA_PROJECT_ID) diff --git a/tests/test_etheroll.py b/tests/test_etheroll.py index f449b5e..5dcf0c6 100644 --- a/tests/test_etheroll.py +++ b/tests/test_etheroll.py @@ -624,7 +624,7 @@ def test_get_log_bet_events(self): "&topic2=0x" "00000000000000000000000046044beaa1e985c67767e04de58181de5daaa00f" "&topic0_2_opr=and&", - headers={'User-Agent': 'https://github.com/AndreMiras/pyetheroll'}, + headers={"User-Agent": "https://github.com/AndreMiras/pyetheroll"}, ) expected_calls = [expected_call] assert m_get.call_args_list == expected_calls @@ -655,7 +655,7 @@ def test_get_log_result_events(self): "&topic3=0x" "00000000000000000000000046044beaa1e985c67767e04de58181de5daaa00f" "&topic0_3_opr=and&", - headers={'User-Agent': 'https://github.com/AndreMiras/pyetheroll'}, + headers={"User-Agent": "https://github.com/AndreMiras/pyetheroll"}, ) expected_calls = [expected_call] assert m_get.call_args_list == expected_calls From 07dd531d572d596ba96b0bf049c96699ad1df14a Mon Sep 17 00:00:00 2001 From: Andre Miras Date: Sat, 21 Mar 2020 23:46:14 +0100 Subject: [PATCH 3/3] 20200320 --- CHANGELOG.md | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e441974..6ac6468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased] +## [20200320] - Move API keys to env var, refs #9 diff --git a/setup.py b/setup.py index db5e1d8..0689593 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ def read(fname): # exposing the params so it can be imported setup_params = { "name": "pyetheroll", - "version": "20191108", + "version": "20200320", "description": "Python library to Etheroll smart contract", "long_description": read("README.md"), "long_description_content_type": "text/markdown",