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

Rose2161 patch 1 #124

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
13 changes: 3 additions & 10 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL
github: [Rose2161, cryptob3auty]
open_collective: cryptob3auty
tidelift: npm/cryptob3auty
22 changes: 22 additions & 0 deletions .github/workflows/Myrust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Myrust

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

env:
CARGO_TERM_COLOR: always

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
30 changes: 30 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

name: Upload Python Package

on:
release:
types: [created]

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Currently, only the following Etherscan.io API modules are available:
- proxies
- blocks
- transactions
- Logs
- Gas Tracker

The remaining available modules provided by Etherscan.io will be added eventually...

Expand All @@ -58,15 +60,11 @@ 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
- Add robust documentation
- Add robust
- Add unit test suite
- Add request throttling based on Etherscan's suggestions

## Holla at ya' boy

BTC: 16Ny72US78VEjL5GUinSAavDwARb8dXWKG

ETH: 0x5E8047fc033499BD5d8C463ADb29f10f11165ed0
18 changes: 17 additions & 1 deletion etherscan/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class BadRequest(ClientException):
"""Invalid request passed"""


class InvalidAPIKey(ClientException):
"""Invalid API key"""


# Assume user puts his API key in the api_key.json
# file under variable name "key"
class Client(object):
Expand Down Expand Up @@ -59,6 +63,11 @@ class Client(object):
TAG = '&tag='
BOOLEAN = '&boolean='
INDEX = '&index='
FROM_BLOCK = '&fromBlock='
TO_BLOCK = '&toBlock='
TOPIC0 = '&topic0='
TOPIC0_1_OPR = '&topic0_1_opr='
TOPIC1 = '&topic1='
API_KEY = '&apikey='

url_dict = {}
Expand Down Expand Up @@ -86,7 +95,12 @@ def __init__(self, address, api_key=''):
(self.TAG, ''),
(self.BOOLEAN, ''),
(self.INDEX, ''),
(self.API_KEY, api_key)])
(self.API_KEY, api_key),
(self.FROM_BLOCK, ''),
(self.TO_BLOCK, ''),
(self.TOPIC0, ''),
(self.TOPIC0_1_OPR, ''),
(self.TOPIC1, '')])

# Var initialization should take place within init
self.url = None
Expand Down Expand Up @@ -119,6 +133,8 @@ def connect(self):
status = data.get('status')
if status == '1' or self.check_keys_api(data):
return data
elif status == '0' and data.get('result') == "Invalid API Key":
raise InvalidAPIKey(data.get('result'))
else:
raise EmptyResponse(data.get('message', 'no message'))
raise BadRequest(
Expand Down
44 changes: 44 additions & 0 deletions etherscan/gas_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from .client import Client


class GasTrackerException(Exception):
"""Base class for exceptions in this module."""
pass


class GasTracker(Client):
def __init__(self, api_key='YourApiKeyToken'):
Client.__init__(self, address='', api_key=api_key)
self.url_dict[self.MODULE] = 'gastracker'

def get_estimation_of_confirmation_time(self, gas_price: str) -> str:
"""
Returns the estimated time, in seconds, for a transaction to be confirmed on the blockchain.
Args:
gas_price (str): the price paid per unit of gas, in wei
Returns:
str: The result is returned in seconds.
"""
self.url_dict[self.ACTION] = 'gasestimate'
self.url_dict[self.GAS_PRICE] = gas_price
self.build_url()
req = self.connect()
return req['result']

def get_gas_oracle(self) -> dict:
"""
Returns the current Safe, Proposed and Fast gas prices.
Returns:
dict: The gas prices are returned in Gwei.
"""
self.url_dict[self.ACTION] = 'gasoracle'
self.build_url()
req = self.connect()
return req['result']

def get_daily_average_gas_limit(self, start_date, end_date) -> list:
# TODO API Pro
pass
48 changes: 48 additions & 0 deletions etherscan/logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from .client import Client


class LogsException(Exception):
"""Base class for exceptions in this module."""
pass


class Logs(Client):
"""
The Event Log API was designed to provide an alternative to the native eth_getLogs.
"""
def __init__(self, api_key='YourApiKeyToken'):
Client.__init__(self, address='', api_key=api_key)
self.url_dict[self.MODULE] = 'logs'

def get_logs(self, from_block: str, to_block='latest',
topic0='', topic1='', topic0_1_opr='and',) -> list:
"""
Get Event Logs from block number [from_block] to block [to_block] ,
where log address = [address], topic[0] = [topic0] 'AND' topic[1] = [topic1]

Args:
from_block (str): start block number
to_block (str, optional): end block number. Defaults to 'latest'.
topic0 (str, optional): Defaults to ''.
topic1 (str, optional): Defaults to ''.
topic0_1_opr (str, optional): and|or between topic0 & topic1. Defaults to 'and'.

Returns:
list: [description]
"""
# TODO: support multi topics
if not topic0 and topic1:
raise(LogsException('can not only set topic1 while topic0 is empty'))
self.url_dict[self.ACTION] = 'getLogs'
self.url_dict[self.FROM_BLOCK] = from_block if type(
from_block) is str else str(from_block)
self.url_dict[self.TO_BLOCK] = to_block if type(
to_block) is str else str(to_block)
self.url_dict[self.TOPIC0] = topic0 if type(
topic0) is str else hex(topic0)
self.url_dict[self.TOPIC1] = topic1 if type(
topic1) is str else hex(topic1)
self.url_dict[self.TOPIC0_1_OPR] = topic0_1_opr
self.build_url()
req = self.connect()
return req['result']
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setuptools.setup(
name='py_etherscan_api',
version='0.8.0',
version='0.9.0',
packages=['examples', 'examples.stats', 'examples.tokens',
'examples.accounts', 'examples.blocks', 'examples.transactions', 'etherscan'],
url='https://github.com/corpetty/py-etherscan-api',
Expand Down
10 changes: 7 additions & 3 deletions tests/test_accounts.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import unittest
import warnings

from etherscan.accounts import Account

SINGLE_BALANCE = '40807178566070000000000'
SINGLE_BALANCE = '40891626854930000000000'
SINGLE_ACCOUNT = '0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a'
MULTI_ACCOUNT = [
'0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a',
'0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a',
]
MULTI_BALANCE = [
{'account': '0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a',
'balance': '40807178566070000000000'},
'balance': '40891626854930000000000'},
{'account': '0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a',
'balance': '40807178566070000000000'}
'balance': '40891626854930000000000'}
]
API_KEY = 'YourAPIkey'


class AccountsTestCase(unittest.TestCase):

def setUp(self):
warnings.simplefilter('ignore', ResourceWarning)

def test_get_balance(self):
api = Account(address=SINGLE_ACCOUNT, api_key=API_KEY)
self.assertEqual(api.get_balance(), SINGLE_BALANCE)
Expand Down
4 changes: 4 additions & 0 deletions tests/test_blocks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
import warnings

from etherscan.blocks import Blocks

Expand All @@ -10,6 +11,9 @@

class BlocksTestCase(unittest.TestCase):

def setUp(self):
warnings.simplefilter('ignore', ResourceWarning)

def test_get_block_reward(self):
api = Blocks(api_key=(API_KEY))
reward_object = api.get_block_reward(BLOCK)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_gas_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import unittest
import warnings

from etherscan.gas_tracker import GasTracker

GAS_PRICE = '2000000000'
PRICE_ORACLE_RESULT_DICT_KEYS = ("SafeGasPrice",
"ProposeGasPrice",
"FastGasPrice",
"suggestBaseFee")
API_KEY = 'YourAPIkey'


class BlocksTestCase(unittest.TestCase):

def setUp(self):
warnings.simplefilter('ignore', ResourceWarning)
self.api = GasTracker(api_key=API_KEY)

def test_get_estimation_of_confirmation_time(self):
estimated_time = self.api.get_estimation_of_confirmation_time(GAS_PRICE)
self.assertTrue(int(estimated_time) > 0)

def test_get_gas_oracle(self):
oracle_price = self.api.get_gas_oracle()
for key in PRICE_ORACLE_RESULT_DICT_KEYS:
self.assertTrue(key in oracle_price and float(oracle_price[key]) > 0)
40 changes: 40 additions & 0 deletions tests/test_logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import unittest
import warnings

from etherscan.logs import Logs, LogsException
from etherscan.client import InvalidAPIKey

FROM_BLOCK = 379224
TO_BLOCK = 400000
ADDRESS = '0x33990122638b9132ca29c723bdf037f1a891a70c'
TOPIC0 = '0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545'
TOPIC1 = '0x72657075746174696f6e00000000000000000000000000000000000000000000'
TOPIC0_1_OPR = 'and'
API_KEY = 'YourAPIkey'


class BlocksTestCase(unittest.TestCase):

def setUp(self):
warnings.simplefilter('ignore', ResourceWarning)
self.api = Logs(api_key=(API_KEY))

def test_invalid_api_key(self):
with self.assertRaises(InvalidAPIKey):
api = Logs(api_key=('invalid' + API_KEY))
api.get_logs(from_block=FROM_BLOCK, topic0=TOPIC0)

def test_get_logs_error(self):
with self.assertRaises(LogsException):
self.api.get_logs(from_block=FROM_BLOCK, topic1=TOPIC1)

def test_get_logs_one_topic(self):
topics = self.api.get_logs(from_block=FROM_BLOCK, topic0=TOPIC0)
for topic in topics:
self.assertTrue(TOPIC0 in topic.get('topics', ''))

def test_get_logs_two_topics(self):
topics = self.api.get_logs(from_block=FROM_BLOCK, topic0=TOPIC0, topic1=TOPIC1)
for topic in topics:
self.assertTrue(TOPIC0 in topic.get('topics', '')
and TOPIC1 in topic.get('topics', ''))
6 changes: 5 additions & 1 deletion tests/test_proxies.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
import unittest
import warnings

from etherscan.proxies import Proxies

Expand All @@ -23,11 +24,14 @@

class ProxiesTestCase(unittest.TestCase):

def setUp(self):
warnings.simplefilter('ignore', ResourceWarning)

def test_get_most_recent_block(self):
api = Proxies(api_key=API_KEY)
most_recent = int(api.get_most_recent_block(), 16)
print(most_recent)
p = re.compile('^[0-9]{7}$')
p = re.compile('^[0-9]{8}$')
self.assertTrue(p.match(str(most_recent)))

def test_get_block_by_number(self):
Expand Down
Loading