cryptoshield 0.2.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.
- cryptoshield-0.2.0/LICENSE +21 -0
- cryptoshield-0.2.0/PKG-INFO +157 -0
- cryptoshield-0.2.0/README.md +133 -0
- cryptoshield-0.2.0/cryptoshield/__init__.py +4 -0
- cryptoshield-0.2.0/cryptoshield/approvals.py +185 -0
- cryptoshield-0.2.0/cryptoshield/cli.py +166 -0
- cryptoshield-0.2.0/cryptoshield/db.py +50 -0
- cryptoshield-0.2.0/cryptoshield/honeypot.py +219 -0
- cryptoshield-0.2.0/cryptoshield/phishing.py +454 -0
- cryptoshield-0.2.0/cryptoshield/rugpull.py +141 -0
- cryptoshield-0.2.0/cryptoshield/solana.py +264 -0
- cryptoshield-0.2.0/cryptoshield/utils.py +194 -0
- cryptoshield-0.2.0/cryptoshield.egg-info/PKG-INFO +157 -0
- cryptoshield-0.2.0/cryptoshield.egg-info/SOURCES.txt +18 -0
- cryptoshield-0.2.0/cryptoshield.egg-info/dependency_links.txt +1 -0
- cryptoshield-0.2.0/cryptoshield.egg-info/entry_points.txt +2 -0
- cryptoshield-0.2.0/cryptoshield.egg-info/requires.txt +3 -0
- cryptoshield-0.2.0/cryptoshield.egg-info/top_level.txt +1 -0
- cryptoshield-0.2.0/pyproject.toml +36 -0
- cryptoshield-0.2.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yossweh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cryptoshield
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: All-in-one crypto security toolkit ā honeypot, approvals, rugpull, phishing
|
|
5
|
+
Author-email: yossweh <cilokcilok15@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yossweh/cryptoshield
|
|
8
|
+
Project-URL: Repository, https://github.com/yossweh/cryptoshield
|
|
9
|
+
Project-URL: Issues, https://github.com/yossweh/cryptoshield/issues
|
|
10
|
+
Keywords: crypto,security,honeypot,rugpull,web3,defi,approval
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: typer>=0.9.0
|
|
21
|
+
Requires-Dist: web3>=6.0.0
|
|
22
|
+
Requires-Dist: requests>=2.28.0
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# š” CryptoShield
|
|
26
|
+
|
|
27
|
+
**All-in-one crypto security toolkit.** Check tokens before you buy. Scan your wallet for dangerous approvals. Detect rugpulls. Block phishing sites.
|
|
28
|
+
|
|
29
|
+
[](https://python.org)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
[](https://pypi.org/project/cryptoshield/)
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- **šÆ Honeypot Detection** ā Can you sell? Hidden taxes? Mint function? Check before you buy.
|
|
36
|
+
- **š Approval Scanner** ā Find all token approvals on your wallet. Flag dangerous unlimited approvals.
|
|
37
|
+
- **š“ Rugpull Scorer** ā Analyze contracts for common rug patterns. Score 0-100.
|
|
38
|
+
- **š£ Phishing Checker** ā 60+ known scam domains. Typosquatting, fake airdrops, wallet drainers.
|
|
39
|
+
- **āļø Solana Support** ā Check SPL tokens, freeze/mint authority, Jupiter listing status.
|
|
40
|
+
- **š¦ Batch Mode** ā Check 100+ tokens/wallets from a file.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install cryptoshield
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or from source:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/yossweh/cryptoshield
|
|
52
|
+
cd cryptoshield
|
|
53
|
+
pip install -e .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Full security report (EVM)
|
|
59
|
+
```bash
|
|
60
|
+
cryptoshield check 0xdAC17F958D2ee523a2206206994597C13D831ec7
|
|
61
|
+
cryptoshield check 0xToken --chain bsc
|
|
62
|
+
cryptoshield check 0xToken --quick # honeypot only
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Solana token check
|
|
66
|
+
```bash
|
|
67
|
+
cryptoshield check EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v --chain solana
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Scan wallet approvals
|
|
71
|
+
```bash
|
|
72
|
+
cryptoshield approvals 0xYourWallet
|
|
73
|
+
cryptoshield approvals 0xYourWallet --chain polygon
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Check phishing URL
|
|
77
|
+
```bash
|
|
78
|
+
cryptoshield check-url uniswap-airdrop.com
|
|
79
|
+
cryptoshield check-url https://app.uniswap.org
|
|
80
|
+
cryptoshield check-url metamask-sync.xyz
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Solana wallet scan
|
|
84
|
+
```bash
|
|
85
|
+
cryptoshield solana YourSolanaWalletAddress
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Batch check
|
|
89
|
+
```bash
|
|
90
|
+
cryptoshield batch tokens.txt --mode honeypot
|
|
91
|
+
cryptoshield batch tokens.txt --mode honeypot --chain solana
|
|
92
|
+
cryptoshield batch wallets.txt --mode approvals --chain bsc
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Example Output
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
š” CRYPTO SHIELD REPORT
|
|
99
|
+
āāāāāāāāāāāāāāāāāāāāāāā
|
|
100
|
+
|
|
101
|
+
šÆ HONEYPOT CHECK ā Tether USD (USDT)
|
|
102
|
+
ā
Can sell: YES
|
|
103
|
+
ā
Tax: 0% buy / 0% sell
|
|
104
|
+
ā Owner can mint: YES ā infinite supply risk
|
|
105
|
+
ā Owner can change balances: YES
|
|
106
|
+
ā
Contract: Verified
|
|
107
|
+
ā¹ļø Holders: 14,585,422
|
|
108
|
+
ā ļø Risk Score: 30/100 ā MEDIUM RISK
|
|
109
|
+
|
|
110
|
+
š£ PHISHING CHECK ā uniswap-airdrop.com
|
|
111
|
+
šØ KNOWN SCAM DOMAIN ā DO NOT VISIT
|
|
112
|
+
ā KNOWN SCAM DOMAIN ā uniswap-airdrop.com is in scam database
|
|
113
|
+
|
|
114
|
+
āļø SOLANA TOKEN CHECK ā SafeToken (SAFE)
|
|
115
|
+
ā
Listed on Jupiter
|
|
116
|
+
ā
On Jupiter Strict List (vetted)
|
|
117
|
+
ā
Freeze Authority: None
|
|
118
|
+
ā
Mint Authority: None (fixed supply)
|
|
119
|
+
ā
Holders: 5,432
|
|
120
|
+
ā
Risk Score: 0/100 ā LOW RISK
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Supported Chains
|
|
124
|
+
|
|
125
|
+
| Chain | Honeypot | Approvals | Rugpull |
|
|
126
|
+
|-------|----------|-----------|---------|
|
|
127
|
+
| Ethereum | ā
| ā
| ā
|
|
|
128
|
+
| BSC | ā
| ā
| ā
|
|
|
129
|
+
| Polygon | ā
| ā
| ā
|
|
|
130
|
+
| Arbitrum | ā
| ā
| ā
|
|
|
131
|
+
| Optimism | ā
| ā
| ā
|
|
|
132
|
+
| Base | ā
| ā
| ā
|
|
|
133
|
+
| Avalanche | ā
| ā
| ā
|
|
|
134
|
+
| Fantom | ā
| ā
| ā
|
|
|
135
|
+
| Solana | ā
| ā | ā
|
|
|
136
|
+
|
|
137
|
+
## Data Sources
|
|
138
|
+
|
|
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
|
|
145
|
+
|
|
146
|
+
## Contributing
|
|
147
|
+
|
|
148
|
+
PRs welcome! Especially:
|
|
149
|
+
|
|
150
|
+
- More scam domains / phishing patterns
|
|
151
|
+
- More chain support (TON, Sui, Aptos)
|
|
152
|
+
- Better rugpull heuristics
|
|
153
|
+
- UI improvements
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# š” CryptoShield
|
|
2
|
+
|
|
3
|
+
**All-in-one crypto security toolkit.** Check tokens before you buy. Scan your wallet for dangerous approvals. Detect rugpulls. Block phishing sites.
|
|
4
|
+
|
|
5
|
+
[](https://python.org)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://pypi.org/project/cryptoshield/)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **šÆ Honeypot Detection** ā Can you sell? Hidden taxes? Mint function? Check before you buy.
|
|
12
|
+
- **š Approval Scanner** ā Find all token approvals on your wallet. Flag dangerous unlimited approvals.
|
|
13
|
+
- **š“ Rugpull Scorer** ā Analyze contracts for common rug patterns. Score 0-100.
|
|
14
|
+
- **š£ Phishing Checker** ā 60+ known scam domains. Typosquatting, fake airdrops, wallet drainers.
|
|
15
|
+
- **āļø Solana Support** ā Check SPL tokens, freeze/mint authority, Jupiter listing status.
|
|
16
|
+
- **š¦ Batch Mode** ā Check 100+ tokens/wallets from a file.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install cryptoshield
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or from source:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/yossweh/cryptoshield
|
|
28
|
+
cd cryptoshield
|
|
29
|
+
pip install -e .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Full security report (EVM)
|
|
35
|
+
```bash
|
|
36
|
+
cryptoshield check 0xdAC17F958D2ee523a2206206994597C13D831ec7
|
|
37
|
+
cryptoshield check 0xToken --chain bsc
|
|
38
|
+
cryptoshield check 0xToken --quick # honeypot only
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Solana token check
|
|
42
|
+
```bash
|
|
43
|
+
cryptoshield check EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v --chain solana
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Scan wallet approvals
|
|
47
|
+
```bash
|
|
48
|
+
cryptoshield approvals 0xYourWallet
|
|
49
|
+
cryptoshield approvals 0xYourWallet --chain polygon
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Check phishing URL
|
|
53
|
+
```bash
|
|
54
|
+
cryptoshield check-url uniswap-airdrop.com
|
|
55
|
+
cryptoshield check-url https://app.uniswap.org
|
|
56
|
+
cryptoshield check-url metamask-sync.xyz
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Solana wallet scan
|
|
60
|
+
```bash
|
|
61
|
+
cryptoshield solana YourSolanaWalletAddress
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Batch check
|
|
65
|
+
```bash
|
|
66
|
+
cryptoshield batch tokens.txt --mode honeypot
|
|
67
|
+
cryptoshield batch tokens.txt --mode honeypot --chain solana
|
|
68
|
+
cryptoshield batch wallets.txt --mode approvals --chain bsc
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Example Output
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
š” CRYPTO SHIELD REPORT
|
|
75
|
+
āāāāāāāāāāāāāāāāāāāāāāā
|
|
76
|
+
|
|
77
|
+
šÆ HONEYPOT CHECK ā Tether USD (USDT)
|
|
78
|
+
ā
Can sell: YES
|
|
79
|
+
ā
Tax: 0% buy / 0% sell
|
|
80
|
+
ā Owner can mint: YES ā infinite supply risk
|
|
81
|
+
ā Owner can change balances: YES
|
|
82
|
+
ā
Contract: Verified
|
|
83
|
+
ā¹ļø Holders: 14,585,422
|
|
84
|
+
ā ļø Risk Score: 30/100 ā MEDIUM RISK
|
|
85
|
+
|
|
86
|
+
š£ PHISHING CHECK ā uniswap-airdrop.com
|
|
87
|
+
šØ KNOWN SCAM DOMAIN ā DO NOT VISIT
|
|
88
|
+
ā KNOWN SCAM DOMAIN ā uniswap-airdrop.com is in scam database
|
|
89
|
+
|
|
90
|
+
āļø SOLANA TOKEN CHECK ā SafeToken (SAFE)
|
|
91
|
+
ā
Listed on Jupiter
|
|
92
|
+
ā
On Jupiter Strict List (vetted)
|
|
93
|
+
ā
Freeze Authority: None
|
|
94
|
+
ā
Mint Authority: None (fixed supply)
|
|
95
|
+
ā
Holders: 5,432
|
|
96
|
+
ā
Risk Score: 0/100 ā LOW RISK
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Supported Chains
|
|
100
|
+
|
|
101
|
+
| Chain | Honeypot | Approvals | Rugpull |
|
|
102
|
+
|-------|----------|-----------|---------|
|
|
103
|
+
| Ethereum | ā
| ā
| ā
|
|
|
104
|
+
| BSC | ā
| ā
| ā
|
|
|
105
|
+
| Polygon | ā
| ā
| ā
|
|
|
106
|
+
| Arbitrum | ā
| ā
| ā
|
|
|
107
|
+
| Optimism | ā
| ā
| ā
|
|
|
108
|
+
| Base | ā
| ā
| ā
|
|
|
109
|
+
| Avalanche | ā
| ā
| ā
|
|
|
110
|
+
| Fantom | ā
| ā
| ā
|
|
|
111
|
+
| Solana | ā
| ā | ā
|
|
|
112
|
+
|
|
113
|
+
## Data Sources
|
|
114
|
+
|
|
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
|
|
121
|
+
|
|
122
|
+
## Contributing
|
|
123
|
+
|
|
124
|
+
PRs welcome! Especially:
|
|
125
|
+
|
|
126
|
+
- More scam domains / phishing patterns
|
|
127
|
+
- More chain support (TON, Sui, Aptos)
|
|
128
|
+
- Better rugpull heuristics
|
|
129
|
+
- UI improvements
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Token approval scanner ā find dangerous approvals on your wallet.
|
|
2
|
+
|
|
3
|
+
Scans on-chain Approval events to find all active token approvals.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from web3 import Web3
|
|
7
|
+
from .utils import (
|
|
8
|
+
get_web3, checksum, label_address, ERC20_ABI, APPROVAL_EVENT,
|
|
9
|
+
KNOWN_ADDRESSES, print_header, print_ok, print_warn, print_fail, print_info,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# Common approval spender labels
|
|
13
|
+
SPENDER_LABELS = {
|
|
14
|
+
**KNOWN_ADDRESSES,
|
|
15
|
+
"0x0000000000000000000000000000000000000000": "Zero Address (burn)",
|
|
16
|
+
"0x000000000000000000000000000000000000dead": "Dead Address (burn)",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Approvals above this are considered "unlimited"
|
|
20
|
+
UNLIMITED_THRESHOLD = 2**250
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def scan_approvals(wallet: str, chain: str = "eth", blocks_back: int = 100_000) -> list:
|
|
24
|
+
"""Scan all token approvals for a wallet."""
|
|
25
|
+
w3 = get_web3(chain)
|
|
26
|
+
wallet = checksum(wallet)
|
|
27
|
+
|
|
28
|
+
current_block = w3.eth.block_number
|
|
29
|
+
from_block = max(0, current_block - blocks_back)
|
|
30
|
+
|
|
31
|
+
# Get Approval events where owner = wallet
|
|
32
|
+
approval_topic = w3.keccak(
|
|
33
|
+
text="Approval(address,address,uint256)"
|
|
34
|
+
).hex()
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
logs = w3.eth.get_logs({
|
|
38
|
+
"fromBlock": from_block,
|
|
39
|
+
"toBlock": current_block,
|
|
40
|
+
"topics": [
|
|
41
|
+
approval_topic,
|
|
42
|
+
"0x" + wallet[2:].lower().zfill(64), # owner
|
|
43
|
+
],
|
|
44
|
+
})
|
|
45
|
+
except Exception as e:
|
|
46
|
+
return [{"error": str(e)}]
|
|
47
|
+
|
|
48
|
+
# Deduplicate: keep latest approval per (token, spender)
|
|
49
|
+
approvals = {}
|
|
50
|
+
for log in logs:
|
|
51
|
+
try:
|
|
52
|
+
token_addr = log["address"]
|
|
53
|
+
spender = "0x" + log["topics"][2].hex()[-40:]
|
|
54
|
+
spender = Web3.to_checksum_address(spender)
|
|
55
|
+
value = int(log["data"].hex(), 16)
|
|
56
|
+
|
|
57
|
+
key = (token_addr.lower(), spender.lower())
|
|
58
|
+
approvals[key] = {
|
|
59
|
+
"token": token_addr,
|
|
60
|
+
"spender": spender,
|
|
61
|
+
"value": value,
|
|
62
|
+
"block": log["blockNumber"],
|
|
63
|
+
}
|
|
64
|
+
except Exception:
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
# Filter: only keep current approvals (check on-chain allowance)
|
|
68
|
+
active = []
|
|
69
|
+
for (token_lower, spender_lower), info in approvals.items():
|
|
70
|
+
if info["value"] == 0:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
contract = w3.eth.contract(
|
|
75
|
+
address=checksum(info["token"]), abi=ERC20_ABI
|
|
76
|
+
)
|
|
77
|
+
current_allowance = contract.functions.allowance(
|
|
78
|
+
wallet, checksum(info["spender"])
|
|
79
|
+
).call()
|
|
80
|
+
|
|
81
|
+
if current_allowance == 0:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
# Get token info
|
|
85
|
+
try:
|
|
86
|
+
name = contract.functions.name().call()
|
|
87
|
+
except Exception:
|
|
88
|
+
name = "Unknown"
|
|
89
|
+
try:
|
|
90
|
+
symbol = contract.functions.symbol().call()
|
|
91
|
+
except Exception:
|
|
92
|
+
symbol = "?"
|
|
93
|
+
|
|
94
|
+
is_unlimited = current_allowance >= UNLIMITED_THRESHOLD
|
|
95
|
+
spender_label = SPENDER_LABELS.get(
|
|
96
|
+
info["spender"].lower(), "Unknown Contract"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
active.append({
|
|
100
|
+
"token": info["token"],
|
|
101
|
+
"token_name": name,
|
|
102
|
+
"token_symbol": symbol,
|
|
103
|
+
"spender": info["spender"],
|
|
104
|
+
"spender_label": spender_label,
|
|
105
|
+
"allowance": current_allowance,
|
|
106
|
+
"is_unlimited": is_unlimited,
|
|
107
|
+
"is_known": info["spender"].lower() in KNOWN_ADDRESSES,
|
|
108
|
+
})
|
|
109
|
+
except Exception:
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
return active
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def print_approval_report(approvals: list, wallet: str):
|
|
116
|
+
"""Pretty-print approval scan results."""
|
|
117
|
+
if not approvals:
|
|
118
|
+
print_ok("No active approvals found ā your wallet is clean!")
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
if "error" in approvals[0]:
|
|
122
|
+
print_fail(f"Error: {approvals[0]['error']}")
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
print_header(f"APPROVAL AUDIT ā {wallet[:6]}...{wallet[-4:]}")
|
|
126
|
+
|
|
127
|
+
dangerous = []
|
|
128
|
+
caution = []
|
|
129
|
+
safe = []
|
|
130
|
+
|
|
131
|
+
for a in approvals:
|
|
132
|
+
if a["is_unlimited"] and not a["is_known"]:
|
|
133
|
+
dangerous.append(a)
|
|
134
|
+
elif a["is_unlimited"] and a["is_known"]:
|
|
135
|
+
caution.append(a)
|
|
136
|
+
else:
|
|
137
|
+
safe.append(a)
|
|
138
|
+
|
|
139
|
+
# Dangerous first
|
|
140
|
+
for a in dangerous:
|
|
141
|
+
print_fail(
|
|
142
|
+
f"{a['token_symbol']} ā UNLIMITED to {label_address(a['spender'])}"
|
|
143
|
+
)
|
|
144
|
+
print(f" ā” RECOMMEND: revoke immediately")
|
|
145
|
+
|
|
146
|
+
# Caution
|
|
147
|
+
for a in caution:
|
|
148
|
+
print_warn(
|
|
149
|
+
f"{a['token_symbol']} ā unlimited to {label_address(a['spender'])}"
|
|
150
|
+
)
|
|
151
|
+
print(f" Known protocol ā consider reducing allowance")
|
|
152
|
+
|
|
153
|
+
# Safe
|
|
154
|
+
for a in safe:
|
|
155
|
+
if a["allowance"] > 10**18:
|
|
156
|
+
amount = f"{a['allowance'] / 10**18:.2f}"
|
|
157
|
+
else:
|
|
158
|
+
amount = str(a["allowance"])
|
|
159
|
+
print_ok(
|
|
160
|
+
f"{a['token_symbol']} ā {amount} to {label_address(a['spender'])}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
print()
|
|
164
|
+
print_info(f"Total approvals: {len(approvals)}")
|
|
165
|
+
if dangerous:
|
|
166
|
+
print_fail(f"{len(dangerous)} HIGH RISK ā revoke now!")
|
|
167
|
+
if caution:
|
|
168
|
+
print_warn(f"{len(caution)} caution ā review recommended")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
REVOKE4_ABI = [
|
|
172
|
+
{
|
|
173
|
+
"inputs": [
|
|
174
|
+
{"name": "token", "type": "address"},
|
|
175
|
+
{"name": "spender", "type": "address"},
|
|
176
|
+
],
|
|
177
|
+
"name": "revoke",
|
|
178
|
+
"outputs": [],
|
|
179
|
+
"stateMutability": "nonpayable",
|
|
180
|
+
"type": "function",
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
# revoke.xyz contract
|
|
185
|
+
REVOKE_CONTRACT = "0x000000000000Dd366e1DA4F6c8a2b2F9e01f6F1E"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""CryptoShield CLI ā All-in-one crypto security toolkit."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from . import __version__
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(
|
|
9
|
+
name="cryptoshield",
|
|
10
|
+
help="š” CryptoShield ā All-in-one crypto security toolkit",
|
|
11
|
+
add_completion=False,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.command()
|
|
16
|
+
def check(
|
|
17
|
+
address: str = typer.Argument(..., help="Token contract address"),
|
|
18
|
+
chain: str = typer.Option("eth", "-c", "--chain", help="Chain: eth, bsc, polygon, arbitrum, optimism, base, avalanche, fantom, solana"),
|
|
19
|
+
quick: bool = typer.Option(False, "-q", "--quick", help="Quick check (honeypot only)"),
|
|
20
|
+
):
|
|
21
|
+
"""Full security report for a token contract."""
|
|
22
|
+
if chain == "solana":
|
|
23
|
+
from .solana import check_solana_token, print_solana_token_report
|
|
24
|
+
print(f"\nš” CRYPTO SHIELD ā Scanning Solana token {address[:10]}...")
|
|
25
|
+
print("ā" * 50)
|
|
26
|
+
report = check_solana_token(address)
|
|
27
|
+
print_solana_token_report(report)
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
from .honeypot import check_honeypot, print_honeypot_report
|
|
31
|
+
from .rugpull import analyze_rugpull, print_rugpull_report
|
|
32
|
+
|
|
33
|
+
print(f"\nš” CRYPTO SHIELD ā Scanning {address[:10]}... on {chain}")
|
|
34
|
+
print("ā" * 50)
|
|
35
|
+
|
|
36
|
+
if quick:
|
|
37
|
+
report = check_honeypot(address, chain)
|
|
38
|
+
print_honeypot_report(report)
|
|
39
|
+
else:
|
|
40
|
+
report = analyze_rugpull(address, chain)
|
|
41
|
+
print_rugpull_report(report)
|
|
42
|
+
print()
|
|
43
|
+
hp = check_honeypot(address, chain)
|
|
44
|
+
print_honeypot_report(hp)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command()
|
|
48
|
+
def approvals(
|
|
49
|
+
wallet: str = typer.Argument(..., help="Wallet address to scan"),
|
|
50
|
+
chain: str = typer.Option("eth", "-c", "--chain", help="Chain to scan"),
|
|
51
|
+
blocks: int = typer.Option(100000, "-b", "--blocks", help="How many blocks back to scan"),
|
|
52
|
+
):
|
|
53
|
+
"""Scan token approvals for a wallet."""
|
|
54
|
+
from .approvals import scan_approvals, print_approval_report
|
|
55
|
+
|
|
56
|
+
print(f"\nš” Scanning approvals for {wallet[:10]}... on {chain}")
|
|
57
|
+
results = scan_approvals(wallet, chain, blocks)
|
|
58
|
+
print_approval_report(results, wallet)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@app.command()
|
|
62
|
+
def rugpull(
|
|
63
|
+
address: str = typer.Argument(..., help="Token contract address"),
|
|
64
|
+
chain: str = typer.Option("eth", "-c", "--chain", help="Chain"),
|
|
65
|
+
):
|
|
66
|
+
"""Analyze rugpull risk for a token."""
|
|
67
|
+
if chain == "solana":
|
|
68
|
+
from .solana import check_solana_token, print_solana_token_report
|
|
69
|
+
report = check_solana_token(address)
|
|
70
|
+
print_solana_token_report(report)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
from .rugpull import analyze_rugpull, print_rugpull_report
|
|
74
|
+
|
|
75
|
+
report = analyze_rugpull(address, chain)
|
|
76
|
+
print_rugpull_report(report)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@app.command("check-url")
|
|
80
|
+
def check_url_cmd(
|
|
81
|
+
url: str = typer.Argument(..., help="URL to check"),
|
|
82
|
+
):
|
|
83
|
+
"""Check if a URL is a known or suspected phishing site."""
|
|
84
|
+
from .phishing import check_url, print_phishing_report
|
|
85
|
+
|
|
86
|
+
report = check_url(url)
|
|
87
|
+
print_phishing_report(report)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@app.command()
|
|
91
|
+
def solana(
|
|
92
|
+
wallet: str = typer.Argument(..., help="Solana wallet address"),
|
|
93
|
+
):
|
|
94
|
+
"""Check Solana wallet ā SOL balance + token holdings."""
|
|
95
|
+
from .solana import check_solana_wallet
|
|
96
|
+
|
|
97
|
+
print(f"\nš” Solana wallet scan ā {wallet[:10]}...")
|
|
98
|
+
print("ā" * 50)
|
|
99
|
+
report = check_solana_wallet(wallet)
|
|
100
|
+
|
|
101
|
+
print(f"\n š° SOL Balance: {report['sol_balance']:.4f} SOL")
|
|
102
|
+
print(f" š¦ Token accounts: {len(report['tokens'])}")
|
|
103
|
+
|
|
104
|
+
if report["tokens"]:
|
|
105
|
+
print("\n Token Holdings:")
|
|
106
|
+
for t in report["tokens"]:
|
|
107
|
+
if t["balance"] > 0:
|
|
108
|
+
print(f" ⢠{t['mint'][:8]}...{t['mint'][-4:]}: {t['balance']:,.2f}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@app.command()
|
|
112
|
+
def batch(
|
|
113
|
+
addresses_file: str = typer.Argument(..., help="File with addresses (one per line)"),
|
|
114
|
+
chain: str = typer.Option("eth", "-c", "--chain", help="Chain"),
|
|
115
|
+
mode: str = typer.Option("honeypot", "-m", "--mode", help="Mode: honeypot, approvals, rugpull"),
|
|
116
|
+
):
|
|
117
|
+
"""Batch check multiple addresses from a file."""
|
|
118
|
+
from pathlib import Path
|
|
119
|
+
|
|
120
|
+
path = Path(addresses_file)
|
|
121
|
+
if not path.exists():
|
|
122
|
+
print(f"File not found: {addresses_file}")
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
addresses = [line.strip() for line in path.read_text().splitlines() if line.strip()]
|
|
126
|
+
print(f"\nš” Batch {mode} check ā {len(addresses)} addresses on {chain}")
|
|
127
|
+
print("ā" * 50)
|
|
128
|
+
|
|
129
|
+
for i, addr in enumerate(addresses, 1):
|
|
130
|
+
print(f"\n[{i}/{len(addresses)}] {addr[:10]}...")
|
|
131
|
+
if mode == "honeypot":
|
|
132
|
+
if chain == "solana":
|
|
133
|
+
from .solana import check_solana_token, print_solana_token_report
|
|
134
|
+
report = check_solana_token(addr)
|
|
135
|
+
print_solana_token_report(report)
|
|
136
|
+
else:
|
|
137
|
+
from .honeypot import check_honeypot, print_honeypot_report
|
|
138
|
+
report = check_honeypot(addr, chain)
|
|
139
|
+
print_honeypot_report(report)
|
|
140
|
+
elif mode == "rugpull":
|
|
141
|
+
if chain == "solana":
|
|
142
|
+
from .solana import check_solana_token, print_solana_token_report
|
|
143
|
+
report = check_solana_token(addr)
|
|
144
|
+
print_solana_token_report(report)
|
|
145
|
+
else:
|
|
146
|
+
from .rugpull import analyze_rugpull, print_rugpull_report
|
|
147
|
+
report = analyze_rugpull(addr, chain)
|
|
148
|
+
print_rugpull_report(report)
|
|
149
|
+
elif mode == "approvals":
|
|
150
|
+
from .approvals import scan_approvals, print_approval_report
|
|
151
|
+
results = scan_approvals(addr, chain)
|
|
152
|
+
print_approval_report(results, addr)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@app.command()
|
|
156
|
+
def version():
|
|
157
|
+
"""Show version."""
|
|
158
|
+
print(f"CryptoShield v{__version__}")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def main():
|
|
162
|
+
app()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
main()
|