Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f31036b
Fix the test case bugs for now, not the robust way.
adamzhang1987 Dec 10, 2021
9a3accf
Merge pull request #113 from adamzhang1987/feature-new-modules-support
corpetty Dec 22, 2021
c8f1fad
Update
Rose2161 Oct 21, 2023
e955637
Update __init__.py
Rose2161 Oct 21, 2023
9dab028
Update setup.py
Rose2161 Oct 21, 2023
c69c531
Update etherscan
Rose2161 Oct 21, 2023
3bb789f
Update README.md
Rose2161 Oct 21, 2023
4f15e67
Update __init__.py
Rose2161 Oct 21, 2023
bab794c
Update test_transactions.py
Rose2161 Oct 21, 2023
5a0bc10
Update api_key.json
Rose2161 Dec 28, 2023
1dd93fa
Update __init__.py
Rose2161 Apr 24, 2024
db2947c
fix: pip-requirements.txt to reduce vulnerabilities (#7)
Rose2161 May 29, 2024
59652e6
Delete examples/__init__.py
Rose2161 Jun 4, 2024
dfbdee5
Update test_transactions.py
Rose2161 Dec 3, 2024
d9252bc
Update api_key.json
Rose2161 Dec 3, 2024
6af2f5b
Update FUNDING.yml
Rose2161 Dec 3, 2024
980de3f
Create workflows
Rose2161 Jul 27, 2025
6ccf867
Delete .github/workflows
Rose2161 Jul 27, 2025
53dcc24
Create python-publish.yaml
Rose2161 Jul 27, 2025
137d08a
Create python-publish.yaml
Rose2161 Jul 27, 2025
ed2eb08
Create config.yml
Rose2161 Jul 27, 2025
a24e319
Update __init__.py
Rose2161 Jul 27, 2025
79c69ad
Update get_total_supply.py
Rose2161 Jul 27, 2025
5224ceb
Update get_token_balance.py
Rose2161 Jul 27, 2025
f6e54e1
Update api_key.json
Rose2161 Jul 27, 2025
fc9f619
Update api_key.json
Rose2161 Jul 27, 2025
9b9ff4f
Update api_key.json
Rose2161 Aug 18, 2025
71a8135
Update api_key.json
Rose2161 Aug 18, 2025
e88842c
Update __init__.py
Rose2161 Aug 18, 2025
570f4a1
Update api_key.json
Rose2161 Aug 18, 2025
83e4be2
Update api_key.json
Rose2161 Aug 18, 2025
59e5365
Update __init__.py
Rose2161 Oct 3, 2025
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
31 changes: 31 additions & 0 deletions . circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 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:
python-version: '3.x'
- 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/*
12 changes: 2 additions & 10 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
# 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
open_collective: Cryptob3auty
30 changes: 30 additions & 0 deletions .github/python-publish.yaml
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/*
31 changes: 31 additions & 0 deletions .github/workflows/python-publish.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 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:
python-version: '3.x'
- 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/*
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# py-etherscan-api module
in# py-etherscan-api module

[![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)

Expand Down 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,8 @@ 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 unit test suite
- Add request throttling based on Etherscan's suggestions

## Holla at ya' boy

BTC: 16Ny72US78VEjL5GUinSAavDwARb8dXWKG

ETH: 0x5E8047fc033499BD5d8C463ADb29f10f11165ed0
4 changes: 2 additions & 2 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__author__ = 'Corey Petty'
name = "py-etherscan-api"
contributor= 'Aisha Redl-Sherwood'
name = "mypy-etherscan-api, forked from "Core Petty's, 'py-etherscan-api'"
1 change: 1 addition & 0 deletions api_key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "key" : "${{secrets.API_KEY}}" }
2 changes: 1 addition & 1 deletion etherscan/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__author__ = 'Corey Petty'
__author__ = 'Aisha Redl-Sherwood'
1 change: 1 addition & 0 deletions etherscan/api_key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "key" : "${{secrets.API_KEY}}" }
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']
1 change: 0 additions & 1 deletion examples/__init__.py

This file was deleted.

2 changes: 1 addition & 1 deletion examples/accounts/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__author__ = 'Corey Petty'
__author__ = 'Aisha Redl'
4 changes: 2 additions & 2 deletions examples/tokens/get_token_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
with open('../../api_key.json', mode='r') as key_file:
key = json.loads(key_file.read())['key']

address = '0xe04f27eb70e025b78871a2ad7eabe85e61212761'
api = Tokens(contract_address='0x57d90b64a1a57749b0f932f1a3395792e12e7055',
address = '0x4f19f6f747f43a3d9fcf8bb7d2214e798cd2ece8'
api = Tokens(contract_address='0xdAC17F958D2ee523a2206206994597C13D831ec7',
api_key=key)
balance = api.get_token_balance(address=address)
print(balance)
2 changes: 1 addition & 1 deletion examples/tokens/get_total_supply.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
# DGD
# MKR
# TheDAO
api = Tokens(tokenname='SNT', api_key=key)
api = Tokens(tokenname='MKR', api_key=key)
supply = api.get_total_supply()
print(supply)
2 changes: 1 addition & 1 deletion pip-requirements.txt
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
requests>=2.20.0
requests>=2.32.0
typing==3.6.4
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

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',
license='MIT',
author='coreypetty',
author_email='petty.btc@gmail.com',
url='https://github.com/rose2161/py-etherscan-api',
license='Gnup',
author='aisha redl',
author_email='aisharose2161@gmail.com',
description='Python Bindings to Etherscan.io API',
install_requires=[
'requests>=2.20.0',
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
Loading