chipi-stack 2.1.0__tar.gz → 2.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 (57) hide show
  1. {chipi_stack-2.1.0/chipi_stack.egg-info → chipi_stack-2.3.0}/PKG-INFO +27 -1
  2. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/README.md +22 -0
  3. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/__init__.py +13 -1
  4. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/constants.py +78 -2
  5. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/execute_paymaster.py +128 -6
  6. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/__init__.py +2 -0
  7. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/wallet.py +84 -18
  8. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/sessions.py +57 -24
  9. chipi_stack-2.3.0/chipi_sdk/shhh/__init__.py +265 -0
  10. chipi_stack-2.3.0/chipi_sdk/shhh/compute_wallet_address.py +97 -0
  11. chipi_stack-2.3.0/chipi_sdk/shhh/execute_paymaster_raw.py +439 -0
  12. chipi_stack-2.3.0/chipi_sdk/shhh/migrate.py +358 -0
  13. chipi_stack-2.3.0/chipi_sdk/shhh/oe.py +189 -0
  14. chipi_stack-2.3.0/chipi_sdk/shhh/recovery.py +450 -0
  15. chipi_stack-2.3.0/chipi_sdk/shhh/signers/__init__.py +101 -0
  16. chipi_stack-2.3.0/chipi_sdk/shhh/signers/ed25519.py +221 -0
  17. chipi_stack-2.3.0/chipi_sdk/shhh/signers/eip191.py +261 -0
  18. chipi_stack-2.3.0/chipi_sdk/shhh/signers/jwt_apple_sub.py +517 -0
  19. chipi_stack-2.3.0/chipi_sdk/shhh/signers/stark.py +92 -0
  20. chipi_stack-2.3.0/chipi_sdk/shhh/signers/webauthn.py +241 -0
  21. chipi_stack-2.3.0/chipi_sdk/shhh/snip12_hash.py +214 -0
  22. chipi_stack-2.3.0/chipi_sdk/shhh/threshold.py +295 -0
  23. chipi_stack-2.3.0/chipi_sdk/wallets.py +700 -0
  24. {chipi_stack-2.1.0 → chipi_stack-2.3.0/chipi_stack.egg-info}/PKG-INFO +27 -1
  25. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_stack.egg-info/SOURCES.txt +14 -0
  26. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_stack.egg-info/requires.txt +4 -0
  27. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/pyproject.toml +10 -1
  28. chipi_stack-2.1.0/chipi_sdk/wallets.py +0 -465
  29. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/LICENSE +0 -0
  30. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/MANIFEST.in +0 -0
  31. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/client.py +0 -0
  32. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/encryption.py +0 -0
  33. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/errors.py +0 -0
  34. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/formatters.py +0 -0
  35. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/core.py +0 -0
  36. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/session.py +0 -0
  37. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/sku.py +0 -0
  38. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/sku_purchase.py +0 -0
  39. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/sku_transaction.py +0 -0
  40. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/transaction.py +0 -0
  41. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/user.py +0 -0
  42. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/models/x402.py +0 -0
  43. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/py.typed +0 -0
  44. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/sdk.py +0 -0
  45. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/sku_purchases.py +0 -0
  46. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/sku_transactions.py +0 -0
  47. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/skus.py +0 -0
  48. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/transactions.py +0 -0
  49. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/users.py +0 -0
  50. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/validators.py +0 -0
  51. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/x402_client.py +0 -0
  52. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/x402_facilitator.py +0 -0
  53. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_sdk/x402_middleware.py +0 -0
  54. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_stack.egg-info/dependency_links.txt +0 -0
  55. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/chipi_stack.egg-info/top_level.txt +0 -0
  56. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/setup.cfg +0 -0
  57. {chipi_stack-2.1.0 → chipi_stack-2.3.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chipi-stack
3
- Version: 2.1.0
3
+ Version: 2.3.0
4
4
  Summary: Python SDK for Starknet Gasless Transactions via Chipi
5
5
  Author-email: Carlos Castillo <carlos@chipipay.com>, Roberto Yamanaka <roberto@chipipay.com>
6
6
  License-Expression: MIT
@@ -25,6 +25,10 @@ Requires-Dist: pydantic>=2.0.0
25
25
  Requires-Dist: httpx>=0.27.0
26
26
  Requires-Dist: cryptography>=42.0.0
27
27
  Requires-Dist: typing-extensions>=4.9.0
28
+ Requires-Dist: poseidon-py>=0.1.5
29
+ Requires-Dist: pycryptodome>=3.20.0
30
+ Requires-Dist: garaga==1.1.0
31
+ Requires-Dist: eth-keys>=0.5.0
28
32
  Provides-Extra: dev
29
33
  Requires-Dist: pytest>=8.0.0; extra == "dev"
30
34
  Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
@@ -75,6 +79,28 @@ uv add chipi-stack
75
79
  - `pydantic>=2.0.0` - Data validation
76
80
  - `httpx>=0.27.0` - HTTP client
77
81
  - `cryptography>=42.0.0` - AES encryption
82
+ - `garaga==1.1.0` - Cairo-compatible BN-curve math for SHHH Ed25519 signing
83
+ - `eth-keys>=0.5.0` - secp256k1 ECDSA for SHHH EIP-191 signing
84
+ - `poseidon-py>=0.1.5` - Poseidon hash (used by SHHH SNIP-12 envelopes)
85
+ - `pycryptodome>=3.20.0` - keccak256 (used by SHHH EIP-191)
86
+
87
+ ### macOS Apple Silicon installation note
88
+
89
+ Garaga's BN-curve math links against libssl. On macOS 14 Sonoma+ on
90
+ Apple Silicon, the system libssl can mis-link during the garaga build.
91
+ If `pip install chipi-stack` fails with "library not loaded" or
92
+ "Symbol not found" mentioning `libssl` / `libcrypto`, install OpenSSL 3
93
+ via Homebrew and re-install:
94
+
95
+ ```bash
96
+ brew install openssl@3
97
+ export LDFLAGS="-L$(brew --prefix openssl@3)/lib"
98
+ export CPPFLAGS="-I$(brew --prefix openssl@3)/include"
99
+ pip install --force-reinstall chipi-stack
100
+ ```
101
+
102
+ Linux and Intel Macs are unaffected. Windows is untested for the SHHH
103
+ Ed25519 signer specifically; the rest of the SDK works on Windows.
78
104
 
79
105
  ## Quick Start
80
106
 
@@ -37,6 +37,28 @@ uv add chipi-stack
37
37
  - `pydantic>=2.0.0` - Data validation
38
38
  - `httpx>=0.27.0` - HTTP client
39
39
  - `cryptography>=42.0.0` - AES encryption
40
+ - `garaga==1.1.0` - Cairo-compatible BN-curve math for SHHH Ed25519 signing
41
+ - `eth-keys>=0.5.0` - secp256k1 ECDSA for SHHH EIP-191 signing
42
+ - `poseidon-py>=0.1.5` - Poseidon hash (used by SHHH SNIP-12 envelopes)
43
+ - `pycryptodome>=3.20.0` - keccak256 (used by SHHH EIP-191)
44
+
45
+ ### macOS Apple Silicon installation note
46
+
47
+ Garaga's BN-curve math links against libssl. On macOS 14 Sonoma+ on
48
+ Apple Silicon, the system libssl can mis-link during the garaga build.
49
+ If `pip install chipi-stack` fails with "library not loaded" or
50
+ "Symbol not found" mentioning `libssl` / `libcrypto`, install OpenSSL 3
51
+ via Homebrew and re-install:
52
+
53
+ ```bash
54
+ brew install openssl@3
55
+ export LDFLAGS="-L$(brew --prefix openssl@3)/lib"
56
+ export CPPFLAGS="-I$(brew --prefix openssl@3)/include"
57
+ pip install --force-reinstall chipi-stack
58
+ ```
59
+
60
+ Linux and Intel Macs are unaffected. Windows is untested for the SHHH
61
+ Ed25519 signer specifically; the rest of the SDK works on Windows.
40
62
 
41
63
  ## Quick Start
42
64
 
@@ -5,7 +5,7 @@ A Python SDK for interacting with Chipi's gasless transaction infrastructure on
5
5
  Supports wallet creation, transaction execution, session keys, and more.
6
6
  """
7
7
 
8
- __version__ = "2.1.0"
8
+ __version__ = "2.3.0"
9
9
 
10
10
  # Main SDK class
11
11
  from .sdk import ChipiSDK
@@ -46,6 +46,7 @@ from .models.wallet import (
46
46
  GetWalletResponse,
47
47
  PasskeyMetadata,
48
48
  PrepareWalletCreationResponse,
49
+ ShhhSignerKind,
49
50
  WalletData,
50
51
  WalletType,
51
52
  PrepareWalletUpgradeParams,
@@ -163,17 +164,22 @@ from .constants import (
163
164
  DEFAULT_PAGINATION,
164
165
  ERRORS,
165
166
  LEGACY_CHIPI_CLASS_HASHES,
167
+ LEGACY_SHHH_CLASS_HASHES,
166
168
  PAYMASTER_CONFIG,
167
169
  SERVICE_TYPES,
168
170
  SESSION_DEFAULTS,
169
171
  SESSION_ENTRYPOINTS,
170
172
  SESSION_ERRORS,
173
+ SHHH_ENVELOPE_TAGS,
174
+ SHHH_KIND_GAS_OVERHEAD,
175
+ SHHH_VERIFIER_CLASS_HASHES,
171
176
  SKU_CONTRACTS,
172
177
  STARKNET_NETWORKS,
173
178
  TOKEN_DECIMALS,
174
179
  WALLET_CLASS_HASHES,
175
180
  WALLET_RPC_ENDPOINTS,
176
181
  get_wallet_type_from_class_hash,
182
+ is_known_shhh_class_hash,
177
183
  )
178
184
 
179
185
  # Utilities
@@ -236,6 +242,7 @@ __all__ = [
236
242
  "PrepareWalletCreationResponse",
237
243
  "WalletData",
238
244
  "WalletType",
245
+ "ShhhSignerKind",
239
246
  "PrepareWalletUpgradeParams",
240
247
  "PrepareWalletUpgradeResponse",
241
248
  "ExecuteWalletUpgradeParams",
@@ -319,7 +326,12 @@ __all__ = [
319
326
  "TOKEN_DECIMALS",
320
327
  "WALLET_CLASS_HASHES",
321
328
  "LEGACY_CHIPI_CLASS_HASHES",
329
+ "LEGACY_SHHH_CLASS_HASHES",
330
+ "SHHH_ENVELOPE_TAGS",
331
+ "SHHH_KIND_GAS_OVERHEAD",
332
+ "SHHH_VERIFIER_CLASS_HASHES",
322
333
  "get_wallet_type_from_class_hash",
334
+ "is_known_shhh_class_hash",
323
335
  "WALLET_RPC_ENDPOINTS",
324
336
  # Utilities
325
337
  "camel_to_snake",
@@ -1,7 +1,7 @@
1
1
  """Constants used across Chipi SDK."""
2
2
 
3
3
  from typing import Optional
4
- from .models.wallet import WalletType
4
+ from .models.wallet import ShhhSignerKind, WalletType
5
5
 
6
6
 
7
7
  # API Versioning
@@ -95,6 +95,7 @@ CHAIN_TOKEN_TYPES = {
95
95
  WALLET_CLASS_HASHES: dict[WalletType, str] = {
96
96
  WalletType.CHIPI: "0x0484bbd2404b3c7264bea271f7267d6d4004821ac7787a9eed7f472e79ef40d1", # v33 — latest, spending policy + audit fixes
97
97
  WalletType.READY: "0x036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f",
98
+ WalletType.SHHH: "0x075dfb396145926bffa6beb659897f46cc082a50b211d80871cff7f1038fa58a", # V8.4 ShhhAccount — declared 2026-05-15, audit-closed
98
99
  }
99
100
 
100
101
  # Previous CHIPI wallet class hashes still deployed on-chain (newest → oldest)
@@ -106,6 +107,54 @@ LEGACY_CHIPI_CLASS_HASHES: list[str] = [
106
107
  "0x02de1565226d5215a38b68c4d9a4913989b54edff64c68c45e453c417b44cd83", # v28
107
108
  ]
108
109
 
110
+ # Deprecated SHHH class hashes still deployed on-chain. V8 series ships
111
+ # without an `upgrade` selector, so existing instances at deprecated classes
112
+ # cannot self-upgrade — they redeploy at the current SHHH class hash.
113
+ LEGACY_SHHH_CLASS_HASHES: list[str] = [
114
+ "0x03bc539295abd3e59bd9ea799d12fe3331748d484bc77bff45b302b1636a87d9", # V8.3 — deprecated 2026-05-15
115
+ ]
116
+
117
+ # SHHH ShhhAccount verifier class hashes (V8.4, mainnet).
118
+ # Account dispatches `verify` to one of these via library_call_syscall based
119
+ # on the owner's `kind_tag`. Chipi routes as consumer; we do not redeclare.
120
+ SHHH_VERIFIER_CLASS_HASHES: dict[ShhhSignerKind, str] = {
121
+ ShhhSignerKind.STARK: "0x00d09209b2da9d49fc805ba26380ba4ce25aa641116c10eb178e1051a71dbf68",
122
+ ShhhSignerKind.ED25519: "0x030a7dfc03e59cef6e41699e734abd2df53ce393a052221c02c6e07665949f74",
123
+ ShhhSignerKind.SECP256K1: "0x03e81667a46bd5287e09a9600fa98d28fdc477735f2689f5f4e8e95f37b67b74",
124
+ ShhhSignerKind.EIP191_SECP256K1: "0x03a75997862059c36cb8e204fb3027eb6d1fdf933488d42c2db4528118d084e6",
125
+ ShhhSignerKind.EIP712_SECP256K1: "0x072a3f77e8c28bfea2ade91ec3fb83b6290169d1ed8c1b2396704231841c6474",
126
+ ShhhSignerKind.P256: "0x01b600709af54c8838e5f18ddad3a26feeb47cb124c239f55a0f1b7a780e2d8a",
127
+ ShhhSignerKind.WEBAUTHN_P256: "0x074f6efd2af9025cd8cab41a4565bc73b6ef097214c31352838fcdbac0a44657",
128
+ ShhhSignerKind.JWT_ES256: "0x002efce875fa3e73e04d825d8ebade53e188cc995dfe0c55a6a2f7fa6c59f497",
129
+ ShhhSignerKind.JWT_ES256_APPLE_SUB: "0x06b67762218a25fdd28e25b063480893a5cef9cdeecbc663e32d444d5734c471",
130
+ ShhhSignerKind.BLS12_381: "0x02623721e74a9ad3e0ba639065f5631a09bf900913de6ab21ea6984973cd2cd1",
131
+ }
132
+
133
+ # Upper-bound l2_gas overhead per SHHH signer kind, in addition to base
134
+ # paymaster execution cost. Numbers from snforge runs at tag v8.4
135
+ # (252-byte calls, single owner, no session key). Paymaster adds ~5M margin.
136
+ # Threshold envelopes: sum the per-owner cost + ~3M aggregation overhead.
137
+ # Session-key envelopes (SNIP-163 4-felt): ~2M + session-policy enforcement.
138
+ SHHH_KIND_GAS_OVERHEAD: dict[ShhhSignerKind, int] = {
139
+ ShhhSignerKind.STARK: 2_000_000,
140
+ ShhhSignerKind.SECP256K1: 5_000_000,
141
+ ShhhSignerKind.EIP191_SECP256K1: 10_000_000,
142
+ ShhhSignerKind.EIP712_SECP256K1: 12_000_000,
143
+ ShhhSignerKind.P256: 30_000_000,
144
+ ShhhSignerKind.ED25519: 33_000_000,
145
+ ShhhSignerKind.WEBAUTHN_P256: 35_000_000,
146
+ ShhhSignerKind.JWT_ES256: 60_000_000,
147
+ ShhhSignerKind.JWT_ES256_APPLE_SUB: 62_000_000,
148
+ ShhhSignerKind.BLS12_381: 80_000_000,
149
+ }
150
+
151
+ # Inner-envelope routing tags per the V8.4 Pluggable-Signer SNIP draft.
152
+ SHHH_ENVELOPE_TAGS = {
153
+ "V2_SNIP12": "V2_SNIP12",
154
+ "V2_THRESHOLD": "V2_THRESHOLD",
155
+ # Session-key envelopes are the SNIP-163 4-felt format (no version tag).
156
+ }
157
+
109
158
 
110
159
  def get_wallet_type_from_class_hash(class_hash: str) -> Optional[str]:
111
160
  """
@@ -115,7 +164,7 @@ def get_wallet_type_from_class_hash(class_hash: str) -> Optional[str]:
115
164
  class_hash: Class hash to look up
116
165
 
117
166
  Returns:
118
- Wallet type string ("CHIPI" or "READY"), or None if unknown
167
+ Wallet type string ("CHIPI", "READY", or "SHHH"), or None if unknown
119
168
  """
120
169
  def normalize(h: str) -> str:
121
170
  import re
@@ -129,12 +178,39 @@ def get_wallet_type_from_class_hash(class_hash: str) -> Optional[str]:
129
178
  return "CHIPI"
130
179
  if any(normalize(h) == normalized for h in LEGACY_CHIPI_CLASS_HASHES):
131
180
  return "CHIPI"
181
+ if normalize(WALLET_CLASS_HASHES[WalletType.SHHH]) == normalized:
182
+ return "SHHH"
183
+ if any(normalize(h) == normalized for h in LEGACY_SHHH_CLASS_HASHES):
184
+ return "SHHH"
132
185
  return None
133
186
 
187
+
188
+ def is_known_shhh_class_hash(class_hash: str) -> bool:
189
+ """
190
+ True if a class hash is a registered SHHH ShhhAccount class (current or
191
+ legacy) OR one of the 10 V8.4 verifier classes. Used by the backend to
192
+ reject unknown class hashes when an integrator opts into SHHH via
193
+ wallet_type="SHHH" (plan D4 safety net — open SDK option, no per-org gate).
194
+ """
195
+ import re
196
+
197
+ def normalize(h: str) -> str:
198
+ return re.sub(r"^0x0+", "0x", h.lower())
199
+
200
+ normalized = normalize(class_hash)
201
+
202
+ if normalize(WALLET_CLASS_HASHES[WalletType.SHHH]) == normalized:
203
+ return True
204
+ if any(normalize(h) == normalized for h in LEGACY_SHHH_CLASS_HASHES):
205
+ return True
206
+ return any(normalize(h) == normalized for h in SHHH_VERIFIER_CLASS_HASHES.values())
207
+
208
+
134
209
  # RPC Endpoints per Wallet Type
135
210
  WALLET_RPC_ENDPOINTS: dict[WalletType, str] = {
136
211
  WalletType.CHIPI: "https://starknet-mainnet.public.blastapi.io/rpc/v0_7",
137
212
  WalletType.READY: "https://cloud.argent-api.com/v1/starknet/mainnet/rpc/v0.7",
213
+ WalletType.SHHH: "https://starknet-mainnet.public.blastapi.io/rpc/v0_7",
138
214
  }
139
215
 
140
216
  # Paymaster Configuration
@@ -58,6 +58,34 @@ async def execute_paymaster_transaction(
58
58
  )
59
59
  raise ValueError(error_msg)
60
60
 
61
+ # SHHH V8.4 ShhhAccount wallets cannot use the legacy
62
+ # prepare-typed-data → sign → execute-sponsored-transaction
63
+ # flow because the Chipi paymaster cannot synthesize SHHH
64
+ # envelopes server-side. Route SHHH OEs through the
65
+ # execute-sponsored-raw forwarder which expects pre-signed
66
+ # calldata. The legacy flow below is unchanged for
67
+ # CHIPI / READY.
68
+ if wallet.wallet_type == WalletType.SHHH:
69
+ from .shhh.execute_paymaster_raw import execute_shhh_sponsored_raw
70
+ from .shhh.snip12_hash import Call as ShhhCall
71
+
72
+ shhh_calls = [
73
+ ShhhCall(
74
+ contract_address=c.get("contractAddress") or c.get("to"),
75
+ entrypoint=c["entrypoint"],
76
+ calldata=list(c.get("calldata") or []),
77
+ )
78
+ for c in calls
79
+ ]
80
+ return await execute_shhh_sponsored_raw(
81
+ client=client,
82
+ bearer_token=bearer_token,
83
+ wallet_public_key=wallet.public_key,
84
+ wallet_encrypted_private_key=wallet.encrypted_private_key,
85
+ encrypt_key=encrypt_key,
86
+ calls=shhh_calls,
87
+ )
88
+
61
89
  # Use READY RPC endpoint for now (matches TypeScript implementation)
62
90
  rpc_url = WALLET_RPC_ENDPOINTS[WalletType.READY]
63
91
  account_class_hash = WALLET_CLASS_HASHES[WalletType.READY]
@@ -168,6 +196,33 @@ def execute_paymaster_transaction_sync(
168
196
  )
169
197
  raise ValueError(error_msg)
170
198
 
199
+ # SHHH parity for the sync API. Sync callers with WalletType.SHHH
200
+ # would otherwise fall into the legacy prepare-typed-data flow,
201
+ # which the paymaster cannot synthesize an envelope for and
202
+ # returns TRANSACTION_EXECUTION_ERROR. Mirror the async branch
203
+ # above via execute_shhh_sponsored_raw_sync (shared payload
204
+ # construction, sync HTTP).
205
+ if wallet.wallet_type == WalletType.SHHH:
206
+ from .shhh.execute_paymaster_raw import execute_shhh_sponsored_raw_sync
207
+ from .shhh.snip12_hash import Call as ShhhCall
208
+
209
+ shhh_calls = [
210
+ ShhhCall(
211
+ contract_address=c.get("contractAddress") or c.get("to"),
212
+ entrypoint=c["entrypoint"],
213
+ calldata=list(c.get("calldata") or []),
214
+ )
215
+ for c in calls
216
+ ]
217
+ return execute_shhh_sponsored_raw_sync(
218
+ client=client,
219
+ bearer_token=bearer_token,
220
+ wallet_public_key=wallet.public_key,
221
+ wallet_encrypted_private_key=wallet.encrypted_private_key,
222
+ encrypt_key=encrypt_key,
223
+ calls=shhh_calls,
224
+ )
225
+
171
226
  # Use READY account class hash (matches TypeScript implementation)
172
227
  account_class_hash = WALLET_CLASS_HASHES[WalletType.READY]
173
228
 
@@ -260,10 +315,15 @@ async def execute_paymaster_transaction_with_session(
260
315
  else:
261
316
  session = session_dict
262
317
 
263
- # Validate this is a CHIPI wallet
264
- if wallet.wallet_type and wallet.wallet_type != WalletType.CHIPI:
318
+ # Accept CHIPI v29 + SHHH V8.4 (both embed SNIP-163 sessions;
319
+ # the 4-felt session sig fast path at account.cairo:389 runs
320
+ # the same on either class).
321
+ if wallet.wallet_type and wallet.wallet_type not in (
322
+ WalletType.CHIPI,
323
+ WalletType.SHHH,
324
+ ):
265
325
  raise ChipiSessionError(
266
- f"Session keys only work with CHIPI wallets. Got: {wallet.wallet_type}",
326
+ f"Session execution requires CHIPI or SHHH wallet type. Got: {wallet.wallet_type}",
267
327
  SESSION_ERRORS["INVALID_WALLET_TYPE_FOR_SESSION"],
268
328
  )
269
329
 
@@ -275,6 +335,34 @@ async def execute_paymaster_transaction_with_session(
275
335
  if not session_private_key:
276
336
  raise ValueError("Failed to decrypt session private key")
277
337
 
338
+ # SHHH branch — route through V8.4 OE path with the 4-felt
339
+ # session sig (bypasses chipi-back's CHIPI-shaped
340
+ # prepare-typed-data + execute-sponsored-transaction). Mirrors
341
+ # the TypeScript SHHH branch shipped in sdks#288.
342
+ if wallet.wallet_type == WalletType.SHHH:
343
+ from .shhh.execute_paymaster_raw import (
344
+ execute_shhh_session_sponsored_raw,
345
+ )
346
+ from .shhh.snip12_hash import Call as ShhhCall
347
+
348
+ shhh_calls = [
349
+ ShhhCall(
350
+ contract_address=c.get("contractAddress") or c.get("to"),
351
+ entrypoint=c["entrypoint"],
352
+ calldata=list(c.get("calldata") or []),
353
+ )
354
+ for c in calls
355
+ ]
356
+ return await execute_shhh_session_sponsored_raw(
357
+ client=client,
358
+ bearer_token=bearer_token,
359
+ wallet_public_key=wallet.public_key,
360
+ session_private_key=session_private_key,
361
+ session_public_key=session.public_key,
362
+ session_valid_until=session.valid_until,
363
+ calls=shhh_calls,
364
+ )
365
+
278
366
  # Use CHIPI RPC endpoint and class hash
279
367
  account_class_hash = WALLET_CLASS_HASHES[WalletType.CHIPI]
280
368
 
@@ -375,13 +463,47 @@ def execute_paymaster_transaction_with_session_sync(
375
463
  else:
376
464
  session = session_dict
377
465
 
378
- # Validate this is a CHIPI wallet
379
- if wallet.wallet_type and wallet.wallet_type != WalletType.CHIPI:
466
+ # Accept CHIPI v29 + SHHH V8.4 — see async path above for details.
467
+ if wallet.wallet_type and wallet.wallet_type not in (
468
+ WalletType.CHIPI,
469
+ WalletType.SHHH,
470
+ ):
380
471
  raise ChipiSessionError(
381
- f"Session keys only work with CHIPI wallets. Got: {wallet.wallet_type}",
472
+ f"Session execution requires CHIPI or SHHH wallet type. Got: {wallet.wallet_type}",
382
473
  SESSION_ERRORS["INVALID_WALLET_TYPE_FOR_SESSION"],
383
474
  )
384
475
 
476
+ # SHHH branch (sync) — same shape as the async path.
477
+ if wallet.wallet_type == WalletType.SHHH:
478
+ session_private_key_sync = decrypt_private_key(
479
+ session.encrypted_private_key, params["encryptKey"]
480
+ )
481
+ if not session_private_key_sync:
482
+ raise ValueError("Failed to decrypt session private key")
483
+
484
+ from .shhh.execute_paymaster_raw import (
485
+ execute_shhh_session_sponsored_raw_sync,
486
+ )
487
+ from .shhh.snip12_hash import Call as ShhhCall
488
+
489
+ shhh_calls = [
490
+ ShhhCall(
491
+ contract_address=c.get("contractAddress") or c.get("to"),
492
+ entrypoint=c["entrypoint"],
493
+ calldata=list(c.get("calldata") or []),
494
+ )
495
+ for c in calls
496
+ ]
497
+ return execute_shhh_session_sponsored_raw_sync(
498
+ client=client,
499
+ bearer_token=bearer_token,
500
+ wallet_public_key=wallet.public_key,
501
+ session_private_key=session_private_key_sync,
502
+ session_public_key=session.public_key,
503
+ session_valid_until=session.valid_until,
504
+ calls=shhh_calls,
505
+ )
506
+
385
507
  # Use CHIPI account class hash
386
508
  account_class_hash = WALLET_CLASS_HASHES[WalletType.CHIPI]
387
509
 
@@ -23,6 +23,7 @@ from .wallet import (
23
23
  GetWalletResponse,
24
24
  PasskeyMetadata,
25
25
  PrepareWalletCreationResponse,
26
+ ShhhSignerKind,
26
27
  WalletData,
27
28
  WalletType,
28
29
  )
@@ -101,6 +102,7 @@ __all__ = [
101
102
  "GetWalletResponse",
102
103
  "PasskeyMetadata",
103
104
  "PrepareWalletCreationResponse",
105
+ "ShhhSignerKind",
104
106
  "WalletData",
105
107
  "WalletType",
106
108
  # Transaction
@@ -3,33 +3,65 @@
3
3
  from datetime import datetime
4
4
  from enum import Enum
5
5
  from typing import Optional
6
- from pydantic import BaseModel, Field, ConfigDict
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field
7
8
 
8
9
  from .core import Chain, ChainToken
9
10
 
10
11
 
11
12
  def to_camel(string: str) -> str:
12
13
  """Convert snake_case to camelCase."""
13
- components = string.split('_')
14
- return components[0] + ''.join(x.title() for x in components[1:])
14
+ components = string.split("_")
15
+ return components[0] + "".join(x.title() for x in components[1:])
15
16
 
16
17
 
17
18
  class WalletType(str, Enum):
18
19
  """
19
20
  Supported wallet types.
20
-
21
- - CHIPI: OpenZeppelin account with SNIP-9 session keys support (default)
22
- - READY: Argent X Account v0.4.0
21
+
22
+ - CHIPI: OpenZeppelin account with SNIP-9 session keys support (legacy v29).
23
+ - READY: Argent X Account v0.4.0.
24
+ - SHHH (default post-P2.2): V8.4 ShhhAccount — pluggable-signer
25
+ Starknet account dispatching to one of 10 verifier classes (STARK,
26
+ ED25519, SECP256K1, EIP191/EIP712, P256, WEBAUTHN_P256, JWT_ES256,
27
+ JWT_ES256_APPLE_SUB, BLS12_381). Requires CHIPI_SHHH_ENABLED on
28
+ the backend.
23
29
  """
30
+
24
31
  CHIPI = "CHIPI"
25
32
  READY = "READY"
33
+ SHHH = "SHHH"
34
+
35
+
36
+ class ShhhSignerKind(str, Enum):
37
+ """
38
+ SHHH signer kinds (V8.4 ShhhAccount). The `kind_tag` value the account
39
+ uses to dispatch `verify` to the matching verifier class via
40
+ library_call_syscall. See SHHH_VERIFIER_CLASS_HASHES and
41
+ SHHH_KIND_GAS_OVERHEAD in chipi_sdk.constants.
42
+
43
+ No WebAuthn signer in Python — server-side only; WebAuthn requires a
44
+ browser/RN context. Wallet creation with wallet_type=SHHH and no
45
+ signer_kind raises in Python (per plan D10).
46
+ """
47
+
48
+ STARK = "STARK"
49
+ ED25519 = "ED25519"
50
+ SECP256K1 = "SECP256K1"
51
+ EIP191_SECP256K1 = "EIP191_SECP256K1"
52
+ EIP712_SECP256K1 = "EIP712_SECP256K1"
53
+ P256 = "P256"
54
+ WEBAUTHN_P256 = "WEBAUTHN_P256"
55
+ JWT_ES256 = "JWT_ES256"
56
+ JWT_ES256_APPLE_SUB = "JWT_ES256_APPLE_SUB"
57
+ BLS12_381 = "BLS12_381"
26
58
 
27
59
 
28
60
  class WalletData(BaseModel):
29
61
  """Core wallet data structure."""
30
-
62
+
31
63
  model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
32
-
64
+
33
65
  public_key: str = Field(..., description="Wallet public address")
34
66
  encrypted_private_key: str = Field(..., description="AES encrypted private key")
35
67
  wallet_type: Optional[WalletType] = Field(None, description="Type of wallet")
@@ -38,7 +70,7 @@ class WalletData(BaseModel):
38
70
 
39
71
  class DeploymentData(BaseModel):
40
72
  """Contract deployment data."""
41
-
73
+
42
74
  class_hash: str
43
75
  salt: str
44
76
  unique: str
@@ -47,12 +79,44 @@ class DeploymentData(BaseModel):
47
79
 
48
80
  class CreateWalletParams(BaseModel):
49
81
  """Parameters for creating a new wallet."""
50
-
82
+
51
83
  encrypt_key: Optional[str] = Field(None, description="Encryption key for private key")
52
84
  external_user_id: str = Field(..., description="External user identifier")
53
85
  user_id: Optional[str] = Field(None, description="Internal user ID")
54
- wallet_type: Optional[WalletType] = Field(WalletType.CHIPI, description="Type of wallet to create")
86
+ # P2.2 default flip (re-applied 2026-05-29 after #270 revert and the
87
+ # post-revert SHHH bug-fix run). Mirrors the TS SDK default and
88
+ # matches every production mainnet smoke from 2026-05-26 onward.
89
+ # Callers who explicitly pass wallet_type=CHIPI / READY are
90
+ # unaffected.
91
+ wallet_type: Optional[WalletType] = Field(
92
+ WalletType.SHHH,
93
+ description="Type of wallet to create (defaults to SHHH V8.4 post-P2.2)",
94
+ )
55
95
  use_passkey: Optional[bool] = Field(False, description="Whether to use passkey authentication")
96
+ signer_kind: Optional[ShhhSignerKind] = Field(
97
+ None,
98
+ description=(
99
+ "Required when wallet_type=SHHH is set EXPLICITLY. When SHHH "
100
+ "is reached via the default-flip (caller omitted wallet_type), "
101
+ "the SDK falls back to STARK to match the TS SDK and the live "
102
+ "on-chain smokes. Supported deploy kinds: STARK + ED25519 "
103
+ "(Chipi-held key), WEBAUTHN_P256 + EIP191_SECP256K1 "
104
+ "(external-rooted — supply owner_pubkey_felts). "
105
+ "JWT_ES256_APPLE_SUB and the remaining kinds raise "
106
+ "NotImplementedError at create time."
107
+ ),
108
+ )
109
+ owner_pubkey_felts: Optional[list[str]] = Field(
110
+ None,
111
+ description=(
112
+ "External-rooted SHHH only. The owner's public key as Cairo "
113
+ "felts (hex strings) when Chipi does NOT hold the signing key: "
114
+ "WEBAUTHN_P256 / EIP191_SECP256K1 → [x_low, x_high, y_low, "
115
+ "y_high] (4 felts); external ED25519 (Phantom) → [low, high] "
116
+ "(2 felts). Omit for STARK and Chipi-held ED25519 (the SDK "
117
+ "generates the key)."
118
+ ),
119
+ )
56
120
 
57
121
 
58
122
  class GetWalletResponse(BaseModel):
@@ -87,7 +151,7 @@ CreateWalletResponse = GetWalletResponse
87
151
 
88
152
  class GetWalletParams(BaseModel):
89
153
  """Parameters for retrieving a wallet."""
90
-
154
+
91
155
  external_user_id: str = Field(..., description="External user identifier")
92
156
 
93
157
 
@@ -104,9 +168,9 @@ class GetTokenBalanceParams(BaseModel):
104
168
 
105
169
  class GetTokenBalanceResponse(BaseModel):
106
170
  """Response from token balance query."""
107
-
171
+
108
172
  model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
109
-
173
+
110
174
  chain: Chain
111
175
  chain_token: ChainToken
112
176
  chain_token_address: str
@@ -116,16 +180,16 @@ class GetTokenBalanceResponse(BaseModel):
116
180
 
117
181
  class PrepareWalletCreationResponse(BaseModel):
118
182
  """Response from wallet creation preparation."""
119
-
183
+
120
184
  model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)
121
-
185
+
122
186
  typed_data: dict
123
187
  account_class_hash: str
124
188
 
125
189
 
126
190
  class CreateCustodialWalletParams(BaseModel):
127
191
  """Parameters for creating a custodial wallet."""
128
-
192
+
129
193
  chain: Chain
130
194
  org_id: str
131
195
 
@@ -145,7 +209,9 @@ class PrepareWalletUpgradeParams(BaseModel):
145
209
  """Parameters for preparing a wallet upgrade."""
146
210
 
147
211
  wallet_address: str = Field(..., description="Wallet address to upgrade")
148
- target_class_hash: Optional[str] = Field(None, description="Target class hash (defaults to latest CHIPI)")
212
+ target_class_hash: Optional[str] = Field(
213
+ None, description="Target class hash (defaults to latest CHIPI)"
214
+ )
149
215
 
150
216
 
151
217
  class PrepareWalletUpgradeResponse(BaseModel):