sclaw-agent 1.0.0
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.
- package/README.md +228 -0
- package/cli/index.ts +159 -0
- package/dist/cli/index.d.ts +18 -0
- package/dist/cli/index.js +142 -0
- package/dist/server/config.d.ts +5 -0
- package/dist/server/config.js +5 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +18 -0
- package/dist/server/lib/claw-agent.d.ts +8 -0
- package/dist/server/lib/claw-agent.js +66 -0
- package/dist/server/lib/helius.d.ts +19 -0
- package/dist/server/lib/helius.js +96 -0
- package/dist/server/lib/token-analysis.d.ts +24 -0
- package/dist/server/lib/token-analysis.js +84 -0
- package/dist/server/routes/risk.d.ts +1 -0
- package/dist/server/routes/risk.js +57 -0
- package/dist/server/routes/simulate.d.ts +1 -0
- package/dist/server/routes/simulate.js +60 -0
- package/dist/server/routes/token.d.ts +1 -0
- package/dist/server/routes/token.js +36 -0
- package/package.json +59 -0
- package/server/config.ts +5 -0
- package/server/index.ts +23 -0
- package/server/lib/claw-agent.ts +78 -0
- package/server/lib/helius.ts +117 -0
- package/server/lib/token-analysis.ts +109 -0
- package/server/routes/risk.ts +65 -0
- package/server/routes/simulate.ts +68 -0
- package/server/routes/token.ts +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">SCLAW</h1>
|
|
3
|
+
<p align="center">ShadowClawAI Pre-Execution Engine</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<a href="#quick-start">Quick Start</a> ·
|
|
6
|
+
<a href="#cli">CLI</a> ·
|
|
7
|
+
<a href="#api">API</a> ·
|
|
8
|
+
<a href="#architecture">Architecture</a>
|
|
9
|
+
</p>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Pre-execution engine that simulates Solana transactions, analyzes token security on-chain, and produces AI risk reports via CLAW agents — before you sign anything.
|
|
15
|
+
|
|
16
|
+
All data sourced from **Helius RPC mainnet**. Zero mocks. Zero fakes.
|
|
17
|
+
|
|
18
|
+
## What It Does
|
|
19
|
+
|
|
20
|
+
**Transaction Simulation** — Dry-run any Solana transaction via Helius `simulateTransaction`. See program logs, compute units, balance changes, and errors without touching the chain.
|
|
21
|
+
|
|
22
|
+
**Token Security Analysis** — Real-time on-chain checks: mint authority, freeze authority, holder concentration, whale detection, metadata verification. Every query hits mainnet.
|
|
23
|
+
|
|
24
|
+
**CLAW Agent Intelligence** — AI-powered risk assessment via LLM (Claude through OpenRouter). Structured reports with risk score 0-100, threat level, specific flags, and detailed analysis. Any LLM can serve as a CLAW agent — swap the model in config.
|
|
25
|
+
|
|
26
|
+
**Wallet Risk Profiling** — Analyze any Solana address: SOL balance, token holdings, transaction patterns, bot detection, counterparty analysis.
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
git clone https://github.com/alc0hol120/SHADOWCLAW.git
|
|
32
|
+
cd SHADOWCLAW
|
|
33
|
+
npm install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Set your API keys:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
export HELIUS_API_KEY=your-helius-key
|
|
40
|
+
export OPENROUTER_API_KEY=your-openrouter-key
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Get a free Helius key at [helius.dev](https://helius.dev). Get an OpenRouter key at [openrouter.ai](https://openrouter.ai).
|
|
44
|
+
|
|
45
|
+
Start the server:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run server
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Verify:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl http://localhost:3001/api/health
|
|
55
|
+
# {"status":"CLAW Engine Online","version":"1.0.0"}
|
|
56
|
+
|
|
57
|
+
curl http://localhost:3001/api/token/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
|
|
58
|
+
# Returns USDC analysis + CLAW report
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## npm
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install sclaw-agent
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## CLI
|
|
68
|
+
|
|
69
|
+
Requires the server running (`npm run server`).
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx tsx cli/index.ts token EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
|
|
73
|
+
|
|
74
|
+
npx tsx cli/index.ts wallet <solana-address>
|
|
75
|
+
|
|
76
|
+
npx tsx cli/index.ts simulate <base64-transaction>
|
|
77
|
+
|
|
78
|
+
npx tsx cli/index.ts --help
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Example output:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
SCLAW — Token Security Analysis
|
|
85
|
+
Analyzing EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
|
|
86
|
+
|
|
87
|
+
USDC USD Coin
|
|
88
|
+
Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
|
|
89
|
+
Supply: 8,680,309,682.51
|
|
90
|
+
Decimals: 6
|
|
91
|
+
Mintable: YES
|
|
92
|
+
Freezable: YES
|
|
93
|
+
Top 10 concentration: 0%
|
|
94
|
+
|
|
95
|
+
CLAW Agent Report
|
|
96
|
+
Risk: 15/100 LOW
|
|
97
|
+
USDC is a legitimate stablecoin but has centralized controls
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API
|
|
101
|
+
|
|
102
|
+
All endpoints return JSON. Server runs on port 3001 by default.
|
|
103
|
+
|
|
104
|
+
| Method | Endpoint | Description |
|
|
105
|
+
|--------|----------|-------------|
|
|
106
|
+
| `GET` | `/api/health` | Server status check |
|
|
107
|
+
| `POST` | `/api/simulate/transaction` | Simulate a base64-encoded transaction |
|
|
108
|
+
| `POST` | `/api/simulate/preview` | Review executed transaction by signature |
|
|
109
|
+
| `GET` | `/api/token/:mint` | Full token security analysis + CLAW report |
|
|
110
|
+
| `GET` | `/api/token/:mint/history` | Recent token transactions via Helius |
|
|
111
|
+
| `GET` | `/api/risk/wallet/:address` | Wallet risk profile + CLAW report |
|
|
112
|
+
| `POST` | `/api/risk/analyze` | Free-form CLAW agent analysis |
|
|
113
|
+
|
|
114
|
+
### Simulate Transaction
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
curl -X POST http://localhost:3001/api/simulate/transaction \
|
|
118
|
+
-H "Content-Type: application/json" \
|
|
119
|
+
-d '{"transaction": "<base64-encoded-tx>", "walletAddress": "<optional>"}'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Response:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"simulation": {
|
|
127
|
+
"success": true,
|
|
128
|
+
"logs": ["Program 111... invoke [1]", "Program 111... success"],
|
|
129
|
+
"unitsConsumed": 150,
|
|
130
|
+
"error": null
|
|
131
|
+
},
|
|
132
|
+
"preBalance": 0.39,
|
|
133
|
+
"estimatedCostSOL": 0.00075,
|
|
134
|
+
"clawAnalysis": {
|
|
135
|
+
"riskScore": 15,
|
|
136
|
+
"riskLevel": "LOW",
|
|
137
|
+
"summary": "Standard system program transaction",
|
|
138
|
+
"flags": [],
|
|
139
|
+
"details": "..."
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Token Analysis
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
curl http://localhost:3001/api/token/<mint-address>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Response includes: mint/freeze authority status, supply, decimals, top 10 holder distribution with whale alerts, and CLAW agent risk report.
|
|
151
|
+
|
|
152
|
+
### Wallet Risk
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
curl http://localhost:3001/api/risk/wallet/<address>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Response includes: SOL balance, token accounts, recent transactions with native/token transfers, and CLAW agent analysis of wallet behavior patterns.
|
|
159
|
+
|
|
160
|
+
## Architecture
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
server/
|
|
164
|
+
index.ts Express API (port 3001)
|
|
165
|
+
config.ts Helius + OpenRouter keys (env vars)
|
|
166
|
+
lib/
|
|
167
|
+
helius.ts Solana mainnet RPC — simulateTransaction,
|
|
168
|
+
getTokenMetadata, getAsset, getBalance,
|
|
169
|
+
getTokenAccounts, getTransactionHistory
|
|
170
|
+
claw-agent.ts AI risk analysis — structured prompts,
|
|
171
|
+
JSON output parsing, fallback handling
|
|
172
|
+
token-analysis.ts On-chain security — mint/freeze authority,
|
|
173
|
+
holder concentration, whale detection
|
|
174
|
+
routes/
|
|
175
|
+
simulate.ts POST /simulate/transaction, /simulate/preview
|
|
176
|
+
token.ts GET /token/:mint, /token/:mint/history
|
|
177
|
+
risk.ts GET /risk/wallet/:address, POST /risk/analyze
|
|
178
|
+
cli/
|
|
179
|
+
index.ts Terminal interface — token, wallet, simulate
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### How It Works
|
|
183
|
+
|
|
184
|
+
1. Request comes in (API call, CLI command)
|
|
185
|
+
2. Server fetches real-time data from Solana via Helius RPC
|
|
186
|
+
3. For transactions: `simulateTransaction` with `replaceRecentBlockhash`
|
|
187
|
+
4. For tokens: mint account info, metadata, largest holders
|
|
188
|
+
5. For wallets: balance, token accounts, transaction history
|
|
189
|
+
6. All collected data is passed to CLAW agent (LLM)
|
|
190
|
+
7. CLAW agent returns structured risk report (score, level, flags, details)
|
|
191
|
+
8. Response sent back with raw data + AI analysis
|
|
192
|
+
|
|
193
|
+
### CLAW Agent
|
|
194
|
+
|
|
195
|
+
CLAW is the AI layer. It receives raw blockchain data and produces structured risk assessments. Under the hood it's an LLM (currently Claude via OpenRouter) with a specialized system prompt for blockchain risk analysis.
|
|
196
|
+
|
|
197
|
+
Any LLM can be a CLAW agent — swap the model in `server/lib/claw-agent.ts`. The prompt and output format stay the same.
|
|
198
|
+
|
|
199
|
+
Risk levels: `SAFE` (0-20) · `LOW` (21-40) · `MEDIUM` (41-60) · `HIGH` (61-80) · `CRITICAL` (81-100)
|
|
200
|
+
|
|
201
|
+
## Environment Variables
|
|
202
|
+
|
|
203
|
+
| Variable | Description | Required |
|
|
204
|
+
|----------|-------------|----------|
|
|
205
|
+
| `HELIUS_API_KEY` | Helius RPC API key | Yes |
|
|
206
|
+
| `OPENROUTER_API_KEY` | OpenRouter API key for CLAW agent | Yes |
|
|
207
|
+
| `PORT` | Server port (default: 3001) | No |
|
|
208
|
+
|
|
209
|
+
## Token Security Checks
|
|
210
|
+
|
|
211
|
+
| Check | What It Detects |
|
|
212
|
+
|-------|----------------|
|
|
213
|
+
| Mint Authority | Can new tokens be minted? Supply inflation risk |
|
|
214
|
+
| Freeze Authority | Can accounts be frozen? Honeypot risk |
|
|
215
|
+
| Holder Concentration | Top 10 wallet % of supply. Rug pull risk |
|
|
216
|
+
| Whale Detection | Single wallet >20% flagged |
|
|
217
|
+
| Metadata | Name, symbol, off-chain data verified |
|
|
218
|
+
|
|
219
|
+
## Stack
|
|
220
|
+
|
|
221
|
+
- **Helius RPC** — Solana mainnet data
|
|
222
|
+
- **Claude AI** via OpenRouter — CLAW agent intelligence
|
|
223
|
+
- **Express.js** — Backend API
|
|
224
|
+
- **TypeScript** — Everything
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT
|
package/cli/index.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const API_BASE = process.env.SCLAW_API || 'http://localhost:3001/api';
|
|
4
|
+
|
|
5
|
+
const COLORS = {
|
|
6
|
+
green: '\x1b[32m',
|
|
7
|
+
red: '\x1b[31m',
|
|
8
|
+
yellow: '\x1b[33m',
|
|
9
|
+
cyan: '\x1b[36m',
|
|
10
|
+
dim: '\x1b[2m',
|
|
11
|
+
bold: '\x1b[1m',
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function c(color: keyof typeof COLORS, text: string) {
|
|
16
|
+
return `${COLORS[color]}${text}${COLORS.reset}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function request(path: string, options?: RequestInit) {
|
|
20
|
+
const res = await fetch(`${API_BASE}${path}`, {
|
|
21
|
+
headers: { 'Content-Type': 'application/json' },
|
|
22
|
+
...options,
|
|
23
|
+
});
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
26
|
+
throw new Error(err.error || 'Request failed');
|
|
27
|
+
}
|
|
28
|
+
return res.json();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function printRisk(analysis: any) {
|
|
32
|
+
if (!analysis) return;
|
|
33
|
+
const levelColor = analysis.riskLevel === 'SAFE' || analysis.riskLevel === 'LOW' ? 'green'
|
|
34
|
+
: analysis.riskLevel === 'MEDIUM' ? 'yellow' : 'red';
|
|
35
|
+
|
|
36
|
+
console.log(`\n${c('bold', 'CLAW Agent Report')}`);
|
|
37
|
+
console.log(` Risk: ${c(levelColor, `${analysis.riskScore}/100 ${analysis.riskLevel}`)}`);
|
|
38
|
+
console.log(` ${analysis.summary}`);
|
|
39
|
+
if (analysis.flags?.length) {
|
|
40
|
+
console.log(`\n ${c('yellow', 'Flags:')}`);
|
|
41
|
+
analysis.flags.forEach((f: string) => console.log(` ${c('yellow', '▸')} ${f}`));
|
|
42
|
+
}
|
|
43
|
+
if (analysis.details) {
|
|
44
|
+
console.log(`\n ${c('dim', analysis.details)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function tokenCmd(mint: string) {
|
|
49
|
+
console.log(`\n${c('cyan', 'SCLAW')} ${c('dim', '— Token Security Analysis')}`);
|
|
50
|
+
console.log(`${c('dim', 'Analyzing')} ${mint}\n`);
|
|
51
|
+
|
|
52
|
+
const data = await request(`/token/${mint}`);
|
|
53
|
+
const t = data.token;
|
|
54
|
+
|
|
55
|
+
console.log(` ${c('bold', t.symbol || 'Unknown')} ${c('dim', t.name || '')}`);
|
|
56
|
+
console.log(` Mint: ${c('dim', t.mint)}`);
|
|
57
|
+
console.log(` Supply: ${t.supply !== null && t.decimals !== null ? (t.supply / Math.pow(10, t.decimals)).toLocaleString() : 'N/A'}`);
|
|
58
|
+
console.log(` Decimals: ${t.decimals ?? 'N/A'}`);
|
|
59
|
+
console.log(` Mintable: ${t.isMintable ? c('red', 'YES') : c('green', 'NO')}`);
|
|
60
|
+
console.log(` Freezable: ${t.isFreezable ? c('red', 'YES') : c('green', 'NO')}`);
|
|
61
|
+
console.log(` Top 10 concentration: ${t.holderConcentration > 80 ? c('red', t.holderConcentration + '%') : t.holderConcentration > 50 ? c('yellow', t.holderConcentration + '%') : c('green', t.holderConcentration + '%')}`);
|
|
62
|
+
|
|
63
|
+
if (t.risks?.length) {
|
|
64
|
+
console.log(`\n ${c('yellow', 'Risk Flags:')}`);
|
|
65
|
+
t.risks.forEach((r: string) => console.log(` ${c('yellow', '▸')} ${r}`));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (t.topHolders?.length) {
|
|
69
|
+
console.log(`\n ${c('dim', 'Top Holders:')}`);
|
|
70
|
+
t.topHolders.slice(0, 5).forEach((h: any, i: number) => {
|
|
71
|
+
console.log(` ${i + 1}. ${h.address.slice(0, 8)}...${h.address.slice(-6)} ${h.amount.toLocaleString()} ${h.percentage}%`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
printRisk(data.clawAnalysis);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function walletCmd(address: string) {
|
|
79
|
+
console.log(`\n${c('cyan', 'SCLAW')} ${c('dim', '— Wallet Risk Analysis')}`);
|
|
80
|
+
console.log(`${c('dim', 'Analyzing')} ${address}\n`);
|
|
81
|
+
|
|
82
|
+
const data = await request(`/risk/wallet/${address}`);
|
|
83
|
+
|
|
84
|
+
console.log(` Balance: ${data.balanceSOL} SOL`);
|
|
85
|
+
const txCount = data.recentTransactions?.length || 0;
|
|
86
|
+
console.log(` Recent transactions: ${txCount}`);
|
|
87
|
+
|
|
88
|
+
if (data.recentTransactions?.length) {
|
|
89
|
+
console.log(`\n ${c('dim', 'Recent Activity:')}`);
|
|
90
|
+
data.recentTransactions.slice(0, 5).forEach((tx: any) => {
|
|
91
|
+
console.log(` ${c('dim', tx.type || '?')} via ${tx.source || '?'}`);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
printRisk(data.clawAnalysis);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function simulateCmd(tx: string) {
|
|
99
|
+
console.log(`\n${c('cyan', 'SCLAW')} ${c('dim', '— Transaction Simulation')}\n`);
|
|
100
|
+
|
|
101
|
+
const data = await request('/simulate/transaction', {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
body: JSON.stringify({ transaction: tx }),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const sim = data.simulation;
|
|
107
|
+
console.log(` Result: ${sim.success ? c('green', 'SUCCESS') : c('red', 'FAILED')}`);
|
|
108
|
+
console.log(` Compute units: ${sim.unitsConsumed}`);
|
|
109
|
+
if (sim.error) console.log(` Error: ${c('red', sim.error)}`);
|
|
110
|
+
if (data.estimatedCostSOL) console.log(` Est. cost: ${data.estimatedCostSOL} SOL`);
|
|
111
|
+
|
|
112
|
+
if (sim.logs?.length) {
|
|
113
|
+
console.log(`\n ${c('dim', 'Logs:')}`);
|
|
114
|
+
sim.logs.slice(0, 10).forEach((l: string) => console.log(` ${c('dim', l)}`));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
printRisk(data.clawAnalysis);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function main() {
|
|
121
|
+
const args = process.argv.slice(2);
|
|
122
|
+
const cmd = args[0];
|
|
123
|
+
const arg = args[1];
|
|
124
|
+
|
|
125
|
+
if (!cmd || cmd === '--help' || cmd === '-h') {
|
|
126
|
+
console.log(`
|
|
127
|
+
${c('cyan', 'SCLAW')} ${c('dim', '— ShadowClawAI Pre-Execution Engine')}
|
|
128
|
+
|
|
129
|
+
${c('bold', 'Usage:')}
|
|
130
|
+
sclaw token <mint-address> Analyze token security
|
|
131
|
+
sclaw wallet <address> Wallet risk profile
|
|
132
|
+
sclaw simulate <base64-tx> Dry-run a transaction
|
|
133
|
+
|
|
134
|
+
${c('bold', 'Options:')}
|
|
135
|
+
--api <url> Custom API endpoint (default: localhost:3001)
|
|
136
|
+
|
|
137
|
+
${c('dim', 'All data sourced from Helius RPC mainnet. AI analysis by CLAW agents.')}
|
|
138
|
+
`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
switch (cmd) {
|
|
144
|
+
case 'token': await tokenCmd(arg); break;
|
|
145
|
+
case 'wallet': await walletCmd(arg); break;
|
|
146
|
+
case 'simulate': await simulateCmd(arg); break;
|
|
147
|
+
default:
|
|
148
|
+
console.error(`Unknown command: ${cmd}. Run sclaw --help`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
} catch (err: any) {
|
|
152
|
+
console.error(`\n${c('red', 'Error:')} ${err.message}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log('');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
main();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
declare const API_BASE: string;
|
|
3
|
+
declare const COLORS: {
|
|
4
|
+
green: string;
|
|
5
|
+
red: string;
|
|
6
|
+
yellow: string;
|
|
7
|
+
cyan: string;
|
|
8
|
+
dim: string;
|
|
9
|
+
bold: string;
|
|
10
|
+
reset: string;
|
|
11
|
+
};
|
|
12
|
+
declare function c(color: keyof typeof COLORS, text: string): string;
|
|
13
|
+
declare function request(path: string, options?: RequestInit): Promise<any>;
|
|
14
|
+
declare function printRisk(analysis: any): void;
|
|
15
|
+
declare function tokenCmd(mint: string): Promise<void>;
|
|
16
|
+
declare function walletCmd(address: string): Promise<void>;
|
|
17
|
+
declare function simulateCmd(tx: string): Promise<void>;
|
|
18
|
+
declare function main(): Promise<void>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const API_BASE = process.env.SCLAW_API || 'http://localhost:3001/api';
|
|
3
|
+
const COLORS = {
|
|
4
|
+
green: '\x1b[32m',
|
|
5
|
+
red: '\x1b[31m',
|
|
6
|
+
yellow: '\x1b[33m',
|
|
7
|
+
cyan: '\x1b[36m',
|
|
8
|
+
dim: '\x1b[2m',
|
|
9
|
+
bold: '\x1b[1m',
|
|
10
|
+
reset: '\x1b[0m',
|
|
11
|
+
};
|
|
12
|
+
function c(color, text) {
|
|
13
|
+
return `${COLORS[color]}${text}${COLORS.reset}`;
|
|
14
|
+
}
|
|
15
|
+
async function request(path, options) {
|
|
16
|
+
const res = await fetch(`${API_BASE}${path}`, {
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
...options,
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
22
|
+
throw new Error(err.error || 'Request failed');
|
|
23
|
+
}
|
|
24
|
+
return res.json();
|
|
25
|
+
}
|
|
26
|
+
function printRisk(analysis) {
|
|
27
|
+
if (!analysis)
|
|
28
|
+
return;
|
|
29
|
+
const levelColor = analysis.riskLevel === 'SAFE' || analysis.riskLevel === 'LOW' ? 'green'
|
|
30
|
+
: analysis.riskLevel === 'MEDIUM' ? 'yellow' : 'red';
|
|
31
|
+
console.log(`\n${c('bold', 'CLAW Agent Report')}`);
|
|
32
|
+
console.log(` Risk: ${c(levelColor, `${analysis.riskScore}/100 ${analysis.riskLevel}`)}`);
|
|
33
|
+
console.log(` ${analysis.summary}`);
|
|
34
|
+
if (analysis.flags?.length) {
|
|
35
|
+
console.log(`\n ${c('yellow', 'Flags:')}`);
|
|
36
|
+
analysis.flags.forEach((f) => console.log(` ${c('yellow', '▸')} ${f}`));
|
|
37
|
+
}
|
|
38
|
+
if (analysis.details) {
|
|
39
|
+
console.log(`\n ${c('dim', analysis.details)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function tokenCmd(mint) {
|
|
43
|
+
console.log(`\n${c('cyan', 'SCLAW')} ${c('dim', '— Token Security Analysis')}`);
|
|
44
|
+
console.log(`${c('dim', 'Analyzing')} ${mint}\n`);
|
|
45
|
+
const data = await request(`/token/${mint}`);
|
|
46
|
+
const t = data.token;
|
|
47
|
+
console.log(` ${c('bold', t.symbol || 'Unknown')} ${c('dim', t.name || '')}`);
|
|
48
|
+
console.log(` Mint: ${c('dim', t.mint)}`);
|
|
49
|
+
console.log(` Supply: ${t.supply !== null && t.decimals !== null ? (t.supply / Math.pow(10, t.decimals)).toLocaleString() : 'N/A'}`);
|
|
50
|
+
console.log(` Decimals: ${t.decimals ?? 'N/A'}`);
|
|
51
|
+
console.log(` Mintable: ${t.isMintable ? c('red', 'YES') : c('green', 'NO')}`);
|
|
52
|
+
console.log(` Freezable: ${t.isFreezable ? c('red', 'YES') : c('green', 'NO')}`);
|
|
53
|
+
console.log(` Top 10 concentration: ${t.holderConcentration > 80 ? c('red', t.holderConcentration + '%') : t.holderConcentration > 50 ? c('yellow', t.holderConcentration + '%') : c('green', t.holderConcentration + '%')}`);
|
|
54
|
+
if (t.risks?.length) {
|
|
55
|
+
console.log(`\n ${c('yellow', 'Risk Flags:')}`);
|
|
56
|
+
t.risks.forEach((r) => console.log(` ${c('yellow', '▸')} ${r}`));
|
|
57
|
+
}
|
|
58
|
+
if (t.topHolders?.length) {
|
|
59
|
+
console.log(`\n ${c('dim', 'Top Holders:')}`);
|
|
60
|
+
t.topHolders.slice(0, 5).forEach((h, i) => {
|
|
61
|
+
console.log(` ${i + 1}. ${h.address.slice(0, 8)}...${h.address.slice(-6)} ${h.amount.toLocaleString()} ${h.percentage}%`);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
printRisk(data.clawAnalysis);
|
|
65
|
+
}
|
|
66
|
+
async function walletCmd(address) {
|
|
67
|
+
console.log(`\n${c('cyan', 'SCLAW')} ${c('dim', '— Wallet Risk Analysis')}`);
|
|
68
|
+
console.log(`${c('dim', 'Analyzing')} ${address}\n`);
|
|
69
|
+
const data = await request(`/risk/wallet/${address}`);
|
|
70
|
+
console.log(` Balance: ${data.balanceSOL} SOL`);
|
|
71
|
+
const txCount = data.recentTransactions?.length || 0;
|
|
72
|
+
console.log(` Recent transactions: ${txCount}`);
|
|
73
|
+
if (data.recentTransactions?.length) {
|
|
74
|
+
console.log(`\n ${c('dim', 'Recent Activity:')}`);
|
|
75
|
+
data.recentTransactions.slice(0, 5).forEach((tx) => {
|
|
76
|
+
console.log(` ${c('dim', tx.type || '?')} via ${tx.source || '?'}`);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
printRisk(data.clawAnalysis);
|
|
80
|
+
}
|
|
81
|
+
async function simulateCmd(tx) {
|
|
82
|
+
console.log(`\n${c('cyan', 'SCLAW')} ${c('dim', '— Transaction Simulation')}\n`);
|
|
83
|
+
const data = await request('/simulate/transaction', {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
body: JSON.stringify({ transaction: tx }),
|
|
86
|
+
});
|
|
87
|
+
const sim = data.simulation;
|
|
88
|
+
console.log(` Result: ${sim.success ? c('green', 'SUCCESS') : c('red', 'FAILED')}`);
|
|
89
|
+
console.log(` Compute units: ${sim.unitsConsumed}`);
|
|
90
|
+
if (sim.error)
|
|
91
|
+
console.log(` Error: ${c('red', sim.error)}`);
|
|
92
|
+
if (data.estimatedCostSOL)
|
|
93
|
+
console.log(` Est. cost: ${data.estimatedCostSOL} SOL`);
|
|
94
|
+
if (sim.logs?.length) {
|
|
95
|
+
console.log(`\n ${c('dim', 'Logs:')}`);
|
|
96
|
+
sim.logs.slice(0, 10).forEach((l) => console.log(` ${c('dim', l)}`));
|
|
97
|
+
}
|
|
98
|
+
printRisk(data.clawAnalysis);
|
|
99
|
+
}
|
|
100
|
+
async function main() {
|
|
101
|
+
const args = process.argv.slice(2);
|
|
102
|
+
const cmd = args[0];
|
|
103
|
+
const arg = args[1];
|
|
104
|
+
if (!cmd || cmd === '--help' || cmd === '-h') {
|
|
105
|
+
console.log(`
|
|
106
|
+
${c('cyan', 'SCLAW')} ${c('dim', '— ShadowClawAI Pre-Execution Engine')}
|
|
107
|
+
|
|
108
|
+
${c('bold', 'Usage:')}
|
|
109
|
+
sclaw token <mint-address> Analyze token security
|
|
110
|
+
sclaw wallet <address> Wallet risk profile
|
|
111
|
+
sclaw simulate <base64-tx> Dry-run a transaction
|
|
112
|
+
|
|
113
|
+
${c('bold', 'Options:')}
|
|
114
|
+
--api <url> Custom API endpoint (default: localhost:3001)
|
|
115
|
+
|
|
116
|
+
${c('dim', 'All data sourced from Helius RPC mainnet. AI analysis by CLAW agents.')}
|
|
117
|
+
`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
switch (cmd) {
|
|
122
|
+
case 'token':
|
|
123
|
+
await tokenCmd(arg);
|
|
124
|
+
break;
|
|
125
|
+
case 'wallet':
|
|
126
|
+
await walletCmd(arg);
|
|
127
|
+
break;
|
|
128
|
+
case 'simulate':
|
|
129
|
+
await simulateCmd(arg);
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
console.error(`Unknown command: ${cmd}. Run sclaw --help`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.error(`\n${c('red', 'Error:')} ${err.message}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
console.log('');
|
|
141
|
+
}
|
|
142
|
+
main();
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const HELIUS_API_KEY: string;
|
|
2
|
+
export declare const HELIUS_RPC_URL: string;
|
|
3
|
+
export declare const HELIUS_API_URL = "https://api.helius.xyz/v0";
|
|
4
|
+
export declare const OPENROUTER_API_KEY: string;
|
|
5
|
+
export declare const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const HELIUS_API_KEY = process.env.HELIUS_API_KEY || '';
|
|
2
|
+
export const HELIUS_RPC_URL = `https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}`;
|
|
3
|
+
export const HELIUS_API_URL = `https://api.helius.xyz/v0`;
|
|
4
|
+
export const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY || '';
|
|
5
|
+
export const OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import { simulateRoute } from './routes/simulate';
|
|
4
|
+
import { tokenRoute } from './routes/token';
|
|
5
|
+
import { riskRoute } from './routes/risk';
|
|
6
|
+
const app = express();
|
|
7
|
+
const PORT = process.env.PORT || 3001;
|
|
8
|
+
app.use(cors());
|
|
9
|
+
app.use(express.json());
|
|
10
|
+
app.use('/api/simulate', simulateRoute);
|
|
11
|
+
app.use('/api/token', tokenRoute);
|
|
12
|
+
app.use('/api/risk', riskRoute);
|
|
13
|
+
app.get('/api/health', (_req, res) => {
|
|
14
|
+
res.json({ status: 'CLAW Engine Online', version: '1.0.0' });
|
|
15
|
+
});
|
|
16
|
+
app.listen(PORT, () => {
|
|
17
|
+
console.log(`[CLAW] ShadowClaw engine running on port ${PORT}`);
|
|
18
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { OPENROUTER_API_KEY, OPENROUTER_URL } from '../config';
|
|
2
|
+
const CLAW_SYSTEM_PROMPT = `You are CLAW (ShadowClawAI), a specialized AI agent for blockchain transaction analysis and risk assessment.
|
|
3
|
+
|
|
4
|
+
Your role:
|
|
5
|
+
- Analyze transaction simulation results and identify risks
|
|
6
|
+
- Detect potential rug pulls, honeypots, and scam patterns
|
|
7
|
+
- Assess smart contract risks based on on-chain data
|
|
8
|
+
- Provide clear, actionable risk reports
|
|
9
|
+
|
|
10
|
+
Rules:
|
|
11
|
+
- Be direct and precise. No fluff.
|
|
12
|
+
- Always provide a risk score from 0-100 (0 = safe, 100 = extreme danger)
|
|
13
|
+
- Categorize risks: SAFE, LOW, MEDIUM, HIGH, CRITICAL
|
|
14
|
+
- Flag specific red flags with evidence
|
|
15
|
+
- Never give financial advice, only technical risk analysis
|
|
16
|
+
- Use real data only, never fabricate information`;
|
|
17
|
+
export async function clawAnalyze(context) {
|
|
18
|
+
const res = await fetch(OPENROUTER_URL, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
'Authorization': `Bearer ${OPENROUTER_API_KEY}`,
|
|
23
|
+
'HTTP-Referer': 'https://sclaw.ai',
|
|
24
|
+
'X-Title': 'SCLAW - ShadowClawAI',
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
model: 'anthropic/claude-sonnet-4',
|
|
28
|
+
messages: [
|
|
29
|
+
{ role: 'system', content: CLAW_SYSTEM_PROMPT },
|
|
30
|
+
{
|
|
31
|
+
role: 'user',
|
|
32
|
+
content: `Analyze the following blockchain data and provide a risk assessment. Respond ONLY with valid JSON matching this schema:
|
|
33
|
+
{
|
|
34
|
+
"riskScore": number (0-100),
|
|
35
|
+
"riskLevel": "SAFE" | "LOW" | "MEDIUM" | "HIGH" | "CRITICAL",
|
|
36
|
+
"summary": "one-line summary",
|
|
37
|
+
"flags": ["array of specific red flags"],
|
|
38
|
+
"details": "detailed analysis"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Data to analyze:
|
|
42
|
+
${context}`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
temperature: 0.1,
|
|
46
|
+
max_tokens: 2000,
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
const data = await res.json();
|
|
50
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
51
|
+
try {
|
|
52
|
+
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
53
|
+
if (jsonMatch) {
|
|
54
|
+
return JSON.parse(jsonMatch[0]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
riskScore: -1,
|
|
61
|
+
riskLevel: 'MEDIUM',
|
|
62
|
+
summary: 'CLAW agent could not complete analysis',
|
|
63
|
+
flags: ['Analysis incomplete — review manually'],
|
|
64
|
+
details: content,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface HeliusRpcRequest {
|
|
2
|
+
method: string;
|
|
3
|
+
params: unknown[];
|
|
4
|
+
}
|
|
5
|
+
export declare function heliusRpc(request: HeliusRpcRequest): Promise<unknown>;
|
|
6
|
+
export declare function simulateTransaction(encodedTx: string): Promise<{
|
|
7
|
+
success: boolean;
|
|
8
|
+
logs: string[];
|
|
9
|
+
unitsConsumed: number;
|
|
10
|
+
error: string | null;
|
|
11
|
+
accounts: unknown[];
|
|
12
|
+
}>;
|
|
13
|
+
export declare function getTokenMetadata(mintAddress: string): Promise<unknown>;
|
|
14
|
+
export declare function getAsset(assetId: string): Promise<unknown>;
|
|
15
|
+
export declare function getBalance(address: string): Promise<number>;
|
|
16
|
+
export declare function getTokenAccounts(ownerAddress: string): Promise<unknown>;
|
|
17
|
+
export declare function getTransactionHistory(address: string, limit?: number): Promise<unknown>;
|
|
18
|
+
export declare function getParsedTransaction(signature: string): Promise<unknown>;
|
|
19
|
+
export {};
|