diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 0000000..a9ff70e --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,25 @@ +name: Pre-commit + +on: + push: + +permissions: + contents: read + +jobs: + pre-commit: + name: Run pre-commit + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11.3 + + - uses: pre-commit/action@v3.0.0 + with: + extra_args: --all-files diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..f89b8c9 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,39 @@ +name: Publish PPoS Dex data + +on: + schedule: + - cron: "0 0 * * *" + +permissions: + contents: read + +jobs: + publish: + name: Publish PPoS Dex data + env: + ALGO_MNEMONIC: ${{ secrets.ALGO_MNEMONIC }} + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11.3 + + - name: Set up Poetry + uses: abatilo/actions-poetry@v2.3.0 + with: + poetry-version: 1.6.1 + + - name: Install Python dependencies + run: poetry install + + - name: Publish data + run: poetry run python3 ppos_dex.py publish diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..c0befad --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,52 @@ +name: PPoS Dex Test + +on: + pull_request: + + push: + branches: [ main ] + + release: + types: [ published ] + +permissions: + contents: read + +jobs: + ppos-dex-test: + name: PPoS Dex Test + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11.3 + + - name: Set up Poetry + uses: abatilo/actions-poetry@v2.3.0 + with: + poetry-version: 1.6.1 + + - name: Install Python dependencies + run: poetry install + + - name: Run LocalNet + run: | + pipx install algokit + algokit localnet start + + - name: Sleep to allow the LocalNet to start + run: | + sleep 5 + algokit localnet status + + - name: Test + run: poetry run pytest --verbose diff --git a/.gitignore b/.gitignore index b6e4761..dc5edc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,129 +1,5 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class +# IDE +.idea/ -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ +# Artifacts +.src/artifacts/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..21414af --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,47 @@ +repos: +# YAML +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + +# Python +- repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black + language_version: python3.11 + +- repo: https://github.com/myint/autoflake + rev: v2.2.0 + hooks: + - id: autoflake + args: + - --in-place + - --remove-unused-variables + - --remove-all-unused-imports + - --expand-star-imports + - --ignore-init-module-imports + +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.5.1 + hooks: + - id: mypy + args: [ + "--check-untyped-defs", + "--ignore-missing-imports", + "--scripts-are-modules", + "--python-version=3.11", + ] + additional_dependencies: ["types-requests", "types-python-dateutil"] + language_version: python3.11 + +# GitHub Actions +- repo: https://github.com/bridgecrewio/checkov.git + rev: 2.4.14 + hooks: + - id: checkov + files: .+ + verbose: false diff --git a/README.md b/README.md index 28fbcc3..4b5adcc 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ ``` - _______ _______ ______ ______ -|_ __ \|_ __ \ .' ____ \ |_ _ `. - | |__) | | |__) | .--. | (___ \_| | | `. \ .---. _ __ + _______ _______ ______ ______ +|_ __ \|_ __ \ .' ____ \ |_ _ `. + | |__) | | |__) | .--. | (___ \_| | | `. \ .---. _ __ | ___/ | ___// .'`\ \ _.____`. | | | |/ /__ \[ \ [ ] - _| |_ _| |_ | \__. || \____) | _| |_.' /| \__., > ' < + _| |_ _| |_ | \__. || \____) | _| |_.' /| \__., > ' < |_____| |_____| '.__.' \______.' |______.' '.__.'[__]`\_] Algorand Pure Proof of Stake Decentralization Index (by cusma) @@ -43,7 +43,7 @@ Communication Protocols, the **exchange of digital value relies on Consensus Protocols**: combining distributed computation, cryptography and game theory into a mathematical equilibrium the **Consensus Protocols are the engines that power Blockchains**, keeping the history of digital value unique, consistent -and tamper proof within a single distributed data ledger on a global scale. +and tamper-proof within a single distributed data ledger on a global scale. Blockchains' technological performances depend on their Consensus Protocols. At the beginning of this technological journey the so-called Proof of Work @@ -70,7 +70,7 @@ of Stake consensus protocol](https://developer.algorand.org/docs/algorand_consen Thanks to Algorand Pure Proof of Stake (PPoS) consensus mechanism, a unique committee of users is randomly and secretly selected to approve every block, through a [Verifiable Random Function](https://developer.algorand.org/docs/algorand_consensus/#verifiable-random-function) -(VRF). This cryptographic primitive acts like a fair, tamper proof, secure and +(VRF). This cryptographic primitive acts like a fair, tamper-proof, secure and provable cryptographic sortition, in which users are free to take part as long as they own any amount of Algorand's native cryptocurrency, named ALGO, registered online as "participating". Each online ALGO could be seen as a ticket of @@ -94,14 +94,16 @@ Scalability can be quantified in serveral ways: - PPoS power consumption - PPoS node minimal hardware requirements -Thanks to its unique consensus protocol Algorand brings the number of finalized +Thanks to its unique consensus protocol Algorand brings the number of finalized transactions per second from few dozen achieved by PoW up to 1000 and shrinks the transactions' confirmation time form PoW’s dozen of minutes to just 4.5 seconds, with no waste of energy, neglectable transaction’s cost (0.001 ALGO) and no hardware barriers. So we have quantitative evidence of Algorand scalability. ### Security -Quantify security is not that simple and out of the scope of this README. +Quantify security is not that simple and out of the scope of this README. At +the time of writing (Sep 2nd 2023) Algorand never experienced a downtime since +the genesis block (June 2019). ### Decentralization Users participating in Algorand Consensus do not delegate their votes, do not @@ -113,11 +115,11 @@ is welcome to do it, showing the real power of Algorand’s decentralization. Algorand is both a digital and physical infrastructure made of: -- Software: in the form of Algorand native cryptocurrency (the ALGO), - representing the power that each member of the ecosystem may have over +- Software: in the form of Algorand native cryptocurrency (the ALGO), + representing the power that each member of the ecosystem may have over the PPoS consensus mechanism - -- Hardware: in the form of a global network composed by [Relay Nodes + +- Hardware: in the form of a global network composed by [Relay Nodes and Participation Nodes](https://developer.algorand.org/docs/run-a-node/setup/types/) We will adopt the following hypotesis: @@ -181,6 +183,9 @@ Let's try first to define an **ideal theoretical conditions of decentralization* Then, everything deviating from those conditions will make PPoS more real and far from platonic ideality. +_NOTE_: Refer to this [article](https://t.co/aLVGnbgNTZ) for additional +considerations over blockchain decentralization. + ### Definitions We will say that PPoS is **"completely decentralized"** if and only if: @@ -194,21 +199,21 @@ with respect the purely theoretical decentralization. 1. `ALGO DYNAMICS = circulating supply / total supply` - * 0 = no ALGO circulation + * 0 = no ALGO circulation * 1 = complete ALGO circulation - + 2. `ALGO ONLINE STAKE = total online stake / circulating supply` * 0 = no PPoS participation * 1 = complete PPoS participation - + 3. `ONLINE ACCOUNTS = online accounts / existing accounts` * 0 = no PPoS participation * 1 = complete PPoS participation - + The statement 4. essentialy implies a measurement of participating accounts' inequality. So, we will adopt well-know wealth inequality or concentration @@ -228,17 +233,17 @@ theoretically range from 0 (complete equality) to 1 (complete inequality). In order to express our PPoS Decentralization Index homogeneously **we will consider Gini's Index complement** (rather than its original form) in letter calculations. - + **Theil's Indexs** are called **Theil's L** and **Theil's T**, they also measure the inequality of a distribution among a set of peoples but with different sensitivity: -- `THEIL's L INDEX` is sensitive to differences at the lower end of the +- `THEIL's L INDEX` is sensitive to differences at the lower end of the distribution (small ALGO amounts); - -- `THEIL's T INDEX` is more sensitive to differences at the top of the + +- `THEIL's T INDEX` is more sensitive to differences at the top of the distribution (large ALGO amounts); - + both can theoretically range from 0 (complete equality) to +inf (complete inequality) and represent two different evaluations of inequality, based on what we tend to consider worse: having even a few small ALGO amounts among @@ -251,8 +256,8 @@ theoretically range from 0 (perfectly competitive market) to 1 (monopoly). - `ALGO HHI INDEX` considers all ALGO stakes, whether they participate in the PPoS or not; - -- `PPoS HHI INDEX` considers only ALGO stakes that are participatinf in the + +- `PPoS HHI INDEX` considers only ALGO stakes that are participatinf in the PPoS; For sake of completenss we will evaluate them all. @@ -263,120 +268,99 @@ theoretical decentralization. So we will measure this distance form ideality as: -- `PPoS DEX INDEX = ALGO DYNAMICS * ALGO ONLINE STAKE * ONLINE ACCOUNTS * (1 - PPoS GINI)` +- `PPoS DEX INDEX V3 = ALGO DYNAMICS * ALGO ONLINE STAKE * ONLINE ACCOUNTS * (1 - PPoS HHI)` * 0 = complete PPoS centralization * 1 = complete PPoS decentralization - + Being aware that the PPoS only tends to the ideal condition, never reaching it. -## Install PPoS Dex -### Step 1 - Python modules -PPoS Dex uses the following Python3 modules: -1. `msgpack` -2. `docopt` -3. `py-algorand-sdk` -4. `schema` -5. `matplotlib` +> ⚠️ _NOTE: PPoS Dex Index V1 and V2 were based on PPoS Gini Index instead of PPoS HHI._ -so you need to install them (if not already present): +## PPoS Dex CLI +### Step 1 - Install dependencies +PPoS Dex uses Poetry for dependencies management. -```bash -$ pip3 install --upgrade msgpack -$ pip3 install --upgrade docopt -$ pip3 install --upgrade py-algorand-sdk -$ pip3 install --upgrade schema -$ pip3 install --upgrade matplotlib +```shell +$ poetry install ``` ### Step 2 - Clients PPoS Dex interacts with Algorand blockchain both thorugh a Node and an Indexer. You may want to choose: -1. Your local hosted Node and Indexer -2. A third party Node and Indexer services +1. Your local hosted Node and Indexer (see [AlgoKit](https://github.com/algorandfoundation/algokit-cli)) +2. A third party Node and Indexer APIs -PPoS Dex by default uses PureStake API as a service, so if you want -to avoid running your own Node and Indexer all you need to do is creating an -account on [PureStake](https://developer.purestake.io/) and get your API token. +PPoS Dex uses AlgoNode APIs by default. -### Step 3 - PPoS files -Copy following PPoS Dex files on your machine: +If you prefer using your local hosted Node and Indexer or other API providers, +the following environment variables must be set: -1. `ppos_dex.py` -2. `ppos_dex_data.py` -3. `ppos_dex_plots.py` -4. `algo_query.py` -5. `inequality_indexes.py` +```shell +$ export ALGOD_SERVER=... +$ export ALGOD_TOKEN=... +$ export INDEXER_SERVER=... +$ export INDEXER_TOKEN=... +``` -## Usage +### Step 3 - Usage Intreacting with PPoS Dex from your CLI is pretty easy, just ask for help: **Input** -```bash +```shell $ python3 ppos_dex.py --help ``` **Output** ``` +Algorand PPoS Decentralization Index. + Usage: - ppos_dex.py publish [--local-host] - [--algod-address=] [--indexer-address=] - [--algo-threshold=] - ppos_dex.py plot [--local-host] [--indexer-address=] - [--data-address=] [--algo-threshold=] - [--starting-block=] [--ending-block=] - ppos_dex.py snapshot [--local-host] [--indexer-address=] - [--data-address=] [--algo-threshold=] - [--starting-block=] - ppos_dex.py export [--local-host] [--indexer-address=] - [--data-address=] [--algo-threshold=] - [--starting-block=] [--ending-block=] + ppos_dex.py publish [--algo-threshold=] [--localhost] + ppos_dex.py plot [--publisher=

] [--algo-threshold=] [--start-block=] [--end-block=] [--localhost] + ppos_dex.py snapshot [--publisher=

] [--algo-threshold=] [--start-block=] [--localhost] + ppos_dex.py export [--publisher=

] [--algo-threshold=] [--start-block=] [--end-block=] [--localhost] + ppos_dex.py health [--localhost] ppos_dex.py [--help] Commands: - publish Contribute publishing PPoS Decentralization Index data on chain. - plot Plots PPoS Decentralization Index evolution over time. - snapshot Plots latest PPoS Decentralization Index data. - export Exports PPoS Decentralization Index data to csv. + publish Publish PPoS Dex data. Requires ALGO_MNEMONIC environment variable. + plot Plot PPoS Dex timeseries. + snapshot Plot latest PPoS Dex data point. + export Export PPoS Dex data to `.csv`. + health Check Algod and Indexer status. Options: - --local-host Use your Algorand Node (default: PureStake) - - -c --algod-address= Algod Client address - [default: https://mainnet-algorand.api.purestake.io/ps2]. - -i --indexer-address= Indexer Client address - [default: https://mainnet-algorand.api.purestake.io/idx2]. - -t --algo-threshold= Algo minimum balance to query - [default: 1000]. - -a --data-address= Algorand address data source - [default: WIPE4JSUWLXKZZK6GJ6VI32PX6ZWPKBRH5YFRJCHWOVC73P5RI4DGUQUWQ]. - -s --starting-block= Data starting block (int) - [default: 11283636]. - -e --ending-block= Data ending block (int) - - -h --help + -t, --algo-threshold= [default: 1000] + -p, --publisher=

[default: WIPE4JSUWLXKZZK6GJ6VI32PX6ZWPKBRH5YFRJCHWOVC73P5RI4DGUQUWQ] + -s, --start-block= [default: 11476070] + -e, --end-block= + -h, --help ``` ### Publish PPoS Dex data Contribute publishing trustless reliable data on Algorand blockchain paying just the minimum network fee (currently 0.001 ALGO). +Set your publisher mnemonic as environment variable: +```shell +$ export ALGO_MNEMONIC=... +``` + **Input** ```bash -$ python3 ppos_dex.py publish +$ python3 ppos_dex.py publish ``` **Options** -1. `[--local-host]` select your local hosted Node and Indexer; -2. `[--algod-address=]` address of your local Algod Client; -3. `[--indexer-address=]` address of your local Indexer Client; -4. `[--algo-threshold=]` consider only accounts that own more than this threshold (default: 1000 ALGO); +1. `[--algo-threshold=]` consider only accounts that own more than this threshold (default: 1000 ALGO); +2. `[--localhost]` select local hosted Node and Indexer or other API providers; Is worth noting that lower values of `[--algo-threshold=]` will require more -querying efforts, so you should avoid going under default threshold, -expecially if you are using a third party API service. +querying efforts, so you should avoid going under default threshold, expecially +if you are using a third party API service. **Output** -```bash +```shell { "algo_threshold": 1000, "accounts": 13346, @@ -398,16 +382,15 @@ Plot PPoS Dex Index timeseries data published by PPoS Dex Oracle (or by yourself). **Input** -```bash -$ python3 ppos_dex.py plot +```shell +$ python3 ppos_dex.py plot ``` **Options** -1. `[--local-host]` select your local hosted Node and Indexer; -2. `[--indexer-address=]` address of your local Indexer Client; -3. `[--data-address=]` publisher account address (default: PPoS Dex Oracle account); -4. `[--algo-threshold=]` plot only data of accounts that own more than this threshold (default: 1000 ALGO); -5. `[--starting-block=]` plot data from this block (if availables); -6. `[--ending-block=]` plot data until this block; +1. `[--publisher=

]` publisher account address (default: PPoS Dex Oracle account); +2. `[--algo-threshold=]` plot only data of accounts that own more than this threshold (default: 1000 ALGO); +3. `[--start-block=]` plot data from this block (if availables); +4. `[--end-block=]` plot data until this block; +5. `[--localhost]` select local hosted Node and Indexer or other API providers; **Output** ![](images/timeseries_ppos_dynamics.png) @@ -419,15 +402,14 @@ Take a snapshot of PPoS Dex Index data published by PPoS Dex Oracle (or by yourself). **Input** -```bash -$ python3 ppos_dex.py snapshot +```shell +$ python3 ppos_dex.py snapshot ``` **Options** -1. `[--local-host]` select your local hosted Node and Indexer; -2. `[--indexer-address=]` address of your local Indexer Client; -3. `[--data-address=]` publisher account address (default: PPoS Dex Oracle account); -4. `[--algo-threshold=]` plot only data of accounts that own more than this threshold (default: 1000 ALGO); -5. `[--starting-block=]` plot data from this block (if availables); +1. `[--publisher=

]` publisher account address (default: PPoS Dex Oracle account); +2. `[--algo-threshold=]` plot only data of accounts that own more than this threshold (default: 1000 ALGO); +3. `[--start-block=]` plot data from this block (if availables); +4. `[--localhost]` select local hosted Node and Indexer or other API providers; **Output** ![](images/snapshot_ppos_dynamics.png) @@ -438,19 +420,18 @@ $ python3 ppos_dex.py snapshot Export PPoS Dex Index data published by PPoS Dex Oracle (or by yourself) to `csv` file. **Input** -```bash -$ python3 ppos_dex.py export +```shell +$ python3 ppos_dex.py export ``` **Options** -1. `[--local-host]` select your local hosted Node and Indexer; -2. `[--indexer-address=]` address of your local Indexer Client; -3. `[--data-address=]` publisher account address (default: PPoS Dex Oracle account); -4. `[--algo-threshold=]` plot only data of accounts that own more than this threshold (default: 1000 ALGO); -5. `[--starting-block=]` plot data from this block (if availables); -6. `[--ending-block=]` plot data until this block; +1. `[--publisher=

]` publisher account address (default: PPoS Dex Oracle account); +2. `[--algo-threshold=]` plot only data of accounts that own more than this threshold (default: 1000 ALGO); +3. `[--start-block=]` plot data from this block (if availables); +4. `[--end-block=]` plot data until this block; +5. `[--localhost]` select local hosted Node and Indexer or other API providers; **Output** -```bash +```shell ppos_dex_data.csv ``` diff --git a/algo_query.py b/algo_query.py deleted file mode 100644 index b0777d1..0000000 --- a/algo_query.py +++ /dev/null @@ -1,84 +0,0 @@ - -def get_algo_owners(indexer_client, algo_min_balance): - """Search for ALGO owners - - Args: - indexer_client (IndexerClient): Indexer Client V2 - algo_min_balance: ALGO minimum balance (converted in microALGO integer) - - Returns: - (dict): accounts that owns at least algo_min_balance. - - """ - - ALGO_TO_MICROALGO = 10 ** 6 - - nexttoken = "" - numtx = 1 - owners = [] - while numtx > 0: - response = indexer_client.accounts( - min_balance=int(algo_min_balance * ALGO_TO_MICROALGO), - next_page=nexttoken, - limit=1000) - accounts = response['accounts'] - owners += accounts - numtx = len(accounts) - if numtx > 0: - # pointer to the next chunk of requests - nexttoken = response['next-token'] - return owners - - -def get_asa_owners(indexer_client, asset_id, asa_min_balance=0): - """Search for ASAs owners - - Args: - indexer_client (IndexerClient): Indexer Client V2 - asset_id (int): ASA Asset ID - asa_min_balance (int): Optional; ASA minimum balance expressed as - minimal int units - - Returns: - (dict): accounts that owns at least asa_min_balance. - - """ - nexttoken = "" - numtx = 1 - owners = [] - while numtx > 0: - result = indexer_client.accounts( - asset_id=asset_id, - asa_min_balance=asa_min_balance, - next_page=nexttoken, - limit=1000) - accounts = result['accounts'] - owners += accounts - numtx = len(accounts) - if numtx > 0: - # pointer to the next chunk of requests - nexttoken = result['next-token'] - return owners - - -def get_address_txns_note(indexer_client, address, start_block=None, - end_block=None): - nexttoken = "" - numtx = 1 - address_txns = [] - while numtx > 0: - result = indexer_client.search_transactions_by_address( - address=address, - limit=1000, - next_page=nexttoken, - min_round=start_block, - max_round=end_block - ) - txns = result['transactions'] - address_txns += txns - numtx = len(txns) - if numtx > 0: - # pointer to the next chunk of requests - nexttoken = result['next-token'] - txns_note = [txn.get('note') for txn in address_txns] - return txns_note diff --git a/inequality_indexes.py b/inequality_indexes.py deleted file mode 100644 index 9678be9..0000000 --- a/inequality_indexes.py +++ /dev/null @@ -1,116 +0,0 @@ - -import math - - -def scalar_product(vector_1, vector_2): - """Scalar Product - - Args: - vector_1 (list): - vector_2 (list): - - Returns: - (float): scalar product (vector_1 * vector_2) - """ - if len(vector_1) != len(vector_2): - return 0 - return sum(i[0] * i[1] for i in zip(vector_1, vector_2)) - - -def gini_index(distribution): - """Gini's Index - - Args: - distribution (list): wealth distribution of the population - - Returns: - (float): Gini's Index (G), inequality of wealth distribution - 0 <= G < 1, - G = 0, perfect equality - """ - n = len(distribution) - distribution.sort() - elements = [i for i in range(1, n + 1)] - numerator = scalar_product(elements, distribution) - denominator = sum(distribution) - return (2 / n) * (numerator / denominator) - (n + 1) / n - - -def generalized_entropy_index(distribution, alpha): - """Genralized Entropy Index - - Args: - distribution (list): wealth distribution of the population - alpha (float): inequality weight, for large alpha the index is - especially sensitive to the existence of large incomes, - whereas for small alpha the index is especially sensitive to the - existence of small incomes - - Returns: - (float): Generalized Entropy Index (GE), inequality of wealth - distribution with inequality weight alpha - 0 <= GE(aplha) < +inf, - GE(alpha) = 0, perfect equality - """ - n = len(distribution) - mean_amount = sum(distribution) / n - epsilon = 0 - for amount in distribution: - epsilon += (amount / mean_amount) ** alpha - 1 - return epsilon / (n * alpha * (alpha - 1)) - - -def theil_l_index(distribution): - """Theil's L Index is sensitive to differences at the lower end of the - distribution (small incomes). - - Args: - distribution (list): wealth distribution of the population - - Returns: - (float): Theil's L Index (TL), inequality of wealth distribution - 0 <= TL < +inf - TL = 0, perfect equality - """ - n = len(distribution) - mean_amount = sum(distribution) / n - epsilon = 0 - for amount in distribution: - epsilon += math.log(amount / mean_amount) - return - epsilon / n - - -def theil_t_index(distribution): - """Theil's T Index is more sensitive to differences at the top of the - distribution (large incomes). - - Args: - distribution (list): wealth distribution of the population - - Returns: - (float): Theil's T Index (TT), inequality of wealth distribution - 0 <= TT < +inf - TT = 0, perfect equality - """ - n = len(distribution) - mean_amount = sum(distribution) / n - epsilon = 0 - for amount in distribution: - epsilon += (amount / mean_amount) * math.log(amount / mean_amount) - return epsilon / n - - -def herfindahl_hirschman_index(distribution): - """Herfindahl–Hirschman Index - - Args: - distribution (list): wealth distribution in the market - - Returns: - (float): Herfindahl–Hirschman Index (HHI), market concentration degree - 0 <= HHI < 1, - HHI = 0, perfectly competitive - """ - market_size = sum(distribution) - market_shares = [share / market_size for share in distribution] - return sum([share ** 2 for share in market_shares]) diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..4c8d6bd --- /dev/null +++ b/poetry.lock @@ -0,0 +1,970 @@ +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + +[[package]] +name = "algokit-utils" +version = "1.3.1" +description = "Utilities for Algorand development for use by AlgoKit" +category = "main" +optional = false +python-versions = ">=3.10,<4.0" +files = [ + {file = "algokit_utils-1.3.1-py3-none-any.whl", hash = "sha256:9a369a3db1c18c5122fce1ec33496db3c4ab0566905f92a486806350eb95a1cb"}, +] + +[package.dependencies] +deprecated = ">=1.2.14,<2.0.0" +py-algorand-sdk = ">=2.2.0,<3.0.0" + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "contextlib2" +version = "21.6.0" +description = "Backports and enhancements for the contextlib module" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "contextlib2-21.6.0-py2.py3-none-any.whl", hash = "sha256:3fbdb64466afd23abaf6c977627b75b6139a5a3e8ce38405c5b413aed7a0471f"}, + {file = "contextlib2-21.6.0.tar.gz", hash = "sha256:ab1e2bfe1d01d968e1b7e8d9023bc51ef3509bba217bb730cee3827e1ee82869"}, +] + +[[package]] +name = "contourpy" +version = "1.1.0" +description = "Python library for calculating contours of 2D quadrilateral grids" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"}, + {file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, + {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, + {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, + {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, + {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, + {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, + {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, + {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, + {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"}, + {file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"}, +] + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "wurlitzer"] + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] + +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + +[[package]] +name = "docopt" +version = "0.6.2" +description = "Pythonic argument parser, that will make you smile" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, +] + +[[package]] +name = "fonttools" +version = "4.42.1" +description = "Tools to manipulate font files" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.42.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ed1a13a27f59d1fc1920394a7f596792e9d546c9ca5a044419dca70c37815d7c"}, + {file = "fonttools-4.42.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9b1ce7a45978b821a06d375b83763b27a3a5e8a2e4570b3065abad240a18760"}, + {file = "fonttools-4.42.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f720fa82a11c0f9042376fd509b5ed88dab7e3cd602eee63a1af08883b37342b"}, + {file = "fonttools-4.42.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db55cbaea02a20b49fefbd8e9d62bd481aaabe1f2301dabc575acc6b358874fa"}, + {file = "fonttools-4.42.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a35981d90feebeaef05e46e33e6b9e5b5e618504672ca9cd0ff96b171e4bfff"}, + {file = "fonttools-4.42.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:68a02bbe020dc22ee0540e040117535f06df9358106d3775e8817d826047f3fd"}, + {file = "fonttools-4.42.1-cp310-cp310-win32.whl", hash = "sha256:12a7c247d1b946829bfa2f331107a629ea77dc5391dfd34fdcd78efa61f354ca"}, + {file = "fonttools-4.42.1-cp310-cp310-win_amd64.whl", hash = "sha256:a398bdadb055f8de69f62b0fc70625f7cbdab436bbb31eef5816e28cab083ee8"}, + {file = "fonttools-4.42.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:689508b918332fb40ce117131633647731d098b1b10d092234aa959b4251add5"}, + {file = "fonttools-4.42.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e36344e48af3e3bde867a1ca54f97c308735dd8697005c2d24a86054a114a71"}, + {file = "fonttools-4.42.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19b7db825c8adee96fac0692e6e1ecd858cae9affb3b4812cdb9d934a898b29e"}, + {file = "fonttools-4.42.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:113337c2d29665839b7d90b39f99b3cac731f72a0eda9306165a305c7c31d341"}, + {file = "fonttools-4.42.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:37983b6bdab42c501202500a2be3a572f50d4efe3237e0686ee9d5f794d76b35"}, + {file = "fonttools-4.42.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6ed2662a3d9c832afa36405f8748c250be94ae5dfc5283d668308391f2102861"}, + {file = "fonttools-4.42.1-cp311-cp311-win32.whl", hash = "sha256:179737095eb98332a2744e8f12037b2977f22948cf23ff96656928923ddf560a"}, + {file = "fonttools-4.42.1-cp311-cp311-win_amd64.whl", hash = "sha256:f2b82f46917d8722e6b5eafeefb4fb585d23babd15d8246c664cd88a5bddd19c"}, + {file = "fonttools-4.42.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:62f481ac772fd68901573956231aea3e4b1ad87b9b1089a61613a91e2b50bb9b"}, + {file = "fonttools-4.42.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2f806990160d1ce42d287aa419df3ffc42dfefe60d473695fb048355fe0c6a0"}, + {file = "fonttools-4.42.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db372213d39fa33af667c2aa586a0c1235e88e9c850f5dd5c8e1f17515861868"}, + {file = "fonttools-4.42.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d18fc642fd0ac29236ff88ecfccff229ec0386090a839dd3f1162e9a7944a40"}, + {file = "fonttools-4.42.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8708b98c278012ad267ee8a7433baeb809948855e81922878118464b274c909d"}, + {file = "fonttools-4.42.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c95b0724a6deea2c8c5d3222191783ced0a2f09bd6d33f93e563f6f1a4b3b3a4"}, + {file = "fonttools-4.42.1-cp38-cp38-win32.whl", hash = "sha256:4aa79366e442dbca6e2c8595645a3a605d9eeabdb7a094d745ed6106816bef5d"}, + {file = "fonttools-4.42.1-cp38-cp38-win_amd64.whl", hash = "sha256:acb47f6f8680de24c1ab65ebde39dd035768e2a9b571a07c7b8da95f6c8815fd"}, + {file = "fonttools-4.42.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb289b7a815638a7613d46bcf324c9106804725b2bb8ad913c12b6958ffc4ec"}, + {file = "fonttools-4.42.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:53eb5091ddc8b1199330bb7b4a8a2e7995ad5d43376cadce84523d8223ef3136"}, + {file = "fonttools-4.42.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46a0ec8adbc6ff13494eb0c9c2e643b6f009ce7320cf640de106fb614e4d4360"}, + {file = "fonttools-4.42.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cc7d685b8eeca7ae69dc6416833fbfea61660684b7089bca666067cb2937dcf"}, + {file = "fonttools-4.42.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:be24fcb80493b2c94eae21df70017351851652a37de514de553435b256b2f249"}, + {file = "fonttools-4.42.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:515607ec756d7865f23070682622c49d922901943697871fc292277cf1e71967"}, + {file = "fonttools-4.42.1-cp39-cp39-win32.whl", hash = "sha256:0eb79a2da5eb6457a6f8ab904838454accc7d4cccdaff1fd2bd3a0679ea33d64"}, + {file = "fonttools-4.42.1-cp39-cp39-win_amd64.whl", hash = "sha256:7286aed4ea271df9eab8d7a9b29e507094b51397812f7ce051ecd77915a6e26b"}, + {file = "fonttools-4.42.1-py3-none-any.whl", hash = "sha256:9398f244e28e0596e2ee6024f808b06060109e33ed38dcc9bded452fd9bbb853"}, + {file = "fonttools-4.42.1.tar.gz", hash = "sha256:c391cd5af88aacaf41dd7cfb96eeedfad297b5899a39e12f4c2c3706d0a3329d"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.0.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "kiwisolver" +version = "1.4.5" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, +] + +[[package]] +name = "matplotlib" +version = "3.7.2" +description = "Python plotting package" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:2699f7e73a76d4c110f4f25be9d2496d6ab4f17345307738557d345f099e07de"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a8035ba590658bae7562786c9cc6ea1a84aa49d3afab157e414c9e2ea74f496d"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f8e4a49493add46ad4a8c92f63e19d548b2b6ebbed75c6b4c7f46f57d36cdd1"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71667eb2ccca4c3537d9414b1bc00554cb7f91527c17ee4ec38027201f8f1603"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:152ee0b569a37630d8628534c628456b28686e085d51394da6b71ef84c4da201"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070f8dddd1f5939e60aacb8fa08f19551f4b0140fab16a3669d5cd6e9cb28fc8"}, + {file = "matplotlib-3.7.2-cp310-cp310-win32.whl", hash = "sha256:fdbb46fad4fb47443b5b8ac76904b2e7a66556844f33370861b4788db0f8816a"}, + {file = "matplotlib-3.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:23fb1750934e5f0128f9423db27c474aa32534cec21f7b2153262b066a581fd1"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:30e1409b857aa8a747c5d4f85f63a79e479835f8dffc52992ac1f3f25837b544"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:50e0a55ec74bf2d7a0ebf50ac580a209582c2dd0f7ab51bc270f1b4a0027454e"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac60daa1dc83e8821eed155796b0f7888b6b916cf61d620a4ddd8200ac70cd64"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305e3da477dc8607336ba10bac96986d6308d614706cae2efe7d3ffa60465b24"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c308b255efb9b06b23874236ec0f10f026673ad6515f602027cc8ac7805352d"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60c521e21031632aa0d87ca5ba0c1c05f3daacadb34c093585a0be6780f698e4"}, + {file = "matplotlib-3.7.2-cp311-cp311-win32.whl", hash = "sha256:26bede320d77e469fdf1bde212de0ec889169b04f7f1179b8930d66f82b30cbc"}, + {file = "matplotlib-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4860132c8c05261a5f5f8467f1b269bf1c7c23902d75f2be57c4a7f2394b3e"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:a1733b8e84e7e40a9853e505fe68cc54339f97273bdfe6f3ed980095f769ddc7"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d9881356dc48e58910c53af82b57183879129fa30492be69058c5b0d9fddf391"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f081c03f413f59390a80b3e351cc2b2ea0205839714dbc364519bcf51f4b56ca"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cd120fca3407a225168238b790bd5c528f0fafde6172b140a2f3ab7a4ea63e9"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c1590b90aa7bd741b54c62b78de05d4186271e34e2377e0289d943b3522273"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d2ff3c984b8a569bc1383cd468fc06b70d7b59d5c2854ca39f1436ae8394117"}, + {file = "matplotlib-3.7.2-cp38-cp38-win32.whl", hash = "sha256:5dea00b62d28654b71ca92463656d80646675628d0828e08a5f3b57e12869e13"}, + {file = "matplotlib-3.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:0f506a1776ee94f9e131af1ac6efa6e5bc7cb606a3e389b0ccb6e657f60bb676"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6515e878f91894c2e4340d81f0911857998ccaf04dbc1bba781e3d89cbf70608"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:71f7a8c6b124e904db550f5b9fe483d28b896d4135e45c4ea381ad3b8a0e3256"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12f01b92ecd518e0697da4d97d163b2b3aa55eb3eb4e2c98235b3396d7dad55f"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7e28d6396563955f7af437894a36bf2b279462239a41028323e04b85179058b"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbcf59334ff645e6a67cd5f78b4b2cdb76384cdf587fa0d2dc85f634a72e1a3e"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:318c89edde72ff95d8df67d82aca03861240512994a597a435a1011ba18dbc7f"}, + {file = "matplotlib-3.7.2-cp39-cp39-win32.whl", hash = "sha256:ce55289d5659b5b12b3db4dc9b7075b70cef5631e56530f14b2945e8836f2d20"}, + {file = "matplotlib-3.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:2ecb5be2b2815431c81dc115667e33da0f5a1bcf6143980d180d09a717c4a12e"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdcd28360dbb6203fb5219b1a5658df226ac9bebc2542a9e8f457de959d713d0"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3cca3e842b11b55b52c6fb8bd6a4088693829acbfcdb3e815fa9b7d5c92c1b"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebf577c7a6744e9e1bd3fee45fc74a02710b214f94e2bde344912d85e0c9af7c"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:936bba394682049919dda062d33435b3be211dc3dcaa011e09634f060ec878b2"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bc221ffbc2150458b1cd71cdd9ddd5bb37962b036e41b8be258280b5b01da1dd"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35d74ebdb3f71f112b36c2629cf32323adfbf42679e2751252acd468f5001c07"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:717157e61b3a71d3d26ad4e1770dc85156c9af435659a25ee6407dc866cb258d"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:20f844d6be031948148ba49605c8b96dfe7d3711d1b63592830d650622458c11"}, + {file = "matplotlib-3.7.2.tar.gz", hash = "sha256:a8cdb91dddb04436bd2f098b8fdf4b81352e68cf4d2c6756fcc414791076569b"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.0.1" +numpy = ">=1.20" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.3.1,<3.1" +python-dateutil = ">=2.7" + +[[package]] +name = "msgpack" +version = "1.0.5" +description = "MessagePack serializer" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"}, + {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"}, + {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"}, + {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"}, + {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"}, + {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"}, + {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"}, + {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"}, + {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"}, + {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"}, + {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"}, + {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"}, + {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"}, + {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"}, + {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"}, + {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"}, +] + +[[package]] +name = "numpy" +version = "1.25.2" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, + {file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, + {file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, + {file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, + {file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, + {file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, + {file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, + {file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, + {file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, + {file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, + {file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, + {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pillow" +version = "10.0.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Pillow-10.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f62406a884ae75fb2f818694469519fb685cc7eaff05d3451a9ebe55c646891"}, + {file = "Pillow-10.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d5db32e2a6ccbb3d34d87c87b432959e0db29755727afb37290e10f6e8e62614"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf4392b77bdc81f36e92d3a07a5cd072f90253197f4a52a55a8cec48a12483b"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:520f2a520dc040512699f20fa1c363eed506e94248d71f85412b625026f6142c"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8c11160913e3dd06c8ffdb5f233a4f254cb449f4dfc0f8f4549eda9e542c93d1"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a74ba0c356aaa3bb8e3eb79606a87669e7ec6444be352870623025d75a14a2bf"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d0dae4cfd56969d23d94dc8e89fb6a217be461c69090768227beb8ed28c0a3"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22c10cc517668d44b211717fd9775799ccec4124b9a7f7b3635fc5386e584992"}, + {file = "Pillow-10.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:dffe31a7f47b603318c609f378ebcd57f1554a3a6a8effbc59c3c69f804296de"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:9fb218c8a12e51d7ead2a7c9e101a04982237d4855716af2e9499306728fb485"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d35e3c8d9b1268cbf5d3670285feb3528f6680420eafe35cccc686b73c1e330f"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ed64f9ca2f0a95411e88a4efbd7a29e5ce2cea36072c53dd9d26d9c76f753b3"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6eb5502f45a60a3f411c63187db83a3d3107887ad0d036c13ce836f8a36f1d"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c1fbe7621c167ecaa38ad29643d77a9ce7311583761abf7836e1510c580bf3dd"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cd25d2a9d2b36fcb318882481367956d2cf91329f6892fe5d385c346c0649629"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"}, + {file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"}, + {file = "Pillow-10.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce543ed15570eedbb85df19b0a1a7314a9c8141a36ce089c0a894adbfccb4568"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:685ac03cc4ed5ebc15ad5c23bc555d68a87777586d970c2c3e216619a5476223"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d72e2ecc68a942e8cf9739619b7f408cc7b272b279b56b2c83c6123fcfa5cdff"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"}, + {file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"}, + {file = "Pillow-10.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f07ea8d2f827d7d2a49ecf1639ec02d75ffd1b88dcc5b3a61bbb37a8759ad8d"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:040586f7d37b34547153fa383f7f9aed68b738992380ac911447bb78f2abe530"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f88a0b92277de8e3ca715a0d79d68dc82807457dae3ab8699c758f07c20b3c51"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c7cf14a27b0d6adfaebb3ae4153f1e516df54e47e42dcc073d7b3d76111a8d86"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3400aae60685b06bb96f99a21e1ada7bc7a413d5f49bce739828ecd9391bb8f7"}, + {file = "Pillow-10.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbc02381779d412145331789b40cc7b11fdf449e5d94f6bc0b080db0a56ea3f0"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9211e7ad69d7c9401cfc0e23d49b69ca65ddd898976d660a2fa5904e3d7a9baa"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:faaf07ea35355b01a35cb442dd950d8f1bb5b040a7787791a535de13db15ed90"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f72a021fbb792ce98306ffb0c348b3c9cb967dce0f12a49aa4c3d3fdefa967"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f7c16705f44e0504a3a2a14197c1f0b32a95731d251777dcb060aa83022cb2d"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:76edb0a1fa2b4745fb0c99fb9fb98f8b180a1bbceb8be49b087e0b21867e77d3"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:368ab3dfb5f49e312231b6f27b8820c823652b7cd29cfbd34090565a015e99ba"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:608bfdee0d57cf297d32bcbb3c728dc1da0907519d1784962c5f0c68bb93e5a3"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5c6e3df6bdd396749bafd45314871b3d0af81ff935b2d188385e970052091017"}, + {file = "Pillow-10.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:7be600823e4c8631b74e4a0d38384c73f680e6105a7d3c6824fcf226c178c7e6"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:92be919bbc9f7d09f7ae343c38f5bb21c973d2576c1d45600fce4b74bafa7ac0"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8182b523b2289f7c415f589118228d30ac8c355baa2f3194ced084dac2dbba"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:38250a349b6b390ee6047a62c086d3817ac69022c127f8a5dc058c31ccef17f3"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88af2003543cc40c80f6fca01411892ec52b11021b3dc22ec3bc9d5afd1c5334"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c189af0545965fa8d3b9613cfdb0cd37f9d71349e0f7750e1fd704648d475ed2"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7b031a6fc11365970e6a5686d7ba8c63e4c1cf1ea143811acbb524295eabed"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db24668940f82321e746773a4bc617bfac06ec831e5c88b643f91f122a785684"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:efe8c0681042536e0d06c11f48cebe759707c9e9abf880ee213541c5b46c5bf3"}, + {file = "Pillow-10.0.0.tar.gz", hash = "sha256:9c82b5b3e043c7af0d95792d0d20ccf68f61a1fec6b3530e718b688422727396"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py-algorand-sdk" +version = "2.4.0" +description = "Algorand SDK in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "py-algorand-sdk-2.4.0.tar.gz", hash = "sha256:92fd321ffbb449fc8138b45f12b81fd7fb4958cc1405939f1567fba6f41969a1"}, + {file = "py_algorand_sdk-2.4.0-py3-none-any.whl", hash = "sha256:a5dbc8d16d67479cf3af8cf9a245c5f1aeb228e01b7252f81169ed38dcce015e"}, +] + +[package.dependencies] +msgpack = ">=1.0.0,<2" +pycryptodomex = ">=3.6.0,<4" +pynacl = ">=1.4.0,<2" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pycryptodomex" +version = "3.18.0" +description = "Cryptographic library for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodomex-3.18.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:160a39a708c36fa0b168ab79386dede588e62aec06eb505add870739329aecc6"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c2953afebf282a444c51bf4effe751706b4d0d63d7ca2cc51db21f902aa5b84e"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ba95abd563b0d1b88401658665a260852a8e6c647026ee6a0a65589287681df8"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:192306cf881fe3467dda0e174a4f47bb3a8bb24b90c9cdfbdc248eec5fc0578c"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:f9ab5ef0718f6a8716695dea16d83b671b22c45e9c0c78fd807c32c0192e54b5"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-win32.whl", hash = "sha256:50308fcdbf8345e5ec224a5502b4215178bdb5e95456ead8ab1a69ffd94779cb"}, + {file = "pycryptodomex-3.18.0-cp27-cp27m-win_amd64.whl", hash = "sha256:4d9379c684efea80fdab02a3eb0169372bca7db13f9332cb67483b8dc8b67c37"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5594a125dae30d60e94f37797fc67ce3c744522de7992c7c360d02fdb34918f8"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8ff129a5a0eb5ff16e45ca4fa70a6051da7f3de303c33b259063c19be0c43d35"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:3d9314ac785a5b75d5aaf924c5f21d6ca7e8df442e5cf4f0fefad4f6e284d422"}, + {file = "pycryptodomex-3.18.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:f237278836dda412a325e9340ba2e6a84cb0f56b9244781e5b61f10b3905de88"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac614363a86cc53d8ba44b6c469831d1555947e69ab3276ae8d6edc219f570f7"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:302a8f37c224e7b5d72017d462a2be058e28f7be627bdd854066e16722d0fc0c"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:6421d23d6a648e83ba2670a352bcd978542dad86829209f59d17a3f087f4afef"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84e105787f5e5d36ec6a581ff37a1048d12e638688074b2a00bcf402f9aa1c2"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6875eb8666f68ddbd39097867325bd22771f595b4e2b0149739b5623c8bf899b"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:27072a494ce621cc7a9096bbf60ed66826bb94db24b49b7359509e7951033e74"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:1949e09ea49b09c36d11a951b16ff2a05a0ffe969dda1846e4686ee342fe8646"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6ed3606832987018615f68e8ed716a7065c09a0fe94afd7c9ca1b6777f0ac6eb"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-win32.whl", hash = "sha256:d56c9ec41258fd3734db9f5e4d2faeabe48644ba9ca23b18e1839b3bdf093222"}, + {file = "pycryptodomex-3.18.0-cp35-abi3-win_amd64.whl", hash = "sha256:e00a4bacb83a2627e8210cb353a2e31f04befc1155db2976e5e239dd66482278"}, + {file = "pycryptodomex-3.18.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2dc4eab20f4f04a2d00220fdc9258717b82d31913552e766d5f00282c031b70a"}, + {file = "pycryptodomex-3.18.0-pp27-pypy_73-win32.whl", hash = "sha256:75672205148bdea34669173366df005dbd52be05115e919551ee97171083423d"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bec6c80994d4e7a38312072f89458903b65ec99bed2d65aa4de96d997a53ea7a"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d35a8ffdc8b05e4b353ba281217c8437f02c57d7233363824e9d794cf753c419"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f0a46bee539dae4b3dfe37216f678769349576b0080fdbe431d19a02da42ff"}, + {file = "pycryptodomex-3.18.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:71687eed47df7e965f6e0bf3cadef98f368d5221f0fb89d2132effe1a3e6a194"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:73d64b32d84cf48d9ec62106aa277dbe99ab5fbfd38c5100bc7bddd3beb569f7"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbdcce0a226d9205560a5936b05208c709b01d493ed8307792075dedfaaffa5f"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196"}, + {file = "pycryptodomex-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470"}, + {file = "pycryptodomex-3.18.0.tar.gz", hash = "sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e"}, +] + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-sugar" +version = "0.9.7" +description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "pytest-sugar-0.9.7.tar.gz", hash = "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46"}, + {file = "pytest_sugar-0.9.7-py2.py3-none-any.whl", hash = "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062"}, +] + +[package.dependencies] +packaging = ">=21.3" +pytest = ">=6.2.0" +termcolor = ">=2.1.0" + +[package.extras] +dev = ["black", "flake8", "pre-commit"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "schema" +version = "0.7.5" +description = "Simple data validation library" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "schema-0.7.5-py2.py3-none-any.whl", hash = "sha256:f3ffdeeada09ec34bf40d7d79996d9f7175db93b7a5065de0faa7f41083c1e6c"}, + {file = "schema-0.7.5.tar.gz", hash = "sha256:f06717112c61895cabc4707752b88716e8420a8819d71404501e114f91043197"}, +] + +[package.dependencies] +contextlib2 = ">=0.5.5" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "a96625b60dd5c5c4609e0369f027556bdf08e4a0d7f74fae0e9d05b34cf27b57" diff --git a/ppos_dex.py b/ppos_dex.py index cc70335..62430a0 100644 --- a/ppos_dex.py +++ b/ppos_dex.py @@ -1,63 +1,64 @@ """ +Algorand PPoS Decentralization Index. + Usage: - ppos_dex.py publish [--local-host] - [--algod-address=] [--indexer-address=] - [--algo-threshold=] - ppos_dex.py plot [--local-host] [--indexer-address=] - [--data-address=] [--algo-threshold=] - [--starting-block=] [--ending-block=] - ppos_dex.py snapshot [--local-host] [--indexer-address=] - [--data-address=] [--algo-threshold=] - [--starting-block=] - ppos_dex.py export [--local-host] [--indexer-address=] - [--data-address=] [--algo-threshold=] - [--starting-block=] [--ending-block=] + ppos_dex.py publish [--algo-threshold=] [--localhost] + ppos_dex.py plot [--publisher=

] [--algo-threshold=] [--start-block=] [--end-block=] [--localhost] + ppos_dex.py snapshot [--publisher=

] [--algo-threshold=] [--start-block=] [--localhost] + ppos_dex.py export [--publisher=

] [--algo-threshold=] [--start-block=] [--end-block=] [--localhost] + ppos_dex.py health [--localhost] ppos_dex.py [--help] Commands: - publish Contribute publishing PPoS Decentralization Index data on chain. - plot Plots PPoS Decentralization Index evolution over time. - snapshot Plots latest PPoS Decentralization Index data. - export Exports PPoS Decentralization Index data to csv. + publish Publish PPoS Dex data. Requires ALGO_MNEMONIC environment variable. + plot Plot PPoS Dex timeseries. + snapshot Plot latest PPoS Dex data point. + export Export PPoS Dex data to `.csv`. + health Check Algod and Indexer status. Options: - --local-host Use your Algorand Node (default: PureStake) - - -c --algod-address= Algod Client address - [default: https://mainnet-algorand.api.purestake.io/ps2]. - -i --indexer-address= Indexer Client address - [default: https://mainnet-algorand.api.purestake.io/idx2]. - -t --algo-threshold= Algo minimum balance to query - [default: 1000]. - -a --data-address= Algorand address data source - [default: WIPE4JSUWLXKZZK6GJ6VI32PX6ZWPKBRH5YFRJCHWOVC73P5RI4DGUQUWQ]. - -s --starting-block= Data starting block (int) - [default: 11283636]. - -e --ending-block= Data ending block (int) - - -h --help + -t, --algo-threshold= [default: 1000] + -p, --publisher=

[default: WIPE4JSUWLXKZZK6GJ6VI32PX6ZWPKBRH5YFRJCHWOVC73P5RI4DGUQUWQ] + -s, --start-block= [default: 11476070] + -e, --end-block= + -h, --help """ -import sys import csv +import sys from docopt import docopt -from ppos_dex_data import * -from ppos_dex_plots import * -from algosdk.v2client import algod -from algosdk.v2client import indexer +from pprint import pprint +from algokit_utils.network_clients import ( + get_algod_client, + get_default_localnet_config, + get_algonode_config, + get_indexer_client, +) + +import src.ppos_dex_data as pposdex +import src.plots as plots +from src.helpers import get_publisher_account + + +def args_types(args: dict) -> dict: + args["--algo-threshold"] = int(args["--algo-threshold"]) + args["--start-block"] = int(args["--start-block"]) + if args["--end-block"] is not None: + args["--end-block"] = int(args["--end-block"]) + return args def main(): print( r""" - _______ _______ ______ ______ - |_ __ \|_ __ \ .' ____ \ |_ _ `. - | |__) | | |__) | .--. | (___ \_| | | `. \ .---. _ __ + _______ _______ ______ ______ + |_ __ \|_ __ \ .' ____ \ |_ _ `. + | |__) | | |__) | .--. | (___ \_| | | `. \ .---. _ __ | ___/ | ___// .'`\ \ _.____`. | | | |/ /__ \[ \ [ ] - _| |_ _| |_ | \__. || \____) | _| |_.' /| \__., > ' < + _| |_ _| |_ | \__. || \____) | _| |_.' /| \__., > ' < |_____| |_____| '.__.' \______.' |______.' '.__.'[__]`\_] - + Algorand Pure Proof of Stake Decentralization Index (by cusma) """ ) @@ -65,69 +66,63 @@ def main(): if len(sys.argv) == 1: # Display help if no arguments, see: # https://github.com/docopt/docopt/issues/420#issuecomment-405018014 - sys.argv.append('--help') + sys.argv.append("--help") - args = docopt(__doc__) + args = args_types(docopt(__doc__)) - token = args[''] - if args['--local-host']: - purestake_token = None - else: - purestake_token = {'X-Api-key': token} - publisher_mnemonic = args[''] - algod_address = args['--algod-address'] - indexer_address = args['--indexer-address'] - data_address = args['--data-address'] - algo_threshold = int(args['--algo-threshold']) - starting_block = int(args['--starting-block']) - if args['--ending-block']: - ending_block = int(args['--ending-block']) + if args["--end-block"]: try: - assert int(args['--starting-block']) < ending_block + assert args["--end-block"] > args["--start-block"] except AssertionError: - raise AssertionError("--ending-block must be grater than " - "--starting-block.") - else: - ending_block = args['--ending-block'] + raise AssertionError("⚠️ --end-block must be grater than --start-block") - algod_client = algod.AlgodClient( - algod_token=token, algod_address=algod_address, - headers=purestake_token) - - indexer_client = indexer.IndexerClient( - indexer_token=token, indexer_address=indexer_address, - headers=purestake_token) - - if args['publish']: - post_ppos_dex_data( - algod_client, indexer_client, publisher_mnemonic, algo_threshold) - - elif args['plot']: - ppos_dex_data = get_ppos_dex_data( - indexer_client, data_address, algo_threshold, starting_block, - ending_block) - - timeseries_plot(ppos_dex_data) - - elif args['snapshot']: - ppos_dex_data = get_ppos_dex_data( - indexer_client, data_address, algo_threshold, starting_block) - - snapshot_plot(ppos_dex_data) - - elif args['export']: - ppos_dex_data = get_ppos_dex_data( - indexer_client, data_address, algo_threshold, starting_block, - ending_block) - - with open('ppos_dex_data.csv', 'w', encoding='utf8', - newline='') as out_file: - fc = csv.DictWriter(out_file, fieldnames=ppos_dex_data[0].keys()) - fc.writeheader() - fc.writerows(ppos_dex_data) + if args["--localhost"]: + algod_config = get_default_localnet_config("algod") + indexer_config = get_default_localnet_config("indexer") + else: + algod_config = get_algonode_config("mainnet", "algod", "") + indexer_config = get_algonode_config("mainnet", "indexer", "") + + algod_client = get_algod_client(algod_config) + indexer_client = get_indexer_client(indexer_config) + + if args["health"]: + print("\n⛓️ Algod Status:") + pprint(algod_client.status(), indent=4) + print("\n📖 Indexer Status:") + pprint(indexer_client.health(), indent=4) + + elif args["publish"]: + publisher = get_publisher_account() + published_data = pposdex.post_ppos_dex_data( + algod_client=algod_client, + indexer_client=indexer_client, + publisher=publisher, + algo_threshold=args["--algo-threshold"], + wait_rounds=3, + data_schema=pposdex.SCHEMA, + ) + print("📈 PPoS Dex published data:") + pprint(published_data, indent=4) else: - print("\nError: read ppos_dex '--help'!\n") + ppos_dex_data = pposdex.get_ppos_dex_data( + indexer_client=indexer_client, + publisher_address=args["--publisher"], + algo_threshold=args["--algo-threshold"], + data_schema=pposdex.SCHEMA, + start_block=args["--start-block"], + end_block=args["--end-block"], + ) + if args["plot"]: + plots.timeseries(ppos_dex_data) + elif args["snapshot"]: + plots.snapshot(ppos_dex_data) + elif args["export"]: + with open("ppos_dex_data.csv", "w", encoding="utf8", newline="") as f: + fc = csv.DictWriter(f, fieldnames=ppos_dex_data[0].keys()) + fc.writeheader() + fc.writerows(ppos_dex_data) if __name__ == "__main__": diff --git a/ppos_dex_data.py b/ppos_dex_data.py deleted file mode 100644 index 9c23202..0000000 --- a/ppos_dex_data.py +++ /dev/null @@ -1,205 +0,0 @@ - -import time -import json -import base64 -import msgpack -from schema import Schema, And, Optional -from datetime import datetime -from algosdk import mnemonic -from algosdk.account import address_from_private_key -from algosdk.error import * -from algosdk.future.transaction import PaymentTxn -from inequality_indexes import * -from algo_query import * - - -def wait_for_confirmation(algod_client, transaction_id, timeout): - """Wait until the transaction is confirmed or rejected, or until 'timeout' - number of rounds have passed. - - Args: - algod_client (AlgodClient): Algod Client - transaction_id (str): the transaction to wait for - timeout (int): maximum number of rounds to wait - - Returns: - (dict): pending transaction information, or throws an error if the - transaction is not confirmed or rejected in the next timeout rounds - """ - start_round = algod_client.status()["last-round"] + 1 - current_round = start_round - - while current_round < start_round + timeout: - algod_client.status_after_block(current_round) - try: - pending_txn = algod_client.pending_transaction_info(transaction_id) - except Exception: - return - if pending_txn.get("confirmed-round", 0) > 0: - return pending_txn - elif pending_txn["pool-error"]: - raise Exception( - 'pool error: {}'.format(pending_txn["pool-error"])) - current_round += 1 - raise Exception( - 'pending tx not found in timeout rounds, timeout value = : {}'.format( - timeout)) - - -def post_ppos_dex_data(algod_client, indexer_client, passphrase, - algo_threshold): - - private_key = mnemonic.to_private_key(passphrase) - - account = {'pk': address_from_private_key(private_key), - 'sk': private_key} - - CONNECTION_ATTEMPT_DELAY_SEC = 3 - MAX_CONNECTION_ATTEMPTS = 10 - MICROALGO_TO_ALGO = 1 / 10 ** 6 - MICROALGO_TOTAL_SUPPLY = 10 ** 16 - - attempts = 1 - params = None - ledger = None - while attempts <= MAX_CONNECTION_ATTEMPTS: - try: - params = algod_client.suggested_params() - ledger = algod_client.ledger_supply() - break - except AlgodHTTPError: - print(f"Algod Client connection attempt " - f"{attempts}/{MAX_CONNECTION_ATTEMPTS}") - print("Trying to contact Algod Client again...") - time.sleep(CONNECTION_ATTEMPT_DELAY_SEC) - finally: - attempts += 1 - if attempts > MAX_CONNECTION_ATTEMPTS: - quit("Unable to connect to Algod Client.") - - attempts = 1 - algo_owners = None - while attempts <= MAX_CONNECTION_ATTEMPTS: - try: - algo_owners = get_algo_owners(indexer_client, algo_threshold) - break - except IndexerHTTPError: - print(f"Indexer Client connection attempt " - f"{attempts}/{MAX_CONNECTION_ATTEMPTS}") - print("Trying to contact Indexer Client again...") - time.sleep(CONNECTION_ATTEMPT_DELAY_SEC) - finally: - attempts += 1 - if attempts > MAX_CONNECTION_ATTEMPTS: - quit("Unable to connect to Indexer Client.") - - stakes = [account['amount'] * MICROALGO_TO_ALGO for - account in algo_owners] - algo_hhi = herfindahl_hirschman_index(stakes) - online_stakes = [account['amount'] * MICROALGO_TO_ALGO - for account in algo_owners - if account['status'] == 'Online'] - algo_dynamics = ledger['total-money'] / MICROALGO_TOTAL_SUPPLY - ppos_online_stake = ledger['online-money'] / ledger['total-money'] - ppos_online_accounts = len(online_stakes) / len(algo_owners) - ppos_gini = gini_index(online_stakes) - ppos_theil_l = theil_l_index(online_stakes) - ppos_theil_t = theil_t_index(online_stakes) - ppos_hhi = herfindahl_hirschman_index(online_stakes) - ppos_dex = (algo_dynamics - * ppos_online_stake - * ppos_online_accounts - * (1 - ppos_gini)) - - note = {'algo_threshold': algo_threshold, - 'accounts': len(algo_owners), - 'algo_hhi': algo_hhi, - 'algo_dynamics': algo_dynamics, - 'ppos_online_stake': ppos_online_stake, - 'ppos_online_accounts': ppos_online_accounts, - 'ppos_gini': ppos_gini, - 'ppos_theil_l': ppos_theil_l, - 'ppos_theil_t': ppos_theil_t, - 'ppos_hhi': ppos_hhi, - 'ppos_dex': ppos_dex, - 'timestamp': str(datetime.now())} - - bytes_note = msgpack.packb(note) - - unsigned_txn = PaymentTxn(sender=account['pk'], - sp=params, - receiver=account['pk'], - amt=0, - note=bytes_note) - - signed_txn = unsigned_txn.sign(account['sk']) - txid = algod_client.send_transaction(signed_txn) - print("Publishing Algorand PPoS Dex data in txID: {}".format(txid)) - - try: - confirmed_txn = wait_for_confirmation(algod_client, txid, 4) - except Exception as err: - print(err) - return - - print("txID: {}".format(txid), " confirmed in round: {}\n".format( - confirmed_txn.get("confirmed-round", 0))) - print("Transaction information:\n{}".format( - json.dumps(confirmed_txn, indent=4))) - - -def get_ppos_dex_data(indexer_client, ppos_dex_address, algo_threshold, - start_block=11476070, end_block=None): - - CONNECTION_ATTEMPT_DELAY_SEC = 3 - MAX_CONNECTION_ATTEMPTS = 10 - - attempts = 1 - ppos_dex_txns_note = None - while attempts <= MAX_CONNECTION_ATTEMPTS: - try: - ppos_dex_txns_note = get_address_txns_note( - indexer_client, ppos_dex_address, start_block, end_block) - break - except IndexerHTTPError: - print(f"Indexer Client connection attempt " - f"{attempts}/{MAX_CONNECTION_ATTEMPTS}") - print("Trying to contact Indexer Client again...") - time.sleep(CONNECTION_ATTEMPT_DELAY_SEC) - finally: - attempts += 1 - if attempts > MAX_CONNECTION_ATTEMPTS: - quit("Unable to connect to Indexer Client.") - - # TODO: make 'algo_hhi' and 'ppos_hhi' mandatory fileds in the schema - schema = Schema({ - 'algo_threshold': int, - 'accounts': And(int, lambda n: 0 <= n), - Optional('algo_hhi'): And(float, lambda n: 0 <= n <= 1), - 'algo_dynamics': And(float, lambda n: 0 <= n), - 'ppos_online_stake': And(float, lambda n: 0 <= n <= 1), - 'ppos_online_accounts': And(float, lambda n: 0 <= n <= 1), - 'ppos_gini': And(float, lambda n: 0 <= n <= 1), - 'ppos_theil_l': And(float, lambda n: 0 <= n), - 'ppos_theil_t': And(float, lambda n: 0 <= n), - Optional('ppos_hhi'): And(float, lambda n: 0 <= n <= 1), - 'ppos_dex': And(float, lambda n: 0 <= n <= 1), - 'timestamp': str - }) - - ppos_dex_data = [] - for txn_note in ppos_dex_txns_note: - try: - data = schema.validate( - msgpack.unpackb(base64.b64decode(txn_note)) - ) - if data['algo_threshold'] == algo_threshold: - ppos_dex_data += [data] - except: - pass - - if not ppos_dex_data: - quit(f"Impossible to find valid PPos Dex data published by " - f"{ppos_dex_address} starting from block {start_block}.") - - return ppos_dex_data diff --git a/ppos_dex_plots.py b/ppos_dex_plots.py deleted file mode 100644 index 2d8f1c1..0000000 --- a/ppos_dex_plots.py +++ /dev/null @@ -1,132 +0,0 @@ - -import matplotlib.pyplot as plt - - -def timeseries_plot(data): - data.reverse() - x_axsis = list(range(len(data))) - algo_dynamics = [ppos_dex_data['algo_dynamics'] - for ppos_dex_data in data] - - ppos_online_stake = [ppos_dex_data['ppos_online_stake'] - for ppos_dex_data in data] - - ppos_online_accounts = [ppos_dex_data['ppos_online_accounts'] - for ppos_dex_data in data] - - ppos_gini = [ppos_dex_data['ppos_gini'] - for ppos_dex_data in data] - - ppos_theil_l = [ppos_dex_data['ppos_theil_l'] - for ppos_dex_data in data] - - ppos_theil_t = [ppos_dex_data['ppos_theil_t'] - for ppos_dex_data in data] - - ppos_dex = [ppos_dex_data['ppos_dex'] - for ppos_dex_data in data] - - x_ticks = [ppos_dex_data['timestamp'][:10] - for ppos_dex_data in data] - - ppos_dynamics = plt - ppos_dynamics.suptitle("Algorand PPoS Dynamics") - ppos_dynamics.title("(1 = complete participation)") - ppos_dynamics.plot(x_axsis, algo_dynamics, - label="Algo Dynamics") - ppos_dynamics.plot(x_axsis, ppos_online_stake, - label="PPoS Online Stake") - ppos_dynamics.plot(x_axsis, ppos_online_accounts, - label="PPoS Online Accounts") - ppos_dynamics.xticks(x_axsis, x_ticks, rotation='45') - ppos_dynamics.legend() - ppos_dynamics.ylim(0, 1) - ppos_dynamics.grid(True) - ppos_dynamics.tight_layout() - ppos_dynamics.show() - - ppos_inequality = plt - ppos_inequality.suptitle("Algorand PPoS Distribution") - ppos_inequality.title("(0 = perfect equality)") - ppos_inequality.plot(x_axsis, ppos_gini, - label="PPoS Gini Index") - ppos_inequality.plot(x_axsis, ppos_theil_l, - label="PPoS Theil's L Index") - ppos_inequality.plot(x_axsis, ppos_theil_t, - label="PPoS Theil's T Index") - ppos_inequality.xticks(x_axsis, x_ticks, rotation='45') - ppos_inequality.legend() - ppos_inequality.ylim(0) - ppos_inequality.grid(True) - ppos_inequality.tight_layout() - ppos_inequality.show() - - ppos_dex_index = plt - ppos_dex_index.suptitle("Algorand PPoS Decentralization Index") - ppos_dex_index.title("(1 = perfect decentralization)") - ppos_dex_index.plot(x_axsis, ppos_dex, - label="PPoS Dex Index") - ppos_dex_index.xticks(x_axsis, x_ticks, rotation='45') - ppos_dex_index.legend() - ppos_dex_index.ylim(0, 1) - ppos_dex_index.grid(True) - ppos_dex_index.tight_layout() - ppos_dex_index.show() - - -def snapshot_plot(data): - - ppos_dynamics = plt - ppos_dynamics.style.use('fivethirtyeight') - ppos_dynamics.suptitle(f"Algorand PPoS Dynamics\n" - f"{data[0]['timestamp'][:10]} - " - f"Threshold: {data[0]['algo_threshold']} ALGO, " - f"Accounts: {data[0]['accounts']}\n" - f"(1 = complete participation)") - ppos_dynamics.subplot(3, 1, 1) - ppos_dynamics.barh('algo_dynamics', data[0]['algo_dynamics']) - ppos_dynamics.xlim(0, 1) - ppos_dynamics.subplot(3, 1, 2) - ppos_dynamics.barh('ppos_online_stake', data[0]['ppos_online_stake']) - ppos_dynamics.xlim(0, 1) - ppos_dynamics.subplot(3, 1, 3) - ppos_dynamics.barh('ppos_online_accounts', data[0]['ppos_online_accounts']) - ppos_dynamics.xlim(0, 1) - ppos_dynamics.tight_layout() - ppos_dynamics.show() - - ppos_inequality = plt - x_upper_lim = round(max(data[0]['ppos_theil_l'], - data[0]['ppos_theil_t'])) + 1 - ppos_inequality.style.use('fivethirtyeight') - ppos_inequality.suptitle(f"Algorand PPoS Distribution\n" - f"{data[0]['timestamp'][:10]} - " - f"Threshold: {data[0]['algo_threshold']} ALGO, " - f"Accounts: {data[0]['accounts']}\n" - f"(0 = perfect equality)") - ppos_inequality.subplot(4, 1, 1) - ppos_inequality.barh('ppos_gini', data[0]['ppos_gini'], color='g') - ppos_inequality.xlim(0, 1) - if data[0].get('ppos_hhi') is not None: - ppos_inequality.subplot(4, 1, 2) - ppos_inequality.barh('ppos_hhi', data[0]['ppos_hhi'], color='g') - ppos_inequality.xlim(0, 1) - ppos_inequality.subplot(4, 1, 3) - ppos_inequality.barh('ppos_theil_l', data[0]['ppos_theil_l']) - ppos_inequality.xlim(0, x_upper_lim) - ppos_inequality.subplot(4, 1, 4) - ppos_inequality.barh('ppos_theil_t', data[0]['ppos_theil_t']) - ppos_inequality.xlim(0, x_upper_lim) - ppos_inequality.tight_layout() - ppos_inequality.show() - - ppos_dex_index = plt - ppos_dex_index.style.use('fivethirtyeight') - ppos_dex_index.suptitle(f"Algorand PPoS Decentralization Index\n" - f"Threshold: {data[0]['algo_threshold']} ALGO, " - f"Accounts: {data[0]['accounts']}\n" - f"(1 = perfect decentralization)") - ppos_dex_index.barh('ppos_dex', data[0]['ppos_dex']) - ppos_dex_index.xlim(0, 1) - ppos_dex_index.tight_layout() - ppos_dex_index.show() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a9d2c3d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "pposdex" +version = "3.0.0" +description = "Algorand Pure Proof of Stake Decentralization Index" +license = "MIT" +authors = ["cusma "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.11" +algokit-utils = "^1.3.1" +docopt = "^0.6.2" +matplotlib = "^3.7.2" +py-algorand-sdk = "^2.4.0" +schema = "^0.7.5" + + +[tool.poetry.group.dev.dependencies] +pytest = "*" +pytest-sugar = "*" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/helpers.py b/src/helpers.py new file mode 100644 index 0000000..d093dc9 --- /dev/null +++ b/src/helpers.py @@ -0,0 +1,174 @@ +""" +Algorand PPoS Dex Helpers +""" + +import functools +import json +import os +import time +from typing import Any, Callable, Optional + +from algosdk import mnemonic +from algosdk.transaction import PaymentTxn, wait_for_confirmation +from algosdk.error import IndexerHTTPError +from algosdk.v2client.algod import AlgodClient +from algosdk.v2client.indexer import IndexerClient +from algokit_utils import Account + +ATTEMPT_DELAY_SEC = 3 +MAX_ATTEMPTS = 10 +ENV_MNEMONIC = "ALGO_MNEMONIC" + + +def indexer_request(request: Callable) -> Callable: + """ + Wraps a request to the Indexer adding connection attempts management. + + Args: + request: Request to IndexerClient + + Returns: + Wrapped Indexer request + """ + + @functools.wraps(request) + def wrapper(*args, **kwargs) -> Any: + attempts = 1 + while attempts <= MAX_ATTEMPTS: + try: + return request(*args, **kwargs) + except IndexerHTTPError: + print(f"Indexer request attempt {attempts}/{MAX_ATTEMPTS}") + print("Trying submit request again...") + time.sleep(ATTEMPT_DELAY_SEC) + finally: + attempts += 1 + quit("Unable to connect to the Algorand Indexer.") + + return wrapper + + +def get_publisher_account() -> Account: + env_mnemonic = os.getenv(f"{ENV_MNEMONIC}") + if env_mnemonic is None: + raise Exception(f"Mnemonic environment variable not set: {ENV_MNEMONIC}") + return Account(private_key=mnemonic.to_private_key(env_mnemonic)) + + +@indexer_request +def get_algo_holders( + indexer_client: IndexerClient, + microalgo_min_balance: Optional[int] = None, +) -> list[dict]: + """ + Get Accounts holding a balance greater than `microalgo_min_balance` + + Args: + indexer_client: Algorand Indexer Client + microalgo_min_balance: microALGO minimum balance + + Returns: + Accounts holding a balance greater than `algo_min_balance` + """ + nexttoken = "" + num_acc = 1 + holders: list[dict] = [] + while num_acc > 0: + response = indexer_client.accounts( + min_balance=microalgo_min_balance, next_page=nexttoken, limit=1000 + ) + accounts = response["accounts"] + holders += accounts + num_acc = len(accounts) + if num_acc > 0: + # Pointer to the next chunk of requests + nexttoken = response["next-token"] + if not holders: + quit(f"No account with minimum balance {microalgo_min_balance} microAlgo.") + return holders + + +@indexer_request +def get_address_txns_note( + indexer_client: IndexerClient, + address: str, + start_block: Optional[int] = None, + end_block: Optional[int] = None, +) -> list[str]: + """ + Get transaction notes published by `address` + + Args: + indexer_client: Algorand Indexer Client + address: Note publisher address + start_block: Note research starting block + end_block: Note research ending block + + Returns: + Notes published by `address` + """ + nexttoken = "" + num_tx = 1 + address_txns: list[dict] = [] + while num_tx > 0: + result = indexer_client.search_transactions_by_address( + address=address, + limit=1000, + next_page=nexttoken, + min_round=start_block, + max_round=end_block, + ) + txns = result["transactions"] + address_txns += txns + num_tx = len(txns) + if num_tx > 0: + # Pointer to the next chunk of requests + nexttoken = result["next-token"] + txns_note: list[str] = [] + for txn in address_txns: + note = txn.get("note") + if note is not None: + txns_note.append(note) + return txns_note + + +def post_note( + algod_client: AlgodClient, + publisher: Account, + note: bytes, + wait_rounds: int = 0, + verbose: bool = False, +) -> dict: + """ + Post a note on Algorand + + Args: + algod_client: Algod Client + publisher: Publisher account + note: Note to publish + wait_rounds: Wait rounds for note publishing confirmation + verbose: Show publication details + + Returns: + Confirmed publication transaction + """ + sp = algod_client.suggested_params() + unsigned_txn = PaymentTxn( + sender=publisher.address, + sp=sp, + receiver=publisher.address, + amt=0, + note=note, + ) + + signed_txn = unsigned_txn.sign(publisher.private_key) + txid = algod_client.send_transaction(signed_txn) + if verbose: + print(f"Publishing Algorand PPoS Dex data in txID: {txid}\n") + + confirmed_txn = wait_for_confirmation(algod_client, txid, wait_rounds) + + if verbose: + print(f"Confirmed in round: {confirmed_txn.get('confirmed-round', 0)}\n") + print(f"Transaction information:\n{json.dumps(confirmed_txn, indent=4)}") + return confirmed_txn diff --git a/src/inequality_idx.py b/src/inequality_idx.py new file mode 100644 index 0000000..9760441 --- /dev/null +++ b/src/inequality_idx.py @@ -0,0 +1,130 @@ +""" +Inequality Indexes +""" + +import math + + +def scalar_product(vector_1: list[float], vector_2: list[float]) -> float: + """ + Scalar Product + + Args: + vector_1: First vector of the scalar product + vector_2: Second vector of the scalar product + + Returns: + Scalar product (vector_1 * vector_2) + """ + assert len(vector_1) == len(vector_2) + return sum(i[0] * i[1] for i in zip(vector_1, vector_2)) + + +def gini(distribution: list[float]) -> float: + """ + Gini's Index + + Args: + distribution: Wealth distribution of the population + + Returns: + Gini's Index (G), inequality of wealth distribution + 0 <= G < 1, + G = 0, perfect equality + """ + assert distribution + n = len(distribution) + distribution.sort() + num = scalar_product(list(range(1, n + 1)), distribution) + den = sum(distribution) + return (2 / n) * (num / den) - (n + 1) / n + + +def generalized_entropy(distribution: list[float], alpha: float) -> float: + """ + Genralized Entropy Index + + Args: + distribution: Wealth distribution of the population + alpha: Inequality weight. For large alpha the index is especially + sensitive to the existence of large incomes, whereas for small + alpha the index is especially sensitive to the existence of + small incomes + + Returns: + Generalized Entropy Index (GE), inequality of wealth + distribution with inequality weight alpha + 0 <= GE(aplha) < +inf, + GE(alpha) = 0, perfect equality, + alpha != 0, 1 + """ + assert distribution + assert alpha != 0 and alpha != 1 + n = len(distribution) + mean_amount = sum(distribution) / n + epsilon: float = 0 + for amount in distribution: + epsilon += (amount / mean_amount) ** alpha - 1 + return epsilon / (n * alpha * (alpha - 1)) + + +def theil_l(distribution: list[float]) -> float: + """ + Theil's L Index is sensitive to differences at the lower end of the + distribution (small incomes). + + Args: + distribution: Wealth distribution of the population + + Returns: + Theil's L Index (TL), inequality of wealth distribution + 0 <= TL < +inf + TL = 0, perfect equality + """ + assert distribution + n = len(distribution) + mean_amount = sum(distribution) / n + epsilon: float = 0 + for amount in distribution: + epsilon += math.log(amount / mean_amount) + return -epsilon / n + + +def theil_t(distribution: list[float]) -> float: + """ + Theil's T Index is more sensitive to differences at the top of the + distribution (large incomes). + + Args: + distribution: Wealth distribution of the population + + Returns: + Theil's T Index (TT), inequality of wealth distribution + 0 <= TT < +inf + TT = 0, perfect equality + """ + assert distribution + n = len(distribution) + mean_amount = sum(distribution) / n + epsilon: float = 0 + for amount in distribution: + epsilon += (amount / mean_amount) * math.log(amount / mean_amount) + return epsilon / n + + +def herfindahl_hirschman(distribution: list[float]) -> float: + """ + Herfindahl–Hirschman Index + + Args: + distribution: Wealth distribution in the market + + Returns: + Herfindahl–Hirschman Index (HHI), market concentration degree + 0 <= HHI < 1, + HHI = 0, perfectly competitive + """ + assert distribution + market_size = sum(distribution) + market_shares = [share / market_size for share in distribution] + return sum([share**2 for share in market_shares]) diff --git a/src/plots.py b/src/plots.py new file mode 100644 index 0000000..e4e7c93 --- /dev/null +++ b/src/plots.py @@ -0,0 +1,132 @@ +import matplotlib.pyplot as plt + +N_XTICKS = 10 + + +def timeseries(ppos_dex_data: list[dict]) -> None: + ppos_dex_data.reverse() + x_axsis = list(range(len(ppos_dex_data))) + x_ticks = [d["timestamp"][:10] for d in ppos_dex_data] + + # Stake Participation Dynamics + algo_dynamics = [d["algo_dynamics"] for d in ppos_dex_data] + online_stake = [d["ppos_online_stake"] for d in ppos_dex_data] + online_accounts = [d["ppos_online_accounts"] for d in ppos_dex_data] + + # PPoS Inequality + ppos_gini = [d["ppos_gini"] for d in ppos_dex_data] + ppos_theil_l = [d["ppos_theil_l"] for d in ppos_dex_data] + ppos_theil_t = [d["ppos_theil_t"] for d in ppos_dex_data] + ppos_hhi = [] + for d in ppos_dex_data: + hhi = d.get("ppos_hhi") + ppos_hhi.append(hhi) if hhi is not None else ppos_hhi.append(0) + + # PPoS Decentralizaation Index + ppos_dex = [d["ppos_dex"] for d in ppos_dex_data] + + ppos_dynamics = plt + ppos_dynamics.suptitle("Algorand PPoS Dynamics") + ppos_dynamics.title("(1 = complete participation)") + ppos_dynamics.plot(x_axsis, algo_dynamics, label="Algo Dynamics") + ppos_dynamics.plot(x_axsis, online_stake, label="PPoS Online Stake") + ppos_dynamics.plot(x_axsis, online_accounts, label="PPoS Online Accounts") + ppos_dynamics.xticks(x_axsis, x_ticks, rotation=45) + ppos_dynamics.legend() + ppos_dynamics.ylim(0, 1) + ppos_dynamics.grid(True) + ppos_dynamics.tight_layout() + ppos_dynamics.locator_params(nbins=N_XTICKS) + ppos_dynamics.show() + + ppos_inequality = plt + ppos_inequality.suptitle("Algorand PPoS Distribution") + ppos_inequality.title("(0 = perfect equality)") + ppos_inequality.plot(x_axsis, ppos_gini, label="PPoS Gini Index") + ppos_inequality.plot(x_axsis, ppos_hhi, label="PPoS HH Index") + ppos_inequality.plot(x_axsis, ppos_theil_l, label="PPoS Theil's L Index") + ppos_inequality.plot(x_axsis, ppos_theil_t, label="PPoS Theil's T Index") + ppos_inequality.xticks(x_axsis, x_ticks, rotation=45) + ppos_inequality.legend() + ppos_inequality.ylim(0) + ppos_inequality.grid(True) + ppos_inequality.tight_layout() + ppos_inequality.locator_params(nbins=N_XTICKS) + ppos_inequality.show() + + ppos_dex_index = plt + ppos_dex_index.suptitle("Algorand PPoS Decentralization Index") + ppos_dex_index.title("(1 = perfect decentralization)") + ppos_dex_index.plot(x_axsis, ppos_dex, label="PPoS Dex Index") + ppos_dex_index.xticks(x_axsis, x_ticks, rotation=45) + ppos_dex_index.legend() + ppos_dex_index.ylim(0) + ppos_dex_index.grid(True) + ppos_dex_index.tight_layout() + ppos_dex_index.locator_params(nbins=N_XTICKS) + ppos_dex_index.show() + + +def snapshot(ppos_dex_data: list[dict]) -> None: + ppos_dynamics = plt + ppos_dynamics.style.use("fivethirtyeight") + ppos_dynamics.suptitle( + f"Algorand PPoS Dynamics\n" + f"{ppos_dex_data[0]['timestamp'][:10]} - " + f"Threshold: {ppos_dex_data[0]['algo_threshold']} ALGO, " + f"Accounts: {ppos_dex_data[0]['accounts']}\n" + f"(1 = complete participation)" + ) + ppos_dynamics.subplot(3, 1, 1) + ppos_dynamics.barh("algo_dynamics", ppos_dex_data[0]["algo_dynamics"]) + ppos_dynamics.xlim(0, 1) + ppos_dynamics.subplot(3, 1, 2) + ppos_dynamics.barh("ppos_online_stake", ppos_dex_data[0]["ppos_online_stake"]) + ppos_dynamics.xlim(0, 1) + ppos_dynamics.subplot(3, 1, 3) + ppos_dynamics.barh("ppos_online_accounts", ppos_dex_data[0]["ppos_online_accounts"]) + ppos_dynamics.xlim(0, 1) + ppos_dynamics.tight_layout() + ppos_dynamics.show() + + ppos_inequality = plt + x_upper_lim = ( + round(max(ppos_dex_data[0]["ppos_theil_l"], ppos_dex_data[0]["ppos_theil_t"])) + + 1 + ) + ppos_inequality.style.use("fivethirtyeight") + ppos_inequality.suptitle( + f"Algorand PPoS Distribution\n" + f"{ppos_dex_data[0]['timestamp'][:10]} - " + f"Threshold: {ppos_dex_data[0]['algo_threshold']} ALGO, " + f"Accounts: {ppos_dex_data[0]['accounts']}\n" + f"(0 = perfect equality)" + ) + ppos_inequality.subplot(4, 1, 1) + ppos_inequality.barh("ppos_gini", ppos_dex_data[0]["ppos_gini"], color="g") + ppos_inequality.xlim(0, 1) + if ppos_dex_data[0].get("ppos_hhi") is not None: + ppos_inequality.subplot(4, 1, 2) + ppos_inequality.barh("ppos_hhi", ppos_dex_data[0]["ppos_hhi"], color="g") + ppos_inequality.xlim(0, 1) + ppos_inequality.subplot(4, 1, 3) + ppos_inequality.barh("ppos_theil_l", ppos_dex_data[0]["ppos_theil_l"]) + ppos_inequality.xlim(0, x_upper_lim) + ppos_inequality.subplot(4, 1, 4) + ppos_inequality.barh("ppos_theil_t", ppos_dex_data[0]["ppos_theil_t"]) + ppos_inequality.xlim(0, x_upper_lim) + ppos_inequality.tight_layout() + ppos_inequality.show() + + ppos_dex_index = plt + ppos_dex_index.style.use("fivethirtyeight") + ppos_dex_index.suptitle( + f"Algorand PPoS Decentralization Index\n" + f"Threshold: {ppos_dex_data[0]['algo_threshold']} ALGO, " + f"Accounts: {ppos_dex_data[0]['accounts']}\n" + f"(1 = perfect decentralization)" + ) + ppos_dex_index.barh("ppos_dex", ppos_dex_data[0]["ppos_dex"]) + ppos_dex_index.xlim(0, 1) + ppos_dex_index.tight_layout() + ppos_dex_index.show() diff --git a/src/ppos_dex_data.py b/src/ppos_dex_data.py new file mode 100644 index 0000000..2145c60 --- /dev/null +++ b/src/ppos_dex_data.py @@ -0,0 +1,148 @@ +import base64 +import msgpack +import schema + +from datetime import datetime +from typing import Optional + +from algokit_utils import Account +from algosdk.constants import microalgos_to_algos_ratio +from algosdk.v2client.algod import AlgodClient +from algosdk.v2client.indexer import IndexerClient + +import src.inequality_idx as idx +import src.helpers as helpers + +MICROALGO_TOTAL_SUPPLY = 10**16 +SCHEMA = schema.Schema( + { + "algo_threshold": int, + "accounts": schema.And(int, lambda n: 0 <= n), + schema.Optional("algo_hhi"): schema.And(float, lambda n: 0 <= n <= 1), + "algo_dynamics": schema.And(float, lambda n: 0 <= n), + "ppos_online_stake": schema.And(float, lambda n: 0 <= n <= 1), + "ppos_online_accounts": schema.And(float, lambda n: 0 <= n <= 1), + "ppos_gini": schema.And(float, lambda n: 0 <= n <= 1), + "ppos_theil_l": schema.And(float, lambda n: 0 <= n), + "ppos_theil_t": schema.And(float, lambda n: 0 <= n), + schema.Optional("ppos_hhi"): schema.And(float, lambda n: 0 <= n <= 1), + "ppos_dex": schema.And(float, lambda n: 0 <= n <= 1), + "timestamp": str, + } +) + + +def post_ppos_dex_data( + algod_client: AlgodClient, + indexer_client: IndexerClient, + publisher: Account, + algo_threshold: int, + data_schema: schema.Schema, + wait_rounds: int = 0, +) -> dict: + """ + Post PPoS Dex data on-chain + + Args: + algod_client: Algod Client + indexer_client: Algorand Indexer Client + publisher: Data publisher account + algo_threshold: Minimum balance data filter (expressed in ALGOs) + data_schema: Data validation schema + wait_rounds: Wait rounds for note publishing confirmation + + Returns: + PPoS Dex published data + """ + microalgo_threshold = algo_threshold * microalgos_to_algos_ratio + holders = helpers.get_algo_holders(indexer_client, microalgo_threshold) + + stakes = [account["amount"] for account in holders] + online_stakes = [ + account["amount"] for account in holders if account["status"] == "Online" + ] + + ledger = algod_client.ledger_supply() + + # Data Processing + algo_hhi = idx.herfindahl_hirschman(stakes) + algo_dynamics = ledger["total-money"] / MICROALGO_TOTAL_SUPPLY + ppos_online_stake = ledger["online-money"] / ledger["total-money"] + ppos_online_accounts = len(online_stakes) / len(stakes) + ppos_gini = idx.gini(online_stakes) + ppos_theil_l = idx.theil_l(online_stakes) + ppos_theil_t = idx.theil_t(online_stakes) + ppos_hhi = idx.herfindahl_hirschman(online_stakes) + ppos_dex = ( + algo_dynamics * ppos_online_stake * ppos_online_accounts * (1 - ppos_gini) + ) + + ppos_dex_data = { + "algo_threshold": algo_threshold, + "accounts": len(stakes), + "algo_hhi": algo_hhi, + "algo_dynamics": algo_dynamics, + "ppos_online_stake": ppos_online_stake, + "ppos_online_accounts": ppos_online_accounts, + "ppos_gini": ppos_gini, + "ppos_theil_l": ppos_theil_l, + "ppos_theil_t": ppos_theil_t, + "ppos_hhi": ppos_hhi, + "ppos_dex": ppos_dex, + "timestamp": str(datetime.now()), + } + ppos_dex_data = data_schema.validate(ppos_dex_data) + + # Publish Data + helpers.post_note( + algod_client, publisher, msgpack.packb(ppos_dex_data), wait_rounds + ) + return ppos_dex_data + + +def get_ppos_dex_data( + indexer_client: IndexerClient, + publisher_address: str, + algo_threshold: int, + data_schema: schema.Schema, + start_block: int, + end_block: Optional[int] = None, +) -> list[dict]: + """ + Get PPoS Dex data from chain + + Args: + indexer_client: Algorand Indexer Client + publisher_address: Publisher addres to read from + algo_threshold: Minimum balance data filter (expressed in ALGOs) + data_schema: Data validation schema + start_block: Data starting block + end_block: Data ending block + + Returns: + List of PPoS Dex data points + """ + ppos_dex_txns_note = helpers.get_address_txns_note( + indexer_client, publisher_address, start_block, end_block + ) + + ppos_dex_data: list[dict] = [] + for txn_note in ppos_dex_txns_note: + try: + data: dict = msgpack.unpackb(base64.b64decode(txn_note)) + data = data_schema.validate(data) + if data["algo_threshold"] == algo_threshold: + ppos_dex_data += [data] + except schema.SchemaError: + # Ignore non compliant data + pass + except msgpack.ExtraData: + # Ignore wrongly serialized data + pass + + if not ppos_dex_data: + quit( + f"No valid PPos Dex data published by {publisher_address} starting from block {start_block}." + ) + + return ppos_dex_data diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..b4717a3 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,56 @@ +import pytest + +from algosdk.v2client.algod import AlgodClient +from algosdk.v2client.indexer import IndexerClient +from algokit_utils import ( + Account, + get_algod_client, + get_default_localnet_config, + get_indexer_client, + get_localnet_default_account, +) + + +@pytest.fixture(scope="session") +def small_equal_distribution() -> list[float]: + return [0.20] * 5 + + +@pytest.fixture(scope="session") +def large_equal_distribution() -> list[float]: + return [0.01] * 100 + + +@pytest.fixture(scope="session") +def inequal_distribution() -> list[float]: + return [0.15] * 6 + [0.01] * 10 + + +@pytest.fixture(scope="session") +def very_inequal_distribution() -> list[float]: + return [0.80] + [0.02] * 5 + [0.01] * 10 + + +@pytest.fixture(scope="session") +def low_end_inequal_distribution() -> list[float]: + return [1.0] + [1000.0] * 99 + + +@pytest.fixture(scope="session") +def high_end_inequal_distribution() -> list[float]: + return [1.0] * 99 + [1000.0] + + +@pytest.fixture(scope="session") +def algod_client() -> AlgodClient: + return get_algod_client(get_default_localnet_config("algod")) + + +@pytest.fixture(scope="session") +def indexer_client() -> IndexerClient: + return get_indexer_client(get_default_localnet_config("indexer")) + + +@pytest.fixture(scope="session") +def faucet(algod_client) -> Account: + return get_localnet_default_account(algod_client) diff --git a/test/test_helpers.py b/test/test_helpers.py new file mode 100644 index 0000000..4dd480b --- /dev/null +++ b/test/test_helpers.py @@ -0,0 +1,38 @@ +import base64 + +from algosdk.v2client.algod import AlgodClient +from algosdk.v2client.indexer import IndexerClient +from algokit_utils import Account + +from src.helpers import get_algo_holders, get_address_txns_note, post_note + + +def test_get_algo_holders( + indexer_client: IndexerClient, + faucet: Account, +) -> None: + holders = get_algo_holders(indexer_client) + assert faucet.address in [account["address"] for account in holders] + + +def test_post_note( + algod_client: AlgodClient, + faucet: Account, +) -> None: + note = b"test" + post_txn = post_note(algod_client, faucet, note) + assert post_txn["txn"]["txn"]["note"] == base64.b64encode(note).decode() + + +def test_get_address_txns_note( + algod_client: AlgodClient, + indexer_client: IndexerClient, + faucet: Account, +) -> None: + note = b"silvio" + post_note(algod_client, faucet, note) + post_note(algod_client, faucet, b"noise") + post_note(algod_client, faucet, b"more noise") + post_note(algod_client, faucet, b"even more noise") + published_notes = get_address_txns_note(indexer_client, faucet.address) + assert base64.b64encode(note).decode() in published_notes diff --git a/test/test_inequality_idx.py b/test/test_inequality_idx.py new file mode 100644 index 0000000..eedefd3 --- /dev/null +++ b/test/test_inequality_idx.py @@ -0,0 +1,154 @@ +import math +import pytest + +import src.inequality_idx as idx + + +def test_scalar_product() -> None: + with pytest.raises(AssertionError): + idx.scalar_product([1, 1], [1, 1, 1]) + + assert idx.scalar_product([1, 0], [0, 1]) == 0 + assert idx.scalar_product([1, 2], [2, 1]) == 4 + + +class TestGiniIndex: + def test_equality( + self, + small_equal_distribution: list[float], + large_equal_distribution: list[float], + ) -> None: + gi_1 = idx.gini(small_equal_distribution) + gi_2 = idx.gini(large_equal_distribution) + assert math.isclose(gi_1, 0, abs_tol=1e-15) + assert math.isclose(gi_2, 0, abs_tol=1e-15) + + def test_inequality( + self, + inequal_distribution: list[float], + very_inequal_distribution: list[float], + ) -> None: + gi_1 = idx.gini(inequal_distribution) + gi_2 = idx.gini(very_inequal_distribution) + assert gi_1 < gi_2 + print(f"Gini - Low Inequality: {gi_1}") + print(f"Gini - High Inequality: {gi_2}") + + def test_value(self) -> None: + assert idx.gini(list(range(1, 5))) == 0.25 + + +class TestGeneralizedEntropy: + def test_equality( + self, + small_equal_distribution: list[float], + large_equal_distribution: list[float], + ) -> None: + ge_1 = idx.generalized_entropy(small_equal_distribution, 2) + ge_2 = idx.generalized_entropy(large_equal_distribution, 3) + assert math.isclose(ge_1, 0, abs_tol=1e-15) + assert math.isclose(ge_2, 0, abs_tol=1e-15) + + def test_inequality( + self, + inequal_distribution: list[float], + very_inequal_distribution: list[float], + ) -> None: + ge_1 = idx.generalized_entropy(inequal_distribution, 2) + ge_2 = idx.generalized_entropy(very_inequal_distribution, 2) + assert ge_1 < ge_2 + + def test_sensitivity( + self, + low_end_inequal_distribution: list[float], + high_end_inequal_distribution: list[float], + ) -> None: + small_alpha = 2 + large_alpha = 20 + + ge_1 = idx.generalized_entropy(low_end_inequal_distribution, small_alpha) + ge_2 = idx.generalized_entropy(low_end_inequal_distribution, large_alpha) + assert ge_1 > ge_2 + print(f"GE - Low End Inequality, Small Alpha: {ge_1}") + print(f"GE - Low End Inequality, Large Alpha: {ge_2}") + + ge_1 = idx.generalized_entropy(high_end_inequal_distribution, small_alpha) + ge_2 = idx.generalized_entropy(high_end_inequal_distribution, large_alpha) + assert ge_1 < ge_2 + print(f"GE - High End Inequality, Small Alpha: {ge_1}") + print(f"GE - High End Inequality, Large Alpha: {ge_2}") + + +class TestTheil: + def test_equality( + self, + small_equal_distribution: list[float], + large_equal_distribution: list[float], + ) -> None: + tl_1 = idx.theil_l(small_equal_distribution) + tl_2 = idx.theil_l(large_equal_distribution) + assert math.isclose(tl_1, 0, abs_tol=1e-15) + assert math.isclose(tl_2, 0, abs_tol=1e-15) + + tt_1 = idx.theil_t(small_equal_distribution) + tt_2 = idx.theil_t(large_equal_distribution) + assert math.isclose(tt_1, 0, abs_tol=1e-15) + assert math.isclose(tt_2, 0, abs_tol=1e-15) + + def test_inequality( + self, + inequal_distribution: list[float], + very_inequal_distribution: list[float], + ) -> None: + tl_1 = idx.theil_l(inequal_distribution) + tl_2 = idx.theil_l(very_inequal_distribution) + assert tl_1 < tl_2 + + tt_1 = idx.theil_t(inequal_distribution) + tt_2 = idx.theil_t(very_inequal_distribution) + assert tt_1 < tt_2 + + def test_sensitivity( + self, + low_end_inequal_distribution: list[float], + high_end_inequal_distribution: list[float], + ) -> None: + tl = idx.theil_l(low_end_inequal_distribution) + tt = idx.theil_t(low_end_inequal_distribution) + assert tl > tt + print(f"TL - Low End Inequality: {tl}") + print(f"TT - Low End Inequality: {tt}") + + tl = idx.theil_l(high_end_inequal_distribution) + tt = idx.theil_t(high_end_inequal_distribution) + assert tl < tt + print(f"TL - High End Inequality: {tl}") + print(f"TT - High End Inequality: {tt}") + + +class TestHHI: + def test_equality( + self, + small_equal_distribution: list[float], + large_equal_distribution: list[float], + ) -> None: + hhi_1 = idx.herfindahl_hirschman(small_equal_distribution) + hhi_2 = idx.herfindahl_hirschman(large_equal_distribution) + assert hhi_1 > hhi_2 + assert math.isclose(hhi_1, 0.20, abs_tol=1e-10) + assert math.isclose(hhi_2, 0.01, abs_tol=1e-10) + print(f"HHI - Small Equal Distribution: {hhi_1}") + print(f"HHI - Large Equal Distribution: {hhi_2}") + + def test_inequality( + self, + inequal_distribution: list[float], + very_inequal_distribution: list[float], + ) -> None: + hhi_1 = idx.herfindahl_hirschman(inequal_distribution) + hhi_2 = idx.herfindahl_hirschman(very_inequal_distribution) + assert hhi_1 < hhi_2 + assert math.isclose(hhi_1, 0.136, abs_tol=1e-10) + assert math.isclose(hhi_2, 0.643, abs_tol=1e-10) + print(f"HHI - Low Inequality: {hhi_1}") + print(f"HHI - High Inequality: {hhi_2}") diff --git a/test/test_ppos_dex_data.py b/test/test_ppos_dex_data.py new file mode 100644 index 0000000..31d2cf7 --- /dev/null +++ b/test/test_ppos_dex_data.py @@ -0,0 +1,19 @@ +from algosdk.v2client.algod import AlgodClient +from algosdk.v2client.indexer import IndexerClient +from algokit_utils import Account + +from src.helpers import post_note +from src.ppos_dex_data import SCHEMA, get_ppos_dex_data, post_ppos_dex_data + + +def test_ppos_dex_data( + algod_client: AlgodClient, + indexer_client: IndexerClient, + faucet: Account, +) -> None: + post_data = post_ppos_dex_data(algod_client, indexer_client, faucet, 0, SCHEMA) + post_note(algod_client, faucet, b"noise") + post_note(algod_client, faucet, b"more noise") + post_note(algod_client, faucet, b"even more noise") + get_data = get_ppos_dex_data(indexer_client, faucet.address, 0, SCHEMA, 0) + assert get_data[0] == post_data