Skip to content

Commit

Permalink
Merge pull request #32 from EtWnn/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
EtWnn authored Jan 5, 2022
2 parents 8344975 + f5fccbe commit 43a7e15
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 27 deletions.
121 changes: 107 additions & 14 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,37 +1,54 @@
==============================
Welcome to ScanWatch 0.1.3
Welcome to ScanWatch 0.2.0
==============================


Note
----

This library is developed and maintained by EtWnn, feel free to drop your suggestions or remarks in
the discussion tab of the git repo. You are also welcome to contribute by submitting PRs.
the `discussion tab <https://github.com/EtWnn/ScanWatch/discussions>`_.
You are also welcome to contribute by submitting PRs.

**Source Code:**
https://github.com/EtWnn/ScanWatch
**Documentation:**
https://scanwatch.readthedocs.io


This library is a local tracker of transactions for the Ethereum chain and the Binance Smart chain.
It is a simple interface with the `etherscan <https://etherscan.io>`_ and the
`bscscan <https://bscscan.com>`_ APIs and will save locally the results to gain time and avoid over-calling the APIs.
| This library is a local tracker of transactions for the Ethereum chain, the Binance Smart chain and the Polygon chain.
| It is a simple single-point interface with the `etherscan <https://etherscan.io>`__, `bscscan <https://bscscan.com>`__
and `polygonscan <https://polygonscan.com>`__ APIs.
| This library will save locally the transactions to gain time and avoid over-calling the APIs.

Announcement
------------

|siren| |siren| |siren|

If you previously used this library with a version inferior to 0.1.3,
please head `here <https://github.com/EtWnn/ScanWatch/discussions/25>`_ to correct a potential bug in the database.

|siren| |siren| |siren|


Quick Tour
----------

You will need to generate an API token to use this library.
Go on `etherscan <https://etherscan.io/myapikey>`__ for the Ethereum chain and on
`bscscan <https://bscscan.com/myapikey>`__ for the BSC chain.
(If you want to use both chains, you will need an API token for each).
1. API Keys
~~~~~~~~~~~

You will need to generate API tokens to use this library:

1. Ethereum chain: go on `etherscan <https://etherscan.io/myapikey>`__
2. Binance Smart chain: go on `bscscan <https://bscscan.com/myapikey>`__
3. Polygon chain: go on `polygonscan <https://polygonscan.com/myapikey>`__

(If you want to use several chains, you will need an API token for each).

2. Installation
~~~~~~~~~~~~~~~

``ScanWatch`` is available on `PYPI <https://pypi.org/project/ScanWatch/>`_, install with ``pip``:

Expand All @@ -45,7 +62,11 @@ You can also install the latest developments (not stable):
pip install git+https://github.com/EtWnn/ScanWatch.git@develop
You can then use your API token to instantiate the manager.
3. Manager
~~~~~~~~~~

| The manager is the object that you will use to update and get the transactions.
| It is instantiated with an API token and an address.
Example for Ethereum:

Expand All @@ -71,6 +92,21 @@ Example for BSC:
manager = ScanManager(address, NETWORK.BSC, api_token)
Example for Polygon:

.. code:: python
from ScanWatch.ScanManager import ScanManager
from ScanWatch.utils.enums import NETWORK
api_token = "<POLYGON_API_TOKEN>"
address = "<YOUR_POLYGON_ADDRESS>"
manager = ScanManager(address, NETWORK.POLYGON, api_token)
4. Transactions Update
~~~~~~~~~~~~~~~~~~~~~~

Once the manager is setup, you can update the locally saved transactions:

.. code:: python
Expand All @@ -79,8 +115,12 @@ Once the manager is setup, you can update the locally saved transactions:
# all transactions updated for address 0xaAC...748E8: 100%|████████████| 4/4 [00:02<00:00, 1.86it/s]
This needs to be done only when new transactions have been made since the last time you called the update method.
Otherwise you can just fetch the transactions that have been previously saved, as shown below
(see the documentation for more details).

5. Transactions
~~~~~~~~~~~~~~~

To fetch the transactions that have been previously saved, just use the methods below.
(see the `documentation <https://scanwatch.readthedocs.io>`_ for more details).

.. code:: python
Expand All @@ -94,6 +134,54 @@ Otherwise you can just fetch the transactions that have been previously saved, a
manager.get_transactions(TRANSACTION.INTERNAL) # internal transactions
6. Holdings
~~~~~~~~~~~

The manager can also give you the current tokens hold by an address:

For erc20 tokens:

.. code:: python
manager.get_erc20_holdings()
.. code:: bash
{
'USDC': Decimal('50'),
'AllianceBlock Token': Decimal('12458.494516884'),
'Blockchain Certified Data Token': Decimal('75174'),
'Compound': Decimal('784.24998156'),
'ZRX': Decimal('3.1')
}
For erc721 tokens:

.. code:: python
manager.get_erc721_holdings()
.. code:: bash
[
{
'contractAddress': '0x8azd48c9ze46azx1e984fraz4da9zz8dssad49ct',
'tokenID': '78941',
'count': 1,
'tokenName': 'SUPER NFT GAME',
'tokenSymbol': 'Hero'
},
{
'contractAddress': '0x6edd39bdba2fazs3db5fxd86908789cbd905f04d',
'tokenID': '33001',
'count': 1,
'tokenName': 'MY FAV NFT ARTIST HANDMADE THIS',
'tokenSymbol': 'dubious thing'
}
]
Main / test nets
----------------
Expand All @@ -107,14 +195,19 @@ If you want to switch from main to test nets, you can specify the net name at th
Supported nets are:
- For Ethereum: "main", "goerli", "kovan", "rinkeby", "ropsten"
- For BSC: "main", "test"
- For Polygon: "main", "test"


Donation
--------

If this library has helped you in any way, feel free to donate:
| If this library has helped you in any way, feel free to help me |blush|
| With your donation, I will be able to keep working on this project and add new features. Thank you!
- **BTC**: 14ou4fMYoMVYbWEKnhADPJUNVytWQWx9HG
- **ETH**: 0xfb0ebcf8224ce561bfb06a56c3b9a43e1a4d1be2
- **ETH**, **BSC**, **Polygon**: 0xA20be1f02B1C9D4FF1442a0F0e7c089fcDd59407
- **LTC**: LfHgc969RFUjnmyLn41SRDvmT146jUg9tE
- **EGLD**: erd1qk98xm2hgztvmq6s4jwtk06g6laattewp6vh20z393drzy5zzfrq0gaefh

.. |siren| replace:: 🚨
.. |blush| replace:: 😊
23 changes: 14 additions & 9 deletions ScanWatch/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

class Client:
"""
Client for the etherscan.io API or the bscscan.io API
Client the API:
https://etherscan.io/apis
https://bscscan.com/apis
https://polygonscan.com/apis
"""
BASE_URLS = {
NETWORK.BSC: {
Expand All @@ -23,6 +24,10 @@ class Client:
"kovan": "https://api-kovan.etherscan.io/api",
"rinkeby": "https://api-rinkeby.etherscan.io/api",
"ropsten": "https://api-ropsten.etherscan.io/api"
},
NETWORK.POLYGON: {
"main": "https://api.polygonscan.com/api",
"test": "https://api-testnet.polygonscan.com/api"
}
}

Expand Down Expand Up @@ -62,7 +67,7 @@ def get_mined_blocks(self, address: str, start_block: Optional[int] = None, end_

def get_erc721_transactions(self, address: str, start_block: Optional[int] = None, end_block: Optional[int] = None):
"""
fetch erc721 transactions on an eth / bsc address
fetch erc721 transactions on an address
:param address: address
:type address: str
Expand All @@ -77,7 +82,7 @@ def get_erc721_transactions(self, address: str, start_block: Optional[int] = Non

def get_erc20_transactions(self, address: str, start_block: Optional[int] = None, end_block: Optional[int] = None):
"""
fetch erc20 transactions on an eth / bsc address
fetch erc20 transactions on an address
:param address: address
:type address: str
Expand Down Expand Up @@ -117,7 +122,7 @@ def get_erc20_transactions(self, address: str, start_block: Optional[int] = None

def get_normal_transactions(self, address: str, start_block: Optional[int] = None, end_block: Optional[int] = None):
"""
fetch normal transactions on an eth / bsc address
fetch normal transactions on an address
:param address: address
:type address: str
Expand Down Expand Up @@ -157,7 +162,7 @@ def get_normal_transactions(self, address: str, start_block: Optional[int] = Non
def get_internal_transactions(self, address: str, start_block: Optional[int] = None,
end_block: Optional[int] = None):
"""
fetch internal transactions on an eth / bsc address
fetch internal transactions on an address
:param address: address
:type address: str
Expand All @@ -173,7 +178,7 @@ def get_internal_transactions(self, address: str, start_block: Optional[int] = N
def _get_transactions(self, address: str, action: str, start_block: Optional[int] = None,
end_block: Optional[int] = None):
"""
fetch transactions on an eth / bsc address
fetch transactions on an address
:param address: address
:type address: str
Expand Down Expand Up @@ -208,7 +213,7 @@ def _get_transactions(self, address: str, action: str, start_block: Optional[int

def get_balance(self, address: str) -> float:
"""
fetch the current eth / bnb balance of an address
fetch the current balance of an address
:param address: address
:type address: str
Expand All @@ -224,7 +229,7 @@ def get_balance(self, address: str) -> float:

def get_url_request(self, **kwargs) -> str:
"""
Construct the url to make a request to the etherscan.io / bscscan.com API
Construct the url to make a request to the API
:param kwargs: keywords args for the endpoint
:type kwargs: Any
Expand All @@ -244,7 +249,7 @@ def get_result(url: str):
"""
call the API with an url, raise if the status is not ok and return the API result
:param url: url to request for the etherscan.io or bscscan.com
:param url: url to request
:type url: str
:return: API result
:rtype: depend of the endpoint
Expand Down
63 changes: 63 additions & 0 deletions ScanWatch/ScanManager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from decimal import Decimal
from typing import Dict, List

from tqdm import tqdm

from ScanWatch.Client import Client
Expand Down Expand Up @@ -78,3 +81,63 @@ def get_transactions(self, tr_type: TRANSACTION):
:rtype: List[Dict]
"""
return self.db.get_transactions(self.address, self.nt_type, self.net, tr_type)

def get_erc20_holdings(self) -> Dict:
"""
Return the amount of every erc20 the address holds at the last update time.
WARNING: Some tokens trigger non-erc20 events, such as internal exchange fee. This will not be picked up by
this function. As a consequence, the balance of such tokens might be wrong.
:return: a dictionary of token amount per token name
:rtype: Dict
"""
txs = self.get_transactions(TRANSACTION.ERC20)
holdings = {}
for tx in txs:
amount = Decimal(tx['value']) / Decimal(10 ** int(tx['tokenDecimal']))
if self.address.lower() == tx['from']:
amount *= -1
try:
holdings[tx['tokenName']] += amount
except KeyError:
if amount < 0:
raise ValueError(f"First operation on an asset is a removal {tx}")
holdings[tx['tokenName']] = amount
return {k: v for k, v in holdings.items() if v != 0}

def get_erc721_holdings(self) -> List[Dict]:
"""
Return the erc721 tokens that the address holds at the time of the last update
:return: List of erc721 tokens owned by the address
:rtype: List[Dict]
"""
txs = self.get_transactions(TRANSACTION.ERC721)
holdings = {}
for tx in txs:
amount = 1
if self.address.lower() == tx['from']:
amount = -1
try:
holdings[tx['contractAddress']][tx['tokenID']]['count'] += amount
except KeyError:
if amount < 0:
raise ValueError(f"First operation on an asset is a removal {tx}")
try:
holdings[tx['contractAddress']][tx['tokenID']] = {'count': amount,
'tokenName': tx['tokenName'],
'tokenSymbol': tx['tokenSymbol']}
except KeyError:
holdings[tx['contractAddress']] = {tx['tokenID']: {'count': amount,
'tokenName': tx['tokenName'],
'tokenSymbol': tx['tokenSymbol']}
}
# Present the result in a single list
result = []
for contract, nfts in holdings.items():
for token_id, nft in nfts.items():
if nft['count'] != 0:
result.append({'contractAddress': contract,
'tokenID': token_id,
**nft})
return result
2 changes: 1 addition & 1 deletion ScanWatch/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__author__ = 'EtWnn'
__version__ = '0.1.3'
__version__ = '0.2.0'
1 change: 1 addition & 0 deletions ScanWatch/utils/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
class NETWORK(Enum):
ETHER = 1
BSC = 2
POLYGON = 3


class TRANSACTION(Enum):
Expand Down
3 changes: 2 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
setuptools~=58.0.4
sphinx~=4.3.2
sphinx_rtd_theme~=1.0.0
twine~=3.7.1
twine~=3.7.1
restructuredtext-lint~=1.3.2
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
author='EtWnn',
author_email='EtWnn0x@gmail.com',
license='MIT',
description='Local tracker of an eth address for ETH and BSC scan',
description='Local tracker for address on Ethereum, BSC and Polygon chains',
long_description=long_description,
long_description_content_type='text/x-rst',
install_requires=requirements,
keywords='eth bsc wallet save tracking history ethereum tracker binance smartchain smart chain',
keywords='eth bsc polygon wallet save tracking history ethereum matic bnb tracker binance smartchain smart chain',
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
Expand Down

0 comments on commit 43a7e15

Please sign in to comment.