shielded-transfers 0.1.1__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 (27) hide show
  1. shielded_transfers-0.1.1/LICENSE +7 -0
  2. shielded_transfers-0.1.1/PKG-INFO +530 -0
  3. shielded_transfers-0.1.1/README.md +497 -0
  4. shielded_transfers-0.1.1/pyproject.toml +56 -0
  5. shielded_transfers-0.1.1/setup.cfg +4 -0
  6. shielded_transfers-0.1.1/setup.py +5 -0
  7. shielded_transfers-0.1.1/shielded_transfers/__init__.py +42 -0
  8. shielded_transfers-0.1.1/shielded_transfers/cli.py +89 -0
  9. shielded_transfers-0.1.1/shielded_transfers/client.py +611 -0
  10. shielded_transfers-0.1.1/shielded_transfers/commitment.py +59 -0
  11. shielded_transfers-0.1.1/shielded_transfers/constants.py +26 -0
  12. shielded_transfers-0.1.1/shielded_transfers/exceptions.py +36 -0
  13. shielded_transfers-0.1.1/shielded_transfers/networks.py +45 -0
  14. shielded_transfers-0.1.1/shielded_transfers/poseidon_circomlibjs.py +30 -0
  15. shielded_transfers-0.1.1/shielded_transfers/poseidon_helper.js +26 -0
  16. shielded_transfers-0.1.1/shielded_transfers/poseidon_helper.mjs +40 -0
  17. shielded_transfers-0.1.1/shielded_transfers/poseidon_onchain.py +51 -0
  18. shielded_transfers-0.1.1/shielded_transfers/poseidon_polkadot.py +33 -0
  19. shielded_transfers-0.1.1/shielded_transfers/poseidon_precompile.py +81 -0
  20. shielded_transfers-0.1.1/shielded_transfers/revive.py +506 -0
  21. shielded_transfers-0.1.1/shielded_transfers/tree.py +164 -0
  22. shielded_transfers-0.1.1/shielded_transfers.egg-info/PKG-INFO +530 -0
  23. shielded_transfers-0.1.1/shielded_transfers.egg-info/SOURCES.txt +25 -0
  24. shielded_transfers-0.1.1/shielded_transfers.egg-info/dependency_links.txt +1 -0
  25. shielded_transfers-0.1.1/shielded_transfers.egg-info/entry_points.txt +3 -0
  26. shielded_transfers-0.1.1/shielded_transfers.egg-info/requires.txt +11 -0
  27. shielded_transfers-0.1.1/shielded_transfers.egg-info/top_level.txt +1 -0
@@ -0,0 +1,7 @@
1
+ Copyright 2025 Kusama Shield Developers on behalf of the Kusama DAO
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,530 @@
1
+ Metadata-Version: 2.4
2
+ Name: shielded-transfers
3
+ Version: 0.1.1
4
+ Summary: Python SDK for the Kusama Shield v7 privacy pool (ZK deposits & withdrawals) on Polkadot AssetHub
5
+ Author: Kusama Shield
6
+ License: MIT
7
+ Project-URL: Homepage, https://shield.markets
8
+ Project-URL: Documentation, https://kusamashield.codeberg.page
9
+ Keywords: shield,privacy,zeroknowledge,zk,polkadot,assethub,shielded,tornado
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Security :: Cryptography
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: web3>=6.0.0
23
+ Requires-Dist: eth-account>=0.9.0
24
+ Requires-Dist: eth-abi>=4.0.0
25
+ Requires-Dist: eth-utils>=2.0.0
26
+ Requires-Dist: light-poseidon-python>=0.1.3
27
+ Requires-Dist: substrateinterface>=1.0.0
28
+ Requires-Dist: requests>=2.28.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: build; extra == "dev"
31
+ Requires-Dist: twine; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # Shielded Transfers Python SDK
35
+
36
+ A Python library for interacting with the Kusama Shield v7 privacy pool on Polkadot AssetHub (Paseo).
37
+
38
+ ## Overview
39
+
40
+ This SDK enables shielded (privacy-preserving) deposits and withdrawals using zero-knowledge proofs. It implements the same cryptographic primitives and Merkle tree logic as the Solidity contracts.
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ cd /home/pi/zk/shielded-transfers-python
46
+ pip install -e .
47
+ ```
48
+
49
+ Or install dependencies only:
50
+ ```bash
51
+ pip install web3>=6.0.0 eth-account>=0.9.0 eth-abi>=4.0.0 eth-utils>=2.0.0 requests
52
+ ```
53
+
54
+ ## Requirements
55
+
56
+ - **Node.js** - Required for Poseidon hashing (poseidon-lite)
57
+ - **snarkjs** - For ZK proof generation (`npm install -g snarkjs`)
58
+ - **Circuit files** - Located at `/home/pi/zk/shielded-transfers/public/` (override via
59
+ the `SHIELDED_CIRCUIT_DIR` env var or the `circuit_dir` constructor arg):
60
+ - `withdraw_phase2_fixed_v7.wasm`
61
+ - `withdraw_phase2_fixed_v7_0001.zkey`
62
+ - **substrateinterface** *(optional, only for `ReviveShieldedClient`)* — requires
63
+ the **modern** API (`Keypair.create_from_uri`, `SubstrateInterface.compose_call`).
64
+ ⚠️ PyPI's `substrateinterface 1.0.0` is too old; use the newer git checkout
65
+ (github.com/polkascan/py-substrate-interface). Imported lazily — the ETH-only
66
+ `ShieldedClient` works without it, and `ReviveShieldedClient` raises a clear
67
+ error if a compatible version isn't installed.
68
+
69
+ ## Two transaction styles
70
+
71
+ The SDK can submit shielded deposits/withdrawals two ways:
72
+
73
+ | Style | Class | Account type | Tx mechanism |
74
+ |-------|-------|--------------|--------------|
75
+ | **ETH** | `ShieldedClient` | ECDSA (`0x...` privkey) | `eth_sendRawTransaction` |
76
+ | **Polkadot / Substrate** | `ReviveShieldedClient` | sr25519 (seed / mnemonic) | `revive.call` extrinsic |
77
+
78
+ Both share the same EVM-based Merkle tree building (`build_tree` / `eth_getLogs`,
79
+ or fetch from the Kusama Shield Flask proxy via `/tree-leaves`). Only the
80
+ transaction submission differs.
81
+
82
+ ## Quick Start
83
+
84
+ ### Polkadot AssetHub (Mainnet)
85
+
86
+ ```python
87
+ from shielded_transfers import ShieldedClient, POLKADOT_ASSET_HUB
88
+ import json
89
+
90
+ client = ShieldedClient(
91
+ rpc_url=POLKADOT_ASSET_HUB["rpc"],
92
+ pool_address=POLKADOT_ASSET_HUB["pool"],
93
+ private_key="0x_your_private_key",
94
+ deployment_block=POLKADOT_ASSET_HUB["deployment_block"],
95
+ native_token="DOT",
96
+ )
97
+
98
+ # Check balances
99
+ wallet_bal, _ = client.get_balance()
100
+ pool_bal, _ = client.get_pool_balance()
101
+ print(f"Wallet: {wallet_bal} wei, Pool: {pool_bal} wei")
102
+
103
+ # Deposit 1 DOT
104
+ note = client.deposit(1 * 10**18)
105
+
106
+ with open("deposit_note.json", "w") as f:
107
+ json.dump(note, f)
108
+
109
+ # ... later ...
110
+
111
+ with open("deposit_note.json") as f:
112
+ note = json.load(f)
113
+
114
+ tx_hash = client.withdraw(note)
115
+ print(f"Withdraw TX: {tx_hash}")
116
+ ```
117
+
118
+ ### Paseo AssetHub (Testnet)
119
+
120
+ ```python
121
+ from shielded_transfers import ShieldedClient, PASEO_ASSET_HUB
122
+ import json
123
+
124
+ client = ShieldedClient(
125
+ rpc_url=PASEO_ASSET_HUB["rpc"],
126
+ pool_address=PASEO_ASSET_HUB["pool"],
127
+ private_key="0x_your_private_key",
128
+ deployment_block=PASEO_ASSET_HUB["deployment_block"],
129
+ native_token="DOT",
130
+ )
131
+
132
+ # Deposit 10 PAS (testnet)
133
+ note = client.deposit(10 * 10**18)
134
+
135
+ with open("deposit_note.json", "w") as f:
136
+ json.dump(note, f)
137
+
138
+ # Withdraw
139
+ with open("deposit_note.json") as f:
140
+ note = json.load(f)
141
+
142
+ tx_hash = client.withdraw(note, recipient="0x_recipient_address")
143
+ print(f"Withdraw TX: {tx_hash}")
144
+ ```
145
+
146
+ ### Polkadot / Substrate (revive.call) — `ReviveShieldedClient`
147
+
148
+ For sr25519 (Substrate) accounts — e.g. a polkadot.js browser wallet or Nova
149
+ account that cannot sign Ethereum transactions directly.
150
+
151
+ ```python
152
+ from shielded_transfers import ReviveShieldedClient, POLKADOT_ASSET_HUB
153
+
154
+ client = ReviveShieldedClient(
155
+ rpc_url=POLKADOT_ASSET_HUB["rpc"], # EVM JSON-RPC (reads/tree)
156
+ ws_url="wss://asset-hub-polkadot-rpc.n.dwellir.com", # Substrate WS (revive)
157
+ pool_address=POLKADOT_ASSET_HUB["pool"],
158
+ substrate_uri="0x_your_sr25519_seed", # or a mnemonic / "//Alice"
159
+ deployment_block=POLKADOT_ASSET_HUB["deployment_block"],
160
+ native_token="DOT",
161
+ native_decimals=10, # DOT = 10, Paseo PAS = 12
162
+ )
163
+
164
+ # One-time mapping of the sr25519 account to its H160 (only needed once)
165
+ client.ensure_mapped()
166
+
167
+ # Deposit 0.01 DOT via revive.call
168
+ note = client.deposit_revive(amount_dot=0.01)
169
+ print("Deposit TX:", note["tx_hash"])
170
+ print("Secret: ", note["secret"]) # keep private for withdrawal
171
+
172
+ # Withdraw back via revive.call, fetching the current tree from the Flask proxy
173
+ tx_hash = client.withdraw_revive(
174
+ note,
175
+ recipient=client.h160,
176
+ proxy_base_url="https://proxyswap.laissez-faire.trade",
177
+ )
178
+ print("Withdraw TX:", tx_hash)
179
+ ```
180
+
181
+ **Important notes for `revive.call`:**
182
+ - The `value` is in **native plancks**, not wei (`1 DOT = 1e10`, Paseo `1 PAS = 1e12`).
183
+ - Requires a large weight limit (handled internally) and a non-zero storage
184
+ deposit (~0.1 native units) — the SDK sets these automatically.
185
+ - `revive.call` EVM logs are **not** indexed by `eth_getLogs` on AssetHub, so
186
+ the tree is best fetched from the Kusama Shield Flask proxy
187
+ (`proxy_base_url=...` → `GET /tree-leaves/<network>`).
188
+
189
+ ## Configuration
190
+
191
+ ### Constructor Parameters
192
+
193
+ | Parameter | Type | Required | Description |
194
+ |-----------|------|----------|-------------|
195
+ | `rpc_url` | str | Yes | RPC endpoint URL |
196
+ | `pool_address` | str | Yes | Shielded pool contract address |
197
+ | `private_key` | str | Yes | Account private key |
198
+ | `deployment_block` | int | Yes | Block number when pool was deployed |
199
+ | `circuit_dir` | Path | No | Directory containing circuit files |
200
+ | `poseidon_helper` | Path | No | Path to poseidon_helper.mjs |
201
+
202
+ ### Active Deployments
203
+
204
+ ```python
205
+ # Polkadot AssetHub (Mainnet)
206
+ POLKADOT_ASSET_HUB = {
207
+ "rpc": "https://polkadot-assethub-rpc.laissez-faire.trade",
208
+ "pool": "0x0D694Da746e73D1e255c1894F90e38170db45809",
209
+ "verifier": "0x6A13781E43AEA21918120CD0E7a2ed8614c01e14",
210
+ "poseidon": "0xB8F0C6679D6Cc56450470522Bd96573C3D615052",
211
+ "deployment_block": 18697500,
212
+ "chain_id": 420420419,
213
+ }
214
+
215
+ # Paseo AssetHub (Testnet)
216
+ PASEO_ASSET_HUB = {
217
+ "rpc": "https://paseo-assethub-rpc.laissez-faire.trade",
218
+ "pool": "0xbcE09D4De052b2816df1285663ac89528DF45380",
219
+ "verifier": "0xcA4cBc5d31eccd08d393C43aF492F729FF30b685",
220
+ "poseidon": "0x1d165f6fE5A30422E0E2140e91C8A9B800380637",
221
+ "deployment_block": 11273491,
222
+ "chain_id": 420420421,
223
+ }
224
+ ```
225
+
226
+ ## API Reference
227
+
228
+ ### ShieldedClient
229
+
230
+ ```python
231
+ client = ShieldedClient(rpc_url, pool_address, private_key, deployment_block)
232
+ ```
233
+
234
+ #### Properties
235
+
236
+ - `client.address` - Account address
237
+ - `client.chain_id` - Chain ID
238
+ - `client.pool_address` - Pool contract address
239
+
240
+ #### Methods
241
+
242
+ ##### get_balance()
243
+
244
+ ```python
245
+ wei, formatted = client.get_balance()
246
+ ```
247
+ Returns wallet balance in wei and formatted string.
248
+
249
+ ##### get_pool_balance()
250
+
251
+ ```python
252
+ wei, formatted = client.get_pool_balance()
253
+ ```
254
+ Returns pool balance in wei and formatted string.
255
+
256
+ ##### get_tree_size()
257
+
258
+ ```python
259
+ size = client.get_tree_size()
260
+ ```
261
+ Returns the current Merkle tree size from the contract.
262
+
263
+ ##### get_root()
264
+
265
+ ```python
266
+ root = client.get_root()
267
+ ```
268
+ Returns the current Merkle tree root.
269
+
270
+ ##### is_known_root(root)
271
+
272
+ ```python
273
+ known = client.is_known_root(root)
274
+ ```
275
+ Checks if a root is in the 16-slot known-roots window.
276
+
277
+ ##### deposit(amount_wei, asset_id=0)
278
+
279
+ ```python
280
+ note = client.deposit(amount_wei, asset_id=0)
281
+ ```
282
+ Creates a shielded deposit.
283
+
284
+ **Parameters:**
285
+ - `amount_wei` (int): Amount in wei
286
+ - `asset_id` (int): Asset ID (0 for native PAS)
287
+
288
+ **Returns:**
289
+ ```python
290
+ {
291
+ "secret": "0x...", # Secret key (keep private!)
292
+ "nullifier": 123..., # Nullifier for proving
293
+ "nullifier_hash": 456..., # Hash for double-spend prevention
294
+ "commitment": 789..., # Public commitment
295
+ "amount_wei": 10000000000000000000,
296
+ "asset_id": 0,
297
+ "tx_hash": "0x...",
298
+ "block_number": 11000000,
299
+ "deposit_block": 11085793,
300
+ }
301
+ ```
302
+
303
+ ##### build_tree(start_block=None)
304
+
305
+ ```python
306
+ tree = client.build_tree(start_block=11085793)
307
+ ```
308
+ Builds the Merkle tree from on-chain events.
309
+
310
+ **Returns:** `LeanIMT` instance
311
+
312
+ ##### withdraw(note, recipient=None)
313
+
314
+ ```python
315
+ tx_hash = client.withdraw(note, recipient=None)
316
+ ```
317
+
318
+ **Parameters:**
319
+ - `note` (dict): Deposit note from `deposit()`
320
+ - `recipient` (str): Recipient address (default: self)
321
+
322
+ **Returns:** Transaction hash
323
+
324
+ ### ReviveShieldedClient
325
+
326
+ ```python
327
+ client = ReviveShieldedClient(rpc_url, ws_url, pool_address, substrate_uri, deployment_block)
328
+ ```
329
+
330
+ Subclasses `ShieldedClient` and adds Substrate (`revive.call`) tx submission.
331
+ Additional constructor parameters:
332
+
333
+ | Parameter | Type | Required | Description |
334
+ |-----------|------|----------|-------------|
335
+ | `ws_url` | str | Yes | Substrate WebSocket RPC URL |
336
+ | `substrate_uri` | str | Yes | sr25519 seed (`0x...`), mnemonic, or SURI (`//Alice`) |
337
+ | `native_decimals` | int | No | Native token decimals (DOT=10, Paseo=12). Defaults to a heuristic |
338
+ | `ss58_format` | int | No | SS58 format (default 42 for AssetHub) |
339
+
340
+ #### Properties
341
+
342
+ - `client.ss58_address` - Substrate (SS58) address
343
+ - `client.h160` - Derived EVM H160 address
344
+ - `client.keypair` - substrateinterface Keypair
345
+
346
+ #### Methods
347
+
348
+ ##### is_mapped()
349
+
350
+ ```python
351
+ mapped = client.is_mapped()
352
+ ```
353
+ Returns whether the sr25519 account is mapped to its H160 for revive.
354
+
355
+ ##### ensure_mapped()
356
+
357
+ ```python
358
+ client.ensure_mapped() # or tx_hash = client.ensure_mapped()
359
+ ```
360
+ Maps the account (one-time, via `revive.mapAccount`) if not already mapped.
361
+
362
+ ##### deposit_revive(amount_dot, wait_for_inclusion=True)
363
+
364
+ ```python
365
+ note = client.deposit_revive(0.01)
366
+ ```
367
+ Deposits native tokens via `revive.call`. `amount_dot` is in native units.
368
+
369
+ ##### withdraw_revive(note, recipient, wait_for_inclusion=True, proxy_base_url=None)
370
+
371
+ ```python
372
+ tx_hash = client.withdraw_revive(note, recipient=client.h160,
373
+ proxy_base_url="https://proxyswap.laissez-faire.trade")
374
+ ```
375
+ Withdraws via `revive.call`. `recipient` is an EVM H160. If `proxy_base_url` is
376
+ set, the Merkle tree is fetched from the proxy's `/tree-leaves/<network>`;
377
+ otherwise it is built locally via `eth_getLogs` (`build_tree`).
378
+
379
+ ##### fetch_tree_from_proxy(base_url="https://proxyswap.laissez-faire.trade", network=None)
380
+
381
+ ```python
382
+ tree = client.fetch_tree_from_proxy(network="polkadot")
383
+ ```
384
+ Fetches the current tree leaves from the Kusama Shield Flask proxy and returns
385
+ a `LeanIMT`.
386
+
387
+ ### Commitment Generation
388
+
389
+ ```python
390
+ from shielded_transfers import generate_commitment
391
+
392
+ note = generate_commitment(secret_hex, amount_wei, asset_id)
393
+ ```
394
+
395
+ ### LeanIMT
396
+
397
+ ```python
398
+ from shielded_transfers import LeanIMT
399
+
400
+ tree = LeanIMT()
401
+ tree.insert(leaf)
402
+ tree.get_proof(leaf_index)
403
+ tree.find_leaf_index(leaf)
404
+ tree.root
405
+ tree.size
406
+ ```
407
+
408
+ ## CLI Usage
409
+
410
+ ### Deposit
411
+
412
+ ```bash
413
+ shielded-deposit \
414
+ --amount 10 \
415
+ --rpc-url https://paseo-assethub-rpc.laissez-faire.trade \
416
+ --private-key 0x... \
417
+ --output deposit_note.json
418
+ ```
419
+
420
+ ### Withdraw
421
+
422
+ ```bash
423
+ shielded-withdraw \
424
+ --note deposit_note.json \
425
+ --rpc-url https://paseo-assethub-rpc.laissez-faire.trade \
426
+ --private-key 0x... \
427
+ --recipient 0x...
428
+ ```
429
+
430
+ ## Environment Variables
431
+
432
+ ```bash
433
+ export PASEO_RPC_URL="https://paseo-assethub-rpc.laissez-faire.trade"
434
+ export PRIVATE_KEY="0x_your_private_key"
435
+ ```
436
+
437
+ ## Architecture
438
+
439
+ ```
440
+ shielded_transfers/
441
+ ├── __init__.py # Package exports
442
+ ├── client.py # ShieldedClient (ETH / eth_sendRawTransaction)
443
+ ├── revive.py # ReviveShieldedClient (Substrate / revive.call)
444
+ ├── commitment.py # Commitment generation (Poseidon)
445
+ ├── tree.py # LeanIMT Merkle tree implementation
446
+ ├── constants.py # Selectors, BN254 parameters
447
+ ├── exceptions.py # Custom exceptions
448
+ ├── cli.py # Command-line interface
449
+ └── poseidon_helper.mjs # Node.js Poseidon hash helper
450
+ ```
451
+
452
+ ### Key Components
453
+
454
+ 1. **Commitment Generation**: Uses Poseidon hash (via Node.js) to compute:
455
+ - `nullifier = poseidon2(secret, 1)`
456
+ - `nullifier_hash = poseidon1(nullifier)`
457
+ - `precommitment = poseidon2(nullifier, secret)`
458
+ - `value_asset_hash = poseidon2(amount, asset_id)`
459
+ - `commitment = poseidon2(value_asset_hash, precommitment)`
460
+
461
+ 2. **Merkle Tree**: LeanIMT with 128 levels, matching the Solidity contract
462
+
463
+ 3. **ZK Proof**: Generated using snarkjs with the v7 circuit
464
+
465
+ ## Known Issues
466
+
467
+ 1. **Withdraw event scanning**: The tree building from events may occasionally miss deposits due to RPC event indexing. The script includes recovery logic to handle this.
468
+
469
+ 2. **Gas estimation**: Some RPCs may fail gas estimation. The SDK uses a default of 500k gas as fallback.
470
+
471
+ 3. **Poseidon dependency**: Requires Node.js process to be running for Poseidon hashing.
472
+
473
+ ## Error Handling
474
+
475
+ ```python
476
+ from shielded_transfers import (
477
+ ShieldedTransfersError,
478
+ DepositError,
479
+ WithdrawError,
480
+ ProofError,
481
+ )
482
+
483
+ try:
484
+ note = client.deposit(amount)
485
+ except DepositError as e:
486
+ print(f"Deposit failed: {e}")
487
+ except ProofError as e:
488
+ print(f"ZK proof failed: {e}")
489
+ ```
490
+
491
+ ## Testing
492
+
493
+ ```bash
494
+ # Run the original roundtrip script
495
+ cd /home/pi/zk/rust_tx_gen
496
+ python3 paseo_v7_roundtrip.py --deposit-only
497
+ python3 paseo_v7_roundtrip.py --withdraw deposit_note_*.json
498
+
499
+ # Or use the library
500
+ cd /home/pi/zk/shielded-transfers-python
501
+ python3 -c "
502
+ from shielded_transfers import ShieldedClient, PASEO_ASSET_HUB
503
+ client = ShieldedClient(
504
+ rpc_url=PASEO_ASSET_HUB['rpc'],
505
+ pool_address=PASEO_ASSET_HUB['pool'],
506
+ private_key='0x...',
507
+ deployment_block=PASEO_ASSET_HUB['deployment_block'],
508
+ )
509
+ note = client.deposit(10**18)
510
+ print(f'Deposit: {note[\"tx_hash\"]}')
511
+ "
512
+ ```
513
+
514
+ ## Files
515
+
516
+ | File | Description |
517
+ |------|-------------|
518
+ | `client.py` | Main SDK client with ETH deposit/withdraw (`eth_sendRawTransaction`) |
519
+ | `revive.py` | Substrate `revive.call` deposit/withdraw (`ReviveShieldedClient`) |
520
+ | `commitment.py` | Commitment and Poseidon hashing |
521
+ | `tree.py` | LeanIMT Merkle tree |
522
+ | `constants.py` | Contract addresses, selectors |
523
+ | `exceptions.py` | Custom exception classes |
524
+ | `cli.py` | Command-line tools |
525
+
526
+ ## Related
527
+
528
+ - [Original roundtrip script](../rust_tx_gen/paseo_v7_roundtrip.py)
529
+ - [Deployment notes](../rust_tx_gen/DEPLOYMENT_NOTES.md)
530
+ - [Circuit files](../shielded-transfers/public/)