hotstuff-python-sdk 0.0.1b3__tar.gz → 0.0.1b4__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 (38) hide show
  1. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/PKG-INFO +1 -1
  2. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/__init__.py +1 -1
  3. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/apis/exchange.py +11 -7
  4. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/utils/__init__.py +2 -1
  5. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/utils/signing.py +17 -1
  6. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/pyproject.toml +2 -5
  7. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/LICENSE +0 -0
  8. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/README.md +0 -0
  9. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/apis/__init__.py +0 -0
  10. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/apis/info.py +0 -0
  11. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/apis/subscription.py +0 -0
  12. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/exceptions.py +0 -0
  13. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/__init__.py +0 -0
  14. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/exchange/__init__.py +0 -0
  15. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/exchange/account.py +0 -0
  16. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/exchange/collateral.py +0 -0
  17. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/exchange/op_codes.py +0 -0
  18. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/exchange/trading.py +0 -0
  19. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/exchange/vault.py +0 -0
  20. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/info/__init__.py +0 -0
  21. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/info/account.py +0 -0
  22. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/info/explorer.py +0 -0
  23. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/info/global.py +0 -0
  24. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/info/market.py +0 -0
  25. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/info/vault.py +0 -0
  26. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/subscription/__init__.py +0 -0
  27. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/subscription/channels.py +0 -0
  28. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/methods/subscription/global.py +0 -0
  29. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/transports/__init__.py +0 -0
  30. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/transports/http.py +0 -0
  31. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/transports/websocket.py +0 -0
  32. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/types/__init__.py +0 -0
  33. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/types/clients.py +0 -0
  34. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/types/exchange.py +0 -0
  35. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/types/transports.py +0 -0
  36. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/utils/address.py +0 -0
  37. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/utils/endpoints.py +0 -0
  38. {hotstuff_python_sdk-0.0.1b3 → hotstuff_python_sdk-0.0.1b4}/hotstuff/utils/nonce.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hotstuff-python-sdk
3
- Version: 0.0.1b3
3
+ Version: 0.0.1b4
4
4
  Summary: Python SDK for interacting with Hotstuff L1
5
5
  License: MIT
6
6
  Keywords: hotstuff,trading,blockchain,defi,exchange
@@ -3,7 +3,7 @@
3
3
  A Python SDK for interacting with Hotstuff L1.
4
4
  """
5
5
 
6
- __version__ = "0.0.1-beta.3"
6
+ __version__ = "0.0.1-beta.4"
7
7
 
8
8
  # Transports
9
9
  from hotstuff.transports import HttpTransport, WebSocketTransport
@@ -2,7 +2,7 @@
2
2
  from typing import Optional, Any, Dict, Callable, Awaitable
3
3
  from eth_account import Account
4
4
 
5
- from hotstuff.utils import sign_action, NonceManager
5
+ from hotstuff.utils import sign_action, NonceManager, canonicalize_for_signing
6
6
  from hotstuff.methods.exchange import (
7
7
  trading as TM,
8
8
  account as AM,
@@ -480,28 +480,32 @@ class ExchangeClient:
480
480
  if "nonce" not in params or params["nonce"] is None:
481
481
  params["nonce"] = await self.nonce()
482
482
 
483
- # Sign the action
483
+ # Canonicalize key order so msgpack bytes match backend (fixes "invalid order signer"
484
+ # for cancelByOid / cancelByCloid when backend expects deterministic key order)
485
+ params_canonical = canonicalize_for_signing(params)
486
+
487
+ # Sign the action (sign_action also canonicalizes; we pass canonical for consistency)
484
488
  signature = sign_action(
485
489
  wallet=self.wallet,
486
- action=params,
490
+ action=params_canonical,
487
491
  tx_type=EXCHANGE_OP_CODES[action],
488
492
  is_testnet=self.transport.is_testnet,
489
493
  )
490
494
 
491
495
  if execute:
492
- # Send to server
496
+ # Send to server with same canonical payload we signed
493
497
  response = await self.transport.request(
494
498
  "exchange",
495
499
  {
496
500
  "action": {
497
- "data": params,
501
+ "data": params_canonical,
498
502
  "type": str(EXCHANGE_OP_CODES[action]),
499
503
  },
500
504
  "signature": signature,
501
- "nonce": params["nonce"],
505
+ "nonce": params_canonical["nonce"],
502
506
  },
503
507
  signal,
504
508
  )
505
509
  return response
506
510
 
507
- return {"params": params, "signature": signature}
511
+ return {"params": params_canonical, "signature": signature}
@@ -1,7 +1,7 @@
1
1
  """Utilities package."""
2
2
  from hotstuff.utils.endpoints import ENDPOINTS_URLS
3
3
  from hotstuff.utils.nonce import NonceManager
4
- from hotstuff.utils.signing import sign_action
4
+ from hotstuff.utils.signing import sign_action, canonicalize_for_signing
5
5
  from hotstuff.utils.address import validate_ethereum_address
6
6
  from hotstuff.methods.exchange.op_codes import EXCHANGE_OP_CODES
7
7
 
@@ -9,6 +9,7 @@ __all__ = [
9
9
  "ENDPOINTS_URLS",
10
10
  "NonceManager",
11
11
  "sign_action",
12
+ "canonicalize_for_signing",
12
13
  "validate_ethereum_address",
13
14
  "EXCHANGE_OP_CODES",
14
15
  ]
@@ -13,6 +13,20 @@ except ImportError:
13
13
  _USE_NEW_API = False
14
14
 
15
15
 
16
+ def canonicalize_for_signing(obj):
17
+ """
18
+ Return a copy of obj with all dict keys sorted alphabetically, recursively.
19
+ Use this before signing and in the request body so msgpack bytes are
20
+ deterministic and match backend verification (fixes "invalid order signer"
21
+ for cancelByOid / cancelByCloid when key order differs from backend).
22
+ """
23
+ if isinstance(obj, dict):
24
+ return {k: canonicalize_for_signing(v) for k, v in sorted(obj.items())}
25
+ if isinstance(obj, list):
26
+ return [canonicalize_for_signing(item) for item in obj]
27
+ return obj
28
+
29
+
16
30
  def sign_action(
17
31
  wallet: Account,
18
32
  action: dict,
@@ -34,8 +48,10 @@ def sign_action(
34
48
  Returns:
35
49
  str: The signature (hex string)
36
50
  """
51
+ # Canonicalize key order so msgpack bytes match backend (deterministic signing)
52
+ canonical_action = canonicalize_for_signing(action)
37
53
  # Encode action to msgpack
38
- action_bytes = msgpack.packb(action)
54
+ action_bytes = msgpack.packb(canonical_action)
39
55
 
40
56
  # Hash the payload
41
57
  payload_hash = keccak(action_bytes)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "hotstuff-python-sdk"
3
- version = "0.0.1-beta.3"
3
+ version = "0.0.1-beta.4"
4
4
  description = "Python SDK for interacting with Hotstuff L1"
5
5
  authors = ["hotstuff"]
6
6
  license = "MIT"
@@ -20,9 +20,7 @@ classifiers = [
20
20
  "Programming Language :: Python :: 3.11",
21
21
  "Programming Language :: Python :: 3.12",
22
22
  ]
23
- packages = [
24
- { include = "hotstuff" }
25
- ]
23
+ packages = [{ include = "hotstuff" }]
26
24
 
27
25
  [tool.poetry.dependencies]
28
26
  python = "^3.8"
@@ -61,4 +59,3 @@ testpaths = ["tests"]
61
59
  markers = [
62
60
  "integration: marks tests as integration tests (deselect with '-m \"not integration\"')",
63
61
  ]
64
-