opinion-clob-sdk 0.1.11__py3-none-any.whl → 0.1.12__py3-none-any.whl

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.

Potentially problematic release.


This version of opinion-clob-sdk might be problematic. Click here for more details.

Files changed (69) hide show
  1. opinion_clob_sdk/__init__.py +1 -1
  2. opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  3. opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  4. opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  5. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  6. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  7. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  8. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  9. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  10. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  11. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  12. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  13. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  14. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  15. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  16. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  17. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  18. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  19. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  20. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  21. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
  22. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +74 -17
  23. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +26 -0
  24. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/__init__.py +0 -0
  25. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contract_caller.py +390 -0
  26. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contracts/__init__.py +0 -0
  27. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contracts/conditional_tokens.py +707 -0
  28. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contracts/erc20.py +111 -0
  29. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/exception.py +11 -0
  30. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/__init__.py +0 -0
  31. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/__init__.py +0 -0
  32. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/base_builder.py +41 -0
  33. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/exception.py +2 -0
  34. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/order_builder.py +90 -0
  35. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/order_builder_test.py +40 -0
  36. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/constants.py +2 -0
  37. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/__init__.py +0 -0
  38. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/order.py +254 -0
  39. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/order_type.py +9 -0
  40. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/sides.py +8 -0
  41. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/signatures.py +8 -0
  42. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/signer.py +20 -0
  43. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +182 -0
  44. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/__init__.py +0 -0
  45. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/constants.py +19 -0
  46. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/eip712/__init__.py +176 -0
  47. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/enums.py +6 -0
  48. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/exceptions.py +94 -0
  49. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/multisend.py +347 -0
  50. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe.py +141 -0
  51. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/__init__.py +0 -0
  52. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/compatibility_fallback_handler_v1_3_0.py +327 -0
  53. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/multisend_v1_3_0.py +22 -0
  54. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/safe_v1_3_0.py +1035 -0
  55. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/utils.py +26 -0
  56. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_signature.py +364 -0
  57. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_test.py +37 -0
  58. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_tx.py +437 -0
  59. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/signatures.py +63 -0
  60. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/typing.py +17 -0
  61. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/utils.py +218 -0
  62. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/config.py +4 -0
  63. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/model.py +19 -0
  64. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/sdk.py +957 -0
  65. opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/verify_api_calls.py +135 -0
  66. {opinion_clob_sdk-0.1.11.dist-info → opinion_clob_sdk-0.1.12.dist-info}/METADATA +1 -1
  67. {opinion_clob_sdk-0.1.11.dist-info → opinion_clob_sdk-0.1.12.dist-info}/RECORD +69 -26
  68. {opinion_clob_sdk-0.1.11.dist-info → opinion_clob_sdk-0.1.12.dist-info}/WHEEL +0 -0
  69. {opinion_clob_sdk-0.1.11.dist-info → opinion_clob_sdk-0.1.12.dist-info}/top_level.txt +0 -0
@@ -29,6 +29,40 @@ def generate_seed() -> int:
29
29
  return round(now * random())
30
30
 
31
31
 
32
+ def round_to_significant_digits(value: int, n: int) -> int:
33
+ """
34
+ Round an integer to n significant digits.
35
+
36
+ Args:
37
+ value: The integer to round
38
+ n: Number of significant digits to keep
39
+
40
+ Returns:
41
+ The rounded integer
42
+
43
+ Example:
44
+ round_to_significant_digits(123456789, 6) -> 123457000
45
+ round_to_significant_digits(10000000000000000000, 6) -> 10000000000000000000
46
+ """
47
+ if value == 0:
48
+ return 0
49
+
50
+ # Get the magnitude (number of digits)
51
+ magnitude = len(str(abs(value)))
52
+
53
+ # If already n digits or fewer, return as-is
54
+ if magnitude <= n:
55
+ return value
56
+
57
+ # Calculate the divisor to round to n significant digits
58
+ divisor = 10 ** (magnitude - n)
59
+
60
+ # Round to n significant digits
61
+ rounded = round(value / divisor) * divisor
62
+
63
+ return int(rounded)
64
+
65
+
32
66
  def prepend_zx(in_str: str) -> str:
33
67
  """
34
68
  Prepend 0x to the input string if it is missing
@@ -79,36 +113,59 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
79
113
 
80
114
  if side == OrderSide.BUY:
81
115
  # For BUY: price = maker/taker
82
- # Goal: Calculate taker = floor(maker / price)
83
- # Then recalculated_maker = floor(taker * price)
116
+ # Goal: Calculate taker and maker with only 6 significant digits each
117
+ # to match the matching engine's precision requirement
84
118
  #
85
- # Note: Due to integer arithmetic, recalculated_maker may be slightly less than
86
- # the original maker_amount. This is expected and unavoidable with fixed precision.
119
+ # Strategy:
120
+ # 1. Round maker_amount to 6 significant digits
121
+ # 2. Calculate taker = round(maker_6digit / price) to 6 significant digits
122
+ # 3. Recalculate maker = round(taker_6digit * price) to verify precision
87
123
  #
88
- # This ensures: given (recalculated_maker, taker, price),
89
- # you can always verify: recalculated_maker = floor(taker * price)
124
+ # This ensures: maker_6digit / taker_6digit = price (exact match within 6 digits)
125
+
126
+ # Step 1: Round maker to 6 significant digits
127
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
90
128
 
91
- # Calculate taker amount: taker = floor(maker / price)
92
- exact_taker = maker_decimal / price_decimal
129
+ # Step 2: Calculate taker = maker / price
130
+ exact_taker = Decimal(str(maker_6digit)) / price_decimal
93
131
  taker_amount = int(exact_taker)
94
132
 
95
- # Recalculate maker = floor(taker * price) using exact Decimal arithmetic
96
- recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
133
+ # Step 3: Round taker to 6 significant digits
134
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
135
+
136
+ # Step 4: Recalculate maker using 6-digit taker to ensure precision
137
+ recalculated_maker_decimal = Decimal(str(taker_6digit)) * price_decimal
97
138
  recalculated_maker_amount = int(recalculated_maker_decimal)
98
139
 
140
+ # Step 5: Round recalculated maker to 6 significant digits
141
+ recalculated_maker_6digit = round_to_significant_digits(recalculated_maker_amount, 6)
142
+
143
+ # Use the 6-digit values
144
+ taker_amount = taker_6digit
145
+ recalculated_maker_amount = recalculated_maker_6digit
146
+
99
147
  else: # SELL
100
148
  # For SELL: price = taker/maker
101
- # We want: taker = floor(maker * price)
102
- # Maker stays the same
149
+ # Goal: Calculate taker and maker with only 6 significant digits each
150
+ #
151
+ # Strategy:
152
+ # 1. Round maker_amount to 6 significant digits
153
+ # 2. Calculate taker = round(maker_6digit * price) to 6 significant digits
154
+ # 3. This ensures: taker_6digit / maker_6digit = price (exact match within 6 digits)
103
155
 
104
- # Calculate exact taker = maker * price
105
- exact_taker = maker_decimal * price_decimal
156
+ # Step 1: Round maker to 6 significant digits
157
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
106
158
 
107
- # Round down to get integer taker
159
+ # Step 2: Calculate taker = maker * price
160
+ exact_taker = Decimal(str(maker_6digit)) * price_decimal
108
161
  taker_amount = int(exact_taker)
109
162
 
110
- # For SELL, maker amount doesn't change
111
- recalculated_maker_amount = int(maker_amount)
163
+ # Step 3: Round taker to 6 significant digits
164
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
165
+
166
+ # Use the 6-digit values
167
+ taker_amount = taker_6digit
168
+ recalculated_maker_amount = maker_6digit
112
169
 
113
170
  # Ensure amounts are at least 1
114
171
  taker_amount = int(max(1, taker_amount))
@@ -12,7 +12,7 @@ from opinion_clob_sdk.chain.exception import (
12
12
  InsufficientGasBalance
13
13
  )
14
14
 
15
- __version__ = "0.1.11"
15
+ __version__ = "0.1.12"
16
16
  __all__ = [
17
17
  "Client",
18
18
  "TopicStatus",
@@ -29,6 +29,40 @@ def generate_seed() -> int:
29
29
  return round(now * random())
30
30
 
31
31
 
32
+ def round_to_significant_digits(value: int, n: int) -> int:
33
+ """
34
+ Round an integer to n significant digits.
35
+
36
+ Args:
37
+ value: The integer to round
38
+ n: Number of significant digits to keep
39
+
40
+ Returns:
41
+ The rounded integer
42
+
43
+ Example:
44
+ round_to_significant_digits(123456789, 6) -> 123457000
45
+ round_to_significant_digits(10000000000000000000, 6) -> 10000000000000000000
46
+ """
47
+ if value == 0:
48
+ return 0
49
+
50
+ # Get the magnitude (number of digits)
51
+ magnitude = len(str(abs(value)))
52
+
53
+ # If already n digits or fewer, return as-is
54
+ if magnitude <= n:
55
+ return value
56
+
57
+ # Calculate the divisor to round to n significant digits
58
+ divisor = 10 ** (magnitude - n)
59
+
60
+ # Round to n significant digits
61
+ rounded = round(value / divisor) * divisor
62
+
63
+ return int(rounded)
64
+
65
+
32
66
  def prepend_zx(in_str: str) -> str:
33
67
  """
34
68
  Prepend 0x to the input string if it is missing
@@ -79,36 +113,59 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
79
113
 
80
114
  if side == OrderSide.BUY:
81
115
  # For BUY: price = maker/taker
82
- # Goal: Calculate taker = floor(maker / price)
83
- # Then recalculated_maker = floor(taker * price)
116
+ # Goal: Calculate taker and maker with only 6 significant digits each
117
+ # to match the matching engine's precision requirement
84
118
  #
85
- # Note: Due to integer arithmetic, recalculated_maker may be slightly less than
86
- # the original maker_amount. This is expected and unavoidable with fixed precision.
119
+ # Strategy:
120
+ # 1. Round maker_amount to 6 significant digits
121
+ # 2. Calculate taker = round(maker_6digit / price) to 6 significant digits
122
+ # 3. Recalculate maker = round(taker_6digit * price) to verify precision
87
123
  #
88
- # This ensures: given (recalculated_maker, taker, price),
89
- # you can always verify: recalculated_maker = floor(taker * price)
124
+ # This ensures: maker_6digit / taker_6digit = price (exact match within 6 digits)
125
+
126
+ # Step 1: Round maker to 6 significant digits
127
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
90
128
 
91
- # Calculate taker amount: taker = floor(maker / price)
92
- exact_taker = maker_decimal / price_decimal
129
+ # Step 2: Calculate taker = maker / price
130
+ exact_taker = Decimal(str(maker_6digit)) / price_decimal
93
131
  taker_amount = int(exact_taker)
94
132
 
95
- # Recalculate maker = floor(taker * price) using exact Decimal arithmetic
96
- recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
133
+ # Step 3: Round taker to 6 significant digits
134
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
135
+
136
+ # Step 4: Recalculate maker using 6-digit taker to ensure precision
137
+ recalculated_maker_decimal = Decimal(str(taker_6digit)) * price_decimal
97
138
  recalculated_maker_amount = int(recalculated_maker_decimal)
98
139
 
140
+ # Step 5: Round recalculated maker to 6 significant digits
141
+ recalculated_maker_6digit = round_to_significant_digits(recalculated_maker_amount, 6)
142
+
143
+ # Use the 6-digit values
144
+ taker_amount = taker_6digit
145
+ recalculated_maker_amount = recalculated_maker_6digit
146
+
99
147
  else: # SELL
100
148
  # For SELL: price = taker/maker
101
- # We want: taker = floor(maker * price)
102
- # Maker stays the same
149
+ # Goal: Calculate taker and maker with only 6 significant digits each
150
+ #
151
+ # Strategy:
152
+ # 1. Round maker_amount to 6 significant digits
153
+ # 2. Calculate taker = round(maker_6digit * price) to 6 significant digits
154
+ # 3. This ensures: taker_6digit / maker_6digit = price (exact match within 6 digits)
103
155
 
104
- # Calculate exact taker = maker * price
105
- exact_taker = maker_decimal * price_decimal
156
+ # Step 1: Round maker to 6 significant digits
157
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
106
158
 
107
- # Round down to get integer taker
159
+ # Step 2: Calculate taker = maker * price
160
+ exact_taker = Decimal(str(maker_6digit)) * price_decimal
108
161
  taker_amount = int(exact_taker)
109
162
 
110
- # For SELL, maker amount doesn't change
111
- recalculated_maker_amount = int(maker_amount)
163
+ # Step 3: Round taker to 6 significant digits
164
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
165
+
166
+ # Use the 6-digit values
167
+ taker_amount = taker_6digit
168
+ recalculated_maker_amount = maker_6digit
112
169
 
113
170
  # Ensure amounts are at least 1
114
171
  taker_amount = int(max(1, taker_amount))
@@ -12,7 +12,7 @@ from opinion_clob_sdk.chain.exception import (
12
12
  InsufficientGasBalance
13
13
  )
14
14
 
15
- __version__ = "0.1.11"
15
+ __version__ = "0.1.12"
16
16
  __all__ = [
17
17
  "Client",
18
18
  "TopicStatus",
@@ -29,6 +29,40 @@ def generate_seed() -> int:
29
29
  return round(now * random())
30
30
 
31
31
 
32
+ def round_to_significant_digits(value: int, n: int) -> int:
33
+ """
34
+ Round an integer to n significant digits.
35
+
36
+ Args:
37
+ value: The integer to round
38
+ n: Number of significant digits to keep
39
+
40
+ Returns:
41
+ The rounded integer
42
+
43
+ Example:
44
+ round_to_significant_digits(123456789, 6) -> 123457000
45
+ round_to_significant_digits(10000000000000000000, 6) -> 10000000000000000000
46
+ """
47
+ if value == 0:
48
+ return 0
49
+
50
+ # Get the magnitude (number of digits)
51
+ magnitude = len(str(abs(value)))
52
+
53
+ # If already n digits or fewer, return as-is
54
+ if magnitude <= n:
55
+ return value
56
+
57
+ # Calculate the divisor to round to n significant digits
58
+ divisor = 10 ** (magnitude - n)
59
+
60
+ # Round to n significant digits
61
+ rounded = round(value / divisor) * divisor
62
+
63
+ return int(rounded)
64
+
65
+
32
66
  def prepend_zx(in_str: str) -> str:
33
67
  """
34
68
  Prepend 0x to the input string if it is missing
@@ -79,36 +113,59 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
79
113
 
80
114
  if side == OrderSide.BUY:
81
115
  # For BUY: price = maker/taker
82
- # Goal: Calculate taker = floor(maker / price)
83
- # Then recalculated_maker = floor(taker * price)
116
+ # Goal: Calculate taker and maker with only 6 significant digits each
117
+ # to match the matching engine's precision requirement
84
118
  #
85
- # Note: Due to integer arithmetic, recalculated_maker may be slightly less than
86
- # the original maker_amount. This is expected and unavoidable with fixed precision.
119
+ # Strategy:
120
+ # 1. Round maker_amount to 6 significant digits
121
+ # 2. Calculate taker = round(maker_6digit / price) to 6 significant digits
122
+ # 3. Recalculate maker = round(taker_6digit * price) to verify precision
87
123
  #
88
- # This ensures: given (recalculated_maker, taker, price),
89
- # you can always verify: recalculated_maker = floor(taker * price)
124
+ # This ensures: maker_6digit / taker_6digit = price (exact match within 6 digits)
125
+
126
+ # Step 1: Round maker to 6 significant digits
127
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
90
128
 
91
- # Calculate taker amount: taker = floor(maker / price)
92
- exact_taker = maker_decimal / price_decimal
129
+ # Step 2: Calculate taker = maker / price
130
+ exact_taker = Decimal(str(maker_6digit)) / price_decimal
93
131
  taker_amount = int(exact_taker)
94
132
 
95
- # Recalculate maker = floor(taker * price) using exact Decimal arithmetic
96
- recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
133
+ # Step 3: Round taker to 6 significant digits
134
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
135
+
136
+ # Step 4: Recalculate maker using 6-digit taker to ensure precision
137
+ recalculated_maker_decimal = Decimal(str(taker_6digit)) * price_decimal
97
138
  recalculated_maker_amount = int(recalculated_maker_decimal)
98
139
 
140
+ # Step 5: Round recalculated maker to 6 significant digits
141
+ recalculated_maker_6digit = round_to_significant_digits(recalculated_maker_amount, 6)
142
+
143
+ # Use the 6-digit values
144
+ taker_amount = taker_6digit
145
+ recalculated_maker_amount = recalculated_maker_6digit
146
+
99
147
  else: # SELL
100
148
  # For SELL: price = taker/maker
101
- # We want: taker = floor(maker * price)
102
- # Maker stays the same
149
+ # Goal: Calculate taker and maker with only 6 significant digits each
150
+ #
151
+ # Strategy:
152
+ # 1. Round maker_amount to 6 significant digits
153
+ # 2. Calculate taker = round(maker_6digit * price) to 6 significant digits
154
+ # 3. This ensures: taker_6digit / maker_6digit = price (exact match within 6 digits)
103
155
 
104
- # Calculate exact taker = maker * price
105
- exact_taker = maker_decimal * price_decimal
156
+ # Step 1: Round maker to 6 significant digits
157
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
106
158
 
107
- # Round down to get integer taker
159
+ # Step 2: Calculate taker = maker * price
160
+ exact_taker = Decimal(str(maker_6digit)) * price_decimal
108
161
  taker_amount = int(exact_taker)
109
162
 
110
- # For SELL, maker amount doesn't change
111
- recalculated_maker_amount = int(maker_amount)
163
+ # Step 3: Round taker to 6 significant digits
164
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
165
+
166
+ # Use the 6-digit values
167
+ taker_amount = taker_6digit
168
+ recalculated_maker_amount = maker_6digit
112
169
 
113
170
  # Ensure amounts are at least 1
114
171
  taker_amount = int(max(1, taker_amount))
@@ -12,7 +12,7 @@ from opinion_clob_sdk.chain.exception import (
12
12
  InsufficientGasBalance
13
13
  )
14
14
 
15
- __version__ = "0.1.11"
15
+ __version__ = "0.1.12"
16
16
  __all__ = [
17
17
  "Client",
18
18
  "TopicStatus",
@@ -29,6 +29,40 @@ def generate_seed() -> int:
29
29
  return round(now * random())
30
30
 
31
31
 
32
+ def round_to_significant_digits(value: int, n: int) -> int:
33
+ """
34
+ Round an integer to n significant digits.
35
+
36
+ Args:
37
+ value: The integer to round
38
+ n: Number of significant digits to keep
39
+
40
+ Returns:
41
+ The rounded integer
42
+
43
+ Example:
44
+ round_to_significant_digits(123456789, 6) -> 123457000
45
+ round_to_significant_digits(10000000000000000000, 6) -> 10000000000000000000
46
+ """
47
+ if value == 0:
48
+ return 0
49
+
50
+ # Get the magnitude (number of digits)
51
+ magnitude = len(str(abs(value)))
52
+
53
+ # If already n digits or fewer, return as-is
54
+ if magnitude <= n:
55
+ return value
56
+
57
+ # Calculate the divisor to round to n significant digits
58
+ divisor = 10 ** (magnitude - n)
59
+
60
+ # Round to n significant digits
61
+ rounded = round(value / divisor) * divisor
62
+
63
+ return int(rounded)
64
+
65
+
32
66
  def prepend_zx(in_str: str) -> str:
33
67
  """
34
68
  Prepend 0x to the input string if it is missing
@@ -79,36 +113,59 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
79
113
 
80
114
  if side == OrderSide.BUY:
81
115
  # For BUY: price = maker/taker
82
- # Goal: Calculate taker = floor(maker / price)
83
- # Then recalculated_maker = floor(taker * price)
116
+ # Goal: Calculate taker and maker with only 6 significant digits each
117
+ # to match the matching engine's precision requirement
84
118
  #
85
- # Note: Due to integer arithmetic, recalculated_maker may be slightly less than
86
- # the original maker_amount. This is expected and unavoidable with fixed precision.
119
+ # Strategy:
120
+ # 1. Round maker_amount to 6 significant digits
121
+ # 2. Calculate taker = round(maker_6digit / price) to 6 significant digits
122
+ # 3. Recalculate maker = round(taker_6digit * price) to verify precision
87
123
  #
88
- # This ensures: given (recalculated_maker, taker, price),
89
- # you can always verify: recalculated_maker = floor(taker * price)
124
+ # This ensures: maker_6digit / taker_6digit = price (exact match within 6 digits)
125
+
126
+ # Step 1: Round maker to 6 significant digits
127
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
90
128
 
91
- # Calculate taker amount: taker = floor(maker / price)
92
- exact_taker = maker_decimal / price_decimal
129
+ # Step 2: Calculate taker = maker / price
130
+ exact_taker = Decimal(str(maker_6digit)) / price_decimal
93
131
  taker_amount = int(exact_taker)
94
132
 
95
- # Recalculate maker = floor(taker * price) using exact Decimal arithmetic
96
- recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
133
+ # Step 3: Round taker to 6 significant digits
134
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
135
+
136
+ # Step 4: Recalculate maker using 6-digit taker to ensure precision
137
+ recalculated_maker_decimal = Decimal(str(taker_6digit)) * price_decimal
97
138
  recalculated_maker_amount = int(recalculated_maker_decimal)
98
139
 
140
+ # Step 5: Round recalculated maker to 6 significant digits
141
+ recalculated_maker_6digit = round_to_significant_digits(recalculated_maker_amount, 6)
142
+
143
+ # Use the 6-digit values
144
+ taker_amount = taker_6digit
145
+ recalculated_maker_amount = recalculated_maker_6digit
146
+
99
147
  else: # SELL
100
148
  # For SELL: price = taker/maker
101
- # We want: taker = floor(maker * price)
102
- # Maker stays the same
149
+ # Goal: Calculate taker and maker with only 6 significant digits each
150
+ #
151
+ # Strategy:
152
+ # 1. Round maker_amount to 6 significant digits
153
+ # 2. Calculate taker = round(maker_6digit * price) to 6 significant digits
154
+ # 3. This ensures: taker_6digit / maker_6digit = price (exact match within 6 digits)
103
155
 
104
- # Calculate exact taker = maker * price
105
- exact_taker = maker_decimal * price_decimal
156
+ # Step 1: Round maker to 6 significant digits
157
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
106
158
 
107
- # Round down to get integer taker
159
+ # Step 2: Calculate taker = maker * price
160
+ exact_taker = Decimal(str(maker_6digit)) * price_decimal
108
161
  taker_amount = int(exact_taker)
109
162
 
110
- # For SELL, maker amount doesn't change
111
- recalculated_maker_amount = int(maker_amount)
163
+ # Step 3: Round taker to 6 significant digits
164
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
165
+
166
+ # Use the 6-digit values
167
+ taker_amount = taker_6digit
168
+ recalculated_maker_amount = maker_6digit
112
169
 
113
170
  # Ensure amounts are at least 1
114
171
  taker_amount = int(max(1, taker_amount))
@@ -12,7 +12,7 @@ from opinion_clob_sdk.chain.exception import (
12
12
  InsufficientGasBalance
13
13
  )
14
14
 
15
- __version__ = "0.1.11"
15
+ __version__ = "0.1.12"
16
16
  __all__ = [
17
17
  "Client",
18
18
  "TopicStatus",
@@ -29,6 +29,40 @@ def generate_seed() -> int:
29
29
  return round(now * random())
30
30
 
31
31
 
32
+ def round_to_significant_digits(value: int, n: int) -> int:
33
+ """
34
+ Round an integer to n significant digits.
35
+
36
+ Args:
37
+ value: The integer to round
38
+ n: Number of significant digits to keep
39
+
40
+ Returns:
41
+ The rounded integer
42
+
43
+ Example:
44
+ round_to_significant_digits(123456789, 6) -> 123457000
45
+ round_to_significant_digits(10000000000000000000, 6) -> 10000000000000000000
46
+ """
47
+ if value == 0:
48
+ return 0
49
+
50
+ # Get the magnitude (number of digits)
51
+ magnitude = len(str(abs(value)))
52
+
53
+ # If already n digits or fewer, return as-is
54
+ if magnitude <= n:
55
+ return value
56
+
57
+ # Calculate the divisor to round to n significant digits
58
+ divisor = 10 ** (magnitude - n)
59
+
60
+ # Round to n significant digits
61
+ rounded = round(value / divisor) * divisor
62
+
63
+ return int(rounded)
64
+
65
+
32
66
  def prepend_zx(in_str: str) -> str:
33
67
  """
34
68
  Prepend 0x to the input string if it is missing
@@ -79,36 +113,59 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
79
113
 
80
114
  if side == OrderSide.BUY:
81
115
  # For BUY: price = maker/taker
82
- # Goal: Calculate taker = floor(maker / price)
83
- # Then recalculated_maker = floor(taker * price)
116
+ # Goal: Calculate taker and maker with only 6 significant digits each
117
+ # to match the matching engine's precision requirement
84
118
  #
85
- # Note: Due to integer arithmetic, recalculated_maker may be slightly less than
86
- # the original maker_amount. This is expected and unavoidable with fixed precision.
119
+ # Strategy:
120
+ # 1. Round maker_amount to 6 significant digits
121
+ # 2. Calculate taker = round(maker_6digit / price) to 6 significant digits
122
+ # 3. Recalculate maker = round(taker_6digit * price) to verify precision
87
123
  #
88
- # This ensures: given (recalculated_maker, taker, price),
89
- # you can always verify: recalculated_maker = floor(taker * price)
124
+ # This ensures: maker_6digit / taker_6digit = price (exact match within 6 digits)
125
+
126
+ # Step 1: Round maker to 6 significant digits
127
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
90
128
 
91
- # Calculate taker amount: taker = floor(maker / price)
92
- exact_taker = maker_decimal / price_decimal
129
+ # Step 2: Calculate taker = maker / price
130
+ exact_taker = Decimal(str(maker_6digit)) / price_decimal
93
131
  taker_amount = int(exact_taker)
94
132
 
95
- # Recalculate maker = floor(taker * price) using exact Decimal arithmetic
96
- recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
133
+ # Step 3: Round taker to 6 significant digits
134
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
135
+
136
+ # Step 4: Recalculate maker using 6-digit taker to ensure precision
137
+ recalculated_maker_decimal = Decimal(str(taker_6digit)) * price_decimal
97
138
  recalculated_maker_amount = int(recalculated_maker_decimal)
98
139
 
140
+ # Step 5: Round recalculated maker to 6 significant digits
141
+ recalculated_maker_6digit = round_to_significant_digits(recalculated_maker_amount, 6)
142
+
143
+ # Use the 6-digit values
144
+ taker_amount = taker_6digit
145
+ recalculated_maker_amount = recalculated_maker_6digit
146
+
99
147
  else: # SELL
100
148
  # For SELL: price = taker/maker
101
- # We want: taker = floor(maker * price)
102
- # Maker stays the same
149
+ # Goal: Calculate taker and maker with only 6 significant digits each
150
+ #
151
+ # Strategy:
152
+ # 1. Round maker_amount to 6 significant digits
153
+ # 2. Calculate taker = round(maker_6digit * price) to 6 significant digits
154
+ # 3. This ensures: taker_6digit / maker_6digit = price (exact match within 6 digits)
103
155
 
104
- # Calculate exact taker = maker * price
105
- exact_taker = maker_decimal * price_decimal
156
+ # Step 1: Round maker to 6 significant digits
157
+ maker_6digit = round_to_significant_digits(maker_amount, 6)
106
158
 
107
- # Round down to get integer taker
159
+ # Step 2: Calculate taker = maker * price
160
+ exact_taker = Decimal(str(maker_6digit)) * price_decimal
108
161
  taker_amount = int(exact_taker)
109
162
 
110
- # For SELL, maker amount doesn't change
111
- recalculated_maker_amount = int(maker_amount)
163
+ # Step 3: Round taker to 6 significant digits
164
+ taker_6digit = round_to_significant_digits(taker_amount, 6)
165
+
166
+ # Use the 6-digit values
167
+ taker_amount = taker_6digit
168
+ recalculated_maker_amount = maker_6digit
112
169
 
113
170
  # Ensure amounts are at least 1
114
171
  taker_amount = int(max(1, taker_amount))
@@ -12,7 +12,7 @@ from opinion_clob_sdk.chain.exception import (
12
12
  InsufficientGasBalance
13
13
  )
14
14
 
15
- __version__ = "0.1.11"
15
+ __version__ = "0.1.12"
16
16
  __all__ = [
17
17
  "Client",
18
18
  "TopicStatus",