tpluspy 0.1.0a0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. tpluspy-0.1.0a0/.github/workflows/publish.yml +29 -0
  2. tpluspy-0.1.0a0/.github/workflows/test.yml +50 -0
  3. tpluspy-0.1.0a0/.gitignore +183 -0
  4. tpluspy-0.1.0a0/.pre-commit-config.yaml +20 -0
  5. tpluspy-0.1.0a0/PKG-INFO +266 -0
  6. tpluspy-0.1.0a0/README.md +219 -0
  7. tpluspy-0.1.0a0/ape_console_extras.py +35 -0
  8. tpluspy-0.1.0a0/examples/decimals.py +29 -0
  9. tpluspy-0.1.0a0/examples/deposit.py +66 -0
  10. tpluspy-0.1.0a0/examples/rest_usage.py +175 -0
  11. tpluspy-0.1.0a0/examples/settlement.py +38 -0
  12. tpluspy-0.1.0a0/examples/two_users_trade.py +98 -0
  13. tpluspy-0.1.0a0/examples/vaults.py +24 -0
  14. tpluspy-0.1.0a0/examples/websocket_usage.py +144 -0
  15. tpluspy-0.1.0a0/pyproject.toml +127 -0
  16. tpluspy-0.1.0a0/setup.cfg +4 -0
  17. tpluspy-0.1.0a0/setup.py +72 -0
  18. tpluspy-0.1.0a0/tests/__init__.py +0 -0
  19. tpluspy-0.1.0a0/tests/client/__init__.py +0 -0
  20. tpluspy-0.1.0a0/tests/client/test_decimal.py +30 -0
  21. tpluspy-0.1.0a0/tests/evm/__init__.py +0 -0
  22. tpluspy-0.1.0a0/tests/evm/conftest.py +6 -0
  23. tpluspy-0.1.0a0/tests/evm/test_eip712.py +27 -0
  24. tpluspy-0.1.0a0/tests/model/__init__.py +0 -0
  25. tpluspy-0.1.0a0/tests/model/conftest.py +8 -0
  26. tpluspy-0.1.0a0/tests/model/test_asset_identifier.py +79 -0
  27. tpluspy-0.1.0a0/tests/model/test_settlement.py +92 -0
  28. tpluspy-0.1.0a0/tests/model/test_types.py +99 -0
  29. tpluspy-0.1.0a0/tests/model/test_withdrawal.py +29 -0
  30. tpluspy-0.1.0a0/tests/utils/__init__.py +0 -0
  31. tpluspy-0.1.0a0/tests/utils/test_asset_identifier.py +54 -0
  32. tpluspy-0.1.0a0/tests/utils/test_bytes32.py +28 -0
  33. tpluspy-0.1.0a0/tests/utils/test_decimals.py +70 -0
  34. tpluspy-0.1.0a0/tests/utils/test_hex.py +10 -0
  35. tpluspy-0.1.0a0/tests/utils/test_user.py +12 -0
  36. tpluspy-0.1.0a0/tplus/__init__.py +0 -0
  37. tpluspy-0.1.0a0/tplus/client/__init__.py +4 -0
  38. tpluspy-0.1.0a0/tplus/client/base.py +291 -0
  39. tpluspy-0.1.0a0/tplus/client/clearingengine/__init__.py +66 -0
  40. tpluspy-0.1.0a0/tplus/client/clearingengine/admin.py +30 -0
  41. tpluspy-0.1.0a0/tplus/client/clearingengine/assetregistry.py +37 -0
  42. tpluspy-0.1.0a0/tplus/client/clearingengine/base.py +5 -0
  43. tpluspy-0.1.0a0/tplus/client/clearingengine/decimal.py +47 -0
  44. tpluspy-0.1.0a0/tplus/client/clearingengine/deposit.py +19 -0
  45. tpluspy-0.1.0a0/tplus/client/clearingengine/settlement.py +84 -0
  46. tpluspy-0.1.0a0/tplus/client/clearingengine/vault.py +32 -0
  47. tpluspy-0.1.0a0/tplus/client/clearingengine/withdrawal.py +57 -0
  48. tpluspy-0.1.0a0/tplus/client/orderbook.py +395 -0
  49. tpluspy-0.1.0a0/tplus/constants.py +4 -0
  50. tpluspy-0.1.0a0/tplus/evm/__init__.py +1 -0
  51. tpluspy-0.1.0a0/tplus/evm/abi.py +21 -0
  52. tpluspy-0.1.0a0/tplus/evm/contracts.py +324 -0
  53. tpluspy-0.1.0a0/tplus/evm/eip712.py +16 -0
  54. tpluspy-0.1.0a0/tplus/evm/exceptions.py +4 -0
  55. tpluspy-0.1.0a0/tplus/evm/utils.py +0 -0
  56. tpluspy-0.1.0a0/tplus/logger.py +10 -0
  57. tpluspy-0.1.0a0/tplus/model/__init__.py +0 -0
  58. tpluspy-0.1.0a0/tplus/model/asset_identifier.py +124 -0
  59. tpluspy-0.1.0a0/tplus/model/cancel_order.py +20 -0
  60. tpluspy-0.1.0a0/tplus/model/klines.py +43 -0
  61. tpluspy-0.1.0a0/tplus/model/limit_order.py +74 -0
  62. tpluspy-0.1.0a0/tplus/model/market.py +17 -0
  63. tpluspy-0.1.0a0/tplus/model/market_order.py +60 -0
  64. tpluspy-0.1.0a0/tplus/model/order.py +219 -0
  65. tpluspy-0.1.0a0/tplus/model/order_trigger.py +25 -0
  66. tpluspy-0.1.0a0/tplus/model/orderbook.py +43 -0
  67. tpluspy-0.1.0a0/tplus/model/replace_order.py +49 -0
  68. tpluspy-0.1.0a0/tplus/model/settlement.py +200 -0
  69. tpluspy-0.1.0a0/tplus/model/signed_message.py +38 -0
  70. tpluspy-0.1.0a0/tplus/model/trades.py +158 -0
  71. tpluspy-0.1.0a0/tplus/model/types.py +87 -0
  72. tpluspy-0.1.0a0/tplus/model/withdrawal.py +57 -0
  73. tpluspy-0.1.0a0/tplus/py.typed +0 -0
  74. tpluspy-0.1.0a0/tplus/utils/__init__.py +0 -0
  75. tpluspy-0.1.0a0/tplus/utils/bytes32.py +23 -0
  76. tpluspy-0.1.0a0/tplus/utils/decimals.py +45 -0
  77. tpluspy-0.1.0a0/tplus/utils/hex.py +78 -0
  78. tpluspy-0.1.0a0/tplus/utils/limit_order.py +54 -0
  79. tpluspy-0.1.0a0/tplus/utils/market_order.py +50 -0
  80. tpluspy-0.1.0a0/tplus/utils/replace_order.py +58 -0
  81. tpluspy-0.1.0a0/tplus/utils/serializers.py +15 -0
  82. tpluspy-0.1.0a0/tplus/utils/signing.py +30 -0
  83. tpluspy-0.1.0a0/tplus/utils/user/__init__.py +10 -0
  84. tpluspy-0.1.0a0/tplus/utils/user/ed_keyfile.py +97 -0
  85. tpluspy-0.1.0a0/tplus/utils/user/manager.py +86 -0
  86. tpluspy-0.1.0a0/tplus/utils/user/model.py +45 -0
  87. tpluspy-0.1.0a0/tplus/utils/user/validate.py +9 -0
  88. tpluspy-0.1.0a0/tplus/version.py +16 -0
  89. tpluspy-0.1.0a0/tpluspy.egg-info/PKG-INFO +266 -0
  90. tpluspy-0.1.0a0/tpluspy.egg-info/SOURCES.txt +92 -0
  91. tpluspy-0.1.0a0/tpluspy.egg-info/dependency_links.txt +1 -0
  92. tpluspy-0.1.0a0/tpluspy.egg-info/not-zip-safe +1 -0
  93. tpluspy-0.1.0a0/tpluspy.egg-info/requires.txt +27 -0
  94. tpluspy-0.1.0a0/tpluspy.egg-info/top_level.txt +2 -0
@@ -0,0 +1,29 @@
1
+ name: Publish Python Package
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ build-and-publish:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - name: Set up Python
14
+ uses: actions/setup-python@v5
15
+ with:
16
+ python-version: 3.11
17
+
18
+ - name: Build package
19
+ run: |
20
+ python -m pip install --upgrade setuptools wheel
21
+ python setup.py sdist bdist_wheel
22
+
23
+ - name: Publish to GitHub Packages
24
+ run: |
25
+ python -m pip install --upgrade twine
26
+ twine upload --repository-url https://upload.pypi.github.com/ dist/*
27
+ env:
28
+ TWINE_USERNAME: __token__
29
+ TWINE_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,50 @@
1
+ name: tpluspy tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: ["**"]
8
+
9
+ concurrency:
10
+ # Cancel older, in-progress jobs from the same PR, same workflow.
11
+ # use run_id if the job is triggered by a push to ensure
12
+ # push-triggered jobs to not get canceled.
13
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ build:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 30
20
+
21
+ steps:
22
+ - name: Checkout repository
23
+ uses: actions/checkout@v4
24
+
25
+ - name: Setup Python
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.10"
29
+
30
+ - name: Install
31
+ run: |
32
+ python -m pip install --upgrade pip
33
+ pip install .[lint,test]
34
+
35
+ - name: Lint
36
+ run: ruff check .
37
+
38
+ - name: Format
39
+ run: ruff format --check .
40
+
41
+ # TODO: Handle type checking issues
42
+ # - name: Type Check
43
+ # run: mypy .
44
+
45
+ - name: Client Tests
46
+ run: pytest tests --ignore=tests/evm
47
+
48
+ # TODO: Uncomment when/if this actually matters (dependency-heavy)
49
+ # - name: EVM Tests
50
+ # run: pytest tests/evm
@@ -0,0 +1,183 @@
1
+ # Ape
2
+ .build
3
+
4
+ # Editor
5
+ .idea
6
+
7
+ # Setuptools SCM
8
+ tplus/version.py
9
+
10
+ # Byte-compiled / optimized / DLL files
11
+ __pycache__/
12
+ *.py[cod]
13
+ *$py.class
14
+
15
+ # C extensions
16
+ *.so
17
+
18
+ # Distribution / packaging
19
+ .Python
20
+ build/
21
+ develop-eggs/
22
+ dist/
23
+ downloads/
24
+ eggs/
25
+ .eggs/
26
+ lib/
27
+ lib64/
28
+ parts/
29
+ sdist/
30
+ var/
31
+ wheels/
32
+ share/python-wheels/
33
+ *.egg-info/
34
+ .installed.cfg
35
+ *.egg
36
+ MANIFEST
37
+
38
+ # PyInstaller
39
+ # Usually these files are written by a python script from a template
40
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
41
+ *.manifest
42
+ *.spec
43
+
44
+ # Installer logs
45
+ pip-log.txt
46
+ pip-delete-this-directory.txt
47
+
48
+ # Unit test / coverage reports
49
+ htmlcov/
50
+ .tox/
51
+ .nox/
52
+ .coverage
53
+ .coverage.*
54
+ .cache
55
+ nosetests.xml
56
+ coverage.xml
57
+ *.cover
58
+ *.py,cover
59
+ .hypothesis/
60
+ .pytest_cache/
61
+ cover/
62
+
63
+ # Translations
64
+ *.mo
65
+ *.pot
66
+
67
+ # Django stuff:
68
+ *.log
69
+ local_settings.py
70
+ db.sqlite3
71
+ db.sqlite3-journal
72
+
73
+ # Flask stuff:
74
+ instance/
75
+ .webassets-cache
76
+
77
+ # Scrapy stuff:
78
+ .scrapy
79
+
80
+ # Sphinx documentation
81
+ docs/_build/
82
+
83
+ # PyBuilder
84
+ .pybuilder/
85
+ target/
86
+
87
+ # Jupyter Notebook
88
+ .ipynb_checkpoints
89
+
90
+ # IPython
91
+ profile_default/
92
+ ipython_config.py
93
+
94
+ # pyenv
95
+ # For a library or package, you might want to ignore these files since the code is
96
+ # intended to run in multiple environments; otherwise, check them in:
97
+ # .python-version
98
+
99
+ # pipenv
100
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
101
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
102
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
103
+ # install all needed dependencies.
104
+ #Pipfile.lock
105
+
106
+ # UV
107
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
108
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
109
+ # commonly ignored for libraries.
110
+ #uv.lock
111
+
112
+ # poetry
113
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
114
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
115
+ # commonly ignored for libraries.
116
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
117
+ #poetry.lock
118
+
119
+ # pdm
120
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
121
+ #pdm.lock
122
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
123
+ # in version control.
124
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
125
+ .pdm.toml
126
+ .pdm-python
127
+ .pdm-build/
128
+
129
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130
+ __pypackages__/
131
+
132
+ # Celery stuff
133
+ celerybeat-schedule
134
+ celerybeat.pid
135
+
136
+ # SageMath parsed files
137
+ *.sage.py
138
+
139
+ # Environments
140
+ .env
141
+ .venv
142
+ env/
143
+ venv/
144
+ ENV/
145
+ env.bak/
146
+ venv.bak/
147
+
148
+ # Spyder project settings
149
+ .spyderproject
150
+ .spyproject
151
+
152
+ # Rope project settings
153
+ .ropeproject
154
+
155
+ # mkdocs documentation
156
+ /site
157
+
158
+ # mypy
159
+ .mypy_cache/
160
+ .dmypy.json
161
+ dmypy.json
162
+
163
+ # Pyre type checker
164
+ .pyre/
165
+
166
+ # pytype static type analyzer
167
+ .pytype/
168
+
169
+ # Cython debug symbols
170
+ cython_debug/
171
+
172
+ # PyCharm
173
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
174
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
175
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
176
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
177
+ #.idea/
178
+
179
+ # Ruff stuff:
180
+ .ruff_cache/
181
+
182
+ # PyPI configuration file
183
+ .pypirc
@@ -0,0 +1,20 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: check-yaml
6
+
7
+ - repo: https://github.com/astral-sh/ruff-pre-commit
8
+ rev: v0.11.7
9
+ hooks:
10
+ - id: ruff
11
+ args: [--fix, --exit-non-zero-on-fix]
12
+ - id: ruff-format
13
+
14
+ - repo: https://github.com/pre-commit/mirrors-mypy
15
+ rev: v1.15.0
16
+ hooks:
17
+ - id: mypy
18
+
19
+ default_language_version:
20
+ python: python3
@@ -0,0 +1,266 @@
1
+ Metadata-Version: 2.1
2
+ Name: tpluspy
3
+ Version: 0.1.0a0
4
+ Summary: tpluspy: Client utilities for interacting with tplus
5
+ Home-page: https://github.com/tpluslabs/tpluspy
6
+ Author: TPlus Labs
7
+ Author-email: admin@tlus.cx
8
+ License: Apache-2.0
9
+ Keywords: ethereum
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: POSIX
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Requires-Python: >=3.10,<4
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: cryptography>=44.0.1
24
+ Requires-Dist: ecdsa>=0.17
25
+ Requires-Dist: httpx>=0.20
26
+ Requires-Dist: pycryptodome>=3.17.1
27
+ Requires-Dist: pydantic<3,>=2.10.4
28
+ Requires-Dist: websockets<14,>=13.1
29
+ Requires-Dist: eth-pydantic-types<0.3,>=0.2.2
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest>=6.0; extra == "test"
32
+ Requires-Dist: pytest-timeout<3,>=2.2.0; extra == "test"
33
+ Requires-Dist: pytest-mock; extra == "test"
34
+ Provides-Extra: lint
35
+ Requires-Dist: mypy<2,>=1.15.0; extra == "lint"
36
+ Requires-Dist: ruff>=0.11.7; extra == "lint"
37
+ Requires-Dist: mypy<2,>=1.15.0; extra == "lint"
38
+ Provides-Extra: release
39
+ Requires-Dist: setuptools>=75.6.0; extra == "release"
40
+ Requires-Dist: wheel; extra == "release"
41
+ Requires-Dist: twine; extra == "release"
42
+ Provides-Extra: evm
43
+ Requires-Dist: ape-tokens; extra == "evm"
44
+ Requires-Dist: click; extra == "evm"
45
+ Requires-Dist: eip712; extra == "evm"
46
+ Requires-Dist: eth-ape<0.9,>=0.8.32; extra == "evm"
47
+
48
+ # TPlus Python Client Utilities
49
+
50
+ Python clients for interacting with tplus.
51
+
52
+ ## Install
53
+
54
+ To install, use either `pip` or `uv pip`:
55
+
56
+ ```shell
57
+ uv pip install -e .
58
+ ```
59
+
60
+ ## Usage Example
61
+
62
+ ### REST and WebSocket Client (`tplus.client`)
63
+
64
+ The `tpluspy` library also provides an asynchronous client (`OrderBookClient`) for interacting with the `tplus-core` REST API and WebSocket streams.
65
+
66
+ #### Initialization
67
+
68
+ To use the client, first initialize it with a `User` object (for signing requests) and the base URL of your `tplus-core` instance:
69
+
70
+ ```python
71
+ import asyncio
72
+ from tplus.client import OrderBookClient
73
+ from tplus.utils.user import User
74
+
75
+ API_BASE_URL = "http://127.0.0.1:8000" # Replace with your API URL
76
+ user = User()
77
+
78
+ async def run_client():
79
+ # Use async context manager for automatic cleanup
80
+ async with OrderBookClient(user=user, base_url=API_BASE_URL) as client:
81
+ print("Client initialized.")
82
+ # ... use client methods ...
83
+
84
+ asyncio.run(run_client())
85
+ ```
86
+
87
+ #### REST API Usage
88
+
89
+ The client offers async methods for common REST endpoints:
90
+
91
+ **Fetching Data:**
92
+
93
+ ```python
94
+ # Get Order Book Snapshot for asset index 200
95
+ from tplus.model.asset_identifier import AssetIdentifier
96
+
97
+ example_asset = AssetIdentifier(Index=200)
98
+ orderbook = await client.get_orderbook_snapshot(example_asset)
99
+ print(f"Snapshot Sequence: {orderbook.sequence_number}")
100
+
101
+ # Get Klines for an asset
102
+ klines = await client.get_klines(example_asset)
103
+ print(f"Klines: {klines}")
104
+
105
+ # Get Market Details for an asset
106
+ market_details = await client.get_market(example_asset)
107
+ print(f"Market Details: Price Decimals={market_details.book_price_decimals}, Quantity Decimals={market_details.book_quantity_decimals}")
108
+
109
+ # Get orders for the user
110
+ user_orders, _ = await client.get_user_orders()
111
+ print(f"User Orders: {user_orders}")
112
+
113
+ # Get trades for the user and asset
114
+ user_asset_trades = await client.get_user_trades_for_asset(example_asset)
115
+ print(f"User Asset Trades: {user_asset_trades}")
116
+
117
+ # Get user inventory
118
+ inventory = await client.get_user_inventory()
119
+ print(f"Inventory: {inventory}")
120
+ ```
121
+
122
+ **Creating Orders:**
123
+
124
+ ```python
125
+ # Ensure example_asset is defined (e.g., from "Fetching Data" section)
126
+ from tplus.model.asset_identifier import AssetIdentifier
127
+ example_asset = AssetIdentifier(200)
128
+
129
+ # Create a Market for an asset (idempotent)
130
+ market_creation_response = await client.create_market(asset_id=example_asset)
131
+ print(f"Market Creation Response: {market_creation_response}")
132
+
133
+ # Create a Market Order for a specific asset
134
+ market_response = await client.create_market_order(
135
+ asset_id=example_asset,
136
+ quantity=10, # integer quantity
137
+ side="Buy",
138
+ fill_or_kill=False,
139
+ )
140
+ print(f"Market Order Response: {market_response}")
141
+
142
+ # Create a Limit Order for a specific asset
143
+ # Good-Till-Cancelled limit order
144
+ from tplus.model.limit_order import GTC
145
+ limit_response = await client.create_limit_order(
146
+ asset_id=example_asset,
147
+ quantity=5,
148
+ price=1_000,
149
+ side="Sell",
150
+ time_in_force=GTC(),
151
+ )
152
+ print(f"Limit Order Response: {limit_response}")
153
+
154
+ # Cancel an Order
155
+ # Order ID should be obtained from an order creation response.
156
+ order_id_to_cancel = "actual-order-id-from-api" # Replace with a real order ID
157
+ cancel_response = await client.cancel_order(
158
+ order_id=order_id_to_cancel,
159
+ asset_id=example_asset
160
+ )
161
+ print(f"Cancel Order Response: {cancel_response}")
162
+
163
+ # Replace an Order
164
+ # Original Order ID should be from an existing, open order.
165
+ original_order_id_to_replace = "actual-original-order-id" # Replace with a real order ID
166
+ replace_response = await client.replace_order(
167
+ original_order_id=original_order_id_to_replace,
168
+ asset_id=example_asset,
169
+ new_quantity=6, # Optional: New integer quantity
170
+ new_price=1050 # Optional: New integer price
171
+ )
172
+ print(f"Replace Order Response: {replace_response}")
173
+ ```
174
+
175
+ See `examples/rest_usage.py` for a runnable demonstration.
176
+
177
+ #### WebSocket Streaming
178
+
179
+ The client provides async iterators to stream real-time data:
180
+
181
+ ```python
182
+ from tplus.model.asset_identifier import AssetIdentifier
183
+ from tplus.model.orderbook import OrderBookDiff
184
+ from tplus.model.trades import Trade
185
+
186
+ example_asset = AssetIdentifier(200)
187
+
188
+ # Stream Order Book Diffs
189
+ async for diff_update in client.stream_depth(example_asset):
190
+ if isinstance(diff_update, OrderBookDiff):
191
+ print(f"[Depth] Seq={diff_update.sequence_number}, Asks={len(diff_update.asks)}, Bids={len(diff_update.bids)}")
192
+ # Add logic to handle the update, e.g., update a local order book
193
+
194
+ # Stream Finalized Trades
195
+ async for trade in client.stream_finalized_trades():
196
+ if isinstance(trade, Trade):
197
+ print(f"[Trade] ID: {trade.trade_id}, Price: {trade.price}, Qty: {trade.quantity}")
198
+ # Add logic to handle the trade
199
+
200
+ # Other available streams:
201
+ # client.stream_orders() -> OrderEvent
202
+ # client.stream_all_trades() -> TradeEvent
203
+ # client.stream_klines(asset_id) -> KlineUpdate
204
+ ```
205
+
206
+ See `examples/websocket_usage.py` for a runnable demonstration using `asyncio.gather` to run multiple streams concurrently.
207
+
208
+ ### Contracts
209
+
210
+ To interact with the contracts or sign T+ specific EIP-712 messages, ensure you have installed the `evm` extra:
211
+
212
+ ```shell
213
+ pip install tpluspy[evm]
214
+ ```
215
+
216
+ Use the `tplusp.contracts` module to read data from t+ contracts.
217
+ For example, launch a Sepolia-connected Ape console:
218
+
219
+ ```shell
220
+ ape console --network ethereum:sepolia:alchemy
221
+ ```
222
+
223
+ **Note**: You can use any provider you want or a RPC directly, it doesn't have to be Alchemy.
224
+
225
+ Then, once in the console, you will already have access to contracts that you can call methods on:
226
+
227
+ ```python
228
+ In [1]: registry.getAssets()
229
+ Out[1]: [getAssets_return(assetAddress=HexBytes('0x000000000000000000000000f08a50178dfcde18524640ea6618a1f965821715'), chainId=11155111, maxDeposits=100)]
230
+ In [2]: registry.admin()
231
+ Out[2]: '0x467a95fC5359edE5d5dDc4f10A1F4B680694858E'
232
+ ```
233
+
234
+ #### EIP-712
235
+
236
+ Sign EIP-712 messages, such as settlements, using the `eip712` library.
237
+
238
+ ```python
239
+ from ape import accounts, convert, chain
240
+ from tplus.evm.eip712 import Order
241
+ from tplus.evm.contracts import vault
242
+ from tplus.utils.user import UserManager
243
+
244
+ # Load your Ethereum account for t+.
245
+ tplus_user = accounts.load("tplus-account")
246
+
247
+ # Load your t+ user (public key).
248
+ user_id = UserManager.load("my_user").public_key
249
+
250
+ # Get the nonce from t+ or the contracts directly.
251
+ nonce = vault.getDepositNonce(tplus_user)
252
+
253
+ order = Order(
254
+ tokenOut="0x62622E77D1349Face943C6e7D5c01C61465FE1dc",
255
+ amountOut=convert("1 ether", int),
256
+ tokenIn="0x58372ab62269A52fA636aD7F200d93999595DCAF",
257
+ amountIn=convert("1 ether", int),
258
+ userId=user_id,
259
+ nonce=nonce,
260
+ validUntil=chain.pending_timestamp,
261
+ )
262
+
263
+ # Use this signature for the settlement.
264
+ signature = tplus_user.sign_message(order).encode_rsv()
265
+ print(signature)
266
+ ```