cryptoshield 0.2.0__tar.gz → 0.2.2__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 (20) hide show
  1. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/PKG-INFO +43 -9
  2. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/README.md +42 -8
  3. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/__init__.py +1 -1
  4. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/rugpull.py +53 -46
  5. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/solana.py +111 -101
  6. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/utils.py +56 -37
  7. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield.egg-info/PKG-INFO +43 -9
  8. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/pyproject.toml +1 -1
  9. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/LICENSE +0 -0
  10. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/approvals.py +0 -0
  11. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/cli.py +0 -0
  12. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/db.py +0 -0
  13. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/honeypot.py +0 -0
  14. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield/phishing.py +0 -0
  15. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield.egg-info/SOURCES.txt +0 -0
  16. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield.egg-info/dependency_links.txt +0 -0
  17. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield.egg-info/entry_points.txt +0 -0
  18. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield.egg-info/requires.txt +0 -0
  19. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/cryptoshield.egg-info/top_level.txt +0 -0
  20. {cryptoshield-0.2.0 → cryptoshield-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cryptoshield
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: All-in-one crypto security toolkit — honeypot, approvals, rugpull, phishing
5
5
  Author-email: yossweh <cilokcilok15@gmail.com>
6
6
  License: MIT
@@ -30,6 +30,27 @@ Dynamic: license-file
30
30
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
31
31
  [![PyPI](https://img.shields.io/pypi/v/cryptoshield.svg)](https://pypi.org/project/cryptoshield/)
32
32
 
33
+ ## Why CryptoShield?
34
+
35
+ Every day, people lose money to:
36
+ - **Honeypot tokens** — you can buy but can't sell. Your funds are trapped.
37
+ - **Unlimited token approvals** — you gave a random contract permission to drain your wallet. Months later, they do.
38
+ - **Rugpulls** — team dumps all tokens, liquidity vanishes, price goes to zero.
39
+ - **Phishing sites** — fake airdrop pages that look like Uniswap but steal your seed phrase.
40
+
41
+ CryptoShield combines all these checks into **one CLI tool**. No API keys needed. No accounts. No BS. Just run a command and get a clear report.
42
+
43
+ ```
44
+ # Before you ape into that token:
45
+ cryptoshield check 0xToken
46
+
47
+ # Before you connect your wallet to some random site:
48
+ cryptoshield check-url suspicious-site.com
49
+
50
+ # Check if your wallet has dangerous approvals:
51
+ cryptoshield approvals 0xYourWallet
52
+ ```
53
+
33
54
  ## Features
34
55
 
35
56
  - **🍯 Honeypot Detection** — Can you sell? Hidden taxes? Mint function? Check before you buy.
@@ -118,6 +139,13 @@ cryptoshield batch wallets.txt --mode approvals --chain bsc
118
139
  ✅ Mint Authority: None (fixed supply)
119
140
  ✅ Holders: 5,432
120
141
  ✅ Risk Score: 0/100 — LOW RISK
142
+
143
+ 📋 APPROVAL AUDIT — 0x47ac...8188
144
+ ❌ USDT → UNLIMITED to 0xUnkn...abcd
145
+ ⚡ RECOMMEND: revoke immediately
146
+ ⚠️ WETH → unlimited to Uniswap V2 Router
147
+ Known protocol — consider reducing allowance
148
+ ✅ DAI → 500.00 to Uniswap V3 Router
121
149
  ```
122
150
 
123
151
  ## Supported Chains
@@ -132,16 +160,21 @@ cryptoshield batch wallets.txt --mode approvals --chain bsc
132
160
  | Base | ✅ | ✅ | ✅ |
133
161
  | Avalanche | ✅ | ✅ | ✅ |
134
162
  | Fantom | ✅ | ✅ | ✅ |
135
- | Solana | ✅ | | ✅ |
163
+ | Solana | ✅ | 🔜 | ✅ |
164
+
165
+ > 🔜 = Coming soon. SPL token delegation uses a different model than ERC-20 approvals.
166
+
167
+ ## How It Works
136
168
 
137
- ## Data Sources
169
+ | Check | Data Source | Key Needed? |
170
+ |-------|-----------|-------------|
171
+ | Honeypot | GoPlus Security API | No |
172
+ | Approvals | On-chain events (RPC) | No |
173
+ | Rugpull | GoPlus + on-chain heuristics | No |
174
+ | Phishing | Pattern matching + 60+ known domains | No |
175
+ | Solana tokens | Jupiter + Birdeye + Solana RPC | No |
138
176
 
139
- - **GoPlus Security API** Honeypot detection, token security analysis (free, no key)
140
- - **Jupiter API** — Solana token data, strict list
141
- - **Birdeye API** — Solana holder count, volume
142
- - **On-chain data** — Approval events, contract code, ownership (direct RPC)
143
- - **Heuristics** — URL pattern matching, domain analysis, typosquatting detection
144
- - **Community scam database** — 60+ known phishing domains
177
+ Everything runs with public APIs and free RPC endpoints. No signups. No API keys. No tracking.
145
178
 
146
179
  ## Contributing
147
180
 
@@ -149,6 +182,7 @@ PRs welcome! Especially:
149
182
 
150
183
  - More scam domains / phishing patterns
151
184
  - More chain support (TON, Sui, Aptos)
185
+ - SPL token delegation scanner (Solana approvals)
152
186
  - Better rugpull heuristics
153
187
  - UI improvements
154
188
 
@@ -6,6 +6,27 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
7
7
  [![PyPI](https://img.shields.io/pypi/v/cryptoshield.svg)](https://pypi.org/project/cryptoshield/)
8
8
 
9
+ ## Why CryptoShield?
10
+
11
+ Every day, people lose money to:
12
+ - **Honeypot tokens** — you can buy but can't sell. Your funds are trapped.
13
+ - **Unlimited token approvals** — you gave a random contract permission to drain your wallet. Months later, they do.
14
+ - **Rugpulls** — team dumps all tokens, liquidity vanishes, price goes to zero.
15
+ - **Phishing sites** — fake airdrop pages that look like Uniswap but steal your seed phrase.
16
+
17
+ CryptoShield combines all these checks into **one CLI tool**. No API keys needed. No accounts. No BS. Just run a command and get a clear report.
18
+
19
+ ```
20
+ # Before you ape into that token:
21
+ cryptoshield check 0xToken
22
+
23
+ # Before you connect your wallet to some random site:
24
+ cryptoshield check-url suspicious-site.com
25
+
26
+ # Check if your wallet has dangerous approvals:
27
+ cryptoshield approvals 0xYourWallet
28
+ ```
29
+
9
30
  ## Features
10
31
 
11
32
  - **🍯 Honeypot Detection** — Can you sell? Hidden taxes? Mint function? Check before you buy.
@@ -94,6 +115,13 @@ cryptoshield batch wallets.txt --mode approvals --chain bsc
94
115
  ✅ Mint Authority: None (fixed supply)
95
116
  ✅ Holders: 5,432
96
117
  ✅ Risk Score: 0/100 — LOW RISK
118
+
119
+ 📋 APPROVAL AUDIT — 0x47ac...8188
120
+ ❌ USDT → UNLIMITED to 0xUnkn...abcd
121
+ ⚡ RECOMMEND: revoke immediately
122
+ ⚠️ WETH → unlimited to Uniswap V2 Router
123
+ Known protocol — consider reducing allowance
124
+ ✅ DAI → 500.00 to Uniswap V3 Router
97
125
  ```
98
126
 
99
127
  ## Supported Chains
@@ -108,16 +136,21 @@ cryptoshield batch wallets.txt --mode approvals --chain bsc
108
136
  | Base | ✅ | ✅ | ✅ |
109
137
  | Avalanche | ✅ | ✅ | ✅ |
110
138
  | Fantom | ✅ | ✅ | ✅ |
111
- | Solana | ✅ | | ✅ |
139
+ | Solana | ✅ | 🔜 | ✅ |
140
+
141
+ > 🔜 = Coming soon. SPL token delegation uses a different model than ERC-20 approvals.
142
+
143
+ ## How It Works
112
144
 
113
- ## Data Sources
145
+ | Check | Data Source | Key Needed? |
146
+ |-------|-----------|-------------|
147
+ | Honeypot | GoPlus Security API | No |
148
+ | Approvals | On-chain events (RPC) | No |
149
+ | Rugpull | GoPlus + on-chain heuristics | No |
150
+ | Phishing | Pattern matching + 60+ known domains | No |
151
+ | Solana tokens | Jupiter + Birdeye + Solana RPC | No |
114
152
 
115
- - **GoPlus Security API** Honeypot detection, token security analysis (free, no key)
116
- - **Jupiter API** — Solana token data, strict list
117
- - **Birdeye API** — Solana holder count, volume
118
- - **On-chain data** — Approval events, contract code, ownership (direct RPC)
119
- - **Heuristics** — URL pattern matching, domain analysis, typosquatting detection
120
- - **Community scam database** — 60+ known phishing domains
153
+ Everything runs with public APIs and free RPC endpoints. No signups. No API keys. No tracking.
121
154
 
122
155
  ## Contributing
123
156
 
@@ -125,6 +158,7 @@ PRs welcome! Especially:
125
158
 
126
159
  - More scam domains / phishing patterns
127
160
  - More chain support (TON, Sui, Aptos)
161
+ - SPL token delegation scanner (Solana approvals)
128
162
  - Better rugpull heuristics
129
163
  - UI improvements
130
164
 
@@ -1,4 +1,4 @@
1
1
  """CryptoShield — All-in-one crypto security toolkit."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
4
4
  __author__ = "yossweh"
@@ -24,18 +24,20 @@ def analyze_rugpull(address: str, chain: str = "eth") -> dict:
24
24
  "risk_level": "UNKNOWN",
25
25
  }
26
26
 
27
- contract = w3.eth.contract(address=addr, abi=ERC20_ABI)
28
-
29
- # 1. Check if contract is verified (has code)
30
- code = w3.eth.get_code(addr)
31
- if code == b"" or code == b"0x":
32
- report["checks"].append({"name": "Contract Code", "status": "FAIL", "detail": "No contract code — not a contract"})
33
- report["score"] += 50
34
- else:
35
- report["checks"].append({"name": "Contract Code", "status": "OK", "detail": f"{len(code)} bytes deployed"})
27
+ # 1. Check if contract has code
28
+ try:
29
+ code = w3.eth.get_code(addr)
30
+ if code == b"" or code == b"0x":
31
+ report["checks"].append({"name": "Contract Code", "status": "FAIL", "detail": "No contract code not a contract"})
32
+ report["score"] += 50
33
+ else:
34
+ report["checks"].append({"name": "Contract Code", "status": "OK", "detail": f"{len(code)} bytes deployed"})
35
+ except Exception as e:
36
+ report["checks"].append({"name": "Contract Code", "status": "INFO", "detail": f"Could not check: {str(e)[:50]}"})
36
37
 
37
38
  # 2. Check if owner is renounced
38
39
  try:
40
+ contract = w3.eth.contract(address=addr, abi=ERC20_ABI)
39
41
  owner = contract.functions.owner().call()
40
42
  zero = "0x0000000000000000000000000000000000000000"
41
43
  dead = "0x000000000000000000000000000000000000dEaD"
@@ -47,8 +49,9 @@ def analyze_rugpull(address: str, chain: str = "eth") -> dict:
47
49
  except Exception:
48
50
  report["checks"].append({"name": "Ownership", "status": "INFO", "detail": "No owner function (could be good or bad)"})
49
51
 
50
- # 3. Check total supply concentration
52
+ # 3. Check total supply
51
53
  try:
54
+ contract = w3.eth.contract(address=addr, abi=ERC20_ABI)
52
55
  total_supply = contract.functions.totalSupply().call()
53
56
  if total_supply > 0:
54
57
  report["checks"].append({"name": "Total Supply", "status": "INFO", "detail": f"{total_supply:,}"})
@@ -56,46 +59,50 @@ def analyze_rugpull(address: str, chain: str = "eth") -> dict:
56
59
  report["checks"].append({"name": "Total Supply", "status": "FAIL", "detail": "Zero supply"})
57
60
  report["score"] += 20
58
61
  except Exception:
59
- report["checks"].append({"name": "Total Supply", "status": "FAIL", "detail": "Cannot read supply"})
62
+ report["checks"].append({"name": "Total Supply", "status": "INFO", "detail": "Cannot read supply"})
60
63
 
61
- # 4. Check liquidity (look for LP tokens)
62
- # This is a simplified check — real check would scan DEX pairs
64
+ # 4. Liquidity hint
63
65
  report["checks"].append({"name": "Liquidity", "status": "INFO", "detail": "Check manually on DEX Screener"})
64
66
 
65
- # 5. Get GoPlus data for additional checks
66
- goplus = check_honeypot(addr, chain)
67
- if "error" not in goplus:
68
- if goplus.get("is_honeypot"):
69
- report["score"] += 40
70
- report["checks"].append({"name": "Honeypot", "status": "FAIL", "detail": "Token is a honeypot"})
71
- else:
72
- report["checks"].append({"name": "Honeypot", "status": "OK", "detail": "Not a honeypot"})
73
-
74
- if not goplus.get("is_open_source"):
75
- report["score"] += 15
76
- report["checks"].append({"name": "Source Code", "status": "FAIL", "detail": "Not verified"})
67
+ # 5. GoPlus data (honeypot, source code, proxy, etc.)
68
+ try:
69
+ goplus = check_honeypot(addr, chain)
70
+ if "error" not in goplus:
71
+ if goplus.get("is_honeypot"):
72
+ report["score"] += 40
73
+ report["checks"].append({"name": "Honeypot", "status": "FAIL", "detail": "Token is a honeypot"})
74
+ else:
75
+ report["checks"].append({"name": "Honeypot", "status": "OK", "detail": "Not a honeypot"})
76
+
77
+ if not goplus.get("is_open_source"):
78
+ report["score"] += 15
79
+ report["checks"].append({"name": "Source Code", "status": "FAIL", "detail": "Not verified"})
80
+ else:
81
+ report["checks"].append({"name": "Source Code", "status": "OK", "detail": "Verified on explorer"})
82
+
83
+ if goplus.get("is_proxy"):
84
+ report["score"] += 5
85
+ report["checks"].append({"name": "Proxy", "status": "WARN", "detail": "Proxy contract — logic can change"})
86
+
87
+ if goplus.get("selfdestruct"):
88
+ report["score"] += 15
89
+ report["checks"].append({"name": "Self-Destruct", "status": "FAIL", "detail": "Has selfdestruct function"})
90
+
91
+ if goplus.get("hidden_owner"):
92
+ report["score"] += 10
93
+ report["checks"].append({"name": "Hidden Owner", "status": "FAIL", "detail": "Hidden owner detected"})
94
+
95
+ if goplus.get("owner_can_mint"):
96
+ report["score"] += 10
97
+ report["checks"].append({"name": "Mintable", "status": "WARN", "detail": "Owner can mint new tokens"})
98
+
99
+ holder_count = goplus.get("holder_count", 0)
100
+ if holder_count:
101
+ report["checks"].append({"name": "Holders", "status": "INFO", "detail": f"{holder_count:,} holders"})
77
102
  else:
78
- report["checks"].append({"name": "Source Code", "status": "OK", "detail": "Verified on explorer"})
79
-
80
- if goplus.get("is_proxy"):
81
- report["score"] += 5
82
- report["checks"].append({"name": "Proxy", "status": "WARN", "detail": "Proxy contract — logic can change"})
83
-
84
- if goplus.get("selfdestruct"):
85
- report["score"] += 15
86
- report["checks"].append({"name": "Self-Destruct", "status": "FAIL", "detail": "Has selfdestruct function"})
87
-
88
- if goplus.get("hidden_owner"):
89
- report["score"] += 10
90
- report["checks"].append({"name": "Hidden Owner", "status": "FAIL", "detail": "Hidden owner detected"})
91
-
92
- if goplus.get("owner_can_mint"):
93
- report["score"] += 10
94
- report["checks"].append({"name": "Mintable", "status": "WARN", "detail": "Owner can mint new tokens"})
95
-
96
- holder_count = goplus.get("holder_count", 0)
97
- if holder_count:
98
- report["checks"].append({"name": "Holders", "status": "INFO", "detail": f"{holder_count:,} holders"})
103
+ report["checks"].append({"name": "GoPlus", "status": "INFO", "detail": f"API error: {goplus['error'][:50]}"})
104
+ except Exception as e:
105
+ report["checks"].append({"name": "GoPlus", "status": "INFO", "detail": f"Could not check: {str(e)[:50]}"})
99
106
 
100
107
  # Cap score at 100
101
108
  report["score"] = min(report["score"], 100)
@@ -1,6 +1,6 @@
1
1
  """Solana token security checker.
2
2
 
3
- Uses Solana RPC + Jupiter API for token data.
3
+ Uses Solana RPC + Solana Token Registry for token data.
4
4
  """
5
5
 
6
6
  import requests
@@ -8,9 +8,36 @@ from .db import cache_get, cache_set
8
8
  from .utils import print_header, print_ok, print_warn, print_fail, print_info
9
9
 
10
10
  SOLANA_RPC = "https://api.mainnet-beta.solana.com"
11
- JUPITER_TOKEN_API = "https://tokens.jup.ag/token/{mint}"
12
- JUPITER_LIST = "https://tokens.jup.ag/strict"
13
- BIRDEYE_TOKEN = "https://public-api.birdeye.so/defi/token_overview?address={mint}"
11
+ TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json"
12
+
13
+ # Cache the token registry
14
+ _registry_cache = None
15
+
16
+
17
+ def _get_token_registry() -> dict:
18
+ """Get the Solana token registry (cached)."""
19
+ global _registry_cache
20
+ if _registry_cache is not None:
21
+ return _registry_cache
22
+
23
+ cached = cache_get("solana_token_registry")
24
+ if cached:
25
+ _registry_cache = cached
26
+ return cached
27
+
28
+ try:
29
+ r = requests.get(TOKEN_REGISTRY_URL, timeout=30)
30
+ r.raise_for_status()
31
+ data = r.json()
32
+ tokens = {}
33
+ for t in data.get("tokens", []):
34
+ tokens[t["address"]] = t
35
+ _registry_cache = tokens
36
+ cache_set("solana_token_registry", tokens, ttl=86400) # Cache 24h
37
+ return tokens
38
+ except Exception:
39
+ _registry_cache = {}
40
+ return {}
14
41
 
15
42
 
16
43
  def check_solana_token(mint: str) -> dict:
@@ -30,35 +57,23 @@ def check_solana_token(mint: str) -> dict:
30
57
  "holder_count": 0,
31
58
  "total_supply": 0,
32
59
  "decimals": 0,
33
- "is_on_jupiter": False,
60
+ "is_known": False,
34
61
  "freeze_authority": None,
35
62
  "mint_authority": None,
36
- "is_mutable": True,
63
+ "tags": [],
37
64
  }
38
65
 
39
- # 1. Get token metadata from Jupiter
40
- try:
41
- r = requests.get(JUPITER_TOKEN_API.format(mint=mint), timeout=10)
42
- if r.status_code == 200:
43
- data = r.json()
44
- report["token_name"] = data.get("name", "Unknown")
45
- report["token_symbol"] = data.get("symbol", "?")
46
- report["decimals"] = data.get("decimals", 0)
47
- report["is_on_jupiter"] = True
48
- report["daily_volume"] = data.get("daily_volume", 0)
49
- except Exception:
50
- pass
51
-
52
- # 2. Check if on Jupiter strict list (vetted tokens)
53
- try:
54
- r = requests.get(JUPITER_LIST, timeout=10)
55
- if r.status_code == 200:
56
- strict_tokens = {t["address"] for t in r.json()}
57
- report["is_on_jupiter_strict"] = mint in strict_tokens
58
- except Exception:
59
- report["is_on_jupiter_strict"] = False
60
-
61
- # 3. Get on-chain token info via RPC
66
+ # 1. Get token info from registry
67
+ registry = _get_token_registry()
68
+ if mint in registry:
69
+ token_info = registry[mint]
70
+ report["token_name"] = token_info.get("name", "Unknown")
71
+ report["token_symbol"] = token_info.get("symbol", "?")
72
+ report["decimals"] = token_info.get("decimals", 0)
73
+ report["is_known"] = True
74
+ report["tags"] = token_info.get("tags", [])
75
+
76
+ # 2. Get on-chain token info via RPC
62
77
  try:
63
78
  payload = {
64
79
  "jsonrpc": "2.0",
@@ -66,7 +81,7 @@ def check_solana_token(mint: str) -> dict:
66
81
  "method": "getAccountInfo",
67
82
  "params": [mint, {"encoding": "jsonParsed"}],
68
83
  }
69
- r = requests.post(SOLANA_RPC, json=payload, timeout=10)
84
+ r = requests.post(SOLANA_RPC, json=payload, timeout=15)
70
85
  data = r.json()
71
86
  account = data.get("result", {}).get("value", {})
72
87
 
@@ -76,26 +91,30 @@ def check_solana_token(mint: str) -> dict:
76
91
 
77
92
  report["mint_authority"] = info.get("mintAuthority")
78
93
  report["freeze_authority"] = info.get("freezeAuthority")
79
- report["is_mutable"] = info.get("isInitialized", True)
80
- report["total_supply"] = int(info.get("supply", "0"))
94
+ supply_raw = int(info.get("supply", "0"))
95
+ report["total_supply"] = supply_raw
81
96
  report["decimals"] = info.get("decimals", report["decimals"])
82
- except Exception:
83
- pass
84
97
 
85
- # 4. Get holder count from Birdeye (optional, may fail without API key)
98
+ # Calculate human-readable supply
99
+ if report["decimals"] > 0:
100
+ report["total_supply_ui"] = supply_raw / (10 ** report["decimals"])
101
+ else:
102
+ report["total_supply_ui"] = supply_raw
103
+ except Exception as e:
104
+ report["rpc_error"] = str(e)[:50]
105
+
106
+ # 3. Get holder count from Solana RPC (approximate)
86
107
  try:
87
- headers = {"X-API-KEY": "public"}
88
- r = requests.get(
89
- BIRDEYE_TOKEN.format(mint=mint),
90
- headers=headers,
91
- timeout=10,
92
- )
93
- if r.status_code == 200:
94
- birddata = r.json().get("data", {})
95
- report["holder_count"] = birddata.get("holder", 0)
96
- report["daily_volume"] = birddata.get("v24hUSD", report.get("daily_volume", 0))
97
- report["market_cap"] = birddata.get("mc", 0)
98
- report["price"] = birddata.get("price", 0)
108
+ payload = {
109
+ "jsonrpc": "2.0",
110
+ "id": 1,
111
+ "method": "getTokenLargestAccounts",
112
+ "params": [mint],
113
+ }
114
+ r = requests.post(SOLANA_RPC, json=payload, timeout=15)
115
+ data = r.json()
116
+ largest = data.get("result", {}).get("value", [])
117
+ report["top_holders"] = len(largest)
99
118
  except Exception:
100
119
  pass
101
120
 
@@ -113,34 +132,23 @@ def check_solana_token(mint: str) -> dict:
113
132
  risks.append("MINT AUTHORITY — unlimited supply, issuer can mint more")
114
133
  score += 15
115
134
 
116
- # Not on Jupiter
117
- if not report["is_on_jupiter"]:
118
- risks.append("NOT ON JUPITER — unvetted token, high risk")
135
+ # Not in registry
136
+ if not report["is_known"]:
137
+ risks.append("NOT IN TOKEN REGISTRY — unvetted token, high risk")
119
138
  score += 15
120
139
 
121
- # Not on Jupiter strict list
122
- if not report.get("is_on_jupiter_strict"):
123
- risks.append("NOT ON JUPITER STRICT LIST not officially vetted")
124
- score += 5
140
+ # Stablecoin tag (positive)
141
+ if "stablecoin" in report.get("tags", []):
142
+ score -= 10 # Lower risk for known stablecoins
125
143
 
126
- # Low holder count
127
- if report["holder_count"] > 0 and report["holder_count"] < 100:
128
- risks.append(f"LOW HOLDER COUNT — only {report['holder_count']} holders")
129
- score += 15
130
- elif report["holder_count"] > 0 and report["holder_count"] < 1000:
131
- risks.append(f"SMALL HOLDER COUNT — {report['holder_count']} holders")
132
- score += 5
133
-
134
- # Low volume
135
- vol = report.get("daily_volume", 0)
136
- if vol > 0 and vol < 1000:
137
- risks.append(f"LOW VOLUME — ${vol:,.0f} in 24h")
138
- score += 10
139
-
140
- report["risk_score"] = min(score, 100)
141
- if score <= 20:
144
+ # Native SOL wrapped (positive)
145
+ if "native" in report.get("tags", []):
146
+ score -= 5
147
+
148
+ report["risk_score"] = max(min(score, 100), 0)
149
+ if report["risk_score"] <= 20:
142
150
  report["risk_level"] = "LOW"
143
- elif score <= 50:
151
+ elif report["risk_score"] <= 50:
144
152
  report["risk_level"] = "MEDIUM"
145
153
  else:
146
154
  report["risk_level"] = "HIGH"
@@ -165,7 +173,7 @@ def check_solana_wallet(wallet: str) -> dict:
165
173
  "method": "getBalance",
166
174
  "params": [wallet],
167
175
  }
168
- r = requests.post(SOLANA_RPC, json=payload, timeout=10)
176
+ r = requests.post(SOLANA_RPC, json=payload, timeout=15)
169
177
  data = r.json()
170
178
  report["sol_balance"] = data.get("result", {}).get("value", 0) / 1e9
171
179
  except Exception:
@@ -183,16 +191,30 @@ def check_solana_wallet(wallet: str) -> dict:
183
191
  {"encoding": "jsonParsed"},
184
192
  ],
185
193
  }
186
- r = requests.post(SOLANA_RPC, json=payload, timeout=10)
194
+ r = requests.post(SOLANA_RPC, json=payload, timeout=15)
187
195
  data = r.json()
188
196
  accounts = data.get("result", {}).get("value", [])
189
197
 
198
+ registry = _get_token_registry()
199
+
190
200
  for acc in accounts:
191
201
  info = acc["account"]["data"]["parsed"]["info"]
192
202
  token_amount = info.get("tokenAmount", {})
203
+ mint = info.get("mint", "")
204
+ balance = token_amount.get("uiAmount", 0)
205
+
206
+ # Get token name from registry
207
+ name = "Unknown"
208
+ symbol = mint[:6]
209
+ if mint in registry:
210
+ name = registry[mint].get("name", "Unknown")
211
+ symbol = registry[mint].get("symbol", symbol)
212
+
193
213
  report["tokens"].append({
194
- "mint": info.get("mint", ""),
195
- "balance": token_amount.get("uiAmount", 0),
214
+ "mint": mint,
215
+ "name": name,
216
+ "symbol": symbol,
217
+ "balance": balance,
196
218
  "decimals": token_amount.get("decimals", 0),
197
219
  })
198
220
  except Exception:
@@ -207,15 +229,14 @@ def print_solana_token_report(report: dict):
207
229
  symbol = report["token_symbol"]
208
230
  print_header(f"SOLANA TOKEN CHECK — {name} ({symbol})")
209
231
 
210
- # Jupiter status
211
- if report["is_on_jupiter"]:
212
- print_ok("Listed on Jupiter")
213
- if report.get("is_on_jupiter_strict"):
214
- print_ok("On Jupiter Strict List (vetted)")
215
- else:
216
- print_warn("NOT on Jupiter Strict List")
232
+ # Known status
233
+ if report.get("is_known"):
234
+ print_ok("Listed in Solana Token Registry")
235
+ tags = report.get("tags", [])
236
+ if tags:
237
+ print_info(f"Tags: {', '.join(tags)}")
217
238
  else:
218
- print_fail("Not listed on Jupiter — unvetted token")
239
+ print_warn("NOT in Token Registry — unvetted token")
219
240
 
220
241
  # Freeze authority
221
242
  if report["freeze_authority"]:
@@ -229,24 +250,13 @@ def print_solana_token_report(report: dict):
229
250
  else:
230
251
  print_ok("Mint Authority: None (fixed supply)")
231
252
 
232
- # Holders
233
- if report["holder_count"]:
234
- if report["holder_count"] < 100:
235
- print_fail(f"Holders: {report['holder_count']:,}")
236
- elif report["holder_count"] < 1000:
237
- print_warn(f"Holders: {report['holder_count']:,}")
238
- else:
239
- print_ok(f"Holders: {report['holder_count']:,}")
240
-
241
- # Volume
242
- vol = report.get("daily_volume", 0)
243
- if vol:
244
- print_info(f"24h Volume: ${vol:,.0f}")
245
-
246
- # Market cap
247
- mc = report.get("market_cap", 0)
248
- if mc:
249
- print_info(f"Market Cap: ${mc:,.0f}")
253
+ # Supply
254
+ if report.get("total_supply_ui"):
255
+ print_info(f"Total Supply: {report['total_supply_ui']:,.2f}")
256
+
257
+ # Top holders
258
+ if report.get("top_holders"):
259
+ print_info(f"Top holders: {report['top_holders']} largest accounts")
250
260
 
251
261
  # Risk score
252
262
  score = report["risk_score"]
@@ -6,16 +6,47 @@ from typing import Optional
6
6
  import requests
7
7
  from web3 import Web3
8
8
 
9
- # Default RPC endpoints (public, no key needed)
9
+ # Default RPC endpoints (public, no key needed) — with fallbacks
10
10
  RPC_ENDPOINTS = {
11
- "eth": "https://eth.llamarpc.com",
12
- "bsc": "https://bsc-dataseed1.binance.org",
13
- "polygon": "https://polygon-rpc.com",
14
- "arbitrum": "https://arb1.arbitrum.io/rpc",
15
- "optimism": "https://mainnet.optimism.io",
16
- "base": "https://mainnet.base.org",
17
- "avalanche": "https://api.avax.network/ext/bc/C/rpc",
18
- "fantom": "https://rpc.ftm.tools",
11
+ "eth": [
12
+ "https://rpc.ankr.com/eth",
13
+ "https://eth.llamarpc.com",
14
+ "https://eth.drpc.org",
15
+ "https://cloudflare-eth.com",
16
+ ],
17
+ "bsc": [
18
+ "https://bsc-dataseed1.binance.org",
19
+ "https://bsc-dataseed2.binance.org",
20
+ "https://rpc.ankr.com/bsc",
21
+ ],
22
+ "polygon": [
23
+ "https://polygon-rpc.com",
24
+ "https://rpc.ankr.com/polygon",
25
+ "https://polygon.drpc.org",
26
+ ],
27
+ "arbitrum": [
28
+ "https://arb1.arbitrum.io/rpc",
29
+ "https://rpc.ankr.com/arbitrum",
30
+ "https://arbitrum.drpc.org",
31
+ ],
32
+ "optimism": [
33
+ "https://mainnet.optimism.io",
34
+ "https://rpc.ankr.com/optimism",
35
+ "https://optimism.drpc.org",
36
+ ],
37
+ "base": [
38
+ "https://mainnet.base.org",
39
+ "https://rpc.ankr.com/base",
40
+ "https://base.drpc.org",
41
+ ],
42
+ "avalanche": [
43
+ "https://api.avax.network/ext/bc/C/rpc",
44
+ "https://rpc.ankr.com/avalanche",
45
+ ],
46
+ "fantom": [
47
+ "https://rpc.ftm.tools",
48
+ "https://rpc.ankr.com/fantom",
49
+ ],
19
50
  }
20
51
 
21
52
  CHAIN_IDS = {
@@ -95,31 +126,7 @@ ERC20_ABI = [
95
126
  },
96
127
  ]
97
128
 
98
- # Transfer event ABI
99
- TRANSFER_EVENT = {
100
- "anonymous": False,
101
- "inputs": [
102
- {"indexed": True, "name": "from", "type": "address"},
103
- {"indexed": True, "name": "to", "type": "address"},
104
- {"indexed": False, "name": "value", "type": "uint256"},
105
- ],
106
- "name": "Transfer",
107
- "type": "event",
108
- }
109
-
110
- # Approval event ABI
111
- APPROVAL_EVENT = {
112
- "anonymous": False,
113
- "inputs": [
114
- {"indexed": True, "name": "owner", "type": "address"},
115
- {"indexed": True, "name": "spender", "type": "address"},
116
- {"indexed": False, "name": "value", "type": "uint256"},
117
- ],
118
- "name": "Approval",
119
- "type": "event",
120
- }
121
-
122
- # Well-known addresses to label
129
+ # Known addresses to label
123
130
  KNOWN_ADDRESSES = {
124
131
  "0x7a250d5630b4cf539739df2c5dacb4c659f2488d": "Uniswap V2 Router",
125
132
  "0xe592427a0aece92de3edee1f18e0157c05861564": "Uniswap V3 Router",
@@ -140,11 +147,23 @@ KNOWN_ADDRESSES = {
140
147
 
141
148
 
142
149
  def get_web3(chain: str = "eth") -> Web3:
143
- rpc = RPC_ENDPOINTS.get(chain)
144
- if not rpc:
150
+ """Get Web3 instance with automatic RPC fallback."""
151
+ endpoints = RPC_ENDPOINTS.get(chain)
152
+ if not endpoints:
145
153
  print(f"Unsupported chain: {chain}")
146
154
  sys.exit(1)
147
- return Web3(Web3.HTTPProvider(rpc))
155
+
156
+ for rpc in endpoints:
157
+ try:
158
+ w3 = Web3(Web3.HTTPProvider(rpc, request_kwargs={"timeout": 10}))
159
+ # Test connection
160
+ w3.eth.block_number
161
+ return w3
162
+ except Exception:
163
+ continue
164
+
165
+ print(f"All RPC endpoints failed for {chain}")
166
+ sys.exit(1)
148
167
 
149
168
 
150
169
  def is_valid_address(addr: str) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cryptoshield
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: All-in-one crypto security toolkit — honeypot, approvals, rugpull, phishing
5
5
  Author-email: yossweh <cilokcilok15@gmail.com>
6
6
  License: MIT
@@ -30,6 +30,27 @@ Dynamic: license-file
30
30
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
31
31
  [![PyPI](https://img.shields.io/pypi/v/cryptoshield.svg)](https://pypi.org/project/cryptoshield/)
32
32
 
33
+ ## Why CryptoShield?
34
+
35
+ Every day, people lose money to:
36
+ - **Honeypot tokens** — you can buy but can't sell. Your funds are trapped.
37
+ - **Unlimited token approvals** — you gave a random contract permission to drain your wallet. Months later, they do.
38
+ - **Rugpulls** — team dumps all tokens, liquidity vanishes, price goes to zero.
39
+ - **Phishing sites** — fake airdrop pages that look like Uniswap but steal your seed phrase.
40
+
41
+ CryptoShield combines all these checks into **one CLI tool**. No API keys needed. No accounts. No BS. Just run a command and get a clear report.
42
+
43
+ ```
44
+ # Before you ape into that token:
45
+ cryptoshield check 0xToken
46
+
47
+ # Before you connect your wallet to some random site:
48
+ cryptoshield check-url suspicious-site.com
49
+
50
+ # Check if your wallet has dangerous approvals:
51
+ cryptoshield approvals 0xYourWallet
52
+ ```
53
+
33
54
  ## Features
34
55
 
35
56
  - **🍯 Honeypot Detection** — Can you sell? Hidden taxes? Mint function? Check before you buy.
@@ -118,6 +139,13 @@ cryptoshield batch wallets.txt --mode approvals --chain bsc
118
139
  ✅ Mint Authority: None (fixed supply)
119
140
  ✅ Holders: 5,432
120
141
  ✅ Risk Score: 0/100 — LOW RISK
142
+
143
+ 📋 APPROVAL AUDIT — 0x47ac...8188
144
+ ❌ USDT → UNLIMITED to 0xUnkn...abcd
145
+ ⚡ RECOMMEND: revoke immediately
146
+ ⚠️ WETH → unlimited to Uniswap V2 Router
147
+ Known protocol — consider reducing allowance
148
+ ✅ DAI → 500.00 to Uniswap V3 Router
121
149
  ```
122
150
 
123
151
  ## Supported Chains
@@ -132,16 +160,21 @@ cryptoshield batch wallets.txt --mode approvals --chain bsc
132
160
  | Base | ✅ | ✅ | ✅ |
133
161
  | Avalanche | ✅ | ✅ | ✅ |
134
162
  | Fantom | ✅ | ✅ | ✅ |
135
- | Solana | ✅ | | ✅ |
163
+ | Solana | ✅ | 🔜 | ✅ |
164
+
165
+ > 🔜 = Coming soon. SPL token delegation uses a different model than ERC-20 approvals.
166
+
167
+ ## How It Works
136
168
 
137
- ## Data Sources
169
+ | Check | Data Source | Key Needed? |
170
+ |-------|-----------|-------------|
171
+ | Honeypot | GoPlus Security API | No |
172
+ | Approvals | On-chain events (RPC) | No |
173
+ | Rugpull | GoPlus + on-chain heuristics | No |
174
+ | Phishing | Pattern matching + 60+ known domains | No |
175
+ | Solana tokens | Jupiter + Birdeye + Solana RPC | No |
138
176
 
139
- - **GoPlus Security API** Honeypot detection, token security analysis (free, no key)
140
- - **Jupiter API** — Solana token data, strict list
141
- - **Birdeye API** — Solana holder count, volume
142
- - **On-chain data** — Approval events, contract code, ownership (direct RPC)
143
- - **Heuristics** — URL pattern matching, domain analysis, typosquatting detection
144
- - **Community scam database** — 60+ known phishing domains
177
+ Everything runs with public APIs and free RPC endpoints. No signups. No API keys. No tracking.
145
178
 
146
179
  ## Contributing
147
180
 
@@ -149,6 +182,7 @@ PRs welcome! Especially:
149
182
 
150
183
  - More scam domains / phishing patterns
151
184
  - More chain support (TON, Sui, Aptos)
185
+ - SPL token delegation scanner (Solana approvals)
152
186
  - Better rugpull heuristics
153
187
  - UI improvements
154
188
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cryptoshield"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "All-in-one crypto security toolkit — honeypot, approvals, rugpull, phishing"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes