clawcontract 0.1.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 +222 -0
- package/dist/analyzer/fallback.d.ts +28 -0
- package/dist/analyzer/fallback.d.ts.map +1 -0
- package/dist/analyzer/fallback.js +142 -0
- package/dist/analyzer/fallback.js.map +1 -0
- package/dist/analyzer/index.d.ts +24 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +73 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/analyzer/slither.d.ts +36 -0
- package/dist/analyzer/slither.d.ts.map +1 -0
- package/dist/analyzer/slither.js +92 -0
- package/dist/analyzer/slither.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +7 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +74 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/delete.d.ts +5 -0
- package/dist/cli/commands/delete.d.ts.map +1 -0
- package/dist/cli/commands/delete.js +48 -0
- package/dist/cli/commands/delete.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +5 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +88 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/full.d.ts +5 -0
- package/dist/cli/commands/full.d.ts.map +1 -0
- package/dist/cli/commands/full.js +83 -0
- package/dist/cli/commands/full.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +5 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +35 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/interact.d.ts +6 -0
- package/dist/cli/commands/interact.d.ts.map +1 -0
- package/dist/cli/commands/interact.js +136 -0
- package/dist/cli/commands/interact.js.map +1 -0
- package/dist/cli/commands/list.d.ts +6 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +37 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +8 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +36 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +9 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +87 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +104 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils.d.ts +6 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +29 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/config/chains.d.ts +18 -0
- package/dist/config/chains.d.ts.map +1 -0
- package/dist/config/chains.js +52 -0
- package/dist/config/chains.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/deployer/compiler.d.ts +10 -0
- package/dist/deployer/compiler.d.ts.map +1 -0
- package/dist/deployer/compiler.js +93 -0
- package/dist/deployer/compiler.js.map +1 -0
- package/dist/deployer/deploy.d.ts +28 -0
- package/dist/deployer/deploy.d.ts.map +1 -0
- package/dist/deployer/deploy.js +60 -0
- package/dist/deployer/deploy.js.map +1 -0
- package/dist/deployer/hardhat-config-template.d.ts +2 -0
- package/dist/deployer/hardhat-config-template.d.ts.map +1 -0
- package/dist/deployer/hardhat-config-template.js +20 -0
- package/dist/deployer/hardhat-config-template.js.map +1 -0
- package/dist/deployer/index.d.ts +7 -0
- package/dist/deployer/index.d.ts.map +1 -0
- package/dist/deployer/index.js +4 -0
- package/dist/deployer/index.js.map +1 -0
- package/dist/deployer/metadata.d.ts +16 -0
- package/dist/deployer/metadata.d.ts.map +1 -0
- package/dist/deployer/metadata.js +315 -0
- package/dist/deployer/metadata.js.map +1 -0
- package/dist/deployer/wallet.d.ts +6 -0
- package/dist/deployer/wallet.d.ts.map +1 -0
- package/dist/deployer/wallet.js +21 -0
- package/dist/deployer/wallet.js.map +1 -0
- package/dist/generator/index.d.ts +19 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +33 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/llm.d.ts +10 -0
- package/dist/generator/llm.d.ts.map +1 -0
- package/dist/generator/llm.js +71 -0
- package/dist/generator/llm.js.map +1 -0
- package/dist/generator/templates/erc20.d.ts +2 -0
- package/dist/generator/templates/erc20.d.ts.map +1 -0
- package/dist/generator/templates/erc20.js +106 -0
- package/dist/generator/templates/erc20.js.map +1 -0
- package/dist/generator/templates/erc20.sol.d.ts +2 -0
- package/dist/generator/templates/erc20.sol.d.ts.map +1 -0
- package/dist/generator/templates/erc20.sol.js +106 -0
- package/dist/generator/templates/erc20.sol.js.map +1 -0
- package/dist/generator/templates/erc721.d.ts +2 -0
- package/dist/generator/templates/erc721.d.ts.map +1 -0
- package/dist/generator/templates/erc721.js +168 -0
- package/dist/generator/templates/erc721.js.map +1 -0
- package/dist/generator/templates/erc721.sol.d.ts +2 -0
- package/dist/generator/templates/erc721.sol.d.ts.map +1 -0
- package/dist/generator/templates/erc721.sol.js +168 -0
- package/dist/generator/templates/erc721.sol.js.map +1 -0
- package/dist/generator/templates/index.d.ts +21 -0
- package/dist/generator/templates/index.d.ts.map +1 -0
- package/dist/generator/templates/index.js +157 -0
- package/dist/generator/templates/index.js.map +1 -0
- package/dist/generator/templates/multisig.d.ts +2 -0
- package/dist/generator/templates/multisig.d.ts.map +1 -0
- package/dist/generator/templates/multisig.js +136 -0
- package/dist/generator/templates/multisig.js.map +1 -0
- package/dist/generator/templates/multisig.sol.d.ts +2 -0
- package/dist/generator/templates/multisig.sol.d.ts.map +1 -0
- package/dist/generator/templates/multisig.sol.js +136 -0
- package/dist/generator/templates/multisig.sol.js.map +1 -0
- package/dist/generator/templates/staking.d.ts +2 -0
- package/dist/generator/templates/staking.d.ts.map +1 -0
- package/dist/generator/templates/staking.js +119 -0
- package/dist/generator/templates/staking.js.map +1 -0
- package/dist/generator/templates/staking.sol.d.ts +2 -0
- package/dist/generator/templates/staking.sol.d.ts.map +1 -0
- package/dist/generator/templates/staking.sol.js +119 -0
- package/dist/generator/templates/staking.sol.js.map +1 -0
- package/dist/generator/templates/vesting.d.ts +2 -0
- package/dist/generator/templates/vesting.d.ts.map +1 -0
- package/dist/generator/templates/vesting.js +133 -0
- package/dist/generator/templates/vesting.js.map +1 -0
- package/dist/generator/templates/vesting.sol.d.ts +2 -0
- package/dist/generator/templates/vesting.sol.d.ts.map +1 -0
- package/dist/generator/templates/vesting.sol.js +133 -0
- package/dist/generator/templates/vesting.sol.js.map +1 -0
- package/dist/generator/utils.d.ts +5 -0
- package/dist/generator/utils.d.ts.map +1 -0
- package/dist/generator/utils.js +49 -0
- package/dist/generator/utils.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/verifier/explorer-api.d.ts +21 -0
- package/dist/verifier/explorer-api.d.ts.map +1 -0
- package/dist/verifier/explorer-api.js +92 -0
- package/dist/verifier/explorer-api.js.map +1 -0
- package/dist/verifier/index.d.ts +20 -0
- package/dist/verifier/index.d.ts.map +1 -0
- package/dist/verifier/index.js +31 -0
- package/dist/verifier/index.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# ClawContract 🦞
|
|
2
|
+
|
|
3
|
+
**AI-powered smart contract generator, analyzer, and deployer for BNB Chain**
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](https://www.bnbchain.org)
|
|
8
|
+
[](#hackathon)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
ClawContract turns natural language into production-ready, deployed, and verified smart contracts on BNB Chain. Describe what you want in plain English — ClawContract generates the Solidity code using AI, runs security analysis, deploys to BSC or opBNB, and verifies the source on BscScan — all in a single command.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Security analysis** — runs Slither static analysis with an automatic regex-based fallback for environments without Python
|
|
19
|
+
- **One-command deployment** — deploy to BSC and opBNB (mainnet + testnet) with gas estimation
|
|
20
|
+
- **Automatic verification** — verify source code on BscScan and opBNBScan immediately after deployment
|
|
21
|
+
- **Non-interactive CLI** — fully automated pipeline with gas estimates, no user prompts required
|
|
22
|
+
- **OpenClaw skill integration** — register as an OpenClaw skill for chat-based contract generation and deployment
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/your-username/ClawContract.git
|
|
28
|
+
cd ClawContract
|
|
29
|
+
pnpm install
|
|
30
|
+
pnpm run build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then set up your environment:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
clawcontract setup
|
|
37
|
+
clawcontract setup --openrouter-key sk-or-... --bscscan-key ABC123
|
|
38
|
+
clawcontract setup --private-key 0xabc... --openrouter-key sk-or-... --openrouter-model anthropic/claude-sonnet-4-20250514
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This writes a `.env` file non-interactively. A wallet is auto-generated if `--private-key` is not provided.
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### Generate a contract
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
clawcontract generate "ERC-20 token called VibeToken with 1M supply"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Analyze a contract for vulnerabilities
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
clawcontract analyze ./contracts/VibeToken.sol
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Deploy to a chain
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
clawcontract deploy ./contracts/VibeToken.sol --chain bsc-testnet
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Verify on block explorer
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
clawcontract verify 0xYourContractAddress --chain bsc-testnet --file ./contracts/VibeToken.sol
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Interact with a deployed contract
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
clawcontract interact 0xYourContractAddress name --chain bsc-testnet
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Call any function on a deployed contract. Read-only functions (`view`/`pure`) are called without gas. State-changing functions are sent as signed transactions.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Read-only call
|
|
79
|
+
clawcontract interact 0xABC... balanceOf 0xDEF... --chain bsc-testnet
|
|
80
|
+
|
|
81
|
+
# State-changing call
|
|
82
|
+
clawcontract interact 0xABC... transfer 0xDEF... 1000 --chain bsc-testnet
|
|
83
|
+
|
|
84
|
+
# Payable call (send BNB value in wei)
|
|
85
|
+
clawcontract interact 0xABC... fundTrade 1 --value 100000000000000 --chain bsc-testnet
|
|
86
|
+
|
|
87
|
+
# Use ABI from source file instead of stored metadata
|
|
88
|
+
clawcontract interact 0xABC... name --chain bsc-testnet --file ./contracts/VibeToken.sol
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### List deployments
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
clawcontract list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
List all stored deployment records. Shows address, contract name, chain, deployer, and deployment date.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# List all deployments
|
|
101
|
+
clawcontract list
|
|
102
|
+
|
|
103
|
+
# Filter by chain
|
|
104
|
+
clawcontract list --chain bsc-testnet
|
|
105
|
+
|
|
106
|
+
# Output as JSON (for scripting)
|
|
107
|
+
clawcontract list --json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Delete a deployment record
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
clawcontract delete <address>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Remove a deployment record from the local store. Shows deployment details and asks for confirmation before deleting. Orphaned ABI files are automatically cleaned up.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Delete with confirmation prompt
|
|
120
|
+
clawcontract delete 0xYourContractAddress
|
|
121
|
+
|
|
122
|
+
# Skip confirmation
|
|
123
|
+
clawcontract delete 0xYourContractAddress --force
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Full pipeline (generate → analyze → deploy → verify)
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
clawcontract full "staking contract for BNB with 10% APY" --chain bsc-testnet
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Global options
|
|
133
|
+
|
|
134
|
+
| Option | Description | Default |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `--chain <chain>` | Target blockchain | `bsc-testnet` |
|
|
137
|
+
| `--output <dir>` | Output directory for generated contracts | `./contracts` |
|
|
138
|
+
|
|
139
|
+
## Supported Chains
|
|
140
|
+
|
|
141
|
+
| Chain | Chain ID | RPC | Explorer |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| BNB Smart Chain | 56 | `https://bsc-dataseed.binance.org` | [bscscan.com](https://bscscan.com) |
|
|
144
|
+
| BNB Smart Chain Testnet | 97 | `https://data-seed-prebsc-1-s1.binance.org:8545` | [testnet.bscscan.com](https://testnet.bscscan.com) |
|
|
145
|
+
| opBNB | 204 | `https://opbnb-mainnet-rpc.bnbchain.org` | [opbnbscan.com](https://opbnbscan.com) |
|
|
146
|
+
| opBNB Testnet | 5611 | `https://opbnb-testnet-rpc.bnbchain.org` | [testnet.opbnbscan.com](https://testnet.opbnbscan.com) |
|
|
147
|
+
|
|
148
|
+
## Architecture
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
src/
|
|
152
|
+
├── cli/ # Commander.js CLI entry point + command handlers
|
|
153
|
+
├── generator/ # Contract generation (template matching + Claude LLM)
|
|
154
|
+
├── analyzer/ # Security analysis (Slither + regex fallback)
|
|
155
|
+
├── deployer/ # Compilation + deployment via Hardhat + ethers.js (saves metadata)
|
|
156
|
+
├── verifier/ # BscScan / opBNBScan source verification
|
|
157
|
+
├── config/ # Chain configurations and constants
|
|
158
|
+
└── openclaw/ # OpenClaw skill definition for chat integration
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Pipeline Flow
|
|
162
|
+
|
|
163
|
+
The `full` command runs the entire pipeline end-to-end. If high-severity issues are found during analysis, the AI will automatically attempt to fix them (up to 3 attempts) before proceeding to deployment.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
Natural Language
|
|
167
|
+
↓
|
|
168
|
+
AI Generation ─── template matching + Claude LLM
|
|
169
|
+
↓
|
|
170
|
+
Security Analysis ── Slither / regex checks
|
|
171
|
+
↓ ↑
|
|
172
|
+
AI Auto-Fix ────────┘ (up to 3 attempts if high-severity issues found)
|
|
173
|
+
↓
|
|
174
|
+
Compilation ────── Hardhat + solc
|
|
175
|
+
↓
|
|
176
|
+
Deployment ─────── ethers.js → BSC / opBNB
|
|
177
|
+
↓
|
|
178
|
+
Verification ───── BscScan / opBNBScan API
|
|
179
|
+
↓
|
|
180
|
+
Interaction ────── ethers.js read/write calls
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## OpenClaw Integration
|
|
184
|
+
|
|
185
|
+
ClawContract ships with an [OpenClaw skill](src/openclaw/SKILL.md) that teaches OpenClaw agents how to use the CLI for chat-based contract generation and deployment. Copy or symlink `src/openclaw/` into your OpenClaw workspace `skills/` directory to enable it.
|
|
186
|
+
|
|
187
|
+
## Configuration
|
|
188
|
+
|
|
189
|
+
Run `clawcontract setup` to generate a `.env` file, or copy the example and fill in manually:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
cp .env.example .env
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
| Variable | Description | Required |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| `PRIVATE_KEY` | Wallet private key for deployment (auto-generated if not set) | No |
|
|
198
|
+
| `OPENROUTER_API_KEY` | OpenRouter API key for AI contract generation | Yes |
|
|
199
|
+
| `OPENROUTER_MODEL` | OpenRouter model (default: `anthropic/claude-sonnet-4-20250514`) | No |
|
|
200
|
+
| `BSCSCAN_API_KEY` | BscScan / opBNBScan API key for contract verification | No |
|
|
201
|
+
|
|
202
|
+
> **Security:** Never commit your `.env` file. The `.env.example` file is provided as a reference.
|
|
203
|
+
|
|
204
|
+
> **Data:** Deployment metadata is saved to `contracts/.deployments/` using a directory-based store with deduplicated ABIs and an index for fast lookups. Legacy `.deployments.json` files are auto-migrated on first access. This directory is local and should not be committed.
|
|
205
|
+
|
|
206
|
+
## Requirements
|
|
207
|
+
|
|
208
|
+
- **Node.js** 20.0.0 or later
|
|
209
|
+
- **pnpm** (recommended package manager)
|
|
210
|
+
- **Python 3.8+** (optional — required for Slither static analysis; regex fallback is used if unavailable)
|
|
211
|
+
|
|
212
|
+
## Hackathon
|
|
213
|
+
|
|
214
|
+
Built for the **Good Vibes Only: OpenClaw Edition** hackathon.
|
|
215
|
+
|
|
216
|
+
- **Track:** Builders' Tools
|
|
217
|
+
- **Chain:** BNB Chain (BSC + opBNB)
|
|
218
|
+
- **Goal:** Make smart contract development accessible to everyone through AI and natural language
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface Finding {
|
|
2
|
+
severity: 'High' | 'Medium' | 'Low' | 'Informational' | 'Optimization';
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
location: string;
|
|
6
|
+
recommendation: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AnalysisSummary {
|
|
9
|
+
high: number;
|
|
10
|
+
medium: number;
|
|
11
|
+
low: number;
|
|
12
|
+
informational: number;
|
|
13
|
+
optimization: number;
|
|
14
|
+
total: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AnalysisResult {
|
|
17
|
+
findings: Finding[];
|
|
18
|
+
summary: AnalysisSummary;
|
|
19
|
+
passed: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function checkReentrancy(source: string): Finding[];
|
|
22
|
+
export declare function checkUncheckedReturn(source: string): Finding[];
|
|
23
|
+
export declare function checkTxOrigin(source: string): Finding[];
|
|
24
|
+
export declare function checkSelfDestruct(source: string): Finding[];
|
|
25
|
+
export declare function checkFloatingPragma(source: string): Finding[];
|
|
26
|
+
export declare function runFallbackAnalysis(source: string): AnalysisResult;
|
|
27
|
+
export declare function runFallbackAnalysisFromFile(filePath: string): AnalysisResult;
|
|
28
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/analyzer/fallback.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CA2BzD;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAoB9D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAkBvD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAkB3D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAkB7D;AAyBD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAalE;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAG5E"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
export function checkReentrancy(source) {
|
|
3
|
+
const findings = [];
|
|
4
|
+
const lines = source.split('\n');
|
|
5
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6
|
+
const line = lines[i].trim();
|
|
7
|
+
if ((line.includes('.call{') || line.includes('.call(') || line.includes('.send(') || line.includes('.transfer(')) &&
|
|
8
|
+
!line.startsWith('//')) {
|
|
9
|
+
for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
|
|
10
|
+
const subsequent = lines[j].trim();
|
|
11
|
+
if (subsequent.includes('=') && !subsequent.startsWith('//') && !subsequent.startsWith('require') && !subsequent.startsWith('if')) {
|
|
12
|
+
findings.push({
|
|
13
|
+
severity: 'High',
|
|
14
|
+
title: '[Basic Check] Potential Reentrancy',
|
|
15
|
+
description: `External call at line ${i + 1} is followed by a state change at line ${j + 1}. This could be vulnerable to reentrancy attacks.`,
|
|
16
|
+
location: `Line ${i + 1}`,
|
|
17
|
+
recommendation: 'Use the checks-effects-interactions pattern or a reentrancy guard. Update state variables before making external calls.',
|
|
18
|
+
});
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return findings;
|
|
25
|
+
}
|
|
26
|
+
export function checkUncheckedReturn(source) {
|
|
27
|
+
const findings = [];
|
|
28
|
+
const lines = source.split('\n');
|
|
29
|
+
for (let i = 0; i < lines.length; i++) {
|
|
30
|
+
const line = lines[i].trim();
|
|
31
|
+
if (line.includes('.call{') || line.includes('.call(')) {
|
|
32
|
+
if (!line.includes('(bool') && !line.includes('require') && !line.includes('if')) {
|
|
33
|
+
findings.push({
|
|
34
|
+
severity: 'Medium',
|
|
35
|
+
title: '[Basic Check] Unchecked Return Value',
|
|
36
|
+
description: `Low-level call at line ${i + 1} does not check the return value. The call could silently fail.`,
|
|
37
|
+
location: `Line ${i + 1}`,
|
|
38
|
+
recommendation: 'Check the return value of low-level calls: (bool success, ) = addr.call{...}(...); require(success);',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return findings;
|
|
44
|
+
}
|
|
45
|
+
export function checkTxOrigin(source) {
|
|
46
|
+
const findings = [];
|
|
47
|
+
const lines = source.split('\n');
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
const line = lines[i].trim();
|
|
50
|
+
if (line.includes('tx.origin') && !line.startsWith('//')) {
|
|
51
|
+
findings.push({
|
|
52
|
+
severity: 'Medium',
|
|
53
|
+
title: '[Basic Check] tx.origin Usage',
|
|
54
|
+
description: `tx.origin used at line ${i + 1}. Using tx.origin for authorization is vulnerable to phishing attacks.`,
|
|
55
|
+
location: `Line ${i + 1}`,
|
|
56
|
+
recommendation: 'Use msg.sender instead of tx.origin for authorization checks.',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return findings;
|
|
61
|
+
}
|
|
62
|
+
export function checkSelfDestruct(source) {
|
|
63
|
+
const findings = [];
|
|
64
|
+
const lines = source.split('\n');
|
|
65
|
+
for (let i = 0; i < lines.length; i++) {
|
|
66
|
+
const line = lines[i].trim();
|
|
67
|
+
if ((line.includes('selfdestruct(') || line.includes('selfdestruct (')) && !line.startsWith('//')) {
|
|
68
|
+
findings.push({
|
|
69
|
+
severity: 'High',
|
|
70
|
+
title: '[Basic Check] selfdestruct Usage',
|
|
71
|
+
description: `selfdestruct found at line ${i + 1}. This can permanently destroy the contract and send remaining Ether to an address.`,
|
|
72
|
+
location: `Line ${i + 1}`,
|
|
73
|
+
recommendation: 'Remove selfdestruct or protect it with strict access control. Note: selfdestruct is deprecated in newer Solidity versions.',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return findings;
|
|
78
|
+
}
|
|
79
|
+
export function checkFloatingPragma(source) {
|
|
80
|
+
const findings = [];
|
|
81
|
+
const lines = source.split('\n');
|
|
82
|
+
for (let i = 0; i < lines.length; i++) {
|
|
83
|
+
const line = lines[i].trim();
|
|
84
|
+
if (line.startsWith('pragma solidity') && line.includes('^')) {
|
|
85
|
+
findings.push({
|
|
86
|
+
severity: 'Informational',
|
|
87
|
+
title: '[Basic Check] Floating Pragma',
|
|
88
|
+
description: `Floating pragma found at line ${i + 1}: "${line}". Contracts should be deployed with the same compiler version they were tested with.`,
|
|
89
|
+
location: `Line ${i + 1}`,
|
|
90
|
+
recommendation: 'Lock the pragma to a specific version, e.g., "pragma solidity 0.8.20;" instead of "pragma solidity ^0.8.20;".',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return findings;
|
|
95
|
+
}
|
|
96
|
+
function buildSummary(findings) {
|
|
97
|
+
const summary = {
|
|
98
|
+
high: 0,
|
|
99
|
+
medium: 0,
|
|
100
|
+
low: 0,
|
|
101
|
+
informational: 0,
|
|
102
|
+
optimization: 0,
|
|
103
|
+
total: findings.length,
|
|
104
|
+
};
|
|
105
|
+
for (const finding of findings) {
|
|
106
|
+
switch (finding.severity) {
|
|
107
|
+
case 'High':
|
|
108
|
+
summary.high++;
|
|
109
|
+
break;
|
|
110
|
+
case 'Medium':
|
|
111
|
+
summary.medium++;
|
|
112
|
+
break;
|
|
113
|
+
case 'Low':
|
|
114
|
+
summary.low++;
|
|
115
|
+
break;
|
|
116
|
+
case 'Informational':
|
|
117
|
+
summary.informational++;
|
|
118
|
+
break;
|
|
119
|
+
case 'Optimization':
|
|
120
|
+
summary.optimization++;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return summary;
|
|
125
|
+
}
|
|
126
|
+
export function runFallbackAnalysis(source) {
|
|
127
|
+
const findings = [
|
|
128
|
+
...checkReentrancy(source),
|
|
129
|
+
...checkUncheckedReturn(source),
|
|
130
|
+
...checkTxOrigin(source),
|
|
131
|
+
...checkSelfDestruct(source),
|
|
132
|
+
...checkFloatingPragma(source),
|
|
133
|
+
];
|
|
134
|
+
const summary = buildSummary(findings);
|
|
135
|
+
const passed = summary.high === 0;
|
|
136
|
+
return { findings, summary, passed };
|
|
137
|
+
}
|
|
138
|
+
export function runFallbackAnalysisFromFile(filePath) {
|
|
139
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
140
|
+
return runFallbackAnalysis(source);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/analyzer/fallback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface Finding {
|
|
2
|
+
severity: 'High' | 'Medium' | 'Low' | 'Informational' | 'Optimization';
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
location: string;
|
|
6
|
+
recommendation: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AnalysisSummary {
|
|
9
|
+
high: number;
|
|
10
|
+
medium: number;
|
|
11
|
+
low: number;
|
|
12
|
+
informational: number;
|
|
13
|
+
optimization: number;
|
|
14
|
+
total: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AnalysisResult {
|
|
17
|
+
findings: Finding[];
|
|
18
|
+
summary: AnalysisSummary;
|
|
19
|
+
passed: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function analyzeContract(filePath: string): Promise<AnalysisResult>;
|
|
22
|
+
export declare function isSlitherAvailable(): boolean;
|
|
23
|
+
export declare function getSlitherInstallInstructions(): string;
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAyBD,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA4B/E;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAWtD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { isSlitherInstalled, runSlither, mapSlitherResults } from './slither.js';
|
|
3
|
+
import { runFallbackAnalysis } from './fallback.js';
|
|
4
|
+
function buildSummary(findings) {
|
|
5
|
+
const summary = {
|
|
6
|
+
high: 0,
|
|
7
|
+
medium: 0,
|
|
8
|
+
low: 0,
|
|
9
|
+
informational: 0,
|
|
10
|
+
optimization: 0,
|
|
11
|
+
total: findings.length,
|
|
12
|
+
};
|
|
13
|
+
for (const finding of findings) {
|
|
14
|
+
switch (finding.severity) {
|
|
15
|
+
case 'High':
|
|
16
|
+
summary.high++;
|
|
17
|
+
break;
|
|
18
|
+
case 'Medium':
|
|
19
|
+
summary.medium++;
|
|
20
|
+
break;
|
|
21
|
+
case 'Low':
|
|
22
|
+
summary.low++;
|
|
23
|
+
break;
|
|
24
|
+
case 'Informational':
|
|
25
|
+
summary.informational++;
|
|
26
|
+
break;
|
|
27
|
+
case 'Optimization':
|
|
28
|
+
summary.optimization++;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return summary;
|
|
33
|
+
}
|
|
34
|
+
export async function analyzeContract(filePath) {
|
|
35
|
+
if (!existsSync(filePath)) {
|
|
36
|
+
throw new Error(`Contract file not found: ${filePath}`);
|
|
37
|
+
}
|
|
38
|
+
if (isSlitherAvailable()) {
|
|
39
|
+
const output = runSlither(filePath);
|
|
40
|
+
const findings = mapSlitherResults(output);
|
|
41
|
+
const summary = buildSummary(findings);
|
|
42
|
+
const passed = summary.high === 0;
|
|
43
|
+
return { findings, summary, passed };
|
|
44
|
+
}
|
|
45
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
46
|
+
const result = runFallbackAnalysis(source);
|
|
47
|
+
result.findings.unshift({
|
|
48
|
+
severity: 'Informational',
|
|
49
|
+
title: '[Basic Check] Slither Not Installed',
|
|
50
|
+
description: 'Slither is not installed. Only basic regex-based checks were performed. Install Slither for comprehensive analysis.',
|
|
51
|
+
location: 'N/A',
|
|
52
|
+
recommendation: getSlitherInstallInstructions(),
|
|
53
|
+
});
|
|
54
|
+
result.summary.informational++;
|
|
55
|
+
result.summary.total++;
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
export function isSlitherAvailable() {
|
|
59
|
+
return isSlitherInstalled();
|
|
60
|
+
}
|
|
61
|
+
export function getSlitherInstallInstructions() {
|
|
62
|
+
return [
|
|
63
|
+
'Install Slither for comprehensive smart contract analysis:',
|
|
64
|
+
' pip3 install slither-analyzer',
|
|
65
|
+
'',
|
|
66
|
+
'Or with pipx:',
|
|
67
|
+
' pipx install slither-analyzer',
|
|
68
|
+
'',
|
|
69
|
+
'Requires Python 3.8+ and solc (Solidity compiler).',
|
|
70
|
+
'More info: https://github.com/crytic/slither',
|
|
71
|
+
].join('\n');
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AA0BpD,SAAS,YAAY,CAAC,QAAmB;IACvC,MAAM,OAAO,GAAoB;QAC/B,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,aAAa,EAAE,CAAC;QAChB,YAAY,EAAE,CAAC;QACf,KAAK,EAAE,QAAQ,CAAC,MAAM;KACvB,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;YACzB,KAAK,MAAM;gBAAE,OAAO,CAAC,IAAI,EAAE,CAAC;gBAAC,MAAM;YACnC,KAAK,QAAQ;gBAAE,OAAO,CAAC,MAAM,EAAE,CAAC;gBAAC,MAAM;YACvC,KAAK,KAAK;gBAAE,OAAO,CAAC,GAAG,EAAE,CAAC;gBAAC,MAAM;YACjC,KAAK,eAAe;gBAAE,OAAO,CAAC,aAAa,EAAE,CAAC;gBAAC,MAAM;YACrD,KAAK,cAAc;gBAAE,OAAO,CAAC,YAAY,EAAE,CAAC;gBAAC,MAAM;QACrD,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,kBAAkB,EAAE,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAc,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC;QAClC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE3C,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtB,QAAQ,EAAE,eAAe;QACzB,KAAK,EAAE,qCAAqC;QAC5C,WAAW,EAAE,qHAAqH;QAClI,QAAQ,EAAE,KAAK;QACf,cAAc,EAAE,6BAA6B,EAAE;KAChD,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IAC/B,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAEvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,kBAAkB,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,OAAO;QACL,4DAA4D;QAC5D,iCAAiC;QACjC,EAAE;QACF,eAAe;QACf,iCAAiC;QACjC,EAAE;QACF,oDAAoD;QACpD,8CAA8C;KAC/C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface SlitherDetector {
|
|
2
|
+
check: string;
|
|
3
|
+
impact: string;
|
|
4
|
+
confidence: string;
|
|
5
|
+
description: string;
|
|
6
|
+
elements: SlitherElement[];
|
|
7
|
+
first_markdown_element: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SlitherElement {
|
|
10
|
+
type: string;
|
|
11
|
+
name: string;
|
|
12
|
+
source_mapping: {
|
|
13
|
+
filename_relative: string;
|
|
14
|
+
lines: number[];
|
|
15
|
+
starting_column: number;
|
|
16
|
+
ending_column: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface SlitherOutput {
|
|
20
|
+
success: boolean;
|
|
21
|
+
error: string | null;
|
|
22
|
+
results: {
|
|
23
|
+
detectors: SlitherDetector[];
|
|
24
|
+
} | null;
|
|
25
|
+
}
|
|
26
|
+
export interface Finding {
|
|
27
|
+
severity: 'High' | 'Medium' | 'Low' | 'Informational' | 'Optimization';
|
|
28
|
+
title: string;
|
|
29
|
+
description: string;
|
|
30
|
+
location: string;
|
|
31
|
+
recommendation: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function isSlitherInstalled(): boolean;
|
|
34
|
+
export declare function runSlither(filePath: string): SlitherOutput;
|
|
35
|
+
export declare function mapSlitherResults(output: SlitherOutput): Finding[];
|
|
36
|
+
//# sourceMappingURL=slither.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slither.d.ts","sourceRoot":"","sources":["../../src/analyzer/slither.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE;QACd,iBAAiB,EAAE,MAAM,CAAC;QAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE;QACP,SAAS,EAAE,eAAe,EAAE,CAAC;KAC9B,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAcD,wBAAgB,kBAAkB,IAAI,OAAO,CAQ5C;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CA6B1D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,EAAE,CAkBlE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
const IMPACT_MAP = {
|
|
4
|
+
High: 'High',
|
|
5
|
+
Medium: 'Medium',
|
|
6
|
+
Low: 'Low',
|
|
7
|
+
Informational: 'Informational',
|
|
8
|
+
Optimization: 'Optimization',
|
|
9
|
+
};
|
|
10
|
+
function mapImpact(impact) {
|
|
11
|
+
return IMPACT_MAP[impact] ?? 'Informational';
|
|
12
|
+
}
|
|
13
|
+
export function isSlitherInstalled() {
|
|
14
|
+
try {
|
|
15
|
+
const cmd = platform() === 'win32' ? 'where slither' : 'which slither';
|
|
16
|
+
execSync(cmd, { stdio: 'pipe' });
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function runSlither(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
const stdout = execSync(`slither ${filePath} --json -`, {
|
|
26
|
+
encoding: 'utf-8',
|
|
27
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
28
|
+
timeout: 120_000,
|
|
29
|
+
});
|
|
30
|
+
const parsed = JSON.parse(stdout);
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err && typeof err === 'object' && 'stdout' in err) {
|
|
35
|
+
const stdout = err.stdout;
|
|
36
|
+
if (stdout && typeof stdout === 'string') {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(stdout);
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// JSON parse failed, fall through
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: err instanceof Error ? err.message : String(err),
|
|
49
|
+
results: null,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function mapSlitherResults(output) {
|
|
54
|
+
if (!output.results?.detectors) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
return output.results.detectors.map((detector) => {
|
|
58
|
+
const location = detector.elements.length > 0
|
|
59
|
+
? formatLocation(detector.elements[0])
|
|
60
|
+
: 'Unknown';
|
|
61
|
+
return {
|
|
62
|
+
severity: mapImpact(detector.impact),
|
|
63
|
+
title: detector.check,
|
|
64
|
+
description: detector.description,
|
|
65
|
+
location,
|
|
66
|
+
recommendation: getRecommendation(detector.check),
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function formatLocation(element) {
|
|
71
|
+
const file = element.source_mapping.filename_relative;
|
|
72
|
+
const lines = element.source_mapping.lines;
|
|
73
|
+
if (lines.length > 0) {
|
|
74
|
+
return `${file}#L${lines[0]}`;
|
|
75
|
+
}
|
|
76
|
+
return file;
|
|
77
|
+
}
|
|
78
|
+
function getRecommendation(check) {
|
|
79
|
+
const recommendations = {
|
|
80
|
+
'reentrancy-eth': 'Use the checks-effects-interactions pattern or a reentrancy guard.',
|
|
81
|
+
'reentrancy-no-eth': 'Use the checks-effects-interactions pattern or a reentrancy guard.',
|
|
82
|
+
'unchecked-lowlevel': 'Check the return value of low-level calls.',
|
|
83
|
+
'unchecked-send': 'Check the return value of send().',
|
|
84
|
+
'tx-origin': 'Use msg.sender instead of tx.origin for authorization.',
|
|
85
|
+
'suicidal': 'Remove or protect selfdestruct with access control.',
|
|
86
|
+
'arbitrary-send': 'Restrict who can call functions that send Ether.',
|
|
87
|
+
'locked-ether': 'Add a withdrawal function for locked Ether.',
|
|
88
|
+
'solc-version': 'Use a fixed Solidity version pragma.',
|
|
89
|
+
};
|
|
90
|
+
return recommendations[check] ?? 'Review and address this finding.';
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=slither.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slither.js","sourceRoot":"","sources":["../../src/analyzer/slither.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Finding } from '../../analyzer/index.js';
|
|
2
|
+
export interface AnalyzeCommandResult {
|
|
3
|
+
passed: boolean;
|
|
4
|
+
findings: Finding[];
|
|
5
|
+
}
|
|
6
|
+
export declare function analyzeCommand(file: string): Promise<AnalyzeCommandResult>;
|
|
7
|
+
//# sourceMappingURL=analyze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/analyze.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAUvD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgEhF"}
|