shield-swap-sdk 0.2.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.
Files changed (57) hide show
  1. shield_swap_sdk-0.2.0/.gitignore +2 -0
  2. shield_swap_sdk-0.2.0/PKG-INFO +182 -0
  3. shield_swap_sdk-0.2.0/README.md +165 -0
  4. shield_swap_sdk-0.2.0/codegen/amm_api.openapi.json +5928 -0
  5. shield_swap_sdk-0.2.0/codegen/regen-abi.sh +52 -0
  6. shield_swap_sdk-0.2.0/codegen/regen-openapi.sh +15 -0
  7. shield_swap_sdk-0.2.0/codegen/shield_swap.abi.json +2777 -0
  8. shield_swap_sdk-0.2.0/codegen/shield_swap.aleo +6437 -0
  9. shield_swap_sdk-0.2.0/pyproject.toml +19 -0
  10. shield_swap_sdk-0.2.0/pyrightconfig.json +8 -0
  11. shield_swap_sdk-0.2.0/pytest.ini +10 -0
  12. shield_swap_sdk-0.2.0/python/aleo_shield_swap/__init__.py +60 -0
  13. shield_swap_sdk-0.2.0/python/aleo_shield_swap/_api_models.py +832 -0
  14. shield_swap_sdk-0.2.0/python/aleo_shield_swap/_calls.py +98 -0
  15. shield_swap_sdk-0.2.0/python/aleo_shield_swap/_core.py +311 -0
  16. shield_swap_sdk-0.2.0/python/aleo_shield_swap/_generated.py +508 -0
  17. shield_swap_sdk-0.2.0/python/aleo_shield_swap/agent.py +177 -0
  18. shield_swap_sdk-0.2.0/python/aleo_shield_swap/api.py +236 -0
  19. shield_swap_sdk-0.2.0/python/aleo_shield_swap/async_client.py +323 -0
  20. shield_swap_sdk-0.2.0/python/aleo_shield_swap/client.py +603 -0
  21. shield_swap_sdk-0.2.0/python/aleo_shield_swap/derivations.py +179 -0
  22. shield_swap_sdk-0.2.0/python/aleo_shield_swap/errors.py +56 -0
  23. shield_swap_sdk-0.2.0/python/aleo_shield_swap/mcp.py +101 -0
  24. shield_swap_sdk-0.2.0/python/aleo_shield_swap/tick_hints.py +29 -0
  25. shield_swap_sdk-0.2.0/python/aleo_shield_swap/tick_math.py +58 -0
  26. shield_swap_sdk-0.2.0/python/aleo_shield_swap/types.py +108 -0
  27. shield_swap_sdk-0.2.0/tests/__init__.py +0 -0
  28. shield_swap_sdk-0.2.0/tests/conftest.py +221 -0
  29. shield_swap_sdk-0.2.0/tests/fixtures/pools_response.json +41 -0
  30. shield_swap_sdk-0.2.0/tests/fixtures/programs/shield_swap_v3.aleo +6439 -0
  31. shield_swap_sdk-0.2.0/tests/fixtures/programs/test_shield_swap_multisig_core.aleo +827 -0
  32. shield_swap_sdk-0.2.0/tests/fixtures/programs/test_token_a.aleo +193 -0
  33. shield_swap_sdk-0.2.0/tests/fixtures/programs/test_token_b.aleo +193 -0
  34. shield_swap_sdk-0.2.0/tests/integration/__init__.py +0 -0
  35. shield_swap_sdk-0.2.0/tests/integration/conftest.py +61 -0
  36. shield_swap_sdk-0.2.0/tests/integration/devnode_amm.py +276 -0
  37. shield_swap_sdk-0.2.0/tests/integration/test_devnode_lifecycle.py +334 -0
  38. shield_swap_sdk-0.2.0/tests/integration/test_drift.py +37 -0
  39. shield_swap_sdk-0.2.0/tests/integration/test_reads_live.py +153 -0
  40. shield_swap_sdk-0.2.0/tests/integration/test_swap_lifecycle.py +84 -0
  41. shield_swap_sdk-0.2.0/tests/test_agent.py +38 -0
  42. shield_swap_sdk-0.2.0/tests/test_api_client.py +90 -0
  43. shield_swap_sdk-0.2.0/tests/test_api_models.py +40 -0
  44. shield_swap_sdk-0.2.0/tests/test_async_client.py +192 -0
  45. shield_swap_sdk-0.2.0/tests/test_blinding.py +92 -0
  46. shield_swap_sdk-0.2.0/tests/test_claim.py +58 -0
  47. shield_swap_sdk-0.2.0/tests/test_client_reads.py +74 -0
  48. shield_swap_sdk-0.2.0/tests/test_core.py +129 -0
  49. shield_swap_sdk-0.2.0/tests/test_derivations.py +37 -0
  50. shield_swap_sdk-0.2.0/tests/test_errors.py +27 -0
  51. shield_swap_sdk-0.2.0/tests/test_generated.py +42 -0
  52. shield_swap_sdk-0.2.0/tests/test_liquidity.py +141 -0
  53. shield_swap_sdk-0.2.0/tests/test_mcp.py +31 -0
  54. shield_swap_sdk-0.2.0/tests/test_package.py +5 -0
  55. shield_swap_sdk-0.2.0/tests/test_swap.py +95 -0
  56. shield_swap_sdk-0.2.0/tests/test_tick_math.py +47 -0
  57. shield_swap_sdk-0.2.0/tests/test_types.py +37 -0
@@ -0,0 +1,2 @@
1
+ __pycache__/
2
+ *.pyc
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: shield-swap-sdk
3
+ Version: 0.2.0
4
+ Summary: Python SDK for the shield swap AMM Dex on Aleo
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: aleo-sdk>=0.2
7
+ Requires-Dist: requests>=2
8
+ Provides-Extra: async
9
+ Requires-Dist: httpx>=0.27; extra == 'async'
10
+ Provides-Extra: dev
11
+ Requires-Dist: datamodel-code-generator>=0.25; extra == 'dev'
12
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
13
+ Requires-Dist: pytest>=8; extra == 'dev'
14
+ Provides-Extra: mcp
15
+ Requires-Dist: mcp>=1.0; extra == 'mcp'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # shield-swap-sdk
19
+
20
+ Typed Python client for the **shield_swap** AMM on Aleo. Sits on top of the
21
+ Aleo Python SDK's facade (`aleo.Aleo`): signer, record provider, proving
22
+ configuration, and network all come from the client you bind — this package
23
+ adds the DEX verbs, the typed results, and the off-chain DEX API, nothing
24
+ else.
25
+
26
+ ```python
27
+ from aleo import Aleo
28
+ from aleo_shield_swap import ShieldSwap
29
+
30
+ aleo = Aleo(Aleo.HTTPProvider("https://api.provable.com"))
31
+ aleo.default_account = account
32
+ dex = ShieldSwap(aleo)
33
+
34
+ pools = dex.api.get_pools() # requires a live DEX API
35
+ handle = dex.swap(pool_key=pools[0].key,
36
+ token_in_id=pools[0].token0,
37
+ amount_in=10**9).delegate() # broadcasts; spends funds
38
+ out = dex.claim_swap_output(handle).delegate() # broadcasts; spends funds
39
+ ```
40
+
41
+ ## Install
42
+
43
+ ```bash
44
+ pip install -e shield-swap-sdk # from the repo root
45
+ pip install -e "shield-swap-sdk[async]" # + AsyncShieldSwap (httpx)
46
+ pip install -e "shield-swap-sdk[mcp]" # + the MCP server
47
+ ```
48
+
49
+ Requires `aleo-sdk>=0.2` (this repo's SDK; imports as `aleo`) and Python 3.10+.
50
+
51
+ ## How calls work
52
+
53
+ Every read returns a value immediately. Every write returns a prepared
54
+ `DexCall` — nothing touches the network until you invoke a terminal verb:
55
+
56
+ ```python
57
+ call = dex.swap(pool_key=key, token_in_id=token, amount_in=10**9)
58
+
59
+ call.simulate() # runs locally; no broadcast, no fee
60
+ call.transact(account) # proves locally, broadcasts, pays the fee
61
+ call.delegate(account) # proves via the delegated proving service
62
+ ```
63
+
64
+ `transact` and `delegate` return the verb's *typed result* (a `SwapHandle`,
65
+ `MintResult`, `ClaimResult`, …) built from the transaction's root-transition
66
+ outputs — not a bare transaction id. Local proving downloads SNARK parameters
67
+ on first use and takes minutes for the larger entrypoints; `delegate` is the
68
+ practical path and requires DPS credentials on the provider
69
+ (`api_key=`, `network_client.consumer_id`).
70
+
71
+ Chain reads and writes live directly on `ShieldSwap`; the off-chain DEX API
72
+ is namespaced under `.api`, so a call site always shows whether a value came
73
+ from the chain or the service.
74
+
75
+ ## The verb surface
76
+
77
+ **Chain reads** (node REST API):
78
+
79
+ | Verb | Returns |
80
+ |---|---|
81
+ | `get_pool(pool_key)` | The pool struct from the `pools` mapping. |
82
+ | `get_slot(pool_key)` | `SlotView` — current tick, sqrt price, liquidity, spacing. |
83
+ | `get_swap_output(swap_id)` | The finalized swap outcome; raises `SwapOutputNotFinalizedError` until the finalize lands (and again after the claim consumes it). |
84
+ | `is_pool_initialized(pool_key)` | Whether the pool exists on chain. |
85
+ | `get_private_balances(programs)` | Summed unspent record amounts per token program (needs a registered record provider). |
86
+ | `get_balances()` | Public + private balances in one shape. |
87
+ | `derive_pool_key(token0, token1, fee)` / `derive_tick_key(pool_key, tick)` | Mapping keys derived locally — no network. |
88
+
89
+ **Writes** (each returns a `DexCall`):
90
+
91
+ | Verb | What it does |
92
+ |---|---|
93
+ | `swap(...)` | Phase one of the two-transaction private swap: locks the input record against a blinded identity. Returns a `SwapHandle` — persist it if the process might die before the claim. |
94
+ | `claim_swap_output(handle)` | Phase two: claims the finalized output as a private record. |
95
+ | `create_pool(...)` | Initializes a pool for a token pair + fee tier. |
96
+ | `mint(...)` | Opens a position in a tick range; returns a `MintResult` with the position's token id. |
97
+ | `increase_liquidity(...)` / `decrease_liquidity(...)` | Resizes a position (spends the position NFT record and returns a fresh one). |
98
+ | `collect(...)` | Pays out `tokens_owed` as private records. |
99
+ | `burn(...)` | Closes an emptied position and removes it from the `positions` mapping. |
100
+
101
+ Quote before you swap: pass `expected_out` from `dex.api.get_route(...)` —
102
+ without it a spot estimate is used, which ignores fees and price impact.
103
+ Amounts are `u128` base units of the token; fees are microcredits.
104
+
105
+ **DEX API** (`dex.api`, standalone as `ApiClient`): `get_pools`,
106
+ `get_tokens`, `get_route`, `get_swap`, `get_ohlcv`, `get_public_balances`.
107
+ Route quoting, OHLCV, and balances are auth-gated — call
108
+ `api.authenticate(address, sign)` once (challenge/verify by signature, no
109
+ funds required); some deployments additionally gate them behind an invite
110
+ code.
111
+
112
+ ## Privacy
113
+
114
+ The swap flow never puts your address on chain next to the output. `swap`
115
+ derives a single-use **blinded identity** from the signer's view key
116
+ (`derive_blinding_factor` / `derive_blinded_address` are exported for
117
+ verification against the TS SDK's vectors); the claim proves knowledge of the
118
+ blinding factor instead of revealing the owner.
119
+
120
+ Two conveniences trade secret material for service:
121
+
122
+ - `delegate` sends the transaction *authorization* to the proving service —
123
+ it can see the transaction's contents (not your private key). Prove
124
+ locally with `transact` if that is unacceptable.
125
+ - The hosted record scanner behind `get_private_balances` /
126
+ `aleo.records.register` shares the account's **view key** with the
127
+ scanning service, which can then see everything the account owns.
128
+ Self-host the scanner if that is unacceptable.
129
+
130
+ ## Async
131
+
132
+ `AsyncShieldSwap` / `AsyncApiClient` mirror the sync surface verb-for-verb on
133
+ `aleo.AsyncAleo` (install the `[async]` extra):
134
+
135
+ ```python
136
+ from aleo_shield_swap import AsyncShieldSwap
137
+
138
+ dex = AsyncShieldSwap(async_aleo)
139
+ handle = await (await dex.swap(pool_key=key, token_in_id=token,
140
+ amount_in=10**9)).delegate()
141
+ ```
142
+
143
+ ## Agent tools and MCP
144
+
145
+ `shield_swap_tools()` returns JSON-schema tool definitions for the whole verb
146
+ surface; `dispatch_tool(dex, name, args)` executes one. For MCP hosts, the
147
+ `[mcp]` extra ships a stdio server over the same definitions:
148
+
149
+ ```bash
150
+ ALEO_PRIVATE_KEY=APrivateKey1... python -m aleo_shield_swap.mcp
151
+ ```
152
+
153
+ Omit `ALEO_PRIVATE_KEY` for a read-only server. See the module docstring for
154
+ the full environment (`ALEO_ENDPOINT`, `ALEO_NETWORK`, DPS credentials).
155
+
156
+ ## Generated bindings
157
+
158
+ The contract surface is pinned, not hand-written: `codegen/` holds the
159
+ deployed program source, its ABI, and the DEX API's OpenAPI document.
160
+ `_generated.py` (program structs + entrypoints via `aleo.codegen`) and
161
+ `_api_models.py` (API response models) are built from those pins. When the
162
+ deployed contract or API drifts, rerun `codegen/regen-abi.sh` /
163
+ `codegen/regen-openapi.sh` and reconcile.
164
+
165
+ ## Tests
166
+
167
+ ```bash
168
+ python -m pytest # hermetic tier — no network
169
+ python -m pytest -m live # read-only against the REAL testnet + DEX API
170
+ python -m pytest -m "live and slow" # spends real testnet funds (DPS proving)
171
+ ALEO_DEVNODE_UNPROVEN=1 \
172
+ python -m pytest -m devnode # full AMM lifecycle on a local aleo-devnode
173
+ ```
174
+
175
+ The devnode tier deploys the vendored `shield_swap_v3.aleo` stack and drives
176
+ pool creation, liquidity, swaps, and burn end-to-end, hermetically. It needs
177
+ the `aleo-devnode` binary (`ALEO_DEVNODE_BIN` or on `PATH`) and skips
178
+ otherwise. Deployments are proofless (dummy verifying keys — the devnode
179
+ skips certificate verification); `ALEO_DEVNODE_UNPROVEN=1` extends that to
180
+ executions and is the fast path (~5 minutes). Without it, executions are
181
+ fully proven locally: expect SNARK parameter downloads and key synthesis on
182
+ first use.
@@ -0,0 +1,165 @@
1
+ # shield-swap-sdk
2
+
3
+ Typed Python client for the **shield_swap** AMM on Aleo. Sits on top of the
4
+ Aleo Python SDK's facade (`aleo.Aleo`): signer, record provider, proving
5
+ configuration, and network all come from the client you bind — this package
6
+ adds the DEX verbs, the typed results, and the off-chain DEX API, nothing
7
+ else.
8
+
9
+ ```python
10
+ from aleo import Aleo
11
+ from aleo_shield_swap import ShieldSwap
12
+
13
+ aleo = Aleo(Aleo.HTTPProvider("https://api.provable.com"))
14
+ aleo.default_account = account
15
+ dex = ShieldSwap(aleo)
16
+
17
+ pools = dex.api.get_pools() # requires a live DEX API
18
+ handle = dex.swap(pool_key=pools[0].key,
19
+ token_in_id=pools[0].token0,
20
+ amount_in=10**9).delegate() # broadcasts; spends funds
21
+ out = dex.claim_swap_output(handle).delegate() # broadcasts; spends funds
22
+ ```
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install -e shield-swap-sdk # from the repo root
28
+ pip install -e "shield-swap-sdk[async]" # + AsyncShieldSwap (httpx)
29
+ pip install -e "shield-swap-sdk[mcp]" # + the MCP server
30
+ ```
31
+
32
+ Requires `aleo-sdk>=0.2` (this repo's SDK; imports as `aleo`) and Python 3.10+.
33
+
34
+ ## How calls work
35
+
36
+ Every read returns a value immediately. Every write returns a prepared
37
+ `DexCall` — nothing touches the network until you invoke a terminal verb:
38
+
39
+ ```python
40
+ call = dex.swap(pool_key=key, token_in_id=token, amount_in=10**9)
41
+
42
+ call.simulate() # runs locally; no broadcast, no fee
43
+ call.transact(account) # proves locally, broadcasts, pays the fee
44
+ call.delegate(account) # proves via the delegated proving service
45
+ ```
46
+
47
+ `transact` and `delegate` return the verb's *typed result* (a `SwapHandle`,
48
+ `MintResult`, `ClaimResult`, …) built from the transaction's root-transition
49
+ outputs — not a bare transaction id. Local proving downloads SNARK parameters
50
+ on first use and takes minutes for the larger entrypoints; `delegate` is the
51
+ practical path and requires DPS credentials on the provider
52
+ (`api_key=`, `network_client.consumer_id`).
53
+
54
+ Chain reads and writes live directly on `ShieldSwap`; the off-chain DEX API
55
+ is namespaced under `.api`, so a call site always shows whether a value came
56
+ from the chain or the service.
57
+
58
+ ## The verb surface
59
+
60
+ **Chain reads** (node REST API):
61
+
62
+ | Verb | Returns |
63
+ |---|---|
64
+ | `get_pool(pool_key)` | The pool struct from the `pools` mapping. |
65
+ | `get_slot(pool_key)` | `SlotView` — current tick, sqrt price, liquidity, spacing. |
66
+ | `get_swap_output(swap_id)` | The finalized swap outcome; raises `SwapOutputNotFinalizedError` until the finalize lands (and again after the claim consumes it). |
67
+ | `is_pool_initialized(pool_key)` | Whether the pool exists on chain. |
68
+ | `get_private_balances(programs)` | Summed unspent record amounts per token program (needs a registered record provider). |
69
+ | `get_balances()` | Public + private balances in one shape. |
70
+ | `derive_pool_key(token0, token1, fee)` / `derive_tick_key(pool_key, tick)` | Mapping keys derived locally — no network. |
71
+
72
+ **Writes** (each returns a `DexCall`):
73
+
74
+ | Verb | What it does |
75
+ |---|---|
76
+ | `swap(...)` | Phase one of the two-transaction private swap: locks the input record against a blinded identity. Returns a `SwapHandle` — persist it if the process might die before the claim. |
77
+ | `claim_swap_output(handle)` | Phase two: claims the finalized output as a private record. |
78
+ | `create_pool(...)` | Initializes a pool for a token pair + fee tier. |
79
+ | `mint(...)` | Opens a position in a tick range; returns a `MintResult` with the position's token id. |
80
+ | `increase_liquidity(...)` / `decrease_liquidity(...)` | Resizes a position (spends the position NFT record and returns a fresh one). |
81
+ | `collect(...)` | Pays out `tokens_owed` as private records. |
82
+ | `burn(...)` | Closes an emptied position and removes it from the `positions` mapping. |
83
+
84
+ Quote before you swap: pass `expected_out` from `dex.api.get_route(...)` —
85
+ without it a spot estimate is used, which ignores fees and price impact.
86
+ Amounts are `u128` base units of the token; fees are microcredits.
87
+
88
+ **DEX API** (`dex.api`, standalone as `ApiClient`): `get_pools`,
89
+ `get_tokens`, `get_route`, `get_swap`, `get_ohlcv`, `get_public_balances`.
90
+ Route quoting, OHLCV, and balances are auth-gated — call
91
+ `api.authenticate(address, sign)` once (challenge/verify by signature, no
92
+ funds required); some deployments additionally gate them behind an invite
93
+ code.
94
+
95
+ ## Privacy
96
+
97
+ The swap flow never puts your address on chain next to the output. `swap`
98
+ derives a single-use **blinded identity** from the signer's view key
99
+ (`derive_blinding_factor` / `derive_blinded_address` are exported for
100
+ verification against the TS SDK's vectors); the claim proves knowledge of the
101
+ blinding factor instead of revealing the owner.
102
+
103
+ Two conveniences trade secret material for service:
104
+
105
+ - `delegate` sends the transaction *authorization* to the proving service —
106
+ it can see the transaction's contents (not your private key). Prove
107
+ locally with `transact` if that is unacceptable.
108
+ - The hosted record scanner behind `get_private_balances` /
109
+ `aleo.records.register` shares the account's **view key** with the
110
+ scanning service, which can then see everything the account owns.
111
+ Self-host the scanner if that is unacceptable.
112
+
113
+ ## Async
114
+
115
+ `AsyncShieldSwap` / `AsyncApiClient` mirror the sync surface verb-for-verb on
116
+ `aleo.AsyncAleo` (install the `[async]` extra):
117
+
118
+ ```python
119
+ from aleo_shield_swap import AsyncShieldSwap
120
+
121
+ dex = AsyncShieldSwap(async_aleo)
122
+ handle = await (await dex.swap(pool_key=key, token_in_id=token,
123
+ amount_in=10**9)).delegate()
124
+ ```
125
+
126
+ ## Agent tools and MCP
127
+
128
+ `shield_swap_tools()` returns JSON-schema tool definitions for the whole verb
129
+ surface; `dispatch_tool(dex, name, args)` executes one. For MCP hosts, the
130
+ `[mcp]` extra ships a stdio server over the same definitions:
131
+
132
+ ```bash
133
+ ALEO_PRIVATE_KEY=APrivateKey1... python -m aleo_shield_swap.mcp
134
+ ```
135
+
136
+ Omit `ALEO_PRIVATE_KEY` for a read-only server. See the module docstring for
137
+ the full environment (`ALEO_ENDPOINT`, `ALEO_NETWORK`, DPS credentials).
138
+
139
+ ## Generated bindings
140
+
141
+ The contract surface is pinned, not hand-written: `codegen/` holds the
142
+ deployed program source, its ABI, and the DEX API's OpenAPI document.
143
+ `_generated.py` (program structs + entrypoints via `aleo.codegen`) and
144
+ `_api_models.py` (API response models) are built from those pins. When the
145
+ deployed contract or API drifts, rerun `codegen/regen-abi.sh` /
146
+ `codegen/regen-openapi.sh` and reconcile.
147
+
148
+ ## Tests
149
+
150
+ ```bash
151
+ python -m pytest # hermetic tier — no network
152
+ python -m pytest -m live # read-only against the REAL testnet + DEX API
153
+ python -m pytest -m "live and slow" # spends real testnet funds (DPS proving)
154
+ ALEO_DEVNODE_UNPROVEN=1 \
155
+ python -m pytest -m devnode # full AMM lifecycle on a local aleo-devnode
156
+ ```
157
+
158
+ The devnode tier deploys the vendored `shield_swap_v3.aleo` stack and drives
159
+ pool creation, liquidity, swaps, and burn end-to-end, hermetically. It needs
160
+ the `aleo-devnode` binary (`ALEO_DEVNODE_BIN` or on `PATH`) and skips
161
+ otherwise. Deployments are proofless (dummy verifying keys — the devnode
162
+ skips certificate verification); `ALEO_DEVNODE_UNPROVEN=1` extends that to
163
+ executions and is the fast path (~5 minutes). Without it, executions are
164
+ fully proven locally: expect SNARK parameter downloads and key synthesis on
165
+ first use.