meshtrade 1.22.0__py3-none-any.whl → 1.23.1__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 meshtrade might be problematic. Click here for more details.
- meshtrade/compliance/client/v1/__init__.py +12 -0
- meshtrade/compliance/client/v1/client_roles.py +123 -0
- meshtrade/compliance/client/v1/service_meshpy.py +1 -0
- meshtrade/iam/api_user/v1/__init__.py +6 -0
- meshtrade/iam/api_user/v1/api_user_state_machine.py +104 -0
- meshtrade/iam/api_user/v1/service_meshpy.py +1 -0
- meshtrade/iam/group/v1/service_meshpy.py +1 -0
- meshtrade/iam/role/v1/__init__.py +16 -2
- meshtrade/iam/role/v1/role.py +162 -11
- meshtrade/iam/user/v1/service_meshpy.py +1 -0
- meshtrade/ledger/transaction/v1/__init__.py +4 -0
- meshtrade/ledger/transaction/v1/service_meshpy.py +2 -1
- meshtrade/ledger/transaction/v1/transaction_state_machine.py +75 -0
- meshtrade/reporting/account_report/v1/__init__.py +4 -0
- meshtrade/reporting/account_report/v1/income_entry.py +35 -0
- meshtrade/reporting/account_report/v1/service_meshpy.py +1 -0
- meshtrade/trading/limit_order/v1/service_meshpy.py +1 -0
- meshtrade/trading/market_order/v1/service_meshpy.py +1 -0
- meshtrade/type/v1/__init__.py +81 -11
- meshtrade/type/v1/amount.py +429 -5
- meshtrade/type/v1/decimal_built_in_conversions.py +8 -3
- meshtrade/type/v1/decimal_operations.py +354 -0
- meshtrade/type/v1/ledger.py +76 -1
- meshtrade/type/v1/token.py +144 -0
- meshtrade/wallet/account/v1/service_meshpy.py +1 -0
- {meshtrade-1.22.0.dist-info → meshtrade-1.23.1.dist-info}/METADATA +1 -1
- {meshtrade-1.22.0.dist-info → meshtrade-1.23.1.dist-info}/RECORD +29 -23
- {meshtrade-1.22.0.dist-info → meshtrade-1.23.1.dist-info}/WHEEL +0 -0
- {meshtrade-1.22.0.dist-info → meshtrade-1.23.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Decimal operations utility functions for Mesh API.
|
|
2
|
+
|
|
3
|
+
This module provides arithmetic, comparison, and utility functions for working
|
|
4
|
+
with protobuf Decimal messages, offering a Pythonic API for decimal arithmetic
|
|
5
|
+
with consistent precision handling.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from decimal import ROUND_HALF_UP
|
|
9
|
+
|
|
10
|
+
from .decimal_built_in_conversions import decimal_to_built_in
|
|
11
|
+
from .decimal_pb2 import Decimal
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def decimal_add(d1: Decimal | None, d2: Decimal | None) -> Decimal:
|
|
15
|
+
"""Add two Decimal values.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
d1: First decimal value (None treated as 0)
|
|
19
|
+
d2: Second decimal value (None treated as 0)
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
New Decimal containing the sum (d1 + d2)
|
|
23
|
+
|
|
24
|
+
None Safety:
|
|
25
|
+
None inputs are treated as zero
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
>>> d1 = Decimal(value="10.5")
|
|
29
|
+
>>> d2 = Decimal(value="20.3")
|
|
30
|
+
>>> result = decimal_add(d1, d2)
|
|
31
|
+
>>> result.value
|
|
32
|
+
'30.8'
|
|
33
|
+
"""
|
|
34
|
+
val1 = decimal_to_built_in(d1)
|
|
35
|
+
val2 = decimal_to_built_in(d2)
|
|
36
|
+
return Decimal(value=str(val1 + val2))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def decimal_sub(d1: Decimal | None, d2: Decimal | None) -> Decimal:
|
|
40
|
+
"""Subtract two Decimal values.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
d1: First decimal value (None treated as 0)
|
|
44
|
+
d2: Second decimal value (subtrahend, None treated as 0)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
New Decimal containing the difference (d1 - d2)
|
|
48
|
+
|
|
49
|
+
None Safety:
|
|
50
|
+
None inputs are treated as zero
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
>>> d1 = Decimal(value="100.5")
|
|
54
|
+
>>> d2 = Decimal(value="30.2")
|
|
55
|
+
>>> result = decimal_sub(d1, d2)
|
|
56
|
+
>>> result.value
|
|
57
|
+
'70.3'
|
|
58
|
+
"""
|
|
59
|
+
val1 = decimal_to_built_in(d1)
|
|
60
|
+
val2 = decimal_to_built_in(d2)
|
|
61
|
+
return Decimal(value=str(val1 - val2))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def decimal_mul(d1: Decimal | None, d2: Decimal | None) -> Decimal:
|
|
65
|
+
"""Multiply two Decimal values.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
d1: First decimal value (None treated as 0)
|
|
69
|
+
d2: Second decimal value (None treated as 0)
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
New Decimal containing the product (d1 * d2)
|
|
73
|
+
|
|
74
|
+
None Safety:
|
|
75
|
+
None inputs are treated as zero
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
>>> d1 = Decimal(value="10.5")
|
|
79
|
+
>>> d2 = Decimal(value="2")
|
|
80
|
+
>>> result = decimal_mul(d1, d2)
|
|
81
|
+
>>> result.value
|
|
82
|
+
'21.0'
|
|
83
|
+
"""
|
|
84
|
+
val1 = decimal_to_built_in(d1)
|
|
85
|
+
val2 = decimal_to_built_in(d2)
|
|
86
|
+
return Decimal(value=str(val1 * val2))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def decimal_div(d1: Decimal | None, d2: Decimal | None) -> Decimal:
|
|
90
|
+
"""Divide two Decimal values.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
d1: Dividend (None treated as 0)
|
|
94
|
+
d2: Divisor (must not be zero, None treated as 0)
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
New Decimal containing the quotient (d1 / d2)
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
ZeroDivisionError: If d2 is zero
|
|
101
|
+
|
|
102
|
+
None Safety:
|
|
103
|
+
None inputs are treated as zero (note: None d2 will raise ZeroDivisionError)
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
>>> d1 = Decimal(value="100")
|
|
107
|
+
>>> d2 = Decimal(value="4")
|
|
108
|
+
>>> result = decimal_div(d1, d2)
|
|
109
|
+
>>> result.value
|
|
110
|
+
'25'
|
|
111
|
+
"""
|
|
112
|
+
val1 = decimal_to_built_in(d1)
|
|
113
|
+
val2 = decimal_to_built_in(d2)
|
|
114
|
+
|
|
115
|
+
if val2 == 0:
|
|
116
|
+
raise ZeroDivisionError("cannot divide by zero")
|
|
117
|
+
|
|
118
|
+
return Decimal(value=str(val1 / val2))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def decimal_equal(d1: Decimal | None, d2: Decimal | None) -> bool:
|
|
122
|
+
"""Check if two Decimal values are equal.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
d1: First decimal value (None treated as 0)
|
|
126
|
+
d2: Second decimal value (None treated as 0)
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
True if d1 == d2, False otherwise
|
|
130
|
+
|
|
131
|
+
None Safety:
|
|
132
|
+
None inputs are treated as zero
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> d1 = Decimal(value="10.5")
|
|
136
|
+
>>> d2 = Decimal(value="10.5")
|
|
137
|
+
>>> decimal_equal(d1, d2)
|
|
138
|
+
True
|
|
139
|
+
"""
|
|
140
|
+
val1 = decimal_to_built_in(d1)
|
|
141
|
+
val2 = decimal_to_built_in(d2)
|
|
142
|
+
return val1 == val2
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def decimal_less_than(d1: Decimal | None, d2: Decimal | None) -> bool:
|
|
146
|
+
"""Check if first Decimal is less than second.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
d1: First decimal value (None treated as 0)
|
|
150
|
+
d2: Second decimal value (None treated as 0)
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
True if d1 < d2, False otherwise
|
|
154
|
+
|
|
155
|
+
None Safety:
|
|
156
|
+
None inputs are treated as zero
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
>>> d1 = Decimal(value="10.5")
|
|
160
|
+
>>> d2 = Decimal(value="20.3")
|
|
161
|
+
>>> decimal_less_than(d1, d2)
|
|
162
|
+
True
|
|
163
|
+
"""
|
|
164
|
+
val1 = decimal_to_built_in(d1)
|
|
165
|
+
val2 = decimal_to_built_in(d2)
|
|
166
|
+
return val1 < val2
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def decimal_less_than_or_equal(d1: Decimal | None, d2: Decimal | None) -> bool:
|
|
170
|
+
"""Check if first Decimal is less than or equal to second.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
d1: First decimal value (None treated as 0)
|
|
174
|
+
d2: Second decimal value (None treated as 0)
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
True if d1 <= d2, False otherwise
|
|
178
|
+
|
|
179
|
+
None Safety:
|
|
180
|
+
None inputs are treated as zero
|
|
181
|
+
|
|
182
|
+
Example:
|
|
183
|
+
>>> d1 = Decimal(value="10.5")
|
|
184
|
+
>>> d2 = Decimal(value="10.5")
|
|
185
|
+
>>> decimal_less_than_or_equal(d1, d2)
|
|
186
|
+
True
|
|
187
|
+
"""
|
|
188
|
+
val1 = decimal_to_built_in(d1)
|
|
189
|
+
val2 = decimal_to_built_in(d2)
|
|
190
|
+
return val1 <= val2
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def decimal_greater_than(d1: Decimal | None, d2: Decimal | None) -> bool:
|
|
194
|
+
"""Check if first Decimal is greater than second.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
d1: First decimal value (None treated as 0)
|
|
198
|
+
d2: Second decimal value (None treated as 0)
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
True if d1 > d2, False otherwise
|
|
202
|
+
|
|
203
|
+
None Safety:
|
|
204
|
+
None inputs are treated as zero
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
>>> d1 = Decimal(value="20.3")
|
|
208
|
+
>>> d2 = Decimal(value="10.5")
|
|
209
|
+
>>> decimal_greater_than(d1, d2)
|
|
210
|
+
True
|
|
211
|
+
"""
|
|
212
|
+
val1 = decimal_to_built_in(d1)
|
|
213
|
+
val2 = decimal_to_built_in(d2)
|
|
214
|
+
return val1 > val2
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def decimal_greater_than_or_equal(d1: Decimal | None, d2: Decimal | None) -> bool:
|
|
218
|
+
"""Check if first Decimal is greater than or equal to second.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
d1: First decimal value (None treated as 0)
|
|
222
|
+
d2: Second decimal value (None treated as 0)
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
True if d1 >= d2, False otherwise
|
|
226
|
+
|
|
227
|
+
None Safety:
|
|
228
|
+
None inputs are treated as zero
|
|
229
|
+
|
|
230
|
+
Example:
|
|
231
|
+
>>> d1 = Decimal(value="10.5")
|
|
232
|
+
>>> d2 = Decimal(value="10.5")
|
|
233
|
+
>>> decimal_greater_than_or_equal(d1, d2)
|
|
234
|
+
True
|
|
235
|
+
"""
|
|
236
|
+
val1 = decimal_to_built_in(d1)
|
|
237
|
+
val2 = decimal_to_built_in(d2)
|
|
238
|
+
return val1 >= val2
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def decimal_is_zero(d: Decimal | None) -> bool:
|
|
242
|
+
"""Check if Decimal value is zero.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
d: Decimal value to check (None treated as 0)
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
True if d == 0, False otherwise
|
|
249
|
+
|
|
250
|
+
None Safety:
|
|
251
|
+
None inputs are treated as zero (returns True)
|
|
252
|
+
|
|
253
|
+
Example:
|
|
254
|
+
>>> d = Decimal(value="0")
|
|
255
|
+
>>> decimal_is_zero(d)
|
|
256
|
+
True
|
|
257
|
+
>>> d2 = Decimal(value="0.001")
|
|
258
|
+
>>> decimal_is_zero(d2)
|
|
259
|
+
False
|
|
260
|
+
"""
|
|
261
|
+
val = decimal_to_built_in(d)
|
|
262
|
+
return val == 0
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def decimal_is_negative(d: Decimal | None) -> bool:
|
|
266
|
+
"""Check if Decimal value is negative (< 0).
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
d: Decimal value to check (None treated as 0)
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
True if d < 0, False if d >= 0
|
|
273
|
+
|
|
274
|
+
None Safety:
|
|
275
|
+
None inputs are treated as zero (returns False)
|
|
276
|
+
|
|
277
|
+
Example:
|
|
278
|
+
>>> d = Decimal(value="-10.5")
|
|
279
|
+
>>> decimal_is_negative(d)
|
|
280
|
+
True
|
|
281
|
+
>>> d2 = Decimal(value="10.5")
|
|
282
|
+
>>> decimal_is_negative(d2)
|
|
283
|
+
False
|
|
284
|
+
"""
|
|
285
|
+
val = decimal_to_built_in(d)
|
|
286
|
+
return val < 0
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def decimal_is_positive(d: Decimal | None) -> bool:
|
|
290
|
+
"""Check if Decimal value is positive (> 0).
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
d: Decimal value to check (None treated as 0)
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
True if d > 0, False if d <= 0
|
|
297
|
+
|
|
298
|
+
None Safety:
|
|
299
|
+
None inputs are treated as zero (returns False)
|
|
300
|
+
|
|
301
|
+
Example:
|
|
302
|
+
>>> d = Decimal(value="10.5")
|
|
303
|
+
>>> decimal_is_positive(d)
|
|
304
|
+
True
|
|
305
|
+
>>> d2 = Decimal(value="0")
|
|
306
|
+
>>> decimal_is_positive(d2)
|
|
307
|
+
False
|
|
308
|
+
"""
|
|
309
|
+
val = decimal_to_built_in(d)
|
|
310
|
+
return val > 0
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def decimal_round(d: Decimal | None, places: int) -> Decimal:
|
|
314
|
+
"""Round Decimal to specified number of decimal places.
|
|
315
|
+
|
|
316
|
+
If places < 0, it rounds the integer part to the nearest 10^(-places).
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
d: Decimal value to round (None treated as 0)
|
|
320
|
+
places: Number of decimal places (can be negative)
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
New Decimal containing the rounded value
|
|
324
|
+
|
|
325
|
+
None Safety:
|
|
326
|
+
None inputs are treated as zero
|
|
327
|
+
|
|
328
|
+
Example:
|
|
329
|
+
>>> d = Decimal(value="5.45")
|
|
330
|
+
>>> result = decimal_round(d, 1)
|
|
331
|
+
>>> result.value
|
|
332
|
+
'5.5'
|
|
333
|
+
>>> d2 = Decimal(value="545")
|
|
334
|
+
>>> result2 = decimal_round(d2, -1)
|
|
335
|
+
>>> result2.value
|
|
336
|
+
'550'
|
|
337
|
+
"""
|
|
338
|
+
from decimal import Decimal as PyDecimal
|
|
339
|
+
|
|
340
|
+
val = decimal_to_built_in(d)
|
|
341
|
+
|
|
342
|
+
if places < 0:
|
|
343
|
+
# For negative places, use shift-quantize-shift back approach
|
|
344
|
+
# to properly round the integer part
|
|
345
|
+
scale = PyDecimal(10) ** (-places)
|
|
346
|
+
shifted = val / scale
|
|
347
|
+
rounded_shifted = shifted.quantize(PyDecimal("1"), rounding=ROUND_HALF_UP)
|
|
348
|
+
rounded_val = rounded_shifted * scale
|
|
349
|
+
else:
|
|
350
|
+
# For positive places, quantize directly
|
|
351
|
+
quantizer = PyDecimal(10) ** -places
|
|
352
|
+
rounded_val = val.quantize(quantizer, rounding=ROUND_HALF_UP)
|
|
353
|
+
|
|
354
|
+
return Decimal(value=str(rounded_val))
|
meshtrade/type/v1/ledger.py
CHANGED
|
@@ -10,7 +10,7 @@ class UnsupportedLedgerError(Exception):
|
|
|
10
10
|
"""Exception raised for unsupported Ledger values."""
|
|
11
11
|
|
|
12
12
|
def __init__(self, ledger: Ledger):
|
|
13
|
-
self.
|
|
13
|
+
self.ledger = ledger
|
|
14
14
|
message = f"Unsupported Ledger: {ledger}"
|
|
15
15
|
super().__init__(message)
|
|
16
16
|
|
|
@@ -23,3 +23,78 @@ def get_ledger_no_decimal_places(ledger: Ledger) -> int:
|
|
|
23
23
|
return _ledger_decimal_places[ledger]
|
|
24
24
|
else:
|
|
25
25
|
raise UnsupportedLedgerError(ledger)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ledger_to_pretty_string(ledger: Ledger) -> str:
|
|
29
|
+
"""Convert the Ledger enum value to a human-readable network name.
|
|
30
|
+
|
|
31
|
+
This method provides user-friendly names for each blockchain network/ledger type.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
ledger: The Ledger enum value
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
A human-readable network name string, or "Unknown" for invalid/unrecognized values
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> ledger_to_pretty_string(Ledger.LEDGER_STELLAR)
|
|
41
|
+
'Stellar'
|
|
42
|
+
>>> ledger_to_pretty_string(Ledger.LEDGER_ETHEREUM)
|
|
43
|
+
'Ethereum'
|
|
44
|
+
>>> ledger_to_pretty_string(Ledger.LEDGER_UNSPECIFIED)
|
|
45
|
+
'Unspecified'
|
|
46
|
+
"""
|
|
47
|
+
if ledger == Ledger.LEDGER_STELLAR:
|
|
48
|
+
return "Stellar"
|
|
49
|
+
elif ledger == Ledger.LEDGER_ETHEREUM:
|
|
50
|
+
return "Ethereum"
|
|
51
|
+
elif ledger == Ledger.LEDGER_BITCOIN:
|
|
52
|
+
return "Bitcoin"
|
|
53
|
+
elif ledger == Ledger.LEDGER_LITECOIN:
|
|
54
|
+
return "Litecoin"
|
|
55
|
+
elif ledger == Ledger.LEDGER_XRP:
|
|
56
|
+
return "XRP"
|
|
57
|
+
elif ledger == Ledger.LEDGER_SA_STOCK_BROKERS:
|
|
58
|
+
return "SA Stock Brokers"
|
|
59
|
+
elif ledger == Ledger.LEDGER_NULL:
|
|
60
|
+
return "Null"
|
|
61
|
+
elif ledger == Ledger.LEDGER_UNSPECIFIED:
|
|
62
|
+
return "Unspecified"
|
|
63
|
+
else:
|
|
64
|
+
return "Unknown"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def ledger_is_valid(ledger: Ledger) -> bool:
|
|
68
|
+
"""Check if ledger value is valid (within enum range).
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
ledger: The Ledger enum value to check
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
True if the ledger is a valid enum value, False otherwise
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
>>> ledger_is_valid(Ledger.LEDGER_STELLAR)
|
|
78
|
+
True
|
|
79
|
+
>>> ledger_is_valid(999) # Invalid enum value
|
|
80
|
+
False
|
|
81
|
+
"""
|
|
82
|
+
return ledger in Ledger.values()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def ledger_is_valid_and_defined(ledger: Ledger) -> bool:
|
|
86
|
+
"""Check if ledger is valid and not UNSPECIFIED.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
ledger: The Ledger enum value to check
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
True if the ledger is valid and not LEDGER_UNSPECIFIED, False otherwise
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> ledger_is_valid_and_defined(Ledger.LEDGER_STELLAR)
|
|
96
|
+
True
|
|
97
|
+
>>> ledger_is_valid_and_defined(Ledger.LEDGER_UNSPECIFIED)
|
|
98
|
+
False
|
|
99
|
+
"""
|
|
100
|
+
return ledger_is_valid(ledger) and ledger != Ledger.LEDGER_UNSPECIFIED
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Token utility functions for Mesh API.
|
|
2
|
+
|
|
3
|
+
This module provides utility functions for working with Token protobuf messages,
|
|
4
|
+
including helper functions for token creation, validation, and formatting.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .ledger_pb2 import Ledger
|
|
8
|
+
from .token_pb2 import Token
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def new_undefined_token() -> Token:
|
|
12
|
+
"""Create a new Token representing an undefined or placeholder token.
|
|
13
|
+
|
|
14
|
+
The undefined token has:
|
|
15
|
+
- Code: "-"
|
|
16
|
+
- Issuer: "-"
|
|
17
|
+
- Ledger: LEDGER_UNSPECIFIED
|
|
18
|
+
|
|
19
|
+
This is useful as a sentinel value to represent the absence of a valid token
|
|
20
|
+
or as a placeholder in data structures.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
A Token configured as undefined
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
>>> token = new_undefined_token()
|
|
27
|
+
>>> token.code
|
|
28
|
+
'-'
|
|
29
|
+
>>> token.issuer
|
|
30
|
+
'-'
|
|
31
|
+
>>> token_is_undefined(token)
|
|
32
|
+
True
|
|
33
|
+
"""
|
|
34
|
+
return Token(
|
|
35
|
+
code="-",
|
|
36
|
+
issuer="-",
|
|
37
|
+
ledger=Ledger.LEDGER_UNSPECIFIED,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def token_is_undefined(token: Token | None) -> bool:
|
|
42
|
+
"""Check whether this token represents an undefined or placeholder token.
|
|
43
|
+
|
|
44
|
+
A token is considered undefined if:
|
|
45
|
+
- The token is None, OR
|
|
46
|
+
- The token has Code == "-" AND Issuer == "-" AND Ledger == LEDGER_UNSPECIFIED
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
token: Token to check (can be None)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
True if the token is undefined (None or matches undefined pattern), False otherwise
|
|
53
|
+
|
|
54
|
+
None Safety:
|
|
55
|
+
Returns True if token is None
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
>>> token = None
|
|
59
|
+
>>> token_is_undefined(token)
|
|
60
|
+
True
|
|
61
|
+
>>> token = new_undefined_token()
|
|
62
|
+
>>> token_is_undefined(token)
|
|
63
|
+
True
|
|
64
|
+
>>> defined = Token(code="USD", issuer="ISSUER", ledger=Ledger.LEDGER_STELLAR)
|
|
65
|
+
>>> token_is_undefined(defined)
|
|
66
|
+
False
|
|
67
|
+
"""
|
|
68
|
+
if token is None:
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
return token.code == "-" and token.issuer == "-" and token.ledger == Ledger.LEDGER_UNSPECIFIED
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def token_is_equal_to(token1: Token | None, token2: Token | None) -> bool:
|
|
75
|
+
"""Compare two tokens for equality.
|
|
76
|
+
|
|
77
|
+
Two tokens are considered equal if and only if all of the following match:
|
|
78
|
+
- Code (asset code)
|
|
79
|
+
- Issuer (asset issuer)
|
|
80
|
+
- Ledger (blockchain/ledger type)
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
token1: First token to compare (can be None)
|
|
84
|
+
token2: Second token to compare (can be None)
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
True if both tokens are equal (including both being None), False otherwise
|
|
88
|
+
|
|
89
|
+
None Safety:
|
|
90
|
+
Returns True if both are None, False if only one is None
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
>>> token1 = Token(code="USD", issuer="ISSUER1", ledger=Ledger.LEDGER_STELLAR)
|
|
94
|
+
>>> token2 = Token(code="USD", issuer="ISSUER1", ledger=Ledger.LEDGER_STELLAR)
|
|
95
|
+
>>> token_is_equal_to(token1, token2)
|
|
96
|
+
True
|
|
97
|
+
>>> token3 = Token(code="EUR", issuer="ISSUER1", ledger=Ledger.LEDGER_STELLAR)
|
|
98
|
+
>>> token_is_equal_to(token1, token3)
|
|
99
|
+
False
|
|
100
|
+
>>> token_is_equal_to(None, None)
|
|
101
|
+
True
|
|
102
|
+
"""
|
|
103
|
+
# Handle None cases
|
|
104
|
+
if token1 is None and token2 is None:
|
|
105
|
+
return True
|
|
106
|
+
if token1 is None or token2 is None:
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
return token1.code == token2.code and token1.issuer == token2.issuer and token1.ledger == token2.ledger
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def token_pretty_string(token: Token | None) -> str:
|
|
113
|
+
"""Return a human-readable string representation of the token.
|
|
114
|
+
|
|
115
|
+
Format: "CODE by ISSUER on NETWORK"
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
token: Token to format (can be None)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
A formatted string describing the token, or "undefined" if None/undefined
|
|
122
|
+
|
|
123
|
+
None Safety:
|
|
124
|
+
Returns "undefined" if token is None
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
>>> token = Token(code="USD", issuer="CIRCLE", ledger=Ledger.LEDGER_STELLAR)
|
|
128
|
+
>>> token_pretty_string(token)
|
|
129
|
+
'USD by CIRCLE on Stellar'
|
|
130
|
+
>>> token_pretty_string(None)
|
|
131
|
+
'undefined'
|
|
132
|
+
>>> token_pretty_string(new_undefined_token())
|
|
133
|
+
'undefined'
|
|
134
|
+
"""
|
|
135
|
+
if token is None:
|
|
136
|
+
return "undefined"
|
|
137
|
+
|
|
138
|
+
if token_is_undefined(token):
|
|
139
|
+
return "undefined"
|
|
140
|
+
|
|
141
|
+
# Import ledger utility for pretty printing
|
|
142
|
+
from .ledger import ledger_to_pretty_string
|
|
143
|
+
|
|
144
|
+
return f"{token.code} by {token.issuer} on {ledger_to_pretty_string(token.ledger)}"
|