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.
@@ -0,0 +1,109 @@
1
+ import { heliusRpc, getTokenMetadata } from './helius';
2
+ import { HELIUS_RPC_URL } from '../config';
3
+
4
+ export interface TokenSecurityReport {
5
+ mint: string;
6
+ name: string | null;
7
+ symbol: string | null;
8
+ supply: number | null;
9
+ decimals: number | null;
10
+ freezeAuthority: string | null;
11
+ mintAuthority: string | null;
12
+ isFreezable: boolean;
13
+ isMintable: boolean;
14
+ topHolders: { address: string; amount: number; percentage: number }[];
15
+ holderConcentration: number;
16
+ lpInfo: { locked: boolean; pool: string | null } | null;
17
+ metadata: unknown;
18
+ risks: string[];
19
+ }
20
+
21
+ export async function analyzeToken(mintAddress: string): Promise<TokenSecurityReport> {
22
+ const risks: string[] = [];
23
+
24
+ const mintInfo = await heliusRpc({
25
+ method: 'getAccountInfo',
26
+ params: [mintAddress, { encoding: 'jsonParsed' }],
27
+ }) as any;
28
+
29
+ const parsed = mintInfo?.value?.data?.parsed?.info;
30
+ const mintAuthority = parsed?.mintAuthority || null;
31
+ const freezeAuthority = parsed?.freezeAuthority || null;
32
+ const supply = parsed?.supply ? Number(parsed.supply) : null;
33
+ const decimals = parsed?.decimals ?? null;
34
+
35
+ const isMintable = mintAuthority !== null;
36
+ const isFreezable = freezeAuthority !== null;
37
+
38
+ if (isMintable) risks.push('MINT_AUTHORITY_ACTIVE — token supply can be increased');
39
+ if (isFreezable) risks.push('FREEZE_AUTHORITY_ACTIVE — accounts can be frozen');
40
+
41
+ let name: string | null = null;
42
+ let symbol: string | null = null;
43
+ let metadata: unknown = null;
44
+ try {
45
+ const metaResult = await getTokenMetadata(mintAddress) as any[];
46
+ if (metaResult?.[0]) {
47
+ metadata = metaResult[0];
48
+ name = metaResult[0]?.onChainMetadata?.metadata?.data?.name ||
49
+ metaResult[0]?.offChainMetadata?.metadata?.name || null;
50
+ symbol = metaResult[0]?.onChainMetadata?.metadata?.data?.symbol ||
51
+ metaResult[0]?.offChainMetadata?.metadata?.symbol || null;
52
+ }
53
+ } catch {
54
+ risks.push('METADATA_FETCH_FAILED — could not retrieve token metadata');
55
+ }
56
+
57
+ let topHolders: { address: string; amount: number; percentage: number }[] = [];
58
+ let holderConcentration = 0;
59
+ try {
60
+ const holders = await heliusRpc({
61
+ method: 'getTokenLargestAccounts',
62
+ params: [mintAddress],
63
+ }) as any;
64
+
65
+ const totalSupply = supply && decimals !== null ? supply / Math.pow(10, decimals) : 0;
66
+ topHolders = (holders?.value || []).slice(0, 10).map((h: any) => {
67
+ const amount = Number(h.amount) / Math.pow(10, decimals || 0);
68
+ const percentage = totalSupply > 0 ? (amount / totalSupply) * 100 : 0;
69
+ return {
70
+ address: h.address,
71
+ amount,
72
+ percentage: Math.round(percentage * 100) / 100,
73
+ };
74
+ });
75
+
76
+ holderConcentration = topHolders.reduce((sum, h) => sum + h.percentage, 0);
77
+ holderConcentration = Math.round(holderConcentration * 100) / 100;
78
+
79
+ if (holderConcentration > 80) {
80
+ risks.push(`HIGH_CONCENTRATION — top 10 holders own ${holderConcentration}% of supply`);
81
+ } else if (holderConcentration > 50) {
82
+ risks.push(`MODERATE_CONCENTRATION — top 10 holders own ${holderConcentration}% of supply`);
83
+ }
84
+
85
+ const bigWhale = topHolders.find(h => h.percentage > 20);
86
+ if (bigWhale) {
87
+ risks.push(`WHALE_ALERT — single holder owns ${bigWhale.percentage}% (${bigWhale.address.slice(0, 8)}...)`);
88
+ }
89
+ } catch {
90
+ risks.push('HOLDER_ANALYSIS_FAILED — could not fetch holder data');
91
+ }
92
+
93
+ return {
94
+ mint: mintAddress,
95
+ name,
96
+ symbol,
97
+ supply,
98
+ decimals,
99
+ freezeAuthority,
100
+ mintAuthority,
101
+ isFreezable,
102
+ isMintable,
103
+ topHolders,
104
+ holderConcentration,
105
+ lpInfo: null,
106
+ metadata,
107
+ risks,
108
+ };
109
+ }
@@ -0,0 +1,65 @@
1
+ import { Router } from 'express';
2
+ import { getBalance, getTokenAccounts, getTransactionHistory } from '../lib/helius';
3
+ import { clawAnalyze } from '../lib/claw-agent';
4
+
5
+ export const riskRoute = Router();
6
+
7
+ riskRoute.get('/wallet/:address', async (req, res) => {
8
+ try {
9
+ const { address } = req.params;
10
+
11
+ const results = await Promise.allSettled([
12
+ getBalance(address),
13
+ getTokenAccounts(address),
14
+ getTransactionHistory(address, 5),
15
+ ]);
16
+
17
+ const balance = results[0].status === 'fulfilled' ? results[0].value as number : 0;
18
+ const tokenAccounts = results[1].status === 'fulfilled' ? results[1].value : { value: [] };
19
+ const recentTxs = results[2].status === 'fulfilled' ? results[2].value : [];
20
+
21
+ const txSummary = (recentTxs as any[])?.map((tx: any) => ({
22
+ type: tx.type,
23
+ source: tx.source,
24
+ fee: tx.fee,
25
+ description: tx.description?.slice(0, 200),
26
+ timestamp: tx.timestamp,
27
+ })) || [];
28
+
29
+ const clawReport = await clawAnalyze(JSON.stringify({
30
+ type: 'WALLET_RISK_ANALYSIS',
31
+ address,
32
+ balanceSOL: balance / 1e9,
33
+ tokenAccountCount: (tokenAccounts as any)?.value?.length || 0,
34
+ recentTransactionSummary: txSummary,
35
+ }));
36
+
37
+ res.json({
38
+ address,
39
+ balanceSOL: balance / 1e9,
40
+ tokenAccounts,
41
+ recentTransactions: recentTxs,
42
+ clawAnalysis: clawReport,
43
+ });
44
+ } catch (err: any) {
45
+ res.status(500).json({ error: err.message || 'Wallet analysis failed' });
46
+ }
47
+ });
48
+
49
+ riskRoute.post('/analyze', async (req, res) => {
50
+ try {
51
+ const { data, context } = req.body;
52
+ if (!data) {
53
+ return res.status(400).json({ error: 'Missing data for analysis' });
54
+ }
55
+
56
+ const clawReport = await clawAnalyze(JSON.stringify({
57
+ type: context || 'CUSTOM_ANALYSIS',
58
+ data,
59
+ }));
60
+
61
+ res.json({ clawAnalysis: clawReport });
62
+ } catch (err: any) {
63
+ res.status(500).json({ error: err.message });
64
+ }
65
+ });
@@ -0,0 +1,68 @@
1
+ import { Router } from 'express';
2
+ import { simulateTransaction, getBalance, getParsedTransaction } from '../lib/helius';
3
+ import { clawAnalyze } from '../lib/claw-agent';
4
+
5
+ export const simulateRoute = Router();
6
+
7
+ simulateRoute.post('/transaction', async (req, res) => {
8
+ try {
9
+ const { transaction, walletAddress } = req.body;
10
+ if (!transaction) {
11
+ return res.status(400).json({ error: 'Missing transaction (base64 encoded)' });
12
+ }
13
+
14
+ let preBalance: number | null = null;
15
+ if (walletAddress) {
16
+ preBalance = await getBalance(walletAddress);
17
+ }
18
+
19
+ const simResult = await simulateTransaction(transaction);
20
+
21
+ let estimatedCost: number | null = null;
22
+ if (preBalance !== null && simResult.unitsConsumed > 0) {
23
+ estimatedCost = simResult.unitsConsumed * 0.000005;
24
+ }
25
+
26
+ const clawReport = await clawAnalyze(JSON.stringify({
27
+ type: 'TRANSACTION_SIMULATION',
28
+ success: simResult.success,
29
+ logs: simResult.logs,
30
+ unitsConsumed: simResult.unitsConsumed,
31
+ error: simResult.error,
32
+ walletAddress,
33
+ preBalance: preBalance ? preBalance / 1e9 : null,
34
+ estimatedCostSOL: estimatedCost,
35
+ }));
36
+
37
+ res.json({
38
+ simulation: simResult,
39
+ preBalance: preBalance ? preBalance / 1e9 : null,
40
+ estimatedCostSOL: estimatedCost,
41
+ clawAnalysis: clawReport,
42
+ });
43
+ } catch (err: any) {
44
+ res.status(500).json({ error: err.message });
45
+ }
46
+ });
47
+
48
+ simulateRoute.post('/preview', async (req, res) => {
49
+ try {
50
+ const { signature } = req.body;
51
+ if (!signature) {
52
+ return res.status(400).json({ error: 'Missing transaction signature' });
53
+ }
54
+
55
+ const txData = await getParsedTransaction(signature);
56
+ const clawReport = await clawAnalyze(JSON.stringify({
57
+ type: 'TRANSACTION_REVIEW',
58
+ transaction: txData,
59
+ }));
60
+
61
+ res.json({
62
+ transaction: txData,
63
+ clawAnalysis: clawReport,
64
+ });
65
+ } catch (err: any) {
66
+ res.status(500).json({ error: err.message });
67
+ }
68
+ });
@@ -0,0 +1,41 @@
1
+ import { Router } from 'express';
2
+ import { analyzeToken } from '../lib/token-analysis';
3
+ import { getTransactionHistory } from '../lib/helius';
4
+ import { clawAnalyze } from '../lib/claw-agent';
5
+
6
+ export const tokenRoute = Router();
7
+
8
+ tokenRoute.get('/:mint', async (req, res) => {
9
+ try {
10
+ const { mint } = req.params;
11
+ if (!mint || mint.length < 32) {
12
+ return res.status(400).json({ error: 'Invalid mint address' });
13
+ }
14
+
15
+ const report = await analyzeToken(mint);
16
+
17
+ const clawReport = await clawAnalyze(JSON.stringify({
18
+ type: 'TOKEN_SECURITY_ANALYSIS',
19
+ ...report,
20
+ }));
21
+
22
+ res.json({
23
+ token: report,
24
+ clawAnalysis: clawReport,
25
+ });
26
+ } catch (err: any) {
27
+ res.status(500).json({ error: err.message });
28
+ }
29
+ });
30
+
31
+ tokenRoute.get('/:mint/history', async (req, res) => {
32
+ try {
33
+ const { mint } = req.params;
34
+ const limit = parseInt(req.query.limit as string) || 10;
35
+
36
+ const history = await getTransactionHistory(mint, limit);
37
+ res.json({ history });
38
+ } catch (err: any) {
39
+ res.status(500).json({ error: err.message });
40
+ }
41
+ });