qorechain-sdk 0.3.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 (113) hide show
  1. qorechain_sdk-0.3.0/.gitignore +49 -0
  2. qorechain_sdk-0.3.0/PKG-INFO +301 -0
  3. qorechain_sdk-0.3.0/README.md +267 -0
  4. qorechain_sdk-0.3.0/pyproject.toml +88 -0
  5. qorechain_sdk-0.3.0/src/qorechain/__init__.py +239 -0
  6. qorechain_sdk-0.3.0/src/qorechain/accounts.py +173 -0
  7. qorechain_sdk-0.3.0/src/qorechain/address.py +79 -0
  8. qorechain_sdk-0.3.0/src/qorechain/client.py +115 -0
  9. qorechain_sdk-0.3.0/src/qorechain/denom.py +87 -0
  10. qorechain_sdk-0.3.0/src/qorechain/errors.py +150 -0
  11. qorechain_sdk-0.3.0/src/qorechain/fees.py +67 -0
  12. qorechain_sdk-0.3.0/src/qorechain/gas.py +186 -0
  13. qorechain_sdk-0.3.0/src/qorechain/jsonrpc.py +112 -0
  14. qorechain_sdk-0.3.0/src/qorechain/messages/__init__.py +97 -0
  15. qorechain_sdk-0.3.0/src/qorechain/messages/_composer.py +60 -0
  16. qorechain_sdk-0.3.0/src/qorechain/messages/cosmos.py +105 -0
  17. qorechain_sdk-0.3.0/src/qorechain/messages/qorechain.py +242 -0
  18. qorechain_sdk-0.3.0/src/qorechain/messages/registry.py +210 -0
  19. qorechain_sdk-0.3.0/src/qorechain/networks.py +109 -0
  20. qorechain_sdk-0.3.0/src/qorechain/pqc.py +134 -0
  21. qorechain_sdk-0.3.0/src/qorechain/proto/__init__.py +1 -0
  22. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/__init__.py +1 -0
  23. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/abstractaccount/__init__.py +1 -0
  24. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/abstractaccount/v1/__init__.py +1 -0
  25. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/abstractaccount/v1/tx_pb2.py +64 -0
  26. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/abstractaccount/v1/tx_pb2.pyi +51 -0
  27. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/amm/__init__.py +1 -0
  28. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/amm/v1/__init__.py +1 -0
  29. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/amm/v1/tx_pb2.py +139 -0
  30. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/amm/v1/tx_pb2.pyi +127 -0
  31. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/bridge/__init__.py +1 -0
  32. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/bridge/v1/__init__.py +1 -0
  33. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/bridge/v1/tx_pb2.py +84 -0
  34. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/bridge/v1/tx_pb2.pyi +89 -0
  35. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/crossvm/__init__.py +1 -0
  36. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/crossvm/v1/__init__.py +1 -0
  37. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/crossvm/v1/query_pb2.py +51 -0
  38. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/crossvm/v1/query_pb2.pyi +74 -0
  39. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/crossvm/v1/tx_pb2.py +67 -0
  40. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/crossvm/v1/tx_pb2.pyi +42 -0
  41. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/license/__init__.py +1 -0
  42. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/license/v1/__init__.py +1 -0
  43. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/license/v1/tx_pb2.py +90 -0
  44. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/license/v1/tx_pb2.pyi +68 -0
  45. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/lightnode/__init__.py +1 -0
  46. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/lightnode/v1/__init__.py +1 -0
  47. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/lightnode/v1/query_pb2.py +59 -0
  48. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/lightnode/v1/query_pb2.pyi +108 -0
  49. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/lightnode/v1/tx_pb2.py +73 -0
  50. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/lightnode/v1/tx_pb2.pyi +54 -0
  51. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/multilayer/__init__.py +1 -0
  52. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/multilayer/v1/__init__.py +1 -0
  53. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/multilayer/v1/tx_pb2.py +118 -0
  54. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/multilayer/v1/tx_pb2.pyi +155 -0
  55. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/pqc/__init__.py +1 -0
  56. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/pqc/v1/__init__.py +1 -0
  57. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/pqc/v1/query_pb2.py +43 -0
  58. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/pqc/v1/query_pb2.pyi +41 -0
  59. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/pqc/v1/tx_pb2.py +96 -0
  60. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/pqc/v1/tx_pb2.pyi +92 -0
  61. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/qca/__init__.py +1 -0
  62. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/qca/v1/__init__.py +1 -0
  63. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/qca/v1/query_pb2.py +41 -0
  64. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/qca/v1/query_pb2.pyi +49 -0
  65. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rdk/__init__.py +1 -0
  66. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rdk/v1/__init__.py +1 -0
  67. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rdk/v1/tx_pb2.py +114 -0
  68. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rdk/v1/tx_pb2.pyi +122 -0
  69. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/reputation/__init__.py +1 -0
  70. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/reputation/v1/__init__.py +1 -0
  71. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/reputation/v1/query_pb2.py +41 -0
  72. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/reputation/v1/query_pb2.pyi +24 -0
  73. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rlconsensus/__init__.py +1 -0
  74. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rlconsensus/v1/__init__.py +1 -0
  75. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rlconsensus/v1/query_pb2.py +62 -0
  76. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rlconsensus/v1/query_pb2.pyi +115 -0
  77. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rlconsensus/v1/tx_pb2.py +69 -0
  78. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/rlconsensus/v1/tx_pb2.pyi +61 -0
  79. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/svm/__init__.py +1 -0
  80. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/svm/v1/__init__.py +1 -0
  81. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/svm/v1/query_pb2.py +53 -0
  82. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/svm/v1/query_pb2.pyi +71 -0
  83. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/svm/v1/tx_pb2.py +90 -0
  84. qorechain_sdk-0.3.0/src/qorechain/proto/qorechain/svm/v1/tx_pb2.pyi +83 -0
  85. qorechain_sdk-0.3.0/src/qorechain/py.typed +0 -0
  86. qorechain_sdk-0.3.0/src/qorechain/qor.py +218 -0
  87. qorechain_sdk-0.3.0/src/qorechain/query/__init__.py +27 -0
  88. qorechain_sdk-0.3.0/src/qorechain/query/grpc.py +296 -0
  89. qorechain_sdk-0.3.0/src/qorechain/rest.py +222 -0
  90. qorechain_sdk-0.3.0/src/qorechain/search.py +92 -0
  91. qorechain_sdk-0.3.0/src/qorechain/subscribe.py +162 -0
  92. qorechain_sdk-0.3.0/src/qorechain/track.py +184 -0
  93. qorechain_sdk-0.3.0/src/qorechain/tx.py +427 -0
  94. qorechain_sdk-0.3.0/src/qorechain/utils/__init__.py +41 -0
  95. qorechain_sdk-0.3.0/src/qorechain/utils/hash.py +58 -0
  96. qorechain_sdk-0.3.0/src/qorechain/utils/units.py +94 -0
  97. qorechain_sdk-0.3.0/src/qorechain/utils/validation.py +94 -0
  98. qorechain_sdk-0.3.0/tests/test_accounts.py +104 -0
  99. qorechain_sdk-0.3.0/tests/test_address.py +63 -0
  100. qorechain_sdk-0.3.0/tests/test_client.py +72 -0
  101. qorechain_sdk-0.3.0/tests/test_denom.py +79 -0
  102. qorechain_sdk-0.3.0/tests/test_errors.py +66 -0
  103. qorechain_sdk-0.3.0/tests/test_gas.py +84 -0
  104. qorechain_sdk-0.3.0/tests/test_messages.py +324 -0
  105. qorechain_sdk-0.3.0/tests/test_networks.py +49 -0
  106. qorechain_sdk-0.3.0/tests/test_pqc.py +106 -0
  107. qorechain_sdk-0.3.0/tests/test_query.py +190 -0
  108. qorechain_sdk-0.3.0/tests/test_query_grpc.py +102 -0
  109. qorechain_sdk-0.3.0/tests/test_search.py +89 -0
  110. qorechain_sdk-0.3.0/tests/test_subscribe.py +83 -0
  111. qorechain_sdk-0.3.0/tests/test_track.py +116 -0
  112. qorechain_sdk-0.3.0/tests/test_tx.py +312 -0
  113. qorechain_sdk-0.3.0/tests/test_utils.py +103 -0
@@ -0,0 +1,49 @@
1
+ # Secrets / env
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ *.pem
6
+ *.key
7
+ *.enc
8
+ secrets/
9
+
10
+ # Node / TS
11
+ node_modules/
12
+ dist/
13
+ build/
14
+ *.tsbuildinfo
15
+ coverage/
16
+ .turbo/
17
+
18
+ # Generated proto staging
19
+ proto/github.com/
20
+
21
+ # CLI bundles a copy of templates/ at build time; the source of truth is the
22
+ # root templates/ dir, so the generated copy is not tracked.
23
+ cli/templates/
24
+
25
+ # Generated API docs (run `pnpm --filter @qorechain/sdk docs:api`)
26
+ docs/api/
27
+ packages/ts/docs/
28
+
29
+ # Docusaurus docs site (standalone; not in the pnpm workspace)
30
+ .docusaurus/
31
+ docs/.docusaurus/
32
+ docs/build/
33
+
34
+ # Per-language (placeholders)
35
+ target/
36
+ __pycache__/
37
+ *.py[cod]
38
+ .venv/
39
+ *.egg-info/
40
+ .pytest_cache/
41
+ .mypy_cache/
42
+ .ruff_cache/
43
+
44
+ # OS / IDE
45
+ .DS_Store
46
+ Thumbs.db
47
+ .idea/
48
+ .vscode/
49
+ *.swp
@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.4
2
+ Name: qorechain-sdk
3
+ Version: 0.3.0
4
+ Summary: Python SDK for QoreChain — typed messages for every module, queries, auto-gas, tx tracking, subscriptions, accounts (native/EVM/SVM), and post-quantum signing.
5
+ Project-URL: Homepage, https://github.com/qorechain/qorechain-sdk
6
+ Project-URL: Repository, https://github.com/qorechain/qorechain-sdk
7
+ Project-URL: Issues, https://github.com/qorechain/qorechain-sdk/issues
8
+ Author-email: Liviu Epure <liviu.etty@gmail.com>
9
+ License-Expression: Apache-2.0
10
+ Keywords: blockchain,cosmos,dilithium,evm,ml-dsa,post-quantum,qorechain,svm
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: base58<3,>=2.1
21
+ Requires-Dist: bech32<2,>=1.2
22
+ Requires-Dist: bip-utils<3,>=2.9
23
+ Requires-Dist: cosmpy<1,>=0.11
24
+ Requires-Dist: dilithium-py>=1.1
25
+ Requires-Dist: httpx<1,>=0.27
26
+ Requires-Dist: websockets<16,>=12
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.10; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
30
+ Requires-Dist: pytest>=8; extra == 'dev'
31
+ Requires-Dist: respx>=0.21; extra == 'dev'
32
+ Requires-Dist: ruff>=0.5; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # qorechain (Python SDK)
36
+
37
+ A typed Python SDK for QoreChain — typed messages for every module, typed query
38
+ clients, auto-gas, tx tracking, error decoding, block/tx search, websocket
39
+ subscriptions, network presets, denom/address utilities, HD account derivation
40
+ (native / EVM / SVM), post-quantum (ML-DSA-87) signing, and read clients (REST +
41
+ `qor_` JSON-RPC). It mirrors the QoreChain TypeScript SDK surface with idiomatic
42
+ Python.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pip install qorechain-sdk
48
+ ```
49
+
50
+ Python 3.10+ is required. The package ships type hints and a `py.typed` marker.
51
+
52
+ ## Quickstart
53
+
54
+ ### Connect a client
55
+
56
+ ```python
57
+ from qorechain import create_client
58
+
59
+ # Defaults to the testnet preset (localhost endpoints).
60
+ client = create_client()
61
+ print(client.network.chain_id) # "qorechain-diana"
62
+
63
+ # Read account balances over REST.
64
+ balances = client.rest.get_all_balances("qor15yk64u7zc9g9k2yr2wmzeva5qgwxps6yjecvvu")
65
+
66
+ # Call the qor_ JSON-RPC namespace.
67
+ stats = client.qor.get_ai_stats()
68
+
69
+ # Estimate a fee (AI oracle with a deterministic static fallback).
70
+ fee = client.fees.estimate("fast")
71
+
72
+ client.close()
73
+ ```
74
+
75
+ Mainnet (chain id `qorechain-vladi`) is live; select it and override the
76
+ localhost defaults with your node URLs:
77
+
78
+ ```python
79
+ client = create_client(
80
+ network="mainnet",
81
+ endpoints={
82
+ "rest": "https://rest.qore.network",
83
+ "evm_rpc": "https://evm.qore.network",
84
+ },
85
+ )
86
+ print(client.network.chain_id) # "qorechain-vladi"
87
+ ```
88
+
89
+ ### Derive accounts
90
+
91
+ ```python
92
+ from qorechain import (
93
+ generate_mnemonic,
94
+ derive_native_account,
95
+ derive_evm_account,
96
+ derive_svm_account,
97
+ )
98
+
99
+ mnemonic = generate_mnemonic() # 12 words; use generate_mnemonic(256) for 24
100
+
101
+ native = derive_native_account(mnemonic, 0) # qor1...
102
+ evm = derive_evm_account(mnemonic, 0) # 0x... (EIP-55 checksummed)
103
+ svm = derive_svm_account(mnemonic, 0) # base58 ed25519 pubkey
104
+
105
+ print(native.address, evm.address, svm.address)
106
+ ```
107
+
108
+ Derivation schemes:
109
+
110
+ | Type | Curve | Path | Address |
111
+ | ------ | --------- | --------------------- | ------- |
112
+ | native | secp256k1 | `m/44'/118'/0'/0/{i}` | bech32 `qor` of `ripemd160(sha256(pubkey))` |
113
+ | evm | secp256k1 | `m/44'/60'/0'/0/{i}` | `0x` + `keccak256(pubkey)[-20:]`, EIP-55 |
114
+ | svm | ed25519 | `m/44'/501'/{i}'/0'` | base58 of the 32-byte public key |
115
+
116
+ The mnemonic is validated (words **and** checksum) before any key is derived, so
117
+ a typo'd phrase raises rather than silently producing a wrong account.
118
+
119
+ ### Denomination math
120
+
121
+ ```python
122
+ from qorechain import to_base, from_base
123
+
124
+ to_base("1.5") # "1500000" (QOR -> uqor, exponent 6)
125
+ from_base("1500000") # "1.5"
126
+ ```
127
+
128
+ All conversions use integer arithmetic — never floats — so they are exact.
129
+
130
+ ### Post-quantum signing (ML-DSA-87 / Dilithium-5)
131
+
132
+ ```python
133
+ from qorechain import (
134
+ generate_pqc_keypair,
135
+ pqc_sign,
136
+ pqc_verify,
137
+ build_hybrid_signature_extension,
138
+ ALGORITHM_DILITHIUM5,
139
+ )
140
+
141
+ kp = generate_pqc_keypair() # public 2592 B, secret 4896 B
142
+ sig = pqc_sign(kp.secret_key, b"msg") # signature 4627 B
143
+ assert pqc_verify(kp.public_key, b"msg", sig)
144
+
145
+ # Build the on-chain hybrid-signature extension (base64-encoded, Go-JSON shape).
146
+ ext = build_hybrid_signature_extension(ALGORITHM_DILITHIUM5, sig, kp.public_key)
147
+ ```
148
+
149
+ ### Async clients
150
+
151
+ ```python
152
+ import asyncio
153
+ from qorechain import AsyncRestClient, AsyncQorClient
154
+
155
+ async def main():
156
+ async with AsyncRestClient("http://localhost:1317") as rest:
157
+ await rest.get_ai_stats()
158
+ async with AsyncQorClient("http://localhost:8545") as qor:
159
+ await qor.get_tokenomics_overview()
160
+
161
+ asyncio.run(main())
162
+ ```
163
+
164
+ ### Typed messages for every module
165
+
166
+ `msg.<module>.<name>(...)` builds any of the chain's 49 custom messages (across
167
+ amm / bridge / rdk / multilayer / pqc / svm / lightnode / license /
168
+ abstractaccount / crossvm / rlconsensus) plus the standard Cosmos modules
169
+ (bank / staking / distribution / gov / authz / feegrant / ibc). Each returns a
170
+ `Msg` (`{type_url, value}`) you pass to `send_messages` or the hybrid PQC path.
171
+
172
+ ```python
173
+ from cosmpy.protos.cosmos.base.v1beta1.coin_pb2 import Coin
174
+ from qorechain import msg, send_messages, build_hybrid_tx, generate_pqc_keypair
175
+
176
+ swap = msg.amm.swap_exact_in(
177
+ sender=native.address,
178
+ pool_id=1,
179
+ token_in=Coin(denom="uqor", amount="1000000"),
180
+ denom_out="uusdc",
181
+ min_out="990000",
182
+ )
183
+ delegate = msg.staking.delegate(delegator_address=native.address, validator_address="qorvaloper1...")
184
+
185
+ # Classical tx carrying any messages.
186
+ built = send_messages(
187
+ account=native, messages=[swap, delegate],
188
+ chain_id="qorechain-diana", account_number=0, sequence=0,
189
+ fee={"amount": [{"denom": "uqor", "amount": "5000"}], "gas": "200000"},
190
+ )
191
+
192
+ # Or a quantum-safe hybrid (classical + ML-DSA-87) tx over the same messages.
193
+ hybrid = build_hybrid_tx(
194
+ account=native, pqc_keypair=generate_pqc_keypair(), messages=[swap],
195
+ fee={"amount": [{"denom": "uqor", "amount": "5000"}], "gas": "200000"},
196
+ chain_id="qorechain-diana", account_number=0, sequence=0,
197
+ )
198
+ ```
199
+
200
+ The `qorechain_registry()` type-URL → proto map and `decode_any(type_url, value)`
201
+ let you parse any supported message back into a typed object.
202
+
203
+ ### Typed query clients (gRPC)
204
+
205
+ Modules with a `Query` service (crossvm, lightnode, pqc, qca, reputation,
206
+ rlconsensus, svm) expose typed callers over a gRPC channel:
207
+
208
+ ```python
209
+ from qorechain import connect_query_clients
210
+
211
+ with connect_query_clients("localhost:9090") as q:
212
+ res = q.crossvm.message("msg-123") # -> QueryMessageResponse
213
+ node = q.lightnode.light_node(native.address)
214
+ acct = q.pqc.account(native.address)
215
+ ```
216
+
217
+ ### Auto-gas, errors, tracking, search
218
+
219
+ ```python
220
+ from qorechain import (
221
+ auto_fee, GasPrice, calculate_fee, # gas
222
+ decode_tx_error, QoreTxError, # errors
223
+ wait_for_tx, broadcast_and_wait, with_retry, # tracking
224
+ get_tx, get_block, search_txs, build_events_query, # search
225
+ )
226
+
227
+ # Simulate -> gas_used x 1.4 x 0.025uqor.
228
+ fee = auto_fee("http://localhost:1317", built)
229
+ calculate_fee(200000, GasPrice.from_string("0.025uqor"))
230
+
231
+ # Wait for inclusion; raises a typed QoreTxError on a non-zero code.
232
+ included = wait_for_tx(client.rest, "TXHASH")
233
+
234
+ search_txs(client.rest, {"message.sender": native.address}, limit=20, order_by="desc")
235
+ ```
236
+
237
+ ### Websocket subscriptions
238
+
239
+ ```python
240
+ from qorechain import SubscriptionClient
241
+
242
+ async def run():
243
+ sub = await SubscriptionClient.connect("http://localhost:26657")
244
+ async def on_block(ev): print("block", ev)
245
+ unsubscribe = await sub.subscribe_new_blocks(on_block)
246
+ await sub.subscribe_tx({"message.sender": native.address}, lambda ev: ...)
247
+ # ... later ...
248
+ await unsubscribe()
249
+ await sub.close()
250
+ ```
251
+
252
+ ### Utilities
253
+
254
+ ```python
255
+ from qorechain import (
256
+ sha256_hex, keccak256_hex, ripemd160_hex,
257
+ parse_units, format_units,
258
+ is_valid_evm_address, is_valid_svm_address, to_checksum_address,
259
+ )
260
+
261
+ parse_units("1.5", 18) # 1500000000000000000
262
+ format_units(1500000000000000000, 18) # "1.5"
263
+ to_checksum_address("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
264
+ ```
265
+
266
+ ## Regenerating protobuf code (maintainers)
267
+
268
+ The generated protobuf modules under `src/qorechain/proto/` are committed, so
269
+ users never run `protoc`. To regenerate after a proto change (requires `buf`):
270
+
271
+ ```bash
272
+ bash scripts/codegen-py.sh
273
+ ```
274
+
275
+ It runs `buf generate` (public-registry `protocolbuffers/python` + `pyi`
276
+ plugins, pinned to the protobuf 5.29.x line), rewrites dependency imports to
277
+ `cosmpy`'s bundled protos so the gencode shares one descriptor pool, and writes
278
+ package `__init__.py` files.
279
+
280
+ ## Out of scope (use a dedicated library)
281
+
282
+ Browser-wallet adapters (Keplr / MetaMask / Phantom) and viem / `@solana/web3.js`
283
+ -style EVM/SVM clients are intentionally **not** part of this SDK — they are
284
+ JS/browser-specific. In Python, talk to the EVM with [web3.py](https://web3py.readthedocs.io)
285
+ and to the SVM with [solana-py](https://michaelhly.github.io/solana-py/), pointing
286
+ them at the network's `evm_rpc` / `svm_rpc` endpoints. This SDK covers the native
287
+ (Cosmos-SDK) chain surface end to end.
288
+
289
+ ## Development
290
+
291
+ ```bash
292
+ python -m venv .venv
293
+ .venv/bin/pip install -e "packages/py[dev]"
294
+ .venv/bin/pytest packages/py
295
+ .venv/bin/mypy packages/py/src
296
+ .venv/bin/ruff check packages/py/src packages/py/tests
297
+ ```
298
+
299
+ ## License
300
+
301
+ Apache-2.0
@@ -0,0 +1,267 @@
1
+ # qorechain (Python SDK)
2
+
3
+ A typed Python SDK for QoreChain — typed messages for every module, typed query
4
+ clients, auto-gas, tx tracking, error decoding, block/tx search, websocket
5
+ subscriptions, network presets, denom/address utilities, HD account derivation
6
+ (native / EVM / SVM), post-quantum (ML-DSA-87) signing, and read clients (REST +
7
+ `qor_` JSON-RPC). It mirrors the QoreChain TypeScript SDK surface with idiomatic
8
+ Python.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install qorechain-sdk
14
+ ```
15
+
16
+ Python 3.10+ is required. The package ships type hints and a `py.typed` marker.
17
+
18
+ ## Quickstart
19
+
20
+ ### Connect a client
21
+
22
+ ```python
23
+ from qorechain import create_client
24
+
25
+ # Defaults to the testnet preset (localhost endpoints).
26
+ client = create_client()
27
+ print(client.network.chain_id) # "qorechain-diana"
28
+
29
+ # Read account balances over REST.
30
+ balances = client.rest.get_all_balances("qor15yk64u7zc9g9k2yr2wmzeva5qgwxps6yjecvvu")
31
+
32
+ # Call the qor_ JSON-RPC namespace.
33
+ stats = client.qor.get_ai_stats()
34
+
35
+ # Estimate a fee (AI oracle with a deterministic static fallback).
36
+ fee = client.fees.estimate("fast")
37
+
38
+ client.close()
39
+ ```
40
+
41
+ Mainnet (chain id `qorechain-vladi`) is live; select it and override the
42
+ localhost defaults with your node URLs:
43
+
44
+ ```python
45
+ client = create_client(
46
+ network="mainnet",
47
+ endpoints={
48
+ "rest": "https://rest.qore.network",
49
+ "evm_rpc": "https://evm.qore.network",
50
+ },
51
+ )
52
+ print(client.network.chain_id) # "qorechain-vladi"
53
+ ```
54
+
55
+ ### Derive accounts
56
+
57
+ ```python
58
+ from qorechain import (
59
+ generate_mnemonic,
60
+ derive_native_account,
61
+ derive_evm_account,
62
+ derive_svm_account,
63
+ )
64
+
65
+ mnemonic = generate_mnemonic() # 12 words; use generate_mnemonic(256) for 24
66
+
67
+ native = derive_native_account(mnemonic, 0) # qor1...
68
+ evm = derive_evm_account(mnemonic, 0) # 0x... (EIP-55 checksummed)
69
+ svm = derive_svm_account(mnemonic, 0) # base58 ed25519 pubkey
70
+
71
+ print(native.address, evm.address, svm.address)
72
+ ```
73
+
74
+ Derivation schemes:
75
+
76
+ | Type | Curve | Path | Address |
77
+ | ------ | --------- | --------------------- | ------- |
78
+ | native | secp256k1 | `m/44'/118'/0'/0/{i}` | bech32 `qor` of `ripemd160(sha256(pubkey))` |
79
+ | evm | secp256k1 | `m/44'/60'/0'/0/{i}` | `0x` + `keccak256(pubkey)[-20:]`, EIP-55 |
80
+ | svm | ed25519 | `m/44'/501'/{i}'/0'` | base58 of the 32-byte public key |
81
+
82
+ The mnemonic is validated (words **and** checksum) before any key is derived, so
83
+ a typo'd phrase raises rather than silently producing a wrong account.
84
+
85
+ ### Denomination math
86
+
87
+ ```python
88
+ from qorechain import to_base, from_base
89
+
90
+ to_base("1.5") # "1500000" (QOR -> uqor, exponent 6)
91
+ from_base("1500000") # "1.5"
92
+ ```
93
+
94
+ All conversions use integer arithmetic — never floats — so they are exact.
95
+
96
+ ### Post-quantum signing (ML-DSA-87 / Dilithium-5)
97
+
98
+ ```python
99
+ from qorechain import (
100
+ generate_pqc_keypair,
101
+ pqc_sign,
102
+ pqc_verify,
103
+ build_hybrid_signature_extension,
104
+ ALGORITHM_DILITHIUM5,
105
+ )
106
+
107
+ kp = generate_pqc_keypair() # public 2592 B, secret 4896 B
108
+ sig = pqc_sign(kp.secret_key, b"msg") # signature 4627 B
109
+ assert pqc_verify(kp.public_key, b"msg", sig)
110
+
111
+ # Build the on-chain hybrid-signature extension (base64-encoded, Go-JSON shape).
112
+ ext = build_hybrid_signature_extension(ALGORITHM_DILITHIUM5, sig, kp.public_key)
113
+ ```
114
+
115
+ ### Async clients
116
+
117
+ ```python
118
+ import asyncio
119
+ from qorechain import AsyncRestClient, AsyncQorClient
120
+
121
+ async def main():
122
+ async with AsyncRestClient("http://localhost:1317") as rest:
123
+ await rest.get_ai_stats()
124
+ async with AsyncQorClient("http://localhost:8545") as qor:
125
+ await qor.get_tokenomics_overview()
126
+
127
+ asyncio.run(main())
128
+ ```
129
+
130
+ ### Typed messages for every module
131
+
132
+ `msg.<module>.<name>(...)` builds any of the chain's 49 custom messages (across
133
+ amm / bridge / rdk / multilayer / pqc / svm / lightnode / license /
134
+ abstractaccount / crossvm / rlconsensus) plus the standard Cosmos modules
135
+ (bank / staking / distribution / gov / authz / feegrant / ibc). Each returns a
136
+ `Msg` (`{type_url, value}`) you pass to `send_messages` or the hybrid PQC path.
137
+
138
+ ```python
139
+ from cosmpy.protos.cosmos.base.v1beta1.coin_pb2 import Coin
140
+ from qorechain import msg, send_messages, build_hybrid_tx, generate_pqc_keypair
141
+
142
+ swap = msg.amm.swap_exact_in(
143
+ sender=native.address,
144
+ pool_id=1,
145
+ token_in=Coin(denom="uqor", amount="1000000"),
146
+ denom_out="uusdc",
147
+ min_out="990000",
148
+ )
149
+ delegate = msg.staking.delegate(delegator_address=native.address, validator_address="qorvaloper1...")
150
+
151
+ # Classical tx carrying any messages.
152
+ built = send_messages(
153
+ account=native, messages=[swap, delegate],
154
+ chain_id="qorechain-diana", account_number=0, sequence=0,
155
+ fee={"amount": [{"denom": "uqor", "amount": "5000"}], "gas": "200000"},
156
+ )
157
+
158
+ # Or a quantum-safe hybrid (classical + ML-DSA-87) tx over the same messages.
159
+ hybrid = build_hybrid_tx(
160
+ account=native, pqc_keypair=generate_pqc_keypair(), messages=[swap],
161
+ fee={"amount": [{"denom": "uqor", "amount": "5000"}], "gas": "200000"},
162
+ chain_id="qorechain-diana", account_number=0, sequence=0,
163
+ )
164
+ ```
165
+
166
+ The `qorechain_registry()` type-URL → proto map and `decode_any(type_url, value)`
167
+ let you parse any supported message back into a typed object.
168
+
169
+ ### Typed query clients (gRPC)
170
+
171
+ Modules with a `Query` service (crossvm, lightnode, pqc, qca, reputation,
172
+ rlconsensus, svm) expose typed callers over a gRPC channel:
173
+
174
+ ```python
175
+ from qorechain import connect_query_clients
176
+
177
+ with connect_query_clients("localhost:9090") as q:
178
+ res = q.crossvm.message("msg-123") # -> QueryMessageResponse
179
+ node = q.lightnode.light_node(native.address)
180
+ acct = q.pqc.account(native.address)
181
+ ```
182
+
183
+ ### Auto-gas, errors, tracking, search
184
+
185
+ ```python
186
+ from qorechain import (
187
+ auto_fee, GasPrice, calculate_fee, # gas
188
+ decode_tx_error, QoreTxError, # errors
189
+ wait_for_tx, broadcast_and_wait, with_retry, # tracking
190
+ get_tx, get_block, search_txs, build_events_query, # search
191
+ )
192
+
193
+ # Simulate -> gas_used x 1.4 x 0.025uqor.
194
+ fee = auto_fee("http://localhost:1317", built)
195
+ calculate_fee(200000, GasPrice.from_string("0.025uqor"))
196
+
197
+ # Wait for inclusion; raises a typed QoreTxError on a non-zero code.
198
+ included = wait_for_tx(client.rest, "TXHASH")
199
+
200
+ search_txs(client.rest, {"message.sender": native.address}, limit=20, order_by="desc")
201
+ ```
202
+
203
+ ### Websocket subscriptions
204
+
205
+ ```python
206
+ from qorechain import SubscriptionClient
207
+
208
+ async def run():
209
+ sub = await SubscriptionClient.connect("http://localhost:26657")
210
+ async def on_block(ev): print("block", ev)
211
+ unsubscribe = await sub.subscribe_new_blocks(on_block)
212
+ await sub.subscribe_tx({"message.sender": native.address}, lambda ev: ...)
213
+ # ... later ...
214
+ await unsubscribe()
215
+ await sub.close()
216
+ ```
217
+
218
+ ### Utilities
219
+
220
+ ```python
221
+ from qorechain import (
222
+ sha256_hex, keccak256_hex, ripemd160_hex,
223
+ parse_units, format_units,
224
+ is_valid_evm_address, is_valid_svm_address, to_checksum_address,
225
+ )
226
+
227
+ parse_units("1.5", 18) # 1500000000000000000
228
+ format_units(1500000000000000000, 18) # "1.5"
229
+ to_checksum_address("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
230
+ ```
231
+
232
+ ## Regenerating protobuf code (maintainers)
233
+
234
+ The generated protobuf modules under `src/qorechain/proto/` are committed, so
235
+ users never run `protoc`. To regenerate after a proto change (requires `buf`):
236
+
237
+ ```bash
238
+ bash scripts/codegen-py.sh
239
+ ```
240
+
241
+ It runs `buf generate` (public-registry `protocolbuffers/python` + `pyi`
242
+ plugins, pinned to the protobuf 5.29.x line), rewrites dependency imports to
243
+ `cosmpy`'s bundled protos so the gencode shares one descriptor pool, and writes
244
+ package `__init__.py` files.
245
+
246
+ ## Out of scope (use a dedicated library)
247
+
248
+ Browser-wallet adapters (Keplr / MetaMask / Phantom) and viem / `@solana/web3.js`
249
+ -style EVM/SVM clients are intentionally **not** part of this SDK — they are
250
+ JS/browser-specific. In Python, talk to the EVM with [web3.py](https://web3py.readthedocs.io)
251
+ and to the SVM with [solana-py](https://michaelhly.github.io/solana-py/), pointing
252
+ them at the network's `evm_rpc` / `svm_rpc` endpoints. This SDK covers the native
253
+ (Cosmos-SDK) chain surface end to end.
254
+
255
+ ## Development
256
+
257
+ ```bash
258
+ python -m venv .venv
259
+ .venv/bin/pip install -e "packages/py[dev]"
260
+ .venv/bin/pytest packages/py
261
+ .venv/bin/mypy packages/py/src
262
+ .venv/bin/ruff check packages/py/src packages/py/tests
263
+ ```
264
+
265
+ ## License
266
+
267
+ Apache-2.0
@@ -0,0 +1,88 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "qorechain-sdk"
7
+ version = "0.3.0"
8
+ description = "Python SDK for QoreChain — typed messages for every module, queries, auto-gas, tx tracking, subscriptions, accounts (native/EVM/SVM), and post-quantum signing."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "Apache-2.0"
12
+ authors = [{ name = "Liviu Epure", email = "liviu.etty@gmail.com" }]
13
+ keywords = ["qorechain", "blockchain", "cosmos", "evm", "svm", "post-quantum", "ml-dsa", "dilithium"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: Apache Software License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = [
25
+ "httpx>=0.27,<1",
26
+ "bip-utils>=2.9,<3",
27
+ "bech32>=1.2,<2",
28
+ "base58>=2.1,<3",
29
+ "dilithium-py>=1.1",
30
+ "cosmpy>=0.11,<1",
31
+ "websockets>=12,<16",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=8",
37
+ "pytest-asyncio>=0.23",
38
+ "respx>=0.21",
39
+ "mypy>=1.10",
40
+ "ruff>=0.5",
41
+ ]
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/qorechain/qorechain-sdk"
45
+ Repository = "https://github.com/qorechain/qorechain-sdk"
46
+ Issues = "https://github.com/qorechain/qorechain-sdk/issues"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/qorechain"]
50
+
51
+ [tool.pytest.ini_options]
52
+ testpaths = ["tests"]
53
+ asyncio_mode = "auto"
54
+
55
+ [tool.mypy]
56
+ python_version = "3.10"
57
+ strict = true
58
+ files = ["src/qorechain"]
59
+ # Generated protobuf gencode is not hand-written and ships its own .pyi stubs;
60
+ # exclude it from strict checking (it is consumed via cosmpy's Any packing).
61
+ exclude = "src/qorechain/proto/"
62
+
63
+ # Third-party crypto libs ship no type stubs / py.typed marker.
64
+ [[tool.mypy.overrides]]
65
+ module = [
66
+ "bip_utils.*",
67
+ "dilithium_py.*",
68
+ "bech32.*",
69
+ "base58.*",
70
+ "cosmpy.*",
71
+ "google.protobuf.*",
72
+ "websockets.*",
73
+ "grpc",
74
+ "grpc.*",
75
+ "Crypto.*",
76
+ # Generated protobuf modules consumed by the typed message layer.
77
+ "qorechain.proto.*",
78
+ ]
79
+ ignore_missing_imports = true
80
+
81
+ [tool.ruff]
82
+ line-length = 100
83
+ target-version = "py310"
84
+ # Generated protobuf gencode is not linted (DO NOT EDIT).
85
+ extend-exclude = ["src/qorechain/proto"]
86
+
87
+ [tool.ruff.lint]
88
+ select = ["E", "F", "I", "UP", "B"]