From 85a0793e4e123edaecad43838fa2cc0642b0debf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:28:13 -0300 Subject: [PATCH 01/30] refactor repo configs/readme --- .env.example | 2 + .github/workflows/ci.yml | 6 +- .pre-commit-config.yaml | 17 +- Makefile | 27 + README.md | 80 +- poetry.lock | 1795 +++++++++++++++++++++++++++++--------- pyproject.toml | 49 +- 7 files changed, 1557 insertions(+), 419 deletions(-) create mode 100644 .env.example create mode 100644 Makefile diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f0f08f0 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +USER_ADDRESS= +PRIVATE_KEY= diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 902da10..e2c31ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: python-version: [ - "3.8", "3.9", "3.10", "3.11", "3.12", + "3.10", "3.11", "3.12", ] timeout-minutes: 10 steps: @@ -32,6 +32,10 @@ jobs: cache: poetry cache-dependency-path: poetry.lock + - name: Poetry lock + run: | + poetry lock + - name: Install dependencies run: | poetry install diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 704b21f..0710f6a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,10 @@ repos: -- repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.1.4 - hooks: - # Run the linter. - - id: ruff - # Run the formatter. - - id: ruff-format + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.3.7 + hooks: + # Run the linter. + - id: ruff + args: [--fix] + # Run the formatter. + - id: ruff-format diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..21ed7dc --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: codegen web3_codegen orderbook_codegen subgraph_codegen test lint format remove_unused_imports + +codegen: web3_codegen orderbook_codegen subgraph_codegen + +# web3_codegen: +# poetry run web3_codegen + +orderbook_codegen: + poetry run datamodel-codegen --url="https://raw.githubusercontent.com/cowprotocol/services/v2.245.1/crates/orderbook/openapi.yml" --output cow_py/order_book/generated/model.py --target-python-version 3.12 --output-model-type pydantic_v2.BaseModel --input-file-type openapi + +subgraph_codegen: + poetry run ariadne-codegen + +test: + poetry run pytest -s + +lint: + poetry run ruff check . --fix + +format: remove_unused_imports + poetry run ruff format + +remove_unused_imports: + poetry run pycln --all . + +typecheck: + poetry run pyright \ No newline at end of file diff --git a/README.md b/README.md index 6f5a576..d81ba0d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Welcome to the CoW Protocol Python SDK (cow_py), a developer-friendly Python lib ## 🐄 Features -- Querying CoW Protocol subgraphs. +- Querying CoW Protocol subgraph. - Managing orders on the CoW Protocol. - Interacting with CoW Protocol smart contracts. - Encoding orders metadata and pinning to CID. @@ -27,14 +27,14 @@ pip install cow_py Here's a simple example to get your hooves dirty: ```python -# TODO: this code is aspirational, this API doesn't exist -from cow_py.order_book import OrderBook -# Initialize the OrderBook -order_book = OrderBook() +from cow_py.order_book.api import OrderBookApi, UID + +# Initialize the OrderBookApi +order_book_api = OrderBookApi() # Fetch and display orders -orders = order_book.get_orders() +orders = order_book.get_order_by_uid(UID("0x...")) print(orders) ``` @@ -44,19 +44,47 @@ print(orders) - `contracts/`(TODO): A pasture of Smart contract ABIs for interaction. - `order_book/`(TODO): Functions to wrangle orders on the CoW Protocol. - `order_signing/`(TODO): Tools for signing and validating orders. Anything inside this module should use higher level modules, and the process of actually signing (ie. calling the web3 function to generate the signature, should be handled in contracts, not here). -- `subgraphs/`(WIP): GraphQL clients for querying CoW Protocol data. +- `subgraph/`(WIP): GraphQL client for querying CoW Protocol's Subgraph. - `web3/`: Web3 providers for blockchain interactions. ## 🐄 How to Use -### Querying the Subgraph (WIP) +### Querying the Subgraph + +Using the built-in GraphQL client, you can query the CoW Protocol's Subgraph to get real-time data on the CoW Protocol. You can query the Subgraph by using the `SubgraphClient` class and passing in the URL of the Subgraph. ```python -from cow_py.subgraphs.queries import TotalsQuery +from cow_py.subgraph.client import SubgraphClient -totals_query = TotalsQuery() -totals = await totals_query.execute() -print(totals) +url = build_subgraph_url() # Default network is Chain.MAINNET and env SubgraphEnvironment.PRODUCTION +client = SubgraphClient(url=url) + +# Fetch the total supply of the CoW Protocol, defined in a query in cow_py/subgraph/queries +totals = await client.totals() +print(totals) # Pydantic model, defined in cow_py/subgraph/graphql_client/{query_name}.py +``` + +Or you can leverage `SubgraphClient` to use a custom query and get the results as JSON: + +```python +from pprint import pprint +from cow_py.subgraph.client import SubgraphClient + +url = build_subgraph_url() # Default network is Chain.MAINNET and env SubgraphEnvironment.PRODUCTION +client = SubgraphClient(url=url) + +response = await client.execute(query=""" + query LastDaysVolume($days: Int!) { + dailyTotals(orderBy: timestamp, orderDirection: desc, first: $days) { + timestamp + volumeUsd + } + } + """, variables=dict(days=2) + ) + +data = client.get_data(response) +pprint(data) ``` ### Signing an Order (TODO) @@ -75,6 +103,34 @@ signed_order = sign_order(order_details, private_key="your_private_key") print(signed_order) ``` +## 🐄 Development + +### 🐄 Tests + +Run tests to ensure everything's working: + +```bash +make test # or poetry run pytest +``` + +### 🐄 Formatting/Linting + +Run the formatter and linter: + +```bash +make format # or ruff check . --fix +make lint # or ruff format +``` + +### 🐄 Codegen + +Generate the SDK from the CoW Protocol smart contracts, Subgraph, and Orderbook API: + +```bash +make codegen +``` + + ## 🐄 Contributing to the Herd Interested in contributing? Here's how you can help: diff --git a/poetry.lock b/poetry.lock index 105b953..ce74efa 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,88 +1,88 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiohttp" -version = "3.9.3" +version = "3.9.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, - {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, - {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, - {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, - {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, - {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, - {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, - {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, - {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, - {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, - {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, - {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, - {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, - {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, - {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, - {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, - {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, - {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, - {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, - {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, - {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, - {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, - {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, - {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, - {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, - {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, ] [package.dependencies] @@ -96,6 +96,17 @@ yarl = ">=1.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns", "brotlicffi"] +[[package]] +name = "aiolimiter" +version = "1.1.0" +description = "asyncio rate limiter, a leaky bucket implementation" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "aiolimiter-1.1.0-py3-none-any.whl", hash = "sha256:0b4997961fc58b8df40279e739f9cf0d3e255e63e9a44f64df567a8c17241e24"}, + {file = "aiolimiter-1.1.0.tar.gz", hash = "sha256:461cf02f82a29347340d031626c92853645c099cb5ff85577b831a7bd21132b5"}, +] + [[package]] name = "aiosignal" version = "1.3.1" @@ -110,6 +121,17 @@ files = [ [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + [[package]] name = "anyio" version = "4.3.0" @@ -133,16 +155,45 @@ test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypo trio = ["trio (>=0.23)"] [[package]] -name = "appnope" -version = "0.1.4" -description = "Disable App Nap on macOS >= 10.9" +name = "argcomplete" +version = "3.3.0" +description = "Bash tab completion for argparse" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, - {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, + {file = "argcomplete-3.3.0-py3-none-any.whl", hash = "sha256:c168c3723482c031df3c207d4ba8fa702717ccb9fc0bfe4117166c1f537b4a54"}, + {file = "argcomplete-3.3.0.tar.gz", hash = "sha256:fd03ff4a5b9e6580569d34b273f741e85cd9e072f3feeeee3eba4891c70eda62"}, ] +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + +[[package]] +name = "ariadne-codegen" +version = "0.13.0" +description = "Generate fully typed GraphQL client from schema, queries and mutations!" +optional = false +python-versions = "*" +files = [ + {file = "ariadne_codegen-0.13.0-py2.py3-none-any.whl", hash = "sha256:61d0be4b1ba711bc5df35aaf0fa61d04cf4ea6f25a0038b687f845ab4f859a23"}, + {file = "ariadne_codegen-0.13.0.tar.gz", hash = "sha256:52aa67037dcd88a2f6013118e69ba0ed35a49f37312668a1969907f0e9c9bf8b"}, +] + +[package.dependencies] +autoflake = "*" +black = "*" +click = ">=8.1,<9.0" +graphql-core = ">=3.2.0,<3.3" +httpx = ">=0.23,<1.0" +isort = "*" +pydantic = ">=2.0.0,<3.0.0" +toml = ">=0.10,<1.0" + +[package.extras] +dev = ["ariadne", "freezegun", "mypy", "pylint", "pytest", "pytest-asyncio", "pytest-httpx", "pytest-mock", "requests-toolbelt", "types-toml"] +opentelemetry = ["opentelemetry-api"] +subscriptions = ["websockets (>=11.0,<12.0)"] + [[package]] name = "asttokens" version = "2.4.1" @@ -192,14 +243,29 @@ tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" +name = "autoflake" +version = "2.3.1" +description = "Removes unused imports and unused variables" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840"}, + {file = "autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e"}, +] + +[package.dependencies] +pyflakes = ">=3.0.0" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, ] [[package]] @@ -333,6 +399,63 @@ files = [ {file = "bitarray-2.9.2.tar.gz", hash = "sha256:a8f286a51a32323715d77755ed959f94bef13972e9a2fe71b609e40e6d27957e"}, ] +[[package]] +name = "black" +version = "24.4.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, + {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, + {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, + {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, + {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, + {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, + {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, + {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, + {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, + {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, + {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, + {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, + {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, + {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, + {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, + {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, + {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, + {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, + {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, + {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, + {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, + {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cached-property" +version = "1.5.2" +description = "A decorator for caching properties in classes." +optional = false +python-versions = "*" +files = [ + {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, + {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, +] + [[package]] name = "certifi" version = "2024.2.2" @@ -454,6 +577,113 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "ckzg" +version = "1.0.0" +description = "Python bindings for C-KZG-4844" +optional = false +python-versions = "*" +files = [ + {file = "ckzg-1.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f40731759b608d74b240fe776853b7b081100d8fc06ac35e22fd0db760b7bcaa"}, + {file = "ckzg-1.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8b5d08189ffda2f869711c4149dc41012f73656bc20606f69b174d15488f6ed1"}, + {file = "ckzg-1.0.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c37af3d01a4b0c3f0a4f51cd0b85df44e30d3686f90c2a7cc84530e4e9d7a00e"}, + {file = "ckzg-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1272db9cf5cdd6f564b3de48dae4646d9e04aa10432c0f278ca7c752cf6a333c"}, + {file = "ckzg-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5a464900627b66848f4187dd415bea5edf78f3918927bd27461749e75730459"}, + {file = "ckzg-1.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e1d4abc0d58cb04678915ef7c4236834e58774ef692194b9bca15f837a0aaff8"}, + {file = "ckzg-1.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9205a6ea38c5e030f6f719b8f8ea6207423378e0339d45db81c946a0818d0f31"}, + {file = "ckzg-1.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9d8c45cd427f34682add5715360b358ffc2cbd9533372470eae12cbb74960042"}, + {file = "ckzg-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:91868e2aa17497ea864bb9408269176d961ba56d89543af292556549b18a03b7"}, + {file = "ckzg-1.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cffc3a23ccc967fd7993a9839aa0c133579bfcfd9f124c1ad8916a21c40ed594"}, + {file = "ckzg-1.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9936e5adf2030fc2747aaadc0cbfee6b5a06507e2b74e70998ac4e37cd7203a6"}, + {file = "ckzg-1.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a8d97acb5f84cf2c4db0c962ce3aefa2819b10c5b6b9dccf55e83f2a999676"}, + {file = "ckzg-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a49bd5dcf288a40df063f7ebd88476fa96a5d22dcbafc843193964993f36e26"}, + {file = "ckzg-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1495b5bb9016160a71d5f2727b935cb532d5578b7d29b280f0531b50c5ef1ee"}, + {file = "ckzg-1.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ad39d0549237d136e32263a71182833e26fab8fe8ab62db4d6161b9a7f74623e"}, + {file = "ckzg-1.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d5a838b4de4cc0b01a84531a115cf19aa508049c20256e493a2cca98cf806e3e"}, + {file = "ckzg-1.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dfadf8aab3f5a9a94796ba2b688f3679d1d681afe92dfa223da7d4f751fe487d"}, + {file = "ckzg-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:6aff64ce8eae856bb5684c76f8e07d4ac31ff07ad46a24bf62c9ea2104975bc9"}, + {file = "ckzg-1.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7b1eed4e35a3fb35f867770eee12018098bd261fa66b768f75b343e0198ff258"}, + {file = "ckzg-1.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d7f609e943880303ea3f60b0426c9b53a596c74bb09ceed00c917618b519373"}, + {file = "ckzg-1.0.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8c422673236ea67608c434956181b050039b2f57b1006503eeec574b1af8467"}, + {file = "ckzg-1.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9321226e65868e66edbe18301b8f76f3298d316e6d3a1261371c7fdbc913816"}, + {file = "ckzg-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f6fd5bc8c2362483c61adbd00188f7448c968807f00ee067666355c63cf45e0"}, + {file = "ckzg-1.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:be79e3c4a735f5bf4c71cc07a89500448555f2d4f4f765da5867194c7e46ec5c"}, + {file = "ckzg-1.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2896c108425b64f6b741cc389beee2b8467a41f8d4f901f4a4ecc037311dc681"}, + {file = "ckzg-1.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd3f0db4cf514054c386d1a38f9a144725b5109379dd9d2c1b4b0736119f848e"}, + {file = "ckzg-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:7a864097cb88be5b7aeff6103bf03d7dfb1c6dda6c8ef82378838ce32e158a15"}, + {file = "ckzg-1.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0404db8ded404b36617d60d678d5671652798952571ae4993d4d379ef6563f4f"}, + {file = "ckzg-1.0.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3aee88b228a9ca81d677d57d8d3f6ee483165d8b3955ea408bda674d0f9b4ee5"}, + {file = "ckzg-1.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6b4d15188803602afc56e113fc588617219a6316789766fc95e0fa010a93ab"}, + {file = "ckzg-1.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae6d24e83af8c097b62fdc2183378b9f2d8253fa14ccfc07d075a579f98d876"}, + {file = "ckzg-1.0.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8facda4eafc451bb5f6019a2b779f1b6da7f91322aef0eab1f1d9f542220de1c"}, + {file = "ckzg-1.0.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:4f552fa3b654bc376fcb73e975d521eacff324dba111fa2f0c80c84ad586a0b1"}, + {file = "ckzg-1.0.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:851b7eaca0034b51b6867623b0fae2260466126d8fc669646890464812afd932"}, + {file = "ckzg-1.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:df63d78d9a3d1ffcf32ccb262512c780de42798543affc1209f6fd0cddac49b4"}, + {file = "ckzg-1.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3aefd29f6d339358904ed88e5a642e5bf338fd85151a982a040d4352ae95e53f"}, + {file = "ckzg-1.0.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f2bbbcd24f5ac7f29a0f3f3f51d8934764f5d579e63601a415ace4dad0c2785"}, + {file = "ckzg-1.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ed54765a3067f20786a0c6ee24a8440cfedfe39c5865744c99f605e6ec4249"}, + {file = "ckzg-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5101500009a8851843b5aab44bc320b281cfe46ffbbab35f29fa763dc2ac4a2"}, + {file = "ckzg-1.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a35e0f027749a131a5086dcb3f094ec424280cdf7708c24e0c45421a0e9bebf"}, + {file = "ckzg-1.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:71860eda6019cc57b197037427ad4078466de232a768fa7c77c7094585689a8d"}, + {file = "ckzg-1.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87729a2e861093d9ee4667dcf047a0073644da7f9de5b9c269821e3c9c3f7164"}, + {file = "ckzg-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d1bd47cfa82f92f14ec77fffee6480b03144f414861fc6664190e89d3aa542d"}, + {file = "ckzg-1.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4644e6e0d66d4a36dc37c2ef64807d1db39bf76b10a933b2f7fbb0b4ee9d991"}, + {file = "ckzg-1.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:96d88c6ea2fd49ecfa16767d05a2d056f1bd1a42b0cf10ab99fb4f88fefab5d7"}, + {file = "ckzg-1.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c921b9172aa155ede173abe9d3495c04a55b1afde317339443451e889b531891"}, + {file = "ckzg-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a09cce801a20929d49337bd0f1df6d079d5a2ebaa58f58ab8649c706485c759"}, + {file = "ckzg-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a02d21ceda0c3bec82342f050de5b22eb4a928be00913fa8992ab0f717095f8"}, + {file = "ckzg-1.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bf751e989a428be569e27010c98192451af4c729d5c27a6e0132647fe93b6e84"}, + {file = "ckzg-1.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37192e9fcbced22e64cd00785ea082bd22254ce7d9cfdfd5364683bea8e1d043"}, + {file = "ckzg-1.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:54808ba5b3692ff31713de6d57c30c21060f11916d2e233f5554fcc85790fcda"}, + {file = "ckzg-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:d3b343a4a26d5994bdb39216f5b03bf2345bb6e37ae90fcf7181df37c244217a"}, + {file = "ckzg-1.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:481dfd101acc8a473146b35e61c11cee2ef41210b77775f306c4f1f7f8bdbf28"}, + {file = "ckzg-1.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd392f3ae05a851f9aa1fc114b565cb7e6744cec39790af56af2adf9dd400f3d"}, + {file = "ckzg-1.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2ca50b9d0e947d3b5530dacf25cc00391d041e861751c4872eba4a4567a2efe"}, + {file = "ckzg-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91dafec4f72e30176fb9861d0e2ed46cd506f6837ed70066f2136378f5cd84df"}, + {file = "ckzg-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c72b07d5cac293d7e49a5510d56163f18cdbf9c7a6c6446422964d5667097c2"}, + {file = "ckzg-1.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:67144d1b545cdd6cb5af38ed2c03b234a24f72b6021ea095b70f0cfe11181bd6"}, + {file = "ckzg-1.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43935d730a9ee13ca264455356bdd01055c55c241508f5682d67265379b29dcf"}, + {file = "ckzg-1.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5911419a785c732f0f5edcda89ecc489e7880191b8c0147f629025cb910f913"}, + {file = "ckzg-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a00c295c5657162c24b162ca9a030fbfbc6930e0782378ce3e3d64b14cf470e"}, + {file = "ckzg-1.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8d272107d63500ba9c62adef39f01835390ee467c2583fd96c78f05773d87b0d"}, + {file = "ckzg-1.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:52bfcad99cc0f5611c3a7e452de4d7fa66ce020327c1c1de425b84b20794687b"}, + {file = "ckzg-1.0.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ae70915d41702d33775d9b81c106b2bff5aa7981f82b06e0c5892daa921ff55"}, + {file = "ckzg-1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5deaae9151215d1fad3934fa423a87ee752345f665448a30f58bf5c3177c4623"}, + {file = "ckzg-1.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f7e8861174fe26e6bb0f13655aa1f07fd7c3300852748b0f6e47b998153b56b"}, + {file = "ckzg-1.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dc6c211e16ef7750b2579346eaa05e4f1e7f1726effa55c2cb42354603800b01"}, + {file = "ckzg-1.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d392ef8281f306a0377f4e5fe816e03e4dce2754a4b2ab209b16d9628b7a0bac"}, + {file = "ckzg-1.0.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52cbe279f5d3ec9dd7745de8e796383ba201302606edaa9838b5dd5a34218241"}, + {file = "ckzg-1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a5d3367cee7ebb48131acc78ca3fb0565e3af3fd8fa8eb4ca25bb88577692c4"}, + {file = "ckzg-1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55e8c6d8df9dc1bdd3862114e239c292f9bdd92d67055ca4e0e7503826e6524f"}, + {file = "ckzg-1.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b040396df453b51cd5f1461bec9b942173b95ca181c7f65caa10c0204cb6144a"}, + {file = "ckzg-1.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7951c53321136aabdab64dc389c92ffeda5859d59304b97092e893a6b09e9722"}, + {file = "ckzg-1.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:56256067d31eb6eed1a42c9f3038936aeb7decee322aa13a3224b51cfa3e8026"}, + {file = "ckzg-1.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c166f254ce3434dd0d56ef64788fc9637d60721f4e7e126b15a847abb9a44962"}, + {file = "ckzg-1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab02c7ad64fb8616a430b05ad2f8fa4f3fc0a22e3dd4ea7a5d5fa4362534bb21"}, + {file = "ckzg-1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63bb5e6bc4822c732270c70ef12522b0215775ff61cae04fb54983973aef32e3"}, + {file = "ckzg-1.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:409f1f18dbc92df5ddbf1ff0d154dc2280a495ec929a4fa27abc69eeacf31ff0"}, + {file = "ckzg-1.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2eceae0ef7189d47bd89fd9efd9d8f54c5b06bc92c435ec00c62815363cd9d79"}, + {file = "ckzg-1.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e189c00a0030d1a593b020264d7f9b30fa0b980d108923f353c565c206a99147"}, + {file = "ckzg-1.0.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36735543ce3aec4730e7128690265ef90781d28e9b56c039c72b6b2ce9b06839"}, + {file = "ckzg-1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fef5f276e24f4bdd19e28ddcc5212e9b6c8514d3c7426bd443c9221f348c176f"}, + {file = "ckzg-1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d02164a0d84e55965c14132f6d43cc367be3d12eb318f79ba2f262dac47665c2"}, + {file = "ckzg-1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:88fafab3493a12d5212374889783352bb4b59dddc9e61c86d063358eff6da7bb"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -465,6 +695,73 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.4.4" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "cytoolz" version = "0.12.3" @@ -584,6 +881,39 @@ toolz = ">=0.8.0" [package.extras] cython = ["cython"] +[[package]] +name = "datamodel-code-generator" +version = "0.25.5" +description = "Datamodel Code Generator" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "datamodel_code_generator-0.25.5-py3-none-any.whl", hash = "sha256:3b62b42c8ebf2bb98cfbc24467b523c5b76780c585b72f4ac2fc1f1f576702ab"}, + {file = "datamodel_code_generator-0.25.5.tar.gz", hash = "sha256:545f897481a94781e32b3c26a452ce049320b091310729f7fc6fa780f6a87898"}, +] + +[package.dependencies] +argcomplete = ">=1.10,<4.0" +black = ">=19.10b0" +genson = ">=1.2.1,<2.0" +inflect = ">=4.1.0,<6.0" +isort = ">=4.3.21,<6.0" +jinja2 = ">=2.10.1,<4.0" +packaging = "*" +pydantic = [ + {version = ">=1.10.0,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, + {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, +] +pyyaml = ">=6.0.1" +toml = {version = ">=0.10.0,<1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +debug = ["PySnooper (>=0.4.1,<2.0.0)"] +graphql = ["graphql-core (>=3.2.3,<4.0.0)"] +http = ["httpx"] +validation = ["openapi-spec-validator (>=0.2.8,<0.7.0)", "prance (>=0.18.2)"] + [[package]] name = "decorator" version = "5.1.1" @@ -606,41 +936,77 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + [[package]] name = "eth-abi" -version = "5.0.0" +version = "5.1.0" description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth_abi-5.0.0-py3-none-any.whl", hash = "sha256:936a715d7366ac13cac665cbdaf01cc4aabbe8c2d810d716287a9634f9665e01"}, - {file = "eth_abi-5.0.0.tar.gz", hash = "sha256:89c4454d794d9ed92ad5cb2794698c5cee6b7b3ca6009187d0e282adc7f9b6dc"}, + {file = "eth_abi-5.1.0-py3-none-any.whl", hash = "sha256:84cac2626a7db8b7d9ebe62b0fdca676ab1014cc7f777189e3c0cd721a4c16d8"}, + {file = "eth_abi-5.1.0.tar.gz", hash = "sha256:33ddd756206e90f7ddff1330cc8cac4aa411a824fe779314a0a52abea2c8fc14"}, ] [package.dependencies] eth-typing = ">=3.0.0" eth-utils = ">=2.0.0" -parsimonious = ">=0.9.0,<0.10.0" +parsimonious = ">=0.10.0,<0.11.0" [package.extras] -dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "hypothesis (>=4.18.2,<5.0.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-pythonpath (>=0.7.1)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "hypothesis (>=4.18.2,<5.0.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-pythonpath (>=0.7.1)", "pytest-timeout (>=2.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] -test = ["eth-hash[pycryptodome]", "hypothesis (>=4.18.2,<5.0.0)", "pytest (>=7.0.0)", "pytest-pythonpath (>=0.7.1)", "pytest-xdist (>=2.4.0)"] +test = ["eth-hash[pycryptodome]", "hypothesis (>=4.18.2,<5.0.0)", "pytest (>=7.0.0)", "pytest-pythonpath (>=0.7.1)", "pytest-timeout (>=2.0.0)", "pytest-xdist (>=2.4.0)"] tools = ["hypothesis (>=4.18.2,<5.0.0)"] [[package]] name = "eth-account" -version = "0.11.0" +version = "0.11.2" description = "eth-account: Sign Ethereum transactions and messages with local private keys" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-account-0.11.0.tar.gz", hash = "sha256:2ffc7a0c7538053a06a7d11495c16c7ad9897dd42be0f64ca7551e9f6e0738c3"}, - {file = "eth_account-0.11.0-py3-none-any.whl", hash = "sha256:76dd261ea096ee09e51455b0a4c99f22185516fdc062f63df0817c28f605e430"}, + {file = "eth-account-0.11.2.tar.gz", hash = "sha256:b43daf2c0ae43f2a24ba754d66889f043fae4d3511559cb26eb0122bae9afbbd"}, + {file = "eth_account-0.11.2-py3-none-any.whl", hash = "sha256:95157c262a9823c1e08be826d4bc304bf32f0c32e80afb38c126a325a64f651a"}, ] [package.dependencies] bitarray = ">=2.4.0" +ckzg = ">=0.4.3" eth-abi = ">=4.0.0-b.2" eth-keyfile = ">=0.6.0" eth-keys = ">=0.4.0" @@ -654,19 +1020,39 @@ dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "coverage", "hypothesis (>=4. docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] test = ["coverage", "hypothesis (>=4.18.0,<5)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] +[[package]] +name = "eth-bloom" +version = "3.0.0" +description = "A python implementation of the bloom filter used by Ethereum" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "eth-bloom-3.0.0.tar.gz", hash = "sha256:94bab384b01f2eb1012abbd6bb504e4c743878414d8695ee5a5d25f4247b3886"}, + {file = "eth_bloom-3.0.0-py3-none-any.whl", hash = "sha256:bb884ece93d292dfbbe4696744db874a88ac5bfc45f6f1b0ee147d801604a46c"}, +] + +[package.dependencies] +eth-hash = {version = ">=0.4.0", extras = ["pycryptodome"]} + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "hypothesis (>=3.31.2)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["towncrier (>=21,<22)"] +test = ["hypothesis (>=3.31.2)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + [[package]] name = "eth-hash" -version = "0.6.0" +version = "0.7.0" description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" optional = false python-versions = ">=3.8, <4" files = [ - {file = "eth-hash-0.6.0.tar.gz", hash = "sha256:ae72889e60db6acbb3872c288cfa02ed157f4c27630fcd7f9c8442302c31e478"}, - {file = "eth_hash-0.6.0-py3-none-any.whl", hash = "sha256:9f8daaa345764f8871dc461855049ac54ae4291d780279bce6fce7f24e3f17d3"}, + {file = "eth-hash-0.7.0.tar.gz", hash = "sha256:bacdc705bfd85dadd055ecd35fd1b4f846b671add101427e089a4ca2e8db310a"}, + {file = "eth_hash-0.7.0-py3-none-any.whl", hash = "sha256:b8d5a230a2b251f4a291e3164a23a14057c4a6de4b0aa4a16fa4dc9161b57e2f"}, ] [package.dependencies] pycryptodome = {version = ">=3.6.6,<4", optional = true, markers = "extra == \"pycryptodome\""} +safe-pysha3 = {version = ">=1.0.0", optional = true, markers = "python_version >= \"3.9\" and extra == \"pysha3\""} [package.extras] dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] @@ -677,13 +1063,13 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "eth-keyfile" -version = "0.7.0" +version = "0.8.0" description = "eth-keyfile: A library for handling the encrypted keyfiles used to store ethereum private keys" optional = false python-versions = ">=3.8, <4" files = [ - {file = "eth-keyfile-0.7.0.tar.gz", hash = "sha256:6bdb8110c3a50439deb68a04c93c9d5ddd5402353bfae1bf4cfca1d6dff14fcf"}, - {file = "eth_keyfile-0.7.0-py3-none-any.whl", hash = "sha256:6a89b231a2fe250c3a8f924f2695bb9cce33ddd0d6f7ebbcdacd183d7f83d537"}, + {file = "eth-keyfile-0.8.0.tar.gz", hash = "sha256:02e3c2e564c7403b92db3fef8ecae3d21123b15787daecd5b643a57369c530f9"}, + {file = "eth_keyfile-0.8.0-py3-none-any.whl", hash = "sha256:9e09f5bc97c8309876c06bdea7a94f0051c25ba3109b5df37afb815418322efe"}, ] [package.dependencies] @@ -739,15 +1125,46 @@ dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "ip docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] test = ["eth-hash[pycryptodome]", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] +[[package]] +name = "eth-tester" +version = "0.11.0b1" +description = "eth-tester: Tools for testing Ethereum applications." +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "eth-tester-0.11.0b1.tar.gz", hash = "sha256:8f9985037bbe6057cfff705063add5de506d75b725740bc44fb5ac03cc7b5130"}, + {file = "eth_tester-0.11.0b1-py3-none-any.whl", hash = "sha256:5ac85ff048ce2a5415aa5624d99093ad8b7d1f1f80e23b6d220ca5432839fb00"}, +] + +[package.dependencies] +eth-abi = ">=3.0.1" +eth-account = ">=0.11.2" +eth-hash = [ + {version = ">=0.1.4,<1.0.0", extras = ["pysha3"], optional = true, markers = "implementation_name == \"cpython\" and extra == \"py-evm\""}, + {version = ">=0.1.4,<1.0.0", extras = ["pycryptodome"], optional = true, markers = "implementation_name == \"pypy\" and extra == \"py-evm\""}, +] +eth-keys = ">=0.4.0" +eth-utils = ">=2.0.0" +py-evm = {version = ">=0.10.0b0,<0.11.0b0", optional = true, markers = "extra == \"py-evm\""} +rlp = ">=3.0.0" +semantic-version = ">=2.6.0" + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome] (>=0.1.4,<1.0.0)", "eth-hash[pycryptodome] (>=0.1.4,<1.0.0)", "eth-hash[pysha3] (>=0.1.4,<1.0.0)", "ipython", "pre-commit (>=3.4.0)", "py-evm (>=0.10.0b0,<0.11.0b0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.0.0,<3)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["towncrier (>=21,<22)"] +py-evm = ["eth-hash[pycryptodome] (>=0.1.4,<1.0.0)", "eth-hash[pysha3] (>=0.1.4,<1.0.0)", "py-evm (>=0.10.0b0,<0.11.0b0)"] +pyevm = ["eth-hash[pycryptodome] (>=0.1.4,<1.0.0)", "eth-hash[pysha3] (>=0.1.4,<1.0.0)", "py-evm (>=0.10.0b0,<0.11.0b0)"] +test = ["eth-hash[pycryptodome] (>=0.1.4,<1.0.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.0.0,<3)"] + [[package]] name = "eth-typing" -version = "4.0.0" +version = "4.1.0" description = "eth-typing: Common type annotations for ethereum python packages" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-typing-4.0.0.tar.gz", hash = "sha256:9af0b6beafbc5c2e18daf19da5f5a68315023172c4e79d149e12ad10a3d3f731"}, - {file = "eth_typing-4.0.0-py3-none-any.whl", hash = "sha256:7e556bea322b6e8c0a231547b736c258e10ce9eed5ddc254f51031b12af66a16"}, + {file = "eth-typing-4.1.0.tar.gz", hash = "sha256:ed52b0c6b049240fd810bc87c8857c7ea39370f060f70b9ca3876285269f2938"}, + {file = "eth_typing-4.1.0-py3-none-any.whl", hash = "sha256:1f1b16bf37bfe0be730731fd24c7398e931a2b45a8feebf82df2e77a611a23be"}, ] [package.extras] @@ -757,13 +1174,13 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "eth-utils" -version = "3.0.0" +version = "4.1.0" description = "eth-utils: Common utility functions for python code that interacts with Ethereum" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-utils-3.0.0.tar.gz", hash = "sha256:8721869568448349bceae63c277b75758d11e0dc190e7ef31e161b89619458f1"}, - {file = "eth_utils-3.0.0-py3-none-any.whl", hash = "sha256:9a284106acf6f6ce91ddf792489cf8bd4c681fd5ae7653d2f3d5d100be5c3905"}, + {file = "eth-utils-4.1.0.tar.gz", hash = "sha256:c170168198ddecac1ea911f74937e9364de81dbd03f42450fe40725c4d6e6220"}, + {file = "eth_utils-4.1.0-py3-none-any.whl", hash = "sha256:f2e0f617edc81e53fad0faca7f7b169e56bef59ecc530d919a7482640236a228"}, ] [package.dependencies] @@ -807,18 +1224,18 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.13.1" +version = "3.13.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, + {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -907,6 +1324,27 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "genson" +version = "1.2.2" +description = "GenSON is a powerful, user-friendly JSON Schema generator." +optional = false +python-versions = "*" +files = [ + {file = "genson-1.2.2.tar.gz", hash = "sha256:8caf69aa10af7aee0e1a1351d1d06801f4696e005f06cedef438635384346a16"}, +] + +[[package]] +name = "graphql-core" +version = "3.2.3" +description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "graphql-core-3.2.3.tar.gz", hash = "sha256:06d2aad0ac723e35b1cb47885d3e5c45e956a53bc1b209a9fc5369007fe46676"}, + {file = "graphql_core-3.2.3-py3-none-any.whl", hash = "sha256:5766780452bd5ec8ba133f8bf287dc92713e3868ddd83aee4faab9fc3e303dc3"}, +] + [[package]] name = "h11" version = "0.14.0" @@ -937,13 +1375,13 @@ test = ["eth-utils (>=1.0.1,<3)", "hypothesis (>=3.44.24,<=6.31.6)", "pytest (>= [[package]] name = "httpcore" -version = "1.0.3" +version = "1.0.5" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.3-py3-none-any.whl", hash = "sha256:9a6a501c3099307d9fd76ac244e08503427679b1e81ceb1d922485e2f2462ad2"}, - {file = "httpcore-1.0.3.tar.gz", hash = "sha256:5c0f9546ad17dac4d0772b0808856eb616eb8b48ce94f49ed819fd6982a8a544"}, + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, ] [package.dependencies] @@ -954,17 +1392,17 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.24.0)"] +trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" -version = "0.26.0" +version = "0.27.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, - {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] [package.dependencies] @@ -996,32 +1434,29 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] -name = "importlib-resources" -version = "6.1.1" -description = "Read resources from Python packages" +name = "inflect" +version = "5.6.2" +description = "Correctly generate plurals, singular nouns, ordinals, indefinite articles; convert numbers to words" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, - {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, + {file = "inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238"}, + {file = "inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9"}, ] -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] +testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "iniconfig" @@ -1036,42 +1471,55 @@ files = [ [[package]] name = "ipython" -version = "8.12.3" +version = "8.23.0" description = "IPython: Productive Interactive Computing" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, - {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, + {file = "ipython-8.23.0-py3-none-any.whl", hash = "sha256:07232af52a5ba146dc3372c7bf52a0f890a23edf38d77caef8d53f9cdc2584c1"}, + {file = "ipython-8.23.0.tar.gz", hash = "sha256:7468edaf4f6de3e1b912e57f66c241e6fd3c7099f2ec2136e239e142e800274d"}, ] [package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt-toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} +traitlets = ">=5.13.0" +typing-extensions = {version = "*", markers = "python_version < \"3.12\""} [package.extras] -all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] kernel = ["ipykernel"] +matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] +test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] [[package]] name = "jedi" @@ -1092,6 +1540,23 @@ docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alab qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "jsonschema" version = "4.21.1" @@ -1105,9 +1570,7 @@ files = [ [package.dependencies] attrs = ">=22.2.0" -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} jsonschema-specifications = ">=2023.03.6" -pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} referencing = ">=0.28.4" rpds-py = ">=0.7.1" @@ -1127,9 +1590,48 @@ files = [ ] [package.dependencies] -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} referencing = ">=0.31.0" +[[package]] +name = "libcst" +version = "1.3.1" +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." +optional = false +python-versions = ">=3.9" +files = [ + {file = "libcst-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:de93193cba6d54f2a4419e94ba2de642b111f52e4fa01bb6e2c655914585f65b"}, + {file = "libcst-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2d64d86dcd6c80a5dac2e243c5ed7a7a193242209ac33bad4b0639b24f6d131"}, + {file = "libcst-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db084f7bbf825c7bd5ed256290066d0336df6a7dc3a029c9870a64cd2298b87f"}, + {file = "libcst-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16880711be03a1f5da7028fe791ba5b482a50d830225a70272dc332dfd927652"}, + {file = "libcst-1.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:189bb28c19c5dd3c64583f969b72f7732dbdb1dee9eca3acc85099e4cef9148b"}, + {file = "libcst-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:181372386c986e3de07d7a93f269214cd825adc714f1f9da8252b44f05e181c4"}, + {file = "libcst-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c2020f7449270e3ff0bdc481ae244d812f2d9a8b7dbff0ea66b830f4b350f54"}, + {file = "libcst-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:be3bf9aaafebda6a21e241e819f0ab67e186e898c3562704e41241cf8738353a"}, + {file = "libcst-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a0d250fb6a2c1d158f30d25ba5e33e3ed3672d2700d480dd47beffd1431a008"}, + {file = "libcst-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad5741b251d901f3da1819ac539192230cc6f8f81aaf04eb4ec0009c1c97285"}, + {file = "libcst-1.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b740dc0c3d1adbd91442fb878343d5a11e23a0e3ac5484be301fd8d148bcb085"}, + {file = "libcst-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9e6bc95fa7dde79cde63a34a0412cd4a3d9fcc27be781a590f8c45c840c83658"}, + {file = "libcst-1.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4186076ce12141609ce950d61867b2a73ea199a7a9870dbafa76ad600e075b3c"}, + {file = "libcst-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ed52a1a2fe4d8603de51649db5e438317b8116ebb9fc09ec68703535fe6c1c8"}, + {file = "libcst-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0886a9963597367b227345f19b24931b3ed6a4703fff237760745f90f0e6a20"}, + {file = "libcst-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:904c4cc5c801a5747e64b43e0accc87c67a4c804842d977ee215872c4cf8cf88"}, + {file = "libcst-1.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cdb7e8a118b60e064a02f6cbfa4d328212a3a115d125244495190f405709d5f"}, + {file = "libcst-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:431badf0e544b79c0ac9682dbd291ff63ddbc3c3aca0d13d3cc7a10c3a9db8a2"}, + {file = "libcst-1.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:701f5335e4fd566871497b9af1e871c98e1ef10c30b3b244f39343d709213401"}, + {file = "libcst-1.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c6e709623b68ca9148e8ecbdc145f7b83befb26032e4bf6a8122500ba558b17"}, + {file = "libcst-1.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ede0f026a82b03b33a559ec566860085ece2e76d8f9bc21cb053eedf9cde8c79"}, + {file = "libcst-1.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c12b7b01d8745f82dd86a82acd2a9f8e8e7d6c94ddcfda996896e83d1a8d5c42"}, + {file = "libcst-1.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2995ca687118a9d3d41876f7270bc29305a2d402f4b8c81a3cff0aeee6d4c81"}, + {file = "libcst-1.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2dbac1ac0a9d59ea7bbc3f87cdcca5bfe98835e31c668e95cb6f3d907ffc53fc"}, + {file = "libcst-1.3.1.tar.gz", hash = "sha256:03b1df1ae02456f1d465fcd5ead1d0d454bb483caefd8c8e6bde515ffdb53d1b"}, +] + +[package.dependencies] +pyyaml = ">=5.2" + +[package.extras] +dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.0.0)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.3)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<1.5)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.5.1)", "usort (==1.0.8.post1)"] + [[package]] name = "lru-dict" version = "1.2.0" @@ -1224,20 +1726,124 @@ files = [ [package.extras] test = ["pytest"] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + [[package]] name = "matplotlib-inline" -version = "0.1.6" +version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, - {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, ] [package.dependencies] traitlets = "*" +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "multidict" version = "6.0.5" @@ -1337,6 +1943,17 @@ files = [ {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -1353,23 +1970,24 @@ setuptools = "*" [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "parsimonious" -version = "0.9.0" +version = "0.10.0" description = "(Soon to be) the fastest pure-Python PEG parser I could muster" optional = false python-versions = "*" files = [ - {file = "parsimonious-0.9.0.tar.gz", hash = "sha256:b2ad1ae63a2f65bd78f5e0a8ac510a98f3607a43f1db2a8d46636a5d9e4a30c1"}, + {file = "parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f"}, + {file = "parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c"}, ] [package.dependencies] @@ -1377,18 +1995,29 @@ regex = ">=2022.3.15" [[package]] name = "parso" -version = "0.8.3" +version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, ] [package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] [[package]] name = "pexpect" @@ -1404,28 +2033,6 @@ files = [ [package.dependencies] ptyprocess = ">=0.5" -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -optional = false -python-versions = "*" -files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] - -[[package]] -name = "pkgutil-resolve-name" -version = "1.3.10" -description = "Resolve a name to an object." -optional = false -python-versions = ">=3.6" -files = [ - {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, - {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, -] - [[package]] name = "platformdirs" version = "4.2.0" @@ -1458,13 +2065,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.5.0" +version = "3.7.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, - {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, + {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, + {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, ] [package.dependencies] @@ -1490,22 +2097,22 @@ wcwidth = "*" [[package]] name = "protobuf" -version = "4.25.3" +version = "5.26.1" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, - {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, - {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, - {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, - {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, - {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, - {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, - {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, - {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, + {file = "protobuf-5.26.1-cp310-abi3-win32.whl", hash = "sha256:3c388ea6ddfe735f8cf69e3f7dc7611e73107b60bdfcf5d0f024c3ccd3794e23"}, + {file = "protobuf-5.26.1-cp310-abi3-win_amd64.whl", hash = "sha256:e6039957449cb918f331d32ffafa8eb9255769c96aa0560d9a5bf0b4e00a2a33"}, + {file = "protobuf-5.26.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:38aa5f535721d5bb99861166c445c4105c4e285c765fbb2ac10f116e32dcd46d"}, + {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fbfe61e7ee8c1860855696e3ac6cfd1b01af5498facc6834fcc345c9684fb2ca"}, + {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f7417703f841167e5a27d48be13389d52ad705ec09eade63dfc3180a959215d7"}, + {file = "protobuf-5.26.1-cp38-cp38-win32.whl", hash = "sha256:d693d2504ca96750d92d9de8a103102dd648fda04540495535f0fec7577ed8fc"}, + {file = "protobuf-5.26.1-cp38-cp38-win_amd64.whl", hash = "sha256:9b557c317ebe6836835ec4ef74ec3e994ad0894ea424314ad3552bc6e8835b4e"}, + {file = "protobuf-5.26.1-cp39-cp39-win32.whl", hash = "sha256:b9ba3ca83c2e31219ffbeb9d76b63aad35a3eb1544170c55336993d7a18ae72c"}, + {file = "protobuf-5.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ee014c2c87582e101d6b54260af03b6596728505c79f17c8586e7523aaa8f8c"}, + {file = "protobuf-5.26.1-py3-none-any.whl", hash = "sha256:da612f2720c0183417194eeaa2523215c4fcc1a1949772dc65f05047e08d5932"}, + {file = "protobuf-5.26.1.tar.gz", hash = "sha256:8ca2a1d97c290ec7b16e4e5dff2e5ae150cc1582f55b5ab300d45cb0dfa90e51"}, ] [[package]] @@ -1533,6 +2140,107 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "py-ecc" +version = "7.0.0" +description = "py-ecc: Elliptic curve crypto in python including secp256k1, alt_bn128, and bls12_381" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "py-ecc-7.0.0.tar.gz", hash = "sha256:c141e7330559758d2233adaf0af1f05ec4cade67e7fa35fc118750ce79d83257"}, + {file = "py_ecc-7.0.0-py3-none-any.whl", hash = "sha256:a655fa499ab2e695c9d4e49be19e40ad195dd61076642edcbcbb5b7c645820cf"}, +] + +[package.dependencies] +cached-property = ">=1.5.1" +eth-typing = ">=3.0.0" +eth-utils = ">=2.0.0" + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "py-evm" +version = "0.10.0b6" +description = "Python implementation of the Ethereum Virtual Machine" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "py_evm-0.10.0b6-py3-none-any.whl", hash = "sha256:98d886d7d50937924cfb3d6607c9ec7259c91b082ce4972a569a89b363d7be32"}, +] + +[package.dependencies] +cached-property = ">=1.5.1" +ckzg = ">=0.4.3" +eth-bloom = ">=1.0.3" +eth-keys = ">=0.4.0" +eth-typing = ">=3.3.0" +eth-utils = ">=2.0.0" +lru-dict = ">=1.1.6" +py-ecc = ">=1.4.7" +rlp = ">=3.0.0" +trie = ">=2.0.0" + +[package.extras] +benchmark = ["termcolor (>=1.1.0)", "web3 (>=6.0.0)"] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "cached-property (>=1.5.1)", "ckzg (>=0.4.3)", "eth-bloom (>=1.0.3)", "eth-keys (>=0.4.0)", "eth-typing (>=3.3.0)", "eth-utils (>=2.0.0)", "factory-boy (>=3.0.0)", "hypothesis (>=5,<6)", "ipython", "lru-dict (>=1.1.6)", "pre-commit (>=3.4.0)", "py-ecc (>=1.4.7)", "py-evm (>=0.8.0b1)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.20.0)", "pytest-cov (>=4.0.0)", "pytest-timeout (>=2.0.0)", "pytest-xdist (>=3.0)", "rlp (>=3.0.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-asyncio (>=0.2.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "trie (>=2.0.0)", "twine", "wheel"] +docs = ["py-evm (>=0.8.0b1)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-asyncio (>=0.2.0)", "towncrier (>=21,<22)"] +eth = ["cached-property (>=1.5.1)", "ckzg (>=0.4.3)", "eth-bloom (>=1.0.3)", "eth-keys (>=0.4.0)", "eth-typing (>=3.3.0)", "eth-utils (>=2.0.0)", "lru-dict (>=1.1.6)", "py-ecc (>=1.4.7)", "rlp (>=3.0.0)", "trie (>=2.0.0)"] +eth-extra = ["blake2b-py (>=0.2.0)", "coincurve (>=18.0.0)"] +test = ["factory-boy (>=3.0.0)", "hypothesis (>=5,<6)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.20.0)", "pytest-cov (>=4.0.0)", "pytest-timeout (>=2.0.0)", "pytest-xdist (>=3.0)"] + +[[package]] +name = "py-geth" +version = "4.4.0" +description = "py-geth: Run Go-Ethereum as a subprocess" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "py-geth-4.4.0.tar.gz", hash = "sha256:c08d84f6dad4f86a9b8ffd74c0a0f160d600db0ee45dfc2a66d5e13522aeb039"}, + {file = "py_geth-4.4.0-py3-none-any.whl", hash = "sha256:ae8771b028c68f6710e6434d2aa1310ce2ba3cc9099d244d9bb5a9e340786a92"}, +] + +[package.dependencies] +semantic-version = ">=2.6.0" + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "flaky (>=3.2.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "requests (>=2.20)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["towncrier (>=21,<22)"] +test = ["flaky (>=3.2.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "pybars3" +version = "0.9.7" +description = "Handlebars.js templating for Python 3 and 2" +optional = false +python-versions = "*" +files = [ + {file = "pybars3-0.9.7.tar.gz", hash = "sha256:6ac847e905e53b9c5b936af112c910475e27bf767f79f4528c16f9af1ec0e252"}, +] + +[package.dependencies] +PyMeta3 = ">=0.5.1" + +[[package]] +name = "pycln" +version = "2.4.0" +description = "A formatter for finding and removing unused import statements." +optional = false +python-versions = ">=3.7.0,<4" +files = [ + {file = "pycln-2.4.0-py3-none-any.whl", hash = "sha256:d1bf648df17077306100815d255d45430035b36f66bac635df04a323c61ba126"}, + {file = "pycln-2.4.0.tar.gz", hash = "sha256:1f3eefb7be18a9ee06c3bdd0ba2e91218cd39317e20130325f107e96eb84b9f6"}, +] + +[package.dependencies] +libcst = ">=0.3.10" +pathspec = ">=0.9.0" +pyyaml = ">=5.3.1" +tomlkit = ">=0.11.1" +typer = ">=0.4.1" + [[package]] name = "pycryptodome" version = "3.20.0" @@ -1574,6 +2282,128 @@ files = [ {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, ] +[[package]] +name = "pydantic" +version = "2.7.0" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.0-py3-none-any.whl", hash = "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352"}, + {file = "pydantic-2.7.0.tar.gz", hash = "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} +pydantic-core = "2.18.1" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226"}, + {file = "pydantic_core-2.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a"}, + {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17"}, + {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7"}, + {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6"}, + {file = "pydantic_core-2.18.1-cp310-none-win32.whl", hash = "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649"}, + {file = "pydantic_core-2.18.1-cp310-none-win_amd64.whl", hash = "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0"}, + {file = "pydantic_core-2.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80"}, + {file = "pydantic_core-2.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc"}, + {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d"}, + {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519"}, + {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9"}, + {file = "pydantic_core-2.18.1-cp311-none-win32.whl", hash = "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb"}, + {file = "pydantic_core-2.18.1-cp311-none-win_amd64.whl", hash = "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9"}, + {file = "pydantic_core-2.18.1-cp311-none-win_arm64.whl", hash = "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0"}, + {file = "pydantic_core-2.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8"}, + {file = "pydantic_core-2.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06"}, + {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90"}, + {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a"}, + {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b"}, + {file = "pydantic_core-2.18.1-cp312-none-win32.whl", hash = "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411"}, + {file = "pydantic_core-2.18.1-cp312-none-win_amd64.whl", hash = "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6"}, + {file = "pydantic_core-2.18.1-cp312-none-win_arm64.whl", hash = "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048"}, + {file = "pydantic_core-2.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09"}, + {file = "pydantic_core-2.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e"}, + {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb"}, + {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9"}, + {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622"}, + {file = "pydantic_core-2.18.1-cp38-none-win32.whl", hash = "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad"}, + {file = "pydantic_core-2.18.1-cp38-none-win_amd64.whl", hash = "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278"}, + {file = "pydantic_core-2.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de"}, + {file = "pydantic_core-2.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570"}, + {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2"}, + {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db"}, + {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6"}, + {file = "pydantic_core-2.18.1-cp39-none-win32.whl", hash = "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b"}, + {file = "pydantic_core-2.18.1-cp39-none-win_amd64.whl", hash = "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539"}, + {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59"}, + {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6"}, + {file = "pydantic_core-2.18.1.tar.gz", hash = "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + [[package]] name = "pygments" version = "2.17.2" @@ -1589,15 +2419,25 @@ files = [ plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pymeta3" +version = "0.5.1" +description = "Pattern-matching language based on OMeta for Python 3 and 2" +optional = false +python-versions = "*" +files = [ + {file = "PyMeta3-0.5.1.tar.gz", hash = "sha256:18bda326d9a9bbf587bfc0ee0bc96864964d78b067288bcf55d4d98681d05bcb"}, +] + [[package]] name = "pyright" -version = "1.1.351" +version = "1.1.358" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.351-py3-none-any.whl", hash = "sha256:83b44b25396ae20661fc5f133c3fce30928ff1296d4f2e5ff0bca5fcf03eb89d"}, - {file = "pyright-1.1.351.tar.gz", hash = "sha256:01124099714eebd7f6525d8cbfa350626b56dfaf771cfcd55c03e69f0f1efbbd"}, + {file = "pyright-1.1.358-py3-none-any.whl", hash = "sha256:0995b6a95eb11bd26f093cd5dee3d5e7258441b1b94d4a171b5dc5b79a1d4f4e"}, + {file = "pyright-1.1.358.tar.gz", hash = "sha256:185524a8d52f6f14bbd3b290b92ad905f25b964dddc9e7148aad760bd35c9f60"}, ] [package.dependencies] @@ -1609,13 +2449,13 @@ dev = ["twine (>=3.4.1)"] [[package]] name = "pytest" -version = "7.4.4" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] @@ -1623,11 +2463,82 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.6" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-httpx" +version = "0.30.0" +description = "Send responses to httpx." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest-httpx-0.30.0.tar.gz", hash = "sha256:755b8edca87c974dd4f3605c374fda11db84631de3d163b99c0df5807023a19a"}, + {file = "pytest_httpx-0.30.0-py3-none-any.whl", hash = "sha256:6d47849691faf11d2532565d0c8e0e02b9f4ee730da31687feae315581d7520c"}, +] + +[package.dependencies] +httpx = "==0.27.*" +pytest = ">=7,<9" + +[package.extras] +testing = ["pytest-asyncio (==0.23.*)", "pytest-cov (==4.*)"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pyunormalize" @@ -1674,7 +2585,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1682,16 +2592,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1708,7 +2610,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1716,7 +2617,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1724,13 +2624,13 @@ files = [ [[package]] name = "referencing" -version = "0.33.0" +version = "0.34.0" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, - {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, + {file = "referencing-0.34.0-py3-none-any.whl", hash = "sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4"}, + {file = "referencing-0.34.0.tar.gz", hash = "sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844"}, ] [package.dependencies] @@ -1739,104 +2639,104 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2023.12.25" +version = "2024.4.16" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.7" files = [ - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, - {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, - {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, - {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, - {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, - {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, - {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, - {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, - {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, - {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, - {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, - {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, - {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, - {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, - {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, + {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08"}, + {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18"}, + {file = "regex-2024.4.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47"}, + {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af"}, + {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a"}, + {file = "regex-2024.4.16-cp310-cp310-win32.whl", hash = "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0"}, + {file = "regex-2024.4.16-cp310-cp310-win_amd64.whl", hash = "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2"}, + {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26"}, + {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd"}, + {file = "regex-2024.4.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89"}, + {file = "regex-2024.4.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d"}, + {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6"}, + {file = "regex-2024.4.16-cp311-cp311-win32.whl", hash = "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d"}, + {file = "regex-2024.4.16-cp311-cp311-win_amd64.whl", hash = "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9"}, + {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd"}, + {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec"}, + {file = "regex-2024.4.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70"}, + {file = "regex-2024.4.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81"}, + {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc"}, + {file = "regex-2024.4.16-cp312-cp312-win32.whl", hash = "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b"}, + {file = "regex-2024.4.16-cp312-cp312-win_amd64.whl", hash = "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9"}, + {file = "regex-2024.4.16-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682"}, + {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5"}, + {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a"}, + {file = "regex-2024.4.16-cp37-cp37m-win32.whl", hash = "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46"}, + {file = "regex-2024.4.16-cp37-cp37m-win_amd64.whl", hash = "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331"}, + {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48"}, + {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4"}, + {file = "regex-2024.4.16-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d"}, + {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76"}, + {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9"}, + {file = "regex-2024.4.16-cp38-cp38-win32.whl", hash = "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e"}, + {file = "regex-2024.4.16-cp38-cp38-win_amd64.whl", hash = "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834"}, + {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94"}, + {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455"}, + {file = "regex-2024.4.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317"}, + {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c"}, + {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50"}, + {file = "regex-2024.4.16-cp39-cp39-win32.whl", hash = "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335"}, + {file = "regex-2024.4.16-cp39-cp39-win_amd64.whl", hash = "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483"}, + {file = "regex-2024.4.16.tar.gz", hash = "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0"}, ] [[package]] @@ -1860,6 +2760,24 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rlp" version = "4.0.0" @@ -1990,45 +2908,81 @@ files = [ [[package]] name = "ruff" -version = "0.2.2" +version = "0.3.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6"}, - {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9"}, - {file = "ruff-0.2.2-py3-none-win32.whl", hash = "sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325"}, - {file = "ruff-0.2.2-py3-none-win_amd64.whl", hash = "sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d"}, - {file = "ruff-0.2.2-py3-none-win_arm64.whl", hash = "sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd"}, - {file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, + {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, + {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, + {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, + {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, +] + +[[package]] +name = "safe-pysha3" +version = "1.0.4" +description = "SHA-3 (Keccak) for Python 3.9 - 3.11" +optional = false +python-versions = "*" +files = [ + {file = "safe-pysha3-1.0.4.tar.gz", hash = "sha256:e429146b1edd198b2ca934a2046a65656c5d31b0ec894bbd6055127f4deaff17"}, ] +[[package]] +name = "semantic-version" +version = "2.10.0" +description = "A library implementing the 'SemVer' scheme." +optional = false +python-versions = ">=2.7" +files = [ + {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"}, + {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"}, +] + +[package.extras] +dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] +doc = ["Sphinx", "sphinx-rtd-theme"] + [[package]] name = "setuptools" -version = "69.1.0" +version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, - {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] [[package]] name = "six" @@ -2043,13 +2997,24 @@ files = [ [[package]] name = "sniffio" -version = "1.3.0" +version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] [[package]] @@ -2071,6 +3036,17 @@ pure-eval = "*" [package.extras] tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -2082,6 +3058,17 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + [[package]] name = "toolz" version = "0.12.1" @@ -2095,28 +3082,68 @@ files = [ [[package]] name = "traitlets" -version = "5.14.1" +version = "5.14.2" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, - {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, + {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"}, + {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "trie" +version = "3.0.0" +description = "Python implementation of the Ethereum Trie structure" +optional = false +python-versions = ">=3.8, <4" +files = [ + {file = "trie-3.0.0-py3-none-any.whl", hash = "sha256:a061507aa3a0076c61a65c0d70de528508a66d948f09054de51f6560fd59c454"}, + {file = "trie-3.0.0.tar.gz", hash = "sha256:180205883dc614cade519b38b3e4ffecb8184e53d594556b38f43446c5cda840"}, +] + +[package.dependencies] +eth-hash = ">=0.1.0" +eth-utils = ">=2.0.0" +hexbytes = ">=0.2.0,<0.4.0" +rlp = ">=3" +sortedcontainers = ">=2.1.0" + +[package.extras] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash (>=0.1.0,<1.0.0)", "hypothesis (>=6.56.4,<7)", "ipython", "pre-commit (>=3.4.0)", "pycryptodome", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["towncrier (>=21,<22)"] +test = ["hypothesis (>=6.56.4,<7)", "pycryptodome", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] + +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] [[package]] @@ -2138,13 +3165,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.25.0" +version = "20.25.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, - {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] [package.dependencies] @@ -2169,26 +3196,28 @@ files = [ [[package]] name = "web3" -version = "6.15.1" +version = "6.17.1" description = "web3.py" optional = false python-versions = ">=3.7.2" files = [ - {file = "web3-6.15.1-py3-none-any.whl", hash = "sha256:4e4a8313aa4556ecde61c852a62405b853b667498b07da6ff05c29fe8c79096b"}, - {file = "web3-6.15.1.tar.gz", hash = "sha256:f9e7eefc1b3c3d194868a4ef9583b625c18ea3f31a48ebe143183db74898f381"}, + {file = "web3-6.17.1-py3-none-any.whl", hash = "sha256:eabc6f0d92a59cc0968e3d98238e40df29c0bedac7760cbd08d9cb0a76741de3"}, + {file = "web3-6.17.1.tar.gz", hash = "sha256:38372fae8adfbf92407ead7396f27e233facb717e6837119a4133f045543d447"}, ] [package.dependencies] aiohttp = ">=3.7.4.post0" eth-abi = ">=4.0.0" -eth-account = ">=0.8.0" +eth-account = ">=0.8.0,<0.13" eth-hash = {version = ">=0.5.1", extras = ["pycryptodome"]} -eth-typing = ">=3.0.0" +eth-tester = {version = ">=0.11.0b1,<0.12.0b1", extras = ["py-evm"], optional = true, markers = "python_version > \"3.7\" and extra == \"tester\""} +eth-typing = ">=3.0.0,<4.2.0" eth-utils = ">=2.1.0" hexbytes = ">=0.1.0,<0.4.0" jsonschema = ">=4.0.0" lru-dict = ">=1.1.6,<1.3.0" protobuf = ">=4.21.6" +py-geth = {version = ">=3.14.0", optional = true, markers = "extra == \"tester\""} pyunormalize = ">=15.0.0" pywin32 = {version = ">=223", markers = "platform_system == \"Windows\""} requests = ">=2.16.0" @@ -2196,11 +3225,10 @@ typing-extensions = ">=4.0.1" websockets = ">=10.0.0" [package.extras] -dev = ["black (>=22.1.0)", "build (>=0.9.0)", "bumpversion", "eth-tester[py-evm] (==v0.9.1-b.2)", "flake8 (==3.8.3)", "flaky (>=3.7.0)", "hypothesis (>=3.31.2)", "importlib-metadata (<5.0)", "ipfshttpclient (==0.8.0a2)", "isort (>=5.11.0)", "mypy (==1.4.1)", "py-geth (>=3.14.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1,<0.23)", "pytest-mock (>=1.10)", "pytest-watch (>=4.2)", "pytest-xdist (>=1.29)", "setuptools (>=38.6.0)", "sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=3.18.0)", "tqdm (>4.32)", "twine (>=1.13)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)", "when-changed (>=0.3.0)"] +dev = ["build (>=0.9.0)", "bumpversion", "eth-tester[py-evm] (>=0.11.0b1,<0.12.0b1)", "eth-tester[py-evm] (>=0.9.0b1,<0.10.0b1)", "flaky (>=3.7.0)", "hypothesis (>=3.31.2)", "importlib-metadata (<5.0)", "ipfshttpclient (==0.8.0a2)", "pre-commit (>=2.21.0)", "py-geth (>=3.14.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1,<0.23)", "pytest-mock (>=1.10)", "pytest-watch (>=4.2)", "pytest-xdist (>=1.29)", "setuptools (>=38.6.0)", "sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=3.18.0)", "tqdm (>4.32)", "twine (>=1.13)", "when-changed (>=0.3.0)"] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] ipfs = ["ipfshttpclient (==0.8.0a2)"] -linter = ["black (>=22.1.0)", "flake8 (==3.8.3)", "isort (>=5.11.0)", "mypy (==1.4.1)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)"] -tester = ["eth-tester[py-evm] (==v0.9.1-b.2)", "py-geth (>=3.14.0)"] +tester = ["eth-tester[py-evm] (>=0.11.0b1,<0.12.0b1)", "eth-tester[py-evm] (>=0.9.0b1,<0.10.0b1)", "py-geth (>=3.14.0)"] [[package]] name = "websockets" @@ -2386,22 +3414,7 @@ files = [ idna = ">=2.0" multidict = ">=4.0" -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" -python-versions = ">=3.8,<4.0" -content-hash = "e6f4df2762a9aad0314bde289a75f06785edaa94fb87909ada4a1e699656f5af" +python-versions = ">=3.10,<4.0" +content-hash = "e02971366a8bd5c85b5e2c32578cd193560f4827d381c2e9341953d453120fad" diff --git a/pyproject.toml b/pyproject.toml index 8de4a48..012d3ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,18 +6,53 @@ authors = ["José Ribeiro "] readme = "README.md" [tool.poetry.dependencies] -python = ">=3.8,<4.0" -httpx = "^0.26.0" +python = ">=3.10,<4.0" +httpx = "^0.27.0" web3 = "^6.15.1" +pybars3 = "^0.9.7" +pydantic = "^2.7.0" +pytest-mock = "^3.14.0" +backoff = "^2.2.1" +aiolimiter = "^1.1.0" [tool.poetry.group.dev.dependencies] -ruff = "^0.2.2" -pyright ="^1.1.296" -pytest ="^7.2.1" -ipython = "*" -pre-commit = "3.*" +ruff = "^0.3.7" +pyright = "^1.1.358" +pytest = "^8.1.1" +ipython = "^8.23.0" +pre-commit = "^3.7.0" +datamodel-code-generator = "^0.25.5" +ariadne-codegen = "^0.13.0" +pytest-httpx = "^0.30.0" +pytest-asyncio = "^0.23.6" +web3 = { extras = ["tester"], version = "^6.17.0" } +pycln = "^2.4.0" +pytest-cov = "^5.0.0" + +[tool.poetry.scripts] [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.ariadne-codegen] +remote_schema_url = "https://api.thegraph.com/subgraphs/name/cowprotocol/cow" +queries_path = "cow_py/subgraph/queries" +target_package_name = "client" +target_package_path = "cow_py/subgraph" +client_name = "SubgraphClient" +client_file_name = "subgraph_client" + +[tool.pyright] +# NOTE: these paths are excluded but not IGNORED. They are still included in the analysis if they are referenced by source files that are not excluded. +exclude = [ + "**/node_modules", + "**/__pycache__", + "cow_py/subgraph/client/*.py", + "cow_py/order_book/generated/*.py", + ".venv/", + "**/__generated__" +] +reportIncompatibleVariableOverride = 'warning' +strictParameterNoneValue = false From f2324d7acf10e41ac017efe1f873bbfdb7e1e104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:28:39 -0300 Subject: [PATCH 02/30] add codegen module --- cow_py/codegen/__init__.py | 5 + cow_py/codegen/abi_handler.py | 265 ++++++++++++++++++ cow_py/codegen/components/__init__.py | 17 ++ cow_py/codegen/components/abi_loader.py | 18 ++ cow_py/codegen/components/base_contract.py | 109 +++++++ cow_py/codegen/components/base_mixin.py | 18 ++ cow_py/codegen/components/contract_factory.py | 44 +++ cow_py/codegen/components/contract_loader.py | 43 +++ cow_py/codegen/components/get_abi_file.py | 15 + .../codegen/components/templates/__init__.py | 0 .../templates/contract_template.hbs | 14 + .../components/templates/partials/__init__.py | 0 .../templates/partials/contract_class.hbs | 6 + .../templates/partials/contract_mixin.hbs | 6 + .../templates/partials/dataclasses.hbs | 8 + .../components/templates/partials/enums.hbs | 10 + cow_py/codegen/main.py | 37 +++ cow_py/codegen/solidity_converter.py | 99 +++++++ tests/codegen/__init__.py | 0 .../codegen/components/test_base_contract.py | 60 ++++ .../components/test_contract_factory.py | 42 +++ tests/codegen/test_abi_handler.py | 210 ++++++++++++++ tests/codegen/test_solidity_converter.py | 84 ++++++ 23 files changed, 1110 insertions(+) create mode 100644 cow_py/codegen/__init__.py create mode 100644 cow_py/codegen/abi_handler.py create mode 100644 cow_py/codegen/components/__init__.py create mode 100644 cow_py/codegen/components/abi_loader.py create mode 100644 cow_py/codegen/components/base_contract.py create mode 100644 cow_py/codegen/components/base_mixin.py create mode 100644 cow_py/codegen/components/contract_factory.py create mode 100644 cow_py/codegen/components/contract_loader.py create mode 100644 cow_py/codegen/components/get_abi_file.py create mode 100644 cow_py/codegen/components/templates/__init__.py create mode 100644 cow_py/codegen/components/templates/contract_template.hbs create mode 100644 cow_py/codegen/components/templates/partials/__init__.py create mode 100644 cow_py/codegen/components/templates/partials/contract_class.hbs create mode 100644 cow_py/codegen/components/templates/partials/contract_mixin.hbs create mode 100644 cow_py/codegen/components/templates/partials/dataclasses.hbs create mode 100644 cow_py/codegen/components/templates/partials/enums.hbs create mode 100644 cow_py/codegen/main.py create mode 100644 cow_py/codegen/solidity_converter.py create mode 100644 tests/codegen/__init__.py create mode 100644 tests/codegen/components/test_base_contract.py create mode 100644 tests/codegen/components/test_contract_factory.py create mode 100644 tests/codegen/test_abi_handler.py create mode 100644 tests/codegen/test_solidity_converter.py diff --git a/cow_py/codegen/__init__.py b/cow_py/codegen/__init__.py new file mode 100644 index 0000000..00b8a0b --- /dev/null +++ b/cow_py/codegen/__init__.py @@ -0,0 +1,5 @@ +from .abi_handler import ABIHandler + +__all__ = [ + "ABIHandler", +] diff --git a/cow_py/codegen/abi_handler.py b/cow_py/codegen/abi_handler.py new file mode 100644 index 0000000..0bcac52 --- /dev/null +++ b/cow_py/codegen/abi_handler.py @@ -0,0 +1,265 @@ +import importlib.resources +import re +from typing import Any, Dict, List + +from pybars import Compiler + +from cow_py.codegen.components import templates +from cow_py.codegen.components.abi_loader import FileAbiLoader +from cow_py.codegen.components.templates import partials +from cow_py.codegen.solidity_converter import SolidityConverter + +CAMEL_TO_SNAKE_REGEX = re.compile( + r"(?<=[a-z0-9])(?=[A-Z])|" # Lowercase or digit to uppercase + r"(?<=[A-Z])(?=[A-Z][a-z])|" # Uppercase to uppercase followed by lowercase + r"(?<=[A-Za-z])(?=[0-9])|" # Letter to digit + r"(?<=[0-9])(?=[A-Z])" # Digit to uppercase +) + + +def compile_partial(partial_path: str) -> str: + with open(partial_path, "r") as file: + partial = file.read() + compiler = Compiler() + return compiler.compile(partial) + + +def get_filename_without_extension(path: str): + """ + Returns the a filename from the path, without the extension. + """ + return path.split("/")[-1].split(".")[0] + + +def to_python_conventional_name(name: str) -> str: + """Converts a camelCase or PascalCase name to a snake_case name.""" + if name.isupper(): + return name.lower() + + return CAMEL_TO_SNAKE_REGEX.sub("_", name).lower() + + +def _get_template_file() -> str: + pkg_files = importlib.resources.files(templates) + return str(next(x for x in pkg_files.iterdir() if x.suffix == ".hbs")) # type: ignore + + +def _get_partials_files() -> str: + pkg_files = importlib.resources.files(partials) + return [str(x) for x in pkg_files.iterdir() if x.suffix == ".hbs"] # type: ignore + + +class ABIHandlerError(Exception): + """Raised when an error occurs in the ABI handler.""" + + pass + + +class ABIHandler: + """ + Handles the generation of Python classes and methods from Ethereum contract ABIs. + + This class reads the ABI of a contract, processes its contents, and generates Python code that mirrors + the contract's functions and data structures. + + Attributes: + contract_name (str): Name of the contract, used for generating class names. + abi_file_path (str): Path to the ABI JSON file of the contract. + + Methods: + generate: Main method to generate Python code from the ABI.. + """ + + def __init__(self, contract_name: str, abi_file_path: str): + self.contract_name = contract_name + self.abi_file_path = abi_file_path + + def generate(self) -> str: + """ + Generates Python code representing the contract's ABI. + + This method processes the ABI file, extracting information about functions, + input/output arguments, enums, and data structures. It then uses this information + to generate corresponding Python classes and methods. + + Returns: + str: The generated Python code as a string. + + Raises: + ABIHandlerError: If an error occurs during ABI processing or code generation. + """ + try: + template_data = self._prepare_template_data() + return self._render_template(template_data) + except Exception as e: + raise ABIHandlerError(f"Error generating code: {str(e)}") from e + + def _prepare_template_data(self) -> Dict[str, Any]: + """ + Prepares data for the template rendering based on the contract's ABI. + + This method processes the ABI to extract relevant information for generating + Python code, such as methods, data classes, and enums. + + Returns: + Dict[str, Any]: A dictionary containing the structured data for rendering. + + Raises: + ABIHandlerError: If an error occurs during ABI processing. + """ + try: + methods, data_classes, enums = [], [], [] + generated_structs, generated_enums = set(), set() + + abi = FileAbiLoader(self.abi_file_path).load_abi() + + for item in abi: + if item["type"] == "function": + methods.append(self._process_function(item)) + for param in item["inputs"] + item.get("outputs", []): + self._process_parameters( + param, + data_classes, + enums, + generated_structs, + generated_enums, + ) + elif item["type"] == "event": + for param in item["inputs"]: + self._process_parameters( + param, + data_classes, + enums, + generated_structs, + generated_enums, + ) + + return { + "abiPath": self.abi_file_path, + "contractName": self.contract_name, + "methods": methods, + "dataClasses": data_classes, + "enums": enums, + } + except Exception as e: + raise ABIHandlerError(f"Error preparing template data: {str(e)}") from e + + def _process_parameters( + self, param, data_classes, enums, generated_structs, generated_enums + ): + if param["type"] == "tuple" and param["internalType"] not in generated_structs: + struct_name = SolidityConverter._get_struct_name(param["internalType"]) + properties = [ + { + "name": comp["name"], + "type": SolidityConverter.convert_type( + comp["type"], comp.get("internalType") + ), + } + for comp in param["components"] + ] + data_classes.append({"name": struct_name, "properties": properties}) + generated_structs.add(param["internalType"]) + elif ( + "enum " in param["internalType"] + and param["internalType"] not in generated_enums + ): + enum_name = SolidityConverter._get_struct_name(param["internalType"]) + enum_values = [ + {"name": item["name"], "value": item["value"]} + for item in param["components"] + ] + enums.append({"name": enum_name, "values": enum_values}) + generated_enums.add(param["internalType"]) + + def _process_function(self, function_item: Dict[str, Any]) -> Dict[str, Any]: + original_name = function_item["name"] + method_name = to_python_conventional_name(original_name) + + input_types = self._generate_function_input_args_with_types(function_item) + output_types = [ + SolidityConverter.convert_type(o["type"], o.get("internalType")) + for o in function_item.get("outputs", []) + ] + output_str = ( + "None" + if not output_types + else output_types[0] + if len(output_types) == 1 + else f'Tuple[{", ".join(output_types)}]' + ) + + return { + "name": method_name, + "inputs": input_types, + "outputType": output_str, + "originalName": original_name, + } + + def _generate_function_input_args_with_types( + self, function_item: Dict[str, Any] + ) -> List[Dict[str, Any]]: + input_args = [] + unnamed_arg_counters = {} # Track unnamed arguments of each type + + for input_item in function_item.get("inputs", []): + input_type = SolidityConverter.convert_type( + input_item["type"], input_item.get("internalType") + ) + + # Regex to transform type names like 'list[int]' into 'int_list' + base_name = re.sub(r"list\[(\w+)\]", r"\1_list", input_type.lower()) + + input_name = input_item.get("name") + if not input_name: + # If the argument is unnamed, use the base_name with a counter to create a unique name + unnamed_arg_counters[base_name] = ( + unnamed_arg_counters.get(base_name, -1) + 1 + ) + input_name = f"{base_name}_arg{unnamed_arg_counters[base_name]}" + + python_input_name = to_python_conventional_name(input_name) + + if input_item["type"] == "tuple": + struct_name = SolidityConverter._get_struct_name( + input_item["internalType"] + ) + properties = [ + { + "name": component["name"], + "type": SolidityConverter.convert_type( + component["type"], component.get("internalType") + ), + } + for component in input_item["components"] + ] + destructured_args = ", ".join( + [f"{python_input_name}.{prop['name']}" for prop in properties] + ) + input_args.append( + { + "name": python_input_name, + "type": struct_name, + "isTuple": True, + "destructuredArgs": f"({destructured_args})", + } + ) + else: + input_args.append( + {"name": python_input_name, "type": input_type, "isTuple": False} + ) + + return input_args + + def _render_template(self, data: Dict[str, Any]) -> str: + partials = { + get_filename_without_extension(partial_path): compile_partial(partial_path) + for partial_path in _get_partials_files() + } + + with open(_get_template_file(), "r") as file: + template = file.read() + + compiler = Compiler() + template = compiler.compile(template) + return template(data, partials=partials) diff --git a/cow_py/codegen/components/__init__.py b/cow_py/codegen/components/__init__.py new file mode 100644 index 0000000..df6ddb9 --- /dev/null +++ b/cow_py/codegen/components/__init__.py @@ -0,0 +1,17 @@ +from cow_py.codegen.components.abi_loader import FileAbiLoader +from cow_py.codegen.components.base_contract import BaseContract +from cow_py.codegen.components.base_mixin import BaseMixin +from cow_py.codegen.components.contract_factory import ContractFactory +from cow_py.codegen.components.contract_loader import ContractLoader +from cow_py.codegen.components.get_abi_file import get_abi_file +from cow_py.codegen.components.templates import partials + +__all__ = [ + "BaseContract", + "ContractFactory", + "FileAbiLoader", + "ContractLoader", + "BaseMixin", + "get_abi_file", + "partials", +] diff --git a/cow_py/codegen/components/abi_loader.py b/cow_py/codegen/components/abi_loader.py new file mode 100644 index 0000000..4cfa24c --- /dev/null +++ b/cow_py/codegen/components/abi_loader.py @@ -0,0 +1,18 @@ +import json +from abc import ABC, abstractmethod +from typing import Any, List + + +class AbiLoader(ABC): + @abstractmethod + def load_abi(self) -> List[Any]: + return [] + + +class FileAbiLoader(AbiLoader): + def __init__(self, file_name: str): + self.file_name = file_name + + def load_abi(self) -> List[Any]: + with open(self.file_name) as f: + return json.load(f) diff --git a/cow_py/codegen/components/base_contract.py b/cow_py/codegen/components/base_contract.py new file mode 100644 index 0000000..38bd724 --- /dev/null +++ b/cow_py/codegen/components/base_contract.py @@ -0,0 +1,109 @@ +from typing import Any, Dict, List, Optional, Tuple, Type + +from cow_py.codegen.components.contract_loader import ContractLoader +from cow_py.common.chains import Chain + + +class BaseContractError(Exception): + """Raised when an error occurs in the BaseContract class.""" + + pass + + +class BaseContract: + """ + A base class for contracts that implements common functionality. + + This class uses a singleton pattern to ensure that there's only one instance + of the contract for each contract address and chain combination. + + :ivar _instances: A dictionary to store instances of the BaseContract class. + """ + + ABI: Optional[List[Any]] = None + _instances: Dict[Tuple[Type, str, Chain], "BaseContract"] = {} + + def __new__(cls, address, chain, *args, **kwargs): + key = (cls, address, chain) + if key not in cls._instances: + cls._instances[key] = super(BaseContract, cls).__new__(cls) + return cls._instances[key] + + def __init__(self, address: str, chain: Chain, abi: List[Any] = None): + """ + Initializes the BaseContract with a contract address, chain, and optionally an ABI. + + :param address: The address of the contract on the specified chain + :param chain: The chain the contract is deployed on + :param abi: The ABI of the contract, optional + """ + if not hasattr(self, "_initialized"): # Avoid re-initialization + # Initialize the instance (only the first time) + self.contract_loader = ContractLoader(chain) + self.web3_contract = self.contract_loader.get_web3_contract( + address, abi or self.ABI or [] + ) + self._initialized = True + + @property + def address(self) -> str: + return self.web3_contract.address + + def _function_exists_in_abi(self, function_name): + """ + Checks if a function exists in the ABI of the contract. + + :param function_name: The name of the function to check for + :return: True if the function exists, False otherwise + """ + return any( + item.get("type") == "function" and item.get("name") == function_name + for item in self.web3_contract.abi + ) + + def _event_exists_in_abi(self, event_name): + """ + Checks if an event exists in the ABI of the contract. + + :param event_name: The name of the event to check for + :return: True if the event exists, False otherwise + """ + return any( + item.get("type") == "event" and item.get("name") == event_name + for item in self.web3_contract.abi + ) + + def __getattr__(self, name): + """ + Makes contract functions directly accessible as attributes of the BaseContract. + + :param name: The name of the attribute being accessed + :return: The wrapped contract function if it exists, raises AttributeError otherwise + + Raises: + BaseContractError: If an error occurs while accessing the contract function. + """ + if name == "_initialized": + # This is needed to avoid infinite recursion + raise AttributeError(name) + + try: + if hasattr(self.web3_contract, name): + return getattr(self.web3_contract, name) + + if self._event_exists_in_abi(name): + return getattr(self.web3_contract.events, name) + + if self._function_exists_in_abi(name): + function = getattr(self.web3_contract.functions, name) + + def wrapped_call(*args, **kwargs): + return function(*args, **kwargs).call() + + return wrapped_call + except Exception as e: + raise BaseContractError( + f"Error accessing attribute {name}: {str(e)}" + ) from e + + raise AttributeError(f"{self.__class__.__name__} has no attribute {name}") diff --git a/cow_py/codegen/components/base_mixin.py b/cow_py/codegen/components/base_mixin.py new file mode 100644 index 0000000..02510f0 --- /dev/null +++ b/cow_py/codegen/components/base_mixin.py @@ -0,0 +1,18 @@ +from abc import ABC + +from web3.contract.async_contract import AsyncContract + + +class BaseMixin(ABC): + web3_contract: AsyncContract + + def call_contract_method(self, method_name, *args): + """ + Generic method to call a contract function. + + :param method_name: The name of the contract method to call. + :param args: Arguments to pass to the contract method. + :return: The result of the contract method call. + """ + method = getattr(self.web3_contract.functions, method_name) + return method(*args).call() diff --git a/cow_py/codegen/components/contract_factory.py b/cow_py/codegen/components/contract_factory.py new file mode 100644 index 0000000..ccfe8e7 --- /dev/null +++ b/cow_py/codegen/components/contract_factory.py @@ -0,0 +1,44 @@ +from typing import Dict, Tuple, Type + +from cow_py.codegen.components.abi_loader import AbiLoader +from cow_py.codegen.components.base_contract import BaseContract +from cow_py.common.chains import Chain + + +class ContractFactory: + _contract_classes: Dict[Tuple[str, Chain], Type[BaseContract]] = {} + + @classmethod + def get_contract_class( + cls, contract_name: str, chain: Chain, abi_loader: AbiLoader + ) -> Type[BaseContract]: + """ + Retrieves the contract class for a given contract name and chain, creating it if it doesn't exist. + + :param contract_name: The name of the contract + :param chain: The chain the contract is deployed on + :return: The contract class for the given contract name and chain + """ + key = (contract_name, chain) + if key not in cls._contract_classes: + abi = abi_loader.load_abi() + cls._contract_classes[key] = type( + f"{contract_name}", (BaseContract,), {"ABI": abi} + ) + return cls._contract_classes[key] + + @classmethod + def create( + cls, contract_name: str, chain: Chain, address: str, abi_loader: AbiLoader + ) -> BaseContract: + """ + Creates an instance of the contract class for a given contract identifier (name or address) and chain. + + :param chain: The chain the contract is deployed on + :param contract_identifier: The name or address of the contract on the specified chain, optional + :param address_override: address with which to instantiate the contract, optional. We do this because some + pool contracts only have a MockPool contract whose ABI we'd like to use + :return: An instance of the contract class for the given contract identifier and chain + """ + contract_class = cls.get_contract_class(contract_name, chain, abi_loader) + return contract_class(address, chain) diff --git a/cow_py/codegen/components/contract_loader.py b/cow_py/codegen/components/contract_loader.py new file mode 100644 index 0000000..aa68d52 --- /dev/null +++ b/cow_py/codegen/components/contract_loader.py @@ -0,0 +1,43 @@ +from cow_py.web3.provider import Web3Provider + + +class ContractLoaderError(Exception): + """Raised when an error occurs in the ContractLoader class.""" + + pass + + +class ContractLoader: + """ + A utility class to load contract ABIs and create web3 contract instances. + """ + + def __init__(self, network): + """ + Initializes a ContractLoader instance for a specified network. + + :param network: The network the contract loader is associated with. + """ + self.network = network + self._abis = {} + + def get_web3_contract(self, contract_address, abi=None): + """ + Creates a web3 contract instance for the specified contract address and ABI. + + :param contract_address: The address of the contract. + :param abi_file_name: The file name of the ABI, optional. + :return: A web3 contract instance. + + Raises: + ContractLoaderError: If an error occurs while creating the web3 contract instance. + """ + try: + w3 = Web3Provider.get_instance(self.network) + + return w3.eth.contract( + address=w3.to_checksum_address(contract_address), + abi=abi, + ) + except Exception as e: + raise ContractLoaderError(f"Error loading contract: {str(e)}") from e diff --git a/cow_py/codegen/components/get_abi_file.py b/cow_py/codegen/components/get_abi_file.py new file mode 100644 index 0000000..721fe6d --- /dev/null +++ b/cow_py/codegen/components/get_abi_file.py @@ -0,0 +1,15 @@ +import importlib.resources + +from cow_py.contracts import abi + + +def get_abi_file(contract_name: str) -> str: + pkg_files = importlib.resources.files(abi) + return str( + next( + x + for x in pkg_files.iterdir() + if x.suffix == ".json" # type: ignore + and x.name.split(".json")[0] == contract_name + ) + ) diff --git a/cow_py/codegen/components/templates/__init__.py b/cow_py/codegen/components/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cow_py/codegen/components/templates/contract_template.hbs b/cow_py/codegen/components/templates/contract_template.hbs new file mode 100644 index 0000000..a9db95d --- /dev/null +++ b/cow_py/codegen/components/templates/contract_template.hbs @@ -0,0 +1,14 @@ +{{! Import statements }} +from typing import List, Tuple, Any +from hexbytes import HexBytes +from cow_py.common.chains import Chain +from dataclasses import dataclass +from enum import Enum +from cow_py.codegen.components import BaseMixin, BaseContract, FileAbiLoader, ContractFactory, get_abi_file + +{{>enums}} +{{>dataclasses}} + +{{>contract_mixin}} + +{{>contract_class}} diff --git a/cow_py/codegen/components/templates/partials/__init__.py b/cow_py/codegen/components/templates/partials/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cow_py/codegen/components/templates/partials/contract_class.hbs b/cow_py/codegen/components/templates/partials/contract_class.hbs new file mode 100644 index 0000000..ce8a4f4 --- /dev/null +++ b/cow_py/codegen/components/templates/partials/contract_class.hbs @@ -0,0 +1,6 @@ +class {{contractName}}(BaseContract, {{contractName}}Mixin): + def __init__(self, chain: Chain = Chain.MAINNET, address: str = ""): + {{!-- TEMPORARY -- abiPath should be a resolved ABI given we're already generating this --}} + abi_loader = FileAbiLoader(get_abi_file("{{contractName}}")) + contract = ContractFactory.create('{{contractName}}', chain, address, abi_loader) + super({{contractName}}, self).__init__(address, chain, abi=contract.ABI) diff --git a/cow_py/codegen/components/templates/partials/contract_mixin.hbs b/cow_py/codegen/components/templates/partials/contract_mixin.hbs new file mode 100644 index 0000000..4a3d24b --- /dev/null +++ b/cow_py/codegen/components/templates/partials/contract_mixin.hbs @@ -0,0 +1,6 @@ +class {{contractName}}Mixin(BaseMixin): +{{#each methods}} + def {{this.name}}(self{{#each this.inputs}}{{#if @first}}, {{/if}}{{this.name}}: {{this.type}}{{#unless @last}}, {{/unless}}{{/each}}) -> {{this.outputType}}: + return self.call_contract_method('{{this.originalName}}'{{#each this.inputs}}{{#if @first}}, {{/if}}{{#if this.isTuple}}{{this.destructuredArgs}}{{else}}{{this.name}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}) + +{{/each}} diff --git a/cow_py/codegen/components/templates/partials/dataclasses.hbs b/cow_py/codegen/components/templates/partials/dataclasses.hbs new file mode 100644 index 0000000..b7e2b9d --- /dev/null +++ b/cow_py/codegen/components/templates/partials/dataclasses.hbs @@ -0,0 +1,8 @@ +{{#each dataClasses}} +@dataclass +class {{this.name}}: +{{#each this.properties}} + {{this.name}}: {{this.type}} +{{/each}} + +{{/each}} \ No newline at end of file diff --git a/cow_py/codegen/components/templates/partials/enums.hbs b/cow_py/codegen/components/templates/partials/enums.hbs new file mode 100644 index 0000000..787d2de --- /dev/null +++ b/cow_py/codegen/components/templates/partials/enums.hbs @@ -0,0 +1,10 @@ +{{#each enums}} +{{#if @first}} +# TODO: Enums must be fixed before using them. They currently only use placeholder values. +{{/if}} +class {{this.name}}(Enum): +{{#each this.values}} + {{this.name}} = {{this.value}} +{{/each}} + +{{/each}} \ No newline at end of file diff --git a/cow_py/codegen/main.py b/cow_py/codegen/main.py new file mode 100644 index 0000000..15604e1 --- /dev/null +++ b/cow_py/codegen/main.py @@ -0,0 +1,37 @@ +import importlib.resources +import os + +from cow_py.codegen.abi_handler import ABIHandler +from cow_py.contracts import abi + + +def get_all_abis(): + pkg_files = importlib.resources.files(abi) + return [ + posix_path + for posix_path in pkg_files.iterdir() + if posix_path.suffix == ".json" # type: ignore + ] + + +def main(): + contracts_abis = get_all_abis() + for abi_file_path in contracts_abis: + contract_name = str(abi_file_path).split("/")[-1].split(".json")[0] + handler = ABIHandler(contract_name, str(abi_file_path)) + + content = handler.generate() + + base_path = os.path.dirname(os.path.abspath(__file__)) + + os.makedirs(f"{base_path}/__generated__", exist_ok=True) + generated = f"{base_path}/__generated__/{contract_name}.py" + + with open(generated, "w") as f: + f.write(content) + + print("Done") + + +if __name__ == "__main__": + main() diff --git a/cow_py/codegen/solidity_converter.py b/cow_py/codegen/solidity_converter.py new file mode 100644 index 0000000..90766f0 --- /dev/null +++ b/cow_py/codegen/solidity_converter.py @@ -0,0 +1,99 @@ +import re +from typing import Optional + +SOLIDITY_TO_PYTHON_TYPES = { + "address": "str", + "bool": "bool", + "string": "str", + "bytes": "HexBytes", + "uint": "int", + "int": "int", +} +DYNAMIC_SOLIDITY_TYPES = { + f"{prefix}{i*8 if prefix != 'bytes' else i}": ( + "int" if prefix != "bytes" else "HexBytes" + ) + for prefix in ["uint", "int", "bytes"] + for i in range(1, 33) +} +SOLIDITY_TO_PYTHON_TYPES.update(DYNAMIC_SOLIDITY_TYPES) + + +class SolidityConverterError(Exception): + """Raised when an error occurs in the SolidityConverter.""" + + pass + + +class SolidityConverter: + """ + Converts Solidity data types to equivalent Python data types. + + This class provides methods to map Solidity types as found in Ethereum smart contracts' ABIs + to Python types, facilitating the generation of Python classes and methods to interact with these contracts. + + Methods: + convert_type: Converts a Solidity data type to its Python equivalent. + """ + + @staticmethod + def _get_struct_name(internal_type: str) -> str: + """ + Extracts the struct name from a given internal type. + + Args: + internal_type (str): The internal type string from an ABI, often representing a struct. + + Returns: + str: The extracted name of the struct. + + Raises: + SolidityConverterError: If the internal type is not in the expected format. + """ + if not internal_type or "struct " not in internal_type: + raise SolidityConverterError( + f"Invalid internal type for struct: {internal_type}" + ) + return internal_type.replace("struct ", "").replace(".", "_").replace("[]", "") + + @classmethod + def convert_type(cls, solidity_type: str, internal_type: str) -> str: + """ + Converts a Solidity type to the corresponding Python type. + + Args: + solidity_type (str): The Solidity type as specified in the contract's ABI. + internal_type (str): The internal type representation, used for more complex data structures. + + Returns: + str: The Python type equivalent to the given Solidity type. + """ + if re.search(r"enum", internal_type) or (re.search(r"enum", solidity_type)): + return cls._extract_enum_name(internal_type, solidity_type) + elif solidity_type == "tuple": + return cls._get_struct_name(internal_type) + else: + return cls._convert_array_or_basic_type(solidity_type) + + @staticmethod + def _extract_enum_name( + internal_type: Optional[str], solidity_type: Optional[str] = None + ) -> str: + if internal_type and re.search(r"enum", internal_type): + return internal_type.replace("enum ", "").replace(".", "_") + elif solidity_type and re.search(r"enum", solidity_type): + return solidity_type.replace("enum ", "").replace(".", "_") + raise SolidityConverterError(f"Invalid internal type for enum: {internal_type}") + + @staticmethod + def _convert_array_or_basic_type(solidity_type: str) -> str: + array_match = re.match(r"(.+?)(\[\d*\])", solidity_type) + if array_match: + base_type, array_size = array_match.groups() + if array_size == "[]": + return f'List[{SOLIDITY_TO_PYTHON_TYPES.get(base_type, "Any")}]' + else: + size = int(array_size[1:-1]) + return f'Tuple[{", ".join([SOLIDITY_TO_PYTHON_TYPES.get(base_type, "Any")] * size)}]' + else: + return SOLIDITY_TO_PYTHON_TYPES.get(solidity_type, "Any") diff --git a/tests/codegen/__init__.py b/tests/codegen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/codegen/components/test_base_contract.py b/tests/codegen/components/test_base_contract.py new file mode 100644 index 0000000..8294dcb --- /dev/null +++ b/tests/codegen/components/test_base_contract.py @@ -0,0 +1,60 @@ +import pytest +from unittest.mock import patch, Mock +from cow_py.codegen.components.base_contract import ( + BaseContract, + Chain, +) + + +@patch("cow_py.codegen.components.base_contract.ContractLoader") +def test_base_contract_singleton(mock_loader): + address = "0x123" + chain = Chain.MAINNET + contract1 = BaseContract(address, chain) + contract2 = BaseContract(address, chain) + assert ( + contract1 is contract2 + ), "BaseContract should return the same instance for the same address and chain" + + +class MockWithoutAttributes(Mock): + # By default Mock objects allow access to any attribute, even if it doesn't exist. + # on the Base Contract class, we want to raise an AttributeError if the attribute doesn't exist. + def __getattr__(self, name: str): + if name == "balanceOf" or name == "nonExistentMethod": + raise AttributeError() + return super().__getattr__(name) + + +@pytest.fixture +def contract_with_abi(): + abi = [ + {"type": "function", "name": "balanceOf"}, + {"type": "event", "name": "Transfer"}, + ] + with patch("cow_py.codegen.components.base_contract.ContractLoader") as mock_loader: + mock_contract = MockWithoutAttributes() + mock_contract.abi = abi + mock_contract.functions = Mock( + balanceOf=Mock(return_value=Mock(call=Mock(return_value="1000"))), + ) + + mock_loader.return_value.get_web3_contract.return_value = mock_contract + contract = BaseContract("0x456", Chain.MAINNET, abi) + return contract + + +def test_base_contract_function_exists_in_abi(contract_with_abi): + assert contract_with_abi._function_exists_in_abi("balanceOf") + assert not contract_with_abi._function_exists_in_abi("transfer") + + +def test_base_contract_event_exists_in_abi(contract_with_abi): + assert contract_with_abi._event_exists_in_abi("Transfer") + assert not contract_with_abi._event_exists_in_abi("Approval") + + +def test_base_contract_getattr(contract_with_abi): + assert contract_with_abi.balanceOf() == "1000" + with pytest.raises(AttributeError): + _ = contract_with_abi.nonExistentMethod diff --git a/tests/codegen/components/test_contract_factory.py b/tests/codegen/components/test_contract_factory.py new file mode 100644 index 0000000..137c6eb --- /dev/null +++ b/tests/codegen/components/test_contract_factory.py @@ -0,0 +1,42 @@ +import unittest +from unittest.mock import MagicMock, patch +from cow_py.codegen.components.contract_factory import ContractFactory, BaseContract +from cow_py.common.chains import Chain + + +class TestContractFactory(unittest.TestCase): + def setUp(self): + self.contract_name = "MockContract" + self.chain = Chain.MAINNET + self.abi_loader = MagicMock() + self.abi_loader.load_abi = MagicMock( + return_value=[{"type": "function", "name": "mockFunction"}] + ) + self.address = "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" + + def test_contract_factory_get_contract_class(self): + with patch.dict( + "cow_py.codegen.components.contract_factory.ContractFactory._contract_classes", + clear=True, + ): + first_class = ContractFactory.get_contract_class( + self.contract_name, self.chain, self.abi_loader + ) + second_class = ContractFactory.get_contract_class( + self.contract_name, self.chain, self.abi_loader + ) + + self.abi_loader.load_abi.assert_called_once() + self.assertEqual(first_class, second_class) + + def test_contract_factory_create(self): + with patch.dict( + "cow_py.codegen.components.contract_factory.ContractFactory._contract_classes", + clear=True, + ): + contract_instance = ContractFactory.create( + self.contract_name, self.chain, self.address, self.abi_loader + ) + + self.assertIsInstance(contract_instance, BaseContract) + self.assertEqual(contract_instance.address, self.address) diff --git a/tests/codegen/test_abi_handler.py b/tests/codegen/test_abi_handler.py new file mode 100644 index 0000000..6110ecb --- /dev/null +++ b/tests/codegen/test_abi_handler.py @@ -0,0 +1,210 @@ +import pytest + +from cow_py.codegen.abi_handler import ( + to_python_conventional_name, + get_filename_without_extension, + _get_template_file, + _get_partials_files, + compile_partial, + ABIHandler, + FileAbiLoader, +) + +from unittest.mock import mock_open + + +@pytest.mark.parametrize( + "input_name, expected_output", + [ + # ("GPv2Order_Data", "gp_v2_order_data"), + ("simpleTest", "simple_test"), + ("ThisIsATest", "this_is_a_test"), + ("number2IsHere", "number_2_is_here"), + ("AnotherTest123", "another_test_123"), + ("JSONData", "json_data"), + # ("GPv2Order_Data_arg_1", "gp_v2_order_data_arg_1"), + ], +) +def test_to_python_conventional_name(input_name, expected_output): + assert to_python_conventional_name(input_name) == expected_output + + +def test_compile_partial(mocker): + # Test that compile_partial correctly compiles a partial template + mocked_file_content = "test content" + mocked_compiled_content = "compiled content" + + mocker.patch("builtins.open", mock_open(read_data=mocked_file_content)) + mocker.patch("pybars.Compiler.compile", return_value=mocked_compiled_content) + result = compile_partial("fake_path") + assert result == mocked_compiled_content + + +def test_get_filename_without_extension(): + # Test that get_filename_without_extension correctly removes the extension + assert get_filename_without_extension("folder/test.py") == "test" + + +def test_get_template_file(): + # Test that _get_template_file returns the correct template file path + assert _get_template_file().endswith("contract_template.hbs") + + +def test_get_partials_files(): + # Test that _get_partials_files returns the correct list of partial files + assert all([f.endswith(".hbs") for f in _get_partials_files()]) + + +@pytest.fixture +def abi_handler(): + return ABIHandler("TestContract", "/fake/path/to/abi.json") + + +def test_abi_handler_generate(mocker, abi_handler): + # Test that ABIHandler.generate correctly generates Python code from an ABI + mocked_abi_data = [ + {"type": "function", "name": "doSomething", "inputs": [], "outputs": []} + ] + mocker.patch( + "cow_py.codegen.abi_handler.FileAbiLoader.load_abi", + return_value=mocked_abi_data, + ) + mocker.patch( + "cow_py.codegen.abi_handler.ABIHandler._prepare_template_data", + return_value={"methods": []}, + ) + mocker.patch( + "cow_py.codegen.abi_handler.ABIHandler._render_template", + return_value="class MyContract:\n pass", + ) + + # Run the method + result = abi_handler.generate() + + # Verify the output + assert ( + result == "class MyContract:\n pass" + ), "Generated Python code does not match expected output." + + +def test_abi_handler_prepare_template_data(mocker, abi_handler): + # Test that ABIHandler._prepare_template_data correctly processes the ABI + sample_abi = [ + { + "type": "function", + "name": "setValue", + "inputs": [{"name": "value", "type": "uint256"}], + "outputs": [], + }, + { + "type": "event", + "name": "ValueChanged", + "inputs": [{"name": "value", "type": "uint256"}], + }, + ] + + mocker.patch.object(FileAbiLoader, "load_abi", return_value=sample_abi) + + mocker.patch.object( + abi_handler, + "_process_function", + return_value={ + "name": "set_value", + "inputs": ["uint256"], + "outputType": "None", + "originalName": "setValue", + }, + ) + mocker.patch.object(abi_handler, "_process_parameters", autospec=True) + + result = abi_handler._prepare_template_data() + + assert result["abiPath"] == "/fake/path/to/abi.json" + assert result["contractName"] == "TestContract" + assert len(result["methods"]) == 1 + assert result["methods"][0]["name"] == "set_value" + assert "dataClasses" in result + assert "enums" in result + + +def test_abi_handler_process_parameters(abi_handler): + # Test that ABIHandler._process_parameters correctly processes function parameters + param = { + "type": "tuple", + "internalType": "struct Value", + "components": [ + {"name": "x", "type": "uint256", "internalType": "uint256"}, + {"name": "y", "type": "uint256", "internalType": "uint256"}, + ], + } + data_classes = [] + enums = [] + generated_structs = set() + generated_enums = set() + + expected_data_class = { + "name": "Value", + "properties": [ + {"name": "x", "type": "int"}, + {"name": "y", "type": "int"}, + ], + } + + abi_handler._process_parameters( + param, data_classes, enums, generated_structs, generated_enums + ) + + assert "struct Value" in generated_structs + assert data_classes[0] == expected_data_class + + +def test_abi_handler_process_function(abi_handler, mocker): + # Test that ABIHandler._process_function correctly processes a function item + function_item = { + "type": "function", + "name": "getValue", + "inputs": [{"name": "key", "type": "uint256", "internalType": "uint256"}], + "outputs": [{"name": "result", "type": "uint256", "internalType": "uint256"}], + } + + result = abi_handler._process_function(function_item) + + expected_result = { + "name": "get_value", + "inputs": [{"name": "key", "type": "int", "isTuple": False}], + "outputType": "int", + "originalName": "getValue", + } + + assert result == expected_result + + +def test_abi_handler_render_template(abi_handler, mocker): + # Test that ABIHandler._render_template correctly renders the template with data + template_data = { + "abiPath": "/fake/path/to/abi.json", + "contractName": "TestContract", + "methods": [ + { + "name": "set_value", + "inputs": ["uint256"], + "outputType": "uint256", + "originalName": "setValue", + } + ], + "dataClasses": [], + "enums": [], + } + template_string = "class {{ contractName }}:\n pass" + expected_rendered_output = "class TestContract:\n pass" + + mocker.patch("builtins.open", mocker.mock_open(read_data=template_string)) + + mocker.patch( + "pybars.Compiler.compile", + return_value=lambda x, **kwargs: expected_rendered_output, + ) + + result = abi_handler._render_template(template_data) + + assert result == expected_rendered_output diff --git a/tests/codegen/test_solidity_converter.py b/tests/codegen/test_solidity_converter.py new file mode 100644 index 0000000..8324b2d --- /dev/null +++ b/tests/codegen/test_solidity_converter.py @@ -0,0 +1,84 @@ +import pytest + +from cow_py.codegen.solidity_converter import SolidityConverter, SolidityConverterError + + +def test_solidity_converter_get_struct_name(): + internal_type = "struct MyStruct" + expected_result = "MyStruct" + result = SolidityConverter._get_struct_name(internal_type) + assert result == expected_result + + +def test_solidity_converter_get_struct_name_invalid_internal_type(): + internal_type = "uint256" + with pytest.raises(SolidityConverterError): + SolidityConverter._get_struct_name(internal_type) + + +def test_solidity_converter_convert_type_enum(): + solidity_type = "enum MyEnum" + internal_type = "" + expected_result = "MyEnum" + result = SolidityConverter.convert_type(solidity_type, internal_type) + assert result == expected_result + + +def test_solidity_converter_convert_type_array(): + solidity_type = "uint256[]" + internal_type = "" + expected_result = "List[int]" + result = SolidityConverter.convert_type(solidity_type, internal_type) + assert result == expected_result + + +def test_solidity_converter_convert_type_tuple(): + solidity_type = "tuple" + internal_type = "struct MyStruct" + expected_result = "MyStruct" + result = SolidityConverter.convert_type(solidity_type, internal_type) + assert result == expected_result + + +def test_solidity_converter_convert_type_fixed_size_array(): + solidity_type = "uint256[3]" + internal_type = "" + expected_result = "Tuple[int, int, int]" + result = SolidityConverter.convert_type(solidity_type, internal_type) + assert result == expected_result + + +def test_solidity_converter_convert_type_unknown_type(): + solidity_type = "unknown_type" + internal_type = "" + expected_result = "Any" + result = SolidityConverter.convert_type(solidity_type, internal_type) + assert result == expected_result + + +def test_solidity_converter_extract_enum_name(): + internal_type = "enum MyEnum.Option" + expected_result = "MyEnum_Option" + result = SolidityConverter._extract_enum_name(internal_type) + assert result == expected_result + + +def test_solidity_converter_convert_array_or_basic_type_dynamic_array(): + solidity_type = "address[]" + expected_result = "List[str]" + result = SolidityConverter._convert_array_or_basic_type(solidity_type) + assert result == expected_result + + +def test_solidity_converter_convert_array_or_basic_type_fixed_size_array(): + solidity_type = "bool[5]" + expected_result = "Tuple[bool, bool, bool, bool, bool]" + result = SolidityConverter._convert_array_or_basic_type(solidity_type) + assert result == expected_result + + +def test_solidity_converter_convert_array_or_basic_type_basic_type(): + solidity_type = "bytes32" + expected_result = "HexBytes" + result = SolidityConverter._convert_array_or_basic_type(solidity_type) + assert result == expected_result From cd653d990410610887330d0d944ebc19c45f7ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:31:55 -0300 Subject: [PATCH 03/30] add common module --- cow_py/common/api/api_base.py | 85 +++++++++++++++++ cow_py/common/api/decorators.py | 58 ++++++++++++ cow_py/common/chains/__init__.py | 32 ++++--- cow_py/common/chains/utils.py | 4 +- cow_py/common/config.py | 31 +++++++ cow_py/common/constants.py | 5 +- tests/common/__init__.py | 0 tests/common/api/test_api_base.py | 129 ++++++++++++++++++++++++++ tests/common/api/test_rate_limiter.py | 28 ++++++ tests/common/test_core_api.py | 8 ++ tests/common/test_cow_error.py | 22 +++++ 11 files changed, 383 insertions(+), 19 deletions(-) create mode 100644 cow_py/common/api/api_base.py create mode 100644 cow_py/common/api/decorators.py create mode 100644 tests/common/__init__.py create mode 100644 tests/common/api/test_api_base.py create mode 100644 tests/common/api/test_rate_limiter.py create mode 100644 tests/common/test_core_api.py create mode 100644 tests/common/test_cow_error.py diff --git a/cow_py/common/api/api_base.py b/cow_py/common/api/api_base.py new file mode 100644 index 0000000..545150e --- /dev/null +++ b/cow_py/common/api/api_base.py @@ -0,0 +1,85 @@ +from abc import ABC +from typing import Any, Optional + +import httpx + +from cow_py.common.api.decorators import rate_limitted, with_backoff +from cow_py.common.config import SupportedChainId + +Context = dict[str, Any] + + +class APIConfig(ABC): + """Base class for API configuration with common functionality.""" + + config_map = {} + + def __init__( + self, chain_id: SupportedChainId, base_context: Optional[Context] = None + ): + self.chain_id = chain_id + self.context = base_context or {} + + def get_base_url(self) -> str: + return self.config_map.get( + self.chain_id, "default URL if chain_id is not found" + ) + + def get_context(self) -> Context: + return {"base_url": self.get_base_url(), **self.context} + + +class RequestStrategy: + async def make_request(self, client, url, method, **request_kwargs): + headers = { + "accept": "application/json", + "content-type": "application/json", + } + + return await client.request( + url=url, headers=headers, method=method, **request_kwargs + ) + + +class ResponseAdapter: + async def adapt_response(self, _response): + raise NotImplementedError() + + +class RequestBuilder: + def __init__(self, strategy, response_adapter): + self.strategy = strategy + self.response_adapter = response_adapter + + async def execute(self, client, url, method, **kwargs): + response = await self.strategy.make_request(client, url, method, **kwargs) + return self.response_adapter.adapt_response(response) + + +class JsonResponseAdapter(ResponseAdapter): + def adapt_response(self, response): + if response.headers.get("content-type") == "application/json": + return response.json() + else: + return response.text + + +class ApiBase: + """Base class for APIs utilizing configuration and request execution.""" + + def __init__(self, config: APIConfig): + self.config = config + + @with_backoff() + @rate_limitted() + async def _fetch(self, path, method="GET", **kwargs): + url = self.config.get_base_url() + path + + del kwargs["context_override"] + + async with httpx.AsyncClient() as client: + builder = RequestBuilder( + RequestStrategy(), + JsonResponseAdapter(), + ) + return await builder.execute(client, url, method, **kwargs) diff --git a/cow_py/common/api/decorators.py b/cow_py/common/api/decorators.py new file mode 100644 index 0000000..305121a --- /dev/null +++ b/cow_py/common/api/decorators.py @@ -0,0 +1,58 @@ +import backoff +import httpx +from aiolimiter import AsyncLimiter + +DEFAULT_LIMITER_OPTIONS = {"rate": 5, "per": 1.0} + +DEFAULT_BACKOFF_OPTIONS = { + "max_tries": 10, + "max_time": None, + "jitter": None, +} + + +def dig(self, *keys): + try: + for key in keys: + self = self[key] + return self + except KeyError: + return None + + +def with_backoff(): + def decorator(func): + async def wrapper(*args, **kwargs): + backoff_opts = dig(kwargs, "context_override", "backoff_opts") + + if backoff_opts is None: + internal_backoff_opts = DEFAULT_BACKOFF_OPTIONS + else: + internal_backoff_opts = backoff_opts + + @backoff.on_exception( + backoff.expo, httpx.HTTPStatusError, **internal_backoff_opts + ) + async def closure(): + return await func(*args, **kwargs) + + return await closure() + + return wrapper + + return decorator + + +def rate_limitted( + rate=DEFAULT_LIMITER_OPTIONS["rate"], per=DEFAULT_LIMITER_OPTIONS["per"] +): + limiter = AsyncLimiter(rate, per) + + def decorator(func): + async def wrapper(*args, **kwargs): + async with limiter: + return await func(*args, **kwargs) + + return wrapper + + return decorator diff --git a/cow_py/common/chains/__init__.py b/cow_py/common/chains/__init__.py index 083df3e..bf21c1d 100644 --- a/cow_py/common/chains/__init__.py +++ b/cow_py/common/chains/__init__.py @@ -6,24 +6,26 @@ class Chain(Enum): Supported chains and their respective `chainId` for the SDK. """ - MAINNET = 1 - GNOSIS = 100 - SEPOLIA = 11155111 + MAINNET = (1, "ethereum", "https://etherscan.io") + GNOSIS = (100, "gnosis", "https://gnosisscan.io") + SEPOLIA = (11155111, "sepolia", "https://sepolia.etherscan.io/") - def __init__(self, id) -> None: + def __init__(self, id: int, network_name: str, explorer_url: str) -> None: self.id = id + self.network_name = network_name + self.explorer_url = explorer_url + @property + def name(self) -> str: + return self.network_name -SUPPORTED_CHAINS = {Chain.MAINNET, Chain.GNOSIS, Chain.SEPOLIA} + @property + def explorer(self) -> str: + return self.explorer_url -CHAIN_NAMES = { - Chain.MAINNET: "ethereum", - Chain.GNOSIS: "gnosis", - Chain.SEPOLIA: "sepolia", -} + @property + def chain_id(self) -> int: + return self.id -CHAIN_SCANNER_MAP = { - Chain.MAINNET: "https://etherscan.io", - Chain.GNOSIS: "https://gnosisscan.io", - Chain.SEPOLIA: "https://sepolia.etherscan.io/", -} + +SUPPORTED_CHAINS = {chain for chain in Chain} diff --git a/cow_py/common/chains/utils.py b/cow_py/common/chains/utils.py index b428e63..7fdf2dc 100644 --- a/cow_py/common/chains/utils.py +++ b/cow_py/common/chains/utils.py @@ -1,6 +1,6 @@ -from cow_py.common.chains import CHAIN_SCANNER_MAP, Chain +from cow_py.common.chains import Chain def get_explorer_link(chain: Chain, tx_hash: str) -> str: """Return the scan link for the provided transaction hash.""" - return f"{CHAIN_SCANNER_MAP[chain]}/tx/{tx_hash}" + return f"{chain.explorer_url}/tx/{tx_hash}" diff --git a/cow_py/common/config.py b/cow_py/common/config.py index c82a166..ae4316b 100644 --- a/cow_py/common/config.py +++ b/cow_py/common/config.py @@ -1,4 +1,35 @@ +from dataclasses import dataclass from enum import Enum +from typing import Dict, Optional + + +class SupportedChainId(Enum): + MAINNET = 1 + GNOSIS_CHAIN = 100 + SEPOLIA = 11155111 + + +class CowEnv(Enum): + PROD = "prod" + STAGING = "staging" + + +ApiBaseUrls = Dict[SupportedChainId, str] + + +@dataclass +class ApiContext: + chain_id: SupportedChainId + env: CowEnv + base_urls: Optional[ApiBaseUrls] = None + max_tries: Optional[int] = 5 + + +# Define the list of available environments. +ENVS_LIST = [CowEnv.PROD, CowEnv.STAGING] + +# Define the default CoW Protocol API context. +DEFAULT_COW_API_CONTEXT = ApiContext(env=CowEnv.PROD, chain_id=SupportedChainId.MAINNET) class IPFSConfig(Enum): diff --git a/cow_py/common/constants.py b/cow_py/common/constants.py index 29ca7c3..2978a26 100644 --- a/cow_py/common/constants.py +++ b/cow_py/common/constants.py @@ -1,5 +1,6 @@ from enum import Enum from typing import Dict + from .chains import Chain """ @@ -17,14 +18,14 @@ class CowContractAddress(Enum): EXTENSIBLE_FALLBACK_HANDLER = "0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5" -def map_address_to_supported_networks(address) -> Dict[Chain, str]: +def map_address_to_supported_networks(address) -> Dict[int, str]: """ Maps a given address to all supported networks. :param address: The address to be mapped. :return: A dictionary mapping the address to each supported chain. """ - return {chain_id: address for chain_id in Chain} + return {chain.chain_id: address for chain in Chain} COW_PROTOCOL_SETTLEMENT_CONTRACT_CHAIN_ADDRESS_MAP = map_address_to_supported_networks( diff --git a/tests/common/__init__.py b/tests/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/common/api/test_api_base.py b/tests/common/api/test_api_base.py new file mode 100644 index 0000000..1c3dd9f --- /dev/null +++ b/tests/common/api/test_api_base.py @@ -0,0 +1,129 @@ +from unittest.mock import AsyncMock, Mock, patch + +import httpx +import pytest + +from cow_py.common.api.api_base import ApiBase, APIConfig +from cow_py.common.api.decorators import DEFAULT_BACKOFF_OPTIONS +from cow_py.common.config import SupportedChainId +from httpx import Request + +ERROR_MESSAGE = "💣💥 Booom!" +OK_RESPONSE = {"status": 200, "ok": True, "content": {"some": "data"}} + + +@pytest.fixture +def sut(): + class MyConfig(APIConfig): + def __init__(self): + super().__init__(SupportedChainId.SEPOLIA, None) + + def get_base_url(self): + return "http://localhost" + + class MyAPI(ApiBase): + @staticmethod + def get_config(context): + return Mock( + chain_id="mainnet", get_base_url=Mock(return_value="http://localhost") + ) + + async def get_version(self, context_override={}): + return await self._fetch( + path="/api/v1/version", context_override=context_override + ) + + return MyAPI(config=MyConfig()) + + +@pytest.fixture +def mock_success_response(): + return AsyncMock( + status_code=200, + headers={"content-type": "application/json"}, + json=Mock(return_value=OK_RESPONSE), + ) + + +@pytest.fixture +def mock_http_status_error(): + return httpx.HTTPStatusError( + message=ERROR_MESSAGE, + request=Request("GET", "http://example.com"), + response=httpx.Response(500), + ) + + +@pytest.mark.asyncio +async def test_no_re_attempt_if_success(sut, mock_success_response): + with patch( + "httpx.AsyncClient.send", side_effect=[mock_success_response] + ) as mock_request: + response = await sut.get_version() + assert mock_request.awaited_once() + assert response["content"]["some"] == "data" + + +@pytest.mark.asyncio +async def test_re_attempts_if_fails_then_succeeds( + sut, mock_success_response, mock_http_status_error +): + with patch( + "httpx.AsyncClient.send", + side_effect=[ + *([mock_http_status_error] * 3), + mock_success_response, + ], + ) as mock_request: + response = await sut.get_version() + + assert response["ok"] is True + assert mock_request.call_count == 4 + + +@pytest.mark.asyncio +async def test_succeeds_last_attempt( + sut, mock_success_response, mock_http_status_error +): + with patch( + "httpx.AsyncClient.send", + side_effect=[ + mock_http_status_error, + mock_http_status_error, + mock_success_response, + ], + ) as mock_send: + response = await sut.get_version() + assert response["ok"] is True + assert mock_send.call_count == 3 + + +@pytest.mark.asyncio +async def test_does_not_reattempt_after_max_failures(sut, mock_http_status_error): + with patch( + "httpx.AsyncClient.request", side_effect=[mock_http_status_error] * 3 + ) as mock_call: + with pytest.raises(httpx.HTTPStatusError): + await sut.get_version(context_override={"backoff_opts": {"max_tries": 3}}) + + assert mock_call.call_count == 3 + + +@pytest.mark.asyncio +async def test_backoff_uses_function_options_instead_of_default( + sut, mock_http_status_error +): + max_tries = 1 + + assert max_tries != DEFAULT_BACKOFF_OPTIONS["max_tries"] + + with patch( + "httpx.AsyncClient.request", + side_effect=[mock_http_status_error] * max_tries, + ) as mock_call: + with pytest.raises(httpx.HTTPStatusError): + await sut.get_version( + context_override={"backoff_opts": {"max_tries": max_tries}} + ) + + assert mock_call.call_count == max_tries diff --git a/tests/common/api/test_rate_limiter.py b/tests/common/api/test_rate_limiter.py new file mode 100644 index 0000000..1e6ca12 --- /dev/null +++ b/tests/common/api/test_rate_limiter.py @@ -0,0 +1,28 @@ +import asyncio + +import pytest + +from cow_py.common.api.decorators import rate_limitted + + +@pytest.mark.asyncio +async def test_call_intervals(): + async def test_function(): + return "called" + + # Set the rate limit for easy calculation (e.g., 2 calls per second) + decorated_function = rate_limitted(2, 1)(test_function) + + call_times = [] + + # Perform a number of calls and record the time each was completed + for _ in range(6): + await decorated_function() + call_times.append(asyncio.get_event_loop().time()) + + # Verify intervals between calls are as expected (at least 0.5 seconds apart after the first batch of 2) + intervals = [call_times[i] - call_times[i - 1] for i in range(1, len(call_times))] + for interval in intervals[2:]: # Ignore the first two immediate calls + assert ( + interval >= 0.5 + ), f"Interval of {interval} too short, should be at least 0.5" diff --git a/tests/common/test_core_api.py b/tests/common/test_core_api.py new file mode 100644 index 0000000..992d22d --- /dev/null +++ b/tests/common/test_core_api.py @@ -0,0 +1,8 @@ +import pytest + +from cow_py.common import chains, config, constants, cow_error + + +@pytest.mark.parametrize("module", [constants, cow_error, chains, config]) +def test_module_existence(module): + assert module is not None diff --git a/tests/common/test_cow_error.py b/tests/common/test_cow_error.py new file mode 100644 index 0000000..31c3aaa --- /dev/null +++ b/tests/common/test_cow_error.py @@ -0,0 +1,22 @@ +from cow_py.common.cow_error import CowError + + +def test_cow_error_inheritance(): + assert issubclass(CowError, Exception) + + +def test_cow_error_initialization(): + message = "An error occurred" + error_code = 1001 + error = CowError(message, error_code) + + assert str(error) == message + assert error.error_code == error_code + + +def test_cow_error_initialization_without_error_code(): + message = "An error occurred" + error = CowError(message) + + assert str(error) == message + assert error.error_code is None From 15df625c197c566b3b7d2fcd7a00f22df226cb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:32:14 -0300 Subject: [PATCH 04/30] remove stale subgraphs/core modules --- cow_py/order_signing/__init__.py | 0 cow_py/subgraphs/__init__.py | 0 cow_py/subgraphs/base/client.py | 52 --------------------------- cow_py/subgraphs/base/query.py | 23 ------------ cow_py/subgraphs/client.py | 14 -------- cow_py/subgraphs/deployments.py | 36 ------------------- cow_py/subgraphs/queries.py | 60 -------------------------------- tests/core/__init__.py | 0 tests/core/test_core_api.py | 7 ---- tests/core/test_cow_error.py | 27 -------------- tests/subgraphs/__init__.py | 0 tests/subgraphs/deployments.py | 43 ----------------------- 12 files changed, 262 deletions(-) delete mode 100644 cow_py/order_signing/__init__.py delete mode 100644 cow_py/subgraphs/__init__.py delete mode 100644 cow_py/subgraphs/base/client.py delete mode 100644 cow_py/subgraphs/base/query.py delete mode 100644 cow_py/subgraphs/client.py delete mode 100644 cow_py/subgraphs/deployments.py delete mode 100644 cow_py/subgraphs/queries.py delete mode 100644 tests/core/__init__.py delete mode 100644 tests/core/test_core_api.py delete mode 100644 tests/core/test_cow_error.py delete mode 100644 tests/subgraphs/__init__.py delete mode 100644 tests/subgraphs/deployments.py diff --git a/cow_py/order_signing/__init__.py b/cow_py/order_signing/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cow_py/subgraphs/__init__.py b/cow_py/subgraphs/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cow_py/subgraphs/base/client.py b/cow_py/subgraphs/base/client.py deleted file mode 100644 index 4dd6d77..0000000 --- a/cow_py/subgraphs/base/client.py +++ /dev/null @@ -1,52 +0,0 @@ -from abc import ABC, abstractmethod - -from cow_py.common.chains import Chain - -import json -import logging - -import httpx - - -class GraphQLError(Exception): - pass - - -async def gql(url, query, variables={}): - logging.debug(f"Executing query: {query[:15]}") - logging.debug(f"URL: {url}") - logging.debug(f"Variables: {variables}") - async with httpx.AsyncClient() as client: - r = await client.post( - url, - json=dict(query=query, variables=variables), - ) - logging.debug(f"Response status: {r.status_code}") - logging.debug(f"Response body: {r.text}") - r.raise_for_status() - - try: - return r.json().get("data", r.json()) - except KeyError: - print(json.dumps(r.json(), indent=2)) - raise GraphQLError - - -class GraphQLClient(ABC): - def __init__(self, chain) -> None: - self.url = self.get_url(chain) - - async def instance_query(self, query, variables=dict()): - return await gql(self.url, query, variables=variables) - - @abstractmethod - def get_url(self, chain) -> str: - pass - - @classmethod - async def query(cls, chain=Chain.MAINNET, query=None, variables=dict()): - if not query: - raise ValueError("query must be provided") - - client = cls(chain) - return await client.instance_query(query, variables) diff --git a/cow_py/subgraphs/base/query.py b/cow_py/subgraphs/base/query.py deleted file mode 100644 index 2d94085..0000000 --- a/cow_py/subgraphs/base/query.py +++ /dev/null @@ -1,23 +0,0 @@ -from abc import ABC, abstractmethod - -from cow_py.common.chains import Chain -from cow_py.subgraphs.base.client import GraphQLClient - - -class GraphQLQuery(ABC): - def __init__(self, chain=Chain.MAINNET, variables=dict()) -> None: - self.chain = chain - self.variables = variables - - @abstractmethod - def get_query(self) -> str: - pass - - @abstractmethod - def get_client(self) -> GraphQLClient: - pass - - async def execute(self): - query = self.get_query() - client = self.get_client() - return await client.__class__.query(self.chain, query, self.variables) diff --git a/cow_py/subgraphs/client.py b/cow_py/subgraphs/client.py deleted file mode 100644 index 035bbb5..0000000 --- a/cow_py/subgraphs/client.py +++ /dev/null @@ -1,14 +0,0 @@ -from cow_py.subgraphs.base.query import GraphQLQuery -from cow_py.subgraphs.base.client import GraphQLClient -from cow_py.subgraphs.deployments import build_subgraph_url, SubgraphEnvironment - - -class CoWSubgraph(GraphQLClient): - def get_url(self, chain): - # TODO: add a nice way to change the environment - return build_subgraph_url(chain, SubgraphEnvironment.PRODUCTION) - - -class CoWSubgraphQuery(GraphQLQuery): - def get_client(self): - return CoWSubgraph(self.chain) diff --git a/cow_py/subgraphs/deployments.py b/cow_py/subgraphs/deployments.py deleted file mode 100644 index ab4ed00..0000000 --- a/cow_py/subgraphs/deployments.py +++ /dev/null @@ -1,36 +0,0 @@ -from cow_py.common.chains import Chain -from dataclasses import dataclass -from enum import Enum - - -class SubgraphEnvironment(Enum): - PRODUCTION = "production" - STAGING = "staging" - - -SUBGRAPH_BASE_URL = "https://api.thegraph.com/subgraphs/name/cowprotocol" - - -def build_subgraph_url(chain: Chain, env: SubgraphEnvironment) -> str: - base_url = SUBGRAPH_BASE_URL - - network_suffix = "" if chain == Chain.MAINNET else "-gc" - env_suffix = "-" + env.value if env == SubgraphEnvironment.STAGING else "" - - if chain == Chain.SEPOLIA: - raise ValueError(f"Unsupported chain: {chain}") - - return f"{base_url}/cow{network_suffix}{env_suffix}" - - -@dataclass -class SubgraphConfig: - chain: Chain - - @property - def production(self) -> str: - return build_subgraph_url(self.chain, SubgraphEnvironment.PRODUCTION) - - @property - def staging(self) -> str: - return build_subgraph_url(self.chain, SubgraphEnvironment.STAGING) diff --git a/cow_py/subgraphs/queries.py b/cow_py/subgraphs/queries.py deleted file mode 100644 index b25ba9a..0000000 --- a/cow_py/subgraphs/queries.py +++ /dev/null @@ -1,60 +0,0 @@ -from cow_py.subgraphs.client import CoWSubgraphQuery - -# /** -# * GraphQL query for the total number of tokens, orders, traders, settlements, volume, and fees. -# */ -TOTALS_QUERY = """ - query Totals { - totals { - tokens - orders - traders - settlements - volumeUsd - volumeEth - feesUsd - feesEth - } - } -""" - -# /** -# * GraphQL query for the total volume over the last N days. -# * @param days The number of days to query. -# */ -LAST_DAYS_VOLUME_QUERY = """ - query LastDaysVolume($days: Int!) { - dailyTotals(orderBy: timestamp, orderDirection: desc, first: $days) { - timestamp - volumeUsd - } - } -""" - -# /** -# * GraphQL query for the total volume over the last N hours. -# * @param hours The number of hours to query. -# */ -LAST_HOURS_VOLUME_QUERY = """ - query LastHoursVolume($hours: Int!) { - hourlyTotals(orderBy: timestamp, orderDirection: desc, first: $hours) { - timestamp - volumeUsd - } - } -""" - - -class TotalsQuery(CoWSubgraphQuery): - def get_query(self): - return TOTALS_QUERY - - -class LastDaysVolumeQuery(CoWSubgraphQuery): - def get_query(self): - return LAST_DAYS_VOLUME_QUERY - - -class LastHoursVolumeQuery(CoWSubgraphQuery): - def get_query(self): - return LAST_HOURS_VOLUME_QUERY diff --git a/tests/core/__init__.py b/tests/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/core/test_core_api.py b/tests/core/test_core_api.py deleted file mode 100644 index f03dd13..0000000 --- a/tests/core/test_core_api.py +++ /dev/null @@ -1,7 +0,0 @@ -import pytest -from cow_py.common import constants, cow_error, chains, config - - -@pytest.mark.parametrize("module", [constants, cow_error, chains, config]) -def test_module_existence(module): - assert module is not None diff --git a/tests/core/test_cow_error.py b/tests/core/test_cow_error.py deleted file mode 100644 index aada517..0000000 --- a/tests/core/test_cow_error.py +++ /dev/null @@ -1,27 +0,0 @@ -from cow_py.common.cow_error import ( - CowError, -) # Adjust the import path according to your project structure - - -def test_cow_error_inheritance(): - # Test that CowError is a subclass of Exception - assert issubclass(CowError, Exception) - - -def test_cow_error_initialization(): - # Test CowError initialization with a message and error_code - message = "An error occurred" - error_code = 1001 - error = CowError(message, error_code) - - assert str(error) == message - assert error.error_code == error_code - - -def test_cow_error_initialization_without_error_code(): - # Test CowError initialization with only a message - message = "An error occurred" - error = CowError(message) - - assert str(error) == message - assert error.error_code is None diff --git a/tests/subgraphs/__init__.py b/tests/subgraphs/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/subgraphs/deployments.py b/tests/subgraphs/deployments.py deleted file mode 100644 index a7c8580..0000000 --- a/tests/subgraphs/deployments.py +++ /dev/null @@ -1,43 +0,0 @@ -import pytest -from cow_py.common.chains import Chain -from cow_py.subgraphs.deployments import ( - build_subgraph_url, - SubgraphConfig, - SubgraphEnvironment, - SUBGRAPH_BASE_URL, -) - - -def test_build_subgraph_url(): - assert ( - build_subgraph_url(Chain.MAINNET, SubgraphEnvironment.PRODUCTION) - == f"{SUBGRAPH_BASE_URL}/cow" - ) - assert ( - build_subgraph_url(Chain.MAINNET, SubgraphEnvironment.STAGING) - == f"{SUBGRAPH_BASE_URL}/cow-staging" - ) - assert ( - build_subgraph_url(Chain.GNOSIS, SubgraphEnvironment.PRODUCTION) - == f"{SUBGRAPH_BASE_URL}/cow-gc" - ) - assert ( - build_subgraph_url(Chain.GNOSIS, SubgraphEnvironment.STAGING) - == f"{SUBGRAPH_BASE_URL}/cow-gc-staging" - ) - - with pytest.raises(ValueError): - build_subgraph_url(Chain.SEPOLIA, SubgraphEnvironment.PRODUCTION) - - -def test_subgraph_config(): - mainnet_config = SubgraphConfig(Chain.MAINNET) - assert mainnet_config.production == f"{SUBGRAPH_BASE_URL}/cow" - assert mainnet_config.staging == f"{SUBGRAPH_BASE_URL}/cow-staging" - - gnosis_chain_config = SubgraphConfig(Chain.GNOSIS) - assert gnosis_chain_config.production == f"{SUBGRAPH_BASE_URL}/cow-gc" - assert gnosis_chain_config.staging == f"{SUBGRAPH_BASE_URL}/cow-gc-staging" - - with pytest.raises(ValueError): - SubgraphConfig(Chain.SEPOLIA).production From 4b923e4275aa888e770c8dedd34f8f464e8a1d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:35:51 -0300 Subject: [PATCH 05/30] add contracts module --- cow_py/contracts/domain.py | 29 ++++ cow_py/contracts/order.py | 243 +++++++++++++++++++++++++++++++++ cow_py/contracts/sign.py | 104 ++++++++++++++ cow_py/web3/provider.py | 1 + tests/contracts/conftest.py | 39 ++++++ tests/contracts/test_orders.py | 0 tests/contracts/test_sign.py | 76 +++++++++++ 7 files changed, 492 insertions(+) create mode 100644 cow_py/contracts/domain.py create mode 100644 cow_py/contracts/order.py create mode 100644 cow_py/contracts/sign.py create mode 100644 tests/contracts/conftest.py create mode 100644 tests/contracts/test_orders.py create mode 100644 tests/contracts/test_sign.py diff --git a/cow_py/contracts/domain.py b/cow_py/contracts/domain.py new file mode 100644 index 0000000..51872df --- /dev/null +++ b/cow_py/contracts/domain.py @@ -0,0 +1,29 @@ +from dataclasses import dataclass +from typing import Optional + +from cow_py.common.chains import Chain + + +@dataclass +class TypedDataDomain: + name: str + version: str + chainId: int + verifyingContract: str + salt: Optional[str] = None + + +def domain(chain: Chain, verifying_contract: str) -> TypedDataDomain: + """ + Return the Gnosis Protocol v2 domain used for signing. + + :param chain: The EIP-155 chain ID. + :param verifying_contract: The address of the contract that will verify the signature. + :return: An EIP-712 compatible typed domain data. + """ + return TypedDataDomain( + name="Gnosis Protocol", + version="v2", + chainId=chain.chain_id, + verifyingContract=verifying_contract, + ) diff --git a/cow_py/contracts/order.py b/cow_py/contracts/order.py new file mode 100644 index 0000000..4702f5a --- /dev/null +++ b/cow_py/contracts/order.py @@ -0,0 +1,243 @@ +from dataclasses import dataclass +from enum import Enum +from typing import Literal, Optional, Union + +from eth_account.messages import _hash_eip191_message, encode_typed_data +from eth_typing import Hash32, HexStr +from eth_utils.conversions import to_bytes, to_hex + + +@dataclass +class Order: + # Sell token address. + sellToken: str + # Buy token address. + buyToken: str + # An optional address to receive the proceeds of the trade instead of the + # owner (i.e. the order signer). + receiver: str + # The order sell amount. + # + # For fill or kill sell orders, this amount represents the exact sell amount + # that will be executed in the trade. For fill or kill buy orders, this + # amount represents the maximum sell amount that can be executed. For partial + # fill orders, this represents a component of the limit price fraction. + # + sellAmount: int + # The order buy amount. + # + # For fill or kill sell orders, this amount represents the minimum buy amount + # that can be executed in the trade. For fill or kill buy orders, this amount + # represents the exact buy amount that will be executed. For partial fill + # orders, this represents a component of the limit price fraction. + # + buyAmount: int + # The timestamp this order is valid until + validTo: int + # Arbitrary application specific data that can be added to an order. This can + # also be used to ensure uniqueness between two orders with otherwise the + # exact same parameters. + appData: str + # Fee to give to the protocol. + feeAmount: int + # The order kind. + kind: str + # Specifies whether or not the order is partially fillable. + partiallyFillable: bool = False + # Specifies how the sell token balance will be withdrawn. It can either be + # taken using ERC20 token allowances made directly to the Vault relayer + # (default) or using Balancer Vault internal or external balances. + sellTokenBalance: Optional[str] = None + # Specifies how the buy token balance will be paid. It can either be paid + # directly in ERC20 tokens (default) in Balancer Vault internal balances. + buyTokenBalance: Optional[str] = None + + +# Gnosis Protocol v2 order cancellation data. +@dataclass +class OrderCancellations: + orderUids: bytearray + + +# Marker address to indicate that an order is buying Ether. +# +# Note that this address is only has special meaning in the `buyToken` and will +# be treated as a ERC20 token address in the `sellToken` position, causing the +# settlement to revert. +BUY_ETH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + +BytesLike = Union[str, bytes, HexStr] + +HashLike = Union[BytesLike, int] + + +class OrderKind(Enum): + SELL = "sell" + BUY = "buy" + + +class OrderBalance(Enum): + # Use ERC20 token balances. + ERC20 = "erc20" + # Use Balancer Vault external balances. + + # This can only be specified specified for the sell balance and allows orders + # to re-use Vault ERC20 allowances. When specified for the buy balance, it + # will be treated as {@link OrderBalance.ERC20}. + EXTERNAL = "external" + # Use Balancer Vault internal balances. + INTERNAL = "internal" + + +# /** +# * The EIP-712 type fields definition for a Gnosis Protocol v2 order. +# */ +ORDER_TYPE_FIELDS = [ + dict(name="sellToken", type="address"), + dict(name="buyToken", type="address"), + dict(name="receiver", type="address"), + dict(name="sellAmount", type="uint256"), + dict(name="buyAmount", type="uint256"), + dict(name="validTo", type="uint32"), + dict(name="appData", type="bytes32"), + dict(name="feeAmount", type="uint256"), + dict(name="kind", type="string"), + dict(name="partiallyFillable", type="bool"), + dict(name="sellTokenBalance", type="string"), + dict(name="buyTokenBalance", type="string"), +] +# The EIP-712 type fields definition for a Gnosis Protocol v2 order. +CANCELLATIONS_TYPE_FIELDS = [ + dict(name="orderUids", type="bytes[]"), +] + + +def hashify(h: Union[int, str, bytes]) -> str: + """ + Normalizes an app data value to a 32-byte hash. + :param h: A hash-like value to normalize. Can be an integer, hexadecimal string, or bytes. + :return: A 32-byte hash encoded as a hex-string. + """ + if isinstance(h, int): + # Convert the integer to a hexadecimal string and pad it to 64 characters + return f"0x{h:064x}" + elif isinstance(h, str): + # Convert string to bytes, then pad it to 32 bytes (64 hex characters) + return to_hex(to_bytes(hexstr=h).rjust(32, b"\0")) + elif isinstance(h, bytes): + # Pad the bytes to 32 bytes (64 hex characters) + return to_hex(h.rjust(32, b"\0")) + else: + raise ValueError("Input must be an integer, a hexadecimal string, or bytes.") + + +def normalize_buy_token_balance( + balance: Optional[str], +) -> Literal["erc20", "internal"]: + """ + Normalizes the balance configuration for a buy token. + + :param balance: The balance configuration. + :return: The normalized balance configuration. + """ + if balance in [None, OrderBalance.ERC20.value, OrderBalance.EXTERNAL.value]: + return OrderBalance.ERC20.value + elif balance == OrderBalance.INTERNAL: + return OrderBalance.INTERNAL.value + else: + raise ValueError(f"Invalid order balance {balance}") + + +ZERO_ADDRESS = "0x" + "00" * 20 + + +def normalize_order(order: Order): + if order.receiver == ZERO_ADDRESS: + raise ValueError("receiver cannot be address(0)") + + return { + "sellToken": order.sellToken, + "buyToken": order.buyToken, + "receiver": order.receiver if order.receiver else ZERO_ADDRESS, + "sellAmount": order.sellAmount, + "buyAmount": order.buyAmount, + "validTo": order.validTo, + "appData": hashify(order.appData), + "feeAmount": order.feeAmount, + "kind": order.kind, + "partiallyFillable": order.partiallyFillable, + "sellTokenBalance": ( + order.sellTokenBalance + if order.sellTokenBalance + else OrderBalance.ERC20.value + ), + "buyTokenBalance": normalize_buy_token_balance(order.buyTokenBalance), + } + + +def hash_typed_data(domain, types, data) -> Hash32: + """ + Compute the 32-byte signing hash for the specified order. + + :param domain: The EIP-712 domain separator to compute the hash for. + :param types: The typed data types. + :param data: The data to compute the digest for. + :return: Hex-encoded 32-byte order digest. + """ + encoded_data = encode_typed_data( + domain_data=domain, message_types=types, message_data=data + ) + return _hash_eip191_message(encoded_data) + + +def hash_order(domain, order): + """ + Compute the 32-byte signing hash for the specified order. + + :param domain: The EIP-712 domain separator to compute the hash for. + :param order: The order to compute the digest for. + :return: Hex-encoded 32-byte order digest. + """ + return hash_typed_data(domain, ORDER_TYPE_FIELDS, normalize_order(order)) + + +def hash_order_cancellation(domain, order_uid) -> str: + """ + Compute the 32-byte signing hash for the specified cancellation. + + :param domain: The EIP-712 domain separator to compute the hash for. + :param order_uid: The unique identifier of the order to cancel. + :return: Hex-encoded 32-byte order digest. + """ + return hash_order_cancellations(domain, [order_uid]) + + +def hash_order_cancellations(domain_data, order_uids) -> str: + """ + Compute the 32-byte signing hash for the specified order cancellations. + + :param domain_data: The EIP-712 domain separator to compute the hash for. + :param order_uids: The unique identifiers of the orders to cancel. + :return: Hex-encoded 32-byte order digest. + """ + return _hash_eip191_message( + encode_typed_data( + domain_data, + message_types={"OrderCancellations": CANCELLATIONS_TYPE_FIELDS}, + message_data={"orderUids": order_uids}, + ) + ).hex() + + +# The byte length of an order UID. +ORDER_UID_LENGTH = 56 + + +@dataclass +class OrderUidParams: + # The EIP-712 order struct hash. + orderDigest: str + # The owner of the order. + owner: str + # The timestamp this order is valid until. + validTo: int diff --git a/cow_py/contracts/sign.py b/cow_py/contracts/sign.py new file mode 100644 index 0000000..4217527 --- /dev/null +++ b/cow_py/contracts/sign.py @@ -0,0 +1,104 @@ +from enum import IntEnum +from typing import List, NamedTuple, Union + +from eth_account import Account +from eth_account.datastructures import SignedMessage +from eth_account.signers.local import LocalAccount +from eth_utils.conversions import to_hex +from eth_utils.crypto import keccak +from web3 import Web3 + +from cow_py.contracts.order import ( + CANCELLATIONS_TYPE_FIELDS, + ORDER_TYPE_FIELDS, + Order, + hash_typed_data, + normalize_order, +) + +EIP1271_MAGICVALUE = to_hex(keccak(text="isValidSignature(bytes32,bytes)"))[:10] +PRE_SIGNED = to_hex(keccak(text="GPv2Signing.Scheme.PreSign")) + + +class SigningScheme(IntEnum): + # The EIP-712 typed data signing scheme. This is the preferred scheme as it + # provides more infomation to wallets performing the signature on the data + # being signed. + # + # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator + EIP712 = 0b00 + # Message signed using eth_sign RPC call. + ETHSIGN = 0b01 + # Smart contract signatures as defined in EIP-1271. + EIP1271 = 0b10 + # Pre-signed order. + PRESIGN = 0b11 + + +class EcdsaSignature(NamedTuple): + scheme: SigningScheme + data: str + + +class Eip1271SignatureData(NamedTuple): + verifier: str + signature: bytes + + +class Eip1271Signature(NamedTuple): + scheme: SigningScheme + data: Eip1271SignatureData + + +class PreSignSignature(NamedTuple): + scheme: SigningScheme + data: str + + +Signature = Union[EcdsaSignature, Eip1271Signature, PreSignSignature] + + +def ecdsa_sign_typed_data( + scheme, owner: LocalAccount, domain_data, message_types, message_data +) -> SignedMessage: + return Account._sign_hash( + hash_typed_data(domain_data, message_types, message_data), owner.key + ) + + +def sign_order( + domain, order: Order, owner: LocalAccount, scheme: SigningScheme +) -> EcdsaSignature: + normalized_order = normalize_order(order) + signed_data = ecdsa_sign_typed_data( + scheme, owner, domain, {"Order": ORDER_TYPE_FIELDS}, normalized_order + ) + return EcdsaSignature( + scheme=scheme, + data=signed_data.signature.hex(), + ) + + +def sign_order_cancellation(domain, order_uid: Union[str, bytes], owner, scheme): + return sign_order_cancellations(domain, [order_uid], owner, scheme) + + +def sign_order_cancellations( + domain, order_uids: List[Union[str, bytes]], owner, scheme +): + data = {"orderUids": order_uids} + types = {"OrderCancellations": CANCELLATIONS_TYPE_FIELDS} + + signed_data = ecdsa_sign_typed_data(scheme, owner, domain, types, data) + + return EcdsaSignature(scheme=scheme, data=signed_data.signature.hex()) + + +def encode_eip1271_signature_data(verifier, signature): + return Web3.solidity_keccak(["address", "bytes"], [verifier, signature]) + + +def decode_eip1271_signature_data(signature): + arrayified_signature = bytes.fromhex(signature[2:]) # Removing '0x' + verifier = Web3.to_checksum_address(arrayified_signature[:20].hex()) + return Eip1271SignatureData(verifier, arrayified_signature[20:]) diff --git a/cow_py/web3/provider.py b/cow_py/web3/provider.py index fc5a95d..7087d24 100644 --- a/cow_py/web3/provider.py +++ b/cow_py/web3/provider.py @@ -1,6 +1,7 @@ from typing import Dict import web3 + from cow_py.common.chains import Chain DEFAULT_PROVIDER_NETWORK_MAPPING = { diff --git a/tests/contracts/conftest.py b/tests/contracts/conftest.py new file mode 100644 index 0000000..98af89f --- /dev/null +++ b/tests/contracts/conftest.py @@ -0,0 +1,39 @@ +from eth_utils.conversions import to_hex +from eth_utils.crypto import keccak +from eth_utils.currency import to_wei + +from cow_py.contracts.order import Order + + +def fill_bytes(count, byte): + return to_hex(bytearray([byte] * count)) + + +def fill_distinct_bytes(count, start): + return to_hex(bytearray([(start + i) % 256 for i in range(count)])) + + +def fill_uint(bits, byte): + return int(fill_bytes(bits // 8, byte), 16) + + +def ceil_div(p, q): + return (p + q - 1) // q + + +ORDER_KIND_SELL = "SELL" + +SAMPLE_ORDER = Order( + **{ + "sellToken": fill_bytes(20, 0x01), + "buyToken": fill_bytes(20, 0x02), + "receiver": fill_bytes(20, 0x03), + "sellAmount": to_wei("42", "ether"), + "buyAmount": to_wei("13.37", "ether"), + "validTo": 0xFFFFFFFF, + "appData": keccak(text="")[0:20], + "feeAmount": to_wei("1.0", "ether"), + "kind": ORDER_KIND_SELL, + "partiallyFillable": False, + } +) diff --git a/tests/contracts/test_orders.py b/tests/contracts/test_orders.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/contracts/test_sign.py b/tests/contracts/test_sign.py new file mode 100644 index 0000000..a15e47b --- /dev/null +++ b/tests/contracts/test_sign.py @@ -0,0 +1,76 @@ +import pytest +from eth_account.messages import SignableMessage +from eth_account.signers.local import LocalAccount +from eth_utils.conversions import to_hex +from web3 import EthereumTesterProvider, Web3 + +from cow_py.contracts.order import hash_order_cancellation + +from cow_py.contracts.sign import SigningScheme, sign_order, sign_order_cancellation + +from .conftest import SAMPLE_ORDER + +w3 = Web3(EthereumTesterProvider()) + + +def patched_sign_message_builder(account: LocalAccount): + def sign_message(message): + # Determine the correct message format + if isinstance(message, SignableMessage): + message_to_hash = message.body + elif isinstance(message, (bytes, str)): + message_to_hash = message + else: + raise TypeError("Unsupported message type for signing.") + + # Hash and sign the message + message_hash = Web3.solidity_keccak(["bytes"], [message_to_hash]) + signature = account.signHash(message_hash) + r, s, v = signature["r"], signature["s"], signature["v"] + + # Adjust v to be 27 or 28 + v_adjusted = v + 27 if v < 27 else v + + # Concatenate the signature components into a hex string + signature_hex = to_hex(r)[2:] + to_hex(s)[2:] + hex(v_adjusted)[2:] + + return signature_hex + + return sign_message + + +@pytest.mark.asyncio +@pytest.mark.parametrize("scheme", [SigningScheme.EIP712, SigningScheme.ETHSIGN]) +async def test_sign_order(monkeypatch, scheme): + signer = w3.eth.account.create() + + patched_sign_message = patched_sign_message_builder(signer) + + # Use monkeypatch to temporarily replace sign_message + monkeypatch.setattr(signer, "sign_message", patched_sign_message) + + domain = {"name": "test"} + signed_order = sign_order(domain, SAMPLE_ORDER, signer, scheme) + + # Extract 'v' value from the last two characters of the signature + v = signed_order.data[-2:] + + assert v in ["1b", "1c"] + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "scheme", [SigningScheme.EIP712.value, SigningScheme.ETHSIGN.value] +) +async def test_sign_order_cancellation(scheme): + signer = w3.eth.account.create() + domain = {"name": "test"} + order_uid = "0x" + "2a" * 56 + + signature_data = sign_order_cancellation(domain, order_uid, signer, scheme) + order_hash = hash_order_cancellation(domain, order_uid) + + assert ( + w3.eth.account._recover_hash(order_hash, signature=signature_data.data) + == signer.address + ) From 04f913911826c71f8d1b11f4894ecc11c226b9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:36:46 -0300 Subject: [PATCH 06/30] add order_book module --- cow_py/order_book/api.py | 203 +++++++++++++++++++++++++++++++++++ cow_py/order_book/config.py | 39 +++++++ tests/order_book/test_api.py | 147 +++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 cow_py/order_book/api.py create mode 100644 cow_py/order_book/config.py create mode 100644 tests/order_book/test_api.py diff --git a/cow_py/order_book/api.py b/cow_py/order_book/api.py new file mode 100644 index 0000000..c99b394 --- /dev/null +++ b/cow_py/order_book/api.py @@ -0,0 +1,203 @@ +import json +from typing import Any, Dict, List + +from cow_py.common.api.api_base import ApiBase, Context +from cow_py.common.config import SupportedChainId +from cow_py.order_book.config import OrderBookAPIConfigFactory +from typing import Union +from cow_py.order_book.generated.model import OrderQuoteSide2, OrderQuoteValidity2 + +from .generated.model import ( + UID, + Address, + AppDataHash, + AppDataObject, + NativePriceResponse, + Order, + OrderCancellation, + OrderCreation, + OrderQuoteRequest, + OrderQuoteResponse, + OrderQuoteSide, + OrderQuoteSide1, + OrderQuoteSide3, + OrderQuoteValidity, + OrderQuoteValidity1, + SolverCompetitionResponse, + TotalSurplus, + Trade, + TransactionHash, +) + + +class OrderBookApi(ApiBase): + def __init__( + self, + config=OrderBookAPIConfigFactory.get_config("prod", SupportedChainId.MAINNET), + ): + self.config = config + + async def get_version(self, context_override: Context = {}) -> str: + return await self._fetch( + path="/api/v1/version", context_override=context_override + ) + + async def get_trades_by_owner( + self, owner: Address, context_override: Context = {} + ) -> List[Trade]: + response = await self._fetch( + path="/api/v1/trades", + params={"owner": owner}, + context_override=context_override, + ) + return [Trade(**trade) for trade in response] + + async def get_trades_by_order_uid( + self, order_uid: UID, context_override: Context = {} + ) -> List[Trade]: + response = await self._fetch( + path="/api/v1/trades", + params={"order_uid": order_uid}, + context_override=context_override, + ) + return [Trade(**trade) for trade in response] + + async def get_orders_by_owner( + self, + owner: Address, + limit: int = 1000, + offset: int = 0, + context_override: Context = {}, + ) -> List[Order]: + return [ + Order(**order) + for order in await self._fetch( + path=f"/api/v1/account/{owner}/orders", + params={"limit": limit, "offset": offset}, + context_override=context_override, + ) + ] + + async def get_order_by_uid( + self, order_uid: UID, context_override: Context = {} + ) -> Order: + response = await self._fetch( + path=f"/api/v1/orders/{order_uid}", + context_override=context_override, + ) + return Order(**response) + + def get_order_link(self, order_uid: UID) -> str: + return self.config.get_base_url() + f"/api/v1/orders/{order_uid.root}" + + async def get_tx_orders( + self, tx_hash: TransactionHash, context_override: Context = {} + ) -> List[Order]: + response = await self._fetch( + path=f"/api/v1/transactions/{tx_hash}/orders", + context_override=context_override, + ) + return [Order(**order) for order in response] + + async def get_native_price( + self, tokenAddress: Address, context_override: Context = {} + ) -> NativePriceResponse: + response = await self._fetch( + path=f"/api/v1/token/{tokenAddress}/native_price", + context_override=context_override, + ) + return NativePriceResponse(**response) + + async def get_total_surplus( + self, user: Address, context_override: Context = {} + ) -> TotalSurplus: + response = await self._fetch( + path=f"/api/v1/users/{user}/total_surplus", + context_override=context_override, + ) + return TotalSurplus(**response) + + async def get_app_data( + self, app_data_hash: AppDataHash, context_override: Context = {} + ) -> Dict[str, Any]: + return await self._fetch( + path=f"/api/v1/app_data/{app_data_hash}", + context_override=context_override, + ) + + async def get_solver_competition( + self, action_id: Union[int, str] = "latest", context_override: Context = {} + ) -> SolverCompetitionResponse: + response = await self._fetch( + path=f"/api/v1/solver_competition/{action_id}", + context_override=context_override, + ) + return SolverCompetitionResponse(**response) + + async def get_solver_competition_by_tx_hash( + self, tx_hash: TransactionHash, context_override: Context = {} + ) -> SolverCompetitionResponse: + response = await self._fetch( + path=f"/api/v1/solver_competition/by_tx_hash/{tx_hash}", + context_override=context_override, + ) + return SolverCompetitionResponse(**response) + + async def post_quote( + self, + request: OrderQuoteRequest, + side: Union[OrderQuoteSide, OrderQuoteSide1, OrderQuoteSide2, OrderQuoteSide3], + validity: Union[ + OrderQuoteValidity, OrderQuoteValidity1, OrderQuoteValidity2 + ] = OrderQuoteValidity1(validTo=None), + context_override: Context = {}, + ) -> OrderQuoteResponse: + response = await self._fetch( + path="/api/v1/quote", + json={ + **request.model_dump(by_alias=True), + # side object need to be converted to json first to avoid on kind type + **json.loads(side.model_dump_json()), + **validity.model_dump(), + }, + context_override=context_override, + method="POST", + ) + return OrderQuoteResponse(**response) + + async def post_order(self, order: OrderCreation, context_override: Context = {}): + response = await self._fetch( + path="/api/v1/orders", + json=json.loads(order.model_dump_json(by_alias=True)), + context_override=context_override, + method="POST", + ) + return UID(response) + + async def delete_order( + self, + orders_cancelation: OrderCancellation, + context_override: Context = {}, + ): + response = await self._fetch( + path="/api/v1/orders", + json=orders_cancelation.model_dump_json(), + context_override=context_override, + method="DELETE", + ) + return UID(response) + + async def put_app_data( + self, + app_data: AppDataObject, + app_data_hash: str = "", + context_override: Context = {}, + ) -> AppDataHash: + app_data_hash_url = app_data_hash if app_data_hash else "" + response = await self._fetch( + path=f"/api/v1/app_data/{app_data_hash_url}", + json=app_data.model_dump_json(), + context_override=context_override, + method="PUT", + ) + return AppDataHash(response) diff --git a/cow_py/order_book/config.py b/cow_py/order_book/config.py new file mode 100644 index 0000000..1953ca7 --- /dev/null +++ b/cow_py/order_book/config.py @@ -0,0 +1,39 @@ +from typing import Dict, Literal, Type + +from cow_py.common.api.api_base import APIConfig +from cow_py.common.config import SupportedChainId + + +class ProdAPIConfig(APIConfig): + config_map = { + SupportedChainId.MAINNET: "https://api.cow.fi/mainnet", + SupportedChainId.GNOSIS_CHAIN: "https://api.cow.fi/xdai", + SupportedChainId.SEPOLIA: "https://api.cow.fi/sepolia", + } + + +class StagingAPIConfig(APIConfig): + config_map = { + SupportedChainId.MAINNET: "https://barn.api.cow.fi/mainnet", + SupportedChainId.GNOSIS_CHAIN: "https://barn.api.cow.fi/xdai", + SupportedChainId.SEPOLIA: "https://barn.api.cow.fi/sepolia", + } + + +Envs = Literal["prod", "staging"] + + +class OrderBookAPIConfigFactory: + config_classes: Dict[Envs, Type[APIConfig]] = { + "prod": ProdAPIConfig, + "staging": StagingAPIConfig, + } + + @staticmethod + def get_config(env: Envs, chain_id: SupportedChainId) -> APIConfig: + config_class = OrderBookAPIConfigFactory.config_classes.get(env) + + if config_class: + return config_class(chain_id) + else: + raise ValueError("Unknown environment") diff --git a/tests/order_book/test_api.py b/tests/order_book/test_api.py new file mode 100644 index 0000000..72f5d22 --- /dev/null +++ b/tests/order_book/test_api.py @@ -0,0 +1,147 @@ +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from cow_py.order_book.api import OrderBookApi +from cow_py.order_book.generated.model import OrderQuoteSide1 +from cow_py.order_book.generated.model import OrderQuoteSideKindSell +from cow_py.order_book.generated.model import TokenAmount +from cow_py.order_book.generated.model import ( + OrderQuoteRequest, + OrderQuoteResponse, + Trade, + OrderCreation, +) + + +@pytest.fixture +def order_book_api(): + return OrderBookApi() + + +@pytest.mark.asyncio +async def test_get_version(order_book_api): + expected_version = "1.0.0" + with patch("httpx.AsyncClient.request", new_callable=AsyncMock) as mock_request: + mock_request.return_value = AsyncMock( + status_code=200, + text=expected_version, + ) + version = await order_book_api.get_version() + + mock_request.assert_awaited_once() + assert version == expected_version + + +@pytest.mark.asyncio +async def test_get_trades_by_order_uid(order_book_api): + mock_trade_data = [ + { + "blockNumber": 123456, + "logIndex": 789, + "orderUid": "mock_order_uid", + "owner": "mock_owner_address", + "sellToken": "mock_sell_token_address", + "buyToken": "mock_buy_token_address", + "sellAmount": "100", + "sellAmountBeforeFees": "120", + "buyAmount": "200", + "txHash": "mock_transaction_hash", + } + ] + mock_trade = Trade(**mock_trade_data[0]) + with patch("httpx.AsyncClient.request", new_callable=AsyncMock) as mock_request: + mock_request.return_value = AsyncMock( + status_code=200, + headers={"content-type": "application/json"}, + json=Mock(return_value=mock_trade_data), + ) + trades = await order_book_api.get_trades_by_order_uid("mock_order_uid") + mock_request.assert_awaited_once() + assert trades == [mock_trade] + + +@pytest.mark.asyncio +async def test_post_quote(order_book_api): + mock_order_quote_request = OrderQuoteRequest( + **{ + "sellToken": "0x", + "buyToken": "0x", + "receiver": "0x", + "appData": "app_data_object", + "appDataHash": "0x", + "from": "0x", + "priceQuality": "verified", + "signingScheme": "eip712", + "onchainOrder": False, + } + ) + + mock_order_quote_side = OrderQuoteSide1( + sellAmountBeforeFee=TokenAmount("0"), kind=OrderQuoteSideKindSell.sell + ) + mock_order_quote_response_data = { + "quote": { + "sellToken": "0x", + "buyToken": "0x", + "receiver": "0x", + "sellAmount": "0", + "buyAmount": "0", + "feeAmount": "0", + "validTo": 0, + "appData": "0x", + "partiallyFillable": True, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + "kind": "buy", + }, + "verified": True, + "from": "0x", + "expiration": "0", + } + mock_order_quote_response = OrderQuoteResponse(**mock_order_quote_response_data) + with patch("httpx.AsyncClient.request", new_callable=AsyncMock) as mock_request: + mock_request.return_value = AsyncMock( + status_code=200, + headers={"content-type": "application/json"}, + json=Mock(return_value=mock_order_quote_response_data), + ) + response = await order_book_api.post_quote( + mock_order_quote_request, mock_order_quote_side + ) + mock_request.assert_awaited_once() + assert response == mock_order_quote_response + + +@pytest.mark.asyncio +async def test_post_order(order_book_api): + mock_response = "mock_uid" + mock_order_creation = OrderCreation( + **{ + "sellToken": "0x", + "buyToken": "0x", + "sellAmount": "0", + "buyAmount": "0", + "validTo": 0, + "feeAmount": "0", + "kind": "buy", + "partiallyFillable": True, + "appData": "0x", + "signingScheme": "eip712", + "signature": "0x", + "receiver": "0x", + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + "quoteId": 0, + "appDataHash": "0x", + "from_": "0x", + } + ) + with patch("httpx.AsyncClient.request", new_callable=AsyncMock) as mock_request: + mock_request.return_value = AsyncMock( + status_code=200, + text=mock_response, + ) + response = await order_book_api.post_order(mock_order_creation) + mock_request.assert_awaited_once() + assert response.root == mock_response From 661fe9b0a05952f43871b620831c59fbd1513968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:36:56 -0300 Subject: [PATCH 07/30] add order posting example --- examples/__init__.py | 0 examples/order_posting_e2e.py | 120 ++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 examples/__init__.py create mode 100644 examples/order_posting_e2e.py diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/order_posting_e2e.py b/examples/order_posting_e2e.py new file mode 100644 index 0000000..5dc9842 --- /dev/null +++ b/examples/order_posting_e2e.py @@ -0,0 +1,120 @@ +# To run this test you will need to fill the .env file with the necessary variables (see .env.example). +# You will also need to have enough funds in you wallet of the sell token to create the order. +# The funds have to already be approved to the CoW Swap Vault Relayer + +import asyncio +import json +import os +from dataclasses import asdict + +from web3 import Account + +from cow_py.common.chains import Chain +from cow_py.common.config import SupportedChainId +from cow_py.common.constants import CowContractAddress +from cow_py.contracts.domain import domain +from cow_py.contracts.order import Order +from cow_py.contracts.sign import EcdsaSignature, SigningScheme +from cow_py.contracts.sign import sign_order as _sign_order +from cow_py.order_book.api import OrderBookApi +from cow_py.order_book.config import OrderBookAPIConfigFactory +from cow_py.order_book.generated.model import OrderQuoteSide1, TokenAmount +from cow_py.order_book.generated.model import OrderQuoteSideKindSell +from cow_py.order_book.generated.model import ( + UID, + OrderCreation, + OrderQuoteRequest, + OrderQuoteResponse, +) + +BUY_TOKEN = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14" # WETH +SELL_TOKEN = "0xbe72E441BF55620febc26715db68d3494213D8Cb" # USDC +SELL_AMOUNT_BEFORE_FEE = "10000000000000000000" # 100 USDC with 18 decimals +ORDER_KIND = "sell" +CHAIN = Chain.SEPOLIA +CHAIN_ID = SupportedChainId.SEPOLIA + +config = OrderBookAPIConfigFactory.get_config("prod", CHAIN_ID) +ORDER_BOOK_API = OrderBookApi(config) + +ADDRESS = os.getenv("USER_ADDRESS") +ACCOUNT = Account.from_key(os.getenv("PRIVATE_KEY")) + + +async def get_order_quote( + order_quote_request: OrderQuoteRequest, order_side: OrderQuoteSide1 +) -> OrderQuoteResponse: + return await ORDER_BOOK_API.post_quote(order_quote_request, order_side) + + +def sign_order(order: Order) -> EcdsaSignature: + order_domain = asdict( + domain( + chain=CHAIN, verifying_contract=CowContractAddress.SETTLEMENT_CONTRACT.value + ) + ) + del order_domain["salt"] # TODO: improve interfaces + + return _sign_order(order_domain, order, ACCOUNT, SigningScheme.EIP712) + + +async def post_order(order: Order, signature: EcdsaSignature) -> UID: + order_creation = OrderCreation( + **{ + "from": ADDRESS, + "sellToken": order.sellToken, + "buyToken": order.buyToken, + "sellAmount": order.sellAmount, + "feeAmount": order.feeAmount, + "buyAmount": order.buyAmount, + "validTo": order.validTo, + "kind": order.kind, + "partiallyFillable": order.partiallyFillable, + "appData": order.appData, + "signature": signature.data, + "signingScheme": "eip712", + "receiver": order.receiver, + }, + ) + return await ORDER_BOOK_API.post_order(order_creation) + + +async def main(): + order_quote_request = OrderQuoteRequest( + **{ + "sellToken": SELL_TOKEN, + "buyToken": BUY_TOKEN, + "from": ADDRESS, + } + ) + order_side = OrderQuoteSide1( + kind=OrderQuoteSideKindSell.sell, + sellAmountBeforeFee=TokenAmount(SELL_AMOUNT_BEFORE_FEE), + ) + + order_quote = await get_order_quote(order_quote_request, order_side) + + order_quote_dict = json.loads(order_quote.quote.model_dump_json(by_alias=True)) + order = Order( + **{ + "sellToken": SELL_TOKEN, + "buyToken": BUY_TOKEN, + "receiver": ADDRESS, + "validTo": order_quote_dict["validTo"], + "appData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sellAmount": SELL_AMOUNT_BEFORE_FEE, # Since it is a sell order, the sellAmountBeforeFee is the same as the sellAmount + "buyAmount": order_quote_dict["buyAmount"], + "feeAmount": "0", # CoW Swap does not charge fees + "kind": ORDER_KIND, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + } + ) + + signature = sign_order(order) + order_uid = await post_order(order, signature) + print(f"order posted on link: {ORDER_BOOK_API.get_order_link(order_uid)}") + + +if __name__ == "__main__": + asyncio.run(main()) From b1a949ca36cb137f746d7e6c2e85385192f56842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:39:38 -0300 Subject: [PATCH 08/30] add generated subgraph files --- cow_py/subgraph/__init__.py | 0 cow_py/subgraph/client/__init__.py | 115 + cow_py/subgraph/client/async_base_client.py | 370 +++ cow_py/subgraph/client/base_model.py | 30 + cow_py/subgraph/client/enums.py | 423 +++ cow_py/subgraph/client/exceptions.py | 85 + cow_py/subgraph/client/input_types.py | 2567 +++++++++++++++++ cow_py/subgraph/client/last_days_volume.py | 17 + cow_py/subgraph/client/last_hours_volume.py | 17 + cow_py/subgraph/client/subgraph_client.py | 75 + cow_py/subgraph/client/totals.py | 23 + cow_py/subgraph/deployments.py | 40 + cow_py/subgraph/queries/lastDayVolume.gql | 10 + cow_py/subgraph/queries/lastHoursVolume.gql | 10 + cow_py/subgraph/queries/totals.gql | 15 + tests/subgraph/__init__.py | 0 tests/subgraph/client/test_subgraph_client.py | 466 +++ tests/subgraph/test_deployments.py | 44 + 18 files changed, 4307 insertions(+) create mode 100644 cow_py/subgraph/__init__.py create mode 100644 cow_py/subgraph/client/__init__.py create mode 100644 cow_py/subgraph/client/async_base_client.py create mode 100644 cow_py/subgraph/client/base_model.py create mode 100644 cow_py/subgraph/client/enums.py create mode 100644 cow_py/subgraph/client/exceptions.py create mode 100644 cow_py/subgraph/client/input_types.py create mode 100644 cow_py/subgraph/client/last_days_volume.py create mode 100644 cow_py/subgraph/client/last_hours_volume.py create mode 100644 cow_py/subgraph/client/subgraph_client.py create mode 100644 cow_py/subgraph/client/totals.py create mode 100644 cow_py/subgraph/deployments.py create mode 100644 cow_py/subgraph/queries/lastDayVolume.gql create mode 100644 cow_py/subgraph/queries/lastHoursVolume.gql create mode 100644 cow_py/subgraph/queries/totals.gql create mode 100644 tests/subgraph/__init__.py create mode 100644 tests/subgraph/client/test_subgraph_client.py create mode 100644 tests/subgraph/test_deployments.py diff --git a/cow_py/subgraph/__init__.py b/cow_py/subgraph/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cow_py/subgraph/client/__init__.py b/cow_py/subgraph/client/__init__.py new file mode 100644 index 0000000..ec3b94a --- /dev/null +++ b/cow_py/subgraph/client/__init__.py @@ -0,0 +1,115 @@ +# Generated by ariadne-codegen + +from .async_base_client import AsyncBaseClient +from .base_model import BaseModel, Upload +from .enums import ( + Aggregation_interval, + Bundle_orderBy, + DailyTotal_orderBy, + HourlyTotal_orderBy, + Order_orderBy, + OrderDirection, + Pair_orderBy, + PairDaily_orderBy, + PairHourly_orderBy, + Settlement_orderBy, + Token_orderBy, + TokenDailyTotal_orderBy, + TokenHourlyTotal_orderBy, + TokenTradingEvent_orderBy, + Total_orderBy, + Trade_orderBy, + UniswapPool_orderBy, + UniswapToken_orderBy, + User_orderBy, + _SubgraphErrorPolicy_, +) +from .exceptions import ( + GraphQLClientError, + GraphQLClientGraphQLError, + GraphQLClientGraphQLMultiError, + GraphQLClientHttpError, + GraphQLClientInvalidResponseError, +) +from .input_types import ( + Block_height, + BlockChangedFilter, + Bundle_filter, + DailyTotal_filter, + HourlyTotal_filter, + Order_filter, + Pair_filter, + PairDaily_filter, + PairHourly_filter, + Settlement_filter, + Token_filter, + TokenDailyTotal_filter, + TokenHourlyTotal_filter, + TokenTradingEvent_filter, + Total_filter, + Trade_filter, + UniswapPool_filter, + UniswapToken_filter, + User_filter, +) +from .last_days_volume import LastDaysVolume, LastDaysVolumeDailyTotals +from .last_hours_volume import LastHoursVolume, LastHoursVolumeHourlyTotals +from .subgraph_client import SubgraphClient +from .totals import Totals, TotalsTotals + +__all__ = [ + "Aggregation_interval", + "AsyncBaseClient", + "BaseModel", + "BlockChangedFilter", + "Block_height", + "Bundle_filter", + "Bundle_orderBy", + "DailyTotal_filter", + "DailyTotal_orderBy", + "GraphQLClientError", + "GraphQLClientGraphQLError", + "GraphQLClientGraphQLMultiError", + "GraphQLClientHttpError", + "GraphQLClientInvalidResponseError", + "HourlyTotal_filter", + "HourlyTotal_orderBy", + "LastDaysVolume", + "LastDaysVolumeDailyTotals", + "LastHoursVolume", + "LastHoursVolumeHourlyTotals", + "OrderDirection", + "Order_filter", + "Order_orderBy", + "PairDaily_filter", + "PairDaily_orderBy", + "PairHourly_filter", + "PairHourly_orderBy", + "Pair_filter", + "Pair_orderBy", + "Settlement_filter", + "Settlement_orderBy", + "SubgraphClient", + "TokenDailyTotal_filter", + "TokenDailyTotal_orderBy", + "TokenHourlyTotal_filter", + "TokenHourlyTotal_orderBy", + "TokenTradingEvent_filter", + "TokenTradingEvent_orderBy", + "Token_filter", + "Token_orderBy", + "Total_filter", + "Total_orderBy", + "Totals", + "TotalsTotals", + "Trade_filter", + "Trade_orderBy", + "UniswapPool_filter", + "UniswapPool_orderBy", + "UniswapToken_filter", + "UniswapToken_orderBy", + "Upload", + "User_filter", + "User_orderBy", + "_SubgraphErrorPolicy_", +] diff --git a/cow_py/subgraph/client/async_base_client.py b/cow_py/subgraph/client/async_base_client.py new file mode 100644 index 0000000..b9f1f6b --- /dev/null +++ b/cow_py/subgraph/client/async_base_client.py @@ -0,0 +1,370 @@ +# Generated by ariadne-codegen + +import enum +import json +from typing import IO, Any, AsyncIterator, Dict, List, Optional, Tuple, TypeVar, cast +from uuid import uuid4 + +import httpx +from pydantic import BaseModel +from pydantic_core import to_jsonable_python + +from .base_model import UNSET, Upload +from .exceptions import ( + GraphQLClientGraphQLMultiError, + GraphQLClientHttpError, + GraphQLClientInvalidMessageFormat, + GraphQLClientInvalidResponseError, +) + +try: + from websockets.client import WebSocketClientProtocol + from websockets.client import connect as ws_connect # type: ignore[import-not-found,unused-ignore] + from websockets.typing import ( # type: ignore[import-not-found,unused-ignore] + Data, + Origin, + Subprotocol, + ) +except ImportError: + from contextlib import asynccontextmanager + + @asynccontextmanager # type: ignore + async def ws_connect(*args, **kwargs): # pylint: disable=unused-argument + raise NotImplementedError("Subscriptions require 'websockets' package.") + yield # pylint: disable=unreachable + + WebSocketClientProtocol = Any # type: ignore[misc,assignment,unused-ignore] + Data = Any # type: ignore[misc,assignment,unused-ignore] + Origin = Any # type: ignore[misc,assignment,unused-ignore] + + def Subprotocol(*args, **kwargs): # type: ignore # pylint: disable=invalid-name + raise NotImplementedError("Subscriptions require 'websockets' package.") + + +Self = TypeVar("Self", bound="AsyncBaseClient") + +GRAPHQL_TRANSPORT_WS = "graphql-transport-ws" + + +class GraphQLTransportWSMessageType(str, enum.Enum): + CONNECTION_INIT = "connection_init" + CONNECTION_ACK = "connection_ack" + PING = "ping" + PONG = "pong" + SUBSCRIBE = "subscribe" + NEXT = "next" + ERROR = "error" + COMPLETE = "complete" + + +class AsyncBaseClient: + def __init__( + self, + url: str = "", + headers: Optional[Dict[str, str]] = None, + http_client: Optional[httpx.AsyncClient] = None, + ws_url: str = "", + ws_headers: Optional[Dict[str, Any]] = None, + ws_origin: Optional[str] = None, + ws_connection_init_payload: Optional[Dict[str, Any]] = None, + ) -> None: + self.url = url + self.headers = headers + self.http_client = ( + http_client if http_client else httpx.AsyncClient(headers=headers) + ) + + self.ws_url = ws_url + self.ws_headers = ws_headers or {} + self.ws_origin = Origin(ws_origin) if ws_origin else None + self.ws_connection_init_payload = ws_connection_init_payload + + async def __aenter__(self: Self) -> Self: + return self + + async def __aexit__( + self, + exc_type: object, + exc_val: object, + exc_tb: object, + ) -> None: + await self.http_client.aclose() + + async def execute( + self, + query: str, + operation_name: Optional[str] = None, + variables: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> httpx.Response: + processed_variables, files, files_map = self._process_variables(variables) + + if files and files_map: + return await self._execute_multipart( + query=query, + operation_name=operation_name, + variables=processed_variables, + files=files, + files_map=files_map, + **kwargs, + ) + + return await self._execute_json( + query=query, + operation_name=operation_name, + variables=processed_variables, + **kwargs, + ) + + def get_data(self, response: httpx.Response) -> Dict[str, Any]: + if not response.is_success: + raise GraphQLClientHttpError( + status_code=response.status_code, response=response + ) + + try: + response_json = response.json() + except ValueError as exc: + raise GraphQLClientInvalidResponseError(response=response) from exc + + if (not isinstance(response_json, dict)) or ( + "data" not in response_json and "errors" not in response_json + ): + raise GraphQLClientInvalidResponseError(response=response) + + data = response_json.get("data") + errors = response_json.get("errors") + + if errors: + raise GraphQLClientGraphQLMultiError.from_errors_dicts( + errors_dicts=errors, data=data + ) + + return cast(Dict[str, Any], data) + + async def execute_ws( + self, + query: str, + operation_name: Optional[str] = None, + variables: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> AsyncIterator[Dict[str, Any]]: + headers = self.ws_headers.copy() + headers.update(kwargs.get("extra_headers", {})) + + merged_kwargs: Dict[str, Any] = {"origin": self.ws_origin} + merged_kwargs.update(kwargs) + merged_kwargs["extra_headers"] = headers + + operation_id = str(uuid4()) + async with ws_connect( + self.ws_url, + subprotocols=[Subprotocol(GRAPHQL_TRANSPORT_WS)], + **merged_kwargs, + ) as websocket: + await self._send_connection_init(websocket) + # wait for connection_ack from server + await self._handle_ws_message( + await websocket.recv(), + websocket, + expected_type=GraphQLTransportWSMessageType.CONNECTION_ACK, + ) + await self._send_subscribe( + websocket, + operation_id=operation_id, + query=query, + operation_name=operation_name, + variables=variables, + ) + + async for message in websocket: + data = await self._handle_ws_message(message, websocket) + if data: + yield data + + def _process_variables( + self, variables: Optional[Dict[str, Any]] + ) -> Tuple[ + Dict[str, Any], Dict[str, Tuple[str, IO[bytes], str]], Dict[str, List[str]] + ]: + if not variables: + return {}, {}, {} + + serializable_variables = self._convert_dict_to_json_serializable(variables) + return self._get_files_from_variables(serializable_variables) + + def _convert_dict_to_json_serializable( + self, dict_: Dict[str, Any] + ) -> Dict[str, Any]: + return { + key: self._convert_value(value) + for key, value in dict_.items() + if value is not UNSET + } + + def _convert_value(self, value: Any) -> Any: + if isinstance(value, BaseModel): + return value.model_dump(by_alias=True, exclude_unset=True) + if isinstance(value, list): + return [self._convert_value(item) for item in value] + return value + + def _get_files_from_variables( + self, variables: Dict[str, Any] + ) -> Tuple[ + Dict[str, Any], Dict[str, Tuple[str, IO[bytes], str]], Dict[str, List[str]] + ]: + files_map: Dict[str, List[str]] = {} + files_list: List[Upload] = [] + + def separate_files(path: str, obj: Any) -> Any: + if isinstance(obj, list): + nulled_list = [] + for index, value in enumerate(obj): + value = separate_files(f"{path}.{index}", value) + nulled_list.append(value) + return nulled_list + + if isinstance(obj, dict): + nulled_dict = {} + for key, value in obj.items(): + value = separate_files(f"{path}.{key}", value) + nulled_dict[key] = value + return nulled_dict + + if isinstance(obj, Upload): + if obj in files_list: + file_index = files_list.index(obj) + files_map[str(file_index)].append(path) + else: + file_index = len(files_list) + files_list.append(obj) + files_map[str(file_index)] = [path] + return None + + return obj + + nulled_variables = separate_files("variables", variables) + files: Dict[str, Tuple[str, IO[bytes], str]] = { + str(i): (file_.filename, cast(IO[bytes], file_.content), file_.content_type) + for i, file_ in enumerate(files_list) + } + return nulled_variables, files, files_map + + async def _execute_multipart( + self, + query: str, + operation_name: Optional[str], + variables: Dict[str, Any], + files: Dict[str, Tuple[str, IO[bytes], str]], + files_map: Dict[str, List[str]], + **kwargs: Any, + ) -> httpx.Response: + data = { + "operations": json.dumps( + { + "query": query, + "operationName": operation_name, + "variables": variables, + }, + default=to_jsonable_python, + ), + "map": json.dumps(files_map, default=to_jsonable_python), + } + + return await self.http_client.post( + url=self.url, data=data, files=files, **kwargs + ) + + async def _execute_json( + self, + query: str, + operation_name: Optional[str], + variables: Dict[str, Any], + **kwargs: Any, + ) -> httpx.Response: + headers: Dict[str, str] = {"Content-Type": "application/json"} + headers.update(kwargs.get("headers", {})) + + merged_kwargs: Dict[str, Any] = kwargs.copy() + merged_kwargs["headers"] = headers + + return await self.http_client.post( + url=self.url, + content=json.dumps( + { + "query": query, + "operationName": operation_name, + "variables": variables, + }, + default=to_jsonable_python, + ), + **merged_kwargs, + ) + + async def _send_connection_init(self, websocket: WebSocketClientProtocol) -> None: + payload: Dict[str, Any] = { + "type": GraphQLTransportWSMessageType.CONNECTION_INIT.value + } + if self.ws_connection_init_payload: + payload["payload"] = self.ws_connection_init_payload + await websocket.send(json.dumps(payload)) + + async def _send_subscribe( + self, + websocket: WebSocketClientProtocol, + operation_id: str, + query: str, + operation_name: Optional[str] = None, + variables: Optional[Dict[str, Any]] = None, + ) -> None: + payload: Dict[str, Any] = { + "id": operation_id, + "type": GraphQLTransportWSMessageType.SUBSCRIBE.value, + "payload": {"query": query, "operationName": operation_name}, + } + if variables: + payload["payload"]["variables"] = self._convert_dict_to_json_serializable( + variables + ) + await websocket.send(json.dumps(payload)) + + async def _handle_ws_message( + self, + message: Data, + websocket: WebSocketClientProtocol, + expected_type: Optional[GraphQLTransportWSMessageType] = None, + ) -> Optional[Dict[str, Any]]: + try: + message_dict = json.loads(message) + except json.JSONDecodeError as exc: + raise GraphQLClientInvalidMessageFormat(message=message) from exc + + type_ = message_dict.get("type") + payload = message_dict.get("payload", {}) + + if not type_ or type_ not in {t.value for t in GraphQLTransportWSMessageType}: + raise GraphQLClientInvalidMessageFormat(message=message) + + if expected_type and expected_type != type_: + raise GraphQLClientInvalidMessageFormat( + f"Invalid message received. Expected: {expected_type.value}" + ) + + if type_ == GraphQLTransportWSMessageType.NEXT: + if "data" not in payload: + raise GraphQLClientInvalidMessageFormat(message=message) + return cast(Dict[str, Any], payload["data"]) + + if type_ == GraphQLTransportWSMessageType.COMPLETE: + await websocket.close() + elif type_ == GraphQLTransportWSMessageType.PING: + await websocket.send( + json.dumps({"type": GraphQLTransportWSMessageType.PONG.value}) + ) + elif type_ == GraphQLTransportWSMessageType.ERROR: + raise GraphQLClientGraphQLMultiError.from_errors_dicts( + errors_dicts=payload, data=message_dict + ) + + return None diff --git a/cow_py/subgraph/client/base_model.py b/cow_py/subgraph/client/base_model.py new file mode 100644 index 0000000..a93b416 --- /dev/null +++ b/cow_py/subgraph/client/base_model.py @@ -0,0 +1,30 @@ +# Generated by ariadne-codegen + +from io import IOBase + +from pydantic import BaseModel as PydanticBaseModel +from pydantic import ConfigDict + + +class UnsetType: + def __bool__(self) -> bool: + return False + + +UNSET = UnsetType() + + +class BaseModel(PydanticBaseModel): + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + arbitrary_types_allowed=True, + protected_namespaces=(), + ) + + +class Upload: + def __init__(self, filename: str, content: IOBase, content_type: str): + self.filename = filename + self.content = content + self.content_type = content_type diff --git a/cow_py/subgraph/client/enums.py b/cow_py/subgraph/client/enums.py new file mode 100644 index 0000000..1225b1a --- /dev/null +++ b/cow_py/subgraph/client/enums.py @@ -0,0 +1,423 @@ +# Generated by ariadne-codegen +# Source: https://api.thegraph.com/subgraphs/name/cowprotocol/cow + +from enum import Enum + + +class Aggregation_interval(str, Enum): + hour = "hour" + day = "day" + + +class Bundle_orderBy(str, Enum): + id = "id" + ethPriceUSD = "ethPriceUSD" + + +class DailyTotal_orderBy(str, Enum): + id = "id" + timestamp = "timestamp" + totalTokens = "totalTokens" + numberOfTrades = "numberOfTrades" + orders = "orders" + settlements = "settlements" + volumeUsd = "volumeUsd" + volumeEth = "volumeEth" + feesUsd = "feesUsd" + feesEth = "feesEth" + tokens = "tokens" + + +class HourlyTotal_orderBy(str, Enum): + id = "id" + timestamp = "timestamp" + totalTokens = "totalTokens" + numberOfTrades = "numberOfTrades" + orders = "orders" + settlements = "settlements" + volumeUsd = "volumeUsd" + volumeEth = "volumeEth" + feesUsd = "feesUsd" + feesEth = "feesEth" + tokens = "tokens" + + +class OrderDirection(str, Enum): + asc = "asc" + desc = "desc" + + +class Order_orderBy(str, Enum): + id = "id" + owner = "owner" + owner__id = "owner__id" + owner__address = "owner__address" + owner__firstTradeTimestamp = "owner__firstTradeTimestamp" + owner__isSolver = "owner__isSolver" + owner__numberOfTrades = "owner__numberOfTrades" + owner__solvedAmountEth = "owner__solvedAmountEth" + owner__solvedAmountUsd = "owner__solvedAmountUsd" + owner__tradedAmountUsd = "owner__tradedAmountUsd" + owner__tradedAmountEth = "owner__tradedAmountEth" + tradesTimestamp = "tradesTimestamp" + invalidateTimestamp = "invalidateTimestamp" + presignTimestamp = "presignTimestamp" + trades = "trades" + isSigned = "isSigned" + isValid = "isValid" + + +class PairDaily_orderBy(str, Enum): + id = "id" + token0 = "token0" + token0__id = "token0__id" + token0__address = "token0__address" + token0__firstTradeTimestamp = "token0__firstTradeTimestamp" + token0__name = "token0__name" + token0__symbol = "token0__symbol" + token0__decimals = "token0__decimals" + token0__totalVolume = "token0__totalVolume" + token0__priceEth = "token0__priceEth" + token0__priceUsd = "token0__priceUsd" + token0__numberOfTrades = "token0__numberOfTrades" + token0__totalVolumeUsd = "token0__totalVolumeUsd" + token0__totalVolumeEth = "token0__totalVolumeEth" + token1 = "token1" + token1__id = "token1__id" + token1__address = "token1__address" + token1__firstTradeTimestamp = "token1__firstTradeTimestamp" + token1__name = "token1__name" + token1__symbol = "token1__symbol" + token1__decimals = "token1__decimals" + token1__totalVolume = "token1__totalVolume" + token1__priceEth = "token1__priceEth" + token1__priceUsd = "token1__priceUsd" + token1__numberOfTrades = "token1__numberOfTrades" + token1__totalVolumeUsd = "token1__totalVolumeUsd" + token1__totalVolumeEth = "token1__totalVolumeEth" + token0Price = "token0Price" + token1Price = "token1Price" + token0relativePrice = "token0relativePrice" + token1relativePrice = "token1relativePrice" + timestamp = "timestamp" + volumeToken0 = "volumeToken0" + volumeToken1 = "volumeToken1" + volumeTradedEth = "volumeTradedEth" + volumeTradedUsd = "volumeTradedUsd" + + +class PairHourly_orderBy(str, Enum): + id = "id" + token0 = "token0" + token0__id = "token0__id" + token0__address = "token0__address" + token0__firstTradeTimestamp = "token0__firstTradeTimestamp" + token0__name = "token0__name" + token0__symbol = "token0__symbol" + token0__decimals = "token0__decimals" + token0__totalVolume = "token0__totalVolume" + token0__priceEth = "token0__priceEth" + token0__priceUsd = "token0__priceUsd" + token0__numberOfTrades = "token0__numberOfTrades" + token0__totalVolumeUsd = "token0__totalVolumeUsd" + token0__totalVolumeEth = "token0__totalVolumeEth" + token1 = "token1" + token1__id = "token1__id" + token1__address = "token1__address" + token1__firstTradeTimestamp = "token1__firstTradeTimestamp" + token1__name = "token1__name" + token1__symbol = "token1__symbol" + token1__decimals = "token1__decimals" + token1__totalVolume = "token1__totalVolume" + token1__priceEth = "token1__priceEth" + token1__priceUsd = "token1__priceUsd" + token1__numberOfTrades = "token1__numberOfTrades" + token1__totalVolumeUsd = "token1__totalVolumeUsd" + token1__totalVolumeEth = "token1__totalVolumeEth" + token0Price = "token0Price" + token1Price = "token1Price" + token0relativePrice = "token0relativePrice" + token1relativePrice = "token1relativePrice" + timestamp = "timestamp" + volumeToken0 = "volumeToken0" + volumeToken1 = "volumeToken1" + volumeTradedEth = "volumeTradedEth" + volumeTradedUsd = "volumeTradedUsd" + + +class Pair_orderBy(str, Enum): + id = "id" + token0 = "token0" + token0__id = "token0__id" + token0__address = "token0__address" + token0__firstTradeTimestamp = "token0__firstTradeTimestamp" + token0__name = "token0__name" + token0__symbol = "token0__symbol" + token0__decimals = "token0__decimals" + token0__totalVolume = "token0__totalVolume" + token0__priceEth = "token0__priceEth" + token0__priceUsd = "token0__priceUsd" + token0__numberOfTrades = "token0__numberOfTrades" + token0__totalVolumeUsd = "token0__totalVolumeUsd" + token0__totalVolumeEth = "token0__totalVolumeEth" + token1 = "token1" + token1__id = "token1__id" + token1__address = "token1__address" + token1__firstTradeTimestamp = "token1__firstTradeTimestamp" + token1__name = "token1__name" + token1__symbol = "token1__symbol" + token1__decimals = "token1__decimals" + token1__totalVolume = "token1__totalVolume" + token1__priceEth = "token1__priceEth" + token1__priceUsd = "token1__priceUsd" + token1__numberOfTrades = "token1__numberOfTrades" + token1__totalVolumeUsd = "token1__totalVolumeUsd" + token1__totalVolumeEth = "token1__totalVolumeEth" + token0Price = "token0Price" + token1Price = "token1Price" + token0relativePrice = "token0relativePrice" + token1relativePrice = "token1relativePrice" + volumeToken0 = "volumeToken0" + volumeToken1 = "volumeToken1" + volumeTradedEth = "volumeTradedEth" + volumeTradedUsd = "volumeTradedUsd" + + +class Settlement_orderBy(str, Enum): + id = "id" + txHash = "txHash" + firstTradeTimestamp = "firstTradeTimestamp" + trades = "trades" + solver = "solver" + solver__id = "solver__id" + solver__address = "solver__address" + solver__firstTradeTimestamp = "solver__firstTradeTimestamp" + solver__isSolver = "solver__isSolver" + solver__numberOfTrades = "solver__numberOfTrades" + solver__solvedAmountEth = "solver__solvedAmountEth" + solver__solvedAmountUsd = "solver__solvedAmountUsd" + solver__tradedAmountUsd = "solver__tradedAmountUsd" + solver__tradedAmountEth = "solver__tradedAmountEth" + + +class TokenDailyTotal_orderBy(str, Enum): + id = "id" + token = "token" + token__id = "token__id" + token__address = "token__address" + token__firstTradeTimestamp = "token__firstTradeTimestamp" + token__name = "token__name" + token__symbol = "token__symbol" + token__decimals = "token__decimals" + token__totalVolume = "token__totalVolume" + token__priceEth = "token__priceEth" + token__priceUsd = "token__priceUsd" + token__numberOfTrades = "token__numberOfTrades" + token__totalVolumeUsd = "token__totalVolumeUsd" + token__totalVolumeEth = "token__totalVolumeEth" + timestamp = "timestamp" + totalVolume = "totalVolume" + totalVolumeUsd = "totalVolumeUsd" + totalVolumeEth = "totalVolumeEth" + totalTrades = "totalTrades" + openPrice = "openPrice" + closePrice = "closePrice" + higherPrice = "higherPrice" + lowerPrice = "lowerPrice" + averagePrice = "averagePrice" + + +class TokenHourlyTotal_orderBy(str, Enum): + id = "id" + token = "token" + token__id = "token__id" + token__address = "token__address" + token__firstTradeTimestamp = "token__firstTradeTimestamp" + token__name = "token__name" + token__symbol = "token__symbol" + token__decimals = "token__decimals" + token__totalVolume = "token__totalVolume" + token__priceEth = "token__priceEth" + token__priceUsd = "token__priceUsd" + token__numberOfTrades = "token__numberOfTrades" + token__totalVolumeUsd = "token__totalVolumeUsd" + token__totalVolumeEth = "token__totalVolumeEth" + timestamp = "timestamp" + totalVolume = "totalVolume" + totalVolumeUsd = "totalVolumeUsd" + totalVolumeEth = "totalVolumeEth" + totalTrades = "totalTrades" + openPrice = "openPrice" + closePrice = "closePrice" + higherPrice = "higherPrice" + lowerPrice = "lowerPrice" + averagePrice = "averagePrice" + + +class TokenTradingEvent_orderBy(str, Enum): + id = "id" + token = "token" + token__id = "token__id" + token__address = "token__address" + token__firstTradeTimestamp = "token__firstTradeTimestamp" + token__name = "token__name" + token__symbol = "token__symbol" + token__decimals = "token__decimals" + token__totalVolume = "token__totalVolume" + token__priceEth = "token__priceEth" + token__priceUsd = "token__priceUsd" + token__numberOfTrades = "token__numberOfTrades" + token__totalVolumeUsd = "token__totalVolumeUsd" + token__totalVolumeEth = "token__totalVolumeEth" + trade = "trade" + trade__id = "trade__id" + trade__timestamp = "trade__timestamp" + trade__gasPrice = "trade__gasPrice" + trade__feeAmount = "trade__feeAmount" + trade__txHash = "trade__txHash" + trade__buyAmount = "trade__buyAmount" + trade__sellAmount = "trade__sellAmount" + trade__buyAmountEth = "trade__buyAmountEth" + trade__sellAmountEth = "trade__sellAmountEth" + trade__buyAmountUsd = "trade__buyAmountUsd" + trade__sellAmountUsd = "trade__sellAmountUsd" + timestamp = "timestamp" + amountEth = "amountEth" + amountUsd = "amountUsd" + + +class Token_orderBy(str, Enum): + id = "id" + address = "address" + firstTradeTimestamp = "firstTradeTimestamp" + name = "name" + symbol = "symbol" + decimals = "decimals" + totalVolume = "totalVolume" + priceEth = "priceEth" + priceUsd = "priceUsd" + history = "history" + hourlyTotals = "hourlyTotals" + dailyTotals = "dailyTotals" + numberOfTrades = "numberOfTrades" + totalVolumeUsd = "totalVolumeUsd" + totalVolumeEth = "totalVolumeEth" + + +class Total_orderBy(str, Enum): + id = "id" + tokens = "tokens" + orders = "orders" + traders = "traders" + numberOfTrades = "numberOfTrades" + settlements = "settlements" + volumeUsd = "volumeUsd" + volumeEth = "volumeEth" + feesUsd = "feesUsd" + feesEth = "feesEth" + + +class Trade_orderBy(str, Enum): + id = "id" + timestamp = "timestamp" + gasPrice = "gasPrice" + feeAmount = "feeAmount" + txHash = "txHash" + settlement = "settlement" + settlement__id = "settlement__id" + settlement__txHash = "settlement__txHash" + settlement__firstTradeTimestamp = "settlement__firstTradeTimestamp" + buyAmount = "buyAmount" + sellAmount = "sellAmount" + sellToken = "sellToken" + sellToken__id = "sellToken__id" + sellToken__address = "sellToken__address" + sellToken__firstTradeTimestamp = "sellToken__firstTradeTimestamp" + sellToken__name = "sellToken__name" + sellToken__symbol = "sellToken__symbol" + sellToken__decimals = "sellToken__decimals" + sellToken__totalVolume = "sellToken__totalVolume" + sellToken__priceEth = "sellToken__priceEth" + sellToken__priceUsd = "sellToken__priceUsd" + sellToken__numberOfTrades = "sellToken__numberOfTrades" + sellToken__totalVolumeUsd = "sellToken__totalVolumeUsd" + sellToken__totalVolumeEth = "sellToken__totalVolumeEth" + buyToken = "buyToken" + buyToken__id = "buyToken__id" + buyToken__address = "buyToken__address" + buyToken__firstTradeTimestamp = "buyToken__firstTradeTimestamp" + buyToken__name = "buyToken__name" + buyToken__symbol = "buyToken__symbol" + buyToken__decimals = "buyToken__decimals" + buyToken__totalVolume = "buyToken__totalVolume" + buyToken__priceEth = "buyToken__priceEth" + buyToken__priceUsd = "buyToken__priceUsd" + buyToken__numberOfTrades = "buyToken__numberOfTrades" + buyToken__totalVolumeUsd = "buyToken__totalVolumeUsd" + buyToken__totalVolumeEth = "buyToken__totalVolumeEth" + order = "order" + order__id = "order__id" + order__tradesTimestamp = "order__tradesTimestamp" + order__invalidateTimestamp = "order__invalidateTimestamp" + order__presignTimestamp = "order__presignTimestamp" + order__isSigned = "order__isSigned" + order__isValid = "order__isValid" + buyAmountEth = "buyAmountEth" + sellAmountEth = "sellAmountEth" + buyAmountUsd = "buyAmountUsd" + sellAmountUsd = "sellAmountUsd" + + +class UniswapPool_orderBy(str, Enum): + id = "id" + token0 = "token0" + token0__id = "token0__id" + token0__address = "token0__address" + token0__name = "token0__name" + token0__symbol = "token0__symbol" + token0__decimals = "token0__decimals" + token0__priceEth = "token0__priceEth" + token0__priceUsd = "token0__priceUsd" + token1 = "token1" + token1__id = "token1__id" + token1__address = "token1__address" + token1__name = "token1__name" + token1__symbol = "token1__symbol" + token1__decimals = "token1__decimals" + token1__priceEth = "token1__priceEth" + token1__priceUsd = "token1__priceUsd" + liquidity = "liquidity" + token0Price = "token0Price" + token1Price = "token1Price" + tick = "tick" + totalValueLockedToken0 = "totalValueLockedToken0" + totalValueLockedToken1 = "totalValueLockedToken1" + + +class UniswapToken_orderBy(str, Enum): + id = "id" + address = "address" + name = "name" + symbol = "symbol" + decimals = "decimals" + priceEth = "priceEth" + priceUsd = "priceUsd" + allowedPools = "allowedPools" + + +class User_orderBy(str, Enum): + id = "id" + address = "address" + firstTradeTimestamp = "firstTradeTimestamp" + ordersPlaced = "ordersPlaced" + isSolver = "isSolver" + numberOfTrades = "numberOfTrades" + solvedAmountEth = "solvedAmountEth" + solvedAmountUsd = "solvedAmountUsd" + tradedAmountUsd = "tradedAmountUsd" + tradedAmountEth = "tradedAmountEth" + + +class _SubgraphErrorPolicy_(str, Enum): + allow = "allow" + deny = "deny" diff --git a/cow_py/subgraph/client/exceptions.py b/cow_py/subgraph/client/exceptions.py new file mode 100644 index 0000000..9fbe116 --- /dev/null +++ b/cow_py/subgraph/client/exceptions.py @@ -0,0 +1,85 @@ +# Generated by ariadne-codegen + +from typing import Any, Dict, List, Optional, Union + +import httpx + + +class GraphQLClientError(Exception): + """Base exception.""" + + +class GraphQLClientHttpError(GraphQLClientError): + def __init__(self, status_code: int, response: httpx.Response) -> None: + self.status_code = status_code + self.response = response + + def __str__(self) -> str: + return f"HTTP status code: {self.status_code}" + + +class GraphQLClientInvalidResponseError(GraphQLClientError): + def __init__(self, response: httpx.Response) -> None: + self.response = response + + def __str__(self) -> str: + return "Invalid response format." + + +class GraphQLClientGraphQLError(GraphQLClientError): + def __init__( + self, + message: str, + locations: Optional[List[Dict[str, int]]] = None, + path: Optional[List[str]] = None, + extensions: Optional[Dict[str, object]] = None, + orginal: Optional[Dict[str, object]] = None, + ): + self.message = message + self.locations = locations + self.path = path + self.extensions = extensions + self.orginal = orginal + + def __str__(self) -> str: + return self.message + + @classmethod + def from_dict(cls, error: Dict[str, Any]) -> "GraphQLClientGraphQLError": + return cls( + message=error["message"], + locations=error.get("locations"), + path=error.get("path"), + extensions=error.get("extensions"), + orginal=error, + ) + + +class GraphQLClientGraphQLMultiError(GraphQLClientError): + def __init__( + self, + errors: List[GraphQLClientGraphQLError], + data: Optional[Dict[str, Any]] = None, + ): + self.errors = errors + self.data = data + + def __str__(self) -> str: + return "; ".join(str(e) for e in self.errors) + + @classmethod + def from_errors_dicts( + cls, errors_dicts: List[Dict[str, Any]], data: Optional[Dict[str, Any]] = None + ) -> "GraphQLClientGraphQLMultiError": + return cls( + errors=[GraphQLClientGraphQLError.from_dict(e) for e in errors_dicts], + data=data, + ) + + +class GraphQLClientInvalidMessageFormat(GraphQLClientError): + def __init__(self, message: Union[str, bytes]) -> None: + self.message = message + + def __str__(self) -> str: + return "Invalid message format." diff --git a/cow_py/subgraph/client/input_types.py b/cow_py/subgraph/client/input_types.py new file mode 100644 index 0000000..19b104b --- /dev/null +++ b/cow_py/subgraph/client/input_types.py @@ -0,0 +1,2567 @@ +# Generated by ariadne-codegen +# Source: https://api.thegraph.com/subgraphs/name/cowprotocol/cow + +from typing import Any, List, Optional + +from pydantic import Field + +from .base_model import BaseModel + + +class BlockChangedFilter(BaseModel): + number_gte: int + + +class Block_height(BaseModel): + hash: Optional[Any] = None + number: Optional[int] = None + number_gte: Optional[int] = None + + +class Bundle_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + eth_price_usd: Optional[Any] = Field(alias="ethPriceUSD", default=None) + eth_price_not: Optional[Any] = Field(alias="ethPriceUSD_not", default=None) + eth_price_gt: Optional[Any] = Field(alias="ethPriceUSD_gt", default=None) + eth_price_lt: Optional[Any] = Field(alias="ethPriceUSD_lt", default=None) + eth_price_gte: Optional[Any] = Field(alias="ethPriceUSD_gte", default=None) + eth_price_lte: Optional[Any] = Field(alias="ethPriceUSD_lte", default=None) + eth_price_in: Optional[List[Any]] = Field(alias="ethPriceUSD_in", default=None) + eth_price_not_in: Optional[List[Any]] = Field( + alias="ethPriceUSD_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["Bundle_filter"]]] = Field(alias="and", default=None) + or_: Optional[List[Optional["Bundle_filter"]]] = Field(alias="or", default=None) + + +class DailyTotal_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + total_tokens: Optional[Any] = Field(alias="totalTokens", default=None) + total_tokens_not: Optional[Any] = Field(alias="totalTokens_not", default=None) + total_tokens_gt: Optional[Any] = Field(alias="totalTokens_gt", default=None) + total_tokens_lt: Optional[Any] = Field(alias="totalTokens_lt", default=None) + total_tokens_gte: Optional[Any] = Field(alias="totalTokens_gte", default=None) + total_tokens_lte: Optional[Any] = Field(alias="totalTokens_lte", default=None) + total_tokens_in: Optional[List[Any]] = Field(alias="totalTokens_in", default=None) + total_tokens_not_in: Optional[List[Any]] = Field( + alias="totalTokens_not_in", default=None + ) + number_of_trades: Optional[Any] = Field(alias="numberOfTrades", default=None) + number_of_trades_not: Optional[Any] = Field( + alias="numberOfTrades_not", default=None + ) + number_of_trades_gt: Optional[Any] = Field(alias="numberOfTrades_gt", default=None) + number_of_trades_lt: Optional[Any] = Field(alias="numberOfTrades_lt", default=None) + number_of_trades_gte: Optional[Any] = Field( + alias="numberOfTrades_gte", default=None + ) + number_of_trades_lte: Optional[Any] = Field( + alias="numberOfTrades_lte", default=None + ) + number_of_trades_in: Optional[List[Any]] = Field( + alias="numberOfTrades_in", default=None + ) + number_of_trades_not_in: Optional[List[Any]] = Field( + alias="numberOfTrades_not_in", default=None + ) + orders: Optional[Any] = None + orders_not: Optional[Any] = None + orders_gt: Optional[Any] = None + orders_lt: Optional[Any] = None + orders_gte: Optional[Any] = None + orders_lte: Optional[Any] = None + orders_in: Optional[List[Any]] = None + orders_not_in: Optional[List[Any]] = None + settlements: Optional[Any] = None + settlements_not: Optional[Any] = None + settlements_gt: Optional[Any] = None + settlements_lt: Optional[Any] = None + settlements_gte: Optional[Any] = None + settlements_lte: Optional[Any] = None + settlements_in: Optional[List[Any]] = None + settlements_not_in: Optional[List[Any]] = None + volume_usd: Optional[Any] = Field(alias="volumeUsd", default=None) + volume_usd_not: Optional[Any] = Field(alias="volumeUsd_not", default=None) + volume_usd_gt: Optional[Any] = Field(alias="volumeUsd_gt", default=None) + volume_usd_lt: Optional[Any] = Field(alias="volumeUsd_lt", default=None) + volume_usd_gte: Optional[Any] = Field(alias="volumeUsd_gte", default=None) + volume_usd_lte: Optional[Any] = Field(alias="volumeUsd_lte", default=None) + volume_usd_in: Optional[List[Any]] = Field(alias="volumeUsd_in", default=None) + volume_usd_not_in: Optional[List[Any]] = Field( + alias="volumeUsd_not_in", default=None + ) + volume_eth: Optional[Any] = Field(alias="volumeEth", default=None) + volume_eth_not: Optional[Any] = Field(alias="volumeEth_not", default=None) + volume_eth_gt: Optional[Any] = Field(alias="volumeEth_gt", default=None) + volume_eth_lt: Optional[Any] = Field(alias="volumeEth_lt", default=None) + volume_eth_gte: Optional[Any] = Field(alias="volumeEth_gte", default=None) + volume_eth_lte: Optional[Any] = Field(alias="volumeEth_lte", default=None) + volume_eth_in: Optional[List[Any]] = Field(alias="volumeEth_in", default=None) + volume_eth_not_in: Optional[List[Any]] = Field( + alias="volumeEth_not_in", default=None + ) + fees_usd: Optional[Any] = Field(alias="feesUsd", default=None) + fees_usd_not: Optional[Any] = Field(alias="feesUsd_not", default=None) + fees_usd_gt: Optional[Any] = Field(alias="feesUsd_gt", default=None) + fees_usd_lt: Optional[Any] = Field(alias="feesUsd_lt", default=None) + fees_usd_gte: Optional[Any] = Field(alias="feesUsd_gte", default=None) + fees_usd_lte: Optional[Any] = Field(alias="feesUsd_lte", default=None) + fees_usd_in: Optional[List[Any]] = Field(alias="feesUsd_in", default=None) + fees_usd_not_in: Optional[List[Any]] = Field(alias="feesUsd_not_in", default=None) + fees_eth: Optional[Any] = Field(alias="feesEth", default=None) + fees_eth_not: Optional[Any] = Field(alias="feesEth_not", default=None) + fees_eth_gt: Optional[Any] = Field(alias="feesEth_gt", default=None) + fees_eth_lt: Optional[Any] = Field(alias="feesEth_lt", default=None) + fees_eth_gte: Optional[Any] = Field(alias="feesEth_gte", default=None) + fees_eth_lte: Optional[Any] = Field(alias="feesEth_lte", default=None) + fees_eth_in: Optional[List[Any]] = Field(alias="feesEth_in", default=None) + fees_eth_not_in: Optional[List[Any]] = Field(alias="feesEth_not_in", default=None) + tokens: Optional[List[str]] = None + tokens_not: Optional[List[str]] = None + tokens_contains: Optional[List[str]] = None + tokens_contains_nocase: Optional[List[str]] = None + tokens_not_contains: Optional[List[str]] = None + tokens_not_contains_nocase: Optional[List[str]] = None + tokens: Optional["Token_filter"] = Field(alias="tokens_", default=None) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["DailyTotal_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["DailyTotal_filter"]]] = Field(alias="or", default=None) + + +class HourlyTotal_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + total_tokens: Optional[Any] = Field(alias="totalTokens", default=None) + total_tokens_not: Optional[Any] = Field(alias="totalTokens_not", default=None) + total_tokens_gt: Optional[Any] = Field(alias="totalTokens_gt", default=None) + total_tokens_lt: Optional[Any] = Field(alias="totalTokens_lt", default=None) + total_tokens_gte: Optional[Any] = Field(alias="totalTokens_gte", default=None) + total_tokens_lte: Optional[Any] = Field(alias="totalTokens_lte", default=None) + total_tokens_in: Optional[List[Any]] = Field(alias="totalTokens_in", default=None) + total_tokens_not_in: Optional[List[Any]] = Field( + alias="totalTokens_not_in", default=None + ) + number_of_trades: Optional[Any] = Field(alias="numberOfTrades", default=None) + number_of_trades_not: Optional[Any] = Field( + alias="numberOfTrades_not", default=None + ) + number_of_trades_gt: Optional[Any] = Field(alias="numberOfTrades_gt", default=None) + number_of_trades_lt: Optional[Any] = Field(alias="numberOfTrades_lt", default=None) + number_of_trades_gte: Optional[Any] = Field( + alias="numberOfTrades_gte", default=None + ) + number_of_trades_lte: Optional[Any] = Field( + alias="numberOfTrades_lte", default=None + ) + number_of_trades_in: Optional[List[Any]] = Field( + alias="numberOfTrades_in", default=None + ) + number_of_trades_not_in: Optional[List[Any]] = Field( + alias="numberOfTrades_not_in", default=None + ) + orders: Optional[Any] = None + orders_not: Optional[Any] = None + orders_gt: Optional[Any] = None + orders_lt: Optional[Any] = None + orders_gte: Optional[Any] = None + orders_lte: Optional[Any] = None + orders_in: Optional[List[Any]] = None + orders_not_in: Optional[List[Any]] = None + settlements: Optional[Any] = None + settlements_not: Optional[Any] = None + settlements_gt: Optional[Any] = None + settlements_lt: Optional[Any] = None + settlements_gte: Optional[Any] = None + settlements_lte: Optional[Any] = None + settlements_in: Optional[List[Any]] = None + settlements_not_in: Optional[List[Any]] = None + volume_usd: Optional[Any] = Field(alias="volumeUsd", default=None) + volume_usd_not: Optional[Any] = Field(alias="volumeUsd_not", default=None) + volume_usd_gt: Optional[Any] = Field(alias="volumeUsd_gt", default=None) + volume_usd_lt: Optional[Any] = Field(alias="volumeUsd_lt", default=None) + volume_usd_gte: Optional[Any] = Field(alias="volumeUsd_gte", default=None) + volume_usd_lte: Optional[Any] = Field(alias="volumeUsd_lte", default=None) + volume_usd_in: Optional[List[Any]] = Field(alias="volumeUsd_in", default=None) + volume_usd_not_in: Optional[List[Any]] = Field( + alias="volumeUsd_not_in", default=None + ) + volume_eth: Optional[Any] = Field(alias="volumeEth", default=None) + volume_eth_not: Optional[Any] = Field(alias="volumeEth_not", default=None) + volume_eth_gt: Optional[Any] = Field(alias="volumeEth_gt", default=None) + volume_eth_lt: Optional[Any] = Field(alias="volumeEth_lt", default=None) + volume_eth_gte: Optional[Any] = Field(alias="volumeEth_gte", default=None) + volume_eth_lte: Optional[Any] = Field(alias="volumeEth_lte", default=None) + volume_eth_in: Optional[List[Any]] = Field(alias="volumeEth_in", default=None) + volume_eth_not_in: Optional[List[Any]] = Field( + alias="volumeEth_not_in", default=None + ) + fees_usd: Optional[Any] = Field(alias="feesUsd", default=None) + fees_usd_not: Optional[Any] = Field(alias="feesUsd_not", default=None) + fees_usd_gt: Optional[Any] = Field(alias="feesUsd_gt", default=None) + fees_usd_lt: Optional[Any] = Field(alias="feesUsd_lt", default=None) + fees_usd_gte: Optional[Any] = Field(alias="feesUsd_gte", default=None) + fees_usd_lte: Optional[Any] = Field(alias="feesUsd_lte", default=None) + fees_usd_in: Optional[List[Any]] = Field(alias="feesUsd_in", default=None) + fees_usd_not_in: Optional[List[Any]] = Field(alias="feesUsd_not_in", default=None) + fees_eth: Optional[Any] = Field(alias="feesEth", default=None) + fees_eth_not: Optional[Any] = Field(alias="feesEth_not", default=None) + fees_eth_gt: Optional[Any] = Field(alias="feesEth_gt", default=None) + fees_eth_lt: Optional[Any] = Field(alias="feesEth_lt", default=None) + fees_eth_gte: Optional[Any] = Field(alias="feesEth_gte", default=None) + fees_eth_lte: Optional[Any] = Field(alias="feesEth_lte", default=None) + fees_eth_in: Optional[List[Any]] = Field(alias="feesEth_in", default=None) + fees_eth_not_in: Optional[List[Any]] = Field(alias="feesEth_not_in", default=None) + tokens: Optional[List[str]] = None + tokens_not: Optional[List[str]] = None + tokens_contains: Optional[List[str]] = None + tokens_contains_nocase: Optional[List[str]] = None + tokens_not_contains: Optional[List[str]] = None + tokens_not_contains_nocase: Optional[List[str]] = None + tokens: Optional["Token_filter"] = Field(alias="tokens_", default=None) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["HourlyTotal_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["HourlyTotal_filter"]]] = Field( + alias="or", default=None + ) + + +class Order_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + owner: Optional[str] = None + owner_not: Optional[str] = None + owner_gt: Optional[str] = None + owner_lt: Optional[str] = None + owner_gte: Optional[str] = None + owner_lte: Optional[str] = None + owner_in: Optional[List[str]] = None + owner_not_in: Optional[List[str]] = None + owner_contains: Optional[str] = None + owner_contains_nocase: Optional[str] = None + owner_not_contains: Optional[str] = None + owner_not_contains_nocase: Optional[str] = None + owner_starts_with: Optional[str] = None + owner_starts_with_nocase: Optional[str] = None + owner_not_starts_with: Optional[str] = None + owner_not_starts_with_nocase: Optional[str] = None + owner_ends_with: Optional[str] = None + owner_ends_with_nocase: Optional[str] = None + owner_not_ends_with: Optional[str] = None + owner_not_ends_with_nocase: Optional[str] = None + owner: Optional["User_filter"] = Field(alias="owner_", default=None) + trades_timestamp: Optional[int] = Field(alias="tradesTimestamp", default=None) + trades_timestamp_not: Optional[int] = Field( + alias="tradesTimestamp_not", default=None + ) + trades_timestamp_gt: Optional[int] = Field(alias="tradesTimestamp_gt", default=None) + trades_timestamp_lt: Optional[int] = Field(alias="tradesTimestamp_lt", default=None) + trades_timestamp_gte: Optional[int] = Field( + alias="tradesTimestamp_gte", default=None + ) + trades_timestamp_lte: Optional[int] = Field( + alias="tradesTimestamp_lte", default=None + ) + trades_timestamp_in: Optional[List[int]] = Field( + alias="tradesTimestamp_in", default=None + ) + trades_timestamp_not_in: Optional[List[int]] = Field( + alias="tradesTimestamp_not_in", default=None + ) + invalidate_timestamp: Optional[int] = Field( + alias="invalidateTimestamp", default=None + ) + invalidate_timestamp_not: Optional[int] = Field( + alias="invalidateTimestamp_not", default=None + ) + invalidate_timestamp_gt: Optional[int] = Field( + alias="invalidateTimestamp_gt", default=None + ) + invalidate_timestamp_lt: Optional[int] = Field( + alias="invalidateTimestamp_lt", default=None + ) + invalidate_timestamp_gte: Optional[int] = Field( + alias="invalidateTimestamp_gte", default=None + ) + invalidate_timestamp_lte: Optional[int] = Field( + alias="invalidateTimestamp_lte", default=None + ) + invalidate_timestamp_in: Optional[List[int]] = Field( + alias="invalidateTimestamp_in", default=None + ) + invalidate_timestamp_not_in: Optional[List[int]] = Field( + alias="invalidateTimestamp_not_in", default=None + ) + presign_timestamp: Optional[int] = Field(alias="presignTimestamp", default=None) + presign_timestamp_not: Optional[int] = Field( + alias="presignTimestamp_not", default=None + ) + presign_timestamp_gt: Optional[int] = Field( + alias="presignTimestamp_gt", default=None + ) + presign_timestamp_lt: Optional[int] = Field( + alias="presignTimestamp_lt", default=None + ) + presign_timestamp_gte: Optional[int] = Field( + alias="presignTimestamp_gte", default=None + ) + presign_timestamp_lte: Optional[int] = Field( + alias="presignTimestamp_lte", default=None + ) + presign_timestamp_in: Optional[List[int]] = Field( + alias="presignTimestamp_in", default=None + ) + presign_timestamp_not_in: Optional[List[int]] = Field( + alias="presignTimestamp_not_in", default=None + ) + trades: Optional["Trade_filter"] = Field(alias="trades_", default=None) + is_signed: Optional[bool] = Field(alias="isSigned", default=None) + is_signed_not: Optional[bool] = Field(alias="isSigned_not", default=None) + is_signed_in: Optional[List[bool]] = Field(alias="isSigned_in", default=None) + is_signed_not_in: Optional[List[bool]] = Field( + alias="isSigned_not_in", default=None + ) + is_valid: Optional[bool] = Field(alias="isValid", default=None) + is_valid_not: Optional[bool] = Field(alias="isValid_not", default=None) + is_valid_in: Optional[List[bool]] = Field(alias="isValid_in", default=None) + is_valid_not_in: Optional[List[bool]] = Field(alias="isValid_not_in", default=None) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["Order_filter"]]] = Field(alias="and", default=None) + or_: Optional[List[Optional["Order_filter"]]] = Field(alias="or", default=None) + + +class PairDaily_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + token_0: Optional[str] = Field(alias="token0", default=None) + token_0_not: Optional[str] = Field(alias="token0_not", default=None) + token_0_gt: Optional[str] = Field(alias="token0_gt", default=None) + token_0_lt: Optional[str] = Field(alias="token0_lt", default=None) + token_0_gte: Optional[str] = Field(alias="token0_gte", default=None) + token_0_lte: Optional[str] = Field(alias="token0_lte", default=None) + token_0_in: Optional[List[str]] = Field(alias="token0_in", default=None) + token_0_not_in: Optional[List[str]] = Field(alias="token0_not_in", default=None) + token_0_contains: Optional[str] = Field(alias="token0_contains", default=None) + token_0_contains_nocase: Optional[str] = Field( + alias="token0_contains_nocase", default=None + ) + token_0_not_contains: Optional[str] = Field( + alias="token0_not_contains", default=None + ) + token_0_not_contains_nocase: Optional[str] = Field( + alias="token0_not_contains_nocase", default=None + ) + token_0_starts_with: Optional[str] = Field(alias="token0_starts_with", default=None) + token_0_starts_with_nocase: Optional[str] = Field( + alias="token0_starts_with_nocase", default=None + ) + token_0_not_starts_with: Optional[str] = Field( + alias="token0_not_starts_with", default=None + ) + token_0_not_starts_with_nocase: Optional[str] = Field( + alias="token0_not_starts_with_nocase", default=None + ) + token_0_ends_with: Optional[str] = Field(alias="token0_ends_with", default=None) + token_0_ends_with_nocase: Optional[str] = Field( + alias="token0_ends_with_nocase", default=None + ) + token_0_not_ends_with: Optional[str] = Field( + alias="token0_not_ends_with", default=None + ) + token_0_not_ends_with_nocase: Optional[str] = Field( + alias="token0_not_ends_with_nocase", default=None + ) + token_0: Optional["Token_filter"] = Field(alias="token0_", default=None) + token_1: Optional[str] = Field(alias="token1", default=None) + token_1_not: Optional[str] = Field(alias="token1_not", default=None) + token_1_gt: Optional[str] = Field(alias="token1_gt", default=None) + token_1_lt: Optional[str] = Field(alias="token1_lt", default=None) + token_1_gte: Optional[str] = Field(alias="token1_gte", default=None) + token_1_lte: Optional[str] = Field(alias="token1_lte", default=None) + token_1_in: Optional[List[str]] = Field(alias="token1_in", default=None) + token_1_not_in: Optional[List[str]] = Field(alias="token1_not_in", default=None) + token_1_contains: Optional[str] = Field(alias="token1_contains", default=None) + token_1_contains_nocase: Optional[str] = Field( + alias="token1_contains_nocase", default=None + ) + token_1_not_contains: Optional[str] = Field( + alias="token1_not_contains", default=None + ) + token_1_not_contains_nocase: Optional[str] = Field( + alias="token1_not_contains_nocase", default=None + ) + token_1_starts_with: Optional[str] = Field(alias="token1_starts_with", default=None) + token_1_starts_with_nocase: Optional[str] = Field( + alias="token1_starts_with_nocase", default=None + ) + token_1_not_starts_with: Optional[str] = Field( + alias="token1_not_starts_with", default=None + ) + token_1_not_starts_with_nocase: Optional[str] = Field( + alias="token1_not_starts_with_nocase", default=None + ) + token_1_ends_with: Optional[str] = Field(alias="token1_ends_with", default=None) + token_1_ends_with_nocase: Optional[str] = Field( + alias="token1_ends_with_nocase", default=None + ) + token_1_not_ends_with: Optional[str] = Field( + alias="token1_not_ends_with", default=None + ) + token_1_not_ends_with_nocase: Optional[str] = Field( + alias="token1_not_ends_with_nocase", default=None + ) + token_1: Optional["Token_filter"] = Field(alias="token1_", default=None) + token_0_price: Optional[Any] = Field(alias="token0Price", default=None) + token_0_price_not: Optional[Any] = Field(alias="token0Price_not", default=None) + token_0_price_gt: Optional[Any] = Field(alias="token0Price_gt", default=None) + token_0_price_lt: Optional[Any] = Field(alias="token0Price_lt", default=None) + token_0_price_gte: Optional[Any] = Field(alias="token0Price_gte", default=None) + token_0_price_lte: Optional[Any] = Field(alias="token0Price_lte", default=None) + token_0_price_in: Optional[List[Any]] = Field(alias="token0Price_in", default=None) + token_0_price_not_in: Optional[List[Any]] = Field( + alias="token0Price_not_in", default=None + ) + token_1_price: Optional[Any] = Field(alias="token1Price", default=None) + token_1_price_not: Optional[Any] = Field(alias="token1Price_not", default=None) + token_1_price_gt: Optional[Any] = Field(alias="token1Price_gt", default=None) + token_1_price_lt: Optional[Any] = Field(alias="token1Price_lt", default=None) + token_1_price_gte: Optional[Any] = Field(alias="token1Price_gte", default=None) + token_1_price_lte: Optional[Any] = Field(alias="token1Price_lte", default=None) + token_1_price_in: Optional[List[Any]] = Field(alias="token1Price_in", default=None) + token_1_price_not_in: Optional[List[Any]] = Field( + alias="token1Price_not_in", default=None + ) + token_0_relative_price: Optional[Any] = Field( + alias="token0relativePrice", default=None + ) + token_0_relative_price_not: Optional[Any] = Field( + alias="token0relativePrice_not", default=None + ) + token_0_relative_price_gt: Optional[Any] = Field( + alias="token0relativePrice_gt", default=None + ) + token_0_relative_price_lt: Optional[Any] = Field( + alias="token0relativePrice_lt", default=None + ) + token_0_relative_price_gte: Optional[Any] = Field( + alias="token0relativePrice_gte", default=None + ) + token_0_relative_price_lte: Optional[Any] = Field( + alias="token0relativePrice_lte", default=None + ) + token_0_relative_price_in: Optional[List[Any]] = Field( + alias="token0relativePrice_in", default=None + ) + token_0_relative_price_not_in: Optional[List[Any]] = Field( + alias="token0relativePrice_not_in", default=None + ) + token_1_relative_price: Optional[Any] = Field( + alias="token1relativePrice", default=None + ) + token_1_relative_price_not: Optional[Any] = Field( + alias="token1relativePrice_not", default=None + ) + token_1_relative_price_gt: Optional[Any] = Field( + alias="token1relativePrice_gt", default=None + ) + token_1_relative_price_lt: Optional[Any] = Field( + alias="token1relativePrice_lt", default=None + ) + token_1_relative_price_gte: Optional[Any] = Field( + alias="token1relativePrice_gte", default=None + ) + token_1_relative_price_lte: Optional[Any] = Field( + alias="token1relativePrice_lte", default=None + ) + token_1_relative_price_in: Optional[List[Any]] = Field( + alias="token1relativePrice_in", default=None + ) + token_1_relative_price_not_in: Optional[List[Any]] = Field( + alias="token1relativePrice_not_in", default=None + ) + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + volume_token_0: Optional[Any] = Field(alias="volumeToken0", default=None) + volume_token_0_not: Optional[Any] = Field(alias="volumeToken0_not", default=None) + volume_token_0_gt: Optional[Any] = Field(alias="volumeToken0_gt", default=None) + volume_token_0_lt: Optional[Any] = Field(alias="volumeToken0_lt", default=None) + volume_token_0_gte: Optional[Any] = Field(alias="volumeToken0_gte", default=None) + volume_token_0_lte: Optional[Any] = Field(alias="volumeToken0_lte", default=None) + volume_token_0_in: Optional[List[Any]] = Field( + alias="volumeToken0_in", default=None + ) + volume_token_0_not_in: Optional[List[Any]] = Field( + alias="volumeToken0_not_in", default=None + ) + volume_token_1: Optional[Any] = Field(alias="volumeToken1", default=None) + volume_token_1_not: Optional[Any] = Field(alias="volumeToken1_not", default=None) + volume_token_1_gt: Optional[Any] = Field(alias="volumeToken1_gt", default=None) + volume_token_1_lt: Optional[Any] = Field(alias="volumeToken1_lt", default=None) + volume_token_1_gte: Optional[Any] = Field(alias="volumeToken1_gte", default=None) + volume_token_1_lte: Optional[Any] = Field(alias="volumeToken1_lte", default=None) + volume_token_1_in: Optional[List[Any]] = Field( + alias="volumeToken1_in", default=None + ) + volume_token_1_not_in: Optional[List[Any]] = Field( + alias="volumeToken1_not_in", default=None + ) + volume_traded_eth: Optional[Any] = Field(alias="volumeTradedEth", default=None) + volume_traded_eth_not: Optional[Any] = Field( + alias="volumeTradedEth_not", default=None + ) + volume_traded_eth_gt: Optional[Any] = Field( + alias="volumeTradedEth_gt", default=None + ) + volume_traded_eth_lt: Optional[Any] = Field( + alias="volumeTradedEth_lt", default=None + ) + volume_traded_eth_gte: Optional[Any] = Field( + alias="volumeTradedEth_gte", default=None + ) + volume_traded_eth_lte: Optional[Any] = Field( + alias="volumeTradedEth_lte", default=None + ) + volume_traded_eth_in: Optional[List[Any]] = Field( + alias="volumeTradedEth_in", default=None + ) + volume_traded_eth_not_in: Optional[List[Any]] = Field( + alias="volumeTradedEth_not_in", default=None + ) + volume_traded_usd: Optional[Any] = Field(alias="volumeTradedUsd", default=None) + volume_traded_usd_not: Optional[Any] = Field( + alias="volumeTradedUsd_not", default=None + ) + volume_traded_usd_gt: Optional[Any] = Field( + alias="volumeTradedUsd_gt", default=None + ) + volume_traded_usd_lt: Optional[Any] = Field( + alias="volumeTradedUsd_lt", default=None + ) + volume_traded_usd_gte: Optional[Any] = Field( + alias="volumeTradedUsd_gte", default=None + ) + volume_traded_usd_lte: Optional[Any] = Field( + alias="volumeTradedUsd_lte", default=None + ) + volume_traded_usd_in: Optional[List[Any]] = Field( + alias="volumeTradedUsd_in", default=None + ) + volume_traded_usd_not_in: Optional[List[Any]] = Field( + alias="volumeTradedUsd_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["PairDaily_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["PairDaily_filter"]]] = Field(alias="or", default=None) + + +class PairHourly_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + token_0: Optional[str] = Field(alias="token0", default=None) + token_0_not: Optional[str] = Field(alias="token0_not", default=None) + token_0_gt: Optional[str] = Field(alias="token0_gt", default=None) + token_0_lt: Optional[str] = Field(alias="token0_lt", default=None) + token_0_gte: Optional[str] = Field(alias="token0_gte", default=None) + token_0_lte: Optional[str] = Field(alias="token0_lte", default=None) + token_0_in: Optional[List[str]] = Field(alias="token0_in", default=None) + token_0_not_in: Optional[List[str]] = Field(alias="token0_not_in", default=None) + token_0_contains: Optional[str] = Field(alias="token0_contains", default=None) + token_0_contains_nocase: Optional[str] = Field( + alias="token0_contains_nocase", default=None + ) + token_0_not_contains: Optional[str] = Field( + alias="token0_not_contains", default=None + ) + token_0_not_contains_nocase: Optional[str] = Field( + alias="token0_not_contains_nocase", default=None + ) + token_0_starts_with: Optional[str] = Field(alias="token0_starts_with", default=None) + token_0_starts_with_nocase: Optional[str] = Field( + alias="token0_starts_with_nocase", default=None + ) + token_0_not_starts_with: Optional[str] = Field( + alias="token0_not_starts_with", default=None + ) + token_0_not_starts_with_nocase: Optional[str] = Field( + alias="token0_not_starts_with_nocase", default=None + ) + token_0_ends_with: Optional[str] = Field(alias="token0_ends_with", default=None) + token_0_ends_with_nocase: Optional[str] = Field( + alias="token0_ends_with_nocase", default=None + ) + token_0_not_ends_with: Optional[str] = Field( + alias="token0_not_ends_with", default=None + ) + token_0_not_ends_with_nocase: Optional[str] = Field( + alias="token0_not_ends_with_nocase", default=None + ) + token_0: Optional["Token_filter"] = Field(alias="token0_", default=None) + token_1: Optional[str] = Field(alias="token1", default=None) + token_1_not: Optional[str] = Field(alias="token1_not", default=None) + token_1_gt: Optional[str] = Field(alias="token1_gt", default=None) + token_1_lt: Optional[str] = Field(alias="token1_lt", default=None) + token_1_gte: Optional[str] = Field(alias="token1_gte", default=None) + token_1_lte: Optional[str] = Field(alias="token1_lte", default=None) + token_1_in: Optional[List[str]] = Field(alias="token1_in", default=None) + token_1_not_in: Optional[List[str]] = Field(alias="token1_not_in", default=None) + token_1_contains: Optional[str] = Field(alias="token1_contains", default=None) + token_1_contains_nocase: Optional[str] = Field( + alias="token1_contains_nocase", default=None + ) + token_1_not_contains: Optional[str] = Field( + alias="token1_not_contains", default=None + ) + token_1_not_contains_nocase: Optional[str] = Field( + alias="token1_not_contains_nocase", default=None + ) + token_1_starts_with: Optional[str] = Field(alias="token1_starts_with", default=None) + token_1_starts_with_nocase: Optional[str] = Field( + alias="token1_starts_with_nocase", default=None + ) + token_1_not_starts_with: Optional[str] = Field( + alias="token1_not_starts_with", default=None + ) + token_1_not_starts_with_nocase: Optional[str] = Field( + alias="token1_not_starts_with_nocase", default=None + ) + token_1_ends_with: Optional[str] = Field(alias="token1_ends_with", default=None) + token_1_ends_with_nocase: Optional[str] = Field( + alias="token1_ends_with_nocase", default=None + ) + token_1_not_ends_with: Optional[str] = Field( + alias="token1_not_ends_with", default=None + ) + token_1_not_ends_with_nocase: Optional[str] = Field( + alias="token1_not_ends_with_nocase", default=None + ) + token_1: Optional["Token_filter"] = Field(alias="token1_", default=None) + token_0_price: Optional[Any] = Field(alias="token0Price", default=None) + token_0_price_not: Optional[Any] = Field(alias="token0Price_not", default=None) + token_0_price_gt: Optional[Any] = Field(alias="token0Price_gt", default=None) + token_0_price_lt: Optional[Any] = Field(alias="token0Price_lt", default=None) + token_0_price_gte: Optional[Any] = Field(alias="token0Price_gte", default=None) + token_0_price_lte: Optional[Any] = Field(alias="token0Price_lte", default=None) + token_0_price_in: Optional[List[Any]] = Field(alias="token0Price_in", default=None) + token_0_price_not_in: Optional[List[Any]] = Field( + alias="token0Price_not_in", default=None + ) + token_1_price: Optional[Any] = Field(alias="token1Price", default=None) + token_1_price_not: Optional[Any] = Field(alias="token1Price_not", default=None) + token_1_price_gt: Optional[Any] = Field(alias="token1Price_gt", default=None) + token_1_price_lt: Optional[Any] = Field(alias="token1Price_lt", default=None) + token_1_price_gte: Optional[Any] = Field(alias="token1Price_gte", default=None) + token_1_price_lte: Optional[Any] = Field(alias="token1Price_lte", default=None) + token_1_price_in: Optional[List[Any]] = Field(alias="token1Price_in", default=None) + token_1_price_not_in: Optional[List[Any]] = Field( + alias="token1Price_not_in", default=None + ) + token_0_relative_price: Optional[Any] = Field( + alias="token0relativePrice", default=None + ) + token_0_relative_price_not: Optional[Any] = Field( + alias="token0relativePrice_not", default=None + ) + token_0_relative_price_gt: Optional[Any] = Field( + alias="token0relativePrice_gt", default=None + ) + token_0_relative_price_lt: Optional[Any] = Field( + alias="token0relativePrice_lt", default=None + ) + token_0_relative_price_gte: Optional[Any] = Field( + alias="token0relativePrice_gte", default=None + ) + token_0_relative_price_lte: Optional[Any] = Field( + alias="token0relativePrice_lte", default=None + ) + token_0_relative_price_in: Optional[List[Any]] = Field( + alias="token0relativePrice_in", default=None + ) + token_0_relative_price_not_in: Optional[List[Any]] = Field( + alias="token0relativePrice_not_in", default=None + ) + token_1_relative_price: Optional[Any] = Field( + alias="token1relativePrice", default=None + ) + token_1_relative_price_not: Optional[Any] = Field( + alias="token1relativePrice_not", default=None + ) + token_1_relative_price_gt: Optional[Any] = Field( + alias="token1relativePrice_gt", default=None + ) + token_1_relative_price_lt: Optional[Any] = Field( + alias="token1relativePrice_lt", default=None + ) + token_1_relative_price_gte: Optional[Any] = Field( + alias="token1relativePrice_gte", default=None + ) + token_1_relative_price_lte: Optional[Any] = Field( + alias="token1relativePrice_lte", default=None + ) + token_1_relative_price_in: Optional[List[Any]] = Field( + alias="token1relativePrice_in", default=None + ) + token_1_relative_price_not_in: Optional[List[Any]] = Field( + alias="token1relativePrice_not_in", default=None + ) + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + volume_token_0: Optional[Any] = Field(alias="volumeToken0", default=None) + volume_token_0_not: Optional[Any] = Field(alias="volumeToken0_not", default=None) + volume_token_0_gt: Optional[Any] = Field(alias="volumeToken0_gt", default=None) + volume_token_0_lt: Optional[Any] = Field(alias="volumeToken0_lt", default=None) + volume_token_0_gte: Optional[Any] = Field(alias="volumeToken0_gte", default=None) + volume_token_0_lte: Optional[Any] = Field(alias="volumeToken0_lte", default=None) + volume_token_0_in: Optional[List[Any]] = Field( + alias="volumeToken0_in", default=None + ) + volume_token_0_not_in: Optional[List[Any]] = Field( + alias="volumeToken0_not_in", default=None + ) + volume_token_1: Optional[Any] = Field(alias="volumeToken1", default=None) + volume_token_1_not: Optional[Any] = Field(alias="volumeToken1_not", default=None) + volume_token_1_gt: Optional[Any] = Field(alias="volumeToken1_gt", default=None) + volume_token_1_lt: Optional[Any] = Field(alias="volumeToken1_lt", default=None) + volume_token_1_gte: Optional[Any] = Field(alias="volumeToken1_gte", default=None) + volume_token_1_lte: Optional[Any] = Field(alias="volumeToken1_lte", default=None) + volume_token_1_in: Optional[List[Any]] = Field( + alias="volumeToken1_in", default=None + ) + volume_token_1_not_in: Optional[List[Any]] = Field( + alias="volumeToken1_not_in", default=None + ) + volume_traded_eth: Optional[Any] = Field(alias="volumeTradedEth", default=None) + volume_traded_eth_not: Optional[Any] = Field( + alias="volumeTradedEth_not", default=None + ) + volume_traded_eth_gt: Optional[Any] = Field( + alias="volumeTradedEth_gt", default=None + ) + volume_traded_eth_lt: Optional[Any] = Field( + alias="volumeTradedEth_lt", default=None + ) + volume_traded_eth_gte: Optional[Any] = Field( + alias="volumeTradedEth_gte", default=None + ) + volume_traded_eth_lte: Optional[Any] = Field( + alias="volumeTradedEth_lte", default=None + ) + volume_traded_eth_in: Optional[List[Any]] = Field( + alias="volumeTradedEth_in", default=None + ) + volume_traded_eth_not_in: Optional[List[Any]] = Field( + alias="volumeTradedEth_not_in", default=None + ) + volume_traded_usd: Optional[Any] = Field(alias="volumeTradedUsd", default=None) + volume_traded_usd_not: Optional[Any] = Field( + alias="volumeTradedUsd_not", default=None + ) + volume_traded_usd_gt: Optional[Any] = Field( + alias="volumeTradedUsd_gt", default=None + ) + volume_traded_usd_lt: Optional[Any] = Field( + alias="volumeTradedUsd_lt", default=None + ) + volume_traded_usd_gte: Optional[Any] = Field( + alias="volumeTradedUsd_gte", default=None + ) + volume_traded_usd_lte: Optional[Any] = Field( + alias="volumeTradedUsd_lte", default=None + ) + volume_traded_usd_in: Optional[List[Any]] = Field( + alias="volumeTradedUsd_in", default=None + ) + volume_traded_usd_not_in: Optional[List[Any]] = Field( + alias="volumeTradedUsd_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["PairHourly_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["PairHourly_filter"]]] = Field(alias="or", default=None) + + +class Pair_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + token_0: Optional[str] = Field(alias="token0", default=None) + token_0_not: Optional[str] = Field(alias="token0_not", default=None) + token_0_gt: Optional[str] = Field(alias="token0_gt", default=None) + token_0_lt: Optional[str] = Field(alias="token0_lt", default=None) + token_0_gte: Optional[str] = Field(alias="token0_gte", default=None) + token_0_lte: Optional[str] = Field(alias="token0_lte", default=None) + token_0_in: Optional[List[str]] = Field(alias="token0_in", default=None) + token_0_not_in: Optional[List[str]] = Field(alias="token0_not_in", default=None) + token_0_contains: Optional[str] = Field(alias="token0_contains", default=None) + token_0_contains_nocase: Optional[str] = Field( + alias="token0_contains_nocase", default=None + ) + token_0_not_contains: Optional[str] = Field( + alias="token0_not_contains", default=None + ) + token_0_not_contains_nocase: Optional[str] = Field( + alias="token0_not_contains_nocase", default=None + ) + token_0_starts_with: Optional[str] = Field(alias="token0_starts_with", default=None) + token_0_starts_with_nocase: Optional[str] = Field( + alias="token0_starts_with_nocase", default=None + ) + token_0_not_starts_with: Optional[str] = Field( + alias="token0_not_starts_with", default=None + ) + token_0_not_starts_with_nocase: Optional[str] = Field( + alias="token0_not_starts_with_nocase", default=None + ) + token_0_ends_with: Optional[str] = Field(alias="token0_ends_with", default=None) + token_0_ends_with_nocase: Optional[str] = Field( + alias="token0_ends_with_nocase", default=None + ) + token_0_not_ends_with: Optional[str] = Field( + alias="token0_not_ends_with", default=None + ) + token_0_not_ends_with_nocase: Optional[str] = Field( + alias="token0_not_ends_with_nocase", default=None + ) + token_0: Optional["Token_filter"] = Field(alias="token0_", default=None) + token_1: Optional[str] = Field(alias="token1", default=None) + token_1_not: Optional[str] = Field(alias="token1_not", default=None) + token_1_gt: Optional[str] = Field(alias="token1_gt", default=None) + token_1_lt: Optional[str] = Field(alias="token1_lt", default=None) + token_1_gte: Optional[str] = Field(alias="token1_gte", default=None) + token_1_lte: Optional[str] = Field(alias="token1_lte", default=None) + token_1_in: Optional[List[str]] = Field(alias="token1_in", default=None) + token_1_not_in: Optional[List[str]] = Field(alias="token1_not_in", default=None) + token_1_contains: Optional[str] = Field(alias="token1_contains", default=None) + token_1_contains_nocase: Optional[str] = Field( + alias="token1_contains_nocase", default=None + ) + token_1_not_contains: Optional[str] = Field( + alias="token1_not_contains", default=None + ) + token_1_not_contains_nocase: Optional[str] = Field( + alias="token1_not_contains_nocase", default=None + ) + token_1_starts_with: Optional[str] = Field(alias="token1_starts_with", default=None) + token_1_starts_with_nocase: Optional[str] = Field( + alias="token1_starts_with_nocase", default=None + ) + token_1_not_starts_with: Optional[str] = Field( + alias="token1_not_starts_with", default=None + ) + token_1_not_starts_with_nocase: Optional[str] = Field( + alias="token1_not_starts_with_nocase", default=None + ) + token_1_ends_with: Optional[str] = Field(alias="token1_ends_with", default=None) + token_1_ends_with_nocase: Optional[str] = Field( + alias="token1_ends_with_nocase", default=None + ) + token_1_not_ends_with: Optional[str] = Field( + alias="token1_not_ends_with", default=None + ) + token_1_not_ends_with_nocase: Optional[str] = Field( + alias="token1_not_ends_with_nocase", default=None + ) + token_1: Optional["Token_filter"] = Field(alias="token1_", default=None) + token_0_price: Optional[Any] = Field(alias="token0Price", default=None) + token_0_price_not: Optional[Any] = Field(alias="token0Price_not", default=None) + token_0_price_gt: Optional[Any] = Field(alias="token0Price_gt", default=None) + token_0_price_lt: Optional[Any] = Field(alias="token0Price_lt", default=None) + token_0_price_gte: Optional[Any] = Field(alias="token0Price_gte", default=None) + token_0_price_lte: Optional[Any] = Field(alias="token0Price_lte", default=None) + token_0_price_in: Optional[List[Any]] = Field(alias="token0Price_in", default=None) + token_0_price_not_in: Optional[List[Any]] = Field( + alias="token0Price_not_in", default=None + ) + token_1_price: Optional[Any] = Field(alias="token1Price", default=None) + token_1_price_not: Optional[Any] = Field(alias="token1Price_not", default=None) + token_1_price_gt: Optional[Any] = Field(alias="token1Price_gt", default=None) + token_1_price_lt: Optional[Any] = Field(alias="token1Price_lt", default=None) + token_1_price_gte: Optional[Any] = Field(alias="token1Price_gte", default=None) + token_1_price_lte: Optional[Any] = Field(alias="token1Price_lte", default=None) + token_1_price_in: Optional[List[Any]] = Field(alias="token1Price_in", default=None) + token_1_price_not_in: Optional[List[Any]] = Field( + alias="token1Price_not_in", default=None + ) + token_0_relative_price: Optional[Any] = Field( + alias="token0relativePrice", default=None + ) + token_0_relative_price_not: Optional[Any] = Field( + alias="token0relativePrice_not", default=None + ) + token_0_relative_price_gt: Optional[Any] = Field( + alias="token0relativePrice_gt", default=None + ) + token_0_relative_price_lt: Optional[Any] = Field( + alias="token0relativePrice_lt", default=None + ) + token_0_relative_price_gte: Optional[Any] = Field( + alias="token0relativePrice_gte", default=None + ) + token_0_relative_price_lte: Optional[Any] = Field( + alias="token0relativePrice_lte", default=None + ) + token_0_relative_price_in: Optional[List[Any]] = Field( + alias="token0relativePrice_in", default=None + ) + token_0_relative_price_not_in: Optional[List[Any]] = Field( + alias="token0relativePrice_not_in", default=None + ) + token_1_relative_price: Optional[Any] = Field( + alias="token1relativePrice", default=None + ) + token_1_relative_price_not: Optional[Any] = Field( + alias="token1relativePrice_not", default=None + ) + token_1_relative_price_gt: Optional[Any] = Field( + alias="token1relativePrice_gt", default=None + ) + token_1_relative_price_lt: Optional[Any] = Field( + alias="token1relativePrice_lt", default=None + ) + token_1_relative_price_gte: Optional[Any] = Field( + alias="token1relativePrice_gte", default=None + ) + token_1_relative_price_lte: Optional[Any] = Field( + alias="token1relativePrice_lte", default=None + ) + token_1_relative_price_in: Optional[List[Any]] = Field( + alias="token1relativePrice_in", default=None + ) + token_1_relative_price_not_in: Optional[List[Any]] = Field( + alias="token1relativePrice_not_in", default=None + ) + volume_token_0: Optional[Any] = Field(alias="volumeToken0", default=None) + volume_token_0_not: Optional[Any] = Field(alias="volumeToken0_not", default=None) + volume_token_0_gt: Optional[Any] = Field(alias="volumeToken0_gt", default=None) + volume_token_0_lt: Optional[Any] = Field(alias="volumeToken0_lt", default=None) + volume_token_0_gte: Optional[Any] = Field(alias="volumeToken0_gte", default=None) + volume_token_0_lte: Optional[Any] = Field(alias="volumeToken0_lte", default=None) + volume_token_0_in: Optional[List[Any]] = Field( + alias="volumeToken0_in", default=None + ) + volume_token_0_not_in: Optional[List[Any]] = Field( + alias="volumeToken0_not_in", default=None + ) + volume_token_1: Optional[Any] = Field(alias="volumeToken1", default=None) + volume_token_1_not: Optional[Any] = Field(alias="volumeToken1_not", default=None) + volume_token_1_gt: Optional[Any] = Field(alias="volumeToken1_gt", default=None) + volume_token_1_lt: Optional[Any] = Field(alias="volumeToken1_lt", default=None) + volume_token_1_gte: Optional[Any] = Field(alias="volumeToken1_gte", default=None) + volume_token_1_lte: Optional[Any] = Field(alias="volumeToken1_lte", default=None) + volume_token_1_in: Optional[List[Any]] = Field( + alias="volumeToken1_in", default=None + ) + volume_token_1_not_in: Optional[List[Any]] = Field( + alias="volumeToken1_not_in", default=None + ) + volume_traded_eth: Optional[Any] = Field(alias="volumeTradedEth", default=None) + volume_traded_eth_not: Optional[Any] = Field( + alias="volumeTradedEth_not", default=None + ) + volume_traded_eth_gt: Optional[Any] = Field( + alias="volumeTradedEth_gt", default=None + ) + volume_traded_eth_lt: Optional[Any] = Field( + alias="volumeTradedEth_lt", default=None + ) + volume_traded_eth_gte: Optional[Any] = Field( + alias="volumeTradedEth_gte", default=None + ) + volume_traded_eth_lte: Optional[Any] = Field( + alias="volumeTradedEth_lte", default=None + ) + volume_traded_eth_in: Optional[List[Any]] = Field( + alias="volumeTradedEth_in", default=None + ) + volume_traded_eth_not_in: Optional[List[Any]] = Field( + alias="volumeTradedEth_not_in", default=None + ) + volume_traded_usd: Optional[Any] = Field(alias="volumeTradedUsd", default=None) + volume_traded_usd_not: Optional[Any] = Field( + alias="volumeTradedUsd_not", default=None + ) + volume_traded_usd_gt: Optional[Any] = Field( + alias="volumeTradedUsd_gt", default=None + ) + volume_traded_usd_lt: Optional[Any] = Field( + alias="volumeTradedUsd_lt", default=None + ) + volume_traded_usd_gte: Optional[Any] = Field( + alias="volumeTradedUsd_gte", default=None + ) + volume_traded_usd_lte: Optional[Any] = Field( + alias="volumeTradedUsd_lte", default=None + ) + volume_traded_usd_in: Optional[List[Any]] = Field( + alias="volumeTradedUsd_in", default=None + ) + volume_traded_usd_not_in: Optional[List[Any]] = Field( + alias="volumeTradedUsd_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["Pair_filter"]]] = Field(alias="and", default=None) + or_: Optional[List[Optional["Pair_filter"]]] = Field(alias="or", default=None) + + +class Settlement_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + tx_hash: Optional[Any] = Field(alias="txHash", default=None) + tx_hash_not: Optional[Any] = Field(alias="txHash_not", default=None) + tx_hash_gt: Optional[Any] = Field(alias="txHash_gt", default=None) + tx_hash_lt: Optional[Any] = Field(alias="txHash_lt", default=None) + tx_hash_gte: Optional[Any] = Field(alias="txHash_gte", default=None) + tx_hash_lte: Optional[Any] = Field(alias="txHash_lte", default=None) + tx_hash_in: Optional[List[Any]] = Field(alias="txHash_in", default=None) + tx_hash_not_in: Optional[List[Any]] = Field(alias="txHash_not_in", default=None) + tx_hash_contains: Optional[Any] = Field(alias="txHash_contains", default=None) + tx_hash_not_contains: Optional[Any] = Field( + alias="txHash_not_contains", default=None + ) + first_trade_timestamp: Optional[int] = Field( + alias="firstTradeTimestamp", default=None + ) + first_trade_timestamp_not: Optional[int] = Field( + alias="firstTradeTimestamp_not", default=None + ) + first_trade_timestamp_gt: Optional[int] = Field( + alias="firstTradeTimestamp_gt", default=None + ) + first_trade_timestamp_lt: Optional[int] = Field( + alias="firstTradeTimestamp_lt", default=None + ) + first_trade_timestamp_gte: Optional[int] = Field( + alias="firstTradeTimestamp_gte", default=None + ) + first_trade_timestamp_lte: Optional[int] = Field( + alias="firstTradeTimestamp_lte", default=None + ) + first_trade_timestamp_in: Optional[List[int]] = Field( + alias="firstTradeTimestamp_in", default=None + ) + first_trade_timestamp_not_in: Optional[List[int]] = Field( + alias="firstTradeTimestamp_not_in", default=None + ) + trades: Optional["Trade_filter"] = Field(alias="trades_", default=None) + solver: Optional[str] = None + solver_not: Optional[str] = None + solver_gt: Optional[str] = None + solver_lt: Optional[str] = None + solver_gte: Optional[str] = None + solver_lte: Optional[str] = None + solver_in: Optional[List[str]] = None + solver_not_in: Optional[List[str]] = None + solver_contains: Optional[str] = None + solver_contains_nocase: Optional[str] = None + solver_not_contains: Optional[str] = None + solver_not_contains_nocase: Optional[str] = None + solver_starts_with: Optional[str] = None + solver_starts_with_nocase: Optional[str] = None + solver_not_starts_with: Optional[str] = None + solver_not_starts_with_nocase: Optional[str] = None + solver_ends_with: Optional[str] = None + solver_ends_with_nocase: Optional[str] = None + solver_not_ends_with: Optional[str] = None + solver_not_ends_with_nocase: Optional[str] = None + solver: Optional["User_filter"] = Field(alias="solver_", default=None) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["Settlement_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["Settlement_filter"]]] = Field(alias="or", default=None) + + +class TokenDailyTotal_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + token: Optional[str] = None + token_not: Optional[str] = None + token_gt: Optional[str] = None + token_lt: Optional[str] = None + token_gte: Optional[str] = None + token_lte: Optional[str] = None + token_in: Optional[List[str]] = None + token_not_in: Optional[List[str]] = None + token_contains: Optional[str] = None + token_contains_nocase: Optional[str] = None + token_not_contains: Optional[str] = None + token_not_contains_nocase: Optional[str] = None + token_starts_with: Optional[str] = None + token_starts_with_nocase: Optional[str] = None + token_not_starts_with: Optional[str] = None + token_not_starts_with_nocase: Optional[str] = None + token_ends_with: Optional[str] = None + token_ends_with_nocase: Optional[str] = None + token_not_ends_with: Optional[str] = None + token_not_ends_with_nocase: Optional[str] = None + token: Optional["Token_filter"] = Field(alias="token_", default=None) + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + total_volume: Optional[Any] = Field(alias="totalVolume", default=None) + total_volume_not: Optional[Any] = Field(alias="totalVolume_not", default=None) + total_volume_gt: Optional[Any] = Field(alias="totalVolume_gt", default=None) + total_volume_lt: Optional[Any] = Field(alias="totalVolume_lt", default=None) + total_volume_gte: Optional[Any] = Field(alias="totalVolume_gte", default=None) + total_volume_lte: Optional[Any] = Field(alias="totalVolume_lte", default=None) + total_volume_in: Optional[List[Any]] = Field(alias="totalVolume_in", default=None) + total_volume_not_in: Optional[List[Any]] = Field( + alias="totalVolume_not_in", default=None + ) + total_volume_usd: Optional[Any] = Field(alias="totalVolumeUsd", default=None) + total_volume_usd_not: Optional[Any] = Field( + alias="totalVolumeUsd_not", default=None + ) + total_volume_usd_gt: Optional[Any] = Field(alias="totalVolumeUsd_gt", default=None) + total_volume_usd_lt: Optional[Any] = Field(alias="totalVolumeUsd_lt", default=None) + total_volume_usd_gte: Optional[Any] = Field( + alias="totalVolumeUsd_gte", default=None + ) + total_volume_usd_lte: Optional[Any] = Field( + alias="totalVolumeUsd_lte", default=None + ) + total_volume_usd_in: Optional[List[Any]] = Field( + alias="totalVolumeUsd_in", default=None + ) + total_volume_usd_not_in: Optional[List[Any]] = Field( + alias="totalVolumeUsd_not_in", default=None + ) + total_volume_eth: Optional[Any] = Field(alias="totalVolumeEth", default=None) + total_volume_eth_not: Optional[Any] = Field( + alias="totalVolumeEth_not", default=None + ) + total_volume_eth_gt: Optional[Any] = Field(alias="totalVolumeEth_gt", default=None) + total_volume_eth_lt: Optional[Any] = Field(alias="totalVolumeEth_lt", default=None) + total_volume_eth_gte: Optional[Any] = Field( + alias="totalVolumeEth_gte", default=None + ) + total_volume_eth_lte: Optional[Any] = Field( + alias="totalVolumeEth_lte", default=None + ) + total_volume_eth_in: Optional[List[Any]] = Field( + alias="totalVolumeEth_in", default=None + ) + total_volume_eth_not_in: Optional[List[Any]] = Field( + alias="totalVolumeEth_not_in", default=None + ) + total_trades: Optional[Any] = Field(alias="totalTrades", default=None) + total_trades_not: Optional[Any] = Field(alias="totalTrades_not", default=None) + total_trades_gt: Optional[Any] = Field(alias="totalTrades_gt", default=None) + total_trades_lt: Optional[Any] = Field(alias="totalTrades_lt", default=None) + total_trades_gte: Optional[Any] = Field(alias="totalTrades_gte", default=None) + total_trades_lte: Optional[Any] = Field(alias="totalTrades_lte", default=None) + total_trades_in: Optional[List[Any]] = Field(alias="totalTrades_in", default=None) + total_trades_not_in: Optional[List[Any]] = Field( + alias="totalTrades_not_in", default=None + ) + open_price: Optional[Any] = Field(alias="openPrice", default=None) + open_price_not: Optional[Any] = Field(alias="openPrice_not", default=None) + open_price_gt: Optional[Any] = Field(alias="openPrice_gt", default=None) + open_price_lt: Optional[Any] = Field(alias="openPrice_lt", default=None) + open_price_gte: Optional[Any] = Field(alias="openPrice_gte", default=None) + open_price_lte: Optional[Any] = Field(alias="openPrice_lte", default=None) + open_price_in: Optional[List[Any]] = Field(alias="openPrice_in", default=None) + open_price_not_in: Optional[List[Any]] = Field( + alias="openPrice_not_in", default=None + ) + close_price: Optional[Any] = Field(alias="closePrice", default=None) + close_price_not: Optional[Any] = Field(alias="closePrice_not", default=None) + close_price_gt: Optional[Any] = Field(alias="closePrice_gt", default=None) + close_price_lt: Optional[Any] = Field(alias="closePrice_lt", default=None) + close_price_gte: Optional[Any] = Field(alias="closePrice_gte", default=None) + close_price_lte: Optional[Any] = Field(alias="closePrice_lte", default=None) + close_price_in: Optional[List[Any]] = Field(alias="closePrice_in", default=None) + close_price_not_in: Optional[List[Any]] = Field( + alias="closePrice_not_in", default=None + ) + higher_price: Optional[Any] = Field(alias="higherPrice", default=None) + higher_price_not: Optional[Any] = Field(alias="higherPrice_not", default=None) + higher_price_gt: Optional[Any] = Field(alias="higherPrice_gt", default=None) + higher_price_lt: Optional[Any] = Field(alias="higherPrice_lt", default=None) + higher_price_gte: Optional[Any] = Field(alias="higherPrice_gte", default=None) + higher_price_lte: Optional[Any] = Field(alias="higherPrice_lte", default=None) + higher_price_in: Optional[List[Any]] = Field(alias="higherPrice_in", default=None) + higher_price_not_in: Optional[List[Any]] = Field( + alias="higherPrice_not_in", default=None + ) + lower_price: Optional[Any] = Field(alias="lowerPrice", default=None) + lower_price_not: Optional[Any] = Field(alias="lowerPrice_not", default=None) + lower_price_gt: Optional[Any] = Field(alias="lowerPrice_gt", default=None) + lower_price_lt: Optional[Any] = Field(alias="lowerPrice_lt", default=None) + lower_price_gte: Optional[Any] = Field(alias="lowerPrice_gte", default=None) + lower_price_lte: Optional[Any] = Field(alias="lowerPrice_lte", default=None) + lower_price_in: Optional[List[Any]] = Field(alias="lowerPrice_in", default=None) + lower_price_not_in: Optional[List[Any]] = Field( + alias="lowerPrice_not_in", default=None + ) + average_price: Optional[Any] = Field(alias="averagePrice", default=None) + average_price_not: Optional[Any] = Field(alias="averagePrice_not", default=None) + average_price_gt: Optional[Any] = Field(alias="averagePrice_gt", default=None) + average_price_lt: Optional[Any] = Field(alias="averagePrice_lt", default=None) + average_price_gte: Optional[Any] = Field(alias="averagePrice_gte", default=None) + average_price_lte: Optional[Any] = Field(alias="averagePrice_lte", default=None) + average_price_in: Optional[List[Any]] = Field(alias="averagePrice_in", default=None) + average_price_not_in: Optional[List[Any]] = Field( + alias="averagePrice_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["TokenDailyTotal_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["TokenDailyTotal_filter"]]] = Field( + alias="or", default=None + ) + + +class TokenHourlyTotal_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + token: Optional[str] = None + token_not: Optional[str] = None + token_gt: Optional[str] = None + token_lt: Optional[str] = None + token_gte: Optional[str] = None + token_lte: Optional[str] = None + token_in: Optional[List[str]] = None + token_not_in: Optional[List[str]] = None + token_contains: Optional[str] = None + token_contains_nocase: Optional[str] = None + token_not_contains: Optional[str] = None + token_not_contains_nocase: Optional[str] = None + token_starts_with: Optional[str] = None + token_starts_with_nocase: Optional[str] = None + token_not_starts_with: Optional[str] = None + token_not_starts_with_nocase: Optional[str] = None + token_ends_with: Optional[str] = None + token_ends_with_nocase: Optional[str] = None + token_not_ends_with: Optional[str] = None + token_not_ends_with_nocase: Optional[str] = None + token: Optional["Token_filter"] = Field(alias="token_", default=None) + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + total_volume: Optional[Any] = Field(alias="totalVolume", default=None) + total_volume_not: Optional[Any] = Field(alias="totalVolume_not", default=None) + total_volume_gt: Optional[Any] = Field(alias="totalVolume_gt", default=None) + total_volume_lt: Optional[Any] = Field(alias="totalVolume_lt", default=None) + total_volume_gte: Optional[Any] = Field(alias="totalVolume_gte", default=None) + total_volume_lte: Optional[Any] = Field(alias="totalVolume_lte", default=None) + total_volume_in: Optional[List[Any]] = Field(alias="totalVolume_in", default=None) + total_volume_not_in: Optional[List[Any]] = Field( + alias="totalVolume_not_in", default=None + ) + total_volume_usd: Optional[Any] = Field(alias="totalVolumeUsd", default=None) + total_volume_usd_not: Optional[Any] = Field( + alias="totalVolumeUsd_not", default=None + ) + total_volume_usd_gt: Optional[Any] = Field(alias="totalVolumeUsd_gt", default=None) + total_volume_usd_lt: Optional[Any] = Field(alias="totalVolumeUsd_lt", default=None) + total_volume_usd_gte: Optional[Any] = Field( + alias="totalVolumeUsd_gte", default=None + ) + total_volume_usd_lte: Optional[Any] = Field( + alias="totalVolumeUsd_lte", default=None + ) + total_volume_usd_in: Optional[List[Any]] = Field( + alias="totalVolumeUsd_in", default=None + ) + total_volume_usd_not_in: Optional[List[Any]] = Field( + alias="totalVolumeUsd_not_in", default=None + ) + total_volume_eth: Optional[Any] = Field(alias="totalVolumeEth", default=None) + total_volume_eth_not: Optional[Any] = Field( + alias="totalVolumeEth_not", default=None + ) + total_volume_eth_gt: Optional[Any] = Field(alias="totalVolumeEth_gt", default=None) + total_volume_eth_lt: Optional[Any] = Field(alias="totalVolumeEth_lt", default=None) + total_volume_eth_gte: Optional[Any] = Field( + alias="totalVolumeEth_gte", default=None + ) + total_volume_eth_lte: Optional[Any] = Field( + alias="totalVolumeEth_lte", default=None + ) + total_volume_eth_in: Optional[List[Any]] = Field( + alias="totalVolumeEth_in", default=None + ) + total_volume_eth_not_in: Optional[List[Any]] = Field( + alias="totalVolumeEth_not_in", default=None + ) + total_trades: Optional[Any] = Field(alias="totalTrades", default=None) + total_trades_not: Optional[Any] = Field(alias="totalTrades_not", default=None) + total_trades_gt: Optional[Any] = Field(alias="totalTrades_gt", default=None) + total_trades_lt: Optional[Any] = Field(alias="totalTrades_lt", default=None) + total_trades_gte: Optional[Any] = Field(alias="totalTrades_gte", default=None) + total_trades_lte: Optional[Any] = Field(alias="totalTrades_lte", default=None) + total_trades_in: Optional[List[Any]] = Field(alias="totalTrades_in", default=None) + total_trades_not_in: Optional[List[Any]] = Field( + alias="totalTrades_not_in", default=None + ) + open_price: Optional[Any] = Field(alias="openPrice", default=None) + open_price_not: Optional[Any] = Field(alias="openPrice_not", default=None) + open_price_gt: Optional[Any] = Field(alias="openPrice_gt", default=None) + open_price_lt: Optional[Any] = Field(alias="openPrice_lt", default=None) + open_price_gte: Optional[Any] = Field(alias="openPrice_gte", default=None) + open_price_lte: Optional[Any] = Field(alias="openPrice_lte", default=None) + open_price_in: Optional[List[Any]] = Field(alias="openPrice_in", default=None) + open_price_not_in: Optional[List[Any]] = Field( + alias="openPrice_not_in", default=None + ) + close_price: Optional[Any] = Field(alias="closePrice", default=None) + close_price_not: Optional[Any] = Field(alias="closePrice_not", default=None) + close_price_gt: Optional[Any] = Field(alias="closePrice_gt", default=None) + close_price_lt: Optional[Any] = Field(alias="closePrice_lt", default=None) + close_price_gte: Optional[Any] = Field(alias="closePrice_gte", default=None) + close_price_lte: Optional[Any] = Field(alias="closePrice_lte", default=None) + close_price_in: Optional[List[Any]] = Field(alias="closePrice_in", default=None) + close_price_not_in: Optional[List[Any]] = Field( + alias="closePrice_not_in", default=None + ) + higher_price: Optional[Any] = Field(alias="higherPrice", default=None) + higher_price_not: Optional[Any] = Field(alias="higherPrice_not", default=None) + higher_price_gt: Optional[Any] = Field(alias="higherPrice_gt", default=None) + higher_price_lt: Optional[Any] = Field(alias="higherPrice_lt", default=None) + higher_price_gte: Optional[Any] = Field(alias="higherPrice_gte", default=None) + higher_price_lte: Optional[Any] = Field(alias="higherPrice_lte", default=None) + higher_price_in: Optional[List[Any]] = Field(alias="higherPrice_in", default=None) + higher_price_not_in: Optional[List[Any]] = Field( + alias="higherPrice_not_in", default=None + ) + lower_price: Optional[Any] = Field(alias="lowerPrice", default=None) + lower_price_not: Optional[Any] = Field(alias="lowerPrice_not", default=None) + lower_price_gt: Optional[Any] = Field(alias="lowerPrice_gt", default=None) + lower_price_lt: Optional[Any] = Field(alias="lowerPrice_lt", default=None) + lower_price_gte: Optional[Any] = Field(alias="lowerPrice_gte", default=None) + lower_price_lte: Optional[Any] = Field(alias="lowerPrice_lte", default=None) + lower_price_in: Optional[List[Any]] = Field(alias="lowerPrice_in", default=None) + lower_price_not_in: Optional[List[Any]] = Field( + alias="lowerPrice_not_in", default=None + ) + average_price: Optional[Any] = Field(alias="averagePrice", default=None) + average_price_not: Optional[Any] = Field(alias="averagePrice_not", default=None) + average_price_gt: Optional[Any] = Field(alias="averagePrice_gt", default=None) + average_price_lt: Optional[Any] = Field(alias="averagePrice_lt", default=None) + average_price_gte: Optional[Any] = Field(alias="averagePrice_gte", default=None) + average_price_lte: Optional[Any] = Field(alias="averagePrice_lte", default=None) + average_price_in: Optional[List[Any]] = Field(alias="averagePrice_in", default=None) + average_price_not_in: Optional[List[Any]] = Field( + alias="averagePrice_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["TokenHourlyTotal_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["TokenHourlyTotal_filter"]]] = Field( + alias="or", default=None + ) + + +class TokenTradingEvent_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + token: Optional[str] = None + token_not: Optional[str] = None + token_gt: Optional[str] = None + token_lt: Optional[str] = None + token_gte: Optional[str] = None + token_lte: Optional[str] = None + token_in: Optional[List[str]] = None + token_not_in: Optional[List[str]] = None + token_contains: Optional[str] = None + token_contains_nocase: Optional[str] = None + token_not_contains: Optional[str] = None + token_not_contains_nocase: Optional[str] = None + token_starts_with: Optional[str] = None + token_starts_with_nocase: Optional[str] = None + token_not_starts_with: Optional[str] = None + token_not_starts_with_nocase: Optional[str] = None + token_ends_with: Optional[str] = None + token_ends_with_nocase: Optional[str] = None + token_not_ends_with: Optional[str] = None + token_not_ends_with_nocase: Optional[str] = None + token: Optional["Token_filter"] = Field(alias="token_", default=None) + trade: Optional[str] = None + trade_not: Optional[str] = None + trade_gt: Optional[str] = None + trade_lt: Optional[str] = None + trade_gte: Optional[str] = None + trade_lte: Optional[str] = None + trade_in: Optional[List[str]] = None + trade_not_in: Optional[List[str]] = None + trade_contains: Optional[str] = None + trade_contains_nocase: Optional[str] = None + trade_not_contains: Optional[str] = None + trade_not_contains_nocase: Optional[str] = None + trade_starts_with: Optional[str] = None + trade_starts_with_nocase: Optional[str] = None + trade_not_starts_with: Optional[str] = None + trade_not_starts_with_nocase: Optional[str] = None + trade_ends_with: Optional[str] = None + trade_ends_with_nocase: Optional[str] = None + trade_not_ends_with: Optional[str] = None + trade_not_ends_with_nocase: Optional[str] = None + trade: Optional["Trade_filter"] = Field(alias="trade_", default=None) + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + amount_eth: Optional[Any] = Field(alias="amountEth", default=None) + amount_eth_not: Optional[Any] = Field(alias="amountEth_not", default=None) + amount_eth_gt: Optional[Any] = Field(alias="amountEth_gt", default=None) + amount_eth_lt: Optional[Any] = Field(alias="amountEth_lt", default=None) + amount_eth_gte: Optional[Any] = Field(alias="amountEth_gte", default=None) + amount_eth_lte: Optional[Any] = Field(alias="amountEth_lte", default=None) + amount_eth_in: Optional[List[Any]] = Field(alias="amountEth_in", default=None) + amount_eth_not_in: Optional[List[Any]] = Field( + alias="amountEth_not_in", default=None + ) + amount_usd: Optional[Any] = Field(alias="amountUsd", default=None) + amount_usd_not: Optional[Any] = Field(alias="amountUsd_not", default=None) + amount_usd_gt: Optional[Any] = Field(alias="amountUsd_gt", default=None) + amount_usd_lt: Optional[Any] = Field(alias="amountUsd_lt", default=None) + amount_usd_gte: Optional[Any] = Field(alias="amountUsd_gte", default=None) + amount_usd_lte: Optional[Any] = Field(alias="amountUsd_lte", default=None) + amount_usd_in: Optional[List[Any]] = Field(alias="amountUsd_in", default=None) + amount_usd_not_in: Optional[List[Any]] = Field( + alias="amountUsd_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["TokenTradingEvent_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["TokenTradingEvent_filter"]]] = Field( + alias="or", default=None + ) + + +class Token_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + address: Optional[Any] = None + address_not: Optional[Any] = None + address_gt: Optional[Any] = None + address_lt: Optional[Any] = None + address_gte: Optional[Any] = None + address_lte: Optional[Any] = None + address_in: Optional[List[Any]] = None + address_not_in: Optional[List[Any]] = None + address_contains: Optional[Any] = None + address_not_contains: Optional[Any] = None + first_trade_timestamp: Optional[int] = Field( + alias="firstTradeTimestamp", default=None + ) + first_trade_timestamp_not: Optional[int] = Field( + alias="firstTradeTimestamp_not", default=None + ) + first_trade_timestamp_gt: Optional[int] = Field( + alias="firstTradeTimestamp_gt", default=None + ) + first_trade_timestamp_lt: Optional[int] = Field( + alias="firstTradeTimestamp_lt", default=None + ) + first_trade_timestamp_gte: Optional[int] = Field( + alias="firstTradeTimestamp_gte", default=None + ) + first_trade_timestamp_lte: Optional[int] = Field( + alias="firstTradeTimestamp_lte", default=None + ) + first_trade_timestamp_in: Optional[List[int]] = Field( + alias="firstTradeTimestamp_in", default=None + ) + first_trade_timestamp_not_in: Optional[List[int]] = Field( + alias="firstTradeTimestamp_not_in", default=None + ) + name: Optional[str] = None + name_not: Optional[str] = None + name_gt: Optional[str] = None + name_lt: Optional[str] = None + name_gte: Optional[str] = None + name_lte: Optional[str] = None + name_in: Optional[List[str]] = None + name_not_in: Optional[List[str]] = None + name_contains: Optional[str] = None + name_contains_nocase: Optional[str] = None + name_not_contains: Optional[str] = None + name_not_contains_nocase: Optional[str] = None + name_starts_with: Optional[str] = None + name_starts_with_nocase: Optional[str] = None + name_not_starts_with: Optional[str] = None + name_not_starts_with_nocase: Optional[str] = None + name_ends_with: Optional[str] = None + name_ends_with_nocase: Optional[str] = None + name_not_ends_with: Optional[str] = None + name_not_ends_with_nocase: Optional[str] = None + symbol: Optional[str] = None + symbol_not: Optional[str] = None + symbol_gt: Optional[str] = None + symbol_lt: Optional[str] = None + symbol_gte: Optional[str] = None + symbol_lte: Optional[str] = None + symbol_in: Optional[List[str]] = None + symbol_not_in: Optional[List[str]] = None + symbol_contains: Optional[str] = None + symbol_contains_nocase: Optional[str] = None + symbol_not_contains: Optional[str] = None + symbol_not_contains_nocase: Optional[str] = None + symbol_starts_with: Optional[str] = None + symbol_starts_with_nocase: Optional[str] = None + symbol_not_starts_with: Optional[str] = None + symbol_not_starts_with_nocase: Optional[str] = None + symbol_ends_with: Optional[str] = None + symbol_ends_with_nocase: Optional[str] = None + symbol_not_ends_with: Optional[str] = None + symbol_not_ends_with_nocase: Optional[str] = None + decimals: Optional[int] = None + decimals_not: Optional[int] = None + decimals_gt: Optional[int] = None + decimals_lt: Optional[int] = None + decimals_gte: Optional[int] = None + decimals_lte: Optional[int] = None + decimals_in: Optional[List[int]] = None + decimals_not_in: Optional[List[int]] = None + total_volume: Optional[Any] = Field(alias="totalVolume", default=None) + total_volume_not: Optional[Any] = Field(alias="totalVolume_not", default=None) + total_volume_gt: Optional[Any] = Field(alias="totalVolume_gt", default=None) + total_volume_lt: Optional[Any] = Field(alias="totalVolume_lt", default=None) + total_volume_gte: Optional[Any] = Field(alias="totalVolume_gte", default=None) + total_volume_lte: Optional[Any] = Field(alias="totalVolume_lte", default=None) + total_volume_in: Optional[List[Any]] = Field(alias="totalVolume_in", default=None) + total_volume_not_in: Optional[List[Any]] = Field( + alias="totalVolume_not_in", default=None + ) + price_eth: Optional[Any] = Field(alias="priceEth", default=None) + price_eth_not: Optional[Any] = Field(alias="priceEth_not", default=None) + price_eth_gt: Optional[Any] = Field(alias="priceEth_gt", default=None) + price_eth_lt: Optional[Any] = Field(alias="priceEth_lt", default=None) + price_eth_gte: Optional[Any] = Field(alias="priceEth_gte", default=None) + price_eth_lte: Optional[Any] = Field(alias="priceEth_lte", default=None) + price_eth_in: Optional[List[Any]] = Field(alias="priceEth_in", default=None) + price_eth_not_in: Optional[List[Any]] = Field(alias="priceEth_not_in", default=None) + price_usd: Optional[Any] = Field(alias="priceUsd", default=None) + price_usd_not: Optional[Any] = Field(alias="priceUsd_not", default=None) + price_usd_gt: Optional[Any] = Field(alias="priceUsd_gt", default=None) + price_usd_lt: Optional[Any] = Field(alias="priceUsd_lt", default=None) + price_usd_gte: Optional[Any] = Field(alias="priceUsd_gte", default=None) + price_usd_lte: Optional[Any] = Field(alias="priceUsd_lte", default=None) + price_usd_in: Optional[List[Any]] = Field(alias="priceUsd_in", default=None) + price_usd_not_in: Optional[List[Any]] = Field(alias="priceUsd_not_in", default=None) + history: Optional["TokenTradingEvent_filter"] = Field( + alias="history_", default=None + ) + hourly_totals: Optional["TokenHourlyTotal_filter"] = Field( + alias="hourlyTotals_", default=None + ) + daily_totals: Optional["TokenDailyTotal_filter"] = Field( + alias="dailyTotals_", default=None + ) + number_of_trades: Optional[int] = Field(alias="numberOfTrades", default=None) + number_of_trades_not: Optional[int] = Field( + alias="numberOfTrades_not", default=None + ) + number_of_trades_gt: Optional[int] = Field(alias="numberOfTrades_gt", default=None) + number_of_trades_lt: Optional[int] = Field(alias="numberOfTrades_lt", default=None) + number_of_trades_gte: Optional[int] = Field( + alias="numberOfTrades_gte", default=None + ) + number_of_trades_lte: Optional[int] = Field( + alias="numberOfTrades_lte", default=None + ) + number_of_trades_in: Optional[List[int]] = Field( + alias="numberOfTrades_in", default=None + ) + number_of_trades_not_in: Optional[List[int]] = Field( + alias="numberOfTrades_not_in", default=None + ) + total_volume_usd: Optional[Any] = Field(alias="totalVolumeUsd", default=None) + total_volume_usd_not: Optional[Any] = Field( + alias="totalVolumeUsd_not", default=None + ) + total_volume_usd_gt: Optional[Any] = Field(alias="totalVolumeUsd_gt", default=None) + total_volume_usd_lt: Optional[Any] = Field(alias="totalVolumeUsd_lt", default=None) + total_volume_usd_gte: Optional[Any] = Field( + alias="totalVolumeUsd_gte", default=None + ) + total_volume_usd_lte: Optional[Any] = Field( + alias="totalVolumeUsd_lte", default=None + ) + total_volume_usd_in: Optional[List[Any]] = Field( + alias="totalVolumeUsd_in", default=None + ) + total_volume_usd_not_in: Optional[List[Any]] = Field( + alias="totalVolumeUsd_not_in", default=None + ) + total_volume_eth: Optional[Any] = Field(alias="totalVolumeEth", default=None) + total_volume_eth_not: Optional[Any] = Field( + alias="totalVolumeEth_not", default=None + ) + total_volume_eth_gt: Optional[Any] = Field(alias="totalVolumeEth_gt", default=None) + total_volume_eth_lt: Optional[Any] = Field(alias="totalVolumeEth_lt", default=None) + total_volume_eth_gte: Optional[Any] = Field( + alias="totalVolumeEth_gte", default=None + ) + total_volume_eth_lte: Optional[Any] = Field( + alias="totalVolumeEth_lte", default=None + ) + total_volume_eth_in: Optional[List[Any]] = Field( + alias="totalVolumeEth_in", default=None + ) + total_volume_eth_not_in: Optional[List[Any]] = Field( + alias="totalVolumeEth_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["Token_filter"]]] = Field(alias="and", default=None) + or_: Optional[List[Optional["Token_filter"]]] = Field(alias="or", default=None) + + +class Total_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + tokens: Optional[Any] = None + tokens_not: Optional[Any] = None + tokens_gt: Optional[Any] = None + tokens_lt: Optional[Any] = None + tokens_gte: Optional[Any] = None + tokens_lte: Optional[Any] = None + tokens_in: Optional[List[Any]] = None + tokens_not_in: Optional[List[Any]] = None + orders: Optional[Any] = None + orders_not: Optional[Any] = None + orders_gt: Optional[Any] = None + orders_lt: Optional[Any] = None + orders_gte: Optional[Any] = None + orders_lte: Optional[Any] = None + orders_in: Optional[List[Any]] = None + orders_not_in: Optional[List[Any]] = None + traders: Optional[Any] = None + traders_not: Optional[Any] = None + traders_gt: Optional[Any] = None + traders_lt: Optional[Any] = None + traders_gte: Optional[Any] = None + traders_lte: Optional[Any] = None + traders_in: Optional[List[Any]] = None + traders_not_in: Optional[List[Any]] = None + number_of_trades: Optional[Any] = Field(alias="numberOfTrades", default=None) + number_of_trades_not: Optional[Any] = Field( + alias="numberOfTrades_not", default=None + ) + number_of_trades_gt: Optional[Any] = Field(alias="numberOfTrades_gt", default=None) + number_of_trades_lt: Optional[Any] = Field(alias="numberOfTrades_lt", default=None) + number_of_trades_gte: Optional[Any] = Field( + alias="numberOfTrades_gte", default=None + ) + number_of_trades_lte: Optional[Any] = Field( + alias="numberOfTrades_lte", default=None + ) + number_of_trades_in: Optional[List[Any]] = Field( + alias="numberOfTrades_in", default=None + ) + number_of_trades_not_in: Optional[List[Any]] = Field( + alias="numberOfTrades_not_in", default=None + ) + settlements: Optional[Any] = None + settlements_not: Optional[Any] = None + settlements_gt: Optional[Any] = None + settlements_lt: Optional[Any] = None + settlements_gte: Optional[Any] = None + settlements_lte: Optional[Any] = None + settlements_in: Optional[List[Any]] = None + settlements_not_in: Optional[List[Any]] = None + volume_usd: Optional[Any] = Field(alias="volumeUsd", default=None) + volume_usd_not: Optional[Any] = Field(alias="volumeUsd_not", default=None) + volume_usd_gt: Optional[Any] = Field(alias="volumeUsd_gt", default=None) + volume_usd_lt: Optional[Any] = Field(alias="volumeUsd_lt", default=None) + volume_usd_gte: Optional[Any] = Field(alias="volumeUsd_gte", default=None) + volume_usd_lte: Optional[Any] = Field(alias="volumeUsd_lte", default=None) + volume_usd_in: Optional[List[Any]] = Field(alias="volumeUsd_in", default=None) + volume_usd_not_in: Optional[List[Any]] = Field( + alias="volumeUsd_not_in", default=None + ) + volume_eth: Optional[Any] = Field(alias="volumeEth", default=None) + volume_eth_not: Optional[Any] = Field(alias="volumeEth_not", default=None) + volume_eth_gt: Optional[Any] = Field(alias="volumeEth_gt", default=None) + volume_eth_lt: Optional[Any] = Field(alias="volumeEth_lt", default=None) + volume_eth_gte: Optional[Any] = Field(alias="volumeEth_gte", default=None) + volume_eth_lte: Optional[Any] = Field(alias="volumeEth_lte", default=None) + volume_eth_in: Optional[List[Any]] = Field(alias="volumeEth_in", default=None) + volume_eth_not_in: Optional[List[Any]] = Field( + alias="volumeEth_not_in", default=None + ) + fees_usd: Optional[Any] = Field(alias="feesUsd", default=None) + fees_usd_not: Optional[Any] = Field(alias="feesUsd_not", default=None) + fees_usd_gt: Optional[Any] = Field(alias="feesUsd_gt", default=None) + fees_usd_lt: Optional[Any] = Field(alias="feesUsd_lt", default=None) + fees_usd_gte: Optional[Any] = Field(alias="feesUsd_gte", default=None) + fees_usd_lte: Optional[Any] = Field(alias="feesUsd_lte", default=None) + fees_usd_in: Optional[List[Any]] = Field(alias="feesUsd_in", default=None) + fees_usd_not_in: Optional[List[Any]] = Field(alias="feesUsd_not_in", default=None) + fees_eth: Optional[Any] = Field(alias="feesEth", default=None) + fees_eth_not: Optional[Any] = Field(alias="feesEth_not", default=None) + fees_eth_gt: Optional[Any] = Field(alias="feesEth_gt", default=None) + fees_eth_lt: Optional[Any] = Field(alias="feesEth_lt", default=None) + fees_eth_gte: Optional[Any] = Field(alias="feesEth_gte", default=None) + fees_eth_lte: Optional[Any] = Field(alias="feesEth_lte", default=None) + fees_eth_in: Optional[List[Any]] = Field(alias="feesEth_in", default=None) + fees_eth_not_in: Optional[List[Any]] = Field(alias="feesEth_not_in", default=None) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["Total_filter"]]] = Field(alias="and", default=None) + or_: Optional[List[Optional["Total_filter"]]] = Field(alias="or", default=None) + + +class Trade_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + timestamp: Optional[int] = None + timestamp_not: Optional[int] = None + timestamp_gt: Optional[int] = None + timestamp_lt: Optional[int] = None + timestamp_gte: Optional[int] = None + timestamp_lte: Optional[int] = None + timestamp_in: Optional[List[int]] = None + timestamp_not_in: Optional[List[int]] = None + gas_price: Optional[Any] = Field(alias="gasPrice", default=None) + gas_price_not: Optional[Any] = Field(alias="gasPrice_not", default=None) + gas_price_gt: Optional[Any] = Field(alias="gasPrice_gt", default=None) + gas_price_lt: Optional[Any] = Field(alias="gasPrice_lt", default=None) + gas_price_gte: Optional[Any] = Field(alias="gasPrice_gte", default=None) + gas_price_lte: Optional[Any] = Field(alias="gasPrice_lte", default=None) + gas_price_in: Optional[List[Any]] = Field(alias="gasPrice_in", default=None) + gas_price_not_in: Optional[List[Any]] = Field(alias="gasPrice_not_in", default=None) + fee_amount: Optional[Any] = Field(alias="feeAmount", default=None) + fee_amount_not: Optional[Any] = Field(alias="feeAmount_not", default=None) + fee_amount_gt: Optional[Any] = Field(alias="feeAmount_gt", default=None) + fee_amount_lt: Optional[Any] = Field(alias="feeAmount_lt", default=None) + fee_amount_gte: Optional[Any] = Field(alias="feeAmount_gte", default=None) + fee_amount_lte: Optional[Any] = Field(alias="feeAmount_lte", default=None) + fee_amount_in: Optional[List[Any]] = Field(alias="feeAmount_in", default=None) + fee_amount_not_in: Optional[List[Any]] = Field( + alias="feeAmount_not_in", default=None + ) + tx_hash: Optional[Any] = Field(alias="txHash", default=None) + tx_hash_not: Optional[Any] = Field(alias="txHash_not", default=None) + tx_hash_gt: Optional[Any] = Field(alias="txHash_gt", default=None) + tx_hash_lt: Optional[Any] = Field(alias="txHash_lt", default=None) + tx_hash_gte: Optional[Any] = Field(alias="txHash_gte", default=None) + tx_hash_lte: Optional[Any] = Field(alias="txHash_lte", default=None) + tx_hash_in: Optional[List[Any]] = Field(alias="txHash_in", default=None) + tx_hash_not_in: Optional[List[Any]] = Field(alias="txHash_not_in", default=None) + tx_hash_contains: Optional[Any] = Field(alias="txHash_contains", default=None) + tx_hash_not_contains: Optional[Any] = Field( + alias="txHash_not_contains", default=None + ) + settlement: Optional[str] = None + settlement_not: Optional[str] = None + settlement_gt: Optional[str] = None + settlement_lt: Optional[str] = None + settlement_gte: Optional[str] = None + settlement_lte: Optional[str] = None + settlement_in: Optional[List[str]] = None + settlement_not_in: Optional[List[str]] = None + settlement_contains: Optional[str] = None + settlement_contains_nocase: Optional[str] = None + settlement_not_contains: Optional[str] = None + settlement_not_contains_nocase: Optional[str] = None + settlement_starts_with: Optional[str] = None + settlement_starts_with_nocase: Optional[str] = None + settlement_not_starts_with: Optional[str] = None + settlement_not_starts_with_nocase: Optional[str] = None + settlement_ends_with: Optional[str] = None + settlement_ends_with_nocase: Optional[str] = None + settlement_not_ends_with: Optional[str] = None + settlement_not_ends_with_nocase: Optional[str] = None + settlement: Optional["Settlement_filter"] = Field(alias="settlement_", default=None) + buy_amount: Optional[Any] = Field(alias="buyAmount", default=None) + buy_amount_not: Optional[Any] = Field(alias="buyAmount_not", default=None) + buy_amount_gt: Optional[Any] = Field(alias="buyAmount_gt", default=None) + buy_amount_lt: Optional[Any] = Field(alias="buyAmount_lt", default=None) + buy_amount_gte: Optional[Any] = Field(alias="buyAmount_gte", default=None) + buy_amount_lte: Optional[Any] = Field(alias="buyAmount_lte", default=None) + buy_amount_in: Optional[List[Any]] = Field(alias="buyAmount_in", default=None) + buy_amount_not_in: Optional[List[Any]] = Field( + alias="buyAmount_not_in", default=None + ) + sell_amount: Optional[Any] = Field(alias="sellAmount", default=None) + sell_amount_not: Optional[Any] = Field(alias="sellAmount_not", default=None) + sell_amount_gt: Optional[Any] = Field(alias="sellAmount_gt", default=None) + sell_amount_lt: Optional[Any] = Field(alias="sellAmount_lt", default=None) + sell_amount_gte: Optional[Any] = Field(alias="sellAmount_gte", default=None) + sell_amount_lte: Optional[Any] = Field(alias="sellAmount_lte", default=None) + sell_amount_in: Optional[List[Any]] = Field(alias="sellAmount_in", default=None) + sell_amount_not_in: Optional[List[Any]] = Field( + alias="sellAmount_not_in", default=None + ) + sell_token: Optional[str] = Field(alias="sellToken", default=None) + sell_token_not: Optional[str] = Field(alias="sellToken_not", default=None) + sell_token_gt: Optional[str] = Field(alias="sellToken_gt", default=None) + sell_token_lt: Optional[str] = Field(alias="sellToken_lt", default=None) + sell_token_gte: Optional[str] = Field(alias="sellToken_gte", default=None) + sell_token_lte: Optional[str] = Field(alias="sellToken_lte", default=None) + sell_token_in: Optional[List[str]] = Field(alias="sellToken_in", default=None) + sell_token_not_in: Optional[List[str]] = Field( + alias="sellToken_not_in", default=None + ) + sell_token_contains: Optional[str] = Field(alias="sellToken_contains", default=None) + sell_token_contains_nocase: Optional[str] = Field( + alias="sellToken_contains_nocase", default=None + ) + sell_token_not_contains: Optional[str] = Field( + alias="sellToken_not_contains", default=None + ) + sell_token_not_contains_nocase: Optional[str] = Field( + alias="sellToken_not_contains_nocase", default=None + ) + sell_token_starts_with: Optional[str] = Field( + alias="sellToken_starts_with", default=None + ) + sell_token_starts_with_nocase: Optional[str] = Field( + alias="sellToken_starts_with_nocase", default=None + ) + sell_token_not_starts_with: Optional[str] = Field( + alias="sellToken_not_starts_with", default=None + ) + sell_token_not_starts_with_nocase: Optional[str] = Field( + alias="sellToken_not_starts_with_nocase", default=None + ) + sell_token_ends_with: Optional[str] = Field( + alias="sellToken_ends_with", default=None + ) + sell_token_ends_with_nocase: Optional[str] = Field( + alias="sellToken_ends_with_nocase", default=None + ) + sell_token_not_ends_with: Optional[str] = Field( + alias="sellToken_not_ends_with", default=None + ) + sell_token_not_ends_with_nocase: Optional[str] = Field( + alias="sellToken_not_ends_with_nocase", default=None + ) + sell_token: Optional["Token_filter"] = Field(alias="sellToken_", default=None) + buy_token: Optional[str] = Field(alias="buyToken", default=None) + buy_token_not: Optional[str] = Field(alias="buyToken_not", default=None) + buy_token_gt: Optional[str] = Field(alias="buyToken_gt", default=None) + buy_token_lt: Optional[str] = Field(alias="buyToken_lt", default=None) + buy_token_gte: Optional[str] = Field(alias="buyToken_gte", default=None) + buy_token_lte: Optional[str] = Field(alias="buyToken_lte", default=None) + buy_token_in: Optional[List[str]] = Field(alias="buyToken_in", default=None) + buy_token_not_in: Optional[List[str]] = Field(alias="buyToken_not_in", default=None) + buy_token_contains: Optional[str] = Field(alias="buyToken_contains", default=None) + buy_token_contains_nocase: Optional[str] = Field( + alias="buyToken_contains_nocase", default=None + ) + buy_token_not_contains: Optional[str] = Field( + alias="buyToken_not_contains", default=None + ) + buy_token_not_contains_nocase: Optional[str] = Field( + alias="buyToken_not_contains_nocase", default=None + ) + buy_token_starts_with: Optional[str] = Field( + alias="buyToken_starts_with", default=None + ) + buy_token_starts_with_nocase: Optional[str] = Field( + alias="buyToken_starts_with_nocase", default=None + ) + buy_token_not_starts_with: Optional[str] = Field( + alias="buyToken_not_starts_with", default=None + ) + buy_token_not_starts_with_nocase: Optional[str] = Field( + alias="buyToken_not_starts_with_nocase", default=None + ) + buy_token_ends_with: Optional[str] = Field(alias="buyToken_ends_with", default=None) + buy_token_ends_with_nocase: Optional[str] = Field( + alias="buyToken_ends_with_nocase", default=None + ) + buy_token_not_ends_with: Optional[str] = Field( + alias="buyToken_not_ends_with", default=None + ) + buy_token_not_ends_with_nocase: Optional[str] = Field( + alias="buyToken_not_ends_with_nocase", default=None + ) + buy_token: Optional["Token_filter"] = Field(alias="buyToken_", default=None) + order: Optional[str] = None + order_not: Optional[str] = None + order_gt: Optional[str] = None + order_lt: Optional[str] = None + order_gte: Optional[str] = None + order_lte: Optional[str] = None + order_in: Optional[List[str]] = None + order_not_in: Optional[List[str]] = None + order_contains: Optional[str] = None + order_contains_nocase: Optional[str] = None + order_not_contains: Optional[str] = None + order_not_contains_nocase: Optional[str] = None + order_starts_with: Optional[str] = None + order_starts_with_nocase: Optional[str] = None + order_not_starts_with: Optional[str] = None + order_not_starts_with_nocase: Optional[str] = None + order_ends_with: Optional[str] = None + order_ends_with_nocase: Optional[str] = None + order_not_ends_with: Optional[str] = None + order_not_ends_with_nocase: Optional[str] = None + order: Optional["Order_filter"] = Field(alias="order_", default=None) + buy_amount_eth: Optional[Any] = Field(alias="buyAmountEth", default=None) + buy_amount_eth_not: Optional[Any] = Field(alias="buyAmountEth_not", default=None) + buy_amount_eth_gt: Optional[Any] = Field(alias="buyAmountEth_gt", default=None) + buy_amount_eth_lt: Optional[Any] = Field(alias="buyAmountEth_lt", default=None) + buy_amount_eth_gte: Optional[Any] = Field(alias="buyAmountEth_gte", default=None) + buy_amount_eth_lte: Optional[Any] = Field(alias="buyAmountEth_lte", default=None) + buy_amount_eth_in: Optional[List[Any]] = Field( + alias="buyAmountEth_in", default=None + ) + buy_amount_eth_not_in: Optional[List[Any]] = Field( + alias="buyAmountEth_not_in", default=None + ) + sell_amount_eth: Optional[Any] = Field(alias="sellAmountEth", default=None) + sell_amount_eth_not: Optional[Any] = Field(alias="sellAmountEth_not", default=None) + sell_amount_eth_gt: Optional[Any] = Field(alias="sellAmountEth_gt", default=None) + sell_amount_eth_lt: Optional[Any] = Field(alias="sellAmountEth_lt", default=None) + sell_amount_eth_gte: Optional[Any] = Field(alias="sellAmountEth_gte", default=None) + sell_amount_eth_lte: Optional[Any] = Field(alias="sellAmountEth_lte", default=None) + sell_amount_eth_in: Optional[List[Any]] = Field( + alias="sellAmountEth_in", default=None + ) + sell_amount_eth_not_in: Optional[List[Any]] = Field( + alias="sellAmountEth_not_in", default=None + ) + buy_amount_usd: Optional[Any] = Field(alias="buyAmountUsd", default=None) + buy_amount_usd_not: Optional[Any] = Field(alias="buyAmountUsd_not", default=None) + buy_amount_usd_gt: Optional[Any] = Field(alias="buyAmountUsd_gt", default=None) + buy_amount_usd_lt: Optional[Any] = Field(alias="buyAmountUsd_lt", default=None) + buy_amount_usd_gte: Optional[Any] = Field(alias="buyAmountUsd_gte", default=None) + buy_amount_usd_lte: Optional[Any] = Field(alias="buyAmountUsd_lte", default=None) + buy_amount_usd_in: Optional[List[Any]] = Field( + alias="buyAmountUsd_in", default=None + ) + buy_amount_usd_not_in: Optional[List[Any]] = Field( + alias="buyAmountUsd_not_in", default=None + ) + sell_amount_usd: Optional[Any] = Field(alias="sellAmountUsd", default=None) + sell_amount_usd_not: Optional[Any] = Field(alias="sellAmountUsd_not", default=None) + sell_amount_usd_gt: Optional[Any] = Field(alias="sellAmountUsd_gt", default=None) + sell_amount_usd_lt: Optional[Any] = Field(alias="sellAmountUsd_lt", default=None) + sell_amount_usd_gte: Optional[Any] = Field(alias="sellAmountUsd_gte", default=None) + sell_amount_usd_lte: Optional[Any] = Field(alias="sellAmountUsd_lte", default=None) + sell_amount_usd_in: Optional[List[Any]] = Field( + alias="sellAmountUsd_in", default=None + ) + sell_amount_usd_not_in: Optional[List[Any]] = Field( + alias="sellAmountUsd_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["Trade_filter"]]] = Field(alias="and", default=None) + or_: Optional[List[Optional["Trade_filter"]]] = Field(alias="or", default=None) + + +class UniswapPool_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + token_0: Optional[str] = Field(alias="token0", default=None) + token_0_not: Optional[str] = Field(alias="token0_not", default=None) + token_0_gt: Optional[str] = Field(alias="token0_gt", default=None) + token_0_lt: Optional[str] = Field(alias="token0_lt", default=None) + token_0_gte: Optional[str] = Field(alias="token0_gte", default=None) + token_0_lte: Optional[str] = Field(alias="token0_lte", default=None) + token_0_in: Optional[List[str]] = Field(alias="token0_in", default=None) + token_0_not_in: Optional[List[str]] = Field(alias="token0_not_in", default=None) + token_0_contains: Optional[str] = Field(alias="token0_contains", default=None) + token_0_contains_nocase: Optional[str] = Field( + alias="token0_contains_nocase", default=None + ) + token_0_not_contains: Optional[str] = Field( + alias="token0_not_contains", default=None + ) + token_0_not_contains_nocase: Optional[str] = Field( + alias="token0_not_contains_nocase", default=None + ) + token_0_starts_with: Optional[str] = Field(alias="token0_starts_with", default=None) + token_0_starts_with_nocase: Optional[str] = Field( + alias="token0_starts_with_nocase", default=None + ) + token_0_not_starts_with: Optional[str] = Field( + alias="token0_not_starts_with", default=None + ) + token_0_not_starts_with_nocase: Optional[str] = Field( + alias="token0_not_starts_with_nocase", default=None + ) + token_0_ends_with: Optional[str] = Field(alias="token0_ends_with", default=None) + token_0_ends_with_nocase: Optional[str] = Field( + alias="token0_ends_with_nocase", default=None + ) + token_0_not_ends_with: Optional[str] = Field( + alias="token0_not_ends_with", default=None + ) + token_0_not_ends_with_nocase: Optional[str] = Field( + alias="token0_not_ends_with_nocase", default=None + ) + token_0: Optional["UniswapToken_filter"] = Field(alias="token0_", default=None) + token_1: Optional[str] = Field(alias="token1", default=None) + token_1_not: Optional[str] = Field(alias="token1_not", default=None) + token_1_gt: Optional[str] = Field(alias="token1_gt", default=None) + token_1_lt: Optional[str] = Field(alias="token1_lt", default=None) + token_1_gte: Optional[str] = Field(alias="token1_gte", default=None) + token_1_lte: Optional[str] = Field(alias="token1_lte", default=None) + token_1_in: Optional[List[str]] = Field(alias="token1_in", default=None) + token_1_not_in: Optional[List[str]] = Field(alias="token1_not_in", default=None) + token_1_contains: Optional[str] = Field(alias="token1_contains", default=None) + token_1_contains_nocase: Optional[str] = Field( + alias="token1_contains_nocase", default=None + ) + token_1_not_contains: Optional[str] = Field( + alias="token1_not_contains", default=None + ) + token_1_not_contains_nocase: Optional[str] = Field( + alias="token1_not_contains_nocase", default=None + ) + token_1_starts_with: Optional[str] = Field(alias="token1_starts_with", default=None) + token_1_starts_with_nocase: Optional[str] = Field( + alias="token1_starts_with_nocase", default=None + ) + token_1_not_starts_with: Optional[str] = Field( + alias="token1_not_starts_with", default=None + ) + token_1_not_starts_with_nocase: Optional[str] = Field( + alias="token1_not_starts_with_nocase", default=None + ) + token_1_ends_with: Optional[str] = Field(alias="token1_ends_with", default=None) + token_1_ends_with_nocase: Optional[str] = Field( + alias="token1_ends_with_nocase", default=None + ) + token_1_not_ends_with: Optional[str] = Field( + alias="token1_not_ends_with", default=None + ) + token_1_not_ends_with_nocase: Optional[str] = Field( + alias="token1_not_ends_with_nocase", default=None + ) + token_1: Optional["UniswapToken_filter"] = Field(alias="token1_", default=None) + liquidity: Optional[Any] = None + liquidity_not: Optional[Any] = None + liquidity_gt: Optional[Any] = None + liquidity_lt: Optional[Any] = None + liquidity_gte: Optional[Any] = None + liquidity_lte: Optional[Any] = None + liquidity_in: Optional[List[Any]] = None + liquidity_not_in: Optional[List[Any]] = None + token_0_price: Optional[Any] = Field(alias="token0Price", default=None) + token_0_price_not: Optional[Any] = Field(alias="token0Price_not", default=None) + token_0_price_gt: Optional[Any] = Field(alias="token0Price_gt", default=None) + token_0_price_lt: Optional[Any] = Field(alias="token0Price_lt", default=None) + token_0_price_gte: Optional[Any] = Field(alias="token0Price_gte", default=None) + token_0_price_lte: Optional[Any] = Field(alias="token0Price_lte", default=None) + token_0_price_in: Optional[List[Any]] = Field(alias="token0Price_in", default=None) + token_0_price_not_in: Optional[List[Any]] = Field( + alias="token0Price_not_in", default=None + ) + token_1_price: Optional[Any] = Field(alias="token1Price", default=None) + token_1_price_not: Optional[Any] = Field(alias="token1Price_not", default=None) + token_1_price_gt: Optional[Any] = Field(alias="token1Price_gt", default=None) + token_1_price_lt: Optional[Any] = Field(alias="token1Price_lt", default=None) + token_1_price_gte: Optional[Any] = Field(alias="token1Price_gte", default=None) + token_1_price_lte: Optional[Any] = Field(alias="token1Price_lte", default=None) + token_1_price_in: Optional[List[Any]] = Field(alias="token1Price_in", default=None) + token_1_price_not_in: Optional[List[Any]] = Field( + alias="token1Price_not_in", default=None + ) + tick: Optional[Any] = None + tick_not: Optional[Any] = None + tick_gt: Optional[Any] = None + tick_lt: Optional[Any] = None + tick_gte: Optional[Any] = None + tick_lte: Optional[Any] = None + tick_in: Optional[List[Any]] = None + tick_not_in: Optional[List[Any]] = None + total_value_locked_token_0: Optional[Any] = Field( + alias="totalValueLockedToken0", default=None + ) + total_value_locked_token_0_not: Optional[Any] = Field( + alias="totalValueLockedToken0_not", default=None + ) + total_value_locked_token_0_gt: Optional[Any] = Field( + alias="totalValueLockedToken0_gt", default=None + ) + total_value_locked_token_0_lt: Optional[Any] = Field( + alias="totalValueLockedToken0_lt", default=None + ) + total_value_locked_token_0_gte: Optional[Any] = Field( + alias="totalValueLockedToken0_gte", default=None + ) + total_value_locked_token_0_lte: Optional[Any] = Field( + alias="totalValueLockedToken0_lte", default=None + ) + total_value_locked_token_0_in: Optional[List[Any]] = Field( + alias="totalValueLockedToken0_in", default=None + ) + total_value_locked_token_0_not_in: Optional[List[Any]] = Field( + alias="totalValueLockedToken0_not_in", default=None + ) + total_value_locked_token_1: Optional[Any] = Field( + alias="totalValueLockedToken1", default=None + ) + total_value_locked_token_1_not: Optional[Any] = Field( + alias="totalValueLockedToken1_not", default=None + ) + total_value_locked_token_1_gt: Optional[Any] = Field( + alias="totalValueLockedToken1_gt", default=None + ) + total_value_locked_token_1_lt: Optional[Any] = Field( + alias="totalValueLockedToken1_lt", default=None + ) + total_value_locked_token_1_gte: Optional[Any] = Field( + alias="totalValueLockedToken1_gte", default=None + ) + total_value_locked_token_1_lte: Optional[Any] = Field( + alias="totalValueLockedToken1_lte", default=None + ) + total_value_locked_token_1_in: Optional[List[Any]] = Field( + alias="totalValueLockedToken1_in", default=None + ) + total_value_locked_token_1_not_in: Optional[List[Any]] = Field( + alias="totalValueLockedToken1_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["UniswapPool_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["UniswapPool_filter"]]] = Field( + alias="or", default=None + ) + + +class UniswapToken_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + address: Optional[Any] = None + address_not: Optional[Any] = None + address_gt: Optional[Any] = None + address_lt: Optional[Any] = None + address_gte: Optional[Any] = None + address_lte: Optional[Any] = None + address_in: Optional[List[Any]] = None + address_not_in: Optional[List[Any]] = None + address_contains: Optional[Any] = None + address_not_contains: Optional[Any] = None + name: Optional[str] = None + name_not: Optional[str] = None + name_gt: Optional[str] = None + name_lt: Optional[str] = None + name_gte: Optional[str] = None + name_lte: Optional[str] = None + name_in: Optional[List[str]] = None + name_not_in: Optional[List[str]] = None + name_contains: Optional[str] = None + name_contains_nocase: Optional[str] = None + name_not_contains: Optional[str] = None + name_not_contains_nocase: Optional[str] = None + name_starts_with: Optional[str] = None + name_starts_with_nocase: Optional[str] = None + name_not_starts_with: Optional[str] = None + name_not_starts_with_nocase: Optional[str] = None + name_ends_with: Optional[str] = None + name_ends_with_nocase: Optional[str] = None + name_not_ends_with: Optional[str] = None + name_not_ends_with_nocase: Optional[str] = None + symbol: Optional[str] = None + symbol_not: Optional[str] = None + symbol_gt: Optional[str] = None + symbol_lt: Optional[str] = None + symbol_gte: Optional[str] = None + symbol_lte: Optional[str] = None + symbol_in: Optional[List[str]] = None + symbol_not_in: Optional[List[str]] = None + symbol_contains: Optional[str] = None + symbol_contains_nocase: Optional[str] = None + symbol_not_contains: Optional[str] = None + symbol_not_contains_nocase: Optional[str] = None + symbol_starts_with: Optional[str] = None + symbol_starts_with_nocase: Optional[str] = None + symbol_not_starts_with: Optional[str] = None + symbol_not_starts_with_nocase: Optional[str] = None + symbol_ends_with: Optional[str] = None + symbol_ends_with_nocase: Optional[str] = None + symbol_not_ends_with: Optional[str] = None + symbol_not_ends_with_nocase: Optional[str] = None + decimals: Optional[int] = None + decimals_not: Optional[int] = None + decimals_gt: Optional[int] = None + decimals_lt: Optional[int] = None + decimals_gte: Optional[int] = None + decimals_lte: Optional[int] = None + decimals_in: Optional[List[int]] = None + decimals_not_in: Optional[List[int]] = None + price_eth: Optional[Any] = Field(alias="priceEth", default=None) + price_eth_not: Optional[Any] = Field(alias="priceEth_not", default=None) + price_eth_gt: Optional[Any] = Field(alias="priceEth_gt", default=None) + price_eth_lt: Optional[Any] = Field(alias="priceEth_lt", default=None) + price_eth_gte: Optional[Any] = Field(alias="priceEth_gte", default=None) + price_eth_lte: Optional[Any] = Field(alias="priceEth_lte", default=None) + price_eth_in: Optional[List[Any]] = Field(alias="priceEth_in", default=None) + price_eth_not_in: Optional[List[Any]] = Field(alias="priceEth_not_in", default=None) + price_usd: Optional[Any] = Field(alias="priceUsd", default=None) + price_usd_not: Optional[Any] = Field(alias="priceUsd_not", default=None) + price_usd_gt: Optional[Any] = Field(alias="priceUsd_gt", default=None) + price_usd_lt: Optional[Any] = Field(alias="priceUsd_lt", default=None) + price_usd_gte: Optional[Any] = Field(alias="priceUsd_gte", default=None) + price_usd_lte: Optional[Any] = Field(alias="priceUsd_lte", default=None) + price_usd_in: Optional[List[Any]] = Field(alias="priceUsd_in", default=None) + price_usd_not_in: Optional[List[Any]] = Field(alias="priceUsd_not_in", default=None) + allowed_pools: Optional[List[str]] = Field(alias="allowedPools", default=None) + allowed_pools_not: Optional[List[str]] = Field( + alias="allowedPools_not", default=None + ) + allowed_pools_contains: Optional[List[str]] = Field( + alias="allowedPools_contains", default=None + ) + allowed_pools_contains_nocase: Optional[List[str]] = Field( + alias="allowedPools_contains_nocase", default=None + ) + allowed_pools_not_contains: Optional[List[str]] = Field( + alias="allowedPools_not_contains", default=None + ) + allowed_pools_not_contains_nocase: Optional[List[str]] = Field( + alias="allowedPools_not_contains_nocase", default=None + ) + allowed_pools: Optional["UniswapToken_filter"] = Field( + alias="allowedPools_", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["UniswapToken_filter"]]] = Field( + alias="and", default=None + ) + or_: Optional[List[Optional["UniswapToken_filter"]]] = Field( + alias="or", default=None + ) + + +class User_filter(BaseModel): + id: Optional[str] = None + id_not: Optional[str] = None + id_gt: Optional[str] = None + id_lt: Optional[str] = None + id_gte: Optional[str] = None + id_lte: Optional[str] = None + id_in: Optional[List[str]] = None + id_not_in: Optional[List[str]] = None + address: Optional[Any] = None + address_not: Optional[Any] = None + address_gt: Optional[Any] = None + address_lt: Optional[Any] = None + address_gte: Optional[Any] = None + address_lte: Optional[Any] = None + address_in: Optional[List[Any]] = None + address_not_in: Optional[List[Any]] = None + address_contains: Optional[Any] = None + address_not_contains: Optional[Any] = None + first_trade_timestamp: Optional[int] = Field( + alias="firstTradeTimestamp", default=None + ) + first_trade_timestamp_not: Optional[int] = Field( + alias="firstTradeTimestamp_not", default=None + ) + first_trade_timestamp_gt: Optional[int] = Field( + alias="firstTradeTimestamp_gt", default=None + ) + first_trade_timestamp_lt: Optional[int] = Field( + alias="firstTradeTimestamp_lt", default=None + ) + first_trade_timestamp_gte: Optional[int] = Field( + alias="firstTradeTimestamp_gte", default=None + ) + first_trade_timestamp_lte: Optional[int] = Field( + alias="firstTradeTimestamp_lte", default=None + ) + first_trade_timestamp_in: Optional[List[int]] = Field( + alias="firstTradeTimestamp_in", default=None + ) + first_trade_timestamp_not_in: Optional[List[int]] = Field( + alias="firstTradeTimestamp_not_in", default=None + ) + orders_placed: Optional["Order_filter"] = Field(alias="ordersPlaced_", default=None) + is_solver: Optional[bool] = Field(alias="isSolver", default=None) + is_solver_not: Optional[bool] = Field(alias="isSolver_not", default=None) + is_solver_in: Optional[List[bool]] = Field(alias="isSolver_in", default=None) + is_solver_not_in: Optional[List[bool]] = Field( + alias="isSolver_not_in", default=None + ) + number_of_trades: Optional[int] = Field(alias="numberOfTrades", default=None) + number_of_trades_not: Optional[int] = Field( + alias="numberOfTrades_not", default=None + ) + number_of_trades_gt: Optional[int] = Field(alias="numberOfTrades_gt", default=None) + number_of_trades_lt: Optional[int] = Field(alias="numberOfTrades_lt", default=None) + number_of_trades_gte: Optional[int] = Field( + alias="numberOfTrades_gte", default=None + ) + number_of_trades_lte: Optional[int] = Field( + alias="numberOfTrades_lte", default=None + ) + number_of_trades_in: Optional[List[int]] = Field( + alias="numberOfTrades_in", default=None + ) + number_of_trades_not_in: Optional[List[int]] = Field( + alias="numberOfTrades_not_in", default=None + ) + solved_amount_eth: Optional[Any] = Field(alias="solvedAmountEth", default=None) + solved_amount_eth_not: Optional[Any] = Field( + alias="solvedAmountEth_not", default=None + ) + solved_amount_eth_gt: Optional[Any] = Field( + alias="solvedAmountEth_gt", default=None + ) + solved_amount_eth_lt: Optional[Any] = Field( + alias="solvedAmountEth_lt", default=None + ) + solved_amount_eth_gte: Optional[Any] = Field( + alias="solvedAmountEth_gte", default=None + ) + solved_amount_eth_lte: Optional[Any] = Field( + alias="solvedAmountEth_lte", default=None + ) + solved_amount_eth_in: Optional[List[Any]] = Field( + alias="solvedAmountEth_in", default=None + ) + solved_amount_eth_not_in: Optional[List[Any]] = Field( + alias="solvedAmountEth_not_in", default=None + ) + solved_amount_usd: Optional[Any] = Field(alias="solvedAmountUsd", default=None) + solved_amount_usd_not: Optional[Any] = Field( + alias="solvedAmountUsd_not", default=None + ) + solved_amount_usd_gt: Optional[Any] = Field( + alias="solvedAmountUsd_gt", default=None + ) + solved_amount_usd_lt: Optional[Any] = Field( + alias="solvedAmountUsd_lt", default=None + ) + solved_amount_usd_gte: Optional[Any] = Field( + alias="solvedAmountUsd_gte", default=None + ) + solved_amount_usd_lte: Optional[Any] = Field( + alias="solvedAmountUsd_lte", default=None + ) + solved_amount_usd_in: Optional[List[Any]] = Field( + alias="solvedAmountUsd_in", default=None + ) + solved_amount_usd_not_in: Optional[List[Any]] = Field( + alias="solvedAmountUsd_not_in", default=None + ) + traded_amount_usd: Optional[Any] = Field(alias="tradedAmountUsd", default=None) + traded_amount_usd_not: Optional[Any] = Field( + alias="tradedAmountUsd_not", default=None + ) + traded_amount_usd_gt: Optional[Any] = Field( + alias="tradedAmountUsd_gt", default=None + ) + traded_amount_usd_lt: Optional[Any] = Field( + alias="tradedAmountUsd_lt", default=None + ) + traded_amount_usd_gte: Optional[Any] = Field( + alias="tradedAmountUsd_gte", default=None + ) + traded_amount_usd_lte: Optional[Any] = Field( + alias="tradedAmountUsd_lte", default=None + ) + traded_amount_usd_in: Optional[List[Any]] = Field( + alias="tradedAmountUsd_in", default=None + ) + traded_amount_usd_not_in: Optional[List[Any]] = Field( + alias="tradedAmountUsd_not_in", default=None + ) + traded_amount_eth: Optional[Any] = Field(alias="tradedAmountEth", default=None) + traded_amount_eth_not: Optional[Any] = Field( + alias="tradedAmountEth_not", default=None + ) + traded_amount_eth_gt: Optional[Any] = Field( + alias="tradedAmountEth_gt", default=None + ) + traded_amount_eth_lt: Optional[Any] = Field( + alias="tradedAmountEth_lt", default=None + ) + traded_amount_eth_gte: Optional[Any] = Field( + alias="tradedAmountEth_gte", default=None + ) + traded_amount_eth_lte: Optional[Any] = Field( + alias="tradedAmountEth_lte", default=None + ) + traded_amount_eth_in: Optional[List[Any]] = Field( + alias="tradedAmountEth_in", default=None + ) + traded_amount_eth_not_in: Optional[List[Any]] = Field( + alias="tradedAmountEth_not_in", default=None + ) + change_block: Optional["BlockChangedFilter"] = Field( + alias="_change_block", default=None + ) + and_: Optional[List[Optional["User_filter"]]] = Field(alias="and", default=None) + or_: Optional[List[Optional["User_filter"]]] = Field(alias="or", default=None) diff --git a/cow_py/subgraph/client/last_days_volume.py b/cow_py/subgraph/client/last_days_volume.py new file mode 100644 index 0000000..a27cdb9 --- /dev/null +++ b/cow_py/subgraph/client/last_days_volume.py @@ -0,0 +1,17 @@ +# Generated by ariadne-codegen +# Source: cow_py/subgraph/queries + +from typing import Any, List, Optional + +from pydantic import Field + +from .base_model import BaseModel + + +class LastDaysVolume(BaseModel): + daily_totals: List["LastDaysVolumeDailyTotals"] = Field(alias="dailyTotals") + + +class LastDaysVolumeDailyTotals(BaseModel): + timestamp: int + volume_usd: Optional[Any] = Field(alias="volumeUsd") diff --git a/cow_py/subgraph/client/last_hours_volume.py b/cow_py/subgraph/client/last_hours_volume.py new file mode 100644 index 0000000..bff2776 --- /dev/null +++ b/cow_py/subgraph/client/last_hours_volume.py @@ -0,0 +1,17 @@ +# Generated by ariadne-codegen +# Source: cow_py/subgraph/queries + +from typing import Any, List, Optional + +from pydantic import Field + +from .base_model import BaseModel + + +class LastHoursVolume(BaseModel): + hourly_totals: List["LastHoursVolumeHourlyTotals"] = Field(alias="hourlyTotals") + + +class LastHoursVolumeHourlyTotals(BaseModel): + timestamp: int + volume_usd: Optional[Any] = Field(alias="volumeUsd") diff --git a/cow_py/subgraph/client/subgraph_client.py b/cow_py/subgraph/client/subgraph_client.py new file mode 100644 index 0000000..4ed8ba8 --- /dev/null +++ b/cow_py/subgraph/client/subgraph_client.py @@ -0,0 +1,75 @@ +# Generated by ariadne-codegen +# Source: cow_py/subgraph/queries + +from typing import Any, Dict + +from .async_base_client import AsyncBaseClient +from .last_days_volume import LastDaysVolume +from .last_hours_volume import LastHoursVolume +from .totals import Totals + + +def gql(q: str) -> str: + return q + + +class SubgraphClient(AsyncBaseClient): + async def last_days_volume(self, days: int, **kwargs: Any) -> LastDaysVolume: + query = gql( + """ + query LastDaysVolume($days: Int!) { + dailyTotals(orderBy: timestamp, orderDirection: desc, first: $days) { + timestamp + volumeUsd + } + } + """ + ) + variables: Dict[str, object] = {"days": days} + response = await self.execute( + query=query, operation_name="LastDaysVolume", variables=variables, **kwargs + ) + data = self.get_data(response) + return LastDaysVolume.model_validate(data) + + async def last_hours_volume(self, hours: int, **kwargs: Any) -> LastHoursVolume: + query = gql( + """ + query LastHoursVolume($hours: Int!) { + hourlyTotals(orderBy: timestamp, orderDirection: desc, first: $hours) { + timestamp + volumeUsd + } + } + """ + ) + variables: Dict[str, object] = {"hours": hours} + response = await self.execute( + query=query, operation_name="LastHoursVolume", variables=variables, **kwargs + ) + data = self.get_data(response) + return LastHoursVolume.model_validate(data) + + async def totals(self, **kwargs: Any) -> Totals: + query = gql( + """ + query Totals { + totals { + tokens + orders + traders + settlements + volumeUsd + volumeEth + feesUsd + feesEth + } + } + """ + ) + variables: Dict[str, object] = {} + response = await self.execute( + query=query, operation_name="Totals", variables=variables, **kwargs + ) + data = self.get_data(response) + return Totals.model_validate(data) diff --git a/cow_py/subgraph/client/totals.py b/cow_py/subgraph/client/totals.py new file mode 100644 index 0000000..1686b3b --- /dev/null +++ b/cow_py/subgraph/client/totals.py @@ -0,0 +1,23 @@ +# Generated by ariadne-codegen +# Source: cow_py/subgraph/queries + +from typing import Any, List, Optional + +from pydantic import Field + +from .base_model import BaseModel + + +class Totals(BaseModel): + totals: List["TotalsTotals"] + + +class TotalsTotals(BaseModel): + tokens: Any + orders: Any + traders: Any + settlements: Any + volume_usd: Optional[Any] = Field(alias="volumeUsd") + volume_eth: Optional[Any] = Field(alias="volumeEth") + fees_usd: Optional[Any] = Field(alias="feesUsd") + fees_eth: Optional[Any] = Field(alias="feesEth") diff --git a/cow_py/subgraph/deployments.py b/cow_py/subgraph/deployments.py new file mode 100644 index 0000000..ce98ebc --- /dev/null +++ b/cow_py/subgraph/deployments.py @@ -0,0 +1,40 @@ +from dataclasses import dataclass +from enum import Enum + +from cow_py.common.chains import Chain + + +class SubgraphEnvironment(Enum): + PRODUCTION = "production" + STAGING = "staging" + + +SUBGRAPH_BASE_URL = "https://api.thegraph.com/subgraph/name/cowprotocol" + + +def build_subgraph_url( + chain: Chain = Chain.MAINNET, + env: SubgraphEnvironment = SubgraphEnvironment.PRODUCTION, +) -> str: + base_url = SUBGRAPH_BASE_URL + + network_suffix = "" if chain == Chain.MAINNET else "-gc" + env_suffix = "-" + env.value if env == SubgraphEnvironment.STAGING else "" + + if chain == Chain.SEPOLIA: + raise ValueError(f"Unsupported chain: {chain}") + + return f"{base_url}/cow{network_suffix}{env_suffix}" + + +@dataclass +class SubgraphConfig: + chain: Chain + + @property + def production(self) -> str: + return build_subgraph_url(self.chain, SubgraphEnvironment.PRODUCTION) + + @property + def staging(self) -> str: + return build_subgraph_url(self.chain, SubgraphEnvironment.STAGING) diff --git a/cow_py/subgraph/queries/lastDayVolume.gql b/cow_py/subgraph/queries/lastDayVolume.gql new file mode 100644 index 0000000..264c3b8 --- /dev/null +++ b/cow_py/subgraph/queries/lastDayVolume.gql @@ -0,0 +1,10 @@ +# /** +# * GraphQL query for the total volume over the last N days. +# * @param days The number of days to query. +# */ +query LastDaysVolume($days: Int!) { + dailyTotals(orderBy: timestamp, orderDirection: desc, first: $days) { + timestamp + volumeUsd + } +} diff --git a/cow_py/subgraph/queries/lastHoursVolume.gql b/cow_py/subgraph/queries/lastHoursVolume.gql new file mode 100644 index 0000000..882aa2e --- /dev/null +++ b/cow_py/subgraph/queries/lastHoursVolume.gql @@ -0,0 +1,10 @@ +# /** +# * GraphQL query for the total volume over the last N hours. +# * @param hours The number of hours to query. +# */ +query LastHoursVolume($hours: Int!) { + hourlyTotals(orderBy: timestamp, orderDirection: desc, first: $hours) { + timestamp + volumeUsd + } +} diff --git a/cow_py/subgraph/queries/totals.gql b/cow_py/subgraph/queries/totals.gql new file mode 100644 index 0000000..241aab9 --- /dev/null +++ b/cow_py/subgraph/queries/totals.gql @@ -0,0 +1,15 @@ +# /** +# * GraphQL query for the total number of tokens, orders, traders, settlements, volume, and fees. +# */ +query Totals { + totals { + tokens + orders + traders + settlements + volumeUsd + volumeEth + feesUsd + feesEth + } +} \ No newline at end of file diff --git a/tests/subgraph/__init__.py b/tests/subgraph/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/subgraph/client/test_subgraph_client.py b/tests/subgraph/client/test_subgraph_client.py new file mode 100644 index 0000000..6bc57e4 --- /dev/null +++ b/tests/subgraph/client/test_subgraph_client.py @@ -0,0 +1,466 @@ +import pytest +from pytest_httpx import HTTPXMock + +from cow_py.subgraph.client import SubgraphClient +from cow_py.subgraph.client.exceptions import GraphQLClientGraphQLMultiError +from cow_py.subgraph.client.last_days_volume import ( + LastDaysVolume, + LastDaysVolumeDailyTotals, +) +from cow_py.subgraph.client.last_hours_volume import ( + LastHoursVolume, + LastHoursVolumeHourlyTotals, +) +from cow_py.subgraph.client.totals import Totals, TotalsTotals + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "query_method, variables, mocked_response, expected_response", + [ + ( + "totals", + dict(), + { + "data": { + "totals": [ + { + "tokens": "192", + "orders": "365210", + "traders": "50731", + "settlements": "160092", + "volumeUsd": "49548634.23978489392550883815112596", + "volumeEth": "20349080.82753326160179174564685693", + "feesUsd": "1495.18088540037791409373835505834", + "feesEth": "632.7328748466552906975758491191759", + } + ] + } + }, + Totals( + totals=[ + TotalsTotals( + tokens="192", + orders="365210", + traders="50731", + settlements="160092", + volumeUsd="49548634.23978489392550883815112596", + volumeEth="20349080.82753326160179174564685693", + feesUsd="1495.18088540037791409373835505834", + feesEth="632.7328748466552906975758491191759", + ) + ] + ), + ), + ( + "last_days_volume", + dict(days=7), + { + "data": { + "dailyTotals": [ + { + "timestamp": "1651104000", + "volumeUsd": "32085.1639220805155999650325844739", + }, + { + "timestamp": "1651017600", + "volumeUsd": "34693.62007717297749801092930059675", + }, + { + "timestamp": "1650931200", + "volumeUsd": "33122.5365226034644783539316622137", + }, + { + "timestamp": "1650844800", + "volumeUsd": "44339.87713137673812392999146686429", + }, + { + "timestamp": "1650758400", + "volumeUsd": "33152.20678736108925012284114682263", + }, + { + "timestamp": "1650672000", + "volumeUsd": "74499.81969341967474378140565634503", + }, + { + "timestamp": "1650585600", + "volumeUsd": "55696.47839632449194353566942457261", + }, + ] + } + }, + LastDaysVolume( + dailyTotals=[ + LastDaysVolumeDailyTotals( + timestamp=1651104000, + volumeUsd="32085.1639220805155999650325844739", + ), + LastDaysVolumeDailyTotals( + timestamp=1651017600, + volumeUsd="34693.62007717297749801092930059675", + ), + LastDaysVolumeDailyTotals( + timestamp=1650931200, + volumeUsd="33122.5365226034644783539316622137", + ), + LastDaysVolumeDailyTotals( + timestamp=1650844800, + volumeUsd="44339.87713137673812392999146686429", + ), + LastDaysVolumeDailyTotals( + timestamp=1650758400, + volumeUsd="33152.20678736108925012284114682263", + ), + LastDaysVolumeDailyTotals( + timestamp=1650672000, + volumeUsd="74499.81969341967474378140565634503", + ), + LastDaysVolumeDailyTotals( + timestamp=1650585600, + volumeUsd="55696.47839632449194353566942457261", + ), + ] + ), + ), + ( + "last_hours_volume", + dict(hours=24), + { + "data": { + "hourlyTotals": [ + { + "timestamp": "1651186800", + "volumeUsd": "190.9404913756501392195019404899438", + }, + { + "timestamp": "1651183200", + "volumeUsd": "529.9946238000561779423929757743504", + }, + { + "timestamp": "1651179600", + "volumeUsd": "645.3587505699802324165958548720157", + }, + { + "timestamp": "1651176000", + "volumeUsd": "1708.483608648853800630669110444808", + }, + { + "timestamp": "1651172400", + "volumeUsd": "7121.457330823292680300996744986044", + }, + { + "timestamp": "1651168800", + "volumeUsd": "1821.297602760111978245784985569166", + }, + { + "timestamp": "1651165200", + "volumeUsd": "2785.484680212634326873580046251588", + }, + { + "timestamp": "1651161600", + "volumeUsd": "1969.469152211506355791899301692229", + }, + { + "timestamp": "1651158000", + "volumeUsd": "2162.897300873319012826008286358389", + }, + { + "timestamp": "1651154400", + "volumeUsd": "1513.553639465779399627761684465762", + }, + { + "timestamp": "1651150800", + "volumeUsd": "187.4730505008263524958136028913312", + }, + { + "timestamp": "1651147200", + "volumeUsd": "1003.733903282400166632845200890861", + }, + { + "timestamp": "1651143600", + "volumeUsd": "430.0861170487354094851133346726692", + }, + { + "timestamp": "1651140000", + "volumeUsd": "332.7800791403069749429589009477125", + }, + { + "timestamp": "1651136400", + "volumeUsd": "97.63235373438852625638744867165181", + }, + { + "timestamp": "1651132800", + "volumeUsd": "30.59818396279718981525514608110329", + }, + { + "timestamp": "1651129200", + "volumeUsd": "4891.57094852254524822966041865283", + }, + { + "timestamp": "1651125600", + "volumeUsd": "0.2822502035827220060153182158280592", + }, + { + "timestamp": "1651122000", + "volumeUsd": "2618.536314756480243120625177213215", + }, + { + "timestamp": "1651118400", + "volumeUsd": "188.6060152287524476251961231904293", + }, + { + "timestamp": "1651114800", + "volumeUsd": "1081.900497533608727191602938189487", + }, + { + "timestamp": "1651111200", + "volumeUsd": "189.2511347347182236433877630220942", + }, + { + "timestamp": "1651107600", + "volumeUsd": "443.7262478626930371100298278690119", + }, + { + "timestamp": "1651104000", + "volumeUsd": "240.7104588694898118223893758683719", + }, + ] + } + }, + LastHoursVolume( + hourlyTotals=[ + LastHoursVolumeHourlyTotals( + timestamp=1651186800, + volumeUsd="190.9404913756501392195019404899438", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651183200, + volumeUsd="529.9946238000561779423929757743504", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651179600, + volumeUsd="645.3587505699802324165958548720157", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651176000, + volumeUsd="1708.483608648853800630669110444808", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651172400, + volumeUsd="7121.457330823292680300996744986044", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651168800, + volumeUsd="1821.297602760111978245784985569166", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651165200, + volumeUsd="2785.484680212634326873580046251588", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651161600, + volumeUsd="1969.469152211506355791899301692229", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651158000, + volumeUsd="2162.897300873319012826008286358389", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651154400, + volumeUsd="1513.553639465779399627761684465762", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651150800, + volumeUsd="187.4730505008263524958136028913312", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651147200, + volumeUsd="1003.733903282400166632845200890861", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651143600, + volumeUsd="430.0861170487354094851133346726692", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651140000, + volumeUsd="332.7800791403069749429589009477125", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651136400, + volumeUsd="97.63235373438852625638744867165181", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651132800, + volumeUsd="30.59818396279718981525514608110329", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651129200, + volumeUsd="4891.57094852254524822966041865283", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651125600, + volumeUsd="0.2822502035827220060153182158280592", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651122000, + volumeUsd="2618.536314756480243120625177213215", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651118400, + volumeUsd="188.6060152287524476251961231904293", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651114800, + volumeUsd="1081.900497533608727191602938189487", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651111200, + volumeUsd="189.2511347347182236433877630220942", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651107600, + volumeUsd="443.7262478626930371100298278690119", + ), + LastHoursVolumeHourlyTotals( + timestamp=1651104000, + volumeUsd="240.7104588694898118223893758683719", + ), + ] + ), + ), + ], +) +async def test_subgraph_client_existing_queries( + httpx_mock: HTTPXMock, query_method, variables, mocked_response, expected_response +): + httpx_mock.add_response(json=mocked_response) + + client = SubgraphClient(url="https://test_url") + query = await getattr(client, query_method)(*variables) + + assert query == expected_response + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "query, mocked_response, expected_response", + [ + ( + """ + query TokensByVolume { + tokens(first: 5, orderBy: totalVolumeUsd, orderDirection: desc) { + address + symbol + totalVolumeUsd + priceUsd + } + } + """, + { + "data": { + "tokens": [ + { + "address": "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d", + "symbol": "WXDAI", + "totalVolumeUsd": "32889034.621839712648167717", + "priceUsd": "1", + }, + { + "address": "0xddafbb505ad214d7b80b1f830fccc89b60fb7a83", + "symbol": "USDC", + "totalVolumeUsd": "31296380.98818012532887553375630894", + "priceUsd": "0.9983008873217955125012875742815512", + }, + { + "address": "0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1", + "symbol": "WETH", + "totalVolumeUsd": "11313677.56406394346907690670613874", + "priceUsd": "2930.058705030603523831034274967579", + }, + { + "address": "0x4ecaba5870353805a9f068101a40e0f32ed605c6", + "symbol": "USDT", + "totalVolumeUsd": "9390602.813697073697851431730404586", + "priceUsd": "1.00560814041993584287438922806966", + }, + { + "address": "0x9c58bacc331c9aa871afd802db6379a98e80cedb", + "symbol": "GNO", + "totalVolumeUsd": "5667965.685777522243148118842046968", + "priceUsd": "327.4436350035803489070442497891915", + }, + ] + } + }, + { + "tokens": [ + { + "address": "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d", + "symbol": "WXDAI", + "totalVolumeUsd": "32889034.621839712648167717", + "priceUsd": "1", + }, + { + "address": "0xddafbb505ad214d7b80b1f830fccc89b60fb7a83", + "symbol": "USDC", + "totalVolumeUsd": "31296380.98818012532887553375630894", + "priceUsd": "0.9983008873217955125012875742815512", + }, + { + "address": "0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1", + "symbol": "WETH", + "totalVolumeUsd": "11313677.56406394346907690670613874", + "priceUsd": "2930.058705030603523831034274967579", + }, + { + "address": "0x4ecaba5870353805a9f068101a40e0f32ed605c6", + "symbol": "USDT", + "totalVolumeUsd": "9390602.813697073697851431730404586", + "priceUsd": "1.00560814041993584287438922806966", + }, + { + "address": "0x9c58bacc331c9aa871afd802db6379a98e80cedb", + "symbol": "GNO", + "totalVolumeUsd": "5667965.685777522243148118842046968", + "priceUsd": "327.4436350035803489070442497891915", + }, + ] + }, + ) + ], +) +async def test_subgraph_client_custom_query( + httpx_mock: HTTPXMock, query, mocked_response, expected_response +): + httpx_mock.add_response(json=mocked_response) + client = SubgraphClient(url="https://test_url") + response = await client.execute(query=query) + + assert client.get_data(response) == expected_response + + +@pytest.mark.asyncio +async def test_subgraph_client_invalid_query(httpx_mock: HTTPXMock): + httpx_mock.add_response( + json={ + "errors": [ + { + "locations": [{"line": 2, "column": 9}], + "message": "Type `Query` has no field `invalidQuery`", + } + ] + } + ) + query = """ + query InvalidQuery { + invalidQuery { + field1 + field2 + } + } + """ + client = SubgraphClient(url="https://test_url") + response = await client.execute(query=query) + + with pytest.raises(GraphQLClientGraphQLMultiError): + client.get_data(response) diff --git a/tests/subgraph/test_deployments.py b/tests/subgraph/test_deployments.py new file mode 100644 index 0000000..2fd097b --- /dev/null +++ b/tests/subgraph/test_deployments.py @@ -0,0 +1,44 @@ +import pytest + +from cow_py.common.chains import Chain +from cow_py.subgraph.deployments import ( + SUBGRAPH_BASE_URL, + SubgraphConfig, + SubgraphEnvironment, + build_subgraph_url, +) + + +def test_build_subgraph_url(): + assert ( + build_subgraph_url(Chain.MAINNET, SubgraphEnvironment.PRODUCTION) + == f"{SUBGRAPH_BASE_URL}/cow" + ) + assert ( + build_subgraph_url(Chain.MAINNET, SubgraphEnvironment.STAGING) + == f"{SUBGRAPH_BASE_URL}/cow-staging" + ) + assert ( + build_subgraph_url(Chain.GNOSIS, SubgraphEnvironment.PRODUCTION) + == f"{SUBGRAPH_BASE_URL}/cow-gc" + ) + assert ( + build_subgraph_url(Chain.GNOSIS, SubgraphEnvironment.STAGING) + == f"{SUBGRAPH_BASE_URL}/cow-gc-staging" + ) + + with pytest.raises(ValueError): + build_subgraph_url(Chain.SEPOLIA, SubgraphEnvironment.PRODUCTION) + + +def test_subgraph_config(): + mainnet_config = SubgraphConfig(Chain.MAINNET) + assert mainnet_config.production == f"{SUBGRAPH_BASE_URL}/cow" + assert mainnet_config.staging == f"{SUBGRAPH_BASE_URL}/cow-staging" + + gnosis_chain_config = SubgraphConfig(Chain.GNOSIS) + assert gnosis_chain_config.production == f"{SUBGRAPH_BASE_URL}/cow-gc" + assert gnosis_chain_config.staging == f"{SUBGRAPH_BASE_URL}/cow-gc-staging" + + with pytest.raises(ValueError): + SubgraphConfig(Chain.SEPOLIA).production From 9cf40215b16e88b25f000165456dfbcca2d10fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:39:53 -0300 Subject: [PATCH 09/30] add contract abis --- cow_py/contracts/abi/Milkman.json | 140 ++++++++++++++++++++++++++++++ cow_py/contracts/abi/__init__.py | 0 2 files changed, 140 insertions(+) create mode 100644 cow_py/contracts/abi/Milkman.json create mode 100644 cow_py/contracts/abi/__init__.py diff --git a/cow_py/contracts/abi/Milkman.json b/cow_py/contracts/abi/Milkman.json new file mode 100644 index 0000000..d8b0488 --- /dev/null +++ b/cow_py/contracts/abi/Milkman.json @@ -0,0 +1,140 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "orderContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "orderCreator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "fromToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "toToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "priceChecker", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "priceCheckerData", + "type": "bytes" + } + ], + "name": "SwapRequested", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { + "internalType": "contract IERC20", + "name": "fromToken", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "toToken", + "type": "address" + }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "address", "name": "priceChecker", "type": "address" }, + { "internalType": "bytes", "name": "priceCheckerData", "type": "bytes" } + ], + "name": "cancelSwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "fromToken", + "type": "address" + }, + { "internalType": "bytes32", "name": "_swapHash", "type": "bytes32" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "orderDigest", "type": "bytes32" }, + { "internalType": "bytes", "name": "encodedOrder", "type": "bytes" } + ], + "name": "isValidSignature", + "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { + "internalType": "contract IERC20", + "name": "fromToken", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "toToken", + "type": "address" + }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "address", "name": "priceChecker", "type": "address" }, + { "internalType": "bytes", "name": "priceCheckerData", "type": "bytes" } + ], + "name": "requestSwapExactTokensForTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "swapHash", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/cow_py/contracts/abi/__init__.py b/cow_py/contracts/abi/__init__.py new file mode 100644 index 0000000..e69de29 From e9a8acd7304b88a7c9128ebe3952468ae1431e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:40:04 -0300 Subject: [PATCH 10/30] add generated codegen --- cow_py/codegen/__generated__/ComposableCow.py | 145 ++++++++++++++++++ .../ExtensibleFallbackHandler.py | 103 +++++++++++++ cow_py/codegen/__generated__/Milkman.py | 73 +++++++++ cow_py/codegen/__generated__/TWAP.py | 95 ++++++++++++ 4 files changed, 416 insertions(+) create mode 100644 cow_py/codegen/__generated__/ComposableCow.py create mode 100644 cow_py/codegen/__generated__/ExtensibleFallbackHandler.py create mode 100644 cow_py/codegen/__generated__/Milkman.py create mode 100644 cow_py/codegen/__generated__/TWAP.py diff --git a/cow_py/codegen/__generated__/ComposableCow.py b/cow_py/codegen/__generated__/ComposableCow.py new file mode 100644 index 0000000..fdfcfa5 --- /dev/null +++ b/cow_py/codegen/__generated__/ComposableCow.py @@ -0,0 +1,145 @@ +from dataclasses import dataclass +from typing import List, Tuple + +from hexbytes import HexBytes + +from cow_py.codegen.components import ( + BaseContract, + BaseMixin, + ContractFactory, + FileAbiLoader, + get_abi_file, +) +from cow_py.common.chains import Chain + + +@dataclass +class IConditionalOrder_ConditionalOrderParams: + handler: str + salt: HexBytes + staticInput: HexBytes + + +@dataclass +class ComposableCoW_Proof: + location: int + data: HexBytes + + +@dataclass +class GPv2Order_Data: + sellToken: str + buyToken: str + receiver: str + sellAmount: int + buyAmount: int + validTo: int + appData: HexBytes + feeAmount: int + kind: HexBytes + partiallyFillable: bool + sellTokenBalance: HexBytes + buyTokenBalance: HexBytes + + +class ComposableCowMixin(BaseMixin): + def cabinet(self, str_arg_0: str, hexbytes_arg_0: HexBytes) -> HexBytes: + return self.call_contract_method("cabinet", str_arg_0, hexbytes_arg_0) + + def create( + self, params: IConditionalOrder_ConditionalOrderParams, dispatch: bool + ) -> None: + return self.call_contract_method( + "create", (params.handler, params.salt, params.staticInput), dispatch + ) + + def create_with_context( + self, + params: IConditionalOrder_ConditionalOrderParams, + factory: str, + data: HexBytes, + dispatch: bool, + ) -> None: + return self.call_contract_method( + "createWithContext", + (params.handler, params.salt, params.staticInput), + factory, + data, + dispatch, + ) + + def domain_separator(self) -> HexBytes: + return self.call_contract_method("domainSeparator") + + def get_tradeable_order_with_signature( + self, + owner: str, + params: IConditionalOrder_ConditionalOrderParams, + offchain_input: HexBytes, + proof: List[HexBytes], + ) -> Tuple[GPv2Order_Data, HexBytes]: + return self.call_contract_method( + "getTradeableOrderWithSignature", + owner, + (params.handler, params.salt, params.staticInput), + offchain_input, + proof, + ) + + def hash(self, params: IConditionalOrder_ConditionalOrderParams) -> HexBytes: + return self.call_contract_method( + "hash", (params.handler, params.salt, params.staticInput) + ) + + def is_valid_safe_signature( + self, + safe: str, + sender: str, + _hash: HexBytes, + _domain_separator: HexBytes, + hexbytes_arg_0: HexBytes, + encode_data: HexBytes, + payload: HexBytes, + ) -> HexBytes: + return self.call_contract_method( + "isValidSafeSignature", + safe, + sender, + _hash, + _domain_separator, + hexbytes_arg_0, + encode_data, + payload, + ) + + def remove(self, single_order_hash: HexBytes) -> None: + return self.call_contract_method("remove", single_order_hash) + + def roots(self, str_arg_0: str) -> HexBytes: + return self.call_contract_method("roots", str_arg_0) + + def set_root(self, root: HexBytes, proof: ComposableCoW_Proof) -> None: + return self.call_contract_method("setRoot", root, (proof.location, proof.data)) + + def set_root_with_context( + self, root: HexBytes, proof: ComposableCoW_Proof, factory: str, data: HexBytes + ) -> None: + return self.call_contract_method( + "setRootWithContext", root, (proof.location, proof.data), factory, data + ) + + def set_swap_guard(self, swap_guard: str) -> None: + return self.call_contract_method("setSwapGuard", swap_guard) + + def single_orders(self, str_arg_0: str, hexbytes_arg_0: HexBytes) -> bool: + return self.call_contract_method("singleOrders", str_arg_0, hexbytes_arg_0) + + def swap_guards(self, str_arg_0: str) -> str: + return self.call_contract_method("swapGuards", str_arg_0) + + +class ComposableCow(BaseContract, ComposableCowMixin): + def __init__(self, chain: Chain = Chain.MAINNET, address: str = ""): + abi_loader = FileAbiLoader(get_abi_file("ComposableCow")) + contract = ContractFactory.create("ComposableCow", chain, address, abi_loader) + super(ComposableCow, self).__init__(address, chain, abi=contract.ABI) diff --git a/cow_py/codegen/__generated__/ExtensibleFallbackHandler.py b/cow_py/codegen/__generated__/ExtensibleFallbackHandler.py new file mode 100644 index 0000000..2492af1 --- /dev/null +++ b/cow_py/codegen/__generated__/ExtensibleFallbackHandler.py @@ -0,0 +1,103 @@ +from typing import List + +from hexbytes import HexBytes + +from cow_py.codegen.components import ( + BaseContract, + BaseMixin, + ContractFactory, + FileAbiLoader, + get_abi_file, +) +from cow_py.common.chains import Chain + + +class ExtensibleFallbackHandlerMixin(BaseMixin): + def domain_verifiers(self, str_arg_0: str, hexbytes_arg_0: HexBytes) -> str: + return self.call_contract_method("domainVerifiers", str_arg_0, hexbytes_arg_0) + + def is_valid_signature(self, _hash: HexBytes, signature: HexBytes) -> HexBytes: + return self.call_contract_method("isValidSignature", _hash, signature) + + def on_erc_1155_batch_received( + self, + str_arg_0: str, + str_arg_1: str, + int_list_arg_0: List[int], + int_list_arg_1: List[int], + hexbytes_arg_0: HexBytes, + ) -> HexBytes: + return self.call_contract_method( + "onERC1155BatchReceived", + str_arg_0, + str_arg_1, + int_list_arg_0, + int_list_arg_1, + hexbytes_arg_0, + ) + + def on_erc_1155_received( + self, + str_arg_0: str, + str_arg_1: str, + int_arg_0: int, + int_arg_1: int, + hexbytes_arg_0: HexBytes, + ) -> HexBytes: + return self.call_contract_method( + "onERC1155Received", + str_arg_0, + str_arg_1, + int_arg_0, + int_arg_1, + hexbytes_arg_0, + ) + + def on_erc_721_received( + self, str_arg_0: str, str_arg_1: str, int_arg_0: int, hexbytes_arg_0: HexBytes + ) -> HexBytes: + return self.call_contract_method( + "onERC721Received", str_arg_0, str_arg_1, int_arg_0, hexbytes_arg_0 + ) + + def safe_interfaces(self, str_arg_0: str, hexbytes_arg_0: HexBytes) -> bool: + return self.call_contract_method("safeInterfaces", str_arg_0, hexbytes_arg_0) + + def safe_methods(self, str_arg_0: str, hexbytes_arg_0: HexBytes) -> HexBytes: + return self.call_contract_method("safeMethods", str_arg_0, hexbytes_arg_0) + + def set_domain_verifier( + self, domain_separator: HexBytes, new_verifier: str + ) -> None: + return self.call_contract_method( + "setDomainVerifier", domain_separator, new_verifier + ) + + def set_safe_method(self, selector: HexBytes, new_method: HexBytes) -> None: + return self.call_contract_method("setSafeMethod", selector, new_method) + + def set_supported_interface(self, interface_id: HexBytes, supported: bool) -> None: + return self.call_contract_method( + "setSupportedInterface", interface_id, supported + ) + + def set_supported_interface_batch( + self, _interface_id: HexBytes, handler_with_selectors: List[HexBytes] + ) -> None: + return self.call_contract_method( + "setSupportedInterfaceBatch", _interface_id, handler_with_selectors + ) + + def supports_interface(self, interface_id: HexBytes) -> bool: + return self.call_contract_method("supportsInterface", interface_id) + + +class ExtensibleFallbackHandler(BaseContract, ExtensibleFallbackHandlerMixin): + def __init__(self, chain: Chain = Chain.MAINNET, address: str = ""): + abi_loader = FileAbiLoader(get_abi_file("ExtensibleFallbackHandler")) + contract = ContractFactory.create( + "ExtensibleFallbackHandler", chain, address, abi_loader + ) + super(ExtensibleFallbackHandler, self).__init__( + address, chain, abi=contract.ABI + ) diff --git a/cow_py/codegen/__generated__/Milkman.py b/cow_py/codegen/__generated__/Milkman.py new file mode 100644 index 0000000..5a35cee --- /dev/null +++ b/cow_py/codegen/__generated__/Milkman.py @@ -0,0 +1,73 @@ +from hexbytes import HexBytes + +from cow_py.codegen.components import ( + BaseContract, + BaseMixin, + ContractFactory, + FileAbiLoader, + get_abi_file, +) +from cow_py.common.chains import Chain + + +class MilkmanMixin(BaseMixin): + def domain_separator(self) -> HexBytes: + return self.call_contract_method("DOMAIN_SEPARATOR") + + def cancel_swap( + self, + amount_in: int, + from_token: str, + to_token: str, + to: str, + price_checker: str, + price_checker_data: HexBytes, + ) -> None: + return self.call_contract_method( + "cancelSwap", + amount_in, + from_token, + to_token, + to, + price_checker, + price_checker_data, + ) + + def initialize(self, from_token: str, _swap_hash: HexBytes) -> None: + return self.call_contract_method("initialize", from_token, _swap_hash) + + def is_valid_signature( + self, order_digest: HexBytes, encoded_order: HexBytes + ) -> HexBytes: + return self.call_contract_method( + "isValidSignature", order_digest, encoded_order + ) + + def request_swap_exact_tokens_for_tokens( + self, + amount_in: int, + from_token: str, + to_token: str, + to: str, + price_checker: str, + price_checker_data: HexBytes, + ) -> None: + return self.call_contract_method( + "requestSwapExactTokensForTokens", + amount_in, + from_token, + to_token, + to, + price_checker, + price_checker_data, + ) + + def swap_hash(self) -> HexBytes: + return self.call_contract_method("swapHash") + + +class Milkman(BaseContract, MilkmanMixin): + def __init__(self, chain: Chain = Chain.MAINNET, address: str = ""): + abi_loader = FileAbiLoader(get_abi_file("Milkman")) + contract = ContractFactory.create("Milkman", chain, address, abi_loader) + super(Milkman, self).__init__(address, chain, abi=contract.ABI) diff --git a/cow_py/codegen/__generated__/TWAP.py b/cow_py/codegen/__generated__/TWAP.py new file mode 100644 index 0000000..877acc5 --- /dev/null +++ b/cow_py/codegen/__generated__/TWAP.py @@ -0,0 +1,95 @@ +from dataclasses import dataclass + +from hexbytes import HexBytes + +from cow_py.codegen.components import ( + BaseContract, + BaseMixin, + ContractFactory, + FileAbiLoader, + get_abi_file, +) +from cow_py.common.chains import Chain + + +@dataclass +class IConditionalOrder_ConditionalOrderParams: + handler: str + salt: HexBytes + staticInput: HexBytes + + +@dataclass +class GPv2Order_Data: + sellToken: str + buyToken: str + receiver: str + sellAmount: int + buyAmount: int + validTo: int + appData: HexBytes + feeAmount: int + kind: HexBytes + partiallyFillable: bool + sellTokenBalance: HexBytes + buyTokenBalance: HexBytes + + +class TWAPMixin(BaseMixin): + def get_tradeable_order( + self, + owner: str, + str_arg_0: str, + ctx: HexBytes, + static_input: HexBytes, + hexbytes_arg_0: HexBytes, + ) -> GPv2Order_Data: + return self.call_contract_method( + "getTradeableOrder", owner, str_arg_0, ctx, static_input, hexbytes_arg_0 + ) + + def supports_interface(self, interface_id: HexBytes) -> bool: + return self.call_contract_method("supportsInterface", interface_id) + + def verify( + self, + owner: str, + sender: str, + _hash: HexBytes, + domain_separator: HexBytes, + ctx: HexBytes, + static_input: HexBytes, + offchain_input: HexBytes, + gpv_2order_data_arg_0: GPv2Order_Data, + ) -> None: + return self.call_contract_method( + "verify", + owner, + sender, + _hash, + domain_separator, + ctx, + static_input, + offchain_input, + ( + gpv_2order_data_arg_0.sellToken, + gpv_2order_data_arg_0.buyToken, + gpv_2order_data_arg_0.receiver, + gpv_2order_data_arg_0.sellAmount, + gpv_2order_data_arg_0.buyAmount, + gpv_2order_data_arg_0.validTo, + gpv_2order_data_arg_0.appData, + gpv_2order_data_arg_0.feeAmount, + gpv_2order_data_arg_0.kind, + gpv_2order_data_arg_0.partiallyFillable, + gpv_2order_data_arg_0.sellTokenBalance, + gpv_2order_data_arg_0.buyTokenBalance, + ), + ) + + +class TWAP(BaseContract, TWAPMixin): + def __init__(self, chain: Chain = Chain.MAINNET, address: str = ""): + abi_loader = FileAbiLoader(get_abi_file("TWAP")) + contract = ContractFactory.create("TWAP", chain, address, abi_loader) + super(TWAP, self).__init__(address, chain, abi=contract.ABI) From ca26b908465df612286d8eb1fb9e6da0e52f7378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 25 Apr 2024 08:40:10 -0300 Subject: [PATCH 11/30] add generated order_book --- cow_py/order_book/generated/model.py | 711 +++++++++++++++++++++++++++ 1 file changed, 711 insertions(+) create mode 100644 cow_py/order_book/generated/model.py diff --git a/cow_py/order_book/generated/model.py b/cow_py/order_book/generated/model.py new file mode 100644 index 0000000..5f5188f --- /dev/null +++ b/cow_py/order_book/generated/model.py @@ -0,0 +1,711 @@ +# generated by datamodel-codegen: +# filename: https://raw.githubusercontent.com/cowprotocol/services/v2.245.1/crates/orderbook/openapi.yml +# timestamp: 2024-04-12T14:44:16+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, Field, RootModel + + +class TransactionHash(RootModel[str]): + root: str = Field( + ..., + description="32 byte digest encoded as a hex with `0x` prefix.", + examples=["0xd51f28edffcaaa76be4a22f6375ad289272c037f3cc072345676e88d92ced8b5"], + ) + + +class Address(RootModel[str]): + root: str = Field( + ..., + description="20 byte Ethereum address encoded as a hex with `0x` prefix.", + examples=["0x6810e776880c02933d47db1b9fc05908e5386b96"], + ) + + +class AppData(RootModel[str]): + root: str = Field( + ..., + description="The string encoding of a JSON object representing some `appData`. The\nformat of the JSON expected in the `appData` field is defined\n[here](https://github.com/cowprotocol/app-data).\n", + examples=['{"version":"0.9.0","metadata":{}}'], + ) + + +class AppDataHash(RootModel[str]): + root: str = Field( + ..., + description="32 bytes encoded as hex with `0x` prefix.\nIt's expected to be the hash of the stringified JSON object representing the `appData`.\n", + examples=["0x0000000000000000000000000000000000000000000000000000000000000000"], + ) + + +class AppDataObject(BaseModel): + fullAppData: Optional[AppData] = None + + +class BigUint(RootModel[str]): + root: str = Field( + ..., + description="A big unsigned integer encoded in decimal.", + examples=["1234567890"], + ) + + +class CallData(RootModel[str]): + root: str = Field( + ..., + description="Some `calldata` sent to a contract in a transaction encoded as a hex with `0x` prefix.", + examples=["0xca11da7a"], + ) + + +class TokenAmount(RootModel[str]): + root: str = Field( + ..., + description="Amount of a token. `uint256` encoded in decimal.", + examples=["1234567890"], + ) + + +class PlacementError(Enum): + QuoteNotFound = "QuoteNotFound" + ValidToTooFarInFuture = "ValidToTooFarInFuture" + PreValidationError = "PreValidationError" + + +class OnchainOrderData(BaseModel): + sender: Address = Field( + ..., + description="If orders are placed as on-chain orders, the owner of the order might\nbe a smart contract, but not the user placing the order. The\nactual user will be provided in this field.\n", + ) + placementError: Optional[PlacementError] = Field( + None, + description="Describes the error, if the order placement was not successful. This could\nhappen, for example, if the `validTo` is too high, or no valid quote was\nfound or generated.\n", + ) + + +class EthflowData(BaseModel): + refundTxHash: TransactionHash = Field( + ..., + description="Specifies in which transaction the order was refunded. If\nthis field is null the order was not yet refunded.\n", + ) + userValidTo: int = Field( + ..., + description="Describes the `validTo` of an order ethflow order.\n\n**NOTE**: For ethflow orders, the `validTo` encoded in the smart\ncontract is `type(uint256).max`.\n", + ) + + +class OrderKind(Enum): + buy = "buy" + sell = "sell" + + +class OrderClass(Enum): + market = "market" + limit = "limit" + liquidity = "liquidity" + + +class SellTokenSource(Enum): + erc20 = "erc20" + internal = "internal" + external = "external" + + +class BuyTokenDestination(Enum): + erc20 = "erc20" + internal = "internal" + + +class PriceQuality(Enum): + fast = "fast" + optimal = "optimal" + verified = "verified" + + +class OrderStatus(Enum): + presignaturePending = "presignaturePending" + open = "open" + fulfilled = "fulfilled" + cancelled = "cancelled" + expired = "expired" + + +class ProtocolAppData(BaseModel): + pass + + +class AuctionPrices(RootModel[Optional[Dict[str, BigUint]]]): + root: Optional[Dict[str, BigUint]] = None + + +class UID(RootModel[str]): + root: str = Field( + ..., + description="Unique identifier for the order: 56 bytes encoded as hex with `0x` prefix.\nBytes 0..32 are the order digest, bytes 30..52 the owner address and bytes\n52..56 the expiry (`validTo`) as a `uint32` unix epoch timestamp.\n", + examples=[ + "0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a" + ], + ) + + +class SigningScheme(Enum): + eip712 = "eip712" + ethsign = "ethsign" + presign = "presign" + eip1271 = "eip1271" + + +class EcdsaSigningScheme(Enum): + eip712 = "eip712" + ethsign = "ethsign" + + +class EcdsaSignature(RootModel[str]): + root: str = Field( + ..., + description="65 bytes encoded as hex with `0x` prefix. `r || s || v` from the spec.", + examples=[ + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + ) + + +class PreSignature(RootModel[str]): + root: str = Field( + ..., + description='Empty signature bytes. Used for "presign" signatures.', + examples=["0x"], + ) + + +class ErrorType(Enum): + DuplicatedOrder = "DuplicatedOrder" + QuoteNotFound = "QuoteNotFound" + InvalidQuote = "InvalidQuote" + MissingFrom = "MissingFrom" + WrongOwner = "WrongOwner" + InvalidEip1271Signature = "InvalidEip1271Signature" + InsufficientBalance = "InsufficientBalance" + InsufficientAllowance = "InsufficientAllowance" + InvalidSignature = "InvalidSignature" + InsufficientFee = "InsufficientFee" + SellAmountOverflow = "SellAmountOverflow" + TransferSimulationFailed = "TransferSimulationFailed" + ZeroAmount = "ZeroAmount" + IncompatibleSigningScheme = "IncompatibleSigningScheme" + TooManyLimitOrders_UnsupportedBuyTokenDestination = ( + "TooManyLimitOrders UnsupportedBuyTokenDestination" + ) + UnsupportedSellTokenSource = "UnsupportedSellTokenSource" + UnsupportedOrderType = "UnsupportedOrderType" + InsufficientValidTo = "InsufficientValidTo" + ExcessiveValidTo = "ExcessiveValidTo" + InvalidNativeSellToken = "InvalidNativeSellToken" + SameBuyAndSellToken = "SameBuyAndSellToken" + UnsupportedToken = "UnsupportedToken" + InvalidAppData = "InvalidAppData" + AppDataHashMismatch = "AppDataHashMismatch" + AppdataFromMismatch = "AppdataFromMismatch" + + +class OrderPostError(BaseModel): + errorType: ErrorType + description: str + + +class ErrorType1(Enum): + InvalidSignature = "InvalidSignature" + WrongOwner = "WrongOwner" + OrderNotFound = "OrderNotFound" + AlreadyCancelled = "AlreadyCancelled" + OrderFullyExecuted = "OrderFullyExecuted" + OrderExpired = "OrderExpired" + OnChainOrder = "OnChainOrder" + + +class OrderCancellationError(BaseModel): + errorType: ErrorType1 + description: str + + +class ErrorType2(Enum): + AlreadyCancelled = "AlreadyCancelled" + OrderFullyExecuted = "OrderFullyExecuted" + OrderExpired = "OrderExpired" + OnChainOrder = "OnChainOrder" + DuplicatedOrder = "DuplicatedOrder" + InsufficientFee = "InsufficientFee" + InsufficientAllowance = "InsufficientAllowance" + InsufficientBalance = "InsufficientBalance" + InsufficientValidTo = "InsufficientValidTo" + ExcessiveValidTo = "ExcessiveValidTo" + InvalidSignature = "InvalidSignature" + TransferSimulationFailed = "TransferSimulationFailed" + UnsupportedToken = "UnsupportedToken" + WrongOwner = "WrongOwner" + SameBuyAndSellToken = "SameBuyAndSellToken" + ZeroAmount = "ZeroAmount" + UnsupportedBuyTokenDestination = "UnsupportedBuyTokenDestination" + UnsupportedSellTokenSource = "UnsupportedSellTokenSource" + UnsupportedOrderType = "UnsupportedOrderType" + + +class ReplaceOrderError(BaseModel): + errorType: ErrorType2 + description: str + + +class ErrorType3(Enum): + UnsupportedToken = "UnsupportedToken" + ZeroAmount = "ZeroAmount" + UnsupportedOrderType = "UnsupportedOrderType" + + +class PriceEstimationError(BaseModel): + errorType: ErrorType3 + description: str + + +class OrderQuoteSideKindSell(Enum): + sell = "sell" + + +class OrderQuoteSideKindBuy(Enum): + buy = "buy" + + +class OrderQuoteValidity1(BaseModel): + validTo: Optional[int] = Field( + None, description="Unix timestamp (`uint32`) until which the order is valid." + ) + + +class OrderQuoteValidity2(BaseModel): + validFor: Optional[int] = Field( + None, + description="Number (`uint32`) of seconds that the order should be valid for.", + ) + + +class OrderQuoteValidity(RootModel[Union[OrderQuoteValidity1, OrderQuoteValidity2]]): + root: Union[OrderQuoteValidity1, OrderQuoteValidity2] = Field( + ..., description="The validity for the order." + ) + + +class Objective(BaseModel): + total: Optional[float] = Field( + None, description="The total objective value used for ranking solutions." + ) + surplus: Optional[float] = None + fees: Optional[float] = None + cost: Optional[float] = None + gas: Optional[int] = None + + +class Order1(BaseModel): + id: Optional[UID] = None + executedAmount: Optional[BigUint] = None + + +class SolverSettlement(BaseModel): + solver: Optional[str] = Field(None, description="Name of the solver.") + solverAddress: Optional[str] = Field( + None, + description="The address used by the solver to execute the settlement on-chain.\nThis field is missing for old settlements, the zero address has been used instead.\n", + ) + objective: Optional[Objective] = None + score: Optional[BigUint] = Field( + None, + description="The score of the current auction as defined in [CIP-20](https://snapshot.org/#/cow.eth/proposal/0x2d3f9bd1ea72dca84b03e97dda3efc1f4a42a772c54bd2037e8b62e7d09a491f).\nIt is `null` for old auctions.\n", + ) + clearingPrices: Optional[Dict[str, BigUint]] = Field( + None, + description="The prices of tokens for settled user orders as passed to the settlement contract.\n", + ) + orders: Optional[List[Order1]] = Field(None, description="Touched orders.") + callData: Optional[CallData] = Field( + None, + description="Transaction `calldata` that is executed on-chain if the settlement is executed.", + ) + uninternalizedCallData: Optional[CallData] = Field( + None, + description="Full `calldata` as generated from the original solver output.\n\nIt can be different from the executed transaction if part of the settlements are internalised\n(use internal liquidity in lieu of trading against on-chain liquidity).\n\nThis field is omitted in case it coincides with `callData`.\n", + ) + + +class NativePriceResponse(BaseModel): + price: Optional[float] = Field(None, description="Estimated price of the token.") + + +class TotalSurplus(BaseModel): + totalSurplus: Optional[str] = Field(None, description="The total surplus.") + + +class InteractionData(BaseModel): + target: Optional[Address] = None + value: Optional[TokenAmount] = None + call_data: Optional[List[CallData]] = Field( + None, description="The call data to be used for the interaction." + ) + + +class Surplus(BaseModel): + factor: float + max_volume_factor: float + + +class Volume(BaseModel): + factor: float + + +class FeePolicy(RootModel[Union[Surplus, Volume]]): + root: Union[Surplus, Volume] = Field( + ..., description="Defines the ways to calculate the protocol fee." + ) + + +class OrderParameters(BaseModel): + sellToken: Address = Field(..., description="ERC-20 token to be sold.") + buyToken: Address = Field(..., description="ERC-20 token to be bought.") + receiver: Optional[Address] = Field( + None, + description="An optional Ethereum address to receive the proceeds of the trade instead\nof the owner (i.e. the order signer).\n", + ) + sellAmount: TokenAmount = Field( + ..., description="Amount of `sellToken` to be sold in atoms." + ) + buyAmount: TokenAmount = Field( + ..., description="Amount of `buyToken` to be bought in atoms." + ) + validTo: int = Field( + ..., description="Unix timestamp (`uint32`) until which the order is valid." + ) + appData: AppDataHash + feeAmount: TokenAmount = Field( + ..., description="feeRatio * sellAmount + minimal_fee in atoms." + ) + kind: OrderKind = Field(..., description="The kind is either a buy or sell order.") + partiallyFillable: bool = Field( + ..., description="Is the order fill-or-kill or partially fillable?" + ) + sellTokenBalance: Optional[SellTokenSource] = "erc20" + buyTokenBalance: Optional[BuyTokenDestination] = "erc20" + signingScheme: Optional[SigningScheme] = "eip712" + + +class OrderMetaData(BaseModel): + creationDate: str = Field( + ..., + description="Creation time of the order. Encoded as ISO 8601 UTC.", + examples=["2020-12-03T18:35:18.814523Z"], + ) + class_: OrderClass = Field(..., alias="class") + owner: Address + uid: UID + availableBalance: Optional[TokenAmount] = Field( + None, + description="Unused field that is currently always set to `null` and will be removed in the future.\n", + ) + executedSellAmount: BigUint = Field( + ..., + description="The total amount of `sellToken` that has been executed for this order including fees.\n", + ) + executedSellAmountBeforeFees: BigUint = Field( + ..., + description="The total amount of `sellToken` that has been executed for this order without fees.\n", + ) + executedBuyAmount: BigUint = Field( + ..., + description="The total amount of `buyToken` that has been executed for this order.\n", + ) + executedFeeAmount: BigUint = Field( + ..., + description="The total amount of fees that have been executed for this order.", + ) + invalidated: bool = Field(..., description="Has this order been invalidated?") + status: OrderStatus = Field(..., description="Order status.") + fullFeeAmount: Optional[TokenAmount] = Field( + None, description="Amount that the signed fee would be without subsidies." + ) + isLiquidityOrder: Optional[bool] = Field( + None, + description="Liquidity orders are functionally the same as normal smart contract orders but are not\nplaced with the intent of actively getting traded. Instead they facilitate the\ntrade of normal orders by allowing them to be matched against liquidity orders which\nuses less gas and can have better prices than external liquidity.\n\nAs such liquidity orders will only be used in order to improve settlement of normal\norders. They should not be expected to be traded otherwise and should not expect to get\nsurplus.\n", + ) + ethflowData: Optional[EthflowData] = None + onchainUser: Optional[Address] = Field( + None, + description="This represents the actual trader of an on-chain order.\n\n### ethflow orders\n\nIn this case, the `owner` would be the `EthFlow` contract and *not* the actual trader.\n", + ) + onchainOrderData: Optional[OnchainOrderData] = Field( + None, + description="There is some data only available for orders that are placed on-chain. This data\ncan be found in this object.\n", + ) + executedSurplusFee: Optional[BigUint] = Field( + None, description="Surplus fee that the limit order was executed with." + ) + fullAppData: Optional[str] = Field( + None, + description="Full `appData`, which the contract-level `appData` is a hash of. See `OrderCreation`\nfor more information.\n", + ) + + +class CompetitionAuction(BaseModel): + orders: Optional[List[UID]] = Field( + None, description="The UIDs of the orders included in the auction.\n" + ) + prices: Optional[AuctionPrices] = None + + +class OrderCancellations(BaseModel): + orderUids: Optional[List[UID]] = Field( + None, description="UIDs of orders to cancel." + ) + signature: EcdsaSignature = Field( + ..., description="`OrderCancellation` signed by the owner." + ) + signingScheme: EcdsaSigningScheme + + +class OrderCancellation(BaseModel): + signature: EcdsaSignature = Field( + ..., description="OrderCancellation signed by owner" + ) + signingScheme: EcdsaSigningScheme + + +class Trade(BaseModel): + blockNumber: int = Field(..., description="Block in which trade occurred.") + logIndex: int = Field( + ..., description="Index in which transaction was included in block." + ) + orderUid: UID = Field(..., description="UID of the order matched by this trade.") + owner: Address = Field(..., description="Address of trader.") + sellToken: Address = Field(..., description="Address of token sold.") + buyToken: Address = Field(..., description="Address of token bought.") + sellAmount: TokenAmount = Field( + ..., + description="Total amount of `sellToken` that has been executed for this trade (including fees).", + ) + sellAmountBeforeFees: BigUint = Field( + ..., + description="The total amount of `sellToken` that has been executed for this order without fees.", + ) + buyAmount: TokenAmount = Field( + ..., description="Total amount of `buyToken` received in this trade." + ) + txHash: TransactionHash = Field( + ..., + description="Transaction hash of the corresponding settlement transaction containing the trade (if available).", + ) + + +class Signature(RootModel[Union[EcdsaSignature, PreSignature]]): + root: Union[EcdsaSignature, PreSignature] = Field(..., description="A signature.") + + +class OrderQuoteSide1(BaseModel): + kind: OrderQuoteSideKindSell + sellAmountBeforeFee: TokenAmount = Field( + ..., + description="The total amount that is available for the order. From this value, the fee\nis deducted and the buy amount is calculated.\n", + ) + + +class OrderQuoteSide2(BaseModel): + kind: OrderQuoteSideKindSell + sellAmountAfterFee: TokenAmount = Field( + ..., description="The `sellAmount` for the order." + ) + + +class OrderQuoteSide3(BaseModel): + kind: OrderQuoteSideKindBuy + buyAmountAfterFee: TokenAmount = Field( + ..., description="The `buyAmount` for the order." + ) + + +class OrderQuoteSide( + RootModel[Union[OrderQuoteSide1, OrderQuoteSide2, OrderQuoteSide3]] +): + root: Union[OrderQuoteSide1, OrderQuoteSide2, OrderQuoteSide3] = Field( + ..., description="The buy or sell side when quoting an order." + ) + + +class OrderQuoteRequest(BaseModel): + sellToken: Address = Field(..., description="ERC-20 token to be sold") + buyToken: Address = Field(..., description="ERC-20 token to be bought") + receiver: Optional[Address] = Field( + None, + description="An optional address to receive the proceeds of the trade instead of the\n`owner` (i.e. the order signer).\n", + ) + appData: Optional[Union[AppData, AppDataHash]] = Field( + None, + description="AppData which will be assigned to the order.\nExpects either a string JSON doc as defined on [AppData](https://github.com/cowprotocol/app-data) or a\nhex encoded string for backwards compatibility.\nWhen the first format is used, it's possible to provide the derived appDataHash field.\n", + ) + appDataHash: Optional[AppDataHash] = Field( + None, + description="The hash of the stringified JSON appData doc.\nIf present, `appData` field must be set with the aforementioned data where this hash is derived from.\nIn case they differ, the call will fail.\n", + ) + sellTokenBalance: Optional[SellTokenSource] = "erc20" + buyTokenBalance: Optional[BuyTokenDestination] = "erc20" + from_: Address = Field(..., alias="from") + priceQuality: Optional[PriceQuality] = "verified" + signingScheme: Optional[SigningScheme] = "eip712" + onchainOrder: Optional[Any] = Field( + False, + description='Flag to signal whether the order is intended for on-chain order placement. Only valid\nfor non ECDSA-signed orders."\n', + ) + + +class OrderQuoteResponse(BaseModel): + quote: OrderParameters + from_: Optional[Address] = Field(None, alias="from") + expiration: str = Field( + ..., + description="Expiration date of the offered fee. Order service might not accept\nthe fee after this expiration date. Encoded as ISO 8601 UTC.\n", + examples=["1985-03-10T18:35:18.814523Z"], + ) + id: Optional[int] = Field( + None, + description="Quote ID linked to a quote to enable providing more metadata when analysing\norder slippage.\n", + ) + verified: bool = Field( + ..., + description="Whether it was possible to verify that the quoted amounts are accurate using a simulation.\n", + ) + + +class SolverCompetitionResponse(BaseModel): + auctionId: Optional[int] = Field( + None, description="The ID of the auction the competition info is for." + ) + transactionHash: Optional[TransactionHash] = Field( + None, + description="The hash of the transaction that the winning solution of this info was submitted in.", + ) + gasPrice: Optional[float] = Field( + None, description="Gas price used for ranking solutions." + ) + liquidityCollectedBlock: Optional[int] = None + competitionSimulationBlock: Optional[int] = None + auction: Optional[CompetitionAuction] = None + solutions: Optional[List[SolverSettlement]] = Field( + None, + description="Maps from solver name to object describing that solver's settlement.", + ) + + +class OrderCreation(BaseModel): + sellToken: Address = Field(..., description="see `OrderParameters::sellToken`") + buyToken: Address = Field(..., description="see `OrderParameters::buyToken`") + receiver: Optional[Address] = Field( + None, description="see `OrderParameters::receiver`" + ) + sellAmount: TokenAmount = Field( + ..., description="see `OrderParameters::sellAmount`" + ) + buyAmount: TokenAmount = Field(..., description="see `OrderParameters::buyAmount`") + validTo: int = Field(..., description="see `OrderParameters::validTo`") + feeAmount: TokenAmount = Field(..., description="see `OrderParameters::feeAmount`") + kind: OrderKind = Field(..., description="see `OrderParameters::kind`") + partiallyFillable: bool = Field( + ..., description="see `OrderParameters::partiallyFillable`" + ) + sellTokenBalance: Optional[SellTokenSource] = Field( + "erc20", description="see `OrderParameters::sellTokenBalance`" + ) + buyTokenBalance: Optional[BuyTokenDestination] = Field( + "erc20", description="see `OrderParameters::buyTokenBalance`" + ) + signingScheme: SigningScheme + signature: Signature + from_: Optional[Address] = Field( + None, + alias="from", + description="If set, the backend enforces that this address matches what is decoded as the *signer* of\nthe signature. This helps catch errors with invalid signature encodings as the backend\nmight otherwise silently work with an unexpected address that for example does not have\nany balance.\n", + ) + quoteId: Optional[int] = Field( + None, + description="Orders can optionally include a quote ID. This way the order can be linked to a quote\nand enable providing more metadata when analysing order slippage.\n", + ) + appData: Union[AppData, AppDataHash] = Field( + ..., + description="This field comes in two forms for backward compatibility. The hash form will eventually \nstop being accepted.\n", + ) + appDataHash: Optional[AppDataHash] = Field( + None, + description="May be set for debugging purposes. If set, this field is compared to what the backend\ninternally calculates as the app data hash based on the contents of `appData`. If the\nhash does not match, an error is returned. If this field is set, then `appData` **MUST** be\na string encoding of a JSON object.\n", + ) + + +class Order(OrderCreation, OrderMetaData): + pass + + +class AuctionOrder(BaseModel): + uid: UID + sellToken: Address = Field(..., description="see `OrderParameters::sellToken`") + buyToken: Address = Field(..., description="see `OrderParameters::buyToken`") + sellAmount: TokenAmount = Field( + ..., description="see `OrderParameters::sellAmount`" + ) + buyAmount: TokenAmount = Field(..., description="see `OrderParameters::buyAmount`") + userFee: TokenAmount = Field(..., description="see `OrderParameters::feeAmount`") + validTo: int = Field(..., description="see `OrderParameters::validTo`") + kind: OrderKind = Field(..., description="see `OrderParameters::kind`") + receiver: Address = Field(..., description="see `OrderParameters::receiver`") + owner: Address + partiallyFillable: bool = Field( + ..., description="see `OrderParameters::partiallyFillable`" + ) + executed: TokenAmount = Field( + ..., + description="Currently executed amount of sell/buy token, depending on the order kind.\n", + ) + preInteractions: List[InteractionData] = Field( + ..., + description="The pre-interactions that need to be executed before the first execution of the order.\n", + ) + postInteractions: List[InteractionData] = Field( + ..., + description="The post-interactions that need to be executed after the execution of the order.\n", + ) + sellTokenBalance: SellTokenSource = Field( + ..., description="see `OrderParameters::sellTokenBalance`" + ) + buyTokenBalance: BuyTokenDestination = Field( + ..., description="see `OrderParameters::buyTokenBalance`" + ) + class_: OrderClass = Field(..., alias="class") + appData: AppDataHash + signature: Signature + protocolFees: List[FeePolicy] = Field( + ..., + description="The fee policies that are used to compute the protocol fees for this order.\n", + ) + + +class Auction(BaseModel): + id: Optional[int] = Field( + None, + description="The unique identifier of the auction. Increment whenever the backend creates a new auction.\n", + ) + block: Optional[int] = Field( + None, + description="The block number for the auction. Orders and prices are guaranteed to be valid on this\nblock. Proposed settlements should be valid for this block as well.\n", + ) + latestSettlementBlock: Optional[int] = Field( + None, + description="The latest block on which a settlement has been processed.\n\n**NOTE**: Under certain conditions it is possible for a settlement to have been mined as\npart of `block` but not have yet been processed.\n", + ) + orders: Optional[List[AuctionOrder]] = Field( + None, description="The solvable orders included in the auction.\n" + ) + prices: Optional[AuctionPrices] = None From 25ae6a5b040544b71a6ca6fb1019c38b4a9d2da1 Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Thu, 15 Aug 2024 18:55:44 -0300 Subject: [PATCH 12/30] add downloaded schemas --- cow_py/app_data/schemas/definitions.json | 45 ++++++++++++ cow_py/app_data/schemas/hook/v0.1.0.json | 35 ++++++++++ cow_py/app_data/schemas/hooks/v0.1.0.json | 35 ++++++++++ .../app_data/schemas/orderClass/v0.1.0.json | 33 +++++++++ .../app_data/schemas/orderClass/v0.2.0.json | 35 ++++++++++ .../app_data/schemas/orderClass/v0.3.0.json | 30 ++++++++ .../app_data/schemas/partnerFee/v0.1.0.json | 20 ++++++ cow_py/app_data/schemas/quote/v0.1.0.json | 34 +++++++++ cow_py/app_data/schemas/quote/v0.2.0.json | 22 ++++++ cow_py/app_data/schemas/quote/v0.3.0.json | 18 +++++ cow_py/app_data/schemas/quote/v1.0.0.json | 15 ++++ cow_py/app_data/schemas/referrer/v0.1.0.json | 21 ++++++ cow_py/app_data/schemas/referrer/v0.2.0.json | 16 +++++ .../schemas/replacedOrder/v0.1.0.json | 15 ++++ cow_py/app_data/schemas/signer/v0.1.0.json | 7 ++ cow_py/app_data/schemas/utm/v0.1.0.json | 54 +++++++++++++++ cow_py/app_data/schemas/utm/v0.2.0.json | 55 +++++++++++++++ cow_py/app_data/schemas/v0.1.0.json | 40 +++++++++++ cow_py/app_data/schemas/v0.10.0.json | 69 +++++++++++++++++++ cow_py/app_data/schemas/v0.11.0.json | 62 +++++++++++++++++ cow_py/app_data/schemas/v0.2.0.json | 43 ++++++++++++ cow_py/app_data/schemas/v0.3.0.json | 55 +++++++++++++++ cow_py/app_data/schemas/v0.4.0.json | 55 +++++++++++++++ cow_py/app_data/schemas/v0.5.0.json | 58 ++++++++++++++++ cow_py/app_data/schemas/v0.6.0.json | 61 ++++++++++++++++ cow_py/app_data/schemas/v0.7.0.json | 61 ++++++++++++++++ cow_py/app_data/schemas/v0.8.0.json | 63 +++++++++++++++++ cow_py/app_data/schemas/v0.9.0.json | 66 ++++++++++++++++++ cow_py/app_data/schemas/v1.0.0.json | 65 +++++++++++++++++ cow_py/app_data/schemas/v1.1.0.json | 68 ++++++++++++++++++ cow_py/app_data/schemas/widget/v0.1.0.json | 24 +++++++ 31 files changed, 1280 insertions(+) create mode 100644 cow_py/app_data/schemas/definitions.json create mode 100644 cow_py/app_data/schemas/hook/v0.1.0.json create mode 100644 cow_py/app_data/schemas/hooks/v0.1.0.json create mode 100644 cow_py/app_data/schemas/orderClass/v0.1.0.json create mode 100644 cow_py/app_data/schemas/orderClass/v0.2.0.json create mode 100644 cow_py/app_data/schemas/orderClass/v0.3.0.json create mode 100644 cow_py/app_data/schemas/partnerFee/v0.1.0.json create mode 100644 cow_py/app_data/schemas/quote/v0.1.0.json create mode 100644 cow_py/app_data/schemas/quote/v0.2.0.json create mode 100644 cow_py/app_data/schemas/quote/v0.3.0.json create mode 100644 cow_py/app_data/schemas/quote/v1.0.0.json create mode 100644 cow_py/app_data/schemas/referrer/v0.1.0.json create mode 100644 cow_py/app_data/schemas/referrer/v0.2.0.json create mode 100644 cow_py/app_data/schemas/replacedOrder/v0.1.0.json create mode 100644 cow_py/app_data/schemas/signer/v0.1.0.json create mode 100644 cow_py/app_data/schemas/utm/v0.1.0.json create mode 100644 cow_py/app_data/schemas/utm/v0.2.0.json create mode 100644 cow_py/app_data/schemas/v0.1.0.json create mode 100644 cow_py/app_data/schemas/v0.10.0.json create mode 100644 cow_py/app_data/schemas/v0.11.0.json create mode 100644 cow_py/app_data/schemas/v0.2.0.json create mode 100644 cow_py/app_data/schemas/v0.3.0.json create mode 100644 cow_py/app_data/schemas/v0.4.0.json create mode 100644 cow_py/app_data/schemas/v0.5.0.json create mode 100644 cow_py/app_data/schemas/v0.6.0.json create mode 100644 cow_py/app_data/schemas/v0.7.0.json create mode 100644 cow_py/app_data/schemas/v0.8.0.json create mode 100644 cow_py/app_data/schemas/v0.9.0.json create mode 100644 cow_py/app_data/schemas/v1.0.0.json create mode 100644 cow_py/app_data/schemas/v1.1.0.json create mode 100644 cow_py/app_data/schemas/widget/v0.1.0.json diff --git a/cow_py/app_data/schemas/definitions.json b/cow_py/app_data/schemas/definitions.json new file mode 100644 index 0000000..bdefabf --- /dev/null +++ b/cow_py/app_data/schemas/definitions.json @@ -0,0 +1,45 @@ +{ + "$id": "#definitions.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Definitions sub-schema", + "definitions": { + "version": { + "$id": "#/definitions/version", + "description": "Semantic versioning of document.", + "readOnly": true, + "examples": ["1.0.0", "1.2.3"], + "title": "Version", + "type": "string" + }, + "ethereumAddress": { + "$id": "#/definitions/ethereumAddress", + "pattern": "^0x[a-fA-F0-9]{40}$", + "title": "Ethereum compatible address", + "examples": ["0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9"], + "type": "string" + }, + "bigNumber": { + "$id": "#/definitions/bigNumber", + "pattern": "^\\d+$", + "title": "BigNumber", + "examples": ["90741097240912730913", "0", "75891372"], + "type": "string" + }, + "bps": { + "$id": "#/definitions/bps", + "title": "Basis Point (BPS)", + "description": "One basis point is equivalent to 0.01% (1/100th of a percent)", + "examples": [0, 10, 50], + "type": "integer", + "maximum": 10000, + "minimum": 0 + }, + "orderUid": { + "$id": "#/definitions/orderUid", + "pattern": "^0x[a-fA-F0-9]{112}$", + "title": "Order UID", + "examples": ["0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a"], + "type": "string" + } + } +} diff --git a/cow_py/app_data/schemas/hook/v0.1.0.json b/cow_py/app_data/schemas/hook/v0.1.0.json new file mode 100644 index 0000000..28c1571 --- /dev/null +++ b/cow_py/app_data/schemas/hook/v0.1.0.json @@ -0,0 +1,35 @@ +{ + "$id": "#hook/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [ + "target", + "callData", + "gasLimit" + ], + "title": "CoW Hook", + "type": "object", + "additionalProperties": false, + "properties": { + "target": { + "$ref": "../definitions.json#/definitions/ethereumAddress", + "title": "Hook Target", + "description": "The contract to call for the hook" + }, + "callData": { + "$id": "#/properties/callData", + "title": "Hook CallData", + "description": "The calldata to use when calling the hook", + "type": "string", + "pattern": "^0x[a-fA-F0-9]*$", + "examples": [ + "0x", + "0x01020304" + ] + }, + "gasLimit": { + "$ref": "../definitions.json#/definitions/bigNumber", + "title": "Hook Gas Limit", + "description": "The gas limit (in gas units) for the hook" + } + } +} diff --git a/cow_py/app_data/schemas/hooks/v0.1.0.json b/cow_py/app_data/schemas/hooks/v0.1.0.json new file mode 100644 index 0000000..37a1c6e --- /dev/null +++ b/cow_py/app_data/schemas/hooks/v0.1.0.json @@ -0,0 +1,35 @@ +{ + "$id": "#hooks/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "default": {}, + "required": [], + "title": "Order interaction hooks", + "description": "Optional Pre and Post order interaction hooks attached to a single order", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "../definitions.json#/definitions/version", + "readOnly": true, + "default": "0.1.0" + }, + "pre": { + "$id": "#/properties/hooks/pre", + "title": "Pre-Hooks", + "description": "CoW Hooks to call before an order executes", + "type": "array", + "items": { + "$ref": "../hook/v0.1.0.json#" + } + }, + "post": { + "$id": "#/properties/hooks/post", + "title": "Post-Hooks", + "description": "CoW Hooks to call after an order executes", + "type": "array", + "items": { + "$ref": "../hook/v0.1.0.json#" + } + } + } +} diff --git a/cow_py/app_data/schemas/orderClass/v0.1.0.json b/cow_py/app_data/schemas/orderClass/v0.1.0.json new file mode 100644 index 0000000..dc97fd7 --- /dev/null +++ b/cow_py/app_data/schemas/orderClass/v0.1.0.json @@ -0,0 +1,33 @@ +{ + "$id": "#orderClass/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [ + "version", + "orderClass" + ], + "title": "Order class", + "type": "object", + "properties": { + "version": { + "$ref": "../definitions.json#/definitions/version", + "readOnly": true, + "default": "0.1.0" + }, + "orderClass": { + "$id": "#/definitions/orderClass", + "description": "Indicator of the order class.", + "examples": [ + "market", + "limit", + "liquidity" + ], + "title": "Order class", + "type": "string", + "enum": [ + "market", + "limit", + "liquidity" + ] + } + } +} diff --git a/cow_py/app_data/schemas/orderClass/v0.2.0.json b/cow_py/app_data/schemas/orderClass/v0.2.0.json new file mode 100644 index 0000000..4a09255 --- /dev/null +++ b/cow_py/app_data/schemas/orderClass/v0.2.0.json @@ -0,0 +1,35 @@ +{ + "$id": "#orderClass/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [ + "version", + "orderClass" + ], + "title": "Order class", + "type": "object", + "properties": { + "version": { + "$ref": "../definitions.json#/definitions/version", + "readOnly": true, + "default": "0.1.0" + }, + "orderClass": { + "$id": "#/definitions/orderClass", + "description": "Indicator of the order class.", + "examples": [ + "market", + "limit", + "liquidity", + "twap" + ], + "title": "Order class", + "type": "string", + "enum": [ + "market", + "limit", + "liquidity", + "twap" + ] + } + } +} diff --git a/cow_py/app_data/schemas/orderClass/v0.3.0.json b/cow_py/app_data/schemas/orderClass/v0.3.0.json new file mode 100644 index 0000000..7491429 --- /dev/null +++ b/cow_py/app_data/schemas/orderClass/v0.3.0.json @@ -0,0 +1,30 @@ +{ + "$id": "#orderClass/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [ + "orderClass" + ], + "title": "Order class", + "type": "object", + "additionalProperties": false, + "properties": { + "orderClass": { + "$id": "#/definitions/orderClass", + "description": "Indicator of the order class.", + "examples": [ + "market", + "limit", + "liquidity", + "twap" + ], + "title": "Order class", + "type": "string", + "enum": [ + "market", + "limit", + "liquidity", + "twap" + ] + } + } +} diff --git a/cow_py/app_data/schemas/partnerFee/v0.1.0.json b/cow_py/app_data/schemas/partnerFee/v0.1.0.json new file mode 100644 index 0000000..ea8eb5a --- /dev/null +++ b/cow_py/app_data/schemas/partnerFee/v0.1.0.json @@ -0,0 +1,20 @@ +{ + "$id": "#partnerFee/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["bps", "recipient"], + "title": "Partner fee", + "type": "object", + "additionalProperties": false, + "properties": { + "bps": { + "title": "Basis Point (BPS)", + "description": "The fee in basis points (BPS) to be paid to the partner. One basis point is equivalent to 0.01% (1/100th of a percent)", + "$ref": "../definitions.json#/definitions/bps" + }, + "recipient": { + "title": "Partner account", + "description": "The Ethereum address of the partner to receive the fee.", + "$ref": "../definitions.json#/definitions/ethereumAddress" + } + } +} diff --git a/cow_py/app_data/schemas/quote/v0.1.0.json b/cow_py/app_data/schemas/quote/v0.1.0.json new file mode 100644 index 0000000..905399f --- /dev/null +++ b/cow_py/app_data/schemas/quote/v0.1.0.json @@ -0,0 +1,34 @@ +{ + "$id": "#quote/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [ + "sellAmount", + "buyAmount", + "version" + ], + "title": "Quote", + "type": "object", + "properties": { + "id": { + "$id": "#/properties/id", + "title": "Quote id", + "examples": [ + "XA23443543534FF" + ], + "type": "string" + }, + "sellAmount": { + "$ref": "../definitions.json#/definitions/bigNumber", + "title": "Quote sell amount" + }, + "buyAmount": { + "$ref": "../definitions.json#/definitions/bigNumber", + "title": "Quote buy amount" + }, + "version": { + "$ref": "../definitions.json#/definitions/version", + "readOnly": true, + "default": "0.1.0" + } + } +} diff --git a/cow_py/app_data/schemas/quote/v0.2.0.json b/cow_py/app_data/schemas/quote/v0.2.0.json new file mode 100644 index 0000000..62bc155 --- /dev/null +++ b/cow_py/app_data/schemas/quote/v0.2.0.json @@ -0,0 +1,22 @@ +{ + "$id": "#quote/v0.2.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["version", "slippageBips"], + "title": "Quote", + "type": "object", + "properties": { + "version": { + "$ref": "../definitions.json#/definitions/version", + "readOnly": true, + "default": "0.2.0" + }, + "slippageBips": { + "$id": "#/properties/slippageBips", + "title": "Slippage Bips", + "description": "Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BPS)", + "examples": ["5", "10", "20", "100"], + "pattern": "^\\d+(\\.\\d+)?$", + "type": "string" + } + } +} diff --git a/cow_py/app_data/schemas/quote/v0.3.0.json b/cow_py/app_data/schemas/quote/v0.3.0.json new file mode 100644 index 0000000..8159205 --- /dev/null +++ b/cow_py/app_data/schemas/quote/v0.3.0.json @@ -0,0 +1,18 @@ +{ + "$id": "#quote/v0.2.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["slippageBips"], + "title": "Quote", + "type": "object", + "additionalProperties": false, + "properties": { + "slippageBips": { + "$id": "#/properties/slippageBips", + "title": "Slippage Bips", + "description": "Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BPS)", + "examples": ["5", "10", "20", "100"], + "pattern": "^\\d+(\\.\\d+)?$", + "type": "string" + } + } +} diff --git a/cow_py/app_data/schemas/quote/v1.0.0.json b/cow_py/app_data/schemas/quote/v1.0.0.json new file mode 100644 index 0000000..6500a00 --- /dev/null +++ b/cow_py/app_data/schemas/quote/v1.0.0.json @@ -0,0 +1,15 @@ +{ + "$id": "#quote/v0.2.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["slippageBips"], + "title": "Quote", + "type": "object", + "additionalProperties": false, + "properties": { + "slippageBips": { + "title": "Slippage Bips", + "description": "Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BPS). One basis point is equivalent to 0.01% (1/100th of a percent)", + "$ref": "../definitions.json#/definitions/bps" + } + } +} diff --git a/cow_py/app_data/schemas/referrer/v0.1.0.json b/cow_py/app_data/schemas/referrer/v0.1.0.json new file mode 100644 index 0000000..6421619 --- /dev/null +++ b/cow_py/app_data/schemas/referrer/v0.1.0.json @@ -0,0 +1,21 @@ +{ + "$id": "#referrer/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [ + "version", + "address" + ], + "title": "Referrer", + "type": "object", + "properties": { + "version": { + "$ref": "../definitions.json#/definitions/version", + "readOnly": true, + "default": "0.1.0" + }, + "address": { + "$ref": "../definitions.json#/definitions/ethereumAddress", + "title": "Referrer address" + } + } +} diff --git a/cow_py/app_data/schemas/referrer/v0.2.0.json b/cow_py/app_data/schemas/referrer/v0.2.0.json new file mode 100644 index 0000000..635830c --- /dev/null +++ b/cow_py/app_data/schemas/referrer/v0.2.0.json @@ -0,0 +1,16 @@ +{ + "$id": "#referrer/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [ + "address" + ], + "title": "Referrer", + "type": "object", + "additionalProperties": false, + "properties": { + "address": { + "$ref": "../definitions.json#/definitions/ethereumAddress", + "title": "Referrer address" + } + } +} diff --git a/cow_py/app_data/schemas/replacedOrder/v0.1.0.json b/cow_py/app_data/schemas/replacedOrder/v0.1.0.json new file mode 100644 index 0000000..ecba827 --- /dev/null +++ b/cow_py/app_data/schemas/replacedOrder/v0.1.0.json @@ -0,0 +1,15 @@ +{ + "$id": "#replacedOrder/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["uid"], + "title": "Replaced order", + "type": "object", + "additionalProperties": false, + "properties": { + "uid": { + "title": "Replaced order UID", + "description": "The replaced order UID.", + "$ref": "../definitions.json#/definitions/orderUid" + } + } +} diff --git a/cow_py/app_data/schemas/signer/v0.1.0.json b/cow_py/app_data/schemas/signer/v0.1.0.json new file mode 100644 index 0000000..aeda275 --- /dev/null +++ b/cow_py/app_data/schemas/signer/v0.1.0.json @@ -0,0 +1,7 @@ +{ + "$id": "#signer/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "$ref": "../definitions.json#/definitions/ethereumAddress", + "title": "Signer", + "description": "The address of the trader who signs the CoW Swap order. This field should normally be omitted; it is recommended to use it if the signer is a smart-contract wallet using EIP-1271 signatures." +} diff --git a/cow_py/app_data/schemas/utm/v0.1.0.json b/cow_py/app_data/schemas/utm/v0.1.0.json new file mode 100644 index 0000000..a9a4d27 --- /dev/null +++ b/cow_py/app_data/schemas/utm/v0.1.0.json @@ -0,0 +1,54 @@ +{ + "$id": "#utm/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [], + "title": "UTM Codes", + "type": "object", + "properties": { + "utmSource": { + "$id": "#/properties/utmSource", + "title": "UTM Source", + "type": "string", + "examples": [ + "twitter" + ], + "description": "Tracks in which medium the traffic originated from (twitter, facebook, etc.)" + }, + "utmMedium": { + "$id": "#/properties/utmMedium", + "title": "UTM Medium", + "type": "string", + "examples": [ + "email" + ], + "description": "Tracks in which medium the traffic originated from (mail, CPC, social, etc.)" + }, + "utmCampaign": { + "$id": "#/properties/utmCampagin", + "title": "UTM Campaign", + "type": "string", + "examples": [ + "everyone-loves-cows-2023" + ], + "description": "Track the performance of a specific campaign" + }, + "utmContent": { + "$id": "#/properties/utmContent", + "title": "UTM Content", + "type": "string", + "examples": [ + "big-fat-button" + ], + "description": "Track which link was clicked" + }, + "utmTerm": { + "$id": "#/properties/utmTem", + "title": "UTM Keyword Term", + "type": "string", + "examples": [ + "coincidence+of+wants" + ], + "description": "Track which keyword term a website visitor came from" + } + } +} diff --git a/cow_py/app_data/schemas/utm/v0.2.0.json b/cow_py/app_data/schemas/utm/v0.2.0.json new file mode 100644 index 0000000..ce085c7 --- /dev/null +++ b/cow_py/app_data/schemas/utm/v0.2.0.json @@ -0,0 +1,55 @@ +{ + "$id": "#utm/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": [], + "title": "UTM Codes", + "type": "object", + "additionalProperties": false, + "properties": { + "utmSource": { + "$id": "#/properties/utmSource", + "title": "UTM Source", + "type": "string", + "examples": [ + "twitter" + ], + "description": "Tracks in which medium the traffic originated from (twitter, facebook, etc.)" + }, + "utmMedium": { + "$id": "#/properties/utmMedium", + "title": "UTM Medium", + "type": "string", + "examples": [ + "email" + ], + "description": "Tracks in which medium the traffic originated from (mail, CPC, social, etc.)" + }, + "utmCampaign": { + "$id": "#/properties/utmCampagin", + "title": "UTM Campaign", + "type": "string", + "examples": [ + "everyone-loves-cows-2023" + ], + "description": "Track the performance of a specific campaign" + }, + "utmContent": { + "$id": "#/properties/utmContent", + "title": "UTM Content", + "type": "string", + "examples": [ + "big-fat-button" + ], + "description": "Track which link was clicked" + }, + "utmTerm": { + "$id": "#/properties/utmTem", + "title": "UTM Keyword Term", + "type": "string", + "examples": [ + "coincidence+of+wants" + ], + "description": "Track which keyword term a website visitor came from" + } + } +} diff --git a/cow_py/app_data/schemas/v0.1.0.json b/cow_py/app_data/schemas/v0.1.0.json new file mode 100644 index 0000000..2d17438 --- /dev/null +++ b/cow_py/app_data/schemas/v0.1.0.json @@ -0,0 +1,40 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.1.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "properties": { + "referrer": { + "$ref": "referrer/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.10.0.json b/cow_py/app_data/schemas/v0.10.0.json new file mode 100644 index 0000000..af27492 --- /dev/null +++ b/cow_py/app_data/schemas/v0.10.0.json @@ -0,0 +1,69 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.10.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.10.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "signer": { + "$ref": "signer/v0.1.0.json#" + }, + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v0.3.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + }, + "hooks": { + "$ref": "hooks/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.11.0.json b/cow_py/app_data/schemas/v0.11.0.json new file mode 100644 index 0000000..5dcd00f --- /dev/null +++ b/cow_py/app_data/schemas/v0.11.0.json @@ -0,0 +1,62 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.11.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": ["version", "metadata"], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.11.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": ["CoW Swap"], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": ["production", "development", "staging", "ens"] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "signer": { + "$ref": "signer/v0.1.0.json#" + }, + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v0.3.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + }, + "hooks": { + "$ref": "hooks/v0.1.0.json#" + }, + "widget": { + "$ref": "widget/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.2.0.json b/cow_py/app_data/schemas/v0.2.0.json new file mode 100644 index 0000000..a8c2dd6 --- /dev/null +++ b/cow_py/app_data/schemas/v0.2.0.json @@ -0,0 +1,43 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.2.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.2.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "properties": { + "referrer": { + "$ref": "referrer/v0.1.0.json#" + }, + "quote": { + "$ref": "quote/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.3.0.json b/cow_py/app_data/schemas/v0.3.0.json new file mode 100644 index 0000000..ebdd176 --- /dev/null +++ b/cow_py/app_data/schemas/v0.3.0.json @@ -0,0 +1,55 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.3.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.3.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "properties": { + "referrer": { + "$ref": "referrer/v0.1.0.json#" + }, + "quote": { + "$ref": "quote/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.4.0.json b/cow_py/app_data/schemas/v0.4.0.json new file mode 100644 index 0000000..5c21521 --- /dev/null +++ b/cow_py/app_data/schemas/v0.4.0.json @@ -0,0 +1,55 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.4.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.4.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "properties": { + "referrer": { + "$ref": "referrer/v0.1.0.json#" + }, + "quote": { + "$ref": "quote/v0.2.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.5.0.json b/cow_py/app_data/schemas/v0.5.0.json new file mode 100644 index 0000000..413e3c6 --- /dev/null +++ b/cow_py/app_data/schemas/v0.5.0.json @@ -0,0 +1,58 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.5.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.5.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "properties": { + "referrer": { + "$ref": "referrer/v0.1.0.json#" + }, + "quote": { + "$ref": "quote/v0.2.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.6.0.json b/cow_py/app_data/schemas/v0.6.0.json new file mode 100644 index 0000000..83db135 --- /dev/null +++ b/cow_py/app_data/schemas/v0.6.0.json @@ -0,0 +1,61 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.6.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.6.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "properties": { + "referrer": { + "$ref": "referrer/v0.1.0.json#" + }, + "utm": { + "$ref": "utm/v0.1.0.json#" + }, + "quote": { + "$ref": "quote/v0.2.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.7.0.json b/cow_py/app_data/schemas/v0.7.0.json new file mode 100644 index 0000000..f044f0d --- /dev/null +++ b/cow_py/app_data/schemas/v0.7.0.json @@ -0,0 +1,61 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.7.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.7.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "properties": { + "referrer": { + "$ref": "referrer/v0.1.0.json#" + }, + "utm": { + "$ref": "utm/v0.1.0.json#" + }, + "quote": { + "$ref": "quote/v0.2.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.2.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.8.0.json b/cow_py/app_data/schemas/v0.8.0.json new file mode 100644 index 0000000..9cb15c5 --- /dev/null +++ b/cow_py/app_data/schemas/v0.8.0.json @@ -0,0 +1,63 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.7.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.8.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v0.3.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v0.9.0.json b/cow_py/app_data/schemas/v0.9.0.json new file mode 100644 index 0000000..c253f1d --- /dev/null +++ b/cow_py/app_data/schemas/v0.9.0.json @@ -0,0 +1,66 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.7.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": [ + "version", + "metadata" + ], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.9.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": [ + "CoW Swap" + ], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": [ + "production", + "development", + "staging", + "ens" + ] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v0.3.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + }, + "hooks": { + "$ref": "hooks/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v1.0.0.json b/cow_py/app_data/schemas/v1.0.0.json new file mode 100644 index 0000000..0add611 --- /dev/null +++ b/cow_py/app_data/schemas/v1.0.0.json @@ -0,0 +1,65 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.11.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": ["version", "metadata"], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.11.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": ["CoW Swap"], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": ["production", "development", "staging", "ens"] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "signer": { + "$ref": "signer/v0.1.0.json#" + }, + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v1.0.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + }, + "hooks": { + "$ref": "hooks/v0.1.0.json#" + }, + "widget": { + "$ref": "widget/v0.1.0.json#" + }, + "partnerFee": { + "$ref": "partnerFee/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/v1.1.0.json b/cow_py/app_data/schemas/v1.1.0.json new file mode 100644 index 0000000..3192276 --- /dev/null +++ b/cow_py/app_data/schemas/v1.1.0.json @@ -0,0 +1,68 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v1.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": ["version", "metadata"], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.11.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": ["CoW Swap"], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": ["production", "development", "staging", "ens"] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "signer": { + "$ref": "signer/v0.1.0.json#" + }, + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v1.0.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + }, + "hooks": { + "$ref": "hooks/v0.1.0.json#" + }, + "widget": { + "$ref": "widget/v0.1.0.json#" + }, + "partnerFee": { + "$ref": "partnerFee/v0.1.0.json#" + }, + "replacedOrder": { + "$ref": "replacedOrder/v0.1.0.json#" + } + } + } + } +} diff --git a/cow_py/app_data/schemas/widget/v0.1.0.json b/cow_py/app_data/schemas/widget/v0.1.0.json new file mode 100644 index 0000000..b5f14f4 --- /dev/null +++ b/cow_py/app_data/schemas/widget/v0.1.0.json @@ -0,0 +1,24 @@ +{ + "$id": "#widget/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["appCode"], + "title": "Widget", + "type": "object", + "additionalProperties": false, + "properties": { + "appCode": { + "$id": "#/properties/appCodeWidget", + "description": "The code identifying the UI powering the widget", + "examples": ["CoW Swap"], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environmentWidget", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": ["production", "development", "staging", "ens"] + } + } +} From 92db8a8122920477e4bc5ae929665697d4caa37e Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Thu, 15 Aug 2024 18:56:53 -0300 Subject: [PATCH 13/30] add utils of app data module --- cow_py/app_data/__init__.py | 0 cow_py/app_data/consts.py | 6 +++++ cow_py/app_data/utils.py | 37 +++++++++++++++++++++++++++ tests/app_data/__init__.py | 0 tests/app_data/mocks.py | 51 +++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 cow_py/app_data/__init__.py create mode 100644 cow_py/app_data/consts.py create mode 100644 cow_py/app_data/utils.py create mode 100644 tests/app_data/__init__.py create mode 100644 tests/app_data/mocks.py diff --git a/cow_py/app_data/__init__.py b/cow_py/app_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cow_py/app_data/consts.py b/cow_py/app_data/consts.py new file mode 100644 index 0000000..e02c39a --- /dev/null +++ b/cow_py/app_data/consts.py @@ -0,0 +1,6 @@ +DEFAULT_IPFS_READ_URI = "https://cloudflare-ipfs.com/ipfs" +DEFAULT_IFPS_WRITE_URI = "https://api.pinata.cloud" + + +class MetaDataError(Exception): + pass diff --git a/cow_py/app_data/utils.py b/cow_py/app_data/utils.py new file mode 100644 index 0000000..c109c67 --- /dev/null +++ b/cow_py/app_data/utils.py @@ -0,0 +1,37 @@ +from typing import Union +from multiformats import CID +from multiformats import base16, base58btc +import binascii +import json + +# CID uses multibase to self-describe the encoding used (See https://github.com/multiformats/multibase) +# - Most reference implementations (multiformats/cid or Pinata, etc) use base58btc encoding +# - However, the backend uses base16 encoding (See https://github.com/cowprotocol/services/blob/main/crates/app-data-hash/src/lib.rs#L64) +MULTIBASE_BASE16 = "f" + + +def parse_cid(ipfs_hash: str) -> CID: + decoder = get_decoder(ipfs_hash) + return CID.decode(ipfs_hash) + + +def decode_cid(bytes_: bytes) -> CID: + return CID.decode(bytes_) + + +def get_decoder(ipfs_hash: str) -> Union[base16, base58btc]: + if ipfs_hash[0] == MULTIBASE_BASE16: + # Base 16 encoding + return base16 + # Use default decoder (base58btc) + return base58btc + + +def extract_digest(cid: str) -> str: + cid_details = parse_cid(cid) + digest = cid_details.multihash.digest + return f"0x{binascii.hexlify(digest).decode('ascii')}" + + +def stringify_deterministic(obj): + return json.dumps(obj, sort_keys=True, separators=(",", ":")) diff --git a/tests/app_data/__init__.py b/tests/app_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/app_data/mocks.py b/tests/app_data/mocks.py new file mode 100644 index 0000000..0958914 --- /dev/null +++ b/tests/app_data/mocks.py @@ -0,0 +1,51 @@ +from cow_py.app_data.appDataCid import AppDataCid +from cow_py.app_data.appDataHex import AppDataHex + +HTTP_STATUS_OK = 200 +HTTP_STATUS_INTERNAL_ERROR = 500 + +APP_DATA_DOC = { + "version": "0.7.0", + "appCode": "CoW Swap", + "metadata": {}, +} + +APP_DATA_STRING = '{"appCode":"CoW Swap","metadata":{},"version":"0.7.0"}' + +CID = AppDataCid("f01551b20337aa6e6c2a7a0d1eb79a35ebd88b08fc963d5f7a3fc953b7ffb2b7f5898a1df") + +APP_DATA_HEX = AppDataHex( + "0x337aa6e6c2a7a0d1eb79a35ebd88b08fc963d5f7a3fc953b7ffb2b7f5898a1df" +) + +APP_DATA_DOC_CUSTOM = { + **APP_DATA_DOC, + "environment": "test", + "metadata": { + "referrer": { + "address": "0x1f5B740436Fc5935622e92aa3b46818906F416E9", + "version": "0.1.0", + }, + "quote": { + "slippageBips": 1, + "version": "0.2.0", + }, + }, +} + +APP_DATA_STRING_2 = '{"appCode":"CoW Swap","environment":"production","metadata":{"quote":{"slippageBips":"50","version":"0.2.0"},"orderClass":{"orderClass":"market","version":"0.1.0"}},"version":"0.6.0"}' + +CID_2 = "f01551b208af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" + +APP_DATA_HEX_2 = "0x8af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" + +APP_DATA_STRING_LEGACY = '{"version":"0.7.0","appCode":"CowSwap","metadata":{}}' + +CID_LEGACY = AppDataCid("QmSwrFbdFcryazEr361YmSwtGcN4uo4U5DKpzA4KbGxw4Q") + +APP_DATA_HEX_LEGACY = AppDataHex( + "0x447320af985c5e834321dc495545f764ad20d8397eeed2f4a2dcbee44a56b725" +) + +PINATA_API_KEY = "apikey" +PINATA_API_SECRET = "apiSecret" From 013a0971d9273db11bece0e0b73f80ee2d0907c9 Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Thu, 15 Aug 2024 18:57:19 -0300 Subject: [PATCH 14/30] wip: start convertion from hex to cid --- cow_py/app_data/appDataCid.py | 3 + cow_py/app_data/appDataDoc.py | 2 + cow_py/app_data/appDataHex.py | 38 ++++++ poetry.lock | 232 +++++++++++++++++++++++++++++++++- pyproject.toml | 5 + tests/app_data/appDataHex.py | 29 +++++ 6 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 cow_py/app_data/appDataCid.py create mode 100644 cow_py/app_data/appDataDoc.py create mode 100644 cow_py/app_data/appDataHex.py create mode 100644 tests/app_data/appDataHex.py diff --git a/cow_py/app_data/appDataCid.py b/cow_py/app_data/appDataCid.py new file mode 100644 index 0000000..65c31c0 --- /dev/null +++ b/cow_py/app_data/appDataCid.py @@ -0,0 +1,3 @@ +class AppDataCid: + def __init__(self, app_data_cid: str): + self.app_data_cid = app_data_cid diff --git a/cow_py/app_data/appDataDoc.py b/cow_py/app_data/appDataDoc.py new file mode 100644 index 0000000..7139bb5 --- /dev/null +++ b/cow_py/app_data/appDataDoc.py @@ -0,0 +1,2 @@ +class AppDataDoc: + pass diff --git a/cow_py/app_data/appDataHex.py b/cow_py/app_data/appDataHex.py new file mode 100644 index 0000000..bb177db --- /dev/null +++ b/cow_py/app_data/appDataHex.py @@ -0,0 +1,38 @@ +from web3 import Web3 +from cid import make_cid +import multihash +from cow_py.app_data.appDataCid import AppDataCid +from cow_py.app_data.consts import MetaDataError + + +class AppDataHex: + def __init__(self, app_data_hex: str): + self.app_data_hex = app_data_hex + + async def to_cid(self) -> AppDataCid: + cid = await self._app_data_hex_to_cid() + await self._assert_cid(cid) + return AppDataCid(cid) + + async def to_cid_legacy(self) -> AppDataCid: + cid = await self._app_data_hex_to_cid_legacy() + await self._assert_cid(cid) + return AppDataCid(cid) + + async def _assert_cid(self, cid: str): + if not cid: + raise MetaDataError( + f"Error getting CID from appDataHex: {self.app_data_hex}" + ) + + async def _app_data_hex_to_cid(self) -> str: + hash_bytes = Web3.to_bytes(hexstr=self.app_data_hex) + mh = multihash.encode(hash_bytes, 'keccak-256') + cid = make_cid(1, 'raw', mh) + return str(cid) + + async def _app_data_hex_to_cid_legacy(self) -> str: + hash_bytes = Web3.to_bytes(hexstr=self.app_data_hex) + mh = multihash.encode(hash_bytes, 'sha2-256') + cid = make_cid(0, 'dag-pb', mh) + return str(cid) \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index ce74efa..b2700db 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +[[package]] +name = "aiofiles" +version = "24.1.0" +description = "File support for asyncio." +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, + {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, +] + [[package]] name = "aiohttp" version = "3.9.5" @@ -268,6 +279,36 @@ files = [ {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, ] +[[package]] +name = "base58" +version = "1.0.3" +description = "Base58 and Base58Check implementation" +optional = false +python-versions = "*" +files = [ + {file = "base58-1.0.3-py2-none-any.whl", hash = "sha256:1e42993c0628ed4f898c03b522b26af78fb05115732549b21a028bc4633d19ab"}, + {file = "base58-1.0.3-py3-none-any.whl", hash = "sha256:6aa0553e477478993588303c54659d15e3c17ae062508c854a8b752d07c716bd"}, + {file = "base58-1.0.3.tar.gz", hash = "sha256:9a793c599979c497800eb414c852b80866f28daaed5494703fc129592cc83e60"}, +] + +[[package]] +name = "bases" +version = "0.3.0" +description = "Python library for general Base-N encodings." +optional = false +python-versions = ">=3.7" +files = [ + {file = "bases-0.3.0-py3-none-any.whl", hash = "sha256:a2fef3366f3e522ff473d2e95c21523fe8e44251038d5c6150c01481585ebf5b"}, + {file = "bases-0.3.0.tar.gz", hash = "sha256:70f04a4a45d63245787f9e89095ca11042685b6b64b542ad916575ba3ccd1570"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0" +typing-validation = ">=1.1.0" + +[package.extras] +dev = ["base58", "mypy", "pylint", "pytest", "pytest-cov"] + [[package]] name = "bitarray" version = "2.9.2" @@ -1557,6 +1598,17 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jsonref" +version = "1.1.0" +description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, + {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, +] + [[package]] name = "jsonschema" version = "4.21.1" @@ -1844,6 +1896,16 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "morphys" +version = "1.0" +description = "Smart conversions between unicode and bytes types for common cases" +optional = false +python-versions = "*" +files = [ + {file = "morphys-1.0-py2.py3-none-any.whl", hash = "sha256:76d6dbaa4d65f597e59d332c81da786d83e4669387b9b2a750cfec74e7beec20"}, +] + [[package]] name = "multidict" version = "6.0.5" @@ -1943,6 +2005,44 @@ files = [ {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] +[[package]] +name = "multiformats" +version = "0.3.1.post4" +description = "Python implementation of multiformats protocols." +optional = false +python-versions = ">=3.7" +files = [ + {file = "multiformats-0.3.1.post4-py3-none-any.whl", hash = "sha256:5b1d61bd8275c9e817bdbee38dbd501b26629011962ee3c86c46e7ccd0b14129"}, + {file = "multiformats-0.3.1.post4.tar.gz", hash = "sha256:d00074fdbc7d603c2084b4c38fa17bbc28173cf2750f51f46fbbc5c4d5605fbb"}, +] + +[package.dependencies] +bases = ">=0.3.0" +multiformats-config = ">=0.3.0" +typing-extensions = ">=4.6.0" +typing-validation = ">=1.1.0" + +[package.extras] +dev = ["blake3", "mmh3", "mypy", "pycryptodomex", "pylint", "pyskein", "pytest", "pytest-cov", "rich"] +full = ["blake3", "mmh3", "pycryptodomex", "pyskein", "rich"] + +[[package]] +name = "multiformats-config" +version = "0.3.1" +description = "Pre-loading configuration module for the 'multiformats' package." +optional = false +python-versions = ">=3.7" +files = [ + {file = "multiformats-config-0.3.1.tar.gz", hash = "sha256:7eaa80ef5d9c5ee9b86612d21f93a087c4a655cbcb68960457e61adbc62b47a7"}, + {file = "multiformats_config-0.3.1-py3-none-any.whl", hash = "sha256:dec4c9d42ed0d9305889b67440f72e8e8d74b82b80abd7219667764b5b0a8e1d"}, +] + +[package.dependencies] +multiformats = "*" + +[package.extras] +dev = ["mypy", "pylint", "pytest", "pytest-cov"] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -2140,6 +2240,24 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "py-cid" +version = "0.3.0" +description = "Self-describing content-addressed identifiers for distributed systems" +optional = false +python-versions = "*" +files = [ + {file = "py-cid-0.3.0.tar.gz", hash = "sha256:22f432cc6fb68d12a9c35dbdc92c95484fc49e31dfcb9e0efb0082233c5394e3"}, + {file = "py_cid-0.3.0-py2.py3-none-any.whl", hash = "sha256:7c48a6ee0bc50fd114d4b24849cd689a31d3ad5bdf8fa073bf68f846fd58c5da"}, +] + +[package.dependencies] +base58 = ">=1.0.2,<2.0" +morphys = ">=1.0,<2.0" +py-multibase = ">=1.0.0,<2.0.0" +py-multicodec = "<0.3.0" +py-multihash = ">=0.2.0,<1.0.0" + [[package]] name = "py-ecc" version = "7.0.0" @@ -2210,6 +2328,55 @@ dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "flaky (>=3.2.0)", "ipython", docs = ["towncrier (>=21,<22)"] test = ["flaky (>=3.2.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] +[[package]] +name = "py-multibase" +version = "1.0.3" +description = "Multibase implementation for Python" +optional = false +python-versions = "*" +files = [ + {file = "py-multibase-1.0.3.tar.gz", hash = "sha256:d28a20efcbb61eec28f55827a0bf329c7cea80fffd933aecaea6ae8431267fe4"}, + {file = "py_multibase-1.0.3-py2.py3-none-any.whl", hash = "sha256:2677c1fafcc0ae15ddb9c7f444c5becc2530b3889124fd4fa2959ddfefb8c15b"}, +] + +[package.dependencies] +morphys = ">=1.0,<2.0" +python-baseconv = ">=1.2.0,<2.0" +six = ">=1.10.0,<2.0" + +[[package]] +name = "py-multicodec" +version = "0.2.1" +description = "Multicodec implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "py-multicodec-0.2.1.tar.gz", hash = "sha256:83021ffe8c0e272d19b5b86bc5b39efa67c8e9f4735ce6cafdbc1ace767ec647"}, + {file = "py_multicodec-0.2.1-py2.py3-none-any.whl", hash = "sha256:55b6bb53088a63e56c434cb11b29795e8805652bac43d50a8f2a9bcf5ca84e1f"}, +] + +[package.dependencies] +morphys = ">=1.0,<2.0" +six = ">=1.10.0,<2.0" +varint = ">=1.0.2,<2.0.0" + +[[package]] +name = "py-multihash" +version = "0.2.3" +description = "Multihash implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "py-multihash-0.2.3.tar.gz", hash = "sha256:f0ade4de820afdc4b4aaa40464ec86c9da5cae3a4578cda2daab4b0eb7e5b18d"}, + {file = "py_multihash-0.2.3-py2.py3-none-any.whl", hash = "sha256:a0602c99093587dfbf1634e2e8c7726de39374b0d68587a36093b4c237af6969"}, +] + +[package.dependencies] +base58 = ">=1.0.2,<2.0" +morphys = ">=1.0,<2.0" +six = ">=1.10.0,<2.0" +varint = ">=1.0.2,<2.0" + [[package]] name = "pybars3" version = "0.9.7" @@ -2429,6 +2596,21 @@ files = [ {file = "PyMeta3-0.5.1.tar.gz", hash = "sha256:18bda326d9a9bbf587bfc0ee0bc96864964d78b067288bcf55d4d98681d05bcb"}, ] +[[package]] +name = "pymultihash" +version = "0.8.2" +description = "Python implementation of the multihash specification" +optional = false +python-versions = "*" +files = [ + {file = "pymultihash-0.8.2-py3-none-any.whl", hash = "sha256:f7fa840b24bd6acbd6b073fcd330f10e15619387297babf1dd13ca4dae6e8209"}, + {file = "pymultihash-0.8.2.tar.gz", hash = "sha256:49c75a1ae9ecc6d22d259064d4597b3685da3f0258f4ded632e03a3a79af215b"}, +] + +[package.extras] +blake2 = ["pyblake2"] +sha3 = ["pysha3"] + [[package]] name = "pyright" version = "1.1.358" @@ -2540,6 +2722,16 @@ pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "python-baseconv" +version = "1.2.2" +description = "Convert numbers from base 10 integers to base X strings and back again." +optional = false +python-versions = "*" +files = [ + {file = "python-baseconv-1.2.2.tar.gz", hash = "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b"}, +] + [[package]] name = "pyunormalize" version = "15.1.0" @@ -2585,6 +2777,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -2592,8 +2785,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -2610,6 +2811,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -2617,6 +2819,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -3146,6 +3349,23 @@ files = [ {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] +[[package]] +name = "typing-validation" +version = "1.2.11.post4" +description = "A simple library for runtime type-checking." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_validation-1.2.11.post4-py3-none-any.whl", hash = "sha256:73dd504ddebf5210e80d5f65ba9b30efbd0fa42f266728fda7c4f0ba335c699c"}, + {file = "typing_validation-1.2.11.post4.tar.gz", hash = "sha256:7aed04ecfbda07e63b7266f90e5d096f96344f7facfe04bb081b21e4a9781670"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["mypy", "pylint", "pytest", "pytest-cov", "rich"] + [[package]] name = "urllib3" version = "2.2.1" @@ -3163,6 +3383,16 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "varint" +version = "1.0.2" +description = "Simple python varint implementation" +optional = false +python-versions = "*" +files = [ + {file = "varint-1.0.2.tar.gz", hash = "sha256:a6ecc02377ac5ee9d65a6a8ad45c9ff1dac8ccee19400a5950fb51d594214ca5"}, +] + [[package]] name = "virtualenv" version = "20.25.1" @@ -3417,4 +3647,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "e02971366a8bd5c85b5e2c32578cd193560f4827d381c2e9341953d453120fad" +content-hash = "993952ba1b62b880a9fea993c0205b00d38854570bbde67475baa21f96bf18e8" diff --git a/pyproject.toml b/pyproject.toml index 012d3ac..2c4eb2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,11 @@ pydantic = "^2.7.0" pytest-mock = "^3.14.0" backoff = "^2.2.1" aiolimiter = "^1.1.0" +aiofiles = "^24.1.0" +jsonref = "^1.1.0" +multiformats = "^0.3.1.post4" +py-cid = "^0.3.0" +pymultihash = "^0.8.2" [tool.poetry.group.dev.dependencies] diff --git a/tests/app_data/appDataHex.py b/tests/app_data/appDataHex.py new file mode 100644 index 0000000..c0a5f9f --- /dev/null +++ b/tests/app_data/appDataHex.py @@ -0,0 +1,29 @@ +import pytest +from cow_py.app_data.appDataHex import AppDataHex +from .mocks import APP_DATA_HEX, CID, APP_DATA_HEX_LEGACY, CID_LEGACY + + +@pytest.mark.asyncio +async def test_app_data_hex_to_cid_happy_path(): + decoded_app_data_hex = await APP_DATA_HEX.to_cid() + assert decoded_app_data_hex.app_data_cid == CID.app_data_cid + + +@pytest.mark.asyncio +async def test_app_data_hex_to_cid_invalid_hash(): + app_data_hex = AppDataHex("invalidHash") + with pytest.raises(Exception): + await app_data_hex("invalidHash") + + +@pytest.mark.asyncio +async def test_app_data_hex_to_cid_legacy_happy_path(): + decoded_app_data_hex = await APP_DATA_HEX_LEGACY.to_cid_legacy() + assert decoded_app_data_hex.app_data_cid == CID_LEGACY.app_data_cid + + +@pytest.mark.asyncio +async def test_app_data_hex_to_cid_legacy_invalid_hash(): + app_data_hex = AppDataHex("invalidHash") + with pytest.raises(Exception): + await app_data_hex.to_cid_legacy() From 8b78530cf6bb72c38c2ab1d8e5d54a16dad7939e Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Fri, 16 Aug 2024 09:58:40 -0300 Subject: [PATCH 15/30] add generated api model --- cow_py/order_book/generated/model.py | 711 +++++++++++++++++++++++++++ 1 file changed, 711 insertions(+) create mode 100644 cow_py/order_book/generated/model.py diff --git a/cow_py/order_book/generated/model.py b/cow_py/order_book/generated/model.py new file mode 100644 index 0000000..bfec9a2 --- /dev/null +++ b/cow_py/order_book/generated/model.py @@ -0,0 +1,711 @@ +# generated by datamodel-codegen: +# filename: https://raw.githubusercontent.com/cowprotocol/services/v2.245.1/crates/orderbook/openapi.yml +# timestamp: 2024-04-12T14:44:16+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, Field, RootModel + + +class TransactionHash(RootModel[str]): + root: str = Field( + ..., + description="32 byte digest encoded as a hex with `0x` prefix.", + examples=["0xd51f28edffcaaa76be4a22f6375ad289272c037f3cc072345676e88d92ced8b5"], + ) + + +class Address(RootModel[str]): + root: str = Field( + ..., + description="20 byte Ethereum address encoded as a hex with `0x` prefix.", + examples=["0x6810e776880c02933d47db1b9fc05908e5386b96"], + ) + + +class AppData(RootModel[str]): + root: str = Field( + ..., + description="The string encoding of a JSON object representing some `appData`. The\nformat of the JSON expected in the `appData` field is defined\n[here](https://github.com/cowprotocol/app-data).\n", + examples=['{"version":"0.9.0","metadata":{}}'], + ) + + +class AppDataHash(RootModel[str]): + root: str = Field( + ..., + description="32 bytes encoded as hex with `0x` prefix.\nIt's expected to be the hash of the stringified JSON object representing the `appData`.\n", + examples=["0x0000000000000000000000000000000000000000000000000000000000000000"], + ) + + +class AppDataObject(BaseModel): + fullAppData: Optional[AppData] = None + + +class BigUint(RootModel[str]): + root: str = Field( + ..., + description="A big unsigned integer encoded in decimal.", + examples=["1234567890"], + ) + + +class CallData(RootModel[str]): + root: str = Field( + ..., + description="Some `calldata` sent to a contract in a transaction encoded as a hex with `0x` prefix.", + examples=["0xca11da7a"], + ) + + +class TokenAmount(RootModel[str]): + root: str = Field( + ..., + description="Amount of a token. `uint256` encoded in decimal.", + examples=["1234567890"], + ) + + +class PlacementError(Enum): + QuoteNotFound = "QuoteNotFound" + ValidToTooFarInFuture = "ValidToTooFarInFuture" + PreValidationError = "PreValidationError" + + +class OnchainOrderData(BaseModel): + sender: Address = Field( + ..., + description="If orders are placed as on-chain orders, the owner of the order might\nbe a smart contract, but not the user placing the order. The\nactual user will be provided in this field.\n", + ) + placementError: Optional[PlacementError] = Field( + None, + description="Describes the error, if the order placement was not successful. This could\nhappen, for example, if the `validTo` is too high, or no valid quote was\nfound or generated.\n", + ) + + +class EthflowData(BaseModel): + refundTxHash: TransactionHash = Field( + ..., + description="Specifies in which transaction the order was refunded. If\nthis field is null the order was not yet refunded.\n", + ) + userValidTo: int = Field( + ..., + description="Describes the `validTo` of an order ethflow order.\n\n**NOTE**: For ethflow orders, the `validTo` encoded in the smart\ncontract is `type(uint256).max`.\n", + ) + + +class OrderKind(Enum): + buy = "buy" + sell = "sell" + + +class OrderClass(Enum): + market = "market" + limit = "limit" + liquidity = "liquidity" + + +class SellTokenSource(Enum): + erc20 = "erc20" + internal = "internal" + external = "external" + + +class BuyTokenDestination(Enum): + erc20 = "erc20" + internal = "internal" + + +class PriceQuality(Enum): + fast = "fast" + optimal = "optimal" + verified = "verified" + + +class OrderStatus(Enum): + presignaturePending = "presignaturePending" + open = "open" + fulfilled = "fulfilled" + cancelled = "cancelled" + expired = "expired" + + +class ProtocolAppData(BaseModel): + pass + + +class AuctionPrices(RootModel[Optional[Dict[str, BigUint]]]): + root: Optional[Dict[str, BigUint]] = None + + +class UID(RootModel[str]): + root: str = Field( + ..., + description="Unique identifier for the order: 56 bytes encoded as hex with `0x` prefix.\nBytes 0..32 are the order digest, bytes 30..52 the owner address and bytes\n52..56 the expiry (`validTo`) as a `uint32` unix epoch timestamp.\n", + examples=[ + "0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a" + ], + ) + + +class SigningScheme(Enum): + eip712 = "eip712" + ethsign = "ethsign" + presign = "presign" + eip1271 = "eip1271" + + +class EcdsaSigningScheme(Enum): + eip712 = "eip712" + ethsign = "ethsign" + + +class EcdsaSignature(RootModel[str]): + root: str = Field( + ..., + description="65 bytes encoded as hex with `0x` prefix. `r || s || v` from the spec.", + examples=[ + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + ) + + +class PreSignature(RootModel[str]): + root: str = Field( + ..., + description='Empty signature bytes. Used for "presign" signatures.', + examples=["0x"], + ) + + +class ErrorType(Enum): + DuplicatedOrder = "DuplicatedOrder" + QuoteNotFound = "QuoteNotFound" + InvalidQuote = "InvalidQuote" + MissingFrom = "MissingFrom" + WrongOwner = "WrongOwner" + InvalidEip1271Signature = "InvalidEip1271Signature" + InsufficientBalance = "InsufficientBalance" + InsufficientAllowance = "InsufficientAllowance" + InvalidSignature = "InvalidSignature" + InsufficientFee = "InsufficientFee" + SellAmountOverflow = "SellAmountOverflow" + TransferSimulationFailed = "TransferSimulationFailed" + ZeroAmount = "ZeroAmount" + IncompatibleSigningScheme = "IncompatibleSigningScheme" + TooManyLimitOrders_UnsupportedBuyTokenDestination = ( + "TooManyLimitOrders UnsupportedBuyTokenDestination" + ) + UnsupportedSellTokenSource = "UnsupportedSellTokenSource" + UnsupportedOrderType = "UnsupportedOrderType" + InsufficientValidTo = "InsufficientValidTo" + ExcessiveValidTo = "ExcessiveValidTo" + InvalidNativeSellToken = "InvalidNativeSellToken" + SameBuyAndSellToken = "SameBuyAndSellToken" + UnsupportedToken = "UnsupportedToken" + InvalidAppData = "InvalidAppData" + AppDataHashMismatch = "AppDataHashMismatch" + AppdataFromMismatch = "AppdataFromMismatch" + + +class OrderPostError(BaseModel): + errorType: ErrorType + description: str + + +class ErrorType1(Enum): + InvalidSignature = "InvalidSignature" + WrongOwner = "WrongOwner" + OrderNotFound = "OrderNotFound" + AlreadyCancelled = "AlreadyCancelled" + OrderFullyExecuted = "OrderFullyExecuted" + OrderExpired = "OrderExpired" + OnChainOrder = "OnChainOrder" + + +class OrderCancellationError(BaseModel): + errorType: ErrorType1 + description: str + + +class ErrorType2(Enum): + AlreadyCancelled = "AlreadyCancelled" + OrderFullyExecuted = "OrderFullyExecuted" + OrderExpired = "OrderExpired" + OnChainOrder = "OnChainOrder" + DuplicatedOrder = "DuplicatedOrder" + InsufficientFee = "InsufficientFee" + InsufficientAllowance = "InsufficientAllowance" + InsufficientBalance = "InsufficientBalance" + InsufficientValidTo = "InsufficientValidTo" + ExcessiveValidTo = "ExcessiveValidTo" + InvalidSignature = "InvalidSignature" + TransferSimulationFailed = "TransferSimulationFailed" + UnsupportedToken = "UnsupportedToken" + WrongOwner = "WrongOwner" + SameBuyAndSellToken = "SameBuyAndSellToken" + ZeroAmount = "ZeroAmount" + UnsupportedBuyTokenDestination = "UnsupportedBuyTokenDestination" + UnsupportedSellTokenSource = "UnsupportedSellTokenSource" + UnsupportedOrderType = "UnsupportedOrderType" + + +class ReplaceOrderError(BaseModel): + errorType: ErrorType2 + description: str + + +class ErrorType3(Enum): + UnsupportedToken = "UnsupportedToken" + ZeroAmount = "ZeroAmount" + UnsupportedOrderType = "UnsupportedOrderType" + + +class PriceEstimationError(BaseModel): + errorType: ErrorType3 + description: str + + +class OrderQuoteSideKindSell(Enum): + sell = "sell" + + +class OrderQuoteSideKindBuy(Enum): + buy = "buy" + + +class OrderQuoteValidity1(BaseModel): + validTo: Optional[int] = Field( + None, description="Unix timestamp (`uint32`) until which the order is valid." + ) + + +class OrderQuoteValidity2(BaseModel): + validFor: Optional[int] = Field( + None, + description="Number (`uint32`) of seconds that the order should be valid for.", + ) + + +class OrderQuoteValidity(RootModel[Union[OrderQuoteValidity1, OrderQuoteValidity2]]): + root: Union[OrderQuoteValidity1, OrderQuoteValidity2] = Field( + ..., description="The validity for the order." + ) + + +class Objective(BaseModel): + total: Optional[float] = Field( + None, description="The total objective value used for ranking solutions." + ) + surplus: Optional[float] = None + fees: Optional[float] = None + cost: Optional[float] = None + gas: Optional[int] = None + + +class Order1(BaseModel): + id: Optional[UID] = None + executedAmount: Optional[BigUint] = None + + +class SolverSettlement(BaseModel): + solver: Optional[str] = Field(None, description="Name of the solver.") + solverAddress: Optional[str] = Field( + None, + description="The address used by the solver to execute the settlement on-chain.\nThis field is missing for old settlements, the zero address has been used instead.\n", + ) + objective: Optional[Objective] = None + score: Optional[BigUint] = Field( + None, + description="The score of the current auction as defined in [CIP-20](https://snapshot.org/#/cow.eth/proposal/0x2d3f9bd1ea72dca84b03e97dda3efc1f4a42a772c54bd2037e8b62e7d09a491f).\nIt is `null` for old auctions.\n", + ) + clearingPrices: Optional[Dict[str, BigUint]] = Field( + None, + description="The prices of tokens for settled user orders as passed to the settlement contract.\n", + ) + orders: Optional[List[Order1]] = Field(None, description="Touched orders.") + callData: Optional[CallData] = Field( + None, + description="Transaction `calldata` that is executed on-chain if the settlement is executed.", + ) + uninternalizedCallData: Optional[CallData] = Field( + None, + description="Full `calldata` as generated from the original solver output.\n\nIt can be different from the executed transaction if part of the settlements are internalised\n(use internal liquidity in lieu of trading against on-chain liquidity).\n\nThis field is omitted in case it coincides with `callData`.\n", + ) + + +class NativePriceResponse(BaseModel): + price: Optional[float] = Field(None, description="Estimated price of the token.") + + +class TotalSurplus(BaseModel): + totalSurplus: Optional[str] = Field(None, description="The total surplus.") + + +class InteractionData(BaseModel): + target: Optional[Address] = None + value: Optional[TokenAmount] = None + call_data: Optional[List[CallData]] = Field( + None, description="The call data to be used for the interaction." + ) + + +class Surplus(BaseModel): + factor: float + max_volume_factor: float + + +class Volume(BaseModel): + factor: float + + +class FeePolicy(RootModel[Union[Surplus, Volume]]): + root: Union[Surplus, Volume] = Field( + ..., description="Defines the ways to calculate the protocol fee." + ) + + +class OrderParameters(BaseModel): + sellToken: Address = Field(..., description="ERC-20 token to be sold.") + buyToken: Address = Field(..., description="ERC-20 token to be bought.") + receiver: Optional[Address] = Field( + None, + description="An optional Ethereum address to receive the proceeds of the trade instead\nof the owner (i.e. the order signer).\n", + ) + sellAmount: TokenAmount = Field( + ..., description="Amount of `sellToken` to be sold in atoms." + ) + buyAmount: TokenAmount = Field( + ..., description="Amount of `buyToken` to be bought in atoms." + ) + validTo: int = Field( + ..., description="Unix timestamp (`uint32`) until which the order is valid." + ) + appData: AppDataHash + feeAmount: TokenAmount = Field( + ..., description="feeRatio * sellAmount + minimal_fee in atoms." + ) + kind: OrderKind = Field(..., description="The kind is either a buy or sell order.") + partiallyFillable: bool = Field( + ..., description="Is the order fill-or-kill or partially fillable?" + ) + sellTokenBalance: Optional[SellTokenSource] = "erc20" + buyTokenBalance: Optional[BuyTokenDestination] = "erc20" + signingScheme: Optional[SigningScheme] = "eip712" + + +class OrderMetaData(BaseModel): + creationDate: str = Field( + ..., + description="Creation time of the order. Encoded as ISO 8601 UTC.", + examples=["2020-12-03T18:35:18.814523Z"], + ) + class_: OrderClass = Field(..., alias="class") + owner: Address + uid: UID + availableBalance: Optional[TokenAmount] = Field( + None, + description="Unused field that is currently always set to `null` and will be removed in the future.\n", + ) + executedSellAmount: BigUint = Field( + ..., + description="The total amount of `sellToken` that has been executed for this order including fees.\n", + ) + executedSellAmountBeforeFees: BigUint = Field( + ..., + description="The total amount of `sellToken` that has been executed for this order without fees.\n", + ) + executedBuyAmount: BigUint = Field( + ..., + description="The total amount of `buyToken` that has been executed for this order.\n", + ) + executedFeeAmount: BigUint = Field( + ..., + description="The total amount of fees that have been executed for this order.", + ) + invalidated: bool = Field(..., description="Has this order been invalidated?") + status: OrderStatus = Field(..., description="Order status.") + fullFeeAmount: Optional[TokenAmount] = Field( + None, description="Amount that the signed fee would be without subsidies." + ) + isLiquidityOrder: Optional[bool] = Field( + None, + description="Liquidity orders are functionally the same as normal smart contract orders but are not\nplaced with the intent of actively getting traded. Instead they facilitate the\ntrade of normal orders by allowing them to be matched against liquidity orders which\nuses less gas and can have better prices than external liquidity.\n\nAs such liquidity orders will only be used in order to improve settlement of normal\norders. They should not be expected to be traded otherwise and should not expect to get\nsurplus.\n", + ) + ethflowData: Optional[EthflowData] = None + onchainUser: Optional[Address] = Field( + None, + description="This represents the actual trader of an on-chain order.\n\n### ethflow orders\n\nIn this case, the `owner` would be the `EthFlow` contract and *not* the actual trader.\n", + ) + onchainOrderData: Optional[OnchainOrderData] = Field( + None, + description="There is some data only available for orders that are placed on-chain. This data\ncan be found in this object.\n", + ) + executedSurplusFee: Optional[BigUint] = Field( + None, description="Surplus fee that the limit order was executed with." + ) + fullAppData: Optional[str] = Field( + None, + description="Full `appData`, which the contract-level `appData` is a hash of. See `OrderCreation`\nfor more information.\n", + ) + + +class CompetitionAuction(BaseModel): + orders: Optional[List[UID]] = Field( + None, description="The UIDs of the orders included in the auction.\n" + ) + prices: Optional[AuctionPrices] = None + + +class OrderCancellations(BaseModel): + orderUids: Optional[List[UID]] = Field( + None, description="UIDs of orders to cancel." + ) + signature: EcdsaSignature = Field( + ..., description="`OrderCancellation` signed by the owner." + ) + signingScheme: EcdsaSigningScheme + + +class OrderCancellation(BaseModel): + signature: EcdsaSignature = Field( + ..., description="OrderCancellation signed by owner" + ) + signingScheme: EcdsaSigningScheme + + +class Trade(BaseModel): + blockNumber: int = Field(..., description="Block in which trade occurred.") + logIndex: int = Field( + ..., description="Index in which transaction was included in block." + ) + orderUid: UID = Field(..., description="UID of the order matched by this trade.") + owner: Address = Field(..., description="Address of trader.") + sellToken: Address = Field(..., description="Address of token sold.") + buyToken: Address = Field(..., description="Address of token bought.") + sellAmount: TokenAmount = Field( + ..., + description="Total amount of `sellToken` that has been executed for this trade (including fees).", + ) + sellAmountBeforeFees: BigUint = Field( + ..., + description="The total amount of `sellToken` that has been executed for this order without fees.", + ) + buyAmount: TokenAmount = Field( + ..., description="Total amount of `buyToken` received in this trade." + ) + txHash: TransactionHash = Field( + ..., + description="Transaction hash of the corresponding settlement transaction containing the trade (if available).", + ) + + +class Signature(RootModel[Union[EcdsaSignature, PreSignature]]): + root: Union[EcdsaSignature, PreSignature] = Field(..., description="A signature.") + + +class OrderQuoteSide1(BaseModel): + kind: OrderQuoteSideKindSell + sellAmountBeforeFee: TokenAmount = Field( + ..., + description="The total amount that is available for the order. From this value, the fee\nis deducted and the buy amount is calculated.\n", + ) + + +class OrderQuoteSide2(BaseModel): + kind: OrderQuoteSideKindSell + sellAmountAfterFee: TokenAmount = Field( + ..., description="The `sellAmount` for the order." + ) + + +class OrderQuoteSide3(BaseModel): + kind: OrderQuoteSideKindBuy + buyAmountAfterFee: TokenAmount = Field( + ..., description="The `buyAmount` for the order." + ) + + +class OrderQuoteSide( + RootModel[Union[OrderQuoteSide1, OrderQuoteSide2, OrderQuoteSide3]] +): + root: Union[OrderQuoteSide1, OrderQuoteSide2, OrderQuoteSide3] = Field( + ..., description="The buy or sell side when quoting an order." + ) + + +class OrderQuoteRequest(BaseModel): + sellToken: Address = Field(..., description="ERC-20 token to be sold") + buyToken: Address = Field(..., description="ERC-20 token to be bought") + receiver: Optional[Address] = Field( + None, + description="An optional address to receive the proceeds of the trade instead of the\n`owner` (i.e. the order signer).\n", + ) + appData: Optional[Union[AppData, AppDataHash]] = Field( + None, + description="AppData which will be assigned to the order.\nExpects either a string JSON doc as defined on [AppData](https://github.com/cowprotocol/app-data) or a\nhex encoded string for backwards compatibility.\nWhen the first format is used, it's possible to provide the derived appDataHash field.\n", + ) + appDataHash: Optional[AppDataHash] = Field( + None, + description="The hash of the stringified JSON appData doc.\nIf present, `appData` field must be set with the aforementioned data where this hash is derived from.\nIn case they differ, the call will fail.\n", + ) + sellTokenBalance: Optional[SellTokenSource] = "erc20" + buyTokenBalance: Optional[BuyTokenDestination] = "erc20" + from_: Address = Field(..., alias="from") + priceQuality: Optional[PriceQuality] = "verified" + signingScheme: Optional[SigningScheme] = "eip712" + onchainOrder: Optional[Any] = Field( + False, + description='Flag to signal whether the order is intended for on-chain order placement. Only valid\nfor non ECDSA-signed orders."\n', + ) + + +class OrderQuoteResponse(BaseModel): + quote: OrderParameters + from_: Optional[Address] = Field(None, alias="from") + expiration: str = Field( + ..., + description="Expiration date of the offered fee. Order service might not accept\nthe fee after this expiration date. Encoded as ISO 8601 UTC.\n", + examples=["1985-03-10T18:35:18.814523Z"], + ) + id: Optional[int] = Field( + None, + description="Quote ID linked to a quote to enable providing more metadata when analysing\norder slippage.\n", + ) + verified: bool = Field( + ..., + description="Whether it was possible to verify that the quoted amounts are accurate using a simulation.\n", + ) + + +class SolverCompetitionResponse(BaseModel): + auctionId: Optional[int] = Field( + None, description="The ID of the auction the competition info is for." + ) + transactionHash: Optional[TransactionHash] = Field( + None, + description="The hash of the transaction that the winning solution of this info was submitted in.", + ) + gasPrice: Optional[float] = Field( + None, description="Gas price used for ranking solutions." + ) + liquidityCollectedBlock: Optional[int] = None + competitionSimulationBlock: Optional[int] = None + auction: Optional[CompetitionAuction] = None + solutions: Optional[List[SolverSettlement]] = Field( + None, + description="Maps from solver name to object describing that solver's settlement.", + ) + + +class OrderCreation(BaseModel): + sellToken: Address = Field(..., description="see `OrderParameters::sellToken`") + buyToken: Address = Field(..., description="see `OrderParameters::buyToken`") + receiver: Optional[Address] = Field( + None, description="see `OrderParameters::receiver`" + ) + sellAmount: TokenAmount = Field( + ..., description="see `OrderParameters::sellAmount`" + ) + buyAmount: TokenAmount = Field(..., description="see `OrderParameters::buyAmount`") + validTo: int = Field(..., description="see `OrderParameters::validTo`") + feeAmount: TokenAmount = Field(..., description="see `OrderParameters::feeAmount`") + kind: OrderKind = Field(..., description="see `OrderParameters::kind`") + partiallyFillable: bool = Field( + ..., description="see `OrderParameters::partiallyFillable`" + ) + sellTokenBalance: Optional[SellTokenSource] = Field( + "erc20", description="see `OrderParameters::sellTokenBalance`" + ) + buyTokenBalance: Optional[BuyTokenDestination] = Field( + "erc20", description="see `OrderParameters::buyTokenBalance`" + ) + signingScheme: SigningScheme + signature: Signature + from_: Optional[Address] = Field( + None, + alias="from", + description="If set, the backend enforces that this address matches what is decoded as the *signer* of\nthe signature. This helps catch errors with invalid signature encodings as the backend\nmight otherwise silently work with an unexpected address that for example does not have\nany balance.\n", + ) + quoteId: Optional[int] = Field( + None, + description="Orders can optionally include a quote ID. This way the order can be linked to a quote\nand enable providing more metadata when analysing order slippage.\n", + ) + appData: Union[AppData, AppDataHash] = Field( + ..., + description="This field comes in two forms for backward compatibility. The hash form will eventually \nstop being accepted.\n", + ) + appDataHash: Optional[AppDataHash] = Field( + None, + description="May be set for debugging purposes. If set, this field is compared to what the backend\ninternally calculates as the app data hash based on the contents of `appData`. If the\nhash does not match, an error is returned. If this field is set, then `appData` **MUST** be\na string encoding of a JSON object.\n", + ) + + +class Order(OrderCreation, OrderMetaData): + pass + + +class AuctionOrder(BaseModel): + uid: UID + sellToken: Address = Field(..., description="see `OrderParameters::sellToken`") + buyToken: Address = Field(..., description="see `OrderParameters::buyToken`") + sellAmount: TokenAmount = Field( + ..., description="see `OrderParameters::sellAmount`" + ) + buyAmount: TokenAmount = Field(..., description="see `OrderParameters::buyAmount`") + userFee: TokenAmount = Field(..., description="see `OrderParameters::feeAmount`") + validTo: int = Field(..., description="see `OrderParameters::validTo`") + kind: OrderKind = Field(..., description="see `OrderParameters::kind`") + receiver: Address = Field(..., description="see `OrderParameters::receiver`") + owner: Address + partiallyFillable: bool = Field( + ..., description="see `OrderParameters::partiallyFillable`" + ) + executed: TokenAmount = Field( + ..., + description="Currently executed amount of sell/buy token, depending on the order kind.\n", + ) + preInteractions: List[InteractionData] = Field( + ..., + description="The pre-interactions that need to be executed before the first execution of the order.\n", + ) + postInteractions: List[InteractionData] = Field( + ..., + description="The post-interactions that need to be executed after the execution of the order.\n", + ) + sellTokenBalance: SellTokenSource = Field( + ..., description="see `OrderParameters::sellTokenBalance`" + ) + buyTokenBalance: BuyTokenDestination = Field( + ..., description="see `OrderParameters::buyTokenBalance`" + ) + class_: OrderClass = Field(..., alias="class") + appData: AppDataHash + signature: Signature + protocolFees: List[FeePolicy] = Field( + ..., + description="The fee policies that are used to compute the protocol fees for this order.\n", + ) + + +class Auction(BaseModel): + id: Optional[int] = Field( + None, + description="The unique identifier of the auction. Increment whenever the backend creates a new auction.\n", + ) + block: Optional[int] = Field( + None, + description="The block number for the auction. Orders and prices are guaranteed to be valid on this\nblock. Proposed settlements should be valid for this block as well.\n", + ) + latestSettlementBlock: Optional[int] = Field( + None, + description="The latest block on which a settlement has been processed.\n\n**NOTE**: Under certain conditions it is possible for a settlement to have been mined as\npart of `block` but not have yet been processed.\n", + ) + orders: Optional[List[AuctionOrder]] = Field( + None, description="The solvable orders included in the auction.\n" + ) + prices: Optional[AuctionPrices] = None \ No newline at end of file From 0f938d5274b6b2d043589a5cead0adc0d03e0ace Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Fri, 16 Aug 2024 09:58:55 -0300 Subject: [PATCH 16/30] add python-dotenv package --- poetry.lock | 27 ++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index ce74efa..1650d28 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2540,6 +2540,20 @@ pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "pyunormalize" version = "15.1.0" @@ -2585,6 +2599,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -2592,8 +2607,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -2610,6 +2633,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -2617,6 +2641,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -3417,4 +3442,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "e02971366a8bd5c85b5e2c32578cd193560f4827d381c2e9341953d453120fad" +content-hash = "cfb0f96417822d96b88102108c501da664fbc58c0cd6f6771524e2152964d0ba" diff --git a/pyproject.toml b/pyproject.toml index 012d3ac..6bb58ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ pydantic = "^2.7.0" pytest-mock = "^3.14.0" backoff = "^2.2.1" aiolimiter = "^1.1.0" +python-dotenv = "^1.0.1" [tool.poetry.group.dev.dependencies] From 5576b57cde5ccab0a9b273b8569f3df5c55e071f Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Fri, 16 Aug 2024 10:00:31 -0300 Subject: [PATCH 17/30] refactor importing sort --- examples/order_posting_e2e.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/order_posting_e2e.py b/examples/order_posting_e2e.py index 5dc9842..a266079 100644 --- a/examples/order_posting_e2e.py +++ b/examples/order_posting_e2e.py @@ -18,13 +18,14 @@ from cow_py.contracts.sign import sign_order as _sign_order from cow_py.order_book.api import OrderBookApi from cow_py.order_book.config import OrderBookAPIConfigFactory -from cow_py.order_book.generated.model import OrderQuoteSide1, TokenAmount -from cow_py.order_book.generated.model import OrderQuoteSideKindSell from cow_py.order_book.generated.model import ( - UID, + OrderQuoteSideKindSell, + OrderQuoteSide1, OrderCreation, OrderQuoteRequest, OrderQuoteResponse, + UID, + TokenAmount, ) BUY_TOKEN = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14" # WETH From a3e1187ee8867300dbd7949fd839b7407f7a869c Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Fri, 16 Aug 2024 10:01:19 -0300 Subject: [PATCH 18/30] refactor .env usage and variables requested --- .env.example | 1 - examples/order_posting_e2e.py | 19 +++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index f0f08f0..6fc16b3 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1 @@ -USER_ADDRESS= PRIVATE_KEY= diff --git a/examples/order_posting_e2e.py b/examples/order_posting_e2e.py index a266079..e0e97ef 100644 --- a/examples/order_posting_e2e.py +++ b/examples/order_posting_e2e.py @@ -7,6 +7,7 @@ import os from dataclasses import asdict +from dotenv import load_dotenv from web3 import Account from cow_py.common.chains import Chain @@ -30,7 +31,7 @@ BUY_TOKEN = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14" # WETH SELL_TOKEN = "0xbe72E441BF55620febc26715db68d3494213D8Cb" # USDC -SELL_AMOUNT_BEFORE_FEE = "10000000000000000000" # 100 USDC with 18 decimals +SELL_AMOUNT_BEFORE_FEE = "5000000000000000000" # 50 USDC with 18 decimals ORDER_KIND = "sell" CHAIN = Chain.SEPOLIA CHAIN_ID = SupportedChainId.SEPOLIA @@ -38,8 +39,14 @@ config = OrderBookAPIConfigFactory.get_config("prod", CHAIN_ID) ORDER_BOOK_API = OrderBookApi(config) -ADDRESS = os.getenv("USER_ADDRESS") -ACCOUNT = Account.from_key(os.getenv("PRIVATE_KEY")) +load_dotenv() + +PRIVATE_KEY = os.getenv("PRIVATE_KEY") + +if not PRIVATE_KEY: + raise ValueError("Missing variables on .env file") + +ACCOUNT = Account.from_key(PRIVATE_KEY) async def get_order_quote( @@ -62,7 +69,7 @@ def sign_order(order: Order) -> EcdsaSignature: async def post_order(order: Order, signature: EcdsaSignature) -> UID: order_creation = OrderCreation( **{ - "from": ADDRESS, + "from": ACCOUNT.address, "sellToken": order.sellToken, "buyToken": order.buyToken, "sellAmount": order.sellAmount, @@ -85,7 +92,7 @@ async def main(): **{ "sellToken": SELL_TOKEN, "buyToken": BUY_TOKEN, - "from": ADDRESS, + "from": ACCOUNT.address, } ) order_side = OrderQuoteSide1( @@ -100,7 +107,7 @@ async def main(): **{ "sellToken": SELL_TOKEN, "buyToken": BUY_TOKEN, - "receiver": ADDRESS, + "receiver": ACCOUNT.address, "validTo": order_quote_dict["validTo"], "appData": "0x0000000000000000000000000000000000000000000000000000000000000000", "sellAmount": SELL_AMOUNT_BEFORE_FEE, # Since it is a sell order, the sellAmountBeforeFee is the same as the sellAmount From 08f1d0391981c9c2378e4d20fa168310f70bdc37 Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Fri, 16 Aug 2024 12:01:55 -0300 Subject: [PATCH 19/30] add appDataHex object and its convertion to cid --- cow_py/app_data/appDataHex.py | 65 ++++++++++++++++++++++++----------- tests/app_data/appDataHex.py | 21 +++++------ 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/cow_py/app_data/appDataHex.py b/cow_py/app_data/appDataHex.py index bb177db..e83505a 100644 --- a/cow_py/app_data/appDataHex.py +++ b/cow_py/app_data/appDataHex.py @@ -1,6 +1,7 @@ +from typing import Dict, Any from web3 import Web3 -from cid import make_cid -import multihash +from multiformats import multibase, CID + from cow_py.app_data.appDataCid import AppDataCid from cow_py.app_data.consts import MetaDataError @@ -9,30 +10,54 @@ class AppDataHex: def __init__(self, app_data_hex: str): self.app_data_hex = app_data_hex - async def to_cid(self) -> AppDataCid: - cid = await self._app_data_hex_to_cid() - await self._assert_cid(cid) + def to_cid(self) -> AppDataCid: + cid = self._app_data_hex_to_cid() + self._assert_cid(cid) return AppDataCid(cid) - async def to_cid_legacy(self) -> AppDataCid: - cid = await self._app_data_hex_to_cid_legacy() - await self._assert_cid(cid) + def to_cid_legacy(self) -> AppDataCid: + cid = self._app_data_hex_to_cid_legacy() + self._assert_cid(cid) return AppDataCid(cid) - async def _assert_cid(self, cid: str): + def _assert_cid(self, cid: str): if not cid: raise MetaDataError( f"Error getting CID from appDataHex: {self.app_data_hex}" ) - async def _app_data_hex_to_cid(self) -> str: - hash_bytes = Web3.to_bytes(hexstr=self.app_data_hex) - mh = multihash.encode(hash_bytes, 'keccak-256') - cid = make_cid(1, 'raw', mh) - return str(cid) - - async def _app_data_hex_to_cid_legacy(self) -> str: - hash_bytes = Web3.to_bytes(hexstr=self.app_data_hex) - mh = multihash.encode(hash_bytes, 'sha2-256') - cid = make_cid(0, 'dag-pb', mh) - return str(cid) \ No newline at end of file + def _app_data_hex_to_cid(self) -> str: + cid_bytes = self._to_cid_bytes( + { + "version": 0x01, # CIDv1 + "multicodec": 0x55, # Raw codec + "hashing_algorithm": 0x1B, # keccak hash algorithm + "hashing_length": 32, # keccak hash length (0x20 = 32) + "multihash_hex": self.app_data_hex, # 32 bytes of the keccak256 hash + } + ) + return multibase.encode(cid_bytes, "base16") + + def _app_data_hex_to_cid_legacy(self) -> str: + cid_bytes = self._to_cid_bytes( + { + "version": 0x01, # CIDv1 + "multicodec": 0x70, # dag-pb + "hashing_algorithm": 0x12, # sha2-256 hash algorithm + "hashing_length": 32, # SHA-256 length (0x20 = 32) + "multihash_hex": self.app_data_hex, # 32 bytes of the sha2-256 hash + } + ) + return str(CID.decode(cid_bytes).set(version=0)) + + def _to_cid_bytes(self, params: Dict[str, Any]) -> bytes: + hash_bytes = Web3.to_bytes(hexstr=params["multihash_hex"]) + cid_prefix = bytes( + [ + params["version"], + params["multicodec"], + params["hashing_algorithm"], + params["hashing_length"], + ] + ) + return cid_prefix + hash_bytes diff --git a/tests/app_data/appDataHex.py b/tests/app_data/appDataHex.py index c0a5f9f..c66e466 100644 --- a/tests/app_data/appDataHex.py +++ b/tests/app_data/appDataHex.py @@ -1,29 +1,26 @@ import pytest +from cow_py.app_data.appDataCid import AppDataCid from cow_py.app_data.appDataHex import AppDataHex from .mocks import APP_DATA_HEX, CID, APP_DATA_HEX_LEGACY, CID_LEGACY -@pytest.mark.asyncio -async def test_app_data_hex_to_cid_happy_path(): - decoded_app_data_hex = await APP_DATA_HEX.to_cid() +def test_app_data_hex_to_cid_happy_path(): + decoded_app_data_hex = APP_DATA_HEX.to_cid() assert decoded_app_data_hex.app_data_cid == CID.app_data_cid -@pytest.mark.asyncio -async def test_app_data_hex_to_cid_invalid_hash(): +def test_app_data_hex_to_cid_invalid_hash(): app_data_hex = AppDataHex("invalidHash") with pytest.raises(Exception): - await app_data_hex("invalidHash") + app_data_hex.to_cid() -@pytest.mark.asyncio -async def test_app_data_hex_to_cid_legacy_happy_path(): - decoded_app_data_hex = await APP_DATA_HEX_LEGACY.to_cid_legacy() +def test_app_data_hex_to_cid_legacy_happy_path(): + decoded_app_data_hex: AppDataCid = APP_DATA_HEX_LEGACY.to_cid_legacy() assert decoded_app_data_hex.app_data_cid == CID_LEGACY.app_data_cid -@pytest.mark.asyncio -async def test_app_data_hex_to_cid_legacy_invalid_hash(): +def test_app_data_hex_to_cid_legacy_invalid_hash(): app_data_hex = AppDataHex("invalidHash") with pytest.raises(Exception): - await app_data_hex.to_cid_legacy() + app_data_hex.to_cid_legacy() From dc75e36c0df1f2e6cc9d2ca4ddc845bb61af78bc Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Fri, 16 Aug 2024 17:19:18 -0300 Subject: [PATCH 20/30] wip: add converstions between cid, hex and doc --- cow_py/app_data/appDataCid.py | 14 ++++++ cow_py/app_data/appDataDoc.py | 85 ++++++++++++++++++++++++++++++++++- cow_py/app_data/appDataHex.py | 32 ++++++++++--- cow_py/app_data/consts.py | 2 +- cow_py/app_data/utils.py | 43 +++++++++--------- tests/app_data/appDataCid.py | 44 ++++++++++++++++++ tests/app_data/appDataHex.py | 51 ++++++++++++++++++--- tests/app_data/mocks.py | 12 +++-- 8 files changed, 243 insertions(+), 40 deletions(-) create mode 100644 tests/app_data/appDataCid.py diff --git a/cow_py/app_data/appDataCid.py b/cow_py/app_data/appDataCid.py index 65c31c0..f07e353 100644 --- a/cow_py/app_data/appDataCid.py +++ b/cow_py/app_data/appDataCid.py @@ -1,3 +1,17 @@ +from typing import Any, Dict +import httpx +from multiformats import CID, multibase + +from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI +from cow_py.app_data.utils import extract_digest, fetch_doc_from_cid + + class AppDataCid: def __init__(self, app_data_cid: str): self.app_data_cid = app_data_cid + + async def to_doc(self, ipfs_uri: str = DEFAULT_IPFS_READ_URI) -> Dict[str, Any]: + return await fetch_doc_from_cid(self.app_data_cid, ipfs_uri) + + def to_hex(self) -> str: + return extract_digest(self.app_data_cid) diff --git a/cow_py/app_data/appDataDoc.py b/cow_py/app_data/appDataDoc.py index 7139bb5..bc16a80 100644 --- a/cow_py/app_data/appDataDoc.py +++ b/cow_py/app_data/appDataDoc.py @@ -1,2 +1,85 @@ +from typing import Dict, Any, TypedDict, Optional + +import httpx + +from cow_py.app_data.consts import DEFAULT_IPFS_WRITE_URI, MetaDataError +from cow_py.app_data.utils import extract_digest, stringify_deterministic + + +class IpfsUploadResult(TypedDict): + appData: str + cid: str + + +class PinataPinResponse(TypedDict): + IpfsHash: str + PinSize: int + Timestamp: str + + +class Ipfs(TypedDict): + writeUri: str + pinataApiKey: str + pinataApiSecret: str + + class AppDataDoc: - pass + def __init__(self, app_data_doc: Dict[str, Any]): + self.app_data_doc = app_data_doc + + # TODO: Missing test + async def upload_to_ipfs_legacy( + self, ipfs_config: Ipfs + ) -> Optional[IpfsUploadResult]: + """ + Uploads a appDocument to IPFS + + @deprecated Pinata IPFS automatically pins the uploaded document using some implicit encoding and hashing algorithm. + This method is not used anymore to make it more explicit these parameters and therefore less dependent on the default implementation of Pinata + + @param app_data_doc Document to upload + @param ipfs_config keys to access the IPFS API + + @returns the IPFS CID v0 of the content + """ + pin_response = await self._pin_json_in_pinata_ipfs( + self.app_data_doc, ipfs_config + ) + cid = pin_response["IpfsHash"] + return { + "appData": extract_digest(cid), + "cid": cid, + } + + async def _pin_json_in_pinata_ipfs( + self, file: Any, ipfs_config: Ipfs + ) -> PinataPinResponse: + write_uri = ipfs_config.get("writeUri", DEFAULT_IPFS_WRITE_URI) + pinata_api_key = ipfs_config.get("pinataApiKey", "") + pinata_api_secret = ipfs_config.get("pinataApiSecret", "") + + if not pinata_api_key or not pinata_api_secret: + raise MetaDataError("You need to pass IPFS api credentials.") + + body = stringify_deterministic( + { + "pinataContent": file, + "pinataMetadata": {"name": "appData"}, + } + ) + + pinata_url = f"{write_uri}/pinning/pinJSONToIPFS" + headers = { + "Content-Type": "application/json", + "pinata_api_key": pinata_api_key, + "pinata_secret_api_key": pinata_api_secret, + } + + async with httpx.AsyncClient() as client: + response = await client.post(pinata_url, json=body, headers=headers) + data = response.json() + if response.status_code != 200: + raise Exception( + data.get("error", {}).get("details") or data.get("error") + ) + return data diff --git a/cow_py/app_data/appDataHex.py b/cow_py/app_data/appDataHex.py index e83505a..f73bcdb 100644 --- a/cow_py/app_data/appDataHex.py +++ b/cow_py/app_data/appDataHex.py @@ -3,22 +3,44 @@ from multiformats import multibase, CID from cow_py.app_data.appDataCid import AppDataCid -from cow_py.app_data.consts import MetaDataError +from cow_py.app_data.appDataDoc import AppDataDoc +from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI, MetaDataError +from cow_py.app_data.utils import fetch_doc_from_cid class AppDataHex: def __init__(self, app_data_hex: str): self.app_data_hex = app_data_hex - def to_cid(self) -> AppDataCid: + def to_cid(self) -> str: cid = self._app_data_hex_to_cid() self._assert_cid(cid) - return AppDataCid(cid) + return cid - def to_cid_legacy(self) -> AppDataCid: + def to_cid_legacy(self) -> str: cid = self._app_data_hex_to_cid_legacy() self._assert_cid(cid) - return AppDataCid(cid) + return cid + + async def to_doc(self, ipfs_uri: str = DEFAULT_IPFS_READ_URI) -> Dict[str, Any]: + try: + cid = self.to_cid() + return await fetch_doc_from_cid(cid, ipfs_uri) + except Exception as e: + raise MetaDataError( + f"Unexpected error decoding AppData: appDataHex={self.app_data_hex}, message={e}" + ) + + async def to_doc_legacy( + self, ipfs_uri: str = DEFAULT_IPFS_READ_URI + ) -> Dict[str, Any]: + try: + cid = self.to_cid_legacy() + return await fetch_doc_from_cid(cid, ipfs_uri) + except Exception as e: + raise MetaDataError( + f"Unexpected error decoding AppData: appDataHex={self.app_data_hex}, message={e}" + ) def _assert_cid(self, cid: str): if not cid: diff --git a/cow_py/app_data/consts.py b/cow_py/app_data/consts.py index e02c39a..736363d 100644 --- a/cow_py/app_data/consts.py +++ b/cow_py/app_data/consts.py @@ -1,5 +1,5 @@ DEFAULT_IPFS_READ_URI = "https://cloudflare-ipfs.com/ipfs" -DEFAULT_IFPS_WRITE_URI = "https://api.pinata.cloud" +DEFAULT_IPFS_WRITE_URI = "https://api.pinata.cloud" class MetaDataError(Exception): diff --git a/cow_py/app_data/utils.py b/cow_py/app_data/utils.py index c109c67..df104a0 100644 --- a/cow_py/app_data/utils.py +++ b/cow_py/app_data/utils.py @@ -1,37 +1,34 @@ -from typing import Union +from re import T +from typing import Any, Dict +import httpx from multiformats import CID -from multiformats import base16, base58btc -import binascii + import json +from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI + # CID uses multibase to self-describe the encoding used (See https://github.com/multiformats/multibase) # - Most reference implementations (multiformats/cid or Pinata, etc) use base58btc encoding # - However, the backend uses base16 encoding (See https://github.com/cowprotocol/services/blob/main/crates/app-data-hash/src/lib.rs#L64) MULTIBASE_BASE16 = "f" -def parse_cid(ipfs_hash: str) -> CID: - decoder = get_decoder(ipfs_hash) - return CID.decode(ipfs_hash) - - -def decode_cid(bytes_: bytes) -> CID: - return CID.decode(bytes_) - +def extract_digest(cid_str: str) -> str: + # TODO: Verify this + cid = CID.decode(cid_str) + if cid_str[0] == MULTIBASE_BASE16: + return "0x" + cid.digest.hex()[4:] -def get_decoder(ipfs_hash: str) -> Union[base16, base58btc]: - if ipfs_hash[0] == MULTIBASE_BASE16: - # Base 16 encoding - return base16 - # Use default decoder (base58btc) - return base58btc - - -def extract_digest(cid: str) -> str: - cid_details = parse_cid(cid) - digest = cid_details.multihash.digest - return f"0x{binascii.hexlify(digest).decode('ascii')}" + return "0x" + cid.set(base="base58btc").digest.hex()[4:] def stringify_deterministic(obj): return json.dumps(obj, sort_keys=True, separators=(",", ":")) + + +async def fetch_doc_from_cid( + cid: str, ipfs_uri: str = DEFAULT_IPFS_READ_URI +) -> Dict[str, Any]: + async with httpx.AsyncClient() as client: + response = await client.get(f"{ipfs_uri}/{cid}") + return response.json() diff --git a/tests/app_data/appDataCid.py b/tests/app_data/appDataCid.py new file mode 100644 index 0000000..dca467a --- /dev/null +++ b/tests/app_data/appDataCid.py @@ -0,0 +1,44 @@ +import httpx +import pytest +from unittest.mock import patch +from cow_py.app_data.appDataCid import AppDataCid +from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI +from .mocks import APP_DATA_HEX, CID, APP_DATA_HEX_2, CID_2 + + +@pytest.mark.asyncio +async def test_fetch_doc_from_cid(): + valid_serialized_cid = "QmZZhNnqMF1gRywNKnTPuZksX7rVjQgTT3TJAZ7R6VE3b2" + expected = { + "appCode": "CowSwap", + "metadata": { + "referrer": { + "address": "0x1f5B740436Fc5935622e92aa3b46818906F416E9", + "version": "0.1.0", + } + }, + "version": "0.1.0", + } + + with patch("cow_py.app_data.appDataCid.httpx.AsyncClient.get") as mock_get: + mock_get.return_value = httpx.Response(200, json=expected) + + app_data_hex = AppDataCid(valid_serialized_cid) + app_data_document = await app_data_hex.to_doc() + + assert app_data_document == expected + mock_get.assert_called_once_with(f"{DEFAULT_IPFS_READ_URI}/{valid_serialized_cid}") + + +def test_app_data_cid_to_hex(): + decoded_app_data_hex = CID.to_hex() + assert decoded_app_data_hex == APP_DATA_HEX.app_data_hex + + decoded_app_data_hex_2 = CID_2.to_hex() + assert decoded_app_data_hex_2 == APP_DATA_HEX_2.app_data_hex + + +def test_app_data_cid_to_hex_invalid_hash(): + app_data_cid = AppDataCid("invalidCid") + with pytest.raises(Exception): + app_data_cid.to_hex() diff --git a/tests/app_data/appDataHex.py b/tests/app_data/appDataHex.py index c66e466..0b00ddd 100644 --- a/tests/app_data/appDataHex.py +++ b/tests/app_data/appDataHex.py @@ -1,12 +1,24 @@ +from email.policy import HTTP +import httpx import pytest from cow_py.app_data.appDataCid import AppDataCid +from unittest.mock import patch +from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI, MetaDataError from cow_py.app_data.appDataHex import AppDataHex -from .mocks import APP_DATA_HEX, CID, APP_DATA_HEX_LEGACY, CID_LEGACY +from .mocks import ( + APP_DATA_DOC_CUSTOM, + APP_DATA_HEX, + CID, + APP_DATA_HEX_LEGACY, + CID_LEGACY, + HTTP_STATUS_INTERNAL_ERROR, + HTTP_STATUS_OK, +) -def test_app_data_hex_to_cid_happy_path(): - decoded_app_data_hex = APP_DATA_HEX.to_cid() - assert decoded_app_data_hex.app_data_cid == CID.app_data_cid +def test_app_data_hex_to_cid(): + decoded_app_data_cid = APP_DATA_HEX.to_cid() + assert decoded_app_data_cid == CID.app_data_cid def test_app_data_hex_to_cid_invalid_hash(): @@ -15,12 +27,37 @@ def test_app_data_hex_to_cid_invalid_hash(): app_data_hex.to_cid() -def test_app_data_hex_to_cid_legacy_happy_path(): - decoded_app_data_hex: AppDataCid = APP_DATA_HEX_LEGACY.to_cid_legacy() - assert decoded_app_data_hex.app_data_cid == CID_LEGACY.app_data_cid +def test_app_data_hex_to_cid_legacy(): + decoded_app_data_cid = APP_DATA_HEX_LEGACY.to_cid_legacy() + assert decoded_app_data_cid == CID_LEGACY.app_data_cid def test_app_data_hex_to_cid_legacy_invalid_hash(): app_data_hex = AppDataHex("invalidHash") with pytest.raises(Exception): app_data_hex.to_cid_legacy() + + +@pytest.mark.asyncio +async def test_fetch_doc_from_app_data_hex_legacy(): + with patch("httpx.AsyncClient.get") as mock_get: + mock_get.return_value = httpx.Response(HTTP_STATUS_OK, json=APP_DATA_DOC_CUSTOM) + mock_get.return_value.status_code = HTTP_STATUS_OK + + app_data_doc = await APP_DATA_HEX_LEGACY.to_doc_legacy() + + mock_get.assert_called_once_with( + f"{DEFAULT_IPFS_READ_URI}/{CID_LEGACY.app_data_cid}" + ) + assert app_data_doc == APP_DATA_DOC_CUSTOM + + +@pytest.mark.asyncio +async def test_fetch_doc_from_app_data_hex_invalid_hash(): + with patch("httpx.AsyncClient.get") as mock_get: + mock_get.return_value = httpx.Response(HTTP_STATUS_INTERNAL_ERROR) + + app_data_hex = AppDataHex("invalidHash") + + with pytest.raises(MetaDataError): + await app_data_hex.to_doc() diff --git a/tests/app_data/mocks.py b/tests/app_data/mocks.py index 0958914..0d943fd 100644 --- a/tests/app_data/mocks.py +++ b/tests/app_data/mocks.py @@ -12,7 +12,9 @@ APP_DATA_STRING = '{"appCode":"CoW Swap","metadata":{},"version":"0.7.0"}' -CID = AppDataCid("f01551b20337aa6e6c2a7a0d1eb79a35ebd88b08fc963d5f7a3fc953b7ffb2b7f5898a1df") +CID = AppDataCid( + "f01551b20337aa6e6c2a7a0d1eb79a35ebd88b08fc963d5f7a3fc953b7ffb2b7f5898a1df" +) APP_DATA_HEX = AppDataHex( "0x337aa6e6c2a7a0d1eb79a35ebd88b08fc963d5f7a3fc953b7ffb2b7f5898a1df" @@ -35,9 +37,13 @@ APP_DATA_STRING_2 = '{"appCode":"CoW Swap","environment":"production","metadata":{"quote":{"slippageBips":"50","version":"0.2.0"},"orderClass":{"orderClass":"market","version":"0.1.0"}},"version":"0.6.0"}' -CID_2 = "f01551b208af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" +CID_2 = AppDataCid( + "f01551b208af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" +) -APP_DATA_HEX_2 = "0x8af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" +APP_DATA_HEX_2 = AppDataHex( + "0x8af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" +) APP_DATA_STRING_LEGACY = '{"version":"0.7.0","appCode":"CowSwap","metadata":{}}' From 659499da4abaf24f6195c54500e2cfcdadac587c Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Mon, 26 Aug 2024 15:01:52 -0300 Subject: [PATCH 21/30] rename files to snakecase --- .../{appDataCid.py => app_data_cid.py} | 2 -- .../{appDataDoc.py => app_data_doc.py} | 0 .../{appDataHex.py => app_data_hex.py} | 30 +++++++++++-------- .../{appDataCid.py => app_data_cid.py} | 4 +-- .../{appDataHex.py => app_data_hex.py} | 4 +-- tests/app_data/mocks.py | 4 +-- 6 files changed, 23 insertions(+), 21 deletions(-) rename cow_py/app_data/{appDataCid.py => app_data_cid.py} (90%) rename cow_py/app_data/{appDataDoc.py => app_data_doc.py} (100%) rename cow_py/app_data/{appDataHex.py => app_data_hex.py} (75%) rename tests/app_data/{appDataCid.py => app_data_cid.py} (90%) rename tests/app_data/{appDataHex.py => app_data_hex.py} (93%) diff --git a/cow_py/app_data/appDataCid.py b/cow_py/app_data/app_data_cid.py similarity index 90% rename from cow_py/app_data/appDataCid.py rename to cow_py/app_data/app_data_cid.py index f07e353..df4327b 100644 --- a/cow_py/app_data/appDataCid.py +++ b/cow_py/app_data/app_data_cid.py @@ -1,6 +1,4 @@ from typing import Any, Dict -import httpx -from multiformats import CID, multibase from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI from cow_py.app_data.utils import extract_digest, fetch_doc_from_cid diff --git a/cow_py/app_data/appDataDoc.py b/cow_py/app_data/app_data_doc.py similarity index 100% rename from cow_py/app_data/appDataDoc.py rename to cow_py/app_data/app_data_doc.py diff --git a/cow_py/app_data/appDataHex.py b/cow_py/app_data/app_data_hex.py similarity index 75% rename from cow_py/app_data/appDataHex.py rename to cow_py/app_data/app_data_hex.py index f73bcdb..089f942 100644 --- a/cow_py/app_data/appDataHex.py +++ b/cow_py/app_data/app_data_hex.py @@ -2,11 +2,17 @@ from web3 import Web3 from multiformats import multibase, CID -from cow_py.app_data.appDataCid import AppDataCid -from cow_py.app_data.appDataDoc import AppDataDoc from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI, MetaDataError from cow_py.app_data.utils import fetch_doc_from_cid +CID_V1_PREFIX = 0x01 +CID_RAW_MULTICODEC = 0x55 +KECCAK_HASHING_ALGORITHM = 0x1B +KECCAK_HASHING_LENGTH = 32 +CID_DAG_PB_MULTICODEC = 0x70 +SHA2_256_HASHING_ALGORITHM = 0x12 +SHA2_256_HASHING_LENGTH = 32 + class AppDataHex: def __init__(self, app_data_hex: str): @@ -51,11 +57,11 @@ def _assert_cid(self, cid: str): def _app_data_hex_to_cid(self) -> str: cid_bytes = self._to_cid_bytes( { - "version": 0x01, # CIDv1 - "multicodec": 0x55, # Raw codec - "hashing_algorithm": 0x1B, # keccak hash algorithm - "hashing_length": 32, # keccak hash length (0x20 = 32) - "multihash_hex": self.app_data_hex, # 32 bytes of the keccak256 hash + "version": CID_V1_PREFIX, + "multicodec": CID_RAW_MULTICODEC, + "hashing_algorithm": KECCAK_HASHING_ALGORITHM, + "hashing_length": KECCAK_HASHING_LENGTH, + "multihash_hex": self.app_data_hex, } ) return multibase.encode(cid_bytes, "base16") @@ -63,11 +69,11 @@ def _app_data_hex_to_cid(self) -> str: def _app_data_hex_to_cid_legacy(self) -> str: cid_bytes = self._to_cid_bytes( { - "version": 0x01, # CIDv1 - "multicodec": 0x70, # dag-pb - "hashing_algorithm": 0x12, # sha2-256 hash algorithm - "hashing_length": 32, # SHA-256 length (0x20 = 32) - "multihash_hex": self.app_data_hex, # 32 bytes of the sha2-256 hash + "version": CID_V1_PREFIX, + "multicodec": CID_DAG_PB_MULTICODEC, + "hashing_algorithm": SHA2_256_HASHING_ALGORITHM, + "hashing_length": SHA2_256_HASHING_LENGTH, + "multihash_hex": self.app_data_hex, } ) return str(CID.decode(cid_bytes).set(version=0)) diff --git a/tests/app_data/appDataCid.py b/tests/app_data/app_data_cid.py similarity index 90% rename from tests/app_data/appDataCid.py rename to tests/app_data/app_data_cid.py index dca467a..6ca50bc 100644 --- a/tests/app_data/appDataCid.py +++ b/tests/app_data/app_data_cid.py @@ -1,7 +1,7 @@ import httpx import pytest from unittest.mock import patch -from cow_py.app_data.appDataCid import AppDataCid +from cow_py.app_data.app_data_cid import AppDataCid from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI from .mocks import APP_DATA_HEX, CID, APP_DATA_HEX_2, CID_2 @@ -20,7 +20,7 @@ async def test_fetch_doc_from_cid(): "version": "0.1.0", } - with patch("cow_py.app_data.appDataCid.httpx.AsyncClient.get") as mock_get: + with patch("httpx.AsyncClient.get") as mock_get: mock_get.return_value = httpx.Response(200, json=expected) app_data_hex = AppDataCid(valid_serialized_cid) diff --git a/tests/app_data/appDataHex.py b/tests/app_data/app_data_hex.py similarity index 93% rename from tests/app_data/appDataHex.py rename to tests/app_data/app_data_hex.py index 0b00ddd..a599452 100644 --- a/tests/app_data/appDataHex.py +++ b/tests/app_data/app_data_hex.py @@ -1,10 +1,8 @@ -from email.policy import HTTP import httpx import pytest -from cow_py.app_data.appDataCid import AppDataCid from unittest.mock import patch from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI, MetaDataError -from cow_py.app_data.appDataHex import AppDataHex +from cow_py.app_data.app_data_hex import AppDataHex from .mocks import ( APP_DATA_DOC_CUSTOM, APP_DATA_HEX, diff --git a/tests/app_data/mocks.py b/tests/app_data/mocks.py index 0d943fd..9cd8fdb 100644 --- a/tests/app_data/mocks.py +++ b/tests/app_data/mocks.py @@ -1,5 +1,5 @@ -from cow_py.app_data.appDataCid import AppDataCid -from cow_py.app_data.appDataHex import AppDataHex +from cow_py.app_data.app_data_cid import AppDataCid +from cow_py.app_data.app_data_hex import AppDataHex HTTP_STATUS_OK = 200 HTTP_STATUS_INTERNAL_ERROR = 500 From afa6893f4b0f32f1aa1ec03f4bfc04c9061c562e Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Mon, 26 Aug 2024 15:14:25 -0300 Subject: [PATCH 22/30] enable env modify ipfs urls --- cow_py/app_data/consts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cow_py/app_data/consts.py b/cow_py/app_data/consts.py index 736363d..75fb7ff 100644 --- a/cow_py/app_data/consts.py +++ b/cow_py/app_data/consts.py @@ -1,5 +1,7 @@ -DEFAULT_IPFS_READ_URI = "https://cloudflare-ipfs.com/ipfs" -DEFAULT_IPFS_WRITE_URI = "https://api.pinata.cloud" +import os + +DEFAULT_IPFS_READ_URI = os.getenv("IPFS_READ_URI", "https://cloudflare-ipfs.com/ipfs") +DEFAULT_IPFS_WRITE_URI = os.getenv("IPFS_WRITE_URI", "https://api.pinata.cloud") class MetaDataError(Exception): From 9b3407ab0cf1adee2041319f210854a192fadff3 Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Mon, 26 Aug 2024 17:37:25 -0300 Subject: [PATCH 23/30] add app data doc tests --- cow_py/app_data/app_data_doc.py | 72 +++++++++--------------------- cow_py/app_data/consts.py | 9 +++- poetry.lock | 26 ++++++++++- pyproject.toml | 2 + tests/app_data/app_data_doc.py | 78 +++++++++++++++++++++++++++++++++ tests/app_data/mocks.py | 3 ++ tests/app_data/utils.py | 50 +++++++++++++++++++++ 7 files changed, 186 insertions(+), 54 deletions(-) create mode 100644 tests/app_data/app_data_doc.py create mode 100644 tests/app_data/utils.py diff --git a/cow_py/app_data/app_data_doc.py b/cow_py/app_data/app_data_doc.py index bc16a80..825c646 100644 --- a/cow_py/app_data/app_data_doc.py +++ b/cow_py/app_data/app_data_doc.py @@ -1,8 +1,8 @@ from typing import Dict, Any, TypedDict, Optional -import httpx +from pinatapy import PinataPy -from cow_py.app_data.consts import DEFAULT_IPFS_WRITE_URI, MetaDataError +from cow_py.app_data.consts import DEFAULT_APP_DATA_DOC, LATEST_APP_DATA_VERSION from cow_py.app_data.utils import extract_digest, stringify_deterministic @@ -11,25 +11,22 @@ class IpfsUploadResult(TypedDict): cid: str -class PinataPinResponse(TypedDict): - IpfsHash: str - PinSize: int - Timestamp: str - - -class Ipfs(TypedDict): - writeUri: str - pinataApiKey: str - pinataApiSecret: str +class PinataConfig(TypedDict): + pinata_api_key: str + pinata_api_secret: str class AppDataDoc: - def __init__(self, app_data_doc: Dict[str, Any]): - self.app_data_doc = app_data_doc + def __init__(self, app_data_doc: Dict[str, Any] = {}): + self.app_data_doc = { + **DEFAULT_APP_DATA_DOC, + **app_data_doc, + "version": LATEST_APP_DATA_VERSION, + } - # TODO: Missing test - async def upload_to_ipfs_legacy( - self, ipfs_config: Ipfs + # TODO: make this async + def upload_to_ipfs_legacy( + self, ipfs_config: PinataConfig ) -> Optional[IpfsUploadResult]: """ Uploads a appDocument to IPFS @@ -42,44 +39,15 @@ async def upload_to_ipfs_legacy( @returns the IPFS CID v0 of the content """ - pin_response = await self._pin_json_in_pinata_ipfs( - self.app_data_doc, ipfs_config + pinata = PinataPy( + ipfs_config.get("pinata_api_key", ""), + ipfs_config.get("pinata_api_secret", ""), + ) + pin_response = pinata.pin_json_to_ipfs( + stringify_deterministic(self.app_data_doc) ) cid = pin_response["IpfsHash"] return { "appData": extract_digest(cid), "cid": cid, } - - async def _pin_json_in_pinata_ipfs( - self, file: Any, ipfs_config: Ipfs - ) -> PinataPinResponse: - write_uri = ipfs_config.get("writeUri", DEFAULT_IPFS_WRITE_URI) - pinata_api_key = ipfs_config.get("pinataApiKey", "") - pinata_api_secret = ipfs_config.get("pinataApiSecret", "") - - if not pinata_api_key or not pinata_api_secret: - raise MetaDataError("You need to pass IPFS api credentials.") - - body = stringify_deterministic( - { - "pinataContent": file, - "pinataMetadata": {"name": "appData"}, - } - ) - - pinata_url = f"{write_uri}/pinning/pinJSONToIPFS" - headers = { - "Content-Type": "application/json", - "pinata_api_key": pinata_api_key, - "pinata_secret_api_key": pinata_api_secret, - } - - async with httpx.AsyncClient() as client: - response = await client.post(pinata_url, json=body, headers=headers) - data = response.json() - if response.status_code != 200: - raise Exception( - data.get("error", {}).get("details") or data.get("error") - ) - return data diff --git a/cow_py/app_data/consts.py b/cow_py/app_data/consts.py index 75fb7ff..4f7e2c4 100644 --- a/cow_py/app_data/consts.py +++ b/cow_py/app_data/consts.py @@ -1,7 +1,14 @@ import os DEFAULT_IPFS_READ_URI = os.getenv("IPFS_READ_URI", "https://cloudflare-ipfs.com/ipfs") -DEFAULT_IPFS_WRITE_URI = os.getenv("IPFS_WRITE_URI", "https://api.pinata.cloud") + +LATEST_APP_DATA_VERSION = "1.1.0" +DEFAULT_APP_CODE = "CoW Swap" +DEFAULT_APP_DATA_DOC = { + "appCode": DEFAULT_APP_CODE, + "metadata": {}, + "version": LATEST_APP_DATA_VERSION, +} class MetaDataError(Exception): diff --git a/poetry.lock b/poetry.lock index b2700db..b6eb472 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1598,6 +1598,16 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "json-stable-stringify-python" +version = "0.2" +description = "Deterministic JSON stringify" +optional = false +python-versions = "*" +files = [ + {file = "json_stable_stringify_python-0.2.tar.gz", hash = "sha256:28057974145844012d0f4628c1ae4a4fe31119293453cd898fe741e6dfb54171"}, +] + [[package]] name = "jsonref" version = "1.1.0" @@ -2133,6 +2143,20 @@ files = [ [package.dependencies] ptyprocess = ">=0.5" +[[package]] +name = "pinatapy-vourhey" +version = "0.2.0" +description = "Non-official Pinata.cloud library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pinatapy-vourhey-0.2.0.tar.gz", hash = "sha256:453a32629ad11b92dd7153feb64d4302c8d17036cc17c5c3d8108809700bdc2b"}, + {file = "pinatapy_vourhey-0.2.0-py3-none-any.whl", hash = "sha256:86c204949eb9dd8e21ca28e5ee1f7b774a72f57f998ef7fba8a4ca41dd5a2554"}, +] + +[package.dependencies] +requests = "*" + [[package]] name = "platformdirs" version = "4.2.0" @@ -3647,4 +3671,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "993952ba1b62b880a9fea993c0205b00d38854570bbde67475baa21f96bf18e8" +content-hash = "6b5c40cee6c82b4f7505408e1e904ddd0fecd377dd2a78103ab06ccc3aca4236" diff --git a/pyproject.toml b/pyproject.toml index 2c4eb2c..201c385 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,8 @@ jsonref = "^1.1.0" multiformats = "^0.3.1.post4" py-cid = "^0.3.0" pymultihash = "^0.8.2" +pinatapy-vourhey = "^0.2.0" +json-stable-stringify-python = "^0.2" [tool.poetry.group.dev.dependencies] diff --git a/tests/app_data/app_data_doc.py b/tests/app_data/app_data_doc.py new file mode 100644 index 0000000..3f0e39e --- /dev/null +++ b/tests/app_data/app_data_doc.py @@ -0,0 +1,78 @@ +from unittest.mock import patch +import httpx +import pytest +from cow_py.app_data.app_data_doc import AppDataDoc +from .mocks import ( + APP_DATA_DOC_CUSTOM, + HTTP_STATUS_INTERNAL_ERROR, + HTTP_STATUS_OK, + IPFS_HASH, + IPFS_HASH_DIGEST, + PINATA_API_KEY, + PINATA_API_SECRET, +) + + +def test_init_empty_metadata(): + app_data_doc = AppDataDoc() + assert app_data_doc.app_data_doc.get("version") + assert app_data_doc.app_data_doc.get("metadata") == {} + assert app_data_doc.app_data_doc.get("appCode") == "CoW Swap" + assert app_data_doc.app_data_doc.get("environment") is None + + +def test_init_custom_metadata(): + app_data_doc = AppDataDoc(APP_DATA_DOC_CUSTOM) + assert app_data_doc.app_data_doc + assert app_data_doc.app_data_doc.get("metadata") == APP_DATA_DOC_CUSTOM.get( + "metadata" + ) + assert app_data_doc.app_data_doc.get("appCode") == APP_DATA_DOC_CUSTOM.get( + "appCode" + ) + assert app_data_doc.app_data_doc.get("environment") == APP_DATA_DOC_CUSTOM.get( + "environment" + ) + + +def test_upload_to_ipfs_legacy_without_ipfs_config(): + app_data_doc = AppDataDoc() + with pytest.raises(Exception): + with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: + mock_pin.return_value = { + "status": HTTP_STATUS_INTERNAL_ERROR, + "reason": "missing ipfs config", + "text": "missing ipfs config", + } + app_data_doc.upload_to_ipfs_legacy({}) + + +def test_upload_to_ipfs_legacy_wrong_credentials(): + app_data_doc = AppDataDoc() + with pytest.raises(Exception): + with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: + mock_pin.return_value = { + "status": HTTP_STATUS_INTERNAL_ERROR, + "reason": "wrong credentials", + "text": "wrong credentials", + } + app_data_doc.upload_to_ipfs_legacy( + { + "pinata_api_key": PINATA_API_KEY, + "pinata_api_secret": PINATA_API_SECRET, + } + ) + + +def test_upload_to_ipfs_legacy(): + app_data_doc = AppDataDoc(APP_DATA_DOC_CUSTOM) + with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: + mock_pin.return_value = {"IpfsHash": IPFS_HASH} + upload_result = app_data_doc.upload_to_ipfs_legacy( + {"pinata_api_key": PINATA_API_KEY, "pinata_api_secret": PINATA_API_SECRET} + ) + assert upload_result == { + "appData": IPFS_HASH_DIGEST, + "cid": IPFS_HASH, + } + assert mock_pin.call_count == 1 diff --git a/tests/app_data/mocks.py b/tests/app_data/mocks.py index 9cd8fdb..ab6e9f0 100644 --- a/tests/app_data/mocks.py +++ b/tests/app_data/mocks.py @@ -55,3 +55,6 @@ PINATA_API_KEY = "apikey" PINATA_API_SECRET = "apiSecret" + +IPFS_HASH = "QmU4j5Y6JM9DqQ6yxB6nMHq4GChWg1zPehs1U7nGPHABRu" +IPFS_HASH_DIGEST = "0x5511c4eac66ab272d9a6ab90e07977d00ff7375fc4dc1038a3c05b2c16ca0b74" diff --git a/tests/app_data/utils.py b/tests/app_data/utils.py new file mode 100644 index 0000000..b116501 --- /dev/null +++ b/tests/app_data/utils.py @@ -0,0 +1,50 @@ +from cow_py.app_data.utils import stringify_deterministic + + +def test_stringify_deterministic_simple_object(): + node = {"c": 6, "b": [4, 5], "a": 3, "z": None} + actual = stringify_deterministic(node) + expected = '{"a":3,"b":[4,5],"c":6,"z":null}' + assert actual == expected + + +def test_stringify_deterministic_object_with_empty_string(): + node = {"a": 3, "z": ""} + actual = stringify_deterministic(node) + expected = '{"a":3,"z":""}' + assert actual == expected + + +def test_stringify_deterministic_nested_object(): + node = {"a": {"b": {"c": [1, 2, 3, None]}}} + actual = stringify_deterministic(node) + expected = '{"a":{"b":{"c":[1,2,3,null]}}}' + assert actual == expected + + +def test_stringify_deterministic_array_with_objects(): + node = [{"z": 1, "a": 2}] + actual = stringify_deterministic(node) + expected = '[{"a":2,"z":1}]' + assert actual == expected + + +def test_stringify_deterministic_nested_array_objects(): + node = [{"z": [[{"y": 1, "b": 2}]], "a": 2}] + actual = stringify_deterministic(node) + expected = '[{"a":2,"z":[[{"b":2,"y":1}]]}]' + assert actual == expected + + +def test_stringify_deterministic_array_with_none(): + node = [1, None] + actual = stringify_deterministic(node) + expected = "[1,null]" + assert actual == expected + + +def test_stringify_deterministic_array_with_empty_string(): + node = [1, ""] + actual = stringify_deterministic(node) + expected = '[1,""]' + assert actual == expected From ba4c3c499f709617317095583ce7e76eeb5a288f Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Mon, 26 Aug 2024 18:22:52 -0300 Subject: [PATCH 24/30] wip: start app data doc to cid --- cow_py/app_data/app_data_doc.py | 30 +++++++++++++++++++----- tests/app_data/app_data_doc.py | 41 ++++++++++++++++++++++----------- tests/app_data/mocks.py | 31 +++++++++++++++++-------- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/cow_py/app_data/app_data_doc.py b/cow_py/app_data/app_data_doc.py index 825c646..c503256 100644 --- a/cow_py/app_data/app_data_doc.py +++ b/cow_py/app_data/app_data_doc.py @@ -1,8 +1,11 @@ from typing import Dict, Any, TypedDict, Optional +from multiformats import CID, multihash from pinatapy import PinataPy +from eth_utils import keccak, to_bytes -from cow_py.app_data.consts import DEFAULT_APP_DATA_DOC, LATEST_APP_DATA_VERSION +from cow_py.app_data.app_data_hex import AppDataHex +from cow_py.app_data.consts import DEFAULT_APP_DATA_DOC from cow_py.app_data.utils import extract_digest, stringify_deterministic @@ -18,11 +21,7 @@ class PinataConfig(TypedDict): class AppDataDoc: def __init__(self, app_data_doc: Dict[str, Any] = {}): - self.app_data_doc = { - **DEFAULT_APP_DATA_DOC, - **app_data_doc, - "version": LATEST_APP_DATA_VERSION, - } + self.app_data_doc = {**DEFAULT_APP_DATA_DOC, **app_data_doc} # TODO: make this async def upload_to_ipfs_legacy( @@ -51,3 +50,22 @@ def upload_to_ipfs_legacy( "appData": extract_digest(cid), "cid": cid, } + + def to_cid(self): + full_app_data_json = stringify_deterministic(self.app_data_doc) + + # TODO: add validation of app data + app_data_bytes = to_bytes(text=full_app_data_json) + appDataHex = AppDataHex(keccak(app_data_bytes).hex()) + return appDataHex.to_cid() + + def to_cid_legacy(self) -> str: + full_app_data_json = stringify_deterministic(self.app_data_doc) + + # TODO: add validation of app data + data_bytes = to_bytes(text=full_app_data_json) + sha2_256 = multihash.get("sha2-256") + mh = sha2_256.digest(data_bytes) + cid = CID("base58btc", 0, "dag-pb", mh) + + return str(cid) diff --git a/tests/app_data/app_data_doc.py b/tests/app_data/app_data_doc.py index 3f0e39e..755e019 100644 --- a/tests/app_data/app_data_doc.py +++ b/tests/app_data/app_data_doc.py @@ -1,11 +1,14 @@ from unittest.mock import patch -import httpx import pytest from cow_py.app_data.app_data_doc import AppDataDoc from .mocks import ( - APP_DATA_DOC_CUSTOM, + APP_DATA_2, + APP_DATA_DOC, + APP_DATA_DOC_CUSTOM_VALUES, + CID, + CID_2, + CID_LEGACY, HTTP_STATUS_INTERNAL_ERROR, - HTTP_STATUS_OK, IPFS_HASH, IPFS_HASH_DIGEST, PINATA_API_KEY, @@ -22,21 +25,20 @@ def test_init_empty_metadata(): def test_init_custom_metadata(): - app_data_doc = AppDataDoc(APP_DATA_DOC_CUSTOM) + app_data_doc = AppDataDoc(APP_DATA_DOC_CUSTOM_VALUES) assert app_data_doc.app_data_doc - assert app_data_doc.app_data_doc.get("metadata") == APP_DATA_DOC_CUSTOM.get( + assert app_data_doc.app_data_doc.get("metadata") == APP_DATA_DOC_CUSTOM_VALUES.get( "metadata" ) - assert app_data_doc.app_data_doc.get("appCode") == APP_DATA_DOC_CUSTOM.get( + assert app_data_doc.app_data_doc.get("appCode") == APP_DATA_DOC_CUSTOM_VALUES.get( "appCode" ) - assert app_data_doc.app_data_doc.get("environment") == APP_DATA_DOC_CUSTOM.get( + assert app_data_doc.app_data_doc.get( "environment" - ) + ) == APP_DATA_DOC_CUSTOM_VALUES.get("environment") def test_upload_to_ipfs_legacy_without_ipfs_config(): - app_data_doc = AppDataDoc() with pytest.raises(Exception): with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: mock_pin.return_value = { @@ -44,11 +46,10 @@ def test_upload_to_ipfs_legacy_without_ipfs_config(): "reason": "missing ipfs config", "text": "missing ipfs config", } - app_data_doc.upload_to_ipfs_legacy({}) + APP_DATA_DOC.upload_to_ipfs_legacy({}) def test_upload_to_ipfs_legacy_wrong_credentials(): - app_data_doc = AppDataDoc() with pytest.raises(Exception): with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: mock_pin.return_value = { @@ -56,7 +57,7 @@ def test_upload_to_ipfs_legacy_wrong_credentials(): "reason": "wrong credentials", "text": "wrong credentials", } - app_data_doc.upload_to_ipfs_legacy( + APP_DATA_DOC.upload_to_ipfs_legacy( { "pinata_api_key": PINATA_API_KEY, "pinata_api_secret": PINATA_API_SECRET, @@ -65,10 +66,9 @@ def test_upload_to_ipfs_legacy_wrong_credentials(): def test_upload_to_ipfs_legacy(): - app_data_doc = AppDataDoc(APP_DATA_DOC_CUSTOM) with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: mock_pin.return_value = {"IpfsHash": IPFS_HASH} - upload_result = app_data_doc.upload_to_ipfs_legacy( + upload_result = APP_DATA_DOC.upload_to_ipfs_legacy( {"pinata_api_key": PINATA_API_KEY, "pinata_api_secret": PINATA_API_SECRET} ) assert upload_result == { @@ -76,3 +76,16 @@ def test_upload_to_ipfs_legacy(): "cid": IPFS_HASH, } assert mock_pin.call_count == 1 + + +def test_app_data_doc_to_cid(): + cid_1 = APP_DATA_DOC.to_cid() + assert cid_1 == CID.app_data_cid + + cid_2 = APP_DATA_2.to_cid() + assert cid_2 == CID_2.app_data_cid + + +def test_app_data_to_cid_legacy(): + cid = APP_DATA_DOC.to_cid_legacy() + assert cid == CID_LEGACY.app_data_cid diff --git a/tests/app_data/mocks.py b/tests/app_data/mocks.py index ab6e9f0..853beb8 100644 --- a/tests/app_data/mocks.py +++ b/tests/app_data/mocks.py @@ -1,16 +1,18 @@ from cow_py.app_data.app_data_cid import AppDataCid +from cow_py.app_data.app_data_doc import AppDataDoc from cow_py.app_data.app_data_hex import AppDataHex HTTP_STATUS_OK = 200 HTTP_STATUS_INTERNAL_ERROR = 500 -APP_DATA_DOC = { - "version": "0.7.0", - "appCode": "CoW Swap", - "metadata": {}, -} +APP_DATA_DOC = AppDataDoc( + { + "version": "0.7.0", + "appCode": "CoW Swap", + "metadata": {}, + } +) -APP_DATA_STRING = '{"appCode":"CoW Swap","metadata":{},"version":"0.7.0"}' CID = AppDataCid( "f01551b20337aa6e6c2a7a0d1eb79a35ebd88b08fc963d5f7a3fc953b7ffb2b7f5898a1df" @@ -20,8 +22,8 @@ "0x337aa6e6c2a7a0d1eb79a35ebd88b08fc963d5f7a3fc953b7ffb2b7f5898a1df" ) -APP_DATA_DOC_CUSTOM = { - **APP_DATA_DOC, +APP_DATA_DOC_CUSTOM_VALUES = { + **APP_DATA_DOC.app_data_doc, "environment": "test", "metadata": { "referrer": { @@ -35,7 +37,18 @@ }, } -APP_DATA_STRING_2 = '{"appCode":"CoW Swap","environment":"production","metadata":{"quote":{"slippageBips":"50","version":"0.2.0"},"orderClass":{"orderClass":"market","version":"0.1.0"}},"version":"0.6.0"}' + +APP_DATA_2 = AppDataDoc( + { + "appCode": "CoW Swap", + "environment": "production", + "metadata": { + "quote": {"slippageBips": "50", "version": "0.2.0"}, + "orderClass": {"orderClass": "market", "version": "0.1.0"}, + }, + "version": "0.6.0", + } +) CID_2 = AppDataCid( "f01551b208af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" From b2895724cb21d1bc0e8eba972835808b2527002f Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Thu, 29 Aug 2024 10:17:59 -0300 Subject: [PATCH 25/30] add app data and remove legacy code --- cow_py/app_data/app_data_doc.py | 77 ++++++++------------------------- cow_py/app_data/app_data_hex.py | 30 +------------ cow_py/app_data/utils.py | 13 +++++- poetry.lock | 26 +---------- pyproject.toml | 2 - tests/app_data/app_data_doc.py | 59 ++++++------------------- tests/app_data/app_data_hex.py | 31 +------------ tests/app_data/mocks.py | 16 +++---- 8 files changed, 53 insertions(+), 201 deletions(-) diff --git a/cow_py/app_data/app_data_doc.py b/cow_py/app_data/app_data_doc.py index c503256..93f1478 100644 --- a/cow_py/app_data/app_data_doc.py +++ b/cow_py/app_data/app_data_doc.py @@ -1,71 +1,30 @@ -from typing import Dict, Any, TypedDict, Optional +from typing import Dict, Any -from multiformats import CID, multihash -from pinatapy import PinataPy -from eth_utils import keccak, to_bytes +from eth_utils import keccak from cow_py.app_data.app_data_hex import AppDataHex from cow_py.app_data.consts import DEFAULT_APP_DATA_DOC -from cow_py.app_data.utils import extract_digest, stringify_deterministic - - -class IpfsUploadResult(TypedDict): - appData: str - cid: str - - -class PinataConfig(TypedDict): - pinata_api_key: str - pinata_api_secret: str +from cow_py.app_data.utils import stringify_deterministic class AppDataDoc: - def __init__(self, app_data_doc: Dict[str, Any] = {}): + def __init__( + self, app_data_doc: Dict[str, Any] = {}, app_data_doc_string: str = "" + ): self.app_data_doc = {**DEFAULT_APP_DATA_DOC, **app_data_doc} + self.app_data_doc_string = app_data_doc_string - # TODO: make this async - def upload_to_ipfs_legacy( - self, ipfs_config: PinataConfig - ) -> Optional[IpfsUploadResult]: - """ - Uploads a appDocument to IPFS - - @deprecated Pinata IPFS automatically pins the uploaded document using some implicit encoding and hashing algorithm. - This method is not used anymore to make it more explicit these parameters and therefore less dependent on the default implementation of Pinata - - @param app_data_doc Document to upload - @param ipfs_config keys to access the IPFS API - - @returns the IPFS CID v0 of the content - """ - pinata = PinataPy( - ipfs_config.get("pinata_api_key", ""), - ipfs_config.get("pinata_api_secret", ""), - ) - pin_response = pinata.pin_json_to_ipfs( - stringify_deterministic(self.app_data_doc) - ) - cid = pin_response["IpfsHash"] - return { - "appData": extract_digest(cid), - "cid": cid, - } - - def to_cid(self): - full_app_data_json = stringify_deterministic(self.app_data_doc) - - # TODO: add validation of app data - app_data_bytes = to_bytes(text=full_app_data_json) - appDataHex = AppDataHex(keccak(app_data_bytes).hex()) - return appDataHex.to_cid() - - def to_cid_legacy(self) -> str: - full_app_data_json = stringify_deterministic(self.app_data_doc) + def to_string(self) -> str: + if self.app_data_doc_string: + return self.app_data_doc_string + return stringify_deterministic(self.app_data_doc) + def to_hex(self) -> str: # TODO: add validation of app data - data_bytes = to_bytes(text=full_app_data_json) - sha2_256 = multihash.get("sha2-256") - mh = sha2_256.digest(data_bytes) - cid = CID("base58btc", 0, "dag-pb", mh) + full_app_data_json = self.to_string() + data_bytes = full_app_data_json.encode("utf-8") + return "0x" + keccak(data_bytes).hex() - return str(cid) + def to_cid(self) -> str: + appDataHex = AppDataHex(self.to_hex()[2:]) + return appDataHex.to_cid() diff --git a/cow_py/app_data/app_data_hex.py b/cow_py/app_data/app_data_hex.py index 089f942..356ed60 100644 --- a/cow_py/app_data/app_data_hex.py +++ b/cow_py/app_data/app_data_hex.py @@ -1,6 +1,6 @@ from typing import Dict, Any from web3 import Web3 -from multiformats import multibase, CID +from multiformats import multibase from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI, MetaDataError from cow_py.app_data.utils import fetch_doc_from_cid @@ -23,11 +23,6 @@ def to_cid(self) -> str: self._assert_cid(cid) return cid - def to_cid_legacy(self) -> str: - cid = self._app_data_hex_to_cid_legacy() - self._assert_cid(cid) - return cid - async def to_doc(self, ipfs_uri: str = DEFAULT_IPFS_READ_URI) -> Dict[str, Any]: try: cid = self.to_cid() @@ -37,17 +32,6 @@ async def to_doc(self, ipfs_uri: str = DEFAULT_IPFS_READ_URI) -> Dict[str, Any]: f"Unexpected error decoding AppData: appDataHex={self.app_data_hex}, message={e}" ) - async def to_doc_legacy( - self, ipfs_uri: str = DEFAULT_IPFS_READ_URI - ) -> Dict[str, Any]: - try: - cid = self.to_cid_legacy() - return await fetch_doc_from_cid(cid, ipfs_uri) - except Exception as e: - raise MetaDataError( - f"Unexpected error decoding AppData: appDataHex={self.app_data_hex}, message={e}" - ) - def _assert_cid(self, cid: str): if not cid: raise MetaDataError( @@ -66,18 +50,6 @@ def _app_data_hex_to_cid(self) -> str: ) return multibase.encode(cid_bytes, "base16") - def _app_data_hex_to_cid_legacy(self) -> str: - cid_bytes = self._to_cid_bytes( - { - "version": CID_V1_PREFIX, - "multicodec": CID_DAG_PB_MULTICODEC, - "hashing_algorithm": SHA2_256_HASHING_ALGORITHM, - "hashing_length": SHA2_256_HASHING_LENGTH, - "multihash_hex": self.app_data_hex, - } - ) - return str(CID.decode(cid_bytes).set(version=0)) - def _to_cid_bytes(self, params: Dict[str, Any]) -> bytes: hash_bytes = Web3.to_bytes(hexstr=params["multihash_hex"]) cid_prefix = bytes( diff --git a/cow_py/app_data/utils.py b/cow_py/app_data/utils.py index df104a0..8fd1036 100644 --- a/cow_py/app_data/utils.py +++ b/cow_py/app_data/utils.py @@ -1,7 +1,8 @@ -from re import T from typing import Any, Dict import httpx from multiformats import CID +from collections.abc import Mapping + import json @@ -22,8 +23,16 @@ def extract_digest(cid_str: str) -> str: return "0x" + cid.set(base="base58btc").digest.hex()[4:] +def sort_nested_dict(d): + return { + k: sort_nested_dict(v) if isinstance(v, Mapping) else v + for k, v in sorted(d.items()) + } + + def stringify_deterministic(obj): - return json.dumps(obj, sort_keys=True, separators=(",", ":")) + sorted_dict = sort_nested_dict(obj) + return json.dumps(sorted_dict, sort_keys=True, separators=(",", ":")) async def fetch_doc_from_cid( diff --git a/poetry.lock b/poetry.lock index b6eb472..b2700db 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1598,16 +1598,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "json-stable-stringify-python" -version = "0.2" -description = "Deterministic JSON stringify" -optional = false -python-versions = "*" -files = [ - {file = "json_stable_stringify_python-0.2.tar.gz", hash = "sha256:28057974145844012d0f4628c1ae4a4fe31119293453cd898fe741e6dfb54171"}, -] - [[package]] name = "jsonref" version = "1.1.0" @@ -2143,20 +2133,6 @@ files = [ [package.dependencies] ptyprocess = ">=0.5" -[[package]] -name = "pinatapy-vourhey" -version = "0.2.0" -description = "Non-official Pinata.cloud library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pinatapy-vourhey-0.2.0.tar.gz", hash = "sha256:453a32629ad11b92dd7153feb64d4302c8d17036cc17c5c3d8108809700bdc2b"}, - {file = "pinatapy_vourhey-0.2.0-py3-none-any.whl", hash = "sha256:86c204949eb9dd8e21ca28e5ee1f7b774a72f57f998ef7fba8a4ca41dd5a2554"}, -] - -[package.dependencies] -requests = "*" - [[package]] name = "platformdirs" version = "4.2.0" @@ -3671,4 +3647,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "6b5c40cee6c82b4f7505408e1e904ddd0fecd377dd2a78103ab06ccc3aca4236" +content-hash = "993952ba1b62b880a9fea993c0205b00d38854570bbde67475baa21f96bf18e8" diff --git a/pyproject.toml b/pyproject.toml index 201c385..2c4eb2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,8 +19,6 @@ jsonref = "^1.1.0" multiformats = "^0.3.1.post4" py-cid = "^0.3.0" pymultihash = "^0.8.2" -pinatapy-vourhey = "^0.2.0" -json-stable-stringify-python = "^0.2" [tool.poetry.group.dev.dependencies] diff --git a/tests/app_data/app_data_doc.py b/tests/app_data/app_data_doc.py index 755e019..6be93c0 100644 --- a/tests/app_data/app_data_doc.py +++ b/tests/app_data/app_data_doc.py @@ -5,14 +5,12 @@ APP_DATA_2, APP_DATA_DOC, APP_DATA_DOC_CUSTOM_VALUES, + APP_DATA_HEX, + APP_DATA_HEX_2, + APP_DATA_STRING, + APP_DATA_STRING_2, CID, CID_2, - CID_LEGACY, - HTTP_STATUS_INTERNAL_ERROR, - IPFS_HASH, - IPFS_HASH_DIGEST, - PINATA_API_KEY, - PINATA_API_SECRET, ) @@ -38,44 +36,20 @@ def test_init_custom_metadata(): ) == APP_DATA_DOC_CUSTOM_VALUES.get("environment") -def test_upload_to_ipfs_legacy_without_ipfs_config(): - with pytest.raises(Exception): - with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: - mock_pin.return_value = { - "status": HTTP_STATUS_INTERNAL_ERROR, - "reason": "missing ipfs config", - "text": "missing ipfs config", - } - APP_DATA_DOC.upload_to_ipfs_legacy({}) +def test_app_data_doc_to_string(): + string_1 = APP_DATA_DOC.to_string() + assert string_1 == APP_DATA_STRING + string_2 = APP_DATA_2.to_string() + assert string_2 == APP_DATA_STRING_2 -def test_upload_to_ipfs_legacy_wrong_credentials(): - with pytest.raises(Exception): - with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: - mock_pin.return_value = { - "status": HTTP_STATUS_INTERNAL_ERROR, - "reason": "wrong credentials", - "text": "wrong credentials", - } - APP_DATA_DOC.upload_to_ipfs_legacy( - { - "pinata_api_key": PINATA_API_KEY, - "pinata_api_secret": PINATA_API_SECRET, - } - ) +def test_app_data_doc_to_hex(): + hex_1 = APP_DATA_DOC.to_hex() + assert hex_1 == APP_DATA_HEX.app_data_hex -def test_upload_to_ipfs_legacy(): - with patch("pinatapy.PinataPy.pin_json_to_ipfs") as mock_pin: - mock_pin.return_value = {"IpfsHash": IPFS_HASH} - upload_result = APP_DATA_DOC.upload_to_ipfs_legacy( - {"pinata_api_key": PINATA_API_KEY, "pinata_api_secret": PINATA_API_SECRET} - ) - assert upload_result == { - "appData": IPFS_HASH_DIGEST, - "cid": IPFS_HASH, - } - assert mock_pin.call_count == 1 + hex_2 = APP_DATA_2.to_hex() + assert hex_2 == APP_DATA_HEX_2.app_data_hex def test_app_data_doc_to_cid(): @@ -84,8 +58,3 @@ def test_app_data_doc_to_cid(): cid_2 = APP_DATA_2.to_cid() assert cid_2 == CID_2.app_data_cid - - -def test_app_data_to_cid_legacy(): - cid = APP_DATA_DOC.to_cid_legacy() - assert cid == CID_LEGACY.app_data_cid diff --git a/tests/app_data/app_data_hex.py b/tests/app_data/app_data_hex.py index a599452..69651dd 100644 --- a/tests/app_data/app_data_hex.py +++ b/tests/app_data/app_data_hex.py @@ -1,16 +1,12 @@ import httpx import pytest from unittest.mock import patch -from cow_py.app_data.consts import DEFAULT_IPFS_READ_URI, MetaDataError +from cow_py.app_data.consts import MetaDataError from cow_py.app_data.app_data_hex import AppDataHex from .mocks import ( - APP_DATA_DOC_CUSTOM, APP_DATA_HEX, CID, - APP_DATA_HEX_LEGACY, - CID_LEGACY, HTTP_STATUS_INTERNAL_ERROR, - HTTP_STATUS_OK, ) @@ -25,31 +21,6 @@ def test_app_data_hex_to_cid_invalid_hash(): app_data_hex.to_cid() -def test_app_data_hex_to_cid_legacy(): - decoded_app_data_cid = APP_DATA_HEX_LEGACY.to_cid_legacy() - assert decoded_app_data_cid == CID_LEGACY.app_data_cid - - -def test_app_data_hex_to_cid_legacy_invalid_hash(): - app_data_hex = AppDataHex("invalidHash") - with pytest.raises(Exception): - app_data_hex.to_cid_legacy() - - -@pytest.mark.asyncio -async def test_fetch_doc_from_app_data_hex_legacy(): - with patch("httpx.AsyncClient.get") as mock_get: - mock_get.return_value = httpx.Response(HTTP_STATUS_OK, json=APP_DATA_DOC_CUSTOM) - mock_get.return_value.status_code = HTTP_STATUS_OK - - app_data_doc = await APP_DATA_HEX_LEGACY.to_doc_legacy() - - mock_get.assert_called_once_with( - f"{DEFAULT_IPFS_READ_URI}/{CID_LEGACY.app_data_cid}" - ) - assert app_data_doc == APP_DATA_DOC_CUSTOM - - @pytest.mark.asyncio async def test_fetch_doc_from_app_data_hex_invalid_hash(): with patch("httpx.AsyncClient.get") as mock_get: diff --git a/tests/app_data/mocks.py b/tests/app_data/mocks.py index 853beb8..9a2cf58 100644 --- a/tests/app_data/mocks.py +++ b/tests/app_data/mocks.py @@ -5,12 +5,14 @@ HTTP_STATUS_OK = 200 HTTP_STATUS_INTERNAL_ERROR = 500 +APP_DATA_STRING = '{"appCode":"CoW Swap","metadata":{},"version":"0.7.0"}' APP_DATA_DOC = AppDataDoc( { "version": "0.7.0", "appCode": "CoW Swap", "metadata": {}, - } + }, + APP_DATA_STRING, ) @@ -37,6 +39,7 @@ }, } +APP_DATA_STRING_2 = '{"appCode":"CoW Swap","environment":"production","metadata":{"quote":{"slippageBips":"50","version":"0.2.0"},"orderClass":{"orderClass":"market","version":"0.1.0"}},"version":"0.6.0"}' APP_DATA_2 = AppDataDoc( { @@ -47,9 +50,11 @@ "orderClass": {"orderClass": "market", "version": "0.1.0"}, }, "version": "0.6.0", - } + }, + APP_DATA_STRING_2, ) + CID_2 = AppDataCid( "f01551b208af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" ) @@ -58,13 +63,6 @@ "0x8af4e8c9973577b08ac21d17d331aade86c11ebcc5124744d621ca8365ec9424" ) -APP_DATA_STRING_LEGACY = '{"version":"0.7.0","appCode":"CowSwap","metadata":{}}' - -CID_LEGACY = AppDataCid("QmSwrFbdFcryazEr361YmSwtGcN4uo4U5DKpzA4KbGxw4Q") - -APP_DATA_HEX_LEGACY = AppDataHex( - "0x447320af985c5e834321dc495545f764ad20d8397eeed2f4a2dcbee44a56b725" -) PINATA_API_KEY = "apikey" PINATA_API_SECRET = "apiSecret" From 4d8554ba3c8da15c3c6c6b70f9d11a26604bbf1d Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Thu, 29 Aug 2024 11:23:36 -0300 Subject: [PATCH 26/30] fix digest util functions --- cow_py/app_data/utils.py | 6 +----- poetry.lock | 43 +++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/cow_py/app_data/utils.py b/cow_py/app_data/utils.py index 8fd1036..2003066 100644 --- a/cow_py/app_data/utils.py +++ b/cow_py/app_data/utils.py @@ -15,12 +15,8 @@ def extract_digest(cid_str: str) -> str: - # TODO: Verify this cid = CID.decode(cid_str) - if cid_str[0] == MULTIBASE_BASE16: - return "0x" + cid.digest.hex()[4:] - - return "0x" + cid.set(base="base58btc").digest.hex()[4:] + return "0x" + cid.raw_digest.hex() def sort_nested_dict(d): diff --git a/poetry.lock b/poetry.lock index b2700db..52dbbb2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2449,6 +2449,47 @@ files = [ {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, ] +[[package]] +name = "pycryptodomex" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodomex-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:645bd4ca6f543685d643dadf6a856cc382b654cc923460e3a10a49c1b3832aeb"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ff5c9a67f8a4fba4aed887216e32cbc48f2a6fb2673bb10a99e43be463e15913"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8ee606964553c1a0bc74057dd8782a37d1c2bc0f01b83193b6f8bb14523b877b"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7805830e0c56d88f4d491fa5ac640dfc894c5ec570d1ece6ed1546e9df2e98d6"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:bc3ee1b4d97081260d92ae813a83de4d2653206967c4a0a017580f8b9548ddbc"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:8af1a451ff9e123d0d8bd5d5e60f8e3315c3a64f3cdd6bc853e26090e195cdc8"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cbe71b6712429650e3883dc81286edb94c328ffcd24849accac0a4dbcc76958a"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:76bd15bb65c14900d98835fcd10f59e5e0435077431d3a394b60b15864fddd64"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:653b29b0819605fe0898829c8ad6400a6ccde096146730c2da54eede9b7b8baa"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a5ec91388984909bb5398ea49ee61b68ecb579123694bffa172c3b0a107079"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:108e5f1c1cd70ffce0b68739c75734437c919d2eaec8e85bffc2c8b4d2794305"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc"}, + {file = "pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458"}, + {file = "pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3584623e68a5064a04748fb6d76117a21a7cb5eaba20608a41c7d0c61721794"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dcac11031a71348faaed1f403a0debd56bf5404232284cf8c761ff918886ebc"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427"}, + {file = "pycryptodomex-3.20.0.tar.gz", hash = "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e"}, +] + [[package]] name = "pydantic" version = "2.7.0" @@ -3647,4 +3688,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "993952ba1b62b880a9fea993c0205b00d38854570bbde67475baa21f96bf18e8" +content-hash = "83792dac0627c3835779ed6d5d55669d2204a8bbbccdd4e50efa433b154a9854" diff --git a/pyproject.toml b/pyproject.toml index 2c4eb2c..4bd0dc7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ jsonref = "^1.1.0" multiformats = "^0.3.1.post4" py-cid = "^0.3.0" pymultihash = "^0.8.2" +pycryptodomex = "^3.20.0" [tool.poetry.group.dev.dependencies] From 20799eeddb660e752e82ad4e7bed7dc8b91ae983 Mon Sep 17 00:00:00 2001 From: Pedro Yves Fracari Date: Thu, 29 Aug 2024 12:41:43 -0300 Subject: [PATCH 27/30] fix import --- cow_py/app_data/app_data_doc.py | 2 +- tests/app_data/app_data_doc.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cow_py/app_data/app_data_doc.py b/cow_py/app_data/app_data_doc.py index 93f1478..e16b438 100644 --- a/cow_py/app_data/app_data_doc.py +++ b/cow_py/app_data/app_data_doc.py @@ -1,6 +1,6 @@ from typing import Dict, Any -from eth_utils import keccak +from eth_utils.crypto import keccak from cow_py.app_data.app_data_hex import AppDataHex from cow_py.app_data.consts import DEFAULT_APP_DATA_DOC diff --git a/tests/app_data/app_data_doc.py b/tests/app_data/app_data_doc.py index 6be93c0..7ec157b 100644 --- a/tests/app_data/app_data_doc.py +++ b/tests/app_data/app_data_doc.py @@ -1,5 +1,3 @@ -from unittest.mock import patch -import pytest from cow_py.app_data.app_data_doc import AppDataDoc from .mocks import ( APP_DATA_2, From 8e8de09dd6e1e45d6becd44d06e4f387814425cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Tue, 10 Sep 2024 13:59:46 -0300 Subject: [PATCH 28/30] chore: relock poetry.lock --- poetry.lock | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/poetry.lock b/poetry.lock index c9dd8fa..f0281fb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -295,24 +295,6 @@ files = [ {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, ] -[[package]] -name = "bases" -version = "0.3.0" -description = "Python library for general Base-N encodings." -optional = false -python-versions = ">=3.7" -files = [ - {file = "bases-0.3.0-py3-none-any.whl", hash = "sha256:a2fef3366f3e522ff473d2e95c21523fe8e44251038d5c6150c01481585ebf5b"}, - {file = "bases-0.3.0.tar.gz", hash = "sha256:70f04a4a45d63245787f9e89095ca11042685b6b64b542ad916575ba3ccd1570"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0" -typing-validation = ">=1.1.0" - -[package.extras] -dev = ["base58", "mypy", "pylint", "pytest", "pytest-cov"] - [[package]] name = "bitarray" version = "2.9.2" @@ -3236,23 +3218,6 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -[[package]] -name = "typing-validation" -version = "1.2.11.post4" -description = "A simple library for runtime type-checking." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_validation-1.2.11.post4-py3-none-any.whl", hash = "sha256:73dd504ddebf5210e80d5f65ba9b30efbd0fa42f266728fda7c4f0ba335c699c"}, - {file = "typing_validation-1.2.11.post4.tar.gz", hash = "sha256:7aed04ecfbda07e63b7266f90e5d096f96344f7facfe04bb081b21e4a9781670"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["mypy", "pylint", "pytest", "pytest-cov", "rich"] - [[package]] name = "urllib3" version = "2.2.2" @@ -3270,16 +3235,6 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "varint" -version = "1.0.2" -description = "Simple python varint implementation" -optional = false -python-versions = "*" -files = [ - {file = "varint-1.0.2.tar.gz", hash = "sha256:a6ecc02377ac5ee9d65a6a8ad45c9ff1dac8ccee19400a5950fb51d594214ca5"}, -] - [[package]] name = "virtualenv" version = "20.26.4" From ea2346e4ae864c0d7dc69711df215c7a40295b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Tue, 10 Sep 2024 14:07:22 -0300 Subject: [PATCH 29/30] chore: add multiformats --- poetry.lock | 75 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index f0281fb..6c1e256 100644 --- a/poetry.lock +++ b/poetry.lock @@ -295,6 +295,24 @@ files = [ {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, ] +[[package]] +name = "bases" +version = "0.3.0" +description = "Python library for general Base-N encodings." +optional = false +python-versions = ">=3.7" +files = [ + {file = "bases-0.3.0-py3-none-any.whl", hash = "sha256:a2fef3366f3e522ff473d2e95c21523fe8e44251038d5c6150c01481585ebf5b"}, + {file = "bases-0.3.0.tar.gz", hash = "sha256:70f04a4a45d63245787f9e89095ca11042685b6b64b542ad916575ba3ccd1570"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0" +typing-validation = ">=1.1.0" + +[package.extras] +dev = ["base58", "mypy", "pylint", "pytest", "pytest-cov"] + [[package]] name = "bitarray" version = "2.9.2" @@ -2001,6 +2019,44 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} +[[package]] +name = "multiformats" +version = "0.3.1.post4" +description = "Python implementation of multiformats protocols." +optional = false +python-versions = ">=3.7" +files = [ + {file = "multiformats-0.3.1.post4-py3-none-any.whl", hash = "sha256:5b1d61bd8275c9e817bdbee38dbd501b26629011962ee3c86c46e7ccd0b14129"}, + {file = "multiformats-0.3.1.post4.tar.gz", hash = "sha256:d00074fdbc7d603c2084b4c38fa17bbc28173cf2750f51f46fbbc5c4d5605fbb"}, +] + +[package.dependencies] +bases = ">=0.3.0" +multiformats-config = ">=0.3.0" +typing-extensions = ">=4.6.0" +typing-validation = ">=1.1.0" + +[package.extras] +dev = ["blake3", "mmh3", "mypy", "pycryptodomex", "pylint", "pyskein", "pytest", "pytest-cov", "rich"] +full = ["blake3", "mmh3", "pycryptodomex", "pyskein", "rich"] + +[[package]] +name = "multiformats-config" +version = "0.3.1" +description = "Pre-loading configuration module for the 'multiformats' package." +optional = false +python-versions = ">=3.7" +files = [ + {file = "multiformats-config-0.3.1.tar.gz", hash = "sha256:7eaa80ef5d9c5ee9b86612d21f93a087c4a655cbcb68960457e61adbc62b47a7"}, + {file = "multiformats_config-0.3.1-py3-none-any.whl", hash = "sha256:dec4c9d42ed0d9305889b67440f72e8e8d74b82b80abd7219667764b5b0a8e1d"}, +] + +[package.dependencies] +multiformats = "*" + +[package.extras] +dev = ["mypy", "pylint", "pytest", "pytest-cov"] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -3218,6 +3274,23 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[[package]] +name = "typing-validation" +version = "1.2.11.post4" +description = "A simple library for runtime type-checking." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_validation-1.2.11.post4-py3-none-any.whl", hash = "sha256:73dd504ddebf5210e80d5f65ba9b30efbd0fa42f266728fda7c4f0ba335c699c"}, + {file = "typing_validation-1.2.11.post4.tar.gz", hash = "sha256:7aed04ecfbda07e63b7266f90e5d096f96344f7facfe04bb081b21e4a9781670"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["mypy", "pylint", "pytest", "pytest-cov", "rich"] + [[package]] name = "urllib3" version = "2.2.2" @@ -3506,4 +3579,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "b560a5c3845be211a0319c912867980bc038e467912ae8cb16ef93a15ce87604" +content-hash = "764273bcc99bc9be5abd014f48e0eac375651b9fd4d48b7d4b6a78fe56a15f2e" diff --git a/pyproject.toml b/pyproject.toml index 91e733c..b41c6db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ pydantic = "^2.7.0" pytest-mock = "^3.14.0" backoff = "^2.2.1" aiolimiter = "^1.1.0" +multiformats = "^0.3.1.post4" [tool.poetry.group.dev.dependencies] From c6cf3f7e54e8c4cf1cd4252b846c5e9f4f3eaf12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Tue, 10 Sep 2024 14:13:14 -0300 Subject: [PATCH 30/30] chore(app_data): remove unused app_data schemas --- cow_py/app_data/schemas/definitions.json | 45 ------------ cow_py/app_data/schemas/hook/v0.1.0.json | 35 ---------- cow_py/app_data/schemas/hooks/v0.1.0.json | 35 ---------- .../app_data/schemas/orderClass/v0.1.0.json | 33 --------- .../app_data/schemas/orderClass/v0.2.0.json | 35 ---------- .../app_data/schemas/orderClass/v0.3.0.json | 30 -------- .../app_data/schemas/partnerFee/v0.1.0.json | 20 ------ cow_py/app_data/schemas/quote/v0.1.0.json | 34 --------- cow_py/app_data/schemas/quote/v0.2.0.json | 22 ------ cow_py/app_data/schemas/quote/v0.3.0.json | 18 ----- cow_py/app_data/schemas/quote/v1.0.0.json | 15 ---- cow_py/app_data/schemas/referrer/v0.1.0.json | 21 ------ cow_py/app_data/schemas/referrer/v0.2.0.json | 16 ----- .../schemas/replacedOrder/v0.1.0.json | 15 ---- cow_py/app_data/schemas/signer/v0.1.0.json | 7 -- cow_py/app_data/schemas/utm/v0.1.0.json | 54 --------------- cow_py/app_data/schemas/utm/v0.2.0.json | 55 --------------- cow_py/app_data/schemas/v0.1.0.json | 40 ----------- cow_py/app_data/schemas/v0.10.0.json | 69 ------------------- cow_py/app_data/schemas/v0.11.0.json | 62 ----------------- cow_py/app_data/schemas/v0.2.0.json | 43 ------------ cow_py/app_data/schemas/v0.3.0.json | 55 --------------- cow_py/app_data/schemas/v0.4.0.json | 55 --------------- cow_py/app_data/schemas/v0.5.0.json | 58 ---------------- cow_py/app_data/schemas/v0.6.0.json | 61 ---------------- cow_py/app_data/schemas/v0.7.0.json | 61 ---------------- cow_py/app_data/schemas/v0.8.0.json | 63 ----------------- cow_py/app_data/schemas/v0.9.0.json | 66 ------------------ cow_py/app_data/schemas/v1.0.0.json | 65 ----------------- cow_py/app_data/schemas/v1.1.0.json | 68 ------------------ cow_py/app_data/schemas/widget/v0.1.0.json | 24 ------- 31 files changed, 1280 deletions(-) delete mode 100644 cow_py/app_data/schemas/definitions.json delete mode 100644 cow_py/app_data/schemas/hook/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/hooks/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/orderClass/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/orderClass/v0.2.0.json delete mode 100644 cow_py/app_data/schemas/orderClass/v0.3.0.json delete mode 100644 cow_py/app_data/schemas/partnerFee/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/quote/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/quote/v0.2.0.json delete mode 100644 cow_py/app_data/schemas/quote/v0.3.0.json delete mode 100644 cow_py/app_data/schemas/quote/v1.0.0.json delete mode 100644 cow_py/app_data/schemas/referrer/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/referrer/v0.2.0.json delete mode 100644 cow_py/app_data/schemas/replacedOrder/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/signer/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/utm/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/utm/v0.2.0.json delete mode 100644 cow_py/app_data/schemas/v0.1.0.json delete mode 100644 cow_py/app_data/schemas/v0.10.0.json delete mode 100644 cow_py/app_data/schemas/v0.11.0.json delete mode 100644 cow_py/app_data/schemas/v0.2.0.json delete mode 100644 cow_py/app_data/schemas/v0.3.0.json delete mode 100644 cow_py/app_data/schemas/v0.4.0.json delete mode 100644 cow_py/app_data/schemas/v0.5.0.json delete mode 100644 cow_py/app_data/schemas/v0.6.0.json delete mode 100644 cow_py/app_data/schemas/v0.7.0.json delete mode 100644 cow_py/app_data/schemas/v0.8.0.json delete mode 100644 cow_py/app_data/schemas/v0.9.0.json delete mode 100644 cow_py/app_data/schemas/v1.0.0.json delete mode 100644 cow_py/app_data/schemas/v1.1.0.json delete mode 100644 cow_py/app_data/schemas/widget/v0.1.0.json diff --git a/cow_py/app_data/schemas/definitions.json b/cow_py/app_data/schemas/definitions.json deleted file mode 100644 index bdefabf..0000000 --- a/cow_py/app_data/schemas/definitions.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "$id": "#definitions.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Definitions sub-schema", - "definitions": { - "version": { - "$id": "#/definitions/version", - "description": "Semantic versioning of document.", - "readOnly": true, - "examples": ["1.0.0", "1.2.3"], - "title": "Version", - "type": "string" - }, - "ethereumAddress": { - "$id": "#/definitions/ethereumAddress", - "pattern": "^0x[a-fA-F0-9]{40}$", - "title": "Ethereum compatible address", - "examples": ["0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9"], - "type": "string" - }, - "bigNumber": { - "$id": "#/definitions/bigNumber", - "pattern": "^\\d+$", - "title": "BigNumber", - "examples": ["90741097240912730913", "0", "75891372"], - "type": "string" - }, - "bps": { - "$id": "#/definitions/bps", - "title": "Basis Point (BPS)", - "description": "One basis point is equivalent to 0.01% (1/100th of a percent)", - "examples": [0, 10, 50], - "type": "integer", - "maximum": 10000, - "minimum": 0 - }, - "orderUid": { - "$id": "#/definitions/orderUid", - "pattern": "^0x[a-fA-F0-9]{112}$", - "title": "Order UID", - "examples": ["0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a"], - "type": "string" - } - } -} diff --git a/cow_py/app_data/schemas/hook/v0.1.0.json b/cow_py/app_data/schemas/hook/v0.1.0.json deleted file mode 100644 index 28c1571..0000000 --- a/cow_py/app_data/schemas/hook/v0.1.0.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$id": "#hook/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [ - "target", - "callData", - "gasLimit" - ], - "title": "CoW Hook", - "type": "object", - "additionalProperties": false, - "properties": { - "target": { - "$ref": "../definitions.json#/definitions/ethereumAddress", - "title": "Hook Target", - "description": "The contract to call for the hook" - }, - "callData": { - "$id": "#/properties/callData", - "title": "Hook CallData", - "description": "The calldata to use when calling the hook", - "type": "string", - "pattern": "^0x[a-fA-F0-9]*$", - "examples": [ - "0x", - "0x01020304" - ] - }, - "gasLimit": { - "$ref": "../definitions.json#/definitions/bigNumber", - "title": "Hook Gas Limit", - "description": "The gas limit (in gas units) for the hook" - } - } -} diff --git a/cow_py/app_data/schemas/hooks/v0.1.0.json b/cow_py/app_data/schemas/hooks/v0.1.0.json deleted file mode 100644 index 37a1c6e..0000000 --- a/cow_py/app_data/schemas/hooks/v0.1.0.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$id": "#hooks/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "default": {}, - "required": [], - "title": "Order interaction hooks", - "description": "Optional Pre and Post order interaction hooks attached to a single order", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "$ref": "../definitions.json#/definitions/version", - "readOnly": true, - "default": "0.1.0" - }, - "pre": { - "$id": "#/properties/hooks/pre", - "title": "Pre-Hooks", - "description": "CoW Hooks to call before an order executes", - "type": "array", - "items": { - "$ref": "../hook/v0.1.0.json#" - } - }, - "post": { - "$id": "#/properties/hooks/post", - "title": "Post-Hooks", - "description": "CoW Hooks to call after an order executes", - "type": "array", - "items": { - "$ref": "../hook/v0.1.0.json#" - } - } - } -} diff --git a/cow_py/app_data/schemas/orderClass/v0.1.0.json b/cow_py/app_data/schemas/orderClass/v0.1.0.json deleted file mode 100644 index dc97fd7..0000000 --- a/cow_py/app_data/schemas/orderClass/v0.1.0.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "$id": "#orderClass/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [ - "version", - "orderClass" - ], - "title": "Order class", - "type": "object", - "properties": { - "version": { - "$ref": "../definitions.json#/definitions/version", - "readOnly": true, - "default": "0.1.0" - }, - "orderClass": { - "$id": "#/definitions/orderClass", - "description": "Indicator of the order class.", - "examples": [ - "market", - "limit", - "liquidity" - ], - "title": "Order class", - "type": "string", - "enum": [ - "market", - "limit", - "liquidity" - ] - } - } -} diff --git a/cow_py/app_data/schemas/orderClass/v0.2.0.json b/cow_py/app_data/schemas/orderClass/v0.2.0.json deleted file mode 100644 index 4a09255..0000000 --- a/cow_py/app_data/schemas/orderClass/v0.2.0.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$id": "#orderClass/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [ - "version", - "orderClass" - ], - "title": "Order class", - "type": "object", - "properties": { - "version": { - "$ref": "../definitions.json#/definitions/version", - "readOnly": true, - "default": "0.1.0" - }, - "orderClass": { - "$id": "#/definitions/orderClass", - "description": "Indicator of the order class.", - "examples": [ - "market", - "limit", - "liquidity", - "twap" - ], - "title": "Order class", - "type": "string", - "enum": [ - "market", - "limit", - "liquidity", - "twap" - ] - } - } -} diff --git a/cow_py/app_data/schemas/orderClass/v0.3.0.json b/cow_py/app_data/schemas/orderClass/v0.3.0.json deleted file mode 100644 index 7491429..0000000 --- a/cow_py/app_data/schemas/orderClass/v0.3.0.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$id": "#orderClass/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [ - "orderClass" - ], - "title": "Order class", - "type": "object", - "additionalProperties": false, - "properties": { - "orderClass": { - "$id": "#/definitions/orderClass", - "description": "Indicator of the order class.", - "examples": [ - "market", - "limit", - "liquidity", - "twap" - ], - "title": "Order class", - "type": "string", - "enum": [ - "market", - "limit", - "liquidity", - "twap" - ] - } - } -} diff --git a/cow_py/app_data/schemas/partnerFee/v0.1.0.json b/cow_py/app_data/schemas/partnerFee/v0.1.0.json deleted file mode 100644 index ea8eb5a..0000000 --- a/cow_py/app_data/schemas/partnerFee/v0.1.0.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$id": "#partnerFee/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": ["bps", "recipient"], - "title": "Partner fee", - "type": "object", - "additionalProperties": false, - "properties": { - "bps": { - "title": "Basis Point (BPS)", - "description": "The fee in basis points (BPS) to be paid to the partner. One basis point is equivalent to 0.01% (1/100th of a percent)", - "$ref": "../definitions.json#/definitions/bps" - }, - "recipient": { - "title": "Partner account", - "description": "The Ethereum address of the partner to receive the fee.", - "$ref": "../definitions.json#/definitions/ethereumAddress" - } - } -} diff --git a/cow_py/app_data/schemas/quote/v0.1.0.json b/cow_py/app_data/schemas/quote/v0.1.0.json deleted file mode 100644 index 905399f..0000000 --- a/cow_py/app_data/schemas/quote/v0.1.0.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$id": "#quote/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [ - "sellAmount", - "buyAmount", - "version" - ], - "title": "Quote", - "type": "object", - "properties": { - "id": { - "$id": "#/properties/id", - "title": "Quote id", - "examples": [ - "XA23443543534FF" - ], - "type": "string" - }, - "sellAmount": { - "$ref": "../definitions.json#/definitions/bigNumber", - "title": "Quote sell amount" - }, - "buyAmount": { - "$ref": "../definitions.json#/definitions/bigNumber", - "title": "Quote buy amount" - }, - "version": { - "$ref": "../definitions.json#/definitions/version", - "readOnly": true, - "default": "0.1.0" - } - } -} diff --git a/cow_py/app_data/schemas/quote/v0.2.0.json b/cow_py/app_data/schemas/quote/v0.2.0.json deleted file mode 100644 index 62bc155..0000000 --- a/cow_py/app_data/schemas/quote/v0.2.0.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$id": "#quote/v0.2.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": ["version", "slippageBips"], - "title": "Quote", - "type": "object", - "properties": { - "version": { - "$ref": "../definitions.json#/definitions/version", - "readOnly": true, - "default": "0.2.0" - }, - "slippageBips": { - "$id": "#/properties/slippageBips", - "title": "Slippage Bips", - "description": "Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BPS)", - "examples": ["5", "10", "20", "100"], - "pattern": "^\\d+(\\.\\d+)?$", - "type": "string" - } - } -} diff --git a/cow_py/app_data/schemas/quote/v0.3.0.json b/cow_py/app_data/schemas/quote/v0.3.0.json deleted file mode 100644 index 8159205..0000000 --- a/cow_py/app_data/schemas/quote/v0.3.0.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$id": "#quote/v0.2.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": ["slippageBips"], - "title": "Quote", - "type": "object", - "additionalProperties": false, - "properties": { - "slippageBips": { - "$id": "#/properties/slippageBips", - "title": "Slippage Bips", - "description": "Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BPS)", - "examples": ["5", "10", "20", "100"], - "pattern": "^\\d+(\\.\\d+)?$", - "type": "string" - } - } -} diff --git a/cow_py/app_data/schemas/quote/v1.0.0.json b/cow_py/app_data/schemas/quote/v1.0.0.json deleted file mode 100644 index 6500a00..0000000 --- a/cow_py/app_data/schemas/quote/v1.0.0.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$id": "#quote/v0.2.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": ["slippageBips"], - "title": "Quote", - "type": "object", - "additionalProperties": false, - "properties": { - "slippageBips": { - "title": "Slippage Bips", - "description": "Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BPS). One basis point is equivalent to 0.01% (1/100th of a percent)", - "$ref": "../definitions.json#/definitions/bps" - } - } -} diff --git a/cow_py/app_data/schemas/referrer/v0.1.0.json b/cow_py/app_data/schemas/referrer/v0.1.0.json deleted file mode 100644 index 6421619..0000000 --- a/cow_py/app_data/schemas/referrer/v0.1.0.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$id": "#referrer/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [ - "version", - "address" - ], - "title": "Referrer", - "type": "object", - "properties": { - "version": { - "$ref": "../definitions.json#/definitions/version", - "readOnly": true, - "default": "0.1.0" - }, - "address": { - "$ref": "../definitions.json#/definitions/ethereumAddress", - "title": "Referrer address" - } - } -} diff --git a/cow_py/app_data/schemas/referrer/v0.2.0.json b/cow_py/app_data/schemas/referrer/v0.2.0.json deleted file mode 100644 index 635830c..0000000 --- a/cow_py/app_data/schemas/referrer/v0.2.0.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$id": "#referrer/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [ - "address" - ], - "title": "Referrer", - "type": "object", - "additionalProperties": false, - "properties": { - "address": { - "$ref": "../definitions.json#/definitions/ethereumAddress", - "title": "Referrer address" - } - } -} diff --git a/cow_py/app_data/schemas/replacedOrder/v0.1.0.json b/cow_py/app_data/schemas/replacedOrder/v0.1.0.json deleted file mode 100644 index ecba827..0000000 --- a/cow_py/app_data/schemas/replacedOrder/v0.1.0.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$id": "#replacedOrder/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": ["uid"], - "title": "Replaced order", - "type": "object", - "additionalProperties": false, - "properties": { - "uid": { - "title": "Replaced order UID", - "description": "The replaced order UID.", - "$ref": "../definitions.json#/definitions/orderUid" - } - } -} diff --git a/cow_py/app_data/schemas/signer/v0.1.0.json b/cow_py/app_data/schemas/signer/v0.1.0.json deleted file mode 100644 index aeda275..0000000 --- a/cow_py/app_data/schemas/signer/v0.1.0.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$id": "#signer/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "$ref": "../definitions.json#/definitions/ethereumAddress", - "title": "Signer", - "description": "The address of the trader who signs the CoW Swap order. This field should normally be omitted; it is recommended to use it if the signer is a smart-contract wallet using EIP-1271 signatures." -} diff --git a/cow_py/app_data/schemas/utm/v0.1.0.json b/cow_py/app_data/schemas/utm/v0.1.0.json deleted file mode 100644 index a9a4d27..0000000 --- a/cow_py/app_data/schemas/utm/v0.1.0.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "$id": "#utm/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [], - "title": "UTM Codes", - "type": "object", - "properties": { - "utmSource": { - "$id": "#/properties/utmSource", - "title": "UTM Source", - "type": "string", - "examples": [ - "twitter" - ], - "description": "Tracks in which medium the traffic originated from (twitter, facebook, etc.)" - }, - "utmMedium": { - "$id": "#/properties/utmMedium", - "title": "UTM Medium", - "type": "string", - "examples": [ - "email" - ], - "description": "Tracks in which medium the traffic originated from (mail, CPC, social, etc.)" - }, - "utmCampaign": { - "$id": "#/properties/utmCampagin", - "title": "UTM Campaign", - "type": "string", - "examples": [ - "everyone-loves-cows-2023" - ], - "description": "Track the performance of a specific campaign" - }, - "utmContent": { - "$id": "#/properties/utmContent", - "title": "UTM Content", - "type": "string", - "examples": [ - "big-fat-button" - ], - "description": "Track which link was clicked" - }, - "utmTerm": { - "$id": "#/properties/utmTem", - "title": "UTM Keyword Term", - "type": "string", - "examples": [ - "coincidence+of+wants" - ], - "description": "Track which keyword term a website visitor came from" - } - } -} diff --git a/cow_py/app_data/schemas/utm/v0.2.0.json b/cow_py/app_data/schemas/utm/v0.2.0.json deleted file mode 100644 index ce085c7..0000000 --- a/cow_py/app_data/schemas/utm/v0.2.0.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$id": "#utm/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": [], - "title": "UTM Codes", - "type": "object", - "additionalProperties": false, - "properties": { - "utmSource": { - "$id": "#/properties/utmSource", - "title": "UTM Source", - "type": "string", - "examples": [ - "twitter" - ], - "description": "Tracks in which medium the traffic originated from (twitter, facebook, etc.)" - }, - "utmMedium": { - "$id": "#/properties/utmMedium", - "title": "UTM Medium", - "type": "string", - "examples": [ - "email" - ], - "description": "Tracks in which medium the traffic originated from (mail, CPC, social, etc.)" - }, - "utmCampaign": { - "$id": "#/properties/utmCampagin", - "title": "UTM Campaign", - "type": "string", - "examples": [ - "everyone-loves-cows-2023" - ], - "description": "Track the performance of a specific campaign" - }, - "utmContent": { - "$id": "#/properties/utmContent", - "title": "UTM Content", - "type": "string", - "examples": [ - "big-fat-button" - ], - "description": "Track which link was clicked" - }, - "utmTerm": { - "$id": "#/properties/utmTem", - "title": "UTM Keyword Term", - "type": "string", - "examples": [ - "coincidence+of+wants" - ], - "description": "Track which keyword term a website visitor came from" - } - } -} diff --git a/cow_py/app_data/schemas/v0.1.0.json b/cow_py/app_data/schemas/v0.1.0.json deleted file mode 100644 index 2d17438..0000000 --- a/cow_py/app_data/schemas/v0.1.0.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.1.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "properties": { - "referrer": { - "$ref": "referrer/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.10.0.json b/cow_py/app_data/schemas/v0.10.0.json deleted file mode 100644 index af27492..0000000 --- a/cow_py/app_data/schemas/v0.10.0.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.10.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.10.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "additionalProperties": false, - "properties": { - "signer": { - "$ref": "signer/v0.1.0.json#" - }, - "referrer": { - "$ref": "referrer/v0.2.0.json#" - }, - "utm": { - "$ref": "utm/v0.2.0.json#" - }, - "quote": { - "$ref": "quote/v0.3.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.3.0.json#" - }, - "hooks": { - "$ref": "hooks/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.11.0.json b/cow_py/app_data/schemas/v0.11.0.json deleted file mode 100644 index 5dcd00f..0000000 --- a/cow_py/app_data/schemas/v0.11.0.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.11.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": ["version", "metadata"], - "title": "AppData Root Schema", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.11.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": ["CoW Swap"], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": ["production", "development", "staging", "ens"] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "additionalProperties": false, - "properties": { - "signer": { - "$ref": "signer/v0.1.0.json#" - }, - "referrer": { - "$ref": "referrer/v0.2.0.json#" - }, - "utm": { - "$ref": "utm/v0.2.0.json#" - }, - "quote": { - "$ref": "quote/v0.3.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.3.0.json#" - }, - "hooks": { - "$ref": "hooks/v0.1.0.json#" - }, - "widget": { - "$ref": "widget/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.2.0.json b/cow_py/app_data/schemas/v0.2.0.json deleted file mode 100644 index a8c2dd6..0000000 --- a/cow_py/app_data/schemas/v0.2.0.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.2.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.2.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "properties": { - "referrer": { - "$ref": "referrer/v0.1.0.json#" - }, - "quote": { - "$ref": "quote/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.3.0.json b/cow_py/app_data/schemas/v0.3.0.json deleted file mode 100644 index ebdd176..0000000 --- a/cow_py/app_data/schemas/v0.3.0.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.3.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.3.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "properties": { - "referrer": { - "$ref": "referrer/v0.1.0.json#" - }, - "quote": { - "$ref": "quote/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.4.0.json b/cow_py/app_data/schemas/v0.4.0.json deleted file mode 100644 index 5c21521..0000000 --- a/cow_py/app_data/schemas/v0.4.0.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.4.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.4.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "properties": { - "referrer": { - "$ref": "referrer/v0.1.0.json#" - }, - "quote": { - "$ref": "quote/v0.2.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.5.0.json b/cow_py/app_data/schemas/v0.5.0.json deleted file mode 100644 index 413e3c6..0000000 --- a/cow_py/app_data/schemas/v0.5.0.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.5.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.5.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "properties": { - "referrer": { - "$ref": "referrer/v0.1.0.json#" - }, - "quote": { - "$ref": "quote/v0.2.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.6.0.json b/cow_py/app_data/schemas/v0.6.0.json deleted file mode 100644 index 83db135..0000000 --- a/cow_py/app_data/schemas/v0.6.0.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.6.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.6.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "properties": { - "referrer": { - "$ref": "referrer/v0.1.0.json#" - }, - "utm": { - "$ref": "utm/v0.1.0.json#" - }, - "quote": { - "$ref": "quote/v0.2.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.7.0.json b/cow_py/app_data/schemas/v0.7.0.json deleted file mode 100644 index f044f0d..0000000 --- a/cow_py/app_data/schemas/v0.7.0.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.7.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.7.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "properties": { - "referrer": { - "$ref": "referrer/v0.1.0.json#" - }, - "utm": { - "$ref": "utm/v0.1.0.json#" - }, - "quote": { - "$ref": "quote/v0.2.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.2.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.8.0.json b/cow_py/app_data/schemas/v0.8.0.json deleted file mode 100644 index 9cb15c5..0000000 --- a/cow_py/app_data/schemas/v0.8.0.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.7.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.8.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "additionalProperties": false, - "properties": { - "referrer": { - "$ref": "referrer/v0.2.0.json#" - }, - "utm": { - "$ref": "utm/v0.2.0.json#" - }, - "quote": { - "$ref": "quote/v0.3.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.3.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v0.9.0.json b/cow_py/app_data/schemas/v0.9.0.json deleted file mode 100644 index c253f1d..0000000 --- a/cow_py/app_data/schemas/v0.9.0.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.7.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": [ - "version", - "metadata" - ], - "title": "AppData Root Schema", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.9.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": [ - "CoW Swap" - ], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": [ - "production", - "development", - "staging", - "ens" - ] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "additionalProperties": false, - "properties": { - "referrer": { - "$ref": "referrer/v0.2.0.json#" - }, - "utm": { - "$ref": "utm/v0.2.0.json#" - }, - "quote": { - "$ref": "quote/v0.3.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.3.0.json#" - }, - "hooks": { - "$ref": "hooks/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v1.0.0.json b/cow_py/app_data/schemas/v1.0.0.json deleted file mode 100644 index 0add611..0000000 --- a/cow_py/app_data/schemas/v1.0.0.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v0.11.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": ["version", "metadata"], - "title": "AppData Root Schema", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.11.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": ["CoW Swap"], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": ["production", "development", "staging", "ens"] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "additionalProperties": false, - "properties": { - "signer": { - "$ref": "signer/v0.1.0.json#" - }, - "referrer": { - "$ref": "referrer/v0.2.0.json#" - }, - "utm": { - "$ref": "utm/v0.2.0.json#" - }, - "quote": { - "$ref": "quote/v1.0.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.3.0.json#" - }, - "hooks": { - "$ref": "hooks/v0.1.0.json#" - }, - "widget": { - "$ref": "widget/v0.1.0.json#" - }, - "partnerFee": { - "$ref": "partnerFee/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/v1.1.0.json b/cow_py/app_data/schemas/v1.1.0.json deleted file mode 100644 index 3192276..0000000 --- a/cow_py/app_data/schemas/v1.1.0.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "$id": "https://cowswap.exchange/schemas/app-data/v1.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "description": "Metadata JSON document for adding information to orders.", - "required": ["version", "metadata"], - "title": "AppData Root Schema", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "$ref": "definitions.json#/definitions/version", - "readOnly": true, - "default": "0.11.0" - }, - "appCode": { - "$id": "#/properties/appCode", - "description": "The code identifying the CLI, UI, service generating the order.", - "examples": ["CoW Swap"], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environment", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": ["production", "development", "staging", "ens"] - }, - "metadata": { - "$id": "#/properties/metadata", - "default": {}, - "description": "Each metadata will specify one aspect of the order.", - "required": [], - "title": "Metadata", - "type": "object", - "additionalProperties": false, - "properties": { - "signer": { - "$ref": "signer/v0.1.0.json#" - }, - "referrer": { - "$ref": "referrer/v0.2.0.json#" - }, - "utm": { - "$ref": "utm/v0.2.0.json#" - }, - "quote": { - "$ref": "quote/v1.0.0.json#" - }, - "orderClass": { - "$ref": "orderClass/v0.3.0.json#" - }, - "hooks": { - "$ref": "hooks/v0.1.0.json#" - }, - "widget": { - "$ref": "widget/v0.1.0.json#" - }, - "partnerFee": { - "$ref": "partnerFee/v0.1.0.json#" - }, - "replacedOrder": { - "$ref": "replacedOrder/v0.1.0.json#" - } - } - } - } -} diff --git a/cow_py/app_data/schemas/widget/v0.1.0.json b/cow_py/app_data/schemas/widget/v0.1.0.json deleted file mode 100644 index b5f14f4..0000000 --- a/cow_py/app_data/schemas/widget/v0.1.0.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$id": "#widget/v0.1.0.json", - "$schema": "http://json-schema.org/draft-07/schema", - "required": ["appCode"], - "title": "Widget", - "type": "object", - "additionalProperties": false, - "properties": { - "appCode": { - "$id": "#/properties/appCodeWidget", - "description": "The code identifying the UI powering the widget", - "examples": ["CoW Swap"], - "title": "App Code", - "type": "string" - }, - "environment": { - "$id": "#/properties/environmentWidget", - "description": "Environment from which the order came from.", - "title": "Environment", - "type": "string", - "examples": ["production", "development", "staging", "ens"] - } - } -}