crypttrace 0.27.0__tar.gz → 0.30.0__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.
- {crypttrace-0.27.0/src/crypttrace.egg-info → crypttrace-0.30.0}/PKG-INFO +34 -16
- {crypttrace-0.27.0 → crypttrace-0.30.0}/README.md +31 -13
- {crypttrace-0.27.0 → crypttrace-0.30.0}/pyproject.toml +2 -2
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/__init__.py +1 -1
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/drain.py +20 -0
- crypttrace-0.30.0/src/crypttrace/labels/known.json +756 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/labels.py +60 -12
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/render.py +9 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/web/index.html +8 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0/src/crypttrace.egg-info}/PKG-INFO +34 -16
- crypttrace-0.27.0/src/crypttrace/labels/known.json +0 -416
- {crypttrace-0.27.0 → crypttrace-0.30.0}/LICENSE +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/setup.cfg +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/addresses.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/analysis.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/assess.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/assets.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/bridges.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/chains.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/cli.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/config.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/evidence.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/fetchers/__init__.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/fetchers/bitcoin.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/fetchers/etherscan.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/fetchers/http.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/fetchers/solana.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/fetchers/ton.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/fetchers/tron.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/freeze.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/funder.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/gascluster.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/investigate.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/__init__.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/audit.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/bulk.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/names.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/partial.json +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/tonapi.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/labels/tronscan.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/offramp.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/poisoning.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/prices.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/report.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/spam.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/store.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/trace.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/verify.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/watch.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace/webapp.py +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace.egg-info/SOURCES.txt +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace.egg-info/dependency_links.txt +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace.egg-info/entry_points.txt +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace.egg-info/requires.txt +0 -0
- {crypttrace-0.27.0 → crypttrace-0.30.0}/src/crypttrace.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crypttrace
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: OSINT toolkit for crypto investigations: trace stolen funds across Ethereum, Bitcoin, Tron, Solana and TON
|
|
5
5
|
Author: bobslayerX
|
|
6
6
|
License-Expression: MIT
|
|
@@ -15,14 +15,14 @@ Classifier: Intended Audience :: Information Technology
|
|
|
15
15
|
Classifier: Intended Audience :: Legal Industry
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
23
|
Classifier: Topic :: Security
|
|
24
24
|
Classifier: Topic :: Utilities
|
|
25
|
-
Requires-Python: >=3.
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE
|
|
28
28
|
Requires-Dist: typer>=0.12
|
|
@@ -66,7 +66,7 @@ pipx install "crypttrace[web]" # or: pip install "crypttrace[web]"
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
`[web]` also installs Flask for the web UI; leave it off for the terminal tool
|
|
69
|
-
alone. Python 3.
|
|
69
|
+
alone. Python 3.10 or newer. To work on the code instead:
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
72
|
git clone https://github.com/bobslayerX/crypttrace
|
|
@@ -546,17 +546,32 @@ with the reason.
|
|
|
546
546
|
### Labels
|
|
547
547
|
|
|
548
548
|
Ships a curated seed set (major exchanges, Tornado Cash, bridges, notable
|
|
549
|
-
hacks). `update-labels` pulls
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
549
|
+
hacks). `update-labels` pulls public lists and merges them into the local DB
|
|
550
|
+
in `~/.crypttrace/`:
|
|
551
|
+
|
|
552
|
+
- **ScamSniffer's phishing blacklist** — about 2,500 EVM addresses of
|
|
553
|
+
phishing sites and wallet drainers, kept by a security team whose lists
|
|
554
|
+
wallets such as Rabby and Phantom use. Updated daily, published with a
|
|
555
|
+
7-day delay. Labelled `scam`.
|
|
556
|
+
- **The OFAC SDN list** — sanctioned addresses. Labelled `sanctioned`; it wins
|
|
557
|
+
where both lists name an address.
|
|
558
|
+
|
|
559
|
+
Each download replaces that list's previous copy, so an address taken off a
|
|
560
|
+
list loses its label too. The lists are fetched on your machine rather than
|
|
561
|
+
shipped with crypttrace (ScamSniffer's is GPL-licensed, and both change
|
|
562
|
+
daily); run `update-labels` now and then. The curated seed wins on conflicts,
|
|
563
|
+
so richer names survive. Add sources in `labels/labels.py` → `SOURCES`.
|
|
564
|
+
|
|
565
|
+
With the phishing list in place, `check` also names any payment the wallet
|
|
566
|
+
made to a listed address. A fake site or a signed token approval takes money
|
|
567
|
+
in a single transfer, so there is no burst for the drain check to find.
|
|
553
568
|
|
|
554
569
|
On Bitcoin, Tron and Solana the labels cover the reserve wallets of Binance,
|
|
555
|
-
OKX and HTX
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
570
|
+
OKX and HTX — each taken from the address list the exchange publishes itself
|
|
571
|
+
for proof-of-reserves; on TON and the EVM chains (Ethereum, BNB Chain,
|
|
572
|
+
Arbitrum, Polygon, Base, Optimism), Binance's reserve wallets. Bybit's (274)
|
|
573
|
+
and MEXC's reserve wallets are covered on every one of these chains, from the
|
|
574
|
+
"verified wallets" table of their monthly Hacken proof-of-reserves audits.
|
|
560
575
|
That is what lets `offramp` flag deposit addresses on those chains. Funds sent to an exchange not on that
|
|
561
576
|
list still show as `unknown`. Every label records where it came from — see
|
|
562
577
|
`crypttrace labels audit`.
|
|
@@ -719,8 +734,10 @@ cases/ # worked investigations with their data
|
|
|
719
734
|
## Roadmap
|
|
720
735
|
|
|
721
736
|
- More exchanges on Bitcoin, Tron, Solana (now: Binance, OKX, HTX, Bybit, MEXC),
|
|
722
|
-
TON and the EVM chains (now: Binance, HTX, MEXC)
|
|
723
|
-
- More label sources: Chainabuse
|
|
737
|
+
TON and the EVM chains (now: Binance, HTX, Bybit, MEXC)
|
|
738
|
+
- More label sources: Chainabuse (needs an API key), more exchange
|
|
739
|
+
deposit-address sets (OKX's is in). CryptoScamDB is gone (its API has been
|
|
740
|
+
down since 2023); ScamSniffer's list took its place.
|
|
724
741
|
|
|
725
742
|
## Contributing
|
|
726
743
|
|
|
@@ -736,7 +753,8 @@ MEXC monthly). `python tools/refresh_por.py binance htx` shows what changed
|
|
|
736
753
|
since the labels were taken; `--write` adds the newly listed wallets and marks
|
|
737
754
|
the ones no longer listed as possibly retired (their labels stay: old funds
|
|
738
755
|
still lead there). For MEXC, download the latest Hacken audit PDF from
|
|
739
|
-
mexc.com/proof-of-reserve/audit-report and pass it with `--mexc FILE
|
|
756
|
+
mexc.com/proof-of-reserve/audit-report and pass it with `--mexc FILE`; for
|
|
757
|
+
Bybit, the one from bybit.com/en/app/user/audit-report with `--bybit FILE`.
|
|
740
758
|
|
|
741
759
|
## Licence
|
|
742
760
|
|
|
@@ -32,7 +32,7 @@ pipx install "crypttrace[web]" # or: pip install "crypttrace[web]"
|
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
`[web]` also installs Flask for the web UI; leave it off for the terminal tool
|
|
35
|
-
alone. Python 3.
|
|
35
|
+
alone. Python 3.10 or newer. To work on the code instead:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
git clone https://github.com/bobslayerX/crypttrace
|
|
@@ -512,17 +512,32 @@ with the reason.
|
|
|
512
512
|
### Labels
|
|
513
513
|
|
|
514
514
|
Ships a curated seed set (major exchanges, Tornado Cash, bridges, notable
|
|
515
|
-
hacks). `update-labels` pulls
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
515
|
+
hacks). `update-labels` pulls public lists and merges them into the local DB
|
|
516
|
+
in `~/.crypttrace/`:
|
|
517
|
+
|
|
518
|
+
- **ScamSniffer's phishing blacklist** — about 2,500 EVM addresses of
|
|
519
|
+
phishing sites and wallet drainers, kept by a security team whose lists
|
|
520
|
+
wallets such as Rabby and Phantom use. Updated daily, published with a
|
|
521
|
+
7-day delay. Labelled `scam`.
|
|
522
|
+
- **The OFAC SDN list** — sanctioned addresses. Labelled `sanctioned`; it wins
|
|
523
|
+
where both lists name an address.
|
|
524
|
+
|
|
525
|
+
Each download replaces that list's previous copy, so an address taken off a
|
|
526
|
+
list loses its label too. The lists are fetched on your machine rather than
|
|
527
|
+
shipped with crypttrace (ScamSniffer's is GPL-licensed, and both change
|
|
528
|
+
daily); run `update-labels` now and then. The curated seed wins on conflicts,
|
|
529
|
+
so richer names survive. Add sources in `labels/labels.py` → `SOURCES`.
|
|
530
|
+
|
|
531
|
+
With the phishing list in place, `check` also names any payment the wallet
|
|
532
|
+
made to a listed address. A fake site or a signed token approval takes money
|
|
533
|
+
in a single transfer, so there is no burst for the drain check to find.
|
|
519
534
|
|
|
520
535
|
On Bitcoin, Tron and Solana the labels cover the reserve wallets of Binance,
|
|
521
|
-
OKX and HTX
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
536
|
+
OKX and HTX — each taken from the address list the exchange publishes itself
|
|
537
|
+
for proof-of-reserves; on TON and the EVM chains (Ethereum, BNB Chain,
|
|
538
|
+
Arbitrum, Polygon, Base, Optimism), Binance's reserve wallets. Bybit's (274)
|
|
539
|
+
and MEXC's reserve wallets are covered on every one of these chains, from the
|
|
540
|
+
"verified wallets" table of their monthly Hacken proof-of-reserves audits.
|
|
526
541
|
That is what lets `offramp` flag deposit addresses on those chains. Funds sent to an exchange not on that
|
|
527
542
|
list still show as `unknown`. Every label records where it came from — see
|
|
528
543
|
`crypttrace labels audit`.
|
|
@@ -685,8 +700,10 @@ cases/ # worked investigations with their data
|
|
|
685
700
|
## Roadmap
|
|
686
701
|
|
|
687
702
|
- More exchanges on Bitcoin, Tron, Solana (now: Binance, OKX, HTX, Bybit, MEXC),
|
|
688
|
-
TON and the EVM chains (now: Binance, HTX, MEXC)
|
|
689
|
-
- More label sources: Chainabuse
|
|
703
|
+
TON and the EVM chains (now: Binance, HTX, Bybit, MEXC)
|
|
704
|
+
- More label sources: Chainabuse (needs an API key), more exchange
|
|
705
|
+
deposit-address sets (OKX's is in). CryptoScamDB is gone (its API has been
|
|
706
|
+
down since 2023); ScamSniffer's list took its place.
|
|
690
707
|
|
|
691
708
|
## Contributing
|
|
692
709
|
|
|
@@ -702,7 +719,8 @@ MEXC monthly). `python tools/refresh_por.py binance htx` shows what changed
|
|
|
702
719
|
since the labels were taken; `--write` adds the newly listed wallets and marks
|
|
703
720
|
the ones no longer listed as possibly retired (their labels stay: old funds
|
|
704
721
|
still lead there). For MEXC, download the latest Hacken audit PDF from
|
|
705
|
-
mexc.com/proof-of-reserve/audit-report and pass it with `--mexc FILE
|
|
722
|
+
mexc.com/proof-of-reserve/audit-report and pass it with `--mexc FILE`; for
|
|
723
|
+
Bybit, the one from bybit.com/en/app/user/audit-report with `--bybit FILE`.
|
|
706
724
|
|
|
707
725
|
## Licence
|
|
708
726
|
|
|
@@ -6,7 +6,7 @@ readme = "README.md"
|
|
|
6
6
|
license = "MIT"
|
|
7
7
|
license-files = ["LICENSE"]
|
|
8
8
|
authors = [{ name = "bobslayerX" }]
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
10
|
keywords = ["osint", "blockchain", "forensics", "cryptocurrency", "bitcoin", "ethereum", "tron", "solana", "ton", "investigation"]
|
|
11
11
|
classifiers = [
|
|
12
12
|
"Development Status :: 4 - Beta",
|
|
@@ -16,11 +16,11 @@ classifiers = [
|
|
|
16
16
|
"Intended Audience :: Legal Industry",
|
|
17
17
|
"Operating System :: OS Independent",
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
19
|
-
"Programming Language :: Python :: 3.9",
|
|
20
19
|
"Programming Language :: Python :: 3.10",
|
|
21
20
|
"Programming Language :: Python :: 3.11",
|
|
22
21
|
"Programming Language :: Python :: 3.12",
|
|
23
22
|
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
24
|
"Topic :: Security",
|
|
25
25
|
"Topic :: Utilities",
|
|
26
26
|
]
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""crypttrace — OSINT crypto investigation CLI."""
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.30.0"
|
|
@@ -72,6 +72,25 @@ def _amounts(rows: List[dict]) -> Dict[str, float]:
|
|
|
72
72
|
return dict(tot)
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def _flagged(out: List[dict]) -> List[dict]:
|
|
76
|
+
"""Payments to an address on a phishing or sanctions list, per address.
|
|
77
|
+
|
|
78
|
+
A phishing site or a signed token approval usually takes money in one
|
|
79
|
+
transfer, so there is no burst to find; the destination's label is the tell."""
|
|
80
|
+
by: Dict[str, dict] = {}
|
|
81
|
+
for r in out:
|
|
82
|
+
kind = labels.type_of(r["to"])
|
|
83
|
+
if kind not in ("scam", "sanctioned"):
|
|
84
|
+
continue
|
|
85
|
+
f = by.setdefault(r["to"], {"address": r["to"], "label": labels.label_of(r["to"]), "type": kind,
|
|
86
|
+
"amounts": defaultdict(float), "first": r["timestamp"], "txs": set()})
|
|
87
|
+
f["amounts"][r.get("symbol") or "?"] += r.get("value") or 0
|
|
88
|
+
f["first"] = min(f["first"], r["timestamp"])
|
|
89
|
+
f["txs"].add(r.get("hash"))
|
|
90
|
+
return [{**f, "amounts": dict(f["amounts"]), "txs": len(f["txs"])}
|
|
91
|
+
for f in sorted(by.values(), key=lambda f: f["first"])]
|
|
92
|
+
|
|
93
|
+
|
|
75
94
|
def check(address: str, chain: str, limit: int = 1000) -> Dict:
|
|
76
95
|
"""Verdict on one wallet: drained (and how), suspicious, or no drain found."""
|
|
77
96
|
me = chains.norm_addr(address, chain)
|
|
@@ -86,6 +105,7 @@ def check(address: str, chain: str, limit: int = 1000) -> Dict:
|
|
|
86
105
|
if not out:
|
|
87
106
|
res["reasons"].append("this wallet has sent nothing in the history read")
|
|
88
107
|
return res
|
|
108
|
+
res["flagged"] = _flagged(out)
|
|
89
109
|
|
|
90
110
|
burst = _best_burst(out, chains.symbol(chain))
|
|
91
111
|
dest, win = burst["to"], burst["rows"]
|