crypttrace 0.26.0__tar.gz → 0.29.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.26.0/src/crypttrace.egg-info → crypttrace-0.29.0}/PKG-INFO +38 -13
- {crypttrace-0.26.0 → crypttrace-0.29.0}/README.md +37 -12
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/__init__.py +1 -1
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/drain.py +20 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/known.json +285 -13
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/labels.py +60 -12
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/render.py +9 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/web/index.html +8 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0/src/crypttrace.egg-info}/PKG-INFO +38 -13
- {crypttrace-0.26.0 → crypttrace-0.29.0}/LICENSE +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/pyproject.toml +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/setup.cfg +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/addresses.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/analysis.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/assess.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/assets.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/bridges.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/chains.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/cli.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/config.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/evidence.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/fetchers/__init__.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/fetchers/bitcoin.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/fetchers/etherscan.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/fetchers/http.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/fetchers/solana.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/fetchers/ton.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/fetchers/tron.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/freeze.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/funder.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/gascluster.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/investigate.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/__init__.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/audit.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/bulk.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/names.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/partial.json +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/tonapi.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/labels/tronscan.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/offramp.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/poisoning.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/prices.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/report.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/spam.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/store.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/trace.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/verify.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/watch.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace/webapp.py +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace.egg-info/SOURCES.txt +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace.egg-info/dependency_links.txt +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace.egg-info/entry_points.txt +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.0}/src/crypttrace.egg-info/requires.txt +0 -0
- {crypttrace-0.26.0 → crypttrace-0.29.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.29.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
|
|
@@ -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`.
|
|
@@ -712,15 +727,17 @@ src/crypttrace/
|
|
|
712
727
|
webapp.py # Flask backend for the web UI
|
|
713
728
|
web/index.html # single-page frontend (interactive graph)
|
|
714
729
|
selftest.py # offline end-to-end checks, run by CI
|
|
730
|
+
tools/refresh_por.py # maintainer: refresh exchange reserve-wallet labels
|
|
715
731
|
cases/ # worked investigations with their data
|
|
716
732
|
```
|
|
717
733
|
|
|
718
734
|
## Roadmap
|
|
719
735
|
|
|
720
736
|
- More exchanges on Bitcoin, Tron, Solana (now: Binance, OKX, HTX, Bybit, MEXC),
|
|
721
|
-
TON and the EVM chains (now: Binance, MEXC)
|
|
722
|
-
|
|
723
|
-
-
|
|
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
|
|
|
@@ -731,6 +748,14 @@ a `source` a reader can check and a `source_kind` (`self-published`,
|
|
|
731
748
|
`crypttrace labels audit` and `python selftest.py` before opening a PR.
|
|
732
749
|
Accuracy matters more than volume — a wrong label is worse than no label.
|
|
733
750
|
|
|
751
|
+
Exchanges republish their reserve-wallet lists (Binance continuously, HTX and
|
|
752
|
+
MEXC monthly). `python tools/refresh_por.py binance htx` shows what changed
|
|
753
|
+
since the labels were taken; `--write` adds the newly listed wallets and marks
|
|
754
|
+
the ones no longer listed as possibly retired (their labels stay: old funds
|
|
755
|
+
still lead there). For MEXC, download the latest Hacken audit PDF from
|
|
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`.
|
|
758
|
+
|
|
734
759
|
## Licence
|
|
735
760
|
|
|
736
761
|
MIT — see [LICENSE](LICENSE).
|
|
@@ -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`.
|
|
@@ -678,15 +693,17 @@ src/crypttrace/
|
|
|
678
693
|
webapp.py # Flask backend for the web UI
|
|
679
694
|
web/index.html # single-page frontend (interactive graph)
|
|
680
695
|
selftest.py # offline end-to-end checks, run by CI
|
|
696
|
+
tools/refresh_por.py # maintainer: refresh exchange reserve-wallet labels
|
|
681
697
|
cases/ # worked investigations with their data
|
|
682
698
|
```
|
|
683
699
|
|
|
684
700
|
## Roadmap
|
|
685
701
|
|
|
686
702
|
- More exchanges on Bitcoin, Tron, Solana (now: Binance, OKX, HTX, Bybit, MEXC),
|
|
687
|
-
TON and the EVM chains (now: Binance, MEXC)
|
|
688
|
-
|
|
689
|
-
-
|
|
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
|
|
|
@@ -697,6 +714,14 @@ a `source` a reader can check and a `source_kind` (`self-published`,
|
|
|
697
714
|
`crypttrace labels audit` and `python selftest.py` before opening a PR.
|
|
698
715
|
Accuracy matters more than volume — a wrong label is worse than no label.
|
|
699
716
|
|
|
717
|
+
Exchanges republish their reserve-wallet lists (Binance continuously, HTX and
|
|
718
|
+
MEXC monthly). `python tools/refresh_por.py binance htx` shows what changed
|
|
719
|
+
since the labels were taken; `--write` adds the newly listed wallets and marks
|
|
720
|
+
the ones no longer listed as possibly retired (their labels stay: old funds
|
|
721
|
+
still lead there). For MEXC, download the latest Hacken audit PDF from
|
|
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`.
|
|
724
|
+
|
|
700
725
|
## Licence
|
|
701
726
|
|
|
702
727
|
MIT — see [LICENSE](LICENSE).
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""crypttrace — OSINT crypto investigation CLI."""
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.29.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"]
|