From f488fe1abd0b21b22edcb397ad0b32d0e2c41609 Mon Sep 17 00:00:00 2001 From: Richard Horrocks Date: Mon, 4 Feb 2019 11:36:33 +0000 Subject: [PATCH 1/3] Addition of Blocks module. --- README.md | 26 +++++++++++++++----------- etherscan/blocks.py | 16 ++++++++++++++++ examples/blocks/__init__.py | 0 examples/blocks/get_block_reward.py | 9 +++++++++ setup.py | 2 +- 5 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 etherscan/blocks.py create mode 100644 examples/blocks/__init__.py create mode 100644 examples/blocks/get_block_reward.py diff --git a/README.md b/README.md index d3bff59..526ef43 100755 --- a/README.md +++ b/README.md @@ -2,30 +2,31 @@ [![Build Status](https://secure.travis-ci.org/corpetty/py-etherscan-api.png?branch=master)](http://travis-ci.org/corpetty/py-etherscan-api) [![Join the chat at https://gitter.im/py-etherscan/Lobby](https://badges.gitter.im/py-etherscan/Lobby.svg)](https://gitter.im/py-etherscan/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - EtherScan.io API python bindings ## Description -This module is written as an effort to provide python bindings to the EtherScan.io API, which can be found at: -https://etherscan.io/apis. If you are interacting with a contract on the Ropsten Testnet please use -https://ropsten.etherscan.io/apis. + +This module is written as an effort to provide python bindings to the EtherScan.io API, which can be found at: +https://etherscan.io/apis. If you are interacting with a contract on the Ropsten Testnet please use +https://ropsten.etherscan.io/apis. In order to use this, you must attain an Etherscan user account, and generate an API key. In order to use the API, you must provide an API key at runtime, which can be found at the Etherscan.io API website. If you'd like to use the provided examples without altering them, then the JSON file `api_key.json` must be stored in -the base directory. Its format is as follows: +the base directory. Its format is as follows: { "key" : "YourApiKeyToken" } - + with `YourApiKeyToken` is your provided API key token from EtherScan.io ## Installation + To install the package to your computer, simply run the following command in the base directory: python3 -m pip install py-etherscan-api - ## Available bindings + Currently, only the following Etherscan.io API modules are available: - accounts @@ -33,16 +34,19 @@ Currently, only the following Etherscan.io API modules are available: - stats - tokens - proxies +- blocks The remaining available modules provided by Etherscan.io will be added eventually... ## Available Networks + Currently, this works for the following networks: - Mainnet - Ropsten ## Examples + All possible calls have an associated example file in the examples folder to show how to call the binding These of course will be fleshed out with more details and explanation in time @@ -53,15 +57,15 @@ Jupyter notebooks area also included in each directory to show all examples - Package and submit to PyPI - Add the following modules: - - event logs - - geth proxy - - websockets + - event logs + - geth proxy + - websockets - Add robust documentation - Add unit test suite - Add request throttling based on Etherscan's suggestions - ## Holla at ya' boy + BTC: 16Ny72US78VEjL5GUinSAavDwARb8dXWKG ETH: 0x5E8047fc033499BD5d8C463ADb29f10f11165ed0 diff --git a/etherscan/blocks.py b/etherscan/blocks.py new file mode 100644 index 0000000..7213a99 --- /dev/null +++ b/etherscan/blocks.py @@ -0,0 +1,16 @@ +from .client import Client +from typing import Union + + +class Blocks(Client): + def __init__(self, api_key='YourApiKeyToken'): + Client.__init__(self, address='', api_key=api_key) + self.url_dict[self.MODULE] = 'block' + + def get_block_reward(self, block_number: Union[str, int]): + self.url_dict[self.ACTION] = 'getblockreward' + self.url_dict[self.BLOCKNO] = block_number if type( + block_number) is str else str(block_number) + self.build_url() + req = self.connect() + return req['result'] diff --git a/examples/blocks/__init__.py b/examples/blocks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/blocks/get_block_reward.py b/examples/blocks/get_block_reward.py new file mode 100644 index 0000000..fa57557 --- /dev/null +++ b/examples/blocks/get_block_reward.py @@ -0,0 +1,9 @@ +from etherscan.blocks import Blocks +import json + +with open('../../api_key.json', mode='r') as key_file: + key = json.loads(key_file.read())['key'] + +api = Blocks(api_key=key) +reward = api.get_block_reward(5747732) +print(reward) diff --git a/setup.py b/setup.py index f2cc69d..f3eb95a 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ name='py_etherscan_api', version='0.8.0', packages=['examples', 'examples.stats', 'examples.tokens', - 'examples.accounts', 'etherscan'], + 'examples.accounts', 'examples.blocks', 'etherscan'], url='https://github.com/corpetty/py-etherscan-api', license='MIT', author='coreypetty', From 5db2cdf885c923c43c6a1b40721677811100137c Mon Sep 17 00:00:00 2001 From: Richard Horrocks Date: Mon, 4 Feb 2019 21:26:26 +0000 Subject: [PATCH 2/3] Blocks module test and example files. --- .../blocks/Blocks Examples Notebook.ipynb | 195 ++++++++++++++++++ examples/blocks/get_block_reward.py | 2 +- tests/test_blocks.py | 17 ++ tests/test_token.py | 2 +- 4 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 examples/blocks/Blocks Examples Notebook.ipynb create mode 100644 tests/test_blocks.py diff --git a/examples/blocks/Blocks Examples Notebook.ipynb b/examples/blocks/Blocks Examples Notebook.ipynb new file mode 100644 index 0000000..866c2fc --- /dev/null +++ b/examples/blocks/Blocks Examples Notebook.ipynb @@ -0,0 +1,195 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "import etherscan.blocks as blocks\n", + "import json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import our api_key" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The JSON keyfile being read in has only one line in the format:\n", + " \n", + " {\"key\" : \"YourApiKey\" }" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "with open('../../api_key.json', mode='r') as key_file:\n", + " key = json.loads(key_file.read())['key']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up API" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "block = 2165403\n", + "api = blocks.Blocks(api_key=key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get the block reward" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "reward = api.get_block_reward(block)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'blockNumber': '2165403',\n", + " 'timeStamp': '1472533979',\n", + " 'blockMiner': '0x13a06d3dfe21e0db5c016c03ea7d2509f7f8d1e3',\n", + " 'blockReward': '5314181600000000000',\n", + " 'uncles': [{'miner': '0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1',\n", + " 'unclePosition': '0',\n", + " 'blockreward': '3750000000000000000'},\n", + " {'miner': '0x0d0c9855c722ff0c78f21e43aa275a5b8ea60dce',\n", + " 'unclePosition': '1',\n", + " 'blockreward': '3750000000000000000'}],\n", + " 'uncleInclusionReward': '312500000000000000'}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reward" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'5314181600000000000'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reward['blockReward']" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'312500000000000000'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reward['uncleInclusionReward']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.0" + }, + "latex_envs": { + "bibliofile": "biblio.bib", + "cite_by": "apalike", + "current_citInitial": 1, + "eqLabelWithNumbers": true, + "eqNumInitial": 0 + }, + "toc": { + "nav_menu": { + "height": "121px", + "width": "252px" + }, + "navigate_menu": true, + "number_sections": true, + "sideBar": true, + "threshold": 4, + "toc_cell": false, + "toc_section_display": "block", + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/blocks/get_block_reward.py b/examples/blocks/get_block_reward.py index fa57557..6d8b17a 100644 --- a/examples/blocks/get_block_reward.py +++ b/examples/blocks/get_block_reward.py @@ -5,5 +5,5 @@ key = json.loads(key_file.read())['key'] api = Blocks(api_key=key) -reward = api.get_block_reward(5747732) +reward = api.get_block_reward(2165403) print(reward) diff --git a/tests/test_blocks.py b/tests/test_blocks.py new file mode 100644 index 0000000..3729588 --- /dev/null +++ b/tests/test_blocks.py @@ -0,0 +1,17 @@ +import unittest + +from etherscan.blocks import Blocks + +BLOCK = 2165403 +BLOCK_REWARD = '5314181600000000000' +UNCLE_INCLUSION_REWARD = '312500000000000000' +API_KEY = 'YourAPIkey' + + +class BlocksTestCase(unittest.TestCase): + + def test_get_block_reward(self): + api = Blocks(api_key=(API_KEY)) + reward_object = api.get_block_reward(BLOCK) + self.assertEqual(reward_object['blockReward'], BLOCK_REWARD) + self.assertEqual(reward_object['uncleInclusionReward'], UNCLE_INCLUSION_REWARD) diff --git a/tests/test_token.py b/tests/test_token.py index 40b39b6..fd1860e 100755 --- a/tests/test_token.py +++ b/tests/test_token.py @@ -9,7 +9,7 @@ API_KEY = 'YourAPIkey' -class ProxiesTestCase(unittest.TestCase): +class TokensTestCase(unittest.TestCase): def test_get_token_supply(self): api = Tokens(contract_address=CONTRACT_ADDRESS, api_key=(API_KEY)) From 0c4576765978b41370d8fd47ae34f1ff1d77105e Mon Sep 17 00:00:00 2001 From: Richard Horrocks Date: Sat, 23 Feb 2019 20:36:25 +0000 Subject: [PATCH 3/3] Addition of Transactions module --- README.md | 1 + etherscan/transactions.py | 21 +++ examples/blocks/__init__.py | 1 + .../Transactions Examples Notebook.ipynb | 175 ++++++++++++++++++ examples/transactions/__init__.py | 1 + examples/transactions/get_status.py | 10 + .../transactions/get_tx_receipt_status.py | 10 + setup.py | 2 +- tests/test_transactions.py | 21 +++ 9 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 etherscan/transactions.py create mode 100644 examples/transactions/Transactions Examples Notebook.ipynb create mode 100644 examples/transactions/__init__.py create mode 100644 examples/transactions/get_status.py create mode 100644 examples/transactions/get_tx_receipt_status.py create mode 100644 tests/test_transactions.py diff --git a/README.md b/README.md index 526ef43..d53fd3d 100755 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Currently, only the following Etherscan.io API modules are available: - tokens - proxies - blocks +- transactions The remaining available modules provided by Etherscan.io will be added eventually... diff --git a/etherscan/transactions.py b/etherscan/transactions.py new file mode 100644 index 0000000..d6b268b --- /dev/null +++ b/etherscan/transactions.py @@ -0,0 +1,21 @@ +from .client import Client + + +class Transactions(Client): + def __init__(self, api_key='YourApiKeyToken'): + Client.__init__(self, address='', api_key=api_key) + self.url_dict[self.MODULE] = 'transaction' + + def get_status(self, tx_hash: str): + self.url_dict[self.ACTION] = 'getstatus' + self.url_dict[self.TXHASH] = tx_hash + self.build_url() + req = self.connect() + return req['result'] + + def get_tx_receipt_status(self, tx_hash: str): + self.url_dict[self.ACTION] = 'gettxreceiptstatus' + self.url_dict[self.TXHASH] = tx_hash + self.build_url() + req = self.connect() + return req['result'] diff --git a/examples/blocks/__init__.py b/examples/blocks/__init__.py index e69de29..9900b50 100644 --- a/examples/blocks/__init__.py +++ b/examples/blocks/__init__.py @@ -0,0 +1 @@ +__author__ = 'Corey Petty' diff --git a/examples/transactions/Transactions Examples Notebook.ipynb b/examples/transactions/Transactions Examples Notebook.ipynb new file mode 100644 index 0000000..9485f6c --- /dev/null +++ b/examples/transactions/Transactions Examples Notebook.ipynb @@ -0,0 +1,175 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "import etherscan.transactions as transactions\n", + "import json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import our api_key" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The JSON keyfile being read in has only one line in the format:\n", + " \n", + " {\"key\" : \"YourApiKey\" }" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "with open('../../api_key.json', mode='r') as key_file:\n", + " key = json.loads(key_file.read())['key']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up API" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "api = transactions.Transactions(api_key=key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get the transaction status and transaction receipt status" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "TX_HASH = '0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a'\n", + "status = api.get_status(tx_hash=TX_HASH)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'isError': '1', 'errDescription': 'Bad jump destination'}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "status" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "TX_HASH = '0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76'\n", + "receipt_status = api.get_tx_receipt_status(tx_hash=TX_HASH)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'status': '1'}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "receipt_status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.0" + }, + "latex_envs": { + "bibliofile": "biblio.bib", + "cite_by": "apalike", + "current_citInitial": 1, + "eqLabelWithNumbers": true, + "eqNumInitial": 0 + }, + "toc": { + "nav_menu": { + "height": "121px", + "width": "252px" + }, + "navigate_menu": true, + "number_sections": true, + "sideBar": true, + "threshold": 4, + "toc_cell": false, + "toc_section_display": "block", + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/transactions/__init__.py b/examples/transactions/__init__.py new file mode 100644 index 0000000..9900b50 --- /dev/null +++ b/examples/transactions/__init__.py @@ -0,0 +1 @@ +__author__ = 'Corey Petty' diff --git a/examples/transactions/get_status.py b/examples/transactions/get_status.py new file mode 100644 index 0000000..5ef8abf --- /dev/null +++ b/examples/transactions/get_status.py @@ -0,0 +1,10 @@ +from etherscan.transactions import Transactions +import json + +with open('../../api_key.json', mode='r') as key_file: + key = json.loads(key_file.read())['key'] + +TX_HASH = '0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a' +api = Transactions(api_key=key) +status = api.get_status(tx_hash=TX_HASH) +print(status) diff --git a/examples/transactions/get_tx_receipt_status.py b/examples/transactions/get_tx_receipt_status.py new file mode 100644 index 0000000..3bd2ffb --- /dev/null +++ b/examples/transactions/get_tx_receipt_status.py @@ -0,0 +1,10 @@ +from etherscan.transactions import Transactions +import json + +with open('../../api_key.json', mode='r') as key_file: + key = json.loads(key_file.read())['key'] + +TX_HASH = '0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76' +api = Transactions(api_key=key) +receipt_status = api.get_tx_receipt_status(tx_hash=TX_HASH) +print(receipt_status) diff --git a/setup.py b/setup.py index f3eb95a..a547046 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ name='py_etherscan_api', version='0.8.0', packages=['examples', 'examples.stats', 'examples.tokens', - 'examples.accounts', 'examples.blocks', 'etherscan'], + 'examples.accounts', 'examples.blocks', 'examples.transactions', 'etherscan'], url='https://github.com/corpetty/py-etherscan-api', license='MIT', author='coreypetty', diff --git a/tests/test_transactions.py b/tests/test_transactions.py new file mode 100644 index 0000000..eceed77 --- /dev/null +++ b/tests/test_transactions.py @@ -0,0 +1,21 @@ +import unittest + +from etherscan.transactions import Transactions + +API_KEY = 'YourAPIkey' +TX_HASH_1 = '0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a' +TX_HASH_2 = '0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76' +ERROR_STRING = 'Bad jump destination' + +class TransactionsTestCase(unittest.TestCase): + + def test_get_status(self): + api = Transactions(api_key=(API_KEY)) + status = api.get_status(TX_HASH_1) + self.assertEqual(status['isError'], '1') + self.assertEqual(status['errDescription'], ERROR_STRING) + + def test_get_tx_receipt_status(self): + api = Transactions(api_key=(API_KEY)) + receipt_status = api.get_tx_receipt_status(TX_HASH_2) + self.assertEqual(receipt_status['status'], '1')