cryptils 0.1.0a1__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.
cryptils/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ from .btc import BTC
2
+ from .eth import ETH
3
+ from .usdc import USDC
4
+ from .usdt import USDT
5
+
6
+ __all__ = ["BTC", "ETH", "USDC", "USDT"]
cryptils/_core.py ADDED
@@ -0,0 +1,128 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABCMeta
4
+ from decimal import Decimal
5
+ from typing import Any, ClassVar, TypeAlias
6
+
7
+ _number_or_str: TypeAlias = Decimal | int | float | str
8
+
9
+
10
+ class CryptoAmount(metaclass=ABCMeta): # noqa: B024
11
+ _name: ClassVar[str] = ""
12
+ _symbol: ClassVar[str] = ""
13
+ _decimals: ClassVar[int] = 0
14
+
15
+ @property
16
+ def name(self) -> str:
17
+ return self._name
18
+
19
+ @property
20
+ def symbol(self) -> str:
21
+ return self._symbol
22
+
23
+ def __init__(self, value: _number_or_str) -> None:
24
+ if type(self) is CryptoAmount:
25
+ raise TypeError("CryptoAmount is an abstract class and cannot be instantiated directly")
26
+ self._value = self._to_decimal(value)
27
+
28
+ def _to_decimal(self, value: _number_or_str) -> Decimal:
29
+ return Decimal(value).quantize(Decimal(10) ** -self._decimals)
30
+
31
+ def as_decimal(self) -> Decimal:
32
+ return self._value
33
+
34
+ def to_string(self):
35
+ return f"{self._value:.{self._decimals}f} {self._symbol}"
36
+
37
+ def __str__(self) -> str:
38
+ return str(self._value)
39
+
40
+ def __repr__(self) -> str:
41
+ return f"{self.__class__.__name__}({self.to_string()})"
42
+
43
+ def _is_compatible(self, other: Any) -> bool:
44
+ return isinstance(other, _number_or_str)
45
+
46
+ def _compare(self, other: Any) -> Decimal:
47
+ if isinstance(other, self.__class__):
48
+ return other._value
49
+ if self._is_compatible(other):
50
+ return self._to_decimal(other)
51
+ return NotImplemented
52
+
53
+ def __eq__(self, other: Any) -> bool:
54
+ other_value = self._compare(other)
55
+ if other_value is NotImplemented:
56
+ return NotImplemented
57
+ return self._value == other_value
58
+
59
+ def __lt__(self, other: Any) -> bool:
60
+ other_value = self._compare(other)
61
+ if other_value is NotImplemented:
62
+ return NotImplemented
63
+ return self._value < other_value
64
+
65
+ def __le__(self, other: Any) -> bool:
66
+ other_value = self._compare(other)
67
+ if other_value is NotImplemented:
68
+ return NotImplemented
69
+ return self._value <= other_value
70
+
71
+ def __gt__(self, other: Any) -> bool:
72
+ other_value = self._compare(other)
73
+ if other_value is NotImplemented:
74
+ return NotImplemented
75
+ return self._value > other_value
76
+
77
+ def __ge__(self, other: Any) -> bool:
78
+ other_value = self._compare(other)
79
+ if other_value is NotImplemented:
80
+ return NotImplemented
81
+ return self._value >= other_value
82
+
83
+ def __hash__(self) -> int:
84
+ return hash((self.__class__, self._value))
85
+
86
+ def __add__(self, other: Any) -> CryptoAmount:
87
+ if isinstance(other, self.__class__):
88
+ return self.__class__(self._value + other._value)
89
+ if self._is_compatible(other):
90
+ return self.__class__(self._value + self._to_decimal(other))
91
+ return NotImplemented
92
+
93
+ def __radd__(self, other: Any) -> CryptoAmount:
94
+ return self.__add__(other)
95
+
96
+ def __sub__(self, other: Any) -> CryptoAmount:
97
+ if isinstance(other, self.__class__):
98
+ return self.__class__(self._value - other._value)
99
+ if self._is_compatible(other):
100
+ return self.__class__(self._value - self._to_decimal(other))
101
+ return NotImplemented
102
+
103
+ def __rsub__(self, other: Any) -> CryptoAmount:
104
+ if self._is_compatible(other):
105
+ return self.__class__(self._to_decimal(other) - self._value)
106
+ return NotImplemented
107
+
108
+ def __mul__(self, other: Any) -> CryptoAmount:
109
+ if isinstance(other, self.__class__):
110
+ raise TypeError(f"Cannot multiply two {type(self).__name__} instances")
111
+ if self._is_compatible(other):
112
+ return self.__class__(self._value * self._to_decimal(other))
113
+ return NotImplemented
114
+
115
+ def __rmul__(self, other: Any) -> CryptoAmount:
116
+ return self.__mul__(other)
117
+
118
+ def __truediv__(self, other: Any) -> CryptoAmount:
119
+ if isinstance(other, self.__class__):
120
+ raise TypeError(f"Cannot divide two {type(self).__name__} instances")
121
+ if self._is_compatible(other):
122
+ return self.__class__(self._value / self._to_decimal(other))
123
+ return NotImplemented
124
+
125
+ def __rtruediv__(self, other: Any) -> CryptoAmount:
126
+ if self._is_compatible(other):
127
+ return self.__class__(self._to_decimal(other) / self._value)
128
+ return NotImplemented
cryptils/btc.py ADDED
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from ._core import CryptoAmount
4
+
5
+
6
+ class BTC(CryptoAmount):
7
+ _name = "Bitcoin"
8
+ _symbol = "BTC"
9
+ _decimals = 8
cryptils/eth.py ADDED
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from ._core import CryptoAmount
4
+
5
+
6
+ class ETH(CryptoAmount):
7
+ _name = "Ethereum"
8
+ _symbol = "ETH"
9
+ _decimals = 18
cryptils/py.typed ADDED
@@ -0,0 +1 @@
1
+ # py.typed marker file for PEP 561
cryptils/usdc.py ADDED
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from ._core import CryptoAmount
4
+
5
+
6
+ class USDC(CryptoAmount):
7
+ _name = "USD Coin"
8
+ _symbol = "USDC"
9
+ _decimals = 6
cryptils/usdt.py ADDED
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from ._core import CryptoAmount
4
+
5
+
6
+ class USDT(CryptoAmount):
7
+ _name = "Tether"
8
+ _symbol = "USDT"
9
+ _decimals = 6
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.4
2
+ Name: cryptils
3
+ Version: 0.1.0a1
4
+ Summary: A simple python utility library for representing cryptocurrency amounts
5
+ Project-URL: Homepage, https://github.com/seba3c/cryptils
6
+ Project-URL: Repository, https://github.com/seba3c/cryptils
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Python: >=3.10
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest-cov>=4.0; extra == 'test'
19
+ Requires-Dist: pytest>=8.0; extra == 'test'
20
+ Requires-Dist: tox>=4.0; extra == 'test'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # cryptils
24
+
25
+ ![Tests](https://github.com/seba3c/cryptils/actions/workflows/tests.yml/badge.svg)
26
+ ![Coverage](https://img.shields.io/badge/coverage-90%25-brightgreen)
27
+ ![License](https://img.shields.io/badge/license-MIT-blue)
28
+ ![PyPI](https://img.shields.io/pypi/v/cryptils)
29
+ ![TestPyPI](https://img.shields.io/badge/testpypi-v0.1.0a1-blue)
30
+
31
+ An utility library for representing cryptocurrency amounts in Python.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install cryptils
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ from cryptils import BTC, ETH, USDC, USDT
43
+
44
+ # Create amounts with exact decimal precision
45
+ btc = BTC("1.5")
46
+ print(btc) # 1.50000000 BTC
47
+
48
+ eth = ETH("2.0")
49
+ print(eth) # 2.000000000000000000 ETH
50
+
51
+ # Arithmetic
52
+ result = BTC("0.5") + BTC("0.25")
53
+ print(result) # 0.75000000 BTC
54
+
55
+ # Different currencies maintain their own precision
56
+ usdc = USDC("100")
57
+ print(usdc) # 100.000000 USDC
58
+
59
+ # Access the raw Decimal value
60
+ print(btc.as_decimal()) # Decimal('1.50000000')
61
+
62
+ # Get the formatted string explicitly
63
+ print(btc.to_string()) # 1.50000000 BTC
64
+ ```
65
+
66
+ ## Features
67
+
68
+ - Uses `decimal.Decimal` internally to avoid floating-point errors.
69
+ - Consistent precision handling per currency (e.g., 8 decimals for BTC, 6 for USDC).
70
+ - Simple, explicit API designed for financial precision.
71
+
72
+ ## Development
73
+
74
+ This project uses [uv](https://docs.astral.sh/uv/) for environment management, [ruff](https://docs.astral.sh/ruff/) for linting and formatting, and [tox](https://tox.wiki/) for testing across Python versions.
75
+
76
+ ```bash
77
+ # Setup environment
78
+ uv sync
79
+
80
+ # Run tests
81
+ uv run pytest
82
+
83
+ # Run tests across all supported Python versions
84
+ uv run tox
85
+
86
+ # Format and lint
87
+ uv run ruff check --fix .
88
+ uv run ruff format .
89
+
90
+ # Install pre-commit hooks
91
+ uv run pre-commit install
92
+
93
+ # Run pre-commit on all files
94
+ uv run pre-commit run --all-files
95
+ ```
96
+
97
+ ## License
98
+
99
+ [MIT](./LICENSE)
@@ -0,0 +1,11 @@
1
+ cryptils/__init__.py,sha256=t-HZlI9wKlLaRr6F6U6AmIPdb4beZoU-XNonDIf0UjM,130
2
+ cryptils/_core.py,sha256=BGvQeLSLt4Y_cJ_SnZ86NPnhmB-XCicdpVMFgfReeE0,4433
3
+ cryptils/btc.py,sha256=0Tmbni5z6ZLFG5w_hEeLlG_lHG_5adyFU9Qfs4pBQmo,155
4
+ cryptils/eth.py,sha256=WMo_q88Or_BfqRvu6vkisyfw9V9rLncmZQzIKlfZeEk,157
5
+ cryptils/py.typed,sha256=-yl3TJziRnBGR4RQVDoLxLVJCMoslIXZL48QaM0ulXI,35
6
+ cryptils/usdc.py,sha256=YA-nUd0rbhoxPXMORNzw-FEgO1vFhFqy-KN0u0RWd2Q,158
7
+ cryptils/usdt.py,sha256=LEB_Zi_JaKjqgCzJWn1xKalldznfbU8glGmYdtz2trY,156
8
+ cryptils-0.1.0a1.dist-info/METADATA,sha256=qrMbt5kmgHelZDg6B0Lqwr6WJe2YDJ-GwLb4BqDEEo8,2672
9
+ cryptils-0.1.0a1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
10
+ cryptils-0.1.0a1.dist-info/licenses/LICENSE,sha256=8h7d-hCmWM6DvUzHq-_svNaXoTmtXLDUuBkBSH9eLIk,1078
11
+ cryptils-0.1.0a1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 cryptils contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.