crypttrace 0.2.0__tar.gz → 0.3.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.
Files changed (44) hide show
  1. {crypttrace-0.2.0/src/crypttrace.egg-info → crypttrace-0.3.0}/PKG-INFO +20 -8
  2. {crypttrace-0.2.0 → crypttrace-0.3.0}/README.md +19 -7
  3. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/__init__.py +1 -1
  4. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/cli.py +4 -4
  5. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/investigate.py +2 -11
  6. crypttrace-0.3.0/src/crypttrace/labels/known.json +284 -0
  7. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/labels/labels.py +10 -0
  8. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/offramp.py +33 -17
  9. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/trace.py +5 -4
  10. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/watch.py +2 -2
  11. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/webapp.py +1 -2
  12. {crypttrace-0.2.0 → crypttrace-0.3.0/src/crypttrace.egg-info}/PKG-INFO +20 -8
  13. crypttrace-0.2.0/src/crypttrace/labels/known.json +0 -148
  14. {crypttrace-0.2.0 → crypttrace-0.3.0}/LICENSE +0 -0
  15. {crypttrace-0.2.0 → crypttrace-0.3.0}/pyproject.toml +0 -0
  16. {crypttrace-0.2.0 → crypttrace-0.3.0}/setup.cfg +0 -0
  17. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/addresses.py +0 -0
  18. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/analysis.py +0 -0
  19. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/assess.py +0 -0
  20. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/assets.py +0 -0
  21. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/bridges.py +0 -0
  22. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/chains.py +0 -0
  23. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/config.py +0 -0
  24. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/fetchers/__init__.py +0 -0
  25. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/fetchers/bitcoin.py +0 -0
  26. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/fetchers/etherscan.py +0 -0
  27. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/fetchers/http.py +0 -0
  28. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/fetchers/solana.py +0 -0
  29. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/fetchers/tron.py +0 -0
  30. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/funder.py +0 -0
  31. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/labels/__init__.py +0 -0
  32. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/labels/audit.py +0 -0
  33. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/labels/partial.json +0 -0
  34. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/prices.py +0 -0
  35. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/render.py +0 -0
  36. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/report.py +0 -0
  37. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/store.py +0 -0
  38. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/verify.py +0 -0
  39. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace/web/index.html +0 -0
  40. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace.egg-info/SOURCES.txt +0 -0
  41. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace.egg-info/dependency_links.txt +0 -0
  42. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace.egg-info/entry_points.txt +0 -0
  43. {crypttrace-0.2.0 → crypttrace-0.3.0}/src/crypttrace.egg-info/requires.txt +0 -0
  44. {crypttrace-0.2.0 → crypttrace-0.3.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.2.0
3
+ Version: 0.3.0
4
4
  Summary: OSINT toolkit for crypto investigations: trace stolen funds across Ethereum, Bitcoin, Tron and Solana
5
5
  Author: bobslayerX
6
6
  License-Expression: MIT
@@ -273,7 +273,9 @@ behaviour: an address forwarding most of its outgoing value to a labelled
273
273
  exchange is almost certainly a deposit address, i.e. the cash-out point where
274
274
  that exchange holds the depositor's KYC. `trace` applies the same heuristic
275
275
  automatically, turning an anonymous intermediary into "→ Binance deposit (KYC
276
- point)".
276
+ point)". It works on every supported chain; on Tron and Solana, where deposits
277
+ are usually swept as stablecoins, it checks USDT and USDC as well as the native
278
+ coin.
277
279
 
278
280
  ### First-funder (deanonymization)
279
281
 
@@ -371,11 +373,12 @@ SDN sanctioned-address list — and merges them into the local DB in
371
373
  `~/.crypttrace/`. The curated seed wins on conflicts, so richer names survive.
372
374
  Add sources in `labels/labels.py` → `SOURCES`.
373
375
 
374
- Bitcoin, Tron and Solana coverage is currently Binance only: its reserve
375
- wallets, taken from the address list Binance publishes itself for
376
- proof-of-reserves. So `offramp` can flag Binance deposit addresses on those
377
- chains, but funds sent to other exchanges there will still show as `unknown`.
378
- Every label records where it came from — see `crypttrace labels audit`.
376
+ On Bitcoin, Tron and Solana the labels cover the reserve wallets of Binance,
377
+ OKX and HTX, plus Bybit's older 2022 list — each taken from the address list the
378
+ exchange publishes itself for proof-of-reserves. That is what lets `offramp`
379
+ flag deposit addresses on those chains. Funds sent to an exchange not on that
380
+ list still show as `unknown`. Every label records where it came from — see
381
+ `crypttrace labels audit`.
379
382
 
380
383
  ### Reports
381
384
 
@@ -478,7 +481,16 @@ cases/ # worked investigations with their data
478
481
 
479
482
  ## Roadmap
480
483
 
481
- - Exchange/service labels for Bitcoin, Tron and Solana
484
+ - More exchanges on Bitcoin, Tron and Solana (now: Binance, OKX, HTX, Bybit),
485
+ and refreshing these lists as the exchanges republish them
486
+ - OKX's full signed address set (~310k, mostly deposit addresses) as an
487
+ optional `update-labels` download, so deposits are labelled outright
488
+ - Address-poisoning detection: flag look-alike addresses (same first and last
489
+ characters) in a victim's history before they copy the wrong one
490
+ - Stablecoin freeze check: whether USDT/USDC at an address is already frozen
491
+ by the issuer, and who to ask for a freeze
492
+ - `report --html`: one self-contained file with the interactive graph, to
493
+ send to an exchange or attach to a police report
482
494
  - `report --pdf` for exchange and law-enforcement filings
483
495
  - Internal transactions (completes `funder` and contract-mediated transfers)
484
496
  - Per-mint filtering for Solana SPL tokens
@@ -239,7 +239,9 @@ behaviour: an address forwarding most of its outgoing value to a labelled
239
239
  exchange is almost certainly a deposit address, i.e. the cash-out point where
240
240
  that exchange holds the depositor's KYC. `trace` applies the same heuristic
241
241
  automatically, turning an anonymous intermediary into "→ Binance deposit (KYC
242
- point)".
242
+ point)". It works on every supported chain; on Tron and Solana, where deposits
243
+ are usually swept as stablecoins, it checks USDT and USDC as well as the native
244
+ coin.
243
245
 
244
246
  ### First-funder (deanonymization)
245
247
 
@@ -337,11 +339,12 @@ SDN sanctioned-address list — and merges them into the local DB in
337
339
  `~/.crypttrace/`. The curated seed wins on conflicts, so richer names survive.
338
340
  Add sources in `labels/labels.py` → `SOURCES`.
339
341
 
340
- Bitcoin, Tron and Solana coverage is currently Binance only: its reserve
341
- wallets, taken from the address list Binance publishes itself for
342
- proof-of-reserves. So `offramp` can flag Binance deposit addresses on those
343
- chains, but funds sent to other exchanges there will still show as `unknown`.
344
- Every label records where it came from — see `crypttrace labels audit`.
342
+ On Bitcoin, Tron and Solana the labels cover the reserve wallets of Binance,
343
+ OKX and HTX, plus Bybit's older 2022 list — each taken from the address list the
344
+ exchange publishes itself for proof-of-reserves. That is what lets `offramp`
345
+ flag deposit addresses on those chains. Funds sent to an exchange not on that
346
+ list still show as `unknown`. Every label records where it came from — see
347
+ `crypttrace labels audit`.
345
348
 
346
349
  ### Reports
347
350
 
@@ -444,7 +447,16 @@ cases/ # worked investigations with their data
444
447
 
445
448
  ## Roadmap
446
449
 
447
- - Exchange/service labels for Bitcoin, Tron and Solana
450
+ - More exchanges on Bitcoin, Tron and Solana (now: Binance, OKX, HTX, Bybit),
451
+ and refreshing these lists as the exchanges republish them
452
+ - OKX's full signed address set (~310k, mostly deposit addresses) as an
453
+ optional `update-labels` download, so deposits are labelled outright
454
+ - Address-poisoning detection: flag look-alike addresses (same first and last
455
+ characters) in a victim's history before they copy the wrong one
456
+ - Stablecoin freeze check: whether USDT/USDC at an address is already frozen
457
+ by the issuer, and who to ask for a freeze
458
+ - `report --html`: one self-contained file with the interactive graph, to
459
+ send to an exchange or attach to a police report
448
460
  - `report --pdf` for exchange and law-enforcement filings
449
461
  - Internal transactions (completes `funder` and contract-mediated transfers)
450
462
  - Per-mint filtering for Solana SPL tokens
@@ -1,2 +1,2 @@
1
1
  """crypttrace — OSINT crypto investigation CLI."""
2
- __version__ = "0.2.0"
2
+ __version__ = "0.3.0"
@@ -262,21 +262,21 @@ def crosschain(
262
262
 
263
263
  @app.command()
264
264
  def offramp(
265
- address: str = typer.Argument(..., help="Address to check (0x…)"),
265
+ address: str = typer.Argument(..., help="Address to check"),
266
266
  chain: str = CHAIN_OPT,
267
267
  ):
268
268
  """Check whether an address is an exchange deposit address (cash-out / off-ramp)."""
269
269
  try:
270
270
  hit = offramp_mod.detect(address, chain)
271
- except etherscan.EtherscanError as e:
271
+ except (chains_mod.ChainError, etherscan.EtherscanError) as e:
272
272
  console.print(f"[red]Error:[/red] {e}")
273
273
  raise typer.Exit(1)
274
274
  if hit:
275
275
  pct = int(hit["fraction"] * 100)
276
276
  console.print(
277
277
  f"[green]➜ Likely off-ramp:[/green] this address forwarded ~{pct}% of outgoing "
278
- f"funds ({hit['forwarded']:.4f}) to [bold]{hit['exchange']}[/bold].\n"
279
- f" It is probably a {hit['exchange']} deposit address — a KYC identification point."
278
+ f"funds ({hit['forwarded']:.4f} {hit['symbol']}) to [bold]{hit['exchange']}[/bold].\n"
279
+ f" It is probably a deposit address at {hit['company']} — a KYC identification point."
280
280
  )
281
281
  else:
282
282
  console.print("[dim]No exchange-forwarding pattern detected. "
@@ -74,7 +74,7 @@ def analyse(address: str, chain: str = "eth", asset: Optional[dict] = None,
74
74
  result["funding"] = []
75
75
 
76
76
  try:
77
- result["offramp"] = offramp_mod.detect(address, chain) if chains.is_evm(chain) else None
77
+ result["offramp"] = offramp_mod.detect(address, chain)
78
78
  except Exception:
79
79
  result["offramp"] = None
80
80
 
@@ -82,20 +82,11 @@ def analyse(address: str, chain: str = "eth", asset: Optional[dict] = None,
82
82
  return result
83
83
 
84
84
 
85
- def _service_name(label: str) -> str:
86
- """'Binance 14 (hot wallet)' -> 'Binance' — victims need the company, not our label."""
87
- import re
88
- name = re.sub(r"\(.*?\)", "", label) # drop parentheticals
89
- name = name.split(" deposit")[0].split(":")[0]
90
- name = re.sub(r"\s+\d+\s*$", "", name.strip()) # drop trailing wallet numbers
91
- return name.strip() or label
92
-
93
-
94
85
  def build_guidance(r: dict) -> dict:
95
86
  """Turn findings into plain-language next steps."""
96
87
  steps: List[dict] = []
97
88
  exchanges = r["exchanges"]
98
- ex_names = sorted({_service_name(f["label"]) for f in exchanges}) if exchanges else []
89
+ ex_names = sorted({labels.company(f["label"]) for f in exchanges}) if exchanges else []
99
90
 
100
91
  if exchanges:
101
92
  headline = ("Good news, relatively speaking: the trail reaches a "