Skip to content

Commit 860ca46

Browse files
authored
feat: add validator for trx addresses (#384)
- feat: add validator for trx addresses - bump version - fix minor issues in changelog
1 parent 4973c49 commit 860ca46

File tree

8 files changed

+144
-4
lines changed

8 files changed

+144
-4
lines changed

CHANGES.md

+20
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@ Note to self: Breaking changes must increment either
99
1010
-->
1111

12+
## 0.30.0 (2024-07-04)
13+
14+
_**Breaking**_
15+
16+
> No breaking changes were introduced in this version.
17+
18+
_**Features**_
19+
20+
- feat: add validator for trx addresses by @msamsami in [#384](https://github.com/python-validators/validators/pull/384)
21+
22+
_**Maintenance**_
23+
24+
- maint: bump version by @msamsami in [#384](https://github.com/python-validators/validators/pull/384)
25+
26+
**Full Changelog**: [`0.29.0...0.30.0`](https://github.com/python-validators/validators/compare/0.29.0...0.30.0)
27+
28+
---
29+
1230
## 0.29.0 (2024-07-01)
1331

1432
_**Breaking**_ ⚠️
@@ -25,6 +43,8 @@ _**Maintenance**_
2543

2644
**Full Changelog**: [`0.28.3...0.29.0`](https://github.com/python-validators/validators/compare/0.28.3...0.29.0)
2745

46+
---
47+
2848
## 0.28.3 (2024-05-25)
2949

3050
_**Breaking**_

SECURITY.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
| Version | Supported |
66
| ---------- | ------------------ |
7-
| `>=0.29.0` | :white_check_mark: |
7+
| `>=0.30.0` | :white_check_mark: |
88

99
## Reporting a Vulnerability
1010

docs/api/crypto_addresses.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
::: validators.crypto_addresses.btc_address
44
::: validators.crypto_addresses.eth_address
5+
::: validators.crypto_addresses.trx_address

docs/api/crypto_addresses.rst

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ crypto_addresses
44
.. module:: validators.crypto_addresses
55
.. autofunction:: btc_address
66
.. autofunction:: eth_address
7+
.. autofunction:: trx_address

src/validators/__init__.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .card import amex, card_number, diners, discover, jcb, mastercard, unionpay, visa
66
from .country import calling_code, country_code, currency
77
from .cron import cron
8-
from .crypto_addresses import btc_address, eth_address
8+
from .crypto_addresses import btc_address, eth_address, trx_address
99
from .domain import domain
1010
from .email import email
1111
from .encoding import base58, base64
@@ -39,6 +39,7 @@
3939
# crypto_addresses
4040
"btc_address",
4141
"eth_address",
42+
"trx_address",
4243
# cards
4344
"amex",
4445
"card_number",
@@ -104,4 +105,4 @@
104105
"validator",
105106
)
106107

107-
__version__ = "0.29.0"
108+
__version__ = "0.30.0"

src/validators/crypto_addresses/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
# local
44
from .btc_address import btc_address
55
from .eth_address import eth_address
6+
from .trx_address import trx_address
67

7-
__all__ = ("btc_address", "eth_address")
8+
__all__ = ("btc_address", "eth_address", "trx_address")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""TRX Address."""
2+
3+
# standard
4+
import hashlib
5+
import re
6+
7+
# local
8+
from validators.utils import validator
9+
10+
11+
def _base58_decode(addr: str) -> bytes:
12+
"""Decode a base58 encoded address."""
13+
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
14+
num = 0
15+
for char in addr:
16+
num = num * 58 + alphabet.index(char)
17+
return num.to_bytes(25, byteorder="big")
18+
19+
20+
def _validate_trx_checksum_address(addr: str) -> bool:
21+
"""Validate TRX type checksum address."""
22+
if len(addr) != 34:
23+
return False
24+
25+
try:
26+
address = _base58_decode(addr)
27+
except ValueError:
28+
return False
29+
30+
if len(address) != 25 or address[0] != 0x41:
31+
return False
32+
33+
check_sum = hashlib.sha256(hashlib.sha256(address[:-4]).digest()).digest()[:4]
34+
return address[-4:] == check_sum
35+
36+
37+
@validator
38+
def trx_address(value: str, /):
39+
"""Return whether or not given value is a valid tron address.
40+
41+
Full validation is implemented for TRC20 tron addresses.
42+
43+
Examples:
44+
>>> trx_address('TLjfbTbpZYDQ4EoA4N5CLNgGjfbF8ZWz38')
45+
# Output: True
46+
>>> trx_address('TR2G7Rm4vFqF8EpY4U5xdLdQ7XgJ2U8Vd')
47+
# Output: ValidationError(func=trx_address, args=...)
48+
49+
Args:
50+
value:
51+
Tron address string to validate.
52+
53+
Returns:
54+
(Literal[True]): If `value` is a valid tron address.
55+
(ValidationError): If `value` is an invalid tron address.
56+
"""
57+
if not value:
58+
return False
59+
60+
return re.compile(r"^[T][a-km-zA-HJ-NP-Z1-9]{33}$").match(
61+
value
62+
) and _validate_trx_checksum_address(value)
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Test TRX address."""
2+
3+
# external
4+
import pytest
5+
6+
# local
7+
from validators import ValidationError, trx_address
8+
9+
10+
@pytest.mark.parametrize(
11+
"value",
12+
[
13+
"TLjfbTbpZYDQ4EoA4N5CLNgGjfbF8ZWz38",
14+
"TDQ6C92wuNqvMWE967sMptCFaXq77uj1PF",
15+
"TFuGbxCQGSL4oLnJzVsen844LDwFbrUY4e",
16+
"TFAPKADDRhkSe3v27CsR8TZSjN8eJ8ycDK",
17+
"TSJHywLNva2MNjCD5iYfn5QAKD9Rk5Ncit",
18+
"TEi1qhi5LuTicg1u9oAstyXCSf5uibSyqo",
19+
"TAGvx5An6VBeHTu91cQwdABNcAYMRPcP4n",
20+
"TXbE5tXTejqT3Q47sYKCDb9NJDm3xrFpab",
21+
"TMTxQWNuWHXvHcYXc5D1wQhFmZFJijAxcG",
22+
"TPHgw9E8QYM3esNWih5KVnUVpUHwLTPfpA",
23+
"TFFLtBTi9jdaGwV3hznjCmPYaJme5AeqwU",
24+
"TC74QG8tbtixG5Raa4fEifywgjrFs45fNz",
25+
],
26+
)
27+
def test_returns_true_on_valid_trx_address(value: str):
28+
"""Test returns true on valid trx address."""
29+
assert trx_address(value)
30+
31+
32+
@pytest.mark.parametrize(
33+
"value",
34+
[
35+
"T12345678901234567890123456789012345",
36+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678",
37+
"TR2G7Rm4vFqF8EpY4U5xdLdQ7XgJ2U8Vd",
38+
"TP6ah2v5mdsj8Z3hGz1yDMvDq7BzEbK8o",
39+
"TQmmhp6uz2Xre8yL3FsPYZyo4mhtw4vg4XX",
40+
"TQNy2C6VHJPk4P32bsEX3QSGx2Qqm4J2k9",
41+
"TP6ah2v5mdsj8Z3hGz1yDMvDq7BzEbK8oN",
42+
"TSTVdfU1x4L7K3Bc3v5C28Gp2J1rPyeL3f",
43+
"THPByuCzvU5QER9j2NC2mUQ2JPyRCam4e7",
44+
"TW5eZqUZgdW4rxFKAKsc2ryJbfFA94WXvD",
45+
"TR2G7Rm4vFqF8EpY4U5xdLdQ7XgJ2U8Vdd",
46+
"tQmmhp6uz2Xre8yL3FsPYZyo4mhtw4vg4X",
47+
"TR2G7Rm4vFqF8EpY4U5xdLdQ7Xg",
48+
"TQmmhp6uz2Xre8yL3FsPYZyo4mhtw4vg4x",
49+
"my-trox-address.trx",
50+
],
51+
)
52+
def test_returns_failed_validation_on_invalid_trx_address(value: str):
53+
"""Test returns failed validation on invalid trx address."""
54+
assert isinstance(trx_address(value), ValidationError)

0 commit comments

Comments
 (0)