synapse-filecoin-sdk 0.1.1__tar.gz → 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 (84) hide show
  1. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/PKG-INFO +29 -3
  2. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/README.md +26 -2
  3. synapse_filecoin_sdk-0.2.0/full_demo.py +224 -0
  4. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/pyproject.toml +4 -1
  5. synapse_filecoin_sdk-0.2.0/src/pynapse/_version.py +1 -0
  6. synapse_filecoin_sdk-0.2.0/src/pynapse/integrations/__init__.py +4 -0
  7. synapse_filecoin_sdk-0.2.0/src/pynapse/integrations/langchain.py +214 -0
  8. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/uv.lock +552 -5
  9. synapse_filecoin_sdk-0.1.1/src/pynapse/_version.py +0 -1
  10. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/.github/workflows/publish-pypi.yml +0 -0
  11. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/.gitignore +0 -0
  12. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/LICENSE.md +0 -0
  13. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/QUICKSTART.md +0 -0
  14. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/demo.py +0 -0
  15. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/get_usdfc.py +0 -0
  16. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/__init__.py +0 -0
  17. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/__init__.py +0 -0
  18. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/abi_registry.py +0 -0
  19. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/addresses.json +0 -0
  20. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/erc20_abi.json +0 -0
  21. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/errorsAbi.json +0 -0
  22. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/filecoinPayV1Abi.json +0 -0
  23. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/filecoinWarmStorageServiceAbi.json +0 -0
  24. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/filecoinWarmStorageServiceStateViewAbi.json +0 -0
  25. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/generated.py +0 -0
  26. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/payments_abi.json +0 -0
  27. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/pdpVerifierAbi.json +0 -0
  28. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/providerIdSetAbi.json +0 -0
  29. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/serviceProviderRegistryAbi.json +0 -0
  30. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/sessionKeyRegistryAbi.json +0 -0
  31. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/__init__.py +0 -0
  32. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/abis.py +0 -0
  33. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/chains.py +0 -0
  34. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/constants.py +0 -0
  35. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/errors.py +0 -0
  36. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/piece.py +0 -0
  37. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/rand.py +0 -0
  38. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/typed_data.py +0 -0
  39. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/utils.py +0 -0
  40. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/evm/__init__.py +0 -0
  41. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/evm/client.py +0 -0
  42. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/filbeam/__init__.py +0 -0
  43. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/filbeam/service.py +0 -0
  44. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/payments/__init__.py +0 -0
  45. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/payments/service.py +0 -0
  46. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/__init__.py +0 -0
  47. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/server.py +0 -0
  48. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/types.py +0 -0
  49. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/verifier.py +0 -0
  50. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/retriever/__init__.py +0 -0
  51. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/retriever/async_chain.py +0 -0
  52. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/retriever/chain.py +0 -0
  53. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/__init__.py +0 -0
  54. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/key.py +0 -0
  55. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/permissions.py +0 -0
  56. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/registry.py +0 -0
  57. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/__init__.py +0 -0
  58. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/capabilities.py +0 -0
  59. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/pdp_capabilities.py +0 -0
  60. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/service.py +0 -0
  61. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/types.py +0 -0
  62. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/__init__.py +0 -0
  63. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/async_context.py +0 -0
  64. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/async_manager.py +0 -0
  65. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/context.py +0 -0
  66. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/manager.py +0 -0
  67. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/synapse.py +0 -0
  68. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/__init__.py +0 -0
  69. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/constants.py +0 -0
  70. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/errors.py +0 -0
  71. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/metadata.py +0 -0
  72. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/piece_url.py +0 -0
  73. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/warm_storage/__init__.py +0 -0
  74. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/src/pynapse/warm_storage/service.py +0 -0
  75. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_async_storage.py +0 -0
  76. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_chains.py +0 -0
  77. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_metadata.py +0 -0
  78. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_pdp_capabilities.py +0 -0
  79. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_piece.py +0 -0
  80. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_piece_url.py +0 -0
  81. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_rand.py +0 -0
  82. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_session_permissions.py +0 -0
  83. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_storage_manager.py +0 -0
  84. {synapse_filecoin_sdk-0.1.1 → synapse_filecoin_sdk-0.2.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synapse-filecoin-sdk
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Python SDK for Filecoin Onchain Cloud (Synapse)
5
5
  Project-URL: Homepage, https://github.com/anjor/pynapse
6
6
  Project-URL: Repository, https://github.com/anjor/pynapse
@@ -18,6 +18,8 @@ Requires-Python: >=3.11
18
18
  Requires-Dist: httpx<0.28.0,>=0.25.0
19
19
  Requires-Dist: multiformats<0.4.0,>=0.3.1
20
20
  Requires-Dist: web3<7,>=6.0.0
21
+ Provides-Extra: langchain
22
+ Requires-Dist: langchain-core<0.4,>=0.1.0; extra == 'langchain'
21
23
  Provides-Extra: test
22
24
  Requires-Dist: pytest-asyncio<0.24,>=0.23.0; extra == 'test'
23
25
  Requires-Dist: pytest<9,>=8.0.0; extra == 'test'
@@ -49,6 +51,30 @@ Python import: `pynapse`
49
51
  pip install synapse-filecoin-sdk
50
52
  ```
51
53
 
54
+ ## Supported Networks
55
+
56
+ Both **Filecoin Mainnet** and **Calibration testnet** are supported:
57
+
58
+ ```python
59
+ from pynapse import AsyncSynapse
60
+
61
+ # Mainnet
62
+ synapse = await AsyncSynapse.create(
63
+ rpc_url="https://api.node.glif.io/rpc/v1",
64
+ chain="mainnet",
65
+ private_key=PRIVATE_KEY
66
+ )
67
+
68
+ # Calibration testnet (for testing)
69
+ synapse = await AsyncSynapse.create(
70
+ rpc_url="https://api.calibration.node.glif.io/rpc/v1",
71
+ chain="calibration",
72
+ private_key=PRIVATE_KEY
73
+ )
74
+ ```
75
+
76
+ See [QUICKSTART.md](QUICKSTART.md) for a full tutorial using Calibration testnet.
77
+
52
78
  ## CommP / PieceCID
53
79
 
54
80
  `pynapse` uses `stream-commp` from `go-fil-commp-hashhash` for PieceCID calculation.
@@ -88,8 +114,8 @@ Publishing is automated via GitHub Actions in `.github/workflows/publish-pypi.ym
88
114
  3. Tag a release and push the tag:
89
115
 
90
116
  ```bash
91
- git tag v0.1.1
92
- git push origin v0.1.1
117
+ git tag v0.2.0
118
+ git push origin v0.2.0
93
119
  ```
94
120
 
95
121
  The workflow builds the package, runs `twine check`, and publishes to PyPI via OIDC (no API token required).
@@ -24,6 +24,30 @@ Python import: `pynapse`
24
24
  pip install synapse-filecoin-sdk
25
25
  ```
26
26
 
27
+ ## Supported Networks
28
+
29
+ Both **Filecoin Mainnet** and **Calibration testnet** are supported:
30
+
31
+ ```python
32
+ from pynapse import AsyncSynapse
33
+
34
+ # Mainnet
35
+ synapse = await AsyncSynapse.create(
36
+ rpc_url="https://api.node.glif.io/rpc/v1",
37
+ chain="mainnet",
38
+ private_key=PRIVATE_KEY
39
+ )
40
+
41
+ # Calibration testnet (for testing)
42
+ synapse = await AsyncSynapse.create(
43
+ rpc_url="https://api.calibration.node.glif.io/rpc/v1",
44
+ chain="calibration",
45
+ private_key=PRIVATE_KEY
46
+ )
47
+ ```
48
+
49
+ See [QUICKSTART.md](QUICKSTART.md) for a full tutorial using Calibration testnet.
50
+
27
51
  ## CommP / PieceCID
28
52
 
29
53
  `pynapse` uses `stream-commp` from `go-fil-commp-hashhash` for PieceCID calculation.
@@ -63,8 +87,8 @@ Publishing is automated via GitHub Actions in `.github/workflows/publish-pypi.ym
63
87
  3. Tag a release and push the tag:
64
88
 
65
89
  ```bash
66
- git tag v0.1.1
67
- git push origin v0.1.1
90
+ git tag v0.2.0
91
+ git push origin v0.2.0
68
92
  ```
69
93
 
70
94
  The workflow builds the package, runs `twine check`, and publishes to PyPI via OIDC (no API token required).
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ pynapse Full Demo - Complete end-to-end test on Filecoin Calibration
4
+ Handles all setup steps: deposit, service approval, then upload/download
5
+ """
6
+ import asyncio
7
+ import sys
8
+ from web3 import AsyncWeb3
9
+ from eth_account import Account
10
+
11
+ # Demo wallet (Calibration testnet only)
12
+ PRIVATE_KEY = "0x7666381bff20b2f0819c77d3846b1d22637be4996012aaaff25e8b93f73a6985"
13
+ RPC_URL = "https://api.calibration.node.glif.io/rpc/v1"
14
+ CHAIN_ID = 314159
15
+
16
+ # Contract addresses (Calibration)
17
+ USDFC = "0xb3042734b608a1B16e9e86B374A3f3e389B4cDf0"
18
+ PAYMENTS = "0x09a0fDc2723fAd1A7b8e3e00eE5DF73841df55a0" # FilecoinPay (updated by sub-agent)
19
+ FWSS = "0x02925630df557F957f70E112bA06e50965417CA0" # WarmStorage service
20
+
21
+ # ABIs
22
+ ERC20_ABI = [
23
+ {'inputs': [{'name': 'account', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
24
+ {'inputs': [{'name': 'spender', 'type': 'address'}, {'name': 'amount', 'type': 'uint256'}], 'name': 'approve', 'outputs': [{'name': '', 'type': 'bool'}], 'stateMutability': 'nonpayable', 'type': 'function'},
25
+ {'inputs': [{'name': 'owner', 'type': 'address'}, {'name': 'spender', 'type': 'address'}], 'name': 'allowance', 'outputs': [{'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
26
+ ]
27
+
28
+ PAYMENTS_ABI = [
29
+ # accounts(token, account) returns (funds, lockupCurrent, lockupRate, lockupLastSettledAt)
30
+ {'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'account', 'type': 'address'}], 'name': 'accounts', 'outputs': [{'name': 'funds', 'type': 'uint256'}, {'name': 'lockupCurrent', 'type': 'uint256'}, {'name': 'lockupRate', 'type': 'uint256'}, {'name': 'lockupLastSettledAt', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
31
+ {'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'to', 'type': 'address'}, {'name': 'amount', 'type': 'uint256'}], 'name': 'deposit', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'},
32
+ # operatorApprovals(token, client, operator) - note: 3 args not 2
33
+ {'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'client', 'type': 'address'}, {'name': 'operator', 'type': 'address'}], 'name': 'operatorApprovals', 'outputs': [{'name': 'approved', 'type': 'bool'}, {'name': 'rateAllowance', 'type': 'uint256'}, {'name': 'lockupAllowance', 'type': 'uint256'}, {'name': 'rateUsage', 'type': 'uint256'}, {'name': 'lockupUsage', 'type': 'uint256'}, {'name': 'maxLockupPeriod', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
34
+ {'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'operator', 'type': 'address'}, {'name': 'approved', 'type': 'bool'}, {'name': 'rateAllowance', 'type': 'uint256'}, {'name': 'lockupAllowance', 'type': 'uint256'}, {'name': 'maxLockupPeriod', 'type': 'uint256'}], 'name': 'setOperatorApproval', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'},
35
+ ]
36
+
37
+
38
+ async def send_tx(w3, account, tx_data, description):
39
+ """Send a transaction with proper Filecoin gas handling"""
40
+ print(f" → {description}...")
41
+
42
+ nonce = await w3.eth.get_transaction_count(account.address)
43
+
44
+ # Get gas estimate
45
+ try:
46
+ gas = await w3.eth.estimate_gas({**tx_data, 'from': account.address})
47
+ gas = int(gas * 1.2) # 20% buffer
48
+ except Exception as e:
49
+ print(f" Gas estimate failed: {e}")
50
+ gas = 30000000
51
+
52
+ # Build transaction with EIP-1559 style fees for Filecoin
53
+ base_fee = await w3.eth.gas_price
54
+
55
+ tx = {
56
+ **tx_data,
57
+ 'from': account.address,
58
+ 'nonce': nonce,
59
+ 'gas': gas,
60
+ 'maxFeePerGas': base_fee * 2,
61
+ 'maxPriorityFeePerGas': base_fee,
62
+ 'chainId': CHAIN_ID,
63
+ }
64
+
65
+ signed = account.sign_transaction(tx)
66
+ tx_hash = await w3.eth.send_raw_transaction(signed.rawTransaction)
67
+ print(f" Tx: {tx_hash.hex()[:20]}...")
68
+
69
+ receipt = await w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
70
+ if receipt.status == 1:
71
+ print(f" ✅ Success (gas: {receipt.gasUsed:,})")
72
+ return True
73
+ else:
74
+ print(f" ❌ Failed")
75
+ return False
76
+
77
+
78
+ async def main():
79
+ print("=" * 60)
80
+ print("pynapse Full Demo - Filecoin Calibration Testnet")
81
+ print("=" * 60)
82
+
83
+ # Connect
84
+ w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(RPC_URL))
85
+ account = Account.from_key(PRIVATE_KEY)
86
+ print(f"\nWallet: {account.address}")
87
+
88
+ usdfc = w3.eth.contract(address=USDFC, abi=ERC20_ABI)
89
+ payments = w3.eth.contract(address=PAYMENTS, abi=PAYMENTS_ABI)
90
+
91
+ # Check balances
92
+ print("\n[1/5] Checking balances...")
93
+ fil_balance = await w3.eth.get_balance(account.address)
94
+ usdfc_balance = await usdfc.functions.balanceOf(account.address).call()
95
+ print(f" FIL: {fil_balance / 1e18:.4f} tFIL")
96
+ print(f" USDFC (wallet): {usdfc_balance / 1e18:.2f}")
97
+
98
+ if fil_balance < 1e18:
99
+ print("\n❌ Need more tFIL for gas. Get from: https://faucet.calibnet.chainsafe-fil.io/")
100
+ return
101
+
102
+ # Check payments contract balance using accounts(token, account)
103
+ try:
104
+ funds, _, _, _ = await payments.functions.accounts(USDFC, account.address).call()
105
+ payments_balance = int(funds)
106
+ print(f" USDFC (payments): {payments_balance / 1e18:.2f}")
107
+ except Exception as e:
108
+ payments_balance = 0
109
+ print(f" USDFC (payments): 0 (query failed: {e})")
110
+
111
+ # Check if we have enough funds (either in wallet or already deposited)
112
+ total_usdfc = usdfc_balance + payments_balance
113
+ if total_usdfc < 10e18:
114
+ print(f"\n❌ Need more USDFC. Total: {total_usdfc / 1e18:.2f}, need at least 10")
115
+ print(" Mint at: https://stg.usdfc.net")
116
+ return
117
+
118
+ # Step 2: Approve USDFC for payments contract (only if we have wallet USDFC)
119
+ print("\n[2/5] Checking USDFC allowance...")
120
+ if usdfc_balance > 0:
121
+ allowance = await usdfc.functions.allowance(account.address, PAYMENTS).call()
122
+ print(f" Current allowance: {allowance / 1e18:.2f}")
123
+
124
+ if allowance < usdfc_balance:
125
+ print(" Need to approve...")
126
+ tx_data = await usdfc.functions.approve(PAYMENTS, 2**256 - 1).build_transaction({'from': account.address})
127
+ if not await send_tx(w3, account, tx_data, "Approving USDFC"):
128
+ return
129
+ else:
130
+ print(" ✅ Already approved")
131
+ else:
132
+ print(" ✅ Skipped (no wallet USDFC to approve)")
133
+
134
+ # Step 3: Deposit USDFC to payments (only if we need more and have wallet USDFC)
135
+ print("\n[3/5] Depositing USDFC to payments contract...")
136
+ min_required = int(10e18) # Need at least 10 USDFC
137
+
138
+ if payments_balance >= min_required:
139
+ print(f" ✅ Already have {payments_balance/1e18:.2f} USDFC deposited")
140
+ elif usdfc_balance > 0:
141
+ deposit_amount = min(usdfc_balance, int(50e18)) # Deposit up to 50 USDFC
142
+ tx_data = await payments.functions.deposit(USDFC, account.address, deposit_amount).build_transaction({'from': account.address})
143
+ if not await send_tx(w3, account, tx_data, f"Depositing {deposit_amount/1e18:.0f} USDFC"):
144
+ print(" ⚠️ Deposit failed - may need different approach")
145
+ # Refresh payments balance
146
+ payments_balance = await payments.functions.balanceOf(USDFC).call({'from': account.address})
147
+ else:
148
+ print(" ⚠️ No wallet USDFC to deposit, hoping payments balance is sufficient")
149
+
150
+ # Step 4: Approve FWSS service
151
+ print("\n[4/5] Checking FWSS operator approval...")
152
+ try:
153
+ # operatorApprovals(token, client, operator)
154
+ approval = await payments.functions.operatorApprovals(USDFC, account.address, FWSS).call()
155
+ is_approved = approval[0]
156
+ print(f" Approved: {is_approved}")
157
+ except Exception as e:
158
+ is_approved = False
159
+ print(f" Query failed: {e}")
160
+
161
+ if not is_approved:
162
+ print(" Need to approve operator...")
163
+ # Approve with generous limits
164
+ rate_allowance = int(1e24) # Large allowance
165
+ lockup_allowance = int(1e24)
166
+ max_lockup_period = 365 * 24 * 60 * 60 # 1 year in seconds
167
+
168
+ tx_data = await payments.functions.setOperatorApproval(
169
+ USDFC, FWSS, True, rate_allowance, lockup_allowance, max_lockup_period
170
+ ).build_transaction({'from': account.address})
171
+ if not await send_tx(w3, account, tx_data, "Approving FWSS operator"):
172
+ print(" ⚠️ Operator approval failed")
173
+ else:
174
+ print(" ✅ Operator already approved")
175
+
176
+ # Step 5: Try upload with pynapse
177
+ print("\n[5/5] Testing pynapse upload...")
178
+ try:
179
+ from pynapse import AsyncSynapse
180
+
181
+ synapse = await AsyncSynapse.create(
182
+ rpc_url=RPC_URL,
183
+ chain='calibration',
184
+ private_key=PRIVATE_KEY
185
+ )
186
+
187
+ print(f" Connected as {synapse.account}")
188
+
189
+ # Check storage providers
190
+ info = await synapse.storage.get_storage_info()
191
+ providers = getattr(info, 'providers', [])
192
+ print(f" Found {len(providers)} storage providers")
193
+
194
+ if not providers:
195
+ print(" ❌ No providers available")
196
+ return
197
+
198
+ # Try to create context and upload
199
+ test_data = b"Hello from pynapse! " + bytes(range(256)) * 10
200
+ print(f" Uploading {len(test_data)} bytes...")
201
+
202
+ ctx = await synapse.storage.get_context()
203
+ piece_cid = await ctx.upload(test_data)
204
+ print(f" ✅ Uploaded! PieceCID: {piece_cid}")
205
+
206
+ # Download and verify
207
+ print(" Downloading...")
208
+ downloaded = await synapse.storage.download(piece_cid)
209
+ if downloaded == test_data:
210
+ print(" ✅ Download verified!")
211
+ else:
212
+ print(f" ⚠️ Data mismatch")
213
+
214
+ print("\n" + "=" * 60)
215
+ print("🎉 pynapse is working end-to-end!")
216
+ print("=" * 60)
217
+
218
+ except Exception as e:
219
+ print(f" ❌ Upload failed: {e}")
220
+ print("\n This may need additional debugging.")
221
+
222
+
223
+ if __name__ == "__main__":
224
+ asyncio.run(main())
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "synapse-filecoin-sdk"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  description = "Python SDK for Filecoin Onchain Cloud (Synapse)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -32,6 +32,9 @@ test = [
32
32
  "pytest>=8.0.0,<9",
33
33
  "pytest-asyncio>=0.23.0,<0.24"
34
34
  ]
35
+ langchain = [
36
+ "langchain-core>=0.1.0,<0.4"
37
+ ]
35
38
 
36
39
  [build-system]
37
40
  requires = ["hatchling"]
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -0,0 +1,4 @@
1
+ """Pynapse integrations with third-party libraries."""
2
+
3
+ # LangChain integration is optional - import directly from langchain module
4
+ # from pynapse.integrations.langchain import FilecoinDocumentLoader, FilecoinStorageTool
@@ -0,0 +1,214 @@
1
+ """
2
+ LangChain integration for pynapse.
3
+
4
+ This module provides LangChain-compatible components for storing and loading
5
+ documents on Filecoin via the Synapse SDK.
6
+
7
+ Installation:
8
+ pip install synapse-filecoin-sdk[langchain]
9
+
10
+ Usage:
11
+ from pynapse.integrations.langchain import FilecoinDocumentLoader, FilecoinStorageTool
12
+
13
+ # Load documents from Filecoin
14
+ loader = FilecoinDocumentLoader(
15
+ rpc_url="https://api.node.glif.io/rpc/v1",
16
+ chain="mainnet",
17
+ private_key=PRIVATE_KEY
18
+ )
19
+ docs = await loader.aload(piece_cid="baga6ea4seaq...")
20
+
21
+ # Store documents on Filecoin (as a LangChain tool for agents)
22
+ tool = FilecoinStorageTool(
23
+ rpc_url="https://api.node.glif.io/rpc/v1",
24
+ chain="mainnet",
25
+ private_key=PRIVATE_KEY
26
+ )
27
+ result = await tool._arun(content="Hello, Filecoin!")
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import asyncio
33
+ from typing import Any, Dict, List, Optional, Type
34
+
35
+ try:
36
+ from langchain_core.documents import Document
37
+ from langchain_core.document_loaders import BaseLoader
38
+ from langchain_core.tools import BaseTool
39
+ from pydantic import BaseModel, Field
40
+ except ImportError as e:
41
+ raise ImportError(
42
+ "LangChain dependencies not installed. "
43
+ "Install with: pip install synapse-filecoin-sdk[langchain]"
44
+ ) from e
45
+
46
+ from pynapse import AsyncSynapse
47
+
48
+
49
+ class FilecoinDocumentLoader(BaseLoader):
50
+ """Load documents from Filecoin using pynapse.
51
+
52
+ This loader retrieves data stored on Filecoin by Piece CID and converts
53
+ it into LangChain Document objects.
54
+
55
+ Example:
56
+ loader = FilecoinDocumentLoader(
57
+ rpc_url="https://api.node.glif.io/rpc/v1",
58
+ chain="mainnet",
59
+ private_key="0x..."
60
+ )
61
+ docs = await loader.aload(piece_cid="baga6ea4seaq...")
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ rpc_url: str,
67
+ chain: str = "mainnet",
68
+ private_key: Optional[str] = None,
69
+ ):
70
+ """Initialize the Filecoin document loader.
71
+
72
+ Args:
73
+ rpc_url: RPC URL for Filecoin node
74
+ chain: Network name ("mainnet" or "calibration")
75
+ private_key: Wallet private key (optional for read-only operations)
76
+ """
77
+ self.rpc_url = rpc_url
78
+ self.chain = chain
79
+ self.private_key = private_key
80
+ self._synapse: Optional[AsyncSynapse] = None
81
+
82
+ async def _get_synapse(self) -> AsyncSynapse:
83
+ """Get or create AsyncSynapse instance."""
84
+ if self._synapse is None:
85
+ self._synapse = await AsyncSynapse.create(
86
+ rpc_url=self.rpc_url,
87
+ chain=self.chain,
88
+ private_key=self.private_key,
89
+ )
90
+ return self._synapse
91
+
92
+ def load(self) -> List[Document]:
93
+ """Synchronous load - not supported, use aload instead."""
94
+ raise NotImplementedError(
95
+ "FilecoinDocumentLoader is async-only. Use aload() instead."
96
+ )
97
+
98
+ async def aload(self, piece_cid: str) -> List[Document]:
99
+ """Load a document from Filecoin by Piece CID.
100
+
101
+ Args:
102
+ piece_cid: The Piece CID of the stored data
103
+
104
+ Returns:
105
+ List containing a single Document with the retrieved content
106
+ """
107
+ synapse = await self._get_synapse()
108
+ ctx = await synapse.storage.get_context()
109
+
110
+ # Download the data
111
+ data = await ctx.download(piece_cid)
112
+
113
+ # Try to decode as text, fall back to repr for binary
114
+ try:
115
+ content = data.decode("utf-8")
116
+ except UnicodeDecodeError:
117
+ content = repr(data)
118
+
119
+ return [
120
+ Document(
121
+ page_content=content,
122
+ metadata={
123
+ "source": f"filecoin://{piece_cid}",
124
+ "piece_cid": piece_cid,
125
+ "chain": self.chain,
126
+ "size": len(data),
127
+ }
128
+ )
129
+ ]
130
+
131
+
132
+ class FilecoinStorageInput(BaseModel):
133
+ """Input schema for FilecoinStorageTool."""
134
+ content: str = Field(description="The text content to store on Filecoin")
135
+
136
+
137
+ class FilecoinStorageTool(BaseTool):
138
+ """LangChain tool for storing data on Filecoin.
139
+
140
+ This tool allows LangChain agents to store arbitrary text content on
141
+ Filecoin and returns the Piece CID for later retrieval.
142
+
143
+ Example:
144
+ tool = FilecoinStorageTool(
145
+ rpc_url="https://api.node.glif.io/rpc/v1",
146
+ chain="mainnet",
147
+ private_key="0x..."
148
+ )
149
+ # Use in an agent or call directly
150
+ result = await tool._arun(content="Store this on Filecoin!")
151
+ """
152
+
153
+ name: str = "filecoin_storage"
154
+ description: str = (
155
+ "Store text content on Filecoin decentralized storage. "
156
+ "Returns the Piece CID which can be used to retrieve the content later. "
157
+ "Use this when you need to permanently store data on decentralized storage."
158
+ )
159
+ args_schema: Type[BaseModel] = FilecoinStorageInput
160
+
161
+ rpc_url: str
162
+ chain: str = "mainnet"
163
+ private_key: str
164
+ _synapse: Optional[AsyncSynapse] = None
165
+
166
+ class Config:
167
+ arbitrary_types_allowed = True
168
+
169
+ async def _get_synapse(self) -> AsyncSynapse:
170
+ """Get or create AsyncSynapse instance."""
171
+ if self._synapse is None:
172
+ self._synapse = await AsyncSynapse.create(
173
+ rpc_url=self.rpc_url,
174
+ chain=self.chain,
175
+ private_key=self.private_key,
176
+ )
177
+ return self._synapse
178
+
179
+ def _run(self, content: str) -> str:
180
+ """Synchronous run - wraps async implementation."""
181
+ return asyncio.run(self._arun(content=content))
182
+
183
+ async def _arun(self, content: str) -> str:
184
+ """Store content on Filecoin and return the Piece CID.
185
+
186
+ Args:
187
+ content: Text content to store
188
+
189
+ Returns:
190
+ JSON string with piece_cid, size, and tx_hash
191
+ """
192
+ synapse = await self._get_synapse()
193
+ ctx = await synapse.storage.get_context()
194
+
195
+ # Encode content and ensure minimum size (256 bytes)
196
+ data = content.encode("utf-8")
197
+ if len(data) < 256:
198
+ data = data + b'\x00' * (256 - len(data))
199
+
200
+ # Upload to Filecoin
201
+ result = await ctx.upload(data)
202
+
203
+ return (
204
+ f'{{"piece_cid": "{result.piece_cid}", '
205
+ f'"size": {result.size}, '
206
+ f'"tx_hash": "{result.tx_hash}"}}'
207
+ )
208
+
209
+
210
+ __all__ = [
211
+ "FilecoinDocumentLoader",
212
+ "FilecoinStorageTool",
213
+ "FilecoinStorageInput",
214
+ ]