diff --git a/CHANGELOG.md b/CHANGELOG.md index 030e2ed..6ac6468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [20200320] + + - 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/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 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/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", 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