olive-solana-sdk 0.1.0__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.
- olive_solana_sdk-0.1.0/.gitignore +46 -0
- olive_solana_sdk-0.1.0/LICENSE +21 -0
- olive_solana_sdk-0.1.0/PKG-INFO +201 -0
- olive_solana_sdk-0.1.0/README.md +165 -0
- olive_solana_sdk-0.1.0/pyproject.toml +80 -0
- olive_solana_sdk-0.1.0/src/olive_solana/__init__.py +127 -0
- olive_solana_sdk-0.1.0/src/olive_solana/cli.py +110 -0
- olive_solana_sdk-0.1.0/src/olive_solana/conformance.py +112 -0
- olive_solana_sdk-0.1.0/src/olive_solana/constants.py +73 -0
- olive_solana_sdk-0.1.0/src/olive_solana/digests.py +137 -0
- olive_solana_sdk-0.1.0/src/olive_solana/doctor.py +289 -0
- olive_solana_sdk-0.1.0/src/olive_solana/managed_maker.py +650 -0
- olive_solana_sdk-0.1.0/src/olive_solana/py.typed +1 -0
- olive_solana_sdk-0.1.0/src/olive_solana/schema.py +188 -0
- olive_solana_sdk-0.1.0/src/olive_solana/schemas/__init__.py +1 -0
- olive_solana_sdk-0.1.0/src/olive_solana/schemas/maker-websocket-v1.fixtures.json +172 -0
- olive_solana_sdk-0.1.0/src/olive_solana/schemas/maker-websocket-v1.golden.json +55 -0
- olive_solana_sdk-0.1.0/src/olive_solana/schemas/maker-websocket-v1.schema.json +1547 -0
- olive_solana_sdk-0.1.0/src/olive_solana/schemas/postgres-maker-store-v1.sql +46 -0
- olive_solana_sdk-0.1.0/src/olive_solana/signer.py +74 -0
- olive_solana_sdk-0.1.0/src/olive_solana/store.py +397 -0
- olive_solana_sdk-0.1.0/src/olive_solana/types.py +657 -0
- olive_solana_sdk-0.1.0/src/olive_solana/ws_client.py +731 -0
- olive_solana_sdk-0.1.0/tests/test_contract.py +131 -0
- olive_solana_sdk-0.1.0/tests/test_postgres_store.py +50 -0
- olive_solana_sdk-0.1.0/tests/test_store_and_conformance.py +86 -0
- olive_solana_sdk-0.1.0/tests/test_ws_and_managed.py +950 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# deps
|
|
2
|
+
node_modules/
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.pyc
|
|
5
|
+
.venv/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.mypy_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.coverage
|
|
11
|
+
|
|
12
|
+
# solana / anchor
|
|
13
|
+
solana/target/*
|
|
14
|
+
!solana/target/idl/
|
|
15
|
+
solana/target/idl/*
|
|
16
|
+
!solana/target/idl/olive_options.json
|
|
17
|
+
solana/.anchor/
|
|
18
|
+
solana/.surfpool/
|
|
19
|
+
solana/test-ledger/
|
|
20
|
+
solana/evidence/
|
|
21
|
+
solana/deployments/*.json
|
|
22
|
+
!solana/deployments/*.example.json
|
|
23
|
+
backend/config/devnet.configured.json
|
|
24
|
+
|
|
25
|
+
# next.js
|
|
26
|
+
frontend/.next*/
|
|
27
|
+
frontend/out/
|
|
28
|
+
|
|
29
|
+
# build
|
|
30
|
+
dist/
|
|
31
|
+
*.tsbuildinfo
|
|
32
|
+
|
|
33
|
+
# env
|
|
34
|
+
.env
|
|
35
|
+
.env.*
|
|
36
|
+
.env.local
|
|
37
|
+
.env.*.local
|
|
38
|
+
!.env.example
|
|
39
|
+
!.env.devnet.example
|
|
40
|
+
|
|
41
|
+
# misc
|
|
42
|
+
.DS_Store
|
|
43
|
+
*.log
|
|
44
|
+
|
|
45
|
+
# Visual QA output from `npm run qa:share-cards`
|
|
46
|
+
frontend/.qa-cards/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Olive
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: olive-solana-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async Python SDK for market makers quoting Olive Solana options
|
|
5
|
+
Project-URL: Documentation, https://olive.ag/docs
|
|
6
|
+
Project-URL: Repository, https://github.com/macols77/olive
|
|
7
|
+
Project-URL: Issues, https://github.com/macols77/olive/issues
|
|
8
|
+
Author: Olive
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: market-maker,olive,options,rfq,solana
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: jsonschema<5,>=4.23
|
|
23
|
+
Requires-Dist: solders<1,>=0.26
|
|
24
|
+
Requires-Dist: websockets<18,>=15
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build<2,>=1.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: mypy<2,>=1.15; extra == 'dev'
|
|
28
|
+
Requires-Dist: psycopg[binary,pool]<4,>=3.2; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio<2,>=0.25; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest<10,>=8.3; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff<1,>=0.11; extra == 'dev'
|
|
32
|
+
Requires-Dist: types-jsonschema<5,>=4.23; extra == 'dev'
|
|
33
|
+
Provides-Extra: postgres
|
|
34
|
+
Requires-Dist: psycopg[binary,pool]<4,>=3.2; extra == 'postgres'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# Olive Solana Python SDK
|
|
38
|
+
|
|
39
|
+
The supported async Python client for market makers quoting Olive Solana options.
|
|
40
|
+
It implements only the native `/maker/v1/ws` protocol—there is no EVM or legacy
|
|
41
|
+
compatibility layer.
|
|
42
|
+
|
|
43
|
+
`ManagedMaker` is the recommended API. Maker code supplies entry premium or exit
|
|
44
|
+
amount and funding source; the SDK validates requests, binds canonical quote
|
|
45
|
+
fields, allocates durable quote IDs and nonces, signs through a local/HSM callback,
|
|
46
|
+
persists before submission, reconnects, and durably routes lifecycle events.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# From this repository:
|
|
52
|
+
pip install -e "packages/python-sdk[postgres]"
|
|
53
|
+
|
|
54
|
+
# After the distribution is published:
|
|
55
|
+
pip install "olive-solana-sdk[postgres]"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Python 3.11 or newer is required. Apply the packaged
|
|
59
|
+
`schemas/postgres-maker-store-v1.sql` with the maker's migration role and use
|
|
60
|
+
`schema_mode="verify"` in production. The default migration mode is intended for
|
|
61
|
+
development. `InMemoryMakerStore` is test-only.
|
|
62
|
+
|
|
63
|
+
The exact migration is also available programmatically with
|
|
64
|
+
`postgres_maker_store_schema()` so deployment tooling does not need to locate
|
|
65
|
+
the installed wheel's data directory.
|
|
66
|
+
|
|
67
|
+
## Managed maker quickstart
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import asyncio
|
|
71
|
+
import os
|
|
72
|
+
|
|
73
|
+
from olive_solana import (
|
|
74
|
+
DeploymentIdentity,
|
|
75
|
+
LocalKeypairMakerSigner,
|
|
76
|
+
ManagedMaker,
|
|
77
|
+
ManagedMakerEventHandlers,
|
|
78
|
+
ManagedMakerOptions,
|
|
79
|
+
PostgresMakerStore,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def main() -> None:
|
|
84
|
+
signer = LocalKeypairMakerSigner.from_json_file(
|
|
85
|
+
os.environ["OLIVE_QUOTE_SIGNER_KEYPAIR"]
|
|
86
|
+
)
|
|
87
|
+
store = PostgresMakerStore(
|
|
88
|
+
os.environ["DATABASE_URL"], schema_mode="verify"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
async def entry(rfq):
|
|
92
|
+
premium = await price_entry(rfq.request)
|
|
93
|
+
return (
|
|
94
|
+
rfq.decline("outside_risk_limits")
|
|
95
|
+
if premium is None
|
|
96
|
+
else rfq.quote(premium_usdc=premium)
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
async def exit_(rfq):
|
|
100
|
+
amount = await price_exit(rfq.request)
|
|
101
|
+
return (
|
|
102
|
+
rfq.decline("outside_risk_limits")
|
|
103
|
+
if amount is None
|
|
104
|
+
else rfq.quote(
|
|
105
|
+
exit_amount_usdc=amount,
|
|
106
|
+
usdc_source=os.environ["OLIVE_EXIT_USDC_SOURCE"],
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
maker = ManagedMaker(
|
|
111
|
+
ManagedMakerOptions(
|
|
112
|
+
url=os.environ["OLIVE_MAKER_WS_URL"],
|
|
113
|
+
maker_config=os.environ["OLIVE_MAKER_CONFIG"],
|
|
114
|
+
expected_deployment=DeploymentIdentity(
|
|
115
|
+
program_id=os.environ["OLIVE_PROGRAM_ID"],
|
|
116
|
+
genesis_hash=os.environ["OLIVE_GENESIS_HASH"],
|
|
117
|
+
chain_tag=os.environ["OLIVE_CHAIN_TAG"],
|
|
118
|
+
),
|
|
119
|
+
signer=signer,
|
|
120
|
+
store=store,
|
|
121
|
+
topics=("btc", "sol"),
|
|
122
|
+
products=(0, 1),
|
|
123
|
+
on_entry_rfq=entry,
|
|
124
|
+
on_exit_rfq=exit_,
|
|
125
|
+
events=ManagedMakerEventHandlers(
|
|
126
|
+
# Selection is feedback, not execution.
|
|
127
|
+
on_entry_selected=record_selection,
|
|
128
|
+
# Only finalized fills trigger hedging/reconciliation.
|
|
129
|
+
on_entry_filled=hedge_finalized_entry,
|
|
130
|
+
on_exit_filled=reconcile_finalized_exit,
|
|
131
|
+
on_funding_required=alert_funding,
|
|
132
|
+
on_default=page_risk,
|
|
133
|
+
on_settlement=record_settlement,
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
try:
|
|
138
|
+
await maker.run()
|
|
139
|
+
finally:
|
|
140
|
+
await store.close()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
asyncio.run(main())
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
An empty topic or product collection subscribes to everything authorized by
|
|
147
|
+
`maker.hello`. Keep `takeover_existing_session=False` for ordinary starts; enable
|
|
148
|
+
it only for an intentional failover.
|
|
149
|
+
|
|
150
|
+
## Operational preflight
|
|
151
|
+
|
|
152
|
+
The doctor authenticates without subscribing, taking over a live maker, or
|
|
153
|
+
submitting quotes:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
olive-maker-doctor --topics btc,sol --products 0,1 \
|
|
157
|
+
--exit-sources "$OLIVE_EXIT_USDC_SOURCE"
|
|
158
|
+
|
|
159
|
+
olive-maker-doctor --json > maker-doctor.json
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Connection and deployment values default from `OLIVE_MAKER_WS_URL`,
|
|
163
|
+
`OLIVE_MAKER_CONFIG`, `OLIVE_PROGRAM_ID`, `OLIVE_GENESIS_HASH`,
|
|
164
|
+
`OLIVE_CHAIN_TAG`, and `OLIVE_QUOTE_SIGNER_KEYPAIR`.
|
|
165
|
+
|
|
166
|
+
## Reliability contract
|
|
167
|
+
|
|
168
|
+
- Wire integers remain canonical decimal strings.
|
|
169
|
+
- Entry and exit nonce namespaces are separate.
|
|
170
|
+
- Complete quote, digest, and signature are committed before submission.
|
|
171
|
+
- Restart retries only the exact stored quote and signature.
|
|
172
|
+
- Durable events are stored before `event.ack`; local handler completion is
|
|
173
|
+
persisted independently.
|
|
174
|
+
- `quote.selected` and `exit_quote.selected` are not fills. Only finalized
|
|
175
|
+
`trade.filled` and `exit.filled` are canonical execution signals.
|
|
176
|
+
- Signatures and private signer data are never included in SDK status events.
|
|
177
|
+
|
|
178
|
+
The low-level `MakerWsClient` remains available for makers requiring custom
|
|
179
|
+
orchestration. Releases must pass the shared fixture/digest suite and all
|
|
180
|
+
fail-closed maker conformance scenarios.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
python -m venv .venv
|
|
186
|
+
.venv/bin/pip install -e ".[dev]"
|
|
187
|
+
.venv/bin/ruff check .
|
|
188
|
+
.venv/bin/mypy src
|
|
189
|
+
.venv/bin/pytest
|
|
190
|
+
.venv/bin/python -m build
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Release
|
|
194
|
+
|
|
195
|
+
PyPI releases use trusted publishing from
|
|
196
|
+
`.github/workflows/publish-python-sdk.yml`; no long-lived API token is stored in
|
|
197
|
+
GitHub. Configure the PyPI publisher for repository `macols77/olive`, workflow
|
|
198
|
+
`publish-python-sdk.yml`, and environment `pypi`. Pushing a tag that exactly
|
|
199
|
+
matches `python-sdk-v<pyproject version>` builds, checks, and uploads both the
|
|
200
|
+
wheel and source distribution. Protect the `pypi` environment with required
|
|
201
|
+
reviewer approval.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Olive Solana Python SDK
|
|
2
|
+
|
|
3
|
+
The supported async Python client for market makers quoting Olive Solana options.
|
|
4
|
+
It implements only the native `/maker/v1/ws` protocol—there is no EVM or legacy
|
|
5
|
+
compatibility layer.
|
|
6
|
+
|
|
7
|
+
`ManagedMaker` is the recommended API. Maker code supplies entry premium or exit
|
|
8
|
+
amount and funding source; the SDK validates requests, binds canonical quote
|
|
9
|
+
fields, allocates durable quote IDs and nonces, signs through a local/HSM callback,
|
|
10
|
+
persists before submission, reconnects, and durably routes lifecycle events.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# From this repository:
|
|
16
|
+
pip install -e "packages/python-sdk[postgres]"
|
|
17
|
+
|
|
18
|
+
# After the distribution is published:
|
|
19
|
+
pip install "olive-solana-sdk[postgres]"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Python 3.11 or newer is required. Apply the packaged
|
|
23
|
+
`schemas/postgres-maker-store-v1.sql` with the maker's migration role and use
|
|
24
|
+
`schema_mode="verify"` in production. The default migration mode is intended for
|
|
25
|
+
development. `InMemoryMakerStore` is test-only.
|
|
26
|
+
|
|
27
|
+
The exact migration is also available programmatically with
|
|
28
|
+
`postgres_maker_store_schema()` so deployment tooling does not need to locate
|
|
29
|
+
the installed wheel's data directory.
|
|
30
|
+
|
|
31
|
+
## Managed maker quickstart
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
import os
|
|
36
|
+
|
|
37
|
+
from olive_solana import (
|
|
38
|
+
DeploymentIdentity,
|
|
39
|
+
LocalKeypairMakerSigner,
|
|
40
|
+
ManagedMaker,
|
|
41
|
+
ManagedMakerEventHandlers,
|
|
42
|
+
ManagedMakerOptions,
|
|
43
|
+
PostgresMakerStore,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def main() -> None:
|
|
48
|
+
signer = LocalKeypairMakerSigner.from_json_file(
|
|
49
|
+
os.environ["OLIVE_QUOTE_SIGNER_KEYPAIR"]
|
|
50
|
+
)
|
|
51
|
+
store = PostgresMakerStore(
|
|
52
|
+
os.environ["DATABASE_URL"], schema_mode="verify"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async def entry(rfq):
|
|
56
|
+
premium = await price_entry(rfq.request)
|
|
57
|
+
return (
|
|
58
|
+
rfq.decline("outside_risk_limits")
|
|
59
|
+
if premium is None
|
|
60
|
+
else rfq.quote(premium_usdc=premium)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
async def exit_(rfq):
|
|
64
|
+
amount = await price_exit(rfq.request)
|
|
65
|
+
return (
|
|
66
|
+
rfq.decline("outside_risk_limits")
|
|
67
|
+
if amount is None
|
|
68
|
+
else rfq.quote(
|
|
69
|
+
exit_amount_usdc=amount,
|
|
70
|
+
usdc_source=os.environ["OLIVE_EXIT_USDC_SOURCE"],
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
maker = ManagedMaker(
|
|
75
|
+
ManagedMakerOptions(
|
|
76
|
+
url=os.environ["OLIVE_MAKER_WS_URL"],
|
|
77
|
+
maker_config=os.environ["OLIVE_MAKER_CONFIG"],
|
|
78
|
+
expected_deployment=DeploymentIdentity(
|
|
79
|
+
program_id=os.environ["OLIVE_PROGRAM_ID"],
|
|
80
|
+
genesis_hash=os.environ["OLIVE_GENESIS_HASH"],
|
|
81
|
+
chain_tag=os.environ["OLIVE_CHAIN_TAG"],
|
|
82
|
+
),
|
|
83
|
+
signer=signer,
|
|
84
|
+
store=store,
|
|
85
|
+
topics=("btc", "sol"),
|
|
86
|
+
products=(0, 1),
|
|
87
|
+
on_entry_rfq=entry,
|
|
88
|
+
on_exit_rfq=exit_,
|
|
89
|
+
events=ManagedMakerEventHandlers(
|
|
90
|
+
# Selection is feedback, not execution.
|
|
91
|
+
on_entry_selected=record_selection,
|
|
92
|
+
# Only finalized fills trigger hedging/reconciliation.
|
|
93
|
+
on_entry_filled=hedge_finalized_entry,
|
|
94
|
+
on_exit_filled=reconcile_finalized_exit,
|
|
95
|
+
on_funding_required=alert_funding,
|
|
96
|
+
on_default=page_risk,
|
|
97
|
+
on_settlement=record_settlement,
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
try:
|
|
102
|
+
await maker.run()
|
|
103
|
+
finally:
|
|
104
|
+
await store.close()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
asyncio.run(main())
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
An empty topic or product collection subscribes to everything authorized by
|
|
111
|
+
`maker.hello`. Keep `takeover_existing_session=False` for ordinary starts; enable
|
|
112
|
+
it only for an intentional failover.
|
|
113
|
+
|
|
114
|
+
## Operational preflight
|
|
115
|
+
|
|
116
|
+
The doctor authenticates without subscribing, taking over a live maker, or
|
|
117
|
+
submitting quotes:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
olive-maker-doctor --topics btc,sol --products 0,1 \
|
|
121
|
+
--exit-sources "$OLIVE_EXIT_USDC_SOURCE"
|
|
122
|
+
|
|
123
|
+
olive-maker-doctor --json > maker-doctor.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Connection and deployment values default from `OLIVE_MAKER_WS_URL`,
|
|
127
|
+
`OLIVE_MAKER_CONFIG`, `OLIVE_PROGRAM_ID`, `OLIVE_GENESIS_HASH`,
|
|
128
|
+
`OLIVE_CHAIN_TAG`, and `OLIVE_QUOTE_SIGNER_KEYPAIR`.
|
|
129
|
+
|
|
130
|
+
## Reliability contract
|
|
131
|
+
|
|
132
|
+
- Wire integers remain canonical decimal strings.
|
|
133
|
+
- Entry and exit nonce namespaces are separate.
|
|
134
|
+
- Complete quote, digest, and signature are committed before submission.
|
|
135
|
+
- Restart retries only the exact stored quote and signature.
|
|
136
|
+
- Durable events are stored before `event.ack`; local handler completion is
|
|
137
|
+
persisted independently.
|
|
138
|
+
- `quote.selected` and `exit_quote.selected` are not fills. Only finalized
|
|
139
|
+
`trade.filled` and `exit.filled` are canonical execution signals.
|
|
140
|
+
- Signatures and private signer data are never included in SDK status events.
|
|
141
|
+
|
|
142
|
+
The low-level `MakerWsClient` remains available for makers requiring custom
|
|
143
|
+
orchestration. Releases must pass the shared fixture/digest suite and all
|
|
144
|
+
fail-closed maker conformance scenarios.
|
|
145
|
+
|
|
146
|
+
## Development
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
python -m venv .venv
|
|
150
|
+
.venv/bin/pip install -e ".[dev]"
|
|
151
|
+
.venv/bin/ruff check .
|
|
152
|
+
.venv/bin/mypy src
|
|
153
|
+
.venv/bin/pytest
|
|
154
|
+
.venv/bin/python -m build
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Release
|
|
158
|
+
|
|
159
|
+
PyPI releases use trusted publishing from
|
|
160
|
+
`.github/workflows/publish-python-sdk.yml`; no long-lived API token is stored in
|
|
161
|
+
GitHub. Configure the PyPI publisher for repository `macols77/olive`, workflow
|
|
162
|
+
`publish-python-sdk.yml`, and environment `pypi`. Pushing a tag that exactly
|
|
163
|
+
matches `python-sdk-v<pyproject version>` builds, checks, and uploads both the
|
|
164
|
+
wheel and source distribution. Protect the `pypi` environment with required
|
|
165
|
+
reviewer approval.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "olive-solana-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Async Python SDK for market makers quoting Olive Solana options"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Olive" }]
|
|
14
|
+
keywords = ["olive", "solana", "options", "rfq", "market-maker"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Framework :: AsyncIO",
|
|
18
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"jsonschema>=4.23,<5",
|
|
28
|
+
"solders>=0.26,<1",
|
|
29
|
+
"websockets>=15,<18",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
postgres = ["psycopg[binary,pool]>=3.2,<4"]
|
|
34
|
+
dev = [
|
|
35
|
+
"build>=1.2,<2",
|
|
36
|
+
"mypy>=1.15,<2",
|
|
37
|
+
"psycopg[binary,pool]>=3.2,<4",
|
|
38
|
+
"pytest>=8.3,<10",
|
|
39
|
+
"pytest-asyncio>=0.25,<2",
|
|
40
|
+
"ruff>=0.11,<1",
|
|
41
|
+
"types-jsonschema>=4.23,<5",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
olive-maker-doctor = "olive_solana.cli:doctor_main"
|
|
46
|
+
olive-maker-conformance = "olive_solana.cli:conformance_main"
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Documentation = "https://olive.ag/docs"
|
|
50
|
+
Repository = "https://github.com/macols77/olive"
|
|
51
|
+
Issues = "https://github.com/macols77/olive/issues"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/olive_solana"]
|
|
55
|
+
|
|
56
|
+
[tool.hatch.build.targets.sdist]
|
|
57
|
+
include = ["src/olive_solana", "tests", "README.md", "LICENSE", "pyproject.toml"]
|
|
58
|
+
|
|
59
|
+
[tool.pytest.ini_options]
|
|
60
|
+
addopts = "-ra"
|
|
61
|
+
asyncio_mode = "auto"
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
line-length = 110
|
|
66
|
+
target-version = "py311"
|
|
67
|
+
|
|
68
|
+
[tool.ruff.lint]
|
|
69
|
+
select = ["E", "F", "I", "UP", "B", "ASYNC"]
|
|
70
|
+
ignore = ["E501", "ASYNC110"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint.per-file-ignores]
|
|
73
|
+
"src/olive_solana/__init__.py" = ["F401"]
|
|
74
|
+
|
|
75
|
+
[tool.mypy]
|
|
76
|
+
python_version = "3.11"
|
|
77
|
+
strict = true
|
|
78
|
+
packages = ["olive_solana"]
|
|
79
|
+
mypy_path = "src"
|
|
80
|
+
warn_unused_ignores = false
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("olive-solana-sdk")
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
from .conformance import ( # noqa: E402
|
|
11
|
+
MAKER_CONFORMANCE_SCENARIOS,
|
|
12
|
+
ConformanceEnvironment,
|
|
13
|
+
format_maker_conformance_report,
|
|
14
|
+
run_maker_conformance_suite,
|
|
15
|
+
)
|
|
16
|
+
from .constants import ( # noqa: E402
|
|
17
|
+
CREDIT_ENTRY_QUOTE_DOMAIN,
|
|
18
|
+
DAILY_ENTRY_QUOTE_DOMAIN,
|
|
19
|
+
EXIT_QUOTE_DOMAIN,
|
|
20
|
+
MAKER_WS_AUTH_DOMAIN,
|
|
21
|
+
MAKER_WS_MAX_FRAME_BYTES,
|
|
22
|
+
MAKER_WS_PATH,
|
|
23
|
+
MAKER_WS_PROTOCOL_VERSION,
|
|
24
|
+
)
|
|
25
|
+
from .digests import ( # noqa: E402
|
|
26
|
+
decode_bytes,
|
|
27
|
+
entry_quote_digest,
|
|
28
|
+
exit_quote_digest,
|
|
29
|
+
integer_le,
|
|
30
|
+
maker_ws_auth_digest,
|
|
31
|
+
public_key_bytes,
|
|
32
|
+
)
|
|
33
|
+
from .doctor import ( # noqa: E402
|
|
34
|
+
MakerDoctorCheck,
|
|
35
|
+
MakerDoctorOptions,
|
|
36
|
+
MakerDoctorReport,
|
|
37
|
+
format_maker_doctor_report,
|
|
38
|
+
run_maker_doctor,
|
|
39
|
+
)
|
|
40
|
+
from .managed_maker import ( # noqa: E402
|
|
41
|
+
DeclineDecision,
|
|
42
|
+
EntryQuoteDecision,
|
|
43
|
+
ExitQuoteDecision,
|
|
44
|
+
ManagedEntryRfq,
|
|
45
|
+
ManagedExitRfq,
|
|
46
|
+
ManagedMaker,
|
|
47
|
+
ManagedMakerEventHandlers,
|
|
48
|
+
ManagedMakerOptions,
|
|
49
|
+
)
|
|
50
|
+
from .schema import ( # noqa: E402
|
|
51
|
+
ProtocolValidationError,
|
|
52
|
+
parse_lifecycle_event,
|
|
53
|
+
parse_notification,
|
|
54
|
+
parse_result,
|
|
55
|
+
parse_rfq,
|
|
56
|
+
validate_entry_quote,
|
|
57
|
+
validate_exit_quote,
|
|
58
|
+
)
|
|
59
|
+
from .signer import CallbackMakerSigner, LocalKeypairMakerSigner # noqa: E402
|
|
60
|
+
from .store import InMemoryMakerStore, PostgresMakerStore, postgres_maker_store_schema # noqa: E402
|
|
61
|
+
from .types import ( # noqa: E402
|
|
62
|
+
AckResult,
|
|
63
|
+
AuthChallenge,
|
|
64
|
+
BucketChainEvent,
|
|
65
|
+
CapacityResult,
|
|
66
|
+
ConnectionStatus,
|
|
67
|
+
DeclineResult,
|
|
68
|
+
DeploymentIdentity,
|
|
69
|
+
EntryExpiredUnfilled,
|
|
70
|
+
EntryOutbid,
|
|
71
|
+
EntryQuote,
|
|
72
|
+
EntryRequest,
|
|
73
|
+
EntryRfqMessage,
|
|
74
|
+
EntryRfqParams,
|
|
75
|
+
EntrySelected,
|
|
76
|
+
EntrySubmitResult,
|
|
77
|
+
ExitExpiredUnfilled,
|
|
78
|
+
ExitFilled,
|
|
79
|
+
ExitOutbid,
|
|
80
|
+
ExitPaymentCapacity,
|
|
81
|
+
ExitQuote,
|
|
82
|
+
ExitRequest,
|
|
83
|
+
ExitRfqMessage,
|
|
84
|
+
ExitRfqParams,
|
|
85
|
+
ExitSelected,
|
|
86
|
+
ExitSource,
|
|
87
|
+
ExitSubmitResult,
|
|
88
|
+
FundingRequired,
|
|
89
|
+
Hello,
|
|
90
|
+
HelloLimits,
|
|
91
|
+
LifecycleEvent,
|
|
92
|
+
MakerPosition,
|
|
93
|
+
MakerSigner,
|
|
94
|
+
MakerStore,
|
|
95
|
+
MakerStoreScope,
|
|
96
|
+
PositionBucket,
|
|
97
|
+
PositionExpired,
|
|
98
|
+
PositionRedeemed,
|
|
99
|
+
PositionsResult,
|
|
100
|
+
PrefundingRequired,
|
|
101
|
+
Product,
|
|
102
|
+
QuoteDomains,
|
|
103
|
+
RecoverResult,
|
|
104
|
+
RfqMessage,
|
|
105
|
+
SettlementRecorded,
|
|
106
|
+
SignerContext,
|
|
107
|
+
StoredEvent,
|
|
108
|
+
StoredSubmission,
|
|
109
|
+
SubscriptionResult,
|
|
110
|
+
TopicCapacity,
|
|
111
|
+
TradeFilled,
|
|
112
|
+
)
|
|
113
|
+
from .ws_client import ( # noqa: E402
|
|
114
|
+
MakerWsClient,
|
|
115
|
+
MakerWsClientOptions,
|
|
116
|
+
MakerWsRpcFailure,
|
|
117
|
+
MakerWsTerminalProtocolFailure,
|
|
118
|
+
maker_ws_url,
|
|
119
|
+
normalize_topics,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
del PackageNotFoundError, version
|
|
123
|
+
__all__ = [
|
|
124
|
+
name
|
|
125
|
+
for name, value in globals().items()
|
|
126
|
+
if not name.startswith("_") and name != "annotations" and value.__class__.__name__ != "module"
|
|
127
|
+
]
|