diff --git a/.gitignore b/.gitignore index 0af19e5..210a6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ # Temporary data .ipynb_checkpoints/ /examples/stats/.ipynb_checkpoints +/.cache/ diff --git a/etherscan/__pycache__/__init__.cpython-36.pyc b/etherscan/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..669c23a Binary files /dev/null and b/etherscan/__pycache__/__init__.cpython-36.pyc differ diff --git a/etherscan/__pycache__/accounts.cpython-36.pyc b/etherscan/__pycache__/accounts.cpython-36.pyc new file mode 100644 index 0000000..09172e5 Binary files /dev/null and b/etherscan/__pycache__/accounts.cpython-36.pyc differ diff --git a/etherscan/__pycache__/client.cpython-36.pyc b/etherscan/__pycache__/client.cpython-36.pyc new file mode 100644 index 0000000..57ec057 Binary files /dev/null and b/etherscan/__pycache__/client.cpython-36.pyc differ diff --git a/etherscan/__pycache__/contracts.cpython-36.pyc b/etherscan/__pycache__/contracts.cpython-36.pyc new file mode 100644 index 0000000..7e36b42 Binary files /dev/null and b/etherscan/__pycache__/contracts.cpython-36.pyc differ diff --git a/etherscan/__pycache__/stats.cpython-36.pyc b/etherscan/__pycache__/stats.cpython-36.pyc new file mode 100644 index 0000000..836fd7b Binary files /dev/null and b/etherscan/__pycache__/stats.cpython-36.pyc differ diff --git a/etherscan/__pycache__/tokens.cpython-36.pyc b/etherscan/__pycache__/tokens.cpython-36.pyc new file mode 100644 index 0000000..546cd08 Binary files /dev/null and b/etherscan/__pycache__/tokens.cpython-36.pyc differ diff --git a/etherscan/accounts.py b/etherscan/accounts.py index 72231f8..78eb8e0 100644 --- a/etherscan/accounts.py +++ b/etherscan/accounts.py @@ -5,45 +5,19 @@ class Account(Client): def __init__(self, address=Client.dao_address, api_key='YourApiKeyToken'): Client.__init__(self, address=address, api_key=api_key) - self.module = self.URL_BASES['module'] + 'account' - - def make_url(self, call_type=''): - if call_type == 'balance': - self.url = self.URL_BASES['prefix'] \ - + self.module \ - + self.action \ - + self.address \ - + self.tag \ - + self.key - elif call_type == 'transactions': - self.url = self.URL_BASES['prefix'] \ - + self.module \ - + self.action \ - + self.address \ - + self.offset \ - + self.page \ - + self.key - elif call_type == 'blocks': - self.url = self.URL_BASES['prefix'] \ - + self.module \ - + self.action \ - + self.address \ - + self.blocktype \ - + self.offset \ - + self.page \ - + self.key + self.url_dict[self.MODULE] = 'account' def get_balance(self): - self.action = self.URL_BASES['action'] + 'balance' - self.tag = self.URL_BASES['tag'] + 'latest' - self.make_url(call_type='transactions') + self.url_dict[self.ACTION] = 'balance' + self.url_dict[self.TAG] = 'latest' + self.build_url() req = self.connect() return req['result'] def get_balance_multiple(self): - self.action = self.URL_BASES['action'] + 'balancemulti' - self.tag = self.URL_BASES['tag'] + 'latest' - self.make_url(call_type='balance') + self.url_dict[self.ACTION] = 'balancemulti' + self.url_dict[self.TAG] = 'latest' + self.build_url() req = self.connect() return req['result'] @@ -78,29 +52,29 @@ def get_transaction_page(self, page=1, offset=10000, sort='asc', internal=False) False -> (default) get normal external transactions """ if internal: - self.action = self.URL_BASES['action'] + 'txlistinternal' + self.url_dict[self.ACTION] = 'txlistinternal' else: - self.action = self.URL_BASES['action'] + 'txlist' - self.page = self.URL_BASES['page'] + str(page) - self.offset = self.URL_BASES['offset'] + str(offset) - self.sort = self.URL_BASES['sort'] + sort - self.make_url(call_type='transactions') + self.url_dict[self.ACTION] = 'txlist' + self.url_dict[self.PAGE] = str(page) + self.url_dict[self.OFFSET] = str(offset) + self.url_dict[self.SORT] = sort + self.build_url() req = self.connect() return req['result'] def get_all_transactions(self, offset=10000, sort='asc', internal=False) -> list: if internal: - self.action = self.URL_BASES['action'] + 'txlistinternal' + self.url_dict[self.ACTION] = 'txlistinternal' else: - self.action = self.URL_BASES['action'] + 'txlist' - self.page = self.URL_BASES['page'] + str(1) - self.offset = self.URL_BASES['offset'] + str(offset) - self.sort = self.URL_BASES['sort'] + sort - self.make_url(call_type='transactions') + self.url_dict[self.ACTION] = 'txlist' + self.url_dict[self.PAGE] = str(1) + self.url_dict[self.OFFSET] = str(offset) + self.url_dict[self.SORT] = sort + self.build_url() trans_list = [] while True: - self.make_url(call_type='transactions') + self.build_url() req = self.connect() if "No transactions found" in req['message']: print("Total number of transactions: {}".format(len(trans_list))) @@ -109,9 +83,9 @@ def get_all_transactions(self, offset=10000, sort='asc', internal=False) -> list else: trans_list += req['result'] # Find any character block that is a integer of any length - page_number = re.findall(r'[1-9](?:\d{0,2})(?:,\d{3})*(?:\.\d*[1-9])?|0?\.\d*[1-9]|0', self.page) + page_number = re.findall(r'[1-9](?:\d{0,2})(?:,\d{3})*(?:\.\d*[1-9])?|0?\.\d*[1-9]|0', self.url_dict[self.PAGE]) print("page {} added".format(page_number[0])) - self.page = self.URL_BASES['page'] + str(int(page_number[0]) + 1) + self.url_dict[self.PAGE] = str(int(page_number[0]) + 1) def get_blocks_mined_page(self, blocktype='blocks', page=1, offset=10000) -> list: """ @@ -124,22 +98,22 @@ def get_blocks_mined_page(self, blocktype='blocks', page=1, offset=10000) -> lis 'blocks' -> full blocks only 'uncles' -> uncles only """ - self.action = self.URL_BASES['action'] + 'getminedblocks' - self.blocktype = self.URL_BASES['blocktype'] + blocktype - self.page = self.URL_BASES['page'] + str(page) - self.offset = self.URL_BASES['offset'] + str(offset) - self.make_url(call_type='blocks') + self.url_dict[self.ACTION] = 'getminedblocks' + self.url_dict[self.BLOCK_TYPE] = blocktype + self.url_dict[self.PAGE] = str(page) + self.url_dict[self.OFFSET] = str(offset) + self.build_url() req = self.connect() return req['result'] def get_all_blocks_mined(self, blocktype='blocks', offset=10000) -> list: - self.action = self.URL_BASES['action'] + 'getminedblocks' - self.blocktype = self.URL_BASES['blocktype'] + blocktype - self.page = self.URL_BASES['page'] + str(1) - self.offset = self.URL_BASES['offset'] + str(offset) + self.url_dict[self.ACTION] = 'getminedblocks' + self.url_dict[self.BLOCK_TYPE] = blocktype + self.url_dict[self.PAGE] = str(1) + self.url_dict[self.OFFSET] = str(offset) blocks_list = [] while True: - self.make_url(call_type='blocks') + self.build_url() req = self.connect() print(req['message']) if "No transactions found" in req['message']: @@ -148,9 +122,9 @@ def get_all_blocks_mined(self, blocktype='blocks', offset=10000) -> list: else: blocks_list += req['result'] # Find any character block that is a integer of any length - page_number = re.findall(r'[1-9](?:\d{0,2})(?:,\d{3})*(?:\.\d*[1-9])?|0?\.\d*[1-9]|0', self.page) + page_number = re.findall(r'[1-9](?:\d{0,2})(?:,\d{3})*(?:\.\d*[1-9])?|0?\.\d*[1-9]|0', self.url_dict[self.PAGE]) print("page {} added".format(page_number[0])) - self.page = self.URL_BASES['page'] + str(int(page_number[0]) + 1) + self.url_dict[self.PAGE] = str(int(page_number[0]) + 1) def get_internal_by_hash(self, tx_hash=''): """ diff --git a/etherscan/client.py b/etherscan/client.py index 6bf8872..e52e560 100644 --- a/etherscan/client.py +++ b/etherscan/client.py @@ -1,44 +1,81 @@ import requests import json - +import collections # Assume user puts his API key in the api_key.json file under variable name "key" class Client(object): dao_address = '0xbb9bc244d798123fde783fcc1c72d3bb8c189413' - URL_BASES = dict( - prefix='https://api.etherscan.io/', - module='api?module=', - action='&action=', - tag='&tag=', - offset='&offset=', - page='&page=', - sort='&sort=', - blocktype='&blocktype=', - key='&apikey=', - address='&address=', - ) - def __init__(self, address, api_key='YourApiKeyToken'): + # Constants + PREFIX = 'https://api.etherscan.io/api?' + MODULE = 'module=' + ACTION = '&action=' + TOKEN_NAME = '&tokenname=' + CONTRACT_ADDRESS = '&contractaddress=' + ADDRESS = '&address=' + OFFSET = '&offset=' + PAGE = '&page=' + SORT = '&sort=' + BLOCK_TYPE = '&blocktype=' + TO = '&to=' + VALUE = '&value=' + DATA = '&data=' + POSITION = '&=' + HEX = '&hex=' + GAS_PRICE = '&gasPrice=' + GAS = '&gas=' + START_BLOCK = '&startblock=' + END_BLOCK = '&endblock=' + BLOCKNO = '&blockno=' + TXHASH = '&txhash=' + TAG = '&tag=' + BOOLEAN = '&boolean=' + INDEX = '&index=' + API_KEY = '&apikey=' + + url_dict = {} + + def __init__(self, address, api_key=''): self.http = requests.session() - self.url = '' - self.module = '' - self.action = '' - self.tag = '' - self.offset = '' - self.page = '' - self.sort = '' - self.blocktype = '' + self.url_dict = collections.OrderedDict( + { + self.MODULE: '', + self.ADDRESS: '', + self.OFFSET: '', + self.PAGE: '', + self.SORT: '', + self.BLOCK_TYPE: '', + self.TO: '', + self.VALUE: '', + self.DATA: '', + self.POSITION: '', + self.HEX: '', + self.GAS_PRICE: '', + self.GAS: '', + self.START_BLOCK: '', + self.END_BLOCK: '', + self.BLOCKNO: '', + self.TXHASH: '', + self.TAG: '', + self.BOOLEAN: '', + self.INDEX: '', + self.API_KEY: api_key, + }) - self.API_KEY = str(api_key) + # self.url_dict[API_KEY] = str(api_key) self.check_and_get_api() - self.key = self.URL_BASES['key'] + self.API_KEY + # self.key = self.URL_BASES['key'] + self.API_KEY + if (len(address) > 20) and (type(address) == list): print("Etherscan only takes 20 addresses at a time") quit() elif (type(address) == list) and (len(address) <= 20): - self.address = self.URL_BASES['address'] + ','.join(address) + self.url_dict[self.ADDRESS] = ','.join(address) else: - self.address = self.URL_BASES['address'] + address + self.url_dict[self.ADDRESS] = address + + def build_url(self): + self.url = self.PREFIX + ''.join([param + val if val else '' for param, val in self.url_dict.items()]) def connect(self): # TODO: deal with "unknown exception" error @@ -54,11 +91,11 @@ def connect(self): print("Invalid Request") exit() else: - print("problem with connection, status code: ", req.status_code) + print("Problem with connection, status code: ", req.status_code) exit() def check_and_get_api(self): - if self.API_KEY != 'YourApiKeyToken': + if self.url_dict[self.API_KEY]: # Check if api_key is empty string pass else: - self.API_KEY = input('Please type your EtherScan.io API key: ') + self.url_dict[self.API_KEY] = input('Please type your EtherScan.io API key: ') diff --git a/etherscan/contracts.py b/etherscan/contracts.py index be21fb4..28c27a2 100644 --- a/etherscan/contracts.py +++ b/etherscan/contracts.py @@ -4,18 +4,10 @@ class Contract(Client): def __init__(self, address=Client.dao_address, api_key='YourApiKeyToken'): Client.__init__(self, address=address, api_key=api_key) - self.module = self.URL_BASES['module'] + 'contract' - - def make_url(self, call_type=''): - if call_type == 'getabi': - self.url = self.URL_BASES['prefix'] \ - + self.module \ - + self.action \ - + self.address \ - + self.key + self.url_dict[self.MODULE] = 'contract' def get_abi(self): - self.action = self.URL_BASES['action'] + 'getabi' - self.make_url(call_type='getabi') + self.url_dict[self.ACTION] = 'getabi' + self.build_url() req = self.connect() - return req['result'] \ No newline at end of file + return req['result'] diff --git a/etherscan/stats.py b/etherscan/stats.py index 6fe74f2..1d61ecb 100644 --- a/etherscan/stats.py +++ b/etherscan/stats.py @@ -4,23 +4,16 @@ class Stats(Client): def __init__(self, api_key='YourApiKeyToken'): Client.__init__(self, address='', api_key=api_key) - self.module = self.URL_BASES['module'] + 'stats' - - def make_url(self, call_type=''): - if call_type == 'stats': - self.url = self.URL_BASES['prefix'] \ - + self.module \ - + self.action \ - + self.key + self.url_dict[self.MODULE] = 'stats' def get_total_ether_supply(self): - self.action = self.URL_BASES['action'] + 'ethsupply' - self.make_url(call_type='stats') + self.url_dict[self.ACTION] = 'ethsupply' + self.build_url() req = self.connect() return req['result'] def get_ether_last_price(self): - self.action = self.URL_BASES['action'] + 'ethprice' - self.make_url(call_type='stats') + self.url_dict[self.ACTION] = 'ethprice' + self.build_url() req = self.connect() return req['result'] diff --git a/etherscan/tokens.py b/etherscan/tokens.py index 195b788..43087d8 100644 --- a/etherscan/tokens.py +++ b/etherscan/tokens.py @@ -4,34 +4,19 @@ class Tokens(Client): def __init__(self, tokenname='TheDAO', api_key='YourApiKeyToken'): Client.__init__(self, address='', api_key=api_key) - self.tokenname = '&tokenname=' + tokenname - - def make_url(self, call_type=''): - if call_type == 'tokensupply': - self.url = self.URL_BASES['prefix'] \ - + self.module \ - + self.action \ - + self.tokenname \ - + self.key - elif call_type == 'tokenbalance': - self.url = self.URL_BASES['prefix'] \ - + self.module \ - + self.action \ - + self.tokenname \ - + self.address \ - + self.key + self.url_dict[self.TOKEN_NAME] = tokenname def get_total_supply(self): - self.action = self.URL_BASES['action'] + 'tokensupply' - self.module = self.URL_BASES['module'] + 'stats' - self.make_url(call_type='tokensupply') + self.url_dict[self.ACTION] = 'tokensupply' + self.url_dict[self.MODULE] = 'stats' + self.build_url() req = self.connect() return req['result'] def get_token_balance(self, address): - self.address = self.URL_BASES['address'] + address - self.module = self.URL_BASES['module'] + 'account' - self.action = self.URL_BASES['action'] + 'tokenbalance' - self.make_url(call_type='tokenbalance') + self.url_dict[self.ADDRESS] = address + self.url_dict[self.MODULE] = 'account' + self.url_dict[self.ACTION] = 'tokenbalance' + self.build_url() req = self.connect() return req['result']