EthTx - Ethereum transactions decoder

Overview

EthTx - Ethereum transactions decoder

Python Black OpenSource Apache

Installation

pip install ethtx

Requirements

The package needs a few external resources, defined in EthTxConfig object:

  1. Geth node - required to have access to the raw Ethereum data; it must be a full archive node with the debug option ON
  2. MongoDB database - required to store smart contracts' ABI and semantics used in the decoding process; it can be a Mongo Atlas or local instance
  3. Etherscan API key - required to get the source code and ABI for smart contracts used in transaction

Getting started

from ethtx import EthTx, EthTxConfig
from ethtx.models.decoded_model import DecodedTransaction


ethtx_config = EthTxConfig(
    mongo_connection_string= ##MongoDB connection string,
    mongo_database= ##MongoDB database,
    etherscan_api_key= ##Etherscan API key,
    web3nodes={
        "mainnet": ##Geth archive node URL,
    },
    default_chain="mainnet",
    etherscan_urls={
        "mainnet": "https://api.etherscan.io/api",
    },
)

ethtx = EthTx.initialize(ethtx_config)
transaction: DecodedTransaction = ethtx.decoders.decode_transaction('0x50051e0a6f216ab9484c2080001c7e12d5138250acee1f4b7c725b8fb6bb922d')

Features

EthTx most important functions:

  1. raw node data access:
ethtx = EthTx.initialize(ethtx_config)
web3provider = ethtx.providers.web3provider

from ethtx.models.w3_model import W3Transaction, W3Block, W3Receipt, W3CallTree

# read raw transaction data directly from the node
w3transaction: W3Transaction = web3provider.get_transaction('0x50051e0a6f216ab9484c2080001c7e12d5138250acee1f4b7c725b8fb6bb922d')
w3block: W3Block = web3provider.get_block(w3transaction.blockNumber)
w3receipt: W3Receipt = web3provider.get_receipt(w3transaction.hash.hex())
w3calls: W3CallTree = web3provider.get_calls(w3transaction.hash.hex()
  1. ABI decoding:
from ethtx.models.objects_model import Transaction, Event, Call
from ethtx.models.decoded_model import DecodedEvent, DecodedCall, DecodedTransfer, DecodedBalance

# read the raw transaction from the node
transaction: Transaction = web3provider.get_full_transaction('0x50051e0a6f216ab9484c2080001c7e12d5138250acee1f4b7c725b8fb6bb922d')

# decode transaction components
abi_decoded_events: List[Event] = ethtx.decoders.abi_decoder.decode_events(transaction.events, transaction.metadata)
abi_decoded_calls: DecodedCall = ethtx.decoders.abi_decoder.decode_calls(transaction.root_call, transaction.metadata)
abi_decoded_transfers: List[DecodedTransfer] = ethtx.decoders.abi_decoder.decode_transfers(abi_decoded_calls, abi_decoded_events)
abi_decoded_balances: List[DecodedBalance] = ethtx.decoders.abi_decoder.decode_balances(abi_decoded_transfers)

# decode a single event
raw_event: Event = transaction.events[3]
abi_decoded_event: DecodedEvent = ethtx.decoders.abi_decoder.decode_event(raw_event, transaction.metadata)

# decode a single call
raw_call: Call = transaction.root_call.subcalls[3].subcalls[2]
abi_decoded_call: DecodedCall = ethtx.decoders.abi_decoder.decode_call(raw_call, transaction.metadata)
  1. Semantic decoding:
from ethtx.models.decoded_model import DecodedTransactionMetadata

# get proxies used in the transaction
proxies = ethtx.decoders.get_proxies(transaction.root_call)

# semantically decode transaction components
decoded_metadata: DecodedTransactionMetadata = ethtx.decoders.semantic_decoder.decode_metadata(block.metadata, transaction.metadata)
decoded_events: List[DecodedEvent] = ethtx.decoders.semantic_decoder.decode_events(abi_decoded_events, decoded_metadata, token_proxies)
decoded_calls: Call = ethtx.decoders.semantic_decoder.decode_calls(abi_decoded_calls, decoded_metadata, proxies)
decoded_transfers: List[DecodedTransfer] = ethtx.decoders.semantic_decoder.decode_transfers(abi_decoded_transfers)
decoded_balances: List[DecodedBalance] = ethtx.decoders.semantic_decoder.decode_balances(abi_decoded_balances)

# semantically decode a single event
decoded_event: DecodedEvent = ethtx.decoders.semantic_decoder.decode_event(abi_decoded_events[0], decoded_metadata, proxies)
# semantically decode a single call
decoded_call: Call = ethtx.decoders.semantic_decoder.decode_call(abi_decoded_calls.subcalls[2].subcalls[0], decoded_metadata, proxies)
Comments
  • No module named 'ethtx.semantics'

    No module named 'ethtx.semantics'

    Windows 10. Python version 3.10.4. Latest version for ethtx is installed with pip install ethtx.

    I created a file test.py with the following content:

    from ethtx import EthTx, EthTxConfig
    from ethtx.models.decoded_model import DecodedTransaction
    
    ethtx_config = EthTxConfig(
        etherscan_api_key="my etherscan API key",  ##Etherscan API key,
        web3nodes={
            "mainnet": {
                "hook": "my Infura url",  # multiple nodes supported, separate them with comma
                "poa": False  # represented by bool value
            }
        },
        default_chain="mainnet",
        etherscan_urls={"mainnet": "https://api.etherscan.io/api" }
    )
    
    ethtx = EthTx.initialize(ethtx_config)
    transaction: DecodedTransaction = ethtx.decoders.decode_transaction(
        '0x50051e0a6f216ab9484c2080001c7e12d5138250acee1f4b7c725b8fb6bb922d')
    

    Once I try to execute the file with py test.py, I get the following error:

    Traceback (most recent call last):
      File "C:\Users\Utente\Desktop\ethtx\test.py", line 1, in <module>
        from ethtx import EthTx, EthTxConfig
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\__init__.py", line 13, in <module>
        from .ethtx import EthTx, EthTxConfig
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\ethtx.py", line 18, in <module>
        from .decoders.abi.decoder import ABIDecoder
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\decoders\abi\decoder.py", line 32, in <module>
        from .abc import IABIDecoder
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\decoders\abi\abc.py", line 25, in <module>
        from ethtx.providers.semantic_providers import SemanticsRepository
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\providers\semantic_providers\__init__.py", line 15, in <module>
        from .repository import SemanticsRepository
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\providers\semantic_providers\repository.py", line 30, in <module>
        from ethtx.semantics.protocols_router import amend_contract_semantics
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\semantics\protocols_router.py", line 21, in <module>
        def amend_contract_semantics(semantics: ContractSemantics, router_=Router()):
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\semantics\router.py", line 30, in __new__
        return cls._get_semantics()
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\site-packages\ethtx\semantics\router.py", line 52, in _get_semantics
        imported_module = importlib.import_module(
      File "C:\Users\Utente\AppData\Local\Programs\Python\Python310\lib\importlib\__init__.py", line 126, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
    ModuleNotFoundError: No module named 'ethtx.semanticsC:\\Users\\Utente\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site-packages\\ethtx\\semantics\\base'
    

    What's the issue? Thanks for the help.

    opened by yuripaoloni 10
  • from_address is none

    from_address is none

    When I run get start script,I get error: Traceback (most recent call last): File "Code/python/Blockchain/eth_trans_decode.py", line 23, in transaction: DecodedTransaction = ethtx.decoders.decode_transaction( File "/opt/anaconda3/envs/py3.8/lib/python3.8/site-packages/ethtx/ethtx.py", line 67, in decode_transaction return self._decoder_service.decode_transaction(chain_id, tx_hash) File "/opt/anaconda3/envs/py3.8/lib/python3.8/site-packages/ethtx/decoders/decoder_service.py", line 47, in decode_transaction transaction = self.web3provider.get_full_transaction( File "/opt/anaconda3/envs/py3.8/lib/python3.8/site-packages/ethtx/providers/web3_provider.py", line 466, in get_full_transaction w3calltree = self.get_calls(tx_hash, chain_id) File "/opt/anaconda3/envs/py3.8/lib/python3.8/site-packages/ethtx/providers/web3_provider.py", line 255, in get_calls return self._create_call_from_debug_trace_tx( File "/opt/anaconda3/envs/py3.8/lib/python3.8/site-packages/ethtx/providers/web3_provider.py", line 490, in _create_call_from_debug_trace_tx main_parent = W3CallTree(tx_hash=tx_hash, chain_id=chain_id, **w3input) File "pydantic/main.py", line 406, in pydantic.main.BaseModel.init pydantic.error_wrappers.ValidationError: 2 validation errors for W3CallTree type field required (type=value_error.missing) from_address none is not an allowed value (type=type_error.none.not_allowed)

    When debug, I found from_address is set to none in ethtx/providers/web3_provider.py , 479 line.

    I have no idea why? And I print the debug_transaction response , it dose not contain from_address , either.

    bug 
    opened by wulasite 10
  • OverflowError: integer division result too large for a float in calls.py L194

    OverflowError: integer division result too large for a float in calls.py L194

    When I install and run starter demo, I put a tx hash : 0x4f378f91bf16a26cff104aea5eceaa66c72d128dade84ddb619d40426f9780a0 and got an error report like the output below:

    Traceback (most recent call last):
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/decoder.py", line 171, in _decode_transaction
        full_decoded_transaction.calls = self.decode_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/decoder.py", line 68, in decode_calls
        return ABICallsDecoder(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 55, in decode
        calls_tree = self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 237, in _decode_nested_calls
        self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 237, in _decode_nested_calls
        self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 237, in _decode_nested_calls
        self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 224, in _decode_nested_calls
        decoded = self.decode_call(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 194, in decode_call
        value=call.call_value / 10**18,
    OverflowError: integer division result too large for a float
    Traceback (most recent call last):
      File "ethtxdemo.py", line 18, in <module>
        transaction: DecodedTransaction = ethtx.decoders.decode_transaction('0x4f378f91bf16a26cff104aea5eceaa66c72d128dade84ddb619d40426f9780a0')
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/ethtx.py", line 67, in decode_transaction
        return self._decoder_service.decode_transaction(chain_id, tx_hash)
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/decoder_service.py", line 63, in decode_transaction
        abi_decoded_tx = self.abi_decoder.decode_transaction(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/decoder.py", line 54, in decode_transaction
        full_decoded_transaction = self._decode_transaction(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/decoder.py", line 180, in _decode_transaction
        raise e
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/decoder.py", line 171, in _decode_transaction
        full_decoded_transaction.calls = self.decode_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/decoder.py", line 68, in decode_calls
        return ABICallsDecoder(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 55, in decode
        calls_tree = self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 237, in _decode_nested_calls
        self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 237, in _decode_nested_calls
        self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 237, in _decode_nested_calls
        self._decode_nested_calls(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 224, in _decode_nested_calls
        decoded = self.decode_call(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/ethtx/decoders/abi/calls.py", line 194, in decode_call
        value=call.call_value / 10**18,
    OverflowError: integer division result too large for a float
    

    I think maybe L194

    value=call.call_value / 10**18,
    

    could be replaced by

    value=Decimal(call.call_value) / 10**18,
    

    and a import should also be added.

    I will push a PR soon

    bug 
    opened by hitdavid 9
  • Getting started script not working

    Getting started script not working

    Just discovered this repo. Really interested to test it out.

    However, when I tried to run the 'Getting started1' script in README, I came across a few errors:

    Error 1:

    File "test.py", line 6
        mongo_database = "mongomock://localhost"
        ^
    SyntaxError: invalid syntax
    

    I got passed this by adding commas before each inline comment. Then I got another error:

    Error 2:

    Traceback (most recent call last):
      File "test.py", line 4, in <module>
        ethtx_config = EthTxConfig(
    TypeError: __init__() got an unexpected keyword argument 'mongo_database'
    

    I looked into ethtx.py and didn't see any mongo_database attribute. So I commented the mongo_database line out. Then I got another error:

    Error 3:

    Traceback (most recent call last):
      File "test.py", line 18, in <module>
        ethtx = EthTx.initialize(ethtx_config)
      File "/home/lsa/anaconda3/lib/python3.8/site-packages/ethtx/ethtx.py", line 122, in initialize
        repository = MongoSemanticsDatabase(db=mongo_client.get_database())
      File "/home/lsa/anaconda3/lib/python3.8/site-packages/mongomock/mongo_client.py", line 133, in get_database
        db = self.get_default_database(
      File "/home/lsa/anaconda3/lib/python3.8/site-packages/mongomock/mongo_client.py", line 151, in get_default_database
        raise ConfigurationError('No default database name defined or provided.')
    pymongo.errors.ConfigurationError: No default database name defined or provided.
    

    Can you advise if I am doing something incorrectly? Thanks.

    bug documentation 
    opened by 0x1355 5
  • Account balances not adding up for Eth for USDT transaction on UniswapV3

    Account balances not adding up for Eth for USDT transaction on UniswapV3

    So it seems that there is some bug with how multicalls are being handled.

    here https://etherscan.io/tx/0x093cdad6594e26543fdec775ddb99c6a761bace9512332a5aec50b38b39e92d9

    You see that I have received $126 USDT for .1 eth.

    However then the transaction is analyzed it does not seem to reflect that: https://ethtx.info/mainnet/0x093cdad6594e26543fdec775ddb99c6a761bace9512332a5aec50b38b39e92d9/

    Other tool seems to handle this better https://phalcon.blocksec.com/tx/eth/0x093cdad6594e26543fdec775ddb99c6a761bace9512332a5aec50b38b39e92d9.

    Anyone know if this is a bug... or am i just misinterpreting the result.

    bug 
    opened by lemiesz 4
  • gunicorn.errors.HaltServer: <HaltServer 'Worker failed to boot.' 3>

    gunicorn.errors.HaltServer:

    Version:0.3.1

    ethtx_ce_1       | [2021-10-15 13:09:57 +0000] [7] [INFO] Starting gunicorn 20.1.0
    ethtx_ce_1       | [2021-10-15 13:09:57 +0000] [7] [INFO] Listening at: http://0.0.0.0:5000 (7)
    ethtx_ce_1       | [2021-10-15 13:09:57 +0000] [7] [INFO] Using worker: sync
    ethtx_ce_1       | [2021-10-15 13:09:57 +0000] [14] [INFO] Booting worker with pid: 14
    ethtx_ce_1       | [2021-10-15 13:09:57 +0000] [15] [INFO] Booting worker with pid: 15
    ethtx_ce_1       | [2021-10-15 13:09:57 +0000] [16] [INFO] Booting worker with pid: 16
    ethtx_ce_1       | [2021-10-15 13:09:57 +0000] [17] [INFO] Booting worker with pid: 17
    ethtx_ce_1       | [2021-10-15 13:09:58 +0000] [14] [ERROR] Exception in worker process
    ethtx_ce_1       | Traceback (most recent call last):
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 589, in spawn_worker
    ethtx_ce_1       |     worker.init_process()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 134, in init_process
    ethtx_ce_1       |     self.load_wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 146, in load_wsgi
    ethtx_ce_1       |     self.wsgi = self.app.wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/base.py", line 67, in wsgi
    ethtx_ce_1       |     self.callable = self.load()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
    ethtx_ce_1       |     return self.load_wsgiapp()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
    ethtx_ce_1       |     return util.import_app(self.app_uri)
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/util.py", line 359, in import_app
    ethtx_ce_1       |     mod = importlib.import_module(module)
    ethtx_ce_1       |   File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    ethtx_ce_1       |     return _bootstrap._gcd_import(name[level:], package, level)
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 991, in _find_and_load
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap_external>", line 843, in exec_module
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
    ethtx_ce_1       |   File "/app/wsgi.py", line 23, in <module>
    ethtx_ce_1       |     ethtx_config = EthTxConfig(
    ethtx_ce_1       | TypeError: __init__() got an unexpected keyword argument 'mongo_database'
    ethtx_ce_1       | [2021-10-15 13:09:58 +0000] [14] [INFO] Worker exiting (pid: 14)
    ethtx_ce_1       | [2021-10-15 13:09:58 +0000] [15] [ERROR] Exception in worker process
    ethtx_ce_1       | Traceback (most recent call last):
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 589, in spawn_worker
    ethtx_ce_1       |     worker.init_process()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 134, in init_process
    ethtx_ce_1       |     self.load_wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 146, in load_wsgi
    ethtx_ce_1       |     self.wsgi = self.app.wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/base.py", line 67, in wsgi
    ethtx_ce_1       |     self.callable = self.load()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
    ethtx_ce_1       |     return self.load_wsgiapp()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
    ethtx_ce_1       |     return util.import_app(self.app_uri)
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/util.py", line 359, in import_app
    ethtx_ce_1       |     mod = importlib.import_module(module)
    ethtx_ce_1       |   File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    ethtx_ce_1       |     return _bootstrap._gcd_import(name[level:], package, level)
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 991, in _find_and_load
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap_external>", line 843, in exec_module
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
    ethtx_ce_1       |   File "/app/wsgi.py", line 23, in <module>
    ethtx_ce_1       |     ethtx_config = EthTxConfig(
    ethtx_ce_1       | TypeError: __init__() got an unexpected keyword argument 'mongo_database'
    ethtx_ce_1       | [2021-10-15 13:09:58 +0000] [15] [INFO] Worker exiting (pid: 15)
    ethtx_ce_1       | [2021-10-15 13:09:59 +0000] [7] [WARNING] Worker with pid 15 was terminated due to signal 15
    ethtx_ce_1       | [2021-10-15 13:09:59 +0000] [16] [ERROR] Exception in worker process
    ethtx_ce_1       | Traceback (most recent call last):
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 589, in spawn_worker
    ethtx_ce_1       |     worker.init_process()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 134, in init_process
    ethtx_ce_1       |     self.load_wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 146, in load_wsgi
    ethtx_ce_1       |     self.wsgi = self.app.wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/base.py", line 67, in wsgi
    ethtx_ce_1       |     self.callable = self.load()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
    ethtx_ce_1       |     return self.load_wsgiapp()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
    ethtx_ce_1       |     return util.import_app(self.app_uri)
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/util.py", line 359, in import_app
    ethtx_ce_1       |     mod = importlib.import_module(module)
    ethtx_ce_1       |   File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    ethtx_ce_1       |     return _bootstrap._gcd_import(name[level:], package, level)
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 991, in _find_and_load
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap_external>", line 843, in exec_module
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
    ethtx_ce_1       |   File "/app/wsgi.py", line 23, in <module>
    ethtx_ce_1       |     ethtx_config = EthTxConfig(
    ethtx_ce_1       | TypeError: __init__() got an unexpected keyword argument 'mongo_database'
    ethtx_ce_1       | [2021-10-15 13:09:59 +0000] [16] [INFO] Worker exiting (pid: 16)
    ethtx_ce_1       | [2021-10-15 13:09:59 +0000] [17] [ERROR] Exception in worker process
    ethtx_ce_1       | Traceback (most recent call last):
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 589, in spawn_worker
    ethtx_ce_1       |     worker.init_process()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 134, in init_process
    ethtx_ce_1       |     self.load_wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/workers/base.py", line 146, in load_wsgi
    ethtx_ce_1       |     self.wsgi = self.app.wsgi()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/base.py", line 67, in wsgi
    ethtx_ce_1       |     self.callable = self.load()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
    ethtx_ce_1       |     return self.load_wsgiapp()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
    ethtx_ce_1       |     return util.import_app(self.app_uri)
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/util.py", line 359, in import_app
    ethtx_ce_1       |     mod = importlib.import_module(module)
    ethtx_ce_1       |   File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    ethtx_ce_1       |     return _bootstrap._gcd_import(name[level:], package, level)
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 991, in _find_and_load
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
    ethtx_ce_1       |   File "<frozen importlib._bootstrap_external>", line 843, in exec_module
    ethtx_ce_1       |   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
    ethtx_ce_1       |   File "/app/wsgi.py", line 23, in <module>
    ethtx_ce_1       |     ethtx_config = EthTxConfig(
    ethtx_ce_1       | TypeError: __init__() got an unexpected keyword argument 'mongo_database'
    ethtx_ce_1       | [2021-10-15 13:09:59 +0000] [17] [INFO] Worker exiting (pid: 17)
    ethtx_ce_1       | Traceback (most recent call last):
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 209, in run
    ethtx_ce_1       |     self.sleep()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 357, in sleep
    ethtx_ce_1       |     ready = select.select([self.PIPE[0]], [], [], 1.0)
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 242, in handle_chld
    ethtx_ce_1       |     self.reap_workers()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 525, in reap_workers
    ethtx_ce_1       |     raise HaltServer(reason, self.WORKER_BOOT_ERROR)
    ethtx_ce_1       | gunicorn.errors.HaltServer: <HaltServer 'Worker failed to boot.' 3>
    ethtx_ce_1       | 
    ethtx_ce_1       | During handling of the above exception, another exception occurred:
    ethtx_ce_1       | 
    ethtx_ce_1       | Traceback (most recent call last):
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/bin/gunicorn", line 8, in <module>
    ethtx_ce_1       |     sys.exit(run())
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 67, in run
    ethtx_ce_1       |     WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/base.py", line 231, in run
    ethtx_ce_1       |     super().run()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/app/base.py", line 72, in run
    ethtx_ce_1       |     Arbiter(self).run()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 229, in run
    ethtx_ce_1       |     self.halt(reason=inst.reason, exit_status=inst.exit_status)
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 342, in halt
    ethtx_ce_1       |     self.stop()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 393, in stop
    ethtx_ce_1       |     time.sleep(0.1)
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 242, in handle_chld
    ethtx_ce_1       |     self.reap_workers()
    ethtx_ce_1       |   File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/gunicorn/arbiter.py", line 525, in reap_workers
    ethtx_ce_1       |     raise HaltServer(reason, self.WORKER_BOOT_ERROR)
    ethtx_ce_1       | gunicorn.errors.HaltServer: <HaltServer 'Worker failed to boot.' 3>
    ethtx_ce_ethtx_ce_1 exited with code 1
    
    opened by z3roTo0ne 3
  • Fix loss of precision decimal numbers

    Fix loss of precision decimal numbers

    Fixed the issue of loss of precision in the decimal representation of large floats. Converted float to Decimals and changed the precision of the context to 256 i.e. getcontext().prec = 256. Eventually returns the string representation of the Decimal object.

    This fixes the issue of scientific notation representation of floats both in Argument value field and also other fields in models from decoded_model.py

    opened by dudzicz 2
  • ENS lookup bug

    ENS lookup bug

    I was comparing ethtx output to other similar tools and found a case where ethtx resolves an incorrect ENS address.

    In ethtx.info, the offerer value is babywhale.eth. Clicking on babywhale.eth in ethtx uses the address 0x8af6e15ed513b5b73573f58158b1b0bbd5085ec7. But babywhale.eth on etherscan resolves to 0x9bf2eb61a3beb7afb1213029f3f1ae3e08ede755 https://ethtx.info/mainnet/0x665c4db0b7dcf44eb2ead3d455b12bfb244de9a2d3239d756ca3fbdbf21817bb/

    Meanwhile, two other transaction explorers (which don't support ENS resolution) show the value 0x8af6e15ed513b5b73573f58158b1b0bbd5085ec7 for the offerer https://phalcon.blocksec.com/tx/eth/0x665c4db0b7dcf44eb2ead3d455b12bfb244de9a2d3239d756ca3fbdbf21817bb https://tx.eth.samczsun.com/ethereum/0x665c4db0b7dcf44eb2ead3d455b12bfb244de9a2d3239d756ca3fbdbf21817bb

    0x8af6e15ed513b5b73573f58158b1b0bbd5085ec7 does hold two ENS addresses, but babywhale.eth is not one of them, so the wrong ENS address is getting displayed.

    bug 
    opened by engn33r 1
  • More flexible dependencies, no cursor timeout, cache 4bytes

    More flexible dependencies, no cursor timeout, cache 4bytes

    • Make more flexible deps - ethtx is easier to install in other apps
    • No timeout for mongo cursor - some collections may sometimes require more time to search
    • Cache 4bytes resposne, if some transactions have a lot of guessed functions/events, it definitely speeds up ethtx!
    • Fix README mongo string
    opened by kchojn 1
  • Port and standardization models with `pydantic`

    Port and standardization models with `pydantic`

    • all models now use pydantic
    • fix types in models
    • removed jsonpickle
    • extend function end event models
    • changing the order of methods in DecoderService
    • update requirements
    • add tests
    • fix bug with empty args from 4byte
    • update Readme
    • strip node URL from env
    opened by kchojn 1
  • Bug in tx data decoding (token transfers & account balances sections)

    Bug in tx data decoding (token transfers & account balances sections)

    Referring to this tx on etherscan, address 0x8c3FA50473065f1D90f186cA8ba1Aa76Aee409Bb is receiving 0.01 eth but as per the tx on ethtx.info the address is sending out the same amount.

    The emitted events show it as a donation sent event which would indicate the aforementioned address should be the recipient of the amount rather than the one sending it, and hence, I'm inclined to believe that the decoding on etherscan is correct and ethtx's isn't.

    I tried to make some more sense of the data and these points might help with debugging the issue: 0x8c3f.. is a gnosis safe multisig. Ethtx shows 0x8c3f.. is sending the eth to 0x34cf.., this recipient is actually the gnosis safe mastercopy contract. I think this is where the problem lies. this mastercopy be receiving the funds from the multisig doesn't seem right to me

    bug 
    opened by shreyjain711 2
  • Account balances broken for specific transaction

    Account balances broken for specific transaction

    opened by franz101 0
  • feat: erc1155 support

    feat: erc1155 support

    Tested with tx: 0x9848cc10366cf7dde89861a6ae5e1166e7d1edb6cd245c2592679848959f168c Decoded tx (removed block and tx metadata):

    events=[DecodedEvent(chain_id='mainnet', tx_hash='0x9848cc10366cf7dde89861a6ae5e1166e7d1edb6cd245c2592679848959f168c', timestamp=datetime.datetime(2021, 12, 13, 12, 16, 50), contract=AddressInfo(address='0x348fc118bcc65a92dc033a951af153d14d945312', name='Mintvial', badge='receiver'), index=261, call_id=None, event_signature='0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62', event_name='TransferSingle', parameters=[Argument(name='operator', type='address', value=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender')), Argument(name='from', type='address', value=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender')), Argument(name='to', type='address', value=AddressInfo(address='0x0000000000000000000000000000000000000000', name='0x0000000000000000000000000000000000000000', badge=None)), Argument(name='id', type='uint256', value=913), Argument(name='value', type='uint256', value=1)], event_guessed=False), DecodedEvent(chain_id='mainnet', tx_hash='0x9848cc10366cf7dde89861a6ae5e1166e7d1edb6cd245c2592679848959f168c', timestamp=datetime.datetime(2021, 12, 13, 12, 16, 50), contract=AddressInfo(address='0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b', name='CloneX', badge=None), index=262, call_id=None, event_signature='0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', event_name='Transfer', parameters=[Argument(name='from', type='address', value=AddressInfo(address='0x0000000000000000000000000000000000000000', name='0x0000000000000000000000000000000000000000', badge=None)), Argument(name='to', type='address', value=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender')), Argument(name='tokenId', type='nft', value={'address': '0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b?a=14609#inventory', 'name': 'NFT 14609'})], event_guessed=False), DecodedEvent(chain_id='mainnet', tx_hash='0x9848cc10366cf7dde89861a6ae5e1166e7d1edb6cd245c2592679848959f168c', timestamp=datetime.datetime(2021, 12, 13, 12, 16, 50), contract=AddressInfo(address='0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b', name='CloneX', badge=None), index=263, call_id=None, event_signature='0x2ce54db2c0bc64dc675f5fae90636ec2f0c88dbd8e7c6a19c9caca9193741b15', event_name='CloneXRevealed', parameters=[Argument(name='tokenId', type='uint256', value=14609), Argument(name='fileId', type='string', value='13192')], event_guessed=False)] calls=DecodedCall(chain_id='mainnet', timestamp=datetime.datetime(2021, 12, 13, 12, 16, 50), tx_hash='0x9848cc10366cf7dde89861a6ae5e1166e7d1edb6cd245c2592679848959f168c', call_id='', call_type='call', from_address=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender'), to_address=AddressInfo(address='0x348fc118bcc65a92dc033a951af153d14d945312', name='Mintvial', badge='receiver'), value=0.0, function_signature='0x5f6be614', function_name='migrateToken', arguments=[Argument(name='id', type='uint256', value=913)], outputs=[Argument(name='', type='uint256', value=14609)], gas_used=164970, error=None, status=True, indent=0, subcalls=[DecodedCall(chain_id='mainnet', timestamp=datetime.datetime(2021, 12, 13, 12, 16, 50), tx_hash='0x9848cc10366cf7dde89861a6ae5e1166e7d1edb6cd245c2592679848959f168c', call_id='0', call_type='call', from_address=AddressInfo(address='0x348fc118bcc65a92dc033a951af153d14d945312', name='Mintvial', badge='receiver'), to_address=AddressInfo(address='0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b', name='CloneX', badge=None), value=0.0, function_signature='0x937f2608', function_name='mintTransfer', arguments=[Argument(name='to', type='address', value=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender'))], outputs=[Argument(name='', type='uint256', value=14609)], gas_used=148545, error=None, status=True, indent=1, subcalls=[DecodedCall(chain_id='mainnet', timestamp=datetime.datetime(2021, 12, 13, 12, 16, 50), tx_hash='0x9848cc10366cf7dde89861a6ae5e1166e7d1edb6cd245c2592679848959f168c', call_id='0_0000', call_type='staticcall', from_address=AddressInfo(address='0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b', name='CloneX', badge=None), to_address=AddressInfo(address='0xffc4dbeace02c578cc189004c0ad25eeb8d8ba3f', name='CloneXRandomizer', badge=None), value=0.0, function_signature='0x14ff5ea3', function_name='getTokenId', arguments=[Argument(name='tokenId', type='uint256', value=14609)], outputs=[Argument(name='', type='string', value='13192')], gas_used=8473, error=None, status=True, indent=2, subcalls=[], function_guessed=False)], function_guessed=False)], function_guessed=False) transfers=[DecodedTransfer(from_address=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender'), to_address=AddressInfo(address='0x0000000000000000000000000000000000000000', name='0x0000000000000000000000000000000000000000', badge=None), token_address='0x348fc118bcc65a92dc033a951af153d14d945312?a=913#inventory', token_symbol='NFT 913', token_standard='ERC1155', value='1.0000'), DecodedTransfer(from_address=AddressInfo(address='0x0000000000000000000000000000000000000000', name='0x0000000000000000000000000000000000000000', badge=None), to_address=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender'), token_address='0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b?a=14609#inventory', token_symbol='NFT 14609', token_standard='ERC721', value='1.0000')] balances=[DecodedBalance(holder=AddressInfo(address='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', name='0x9e7c078f8da492d80d567705d0dd12dbfffd65ea', badge='sender'), tokens=[{'token_address': '0x348fc118bcc65a92dc033a951af153d14d945312?a=913#inventory', 'token_symbol': 'NFT 913', 'token_standard': 'ERC1155', 'balance': '-1.0000'}, {'token_address': '0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b?a=14609#inventory', 'token_symbol': 'NFT 14609', 'token_standard': 'ERC721', 'balance': '1.0000'}])] status=True
    

    Fixes #125

    feature 
    opened by 0xbhoori 0
Releases(0.3.21)
Python implementation of Gorilla time series compression

Gorilla Time Series Compression This is an implementation (with some adaptations) of the compression algorithm described in section 4.1 (Time series c

Ghiles Meddour 19 Jan 01, 2023
Backman is a random/fixed background image setter for wlroots based compositors

backman Backman is a random/fixed background image setter for wlroots based compositors Dependencies: The program depends on swaybg, python3-toml (or

Hemish 3 Mar 09, 2022
Functional UUIDs for Python.

🏷️FUUID stands for Functional Universally Unique IDentifier. FUUIDs are compatible with regular UUIDs but are naturally ordered by generation time, collision-free and support succinct representations

Phil Demetriou 147 Oct 27, 2022
A (very dirty) experiment to remove layers from a Docker image.

Surgically remove layers from a Docker image (with a chainsaw)

Jérôme Petazzoni 9 Jun 08, 2022
Course-parsing - Parsing Course Info for NIT Kurukshetra

Parsing Course Info for NIT Kurukshetra Overview This repository houses code for

Saksham Mittal 3 Feb 03, 2022
A meme error handler for python

Pwython OwO what's this? Pwython is project aiming to fill in one of the biggest problems with python, which is that it is slow lacks owoified text. N

SystematicError 23 Jan 15, 2022
A multipurpose python module

pysherlock pysherlock is a Python library for dealing with web scraping using images, it's a Python application of the rendertron headless browser API

Sachit 2 Nov 11, 2021
Extract XML from the OS X dictionaries.

Extract XML from the OS X dictionaries.

Joshua Olson 13 Dec 11, 2022
Toolkit for collecting and applying templates of prompting instances

PromptSource Toolkit for collecting and applying templates of prompting instances. WIP Setup Download the repo Navigate to root directory of the repo

BigScience Workshop 1k Jan 05, 2023
ecowater-softner is a Python library for collecting information from Ecowater water softeners.

Ecowater Softner ecowater-softner is a Python library for collecting information from Ecowater water softeners. Installation Use the package manager p

6 Dec 08, 2022
Playing with python imports and inducing those pesky errors.

super-duper-python-imports In this repository we are playing with python imports and inducing those pesky ImportErrors. File Organization project │

James Kelsey 2 Oct 14, 2021
Find unused resource keys in properties files in a Salesforce Commerce Cloud project and get rid of them.

Find Unused Resource Keys Find unused resource keys in properties files in a Salesforce Commerce Cloud project and get rid of them. It looks through a

Noël 5 Jan 08, 2022
A simple tool to move and rename Nvidia Share recordings to a more sensible format.

A simple tool to move and rename Nvidia Share recordings to a more sensible format.

Jasper Rebane 8 Dec 23, 2022
Python 3 script unpacking statically x86 CryptOne packer.

Python 3 script unpacking statically x86 CryptOne packer. CryptOne versions: 2021/08 until now (2021/12)

5 Feb 23, 2022
Astvuln is a simple AST scanner which recursively scans a directory, parses each file as AST and runs specified method.

Astvuln Astvuln is a simple AST scanner which recursively scans a directory, parses each file as AST and runs specified method. Some search methods ar

Bitstamp Security 7 May 29, 2022
A collection of utility functions to prototype geometry processing research in python

gpytoolbox This repo is a work in progress and contains general utility functions I have needed to code while trying to work on geometry process resea

Silvia Sellán 73 Jan 06, 2023
Create password - Generate Random Password with Passphrase

Generate Random Password with Passphrase This is a python code to generate stron

1 Jan 18, 2022
A simple package for handling variables in string.

A simple package for handling string variables. Welcome! This is a simple package for handling variables in string, You can add or remove variables wi

1 Dec 31, 2021
Kanye West Lyrics Generator

aikanye Kanye West Lyrics Generator Python script for generating Kanye West lyrics Put kanye.txt in the same folder as the python script and run "pyth

4 Jan 21, 2022
A python package for your Kali Linux distro that find the fastest mirror and configure your apt to use that mirror

Kali Mirror Finder Using Single Python File A python package for your Kali Linux distro that find the fastest mirror and configure your apt to use tha

MrSingh 6 Dec 12, 2022