├── auditai-cli ├── .gitignore ├── package.json ├── index.js ├── test.sol ├── src │ └── ai-prompt.js ├── yarn.lock └── package-lock.json ├── public ├── audit.png ├── report.png ├── thumbnail.jpg ├── vercel.svg └── next.svg ├── next.config.mjs ├── postcss.config.mjs ├── utils ├── cn.ts └── ai-prompt.ts ├── .gitignore ├── components ├── header.tsx ├── contract-input.tsx ├── ui │ ├── wavy-background.tsx │ └── placeholders-and-vanish-input.tsx └── result-modal.tsx ├── tailwind.config.ts ├── app ├── layout.tsx ├── globals.css └── page.jsx ├── tsconfig.json ├── package.json ├── test.sol └── README.md /auditai-cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .env 4 | -------------------------------------------------------------------------------- /public/audit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mendsalbert/auditra/HEAD/public/audit.png -------------------------------------------------------------------------------- /public/report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mendsalbert/auditra/HEAD/public/report.png -------------------------------------------------------------------------------- /public/thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mendsalbert/auditra/HEAD/public/thumbnail.jpg -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /utils/cn.ts: -------------------------------------------------------------------------------- 1 | import { ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | .yarn/install-state.gz 6 | 7 | # testing 8 | /coverage 9 | 10 | # next.js 11 | /.next/ 12 | /out/ 13 | 14 | # production 15 | /build 16 | 17 | # misc 18 | .DS_Store 19 | *.pem 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env*.local 28 | .env 29 | /.env 30 | /env 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /components/header.tsx: -------------------------------------------------------------------------------- 1 | import { WavyBackground } from "@/components/ui/wavy-background"; 2 | 3 | export default function Header() { 4 | return ( 5 | 6 |

7 | Auditra, AI Smart Contract Auditor 8 |

9 |

10 | Leverage the power of AI to audit your smart contracts 11 |

12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Poppins } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const poppins = Poppins({ weight: ["400", "700"], subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "Auditra", 9 | description: "AI Smart Contract Auditor", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /auditai-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auditai-cli", 3 | "version": "1.0.5", 4 | "description": "A CLI tool to audit smart contracts using OpenAI", 5 | "main": "index.js", 6 | "bin": { 7 | "auditai": "./index.js" 8 | }, 9 | "scripts": { 10 | "start": "node index.js" 11 | }, 12 | "keywords": [ 13 | "cli", 14 | "smart contract", 15 | "audit", 16 | "openai" 17 | ], 18 | "author": "Your Name", 19 | "license": "MIT", 20 | "dependencies": { 21 | "commander": "^8.0.0", 22 | "dotenv": "^10.0.0", 23 | "inquirer": "^8.0.0", 24 | "openai": "^4.53.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | @layer utilities { 30 | .text-balance { 31 | text-wrap: balance; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "forceConsistentCasingInFileNames": true, 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": [ 27 | "next-env.d.ts", 28 | "**/*.ts", 29 | "**/*.tsx", 30 | ".next/types/**/*.ts", 31 | "app/page.jsx", 32 | "auditai-cli/src/auditai.js" 33 | ], 34 | "exclude": ["node_modules"] 35 | } 36 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Header from "@/components/header"; 5 | import ContractInput from "@/components/contract-input"; 6 | import ResultsModal from "@/components/result-modal"; 7 | import { analyzeContract } from "@/utils/ai-prompt"; 8 | 9 | export default function Home() { 10 | const [loading, setLoading] = useState(false); 11 | const [contract, setContract] = useState(""); 12 | const [results, setResults] = useState(null); 13 | const [isModalOpen, setIsModalOpen] = useState(false); 14 | 15 | const analyze = async () => { 16 | setIsModalOpen(true); 17 | await analyzeContract(contract, setResults, setLoading); 18 | }; 19 | 20 | const fixIssues = async () => { 21 | // const suggestions = results.find( 22 | // (r) => r.section === "Suggestions for Improvement" 23 | // ).details; 24 | // await fixIssues(contract, suggestions, setContract, setLoading); 25 | }; 26 | 27 | return ( 28 |
29 |
30 | 35 | setIsModalOpen(false)} 38 | loading={loading} 39 | results={results} 40 | fixIssues={fixIssues} 41 | /> 42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /auditai-cli/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { Command } = require("commander"); 4 | const inquirer = require("inquirer"); 5 | const { analyzeContract } = require("./src/ai-prompt"); 6 | const fs = require("fs"); 7 | const path = require("path"); 8 | require("dotenv").config(); 9 | 10 | const program = new Command(); 11 | 12 | program 13 | .name("auditai") 14 | .description("A CLI tool to audit smart contracts using OpenAI") 15 | .version("1.0.0"); 16 | 17 | const getApiKey = async () => { 18 | const { apiKey } = await inquirer.prompt([ 19 | { 20 | type: "input", 21 | name: "apiKey", 22 | message: "Enter your OpenAI API key:", 23 | validate: (input) => input.length > 0 || "API key is required", 24 | }, 25 | ]); 26 | return apiKey; 27 | }; 28 | 29 | program 30 | .command("check ") 31 | .description("Analyze a smart contract") 32 | .action(async (file) => { 33 | try { 34 | const apiKey = await getApiKey(); 35 | 36 | const contractPath = path.resolve(process.cwd(), file); 37 | console.log(`Checking file at path: ${contractPath}`); 38 | 39 | if (!fs.existsSync(contractPath)) { 40 | console.error(`File not found: ${contractPath}`); 41 | process.exit(1); 42 | } 43 | 44 | if (fs.statSync(contractPath).isDirectory()) { 45 | console.error(`Path is a directory, not a file: ${contractPath}`); 46 | process.exit(1); 47 | } 48 | 49 | const contract = fs.readFileSync(contractPath, "utf8"); 50 | await analyzeContract(contract, apiKey); 51 | } catch (error) { 52 | console.error("Error during analysis:", error.message); 53 | } 54 | }); 55 | 56 | program.parse(process.argv); 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auditai", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@chainlink/contracts": "^1.1.1", 13 | "@headlessui/react": "^2.0.4", 14 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", 15 | "@nomicfoundation/hardhat-ethers": "^3.0.6", 16 | "@nomicfoundation/hardhat-ignition": "^0.15.4", 17 | "@nomicfoundation/hardhat-ignition-ethers": "^0.15.4", 18 | "@nomicfoundation/hardhat-network-helpers": "^1.0.10", 19 | "@nomicfoundation/hardhat-toolbox": "^5.0.0", 20 | "@nomicfoundation/hardhat-verify": "^2.0.7", 21 | "@tabler/icons-react": "^3.11.0", 22 | "@typechain/ethers-v6": "^0.5.1", 23 | "@typechain/hardhat": "^9.1.0", 24 | "@types/prismjs": "^1.26.4", 25 | "auditai-cli": "^1.0.2", 26 | "chai": "4.3.7", 27 | "clsx": "^2.1.1", 28 | "ethers": "5.7.2", 29 | "framer-motion": "^11.2.6", 30 | "hardhat": "^2.22.4", 31 | "hardhat-gas-reporter": "^2.2.0", 32 | "next": "14.2.3", 33 | "openai": "^4.47.1", 34 | "prismjs": "^1.29.0", 35 | "react": "^18", 36 | "react-circular-progressbar": "^2.1.0", 37 | "react-dom": "^18", 38 | "react-simple-code-editor": "^0.14.1", 39 | "react-syntax-highlighter": "^15.5.0", 40 | "simplex-noise": "^4.0.1", 41 | "solidity-coverage": "^0.8.12", 42 | "tailwind-merge": "^2.3.0", 43 | "thirdweb": "5.24.0", 44 | "typechain": "^8.3.2" 45 | }, 46 | "devDependencies": { 47 | "@types/node": "^20", 48 | "@types/react": "^18", 49 | "@types/react-dom": "^18", 50 | "postcss": "^8", 51 | "tailwindcss": "^3.4.1", 52 | "typescript": "^5" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract AuditAI { 5 | string public name = "AuditAI Token"; 6 | string public symbol = "AAT"; 7 | uint8 public decimals = 18; 8 | uint256 public totalSupply = 1000000 * 10 ** uint256(decimals); 9 | mapping(address => uint256) public balanceOf; 10 | mapping(address => mapping(address => uint256)) public allowance; 11 | 12 | event Transfer(address indexed from, address indexed to, uint256 value); 13 | event Approval(address indexed owner, address indexed spender, uint256 value); 14 | event SmartContractAudited(address indexed auditor, string contractCode, string auditReport); 15 | 16 | constructor() { 17 | balanceOf[msg.sender] = totalSupply; 18 | } 19 | 20 | function transfer(address _to, uint256 _value) public returns (bool success) { 21 | require(balanceOf[msg.sender] >= _value, "Insufficient balance"); 22 | balanceOf[msg.sender] -= _value; 23 | balanceOf[_to] += _value; 24 | emit Transfer(msg.sender, _to, _value); 25 | return true; 26 | } 27 | 28 | function approve(address _spender, uint256 _value) public returns (bool success) { 29 | allowance[msg.sender][_spender] = _value; 30 | emit Approval(msg.sender, _spender, _value); 31 | return true; 32 | } 33 | 34 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 35 | require(_value <= balanceOf[_from], "Insufficient balance"); 36 | require(_value <= allowance[_from][msg.sender], "Allowance exceeded"); 37 | balanceOf[_from] -= _value; 38 | balanceOf[_to] += _value; 39 | allowance[_from][msg.sender] -= _value; 40 | emit Transfer(_from, _to, _value); 41 | return true; 42 | } 43 | 44 | function auditSmartContract(string calldata contractCode, string calldata auditReport) external { 45 | emit SmartContractAudited(msg.sender, contractCode, auditReport); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /auditai-cli/test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract AuditAI { 5 | string public name = "AuditAI Token"; 6 | string public symbol = "AAT"; 7 | uint8 public decimals = 18; 8 | uint256 public totalSupply = 1000000 * 10 ** uint256(decimals); 9 | mapping(address => uint256) public balanceOf; 10 | mapping(address => mapping(address => uint256)) public allowance; 11 | 12 | event Transfer(address indexed from, address indexed to, uint256 value); 13 | event Approval(address indexed owner, address indexed spender, uint256 value); 14 | event SmartContractAudited(address indexed auditor, string contractCode, string auditReport); 15 | 16 | constructor() { 17 | balanceOf[msg.sender] = totalSupply; 18 | } 19 | 20 | function transfer(address _to, uint256 _value) public returns (bool success) { 21 | require(balanceOf[msg.sender] >= _value, "Insufficient balance"); 22 | balanceOf[msg.sender] -= _value; 23 | balanceOf[_to] += _value; 24 | emit Transfer(msg.sender, _to, _value); 25 | return true; 26 | } 27 | 28 | function approve(address _spender, uint256 _value) public returns (bool success) { 29 | allowance[msg.sender][_spender] = _value; 30 | emit Approval(msg.sender, _spender, _value); 31 | return true; 32 | } 33 | 34 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 35 | require(_value <= balanceOf[_from], "Insufficient balance"); 36 | require(_value <= allowance[_from][msg.sender], "Allowance exceeded"); 37 | balanceOf[_from] -= _value; 38 | balanceOf[_to] += _value; 39 | allowance[_from][msg.sender] -= _value; 40 | emit Transfer(_from, _to, _value); 41 | return true; 42 | } 43 | 44 | function auditSmartContract(string calldata contractCode, string calldata auditReport) external { 45 | emit SmartContractAudited(msg.sender, contractCode, auditReport); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /auditai-cli/src/ai-prompt.js: -------------------------------------------------------------------------------- 1 | const OpenAI = require("openai"); 2 | 3 | const analyzeContract = async (contract, apiKey) => { 4 | const openai = new OpenAI({ 5 | apiKey: apiKey, 6 | }); 7 | 8 | const params = { 9 | model: "gpt-3.5-turbo", 10 | messages: [ 11 | { 12 | role: "user", 13 | content: `Your role and goal is to be an AI Smart Contract Auditor. Your job is to perform an audit on the given smart contract. Here is the smart contract: ${contract}. 14 | Please provide the results in the following array format for easy front-end display: 15 | [ 16 | { 17 | "section": "Audit Report", 18 | "details": "A detailed audit report of the smart contract, covering security, performance, and any other relevant aspects." 19 | }, 20 | { 21 | "section": "Metric Scores", 22 | "details": [ 23 | { 24 | "metric": "Security", 25 | "score": 0-10 26 | }, 27 | { 28 | "metric": "Performance", 29 | "score": 0-10 30 | }, 31 | { 32 | "metric": "Other Key Areas", 33 | "score": 0-10 34 | }, 35 | { 36 | "metric": "Gas Efficiency", 37 | "score": 0-10 38 | }, 39 | { 40 | "metric": "Code Quality", 41 | "score": 0-10 42 | }, 43 | { 44 | "metric": "Documentation", 45 | "score": 0-10 46 | } 47 | ] 48 | }, 49 | { 50 | "section": "Suggestions for Improvement", 51 | "details": "Suggestions for improving the smart contract in terms of security, performance, and any other identified weaknesses." 52 | } 53 | ] 54 | Thank you.`, 55 | }, 56 | ], 57 | }; 58 | 59 | const chatCompletion = await openai.chat.completions.create(params); 60 | 61 | const auditResults = JSON.parse(chatCompletion.choices[0].message.content); 62 | 63 | console.log("Audit Report:"); 64 | console.log(auditResults.find((r) => r.section === "Audit Report").details); 65 | 66 | console.log("\nMetric Scores:"); 67 | auditResults 68 | .find((r) => r.section === "Metric Scores") 69 | .details.forEach((metric) => { 70 | console.log(`${metric.metric}: ${metric.score}/10`); 71 | }); 72 | 73 | console.log("\nSuggestions for Improvement:"); 74 | console.log( 75 | auditResults.find((r) => r.section === "Suggestions for Improvement") 76 | .details 77 | ); 78 | }; 79 | 80 | module.exports = { analyzeContract }; 81 | -------------------------------------------------------------------------------- /utils/ai-prompt.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from "openai"; 2 | 3 | const apiKey = process.env.NEXT_PUBLIC_API_KEY; 4 | 5 | const openai = new OpenAI({ 6 | apiKey: apiKey, 7 | dangerouslyAllowBrowser: true, 8 | }); 9 | 10 | export const analyzeContract = async ( 11 | contract: string, 12 | setResults: any, 13 | setLoading: any, 14 | auditSmartContract: any 15 | ) => { 16 | setLoading(true); 17 | const chatCompletion = (await openai.chat.completions.create({ 18 | messages: [ 19 | { 20 | role: "user", 21 | content: `Your role and goal is to be an AI Smart Contract Auditor. Your job is to perform an audit on the given smart contract. Here is the smart contract: ${contract}. 22 | 23 | Please provide the results in the following array format for easy front-end display: 24 | 25 | [ 26 | { 27 | "section": "Audit Report", 28 | "details": "A detailed audit report of the smart contract, covering security, performance, and any other relevant aspects." 29 | }, 30 | { 31 | "section": "Metric Scores", 32 | "details": [ 33 | { 34 | "metric": "Security", 35 | "score": 0-10 36 | }, 37 | { 38 | "metric": "Performance", 39 | "score": 0-10 40 | }, 41 | { 42 | "metric": "Other Key Areas", 43 | "score": 0-10 44 | }, 45 | { 46 | "metric": "Gas Efficiency", 47 | "score": 0-10 48 | }, 49 | { 50 | "metric": "Code Quality", 51 | "score": 0-10 52 | }, 53 | { 54 | "metric": "Documentation", 55 | "score": 0-10 56 | } 57 | ] 58 | }, 59 | { 60 | "section": "Suggestions for Improvement", 61 | "details": "Suggestions for improving the smart contract in terms of security, performance, and any other identified weaknesses." 62 | } 63 | ] 64 | 65 | Thank you.`, 66 | }, 67 | ], 68 | model: "gpt-3.5-turbo", 69 | })) as any; 70 | 71 | const auditResults = JSON.parse(chatCompletion.choices[0].message.content); 72 | setResults(auditResults); 73 | setLoading(false); 74 | }; 75 | 76 | export const fixIssues = async ( 77 | contract: string, 78 | suggestions: string, 79 | setContract: (contract: string) => void, 80 | setLoading: (loading: boolean) => void 81 | ) => { 82 | setLoading(true); 83 | 84 | const response = (await openai.chat.completions.create({ 85 | messages: [ 86 | { 87 | role: "user", 88 | content: `Here is the smart contract with the following issues: ${suggestions}. Please provide a fixed version of the contract:\n\n${contract}`, 89 | }, 90 | ], 91 | model: "gpt-3.5-turbo", 92 | })) as any; 93 | 94 | const fixedContract = response.choices[0].message.content; 95 | setContract(fixedContract.trim()); 96 | setLoading(false); 97 | }; 98 | -------------------------------------------------------------------------------- /components/contract-input.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Editor from "react-simple-code-editor"; 3 | import Prism from "prismjs"; 4 | import "prismjs/components/prism-solidity"; 5 | import "prismjs/themes/prism-tomorrow.css"; // Import a Prism theme for styling 6 | import { IconChecklist, IconPaperclip, IconSend } from "@tabler/icons-react"; 7 | 8 | interface CustomCodeEditorProps { 9 | contract: string; 10 | setContract: React.Dispatch>; 11 | analyze: () => Promise; 12 | } 13 | 14 | const highlightWithPrism = (code: string) => { 15 | return Prism.highlight(code, Prism.languages.solidity, "solidity"); 16 | }; 17 | 18 | // Utility function to check if the contract contains the required SPDX license and pragma directive 19 | const isValidSolidityContract = (code: string) => { 20 | const SPDXRegex = /\/\/\s*SPDX-License-Identifier:\s*[^\s]+/; 21 | const pragmaRegex = /pragma\s+solidity\s+[^;]+;/; 22 | return SPDXRegex.test(code) && pragmaRegex.test(code); 23 | }; 24 | 25 | const CustomCodeEditor: React.FC = ({ 26 | contract, 27 | setContract, 28 | analyze, 29 | }) => { 30 | const handleAnalyze = () => { 31 | if (!isValidSolidityContract(contract)) { 32 | alert( 33 | "The provided code does not appear to be a valid Solidity smart contract. Make sure it starts with the SPDX license identifier and the 'pragma' directive." 34 | ); 35 | return; 36 | } 37 | analyze(); 38 | }; 39 | 40 | return ( 41 |
42 |
46 | setContract(code)} 49 | highlight={(code) => highlightWithPrism(code)} 50 | padding={15} 51 | textareaId="code-editor" 52 | className="textarea-editor" 53 | textareaClassName="outline-none " 54 | style={{ 55 | fontFamily: ' "Fira Mono", monospace', 56 | fontSize: 17, 57 | minHeight: "100%", 58 | background: "transparent", 59 | color: "inherit", 60 | }} 61 | /> 62 |
63 | 64 |
65 |
66 |
67 | 73 |
74 | 75 |
76 | 84 |
85 |
86 |
87 |
88 | ); 89 | }; 90 | 91 | export default CustomCodeEditor; 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Project Screenshot 5 | 6 | 7 |
8 | 9 |
10 | javascript 11 | openai 12 |
13 | 14 |

AuditAI

15 | 16 |
17 | AuditAI is an innovative tool designed to leverage the power of AI to audit smart contracts. This project uses the OpenAI API to generate detailed audit reports. 18 |
19 |
20 | 21 | ## 📋 Table of Contents 22 | 23 | 1. 🤖 [Introduction](#introduction) 24 | 2. 🔋 [Features](#features) 25 | 3. ⚙️ [Architecture](#architecture) 26 | 4. 🤸 [Setup and Deployment](#setup-and-deployment) 27 | 5. 🚀 [Usage](#usage) 28 | 6. 🤝 [Contributing](#contributing) 29 | 7. 📜 [License](#license) 30 | 31 | ## 🤖 Introduction 32 | 33 | AuditAI provides an easy and efficient way to audit your smart contracts using AI. It interacts with the OpenAI API to analyze and generate detailed reports on the provided smart contract code. 34 | 35 | ## 🔋 Features 36 | 37 | - **AI-Powered Auditing**: Leverages OpenAI to generate comprehensive audit reports. 38 | - **Command Line Tool**: Provides a CLI for easy integration into development workflows. 39 | - **User-Friendly Interface**: Offers an easy-to-use frontend for auditing smart contracts. 40 | 41 | ## ⚙️ Architecture 42 | 43 | 1. **Command Line Tool**: `auditai` 44 | 2. **Frontend**: React-based user interface 45 | 3. **API Integration**: OpenAI API 46 | 47 | ## 🤸 Setup and Deployment 48 | 49 | ### Prerequisites 50 | 51 | - Node.js and npm installed 52 | 53 | ### Steps 54 | 55 | 1. **Clone the Repository** 56 | 57 | ```bash 58 | git clone https://github.com/yourusername/AuditAI.git 59 | cd AuditAI 60 | ``` 61 | 62 | 2. **Install Dependencies** 63 | 64 | ```bash 65 | npm install 66 | ``` 67 | 68 | 3. **Setup Environment Variables** 69 | 70 | Create a `.env` file in the root directory with the following content: 71 | 72 | ```plaintext 73 | OPENAI_API_KEY=your_openai_api_key 74 | ``` 75 | 76 | 4. **Build the Project** 77 | 78 | ```bash 79 | npm run build 80 | ``` 81 | 82 | 5. **Install the CLI Globally** 83 | 84 | ```bash 85 | npm install -g . 86 | ``` 87 | 88 | ## 🚀 Usage 89 | 90 | ### Using the CLI 91 | 92 | 1. **Analyze a Smart Contract** 93 | 94 | ```bash 95 | auditai check 96 | ``` 97 | 98 | ### Using the Frontend 99 | 100 | 1. **Start the Frontend** 101 | 102 | ```bash 103 | npm run dev 104 | ``` 105 | 106 | 2. **Connect Wallet**: Connect your MetaMask wallet to the appropriate network. 107 | 3. **Submit Contract Code**: Paste your smart contract code into the provided textarea and click "Analyze". 108 | 4. **View Results**: After analysis, view the detailed audit report and metrics in the modal that appears. 109 | 110 | ## 🤝 Contributing 111 | 112 | Contributions are welcome! Please fork the repository and submit a pull request for any improvements or bug fixes. 113 | 114 | ## 📜 License 115 | 116 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 117 | 118 | ``` 119 | 120 | This version should display correctly in your README. Adjust any specific details such as image links, repository links, and API keys to match your project. 121 | ``` 122 | -------------------------------------------------------------------------------- /components/ui/wavy-background.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { cn } from "@/utils/cn"; 3 | import React, { useEffect, useRef, useState } from "react"; 4 | import { createNoise3D } from "simplex-noise"; 5 | 6 | export const WavyBackground = ({ 7 | children, 8 | className, 9 | containerClassName, 10 | colors, 11 | waveWidth, 12 | backgroundFill, 13 | blur = 10, 14 | speed = "fast", 15 | waveOpacity = 0.5, 16 | ...props 17 | }: { 18 | children?: any; 19 | className?: string; 20 | containerClassName?: string; 21 | colors?: string[]; 22 | waveWidth?: number; 23 | backgroundFill?: string; 24 | blur?: number; 25 | speed?: "slow" | "fast"; 26 | waveOpacity?: number; 27 | [key: string]: any; 28 | }) => { 29 | const noise = createNoise3D(); 30 | let w: number, 31 | h: number, 32 | nt: number, 33 | i: number, 34 | x: number, 35 | ctx: any, 36 | canvas: any; 37 | const canvasRef = useRef(null); 38 | const getSpeed = () => { 39 | switch (speed) { 40 | case "slow": 41 | return 0.001; 42 | case "fast": 43 | return 0.002; 44 | default: 45 | return 0.001; 46 | } 47 | }; 48 | 49 | const init = () => { 50 | canvas = canvasRef.current; 51 | ctx = canvas.getContext("2d"); 52 | w = ctx.canvas.width = window.innerWidth; 53 | h = ctx.canvas.height = window.innerHeight; 54 | ctx.filter = `blur(${blur}px)`; 55 | nt = 0; 56 | window.onresize = function () { 57 | w = ctx.canvas.width = window.innerWidth; 58 | h = ctx.canvas.height = window.innerHeight; 59 | ctx.filter = `blur(${blur}px)`; 60 | }; 61 | render(); 62 | }; 63 | 64 | const waveColors = colors ?? [ 65 | "#38bdf8", 66 | "#818cf8", 67 | "#c084fc", 68 | "#e879f9", 69 | "#22d3ee", 70 | ]; 71 | const drawWave = (n: number) => { 72 | nt += getSpeed(); 73 | for (i = 0; i < n; i++) { 74 | ctx.beginPath(); 75 | ctx.lineWidth = waveWidth || 50; 76 | ctx.strokeStyle = waveColors[i % waveColors.length]; 77 | for (x = 0; x < w; x += 5) { 78 | var y = noise(x / 800, 0.3 * i, nt) * 100; 79 | ctx.lineTo(x, y + h * 0.5); // adjust for height, currently at 50% of the container 80 | } 81 | ctx.stroke(); 82 | ctx.closePath(); 83 | } 84 | }; 85 | 86 | let animationId: number; 87 | const render = () => { 88 | ctx.fillStyle = backgroundFill || "black"; 89 | ctx.globalAlpha = waveOpacity || 0.5; 90 | ctx.fillRect(0, 0, w, h); 91 | drawWave(5); 92 | animationId = requestAnimationFrame(render); 93 | }; 94 | 95 | useEffect(() => { 96 | init(); 97 | return () => { 98 | cancelAnimationFrame(animationId); 99 | }; 100 | }, []); 101 | 102 | const [isSafari, setIsSafari] = useState(false); 103 | useEffect(() => { 104 | // I'm sorry but i have got to support it on safari. 105 | setIsSafari( 106 | typeof window !== "undefined" && 107 | navigator.userAgent.includes("Safari") && 108 | !navigator.userAgent.includes("Chrome") 109 | ); 110 | }, []); 111 | 112 | return ( 113 |
119 | 127 |
128 | {children} 129 |
130 |
131 | ); 132 | }; 133 | -------------------------------------------------------------------------------- /components/ui/placeholders-and-vanish-input.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AnimatePresence, motion } from "framer-motion"; 4 | import { useCallback, useEffect, useRef, useState } from "react"; 5 | import { cn } from "@/utils/cn"; 6 | 7 | export function PlaceholdersAndVanishTextarea({ 8 | placeholders, 9 | onChange, 10 | onSubmit, 11 | }: { 12 | placeholders: string[]; 13 | onChange: (e: React.ChangeEvent) => void; 14 | onSubmit: (e: React.FormEvent) => void; 15 | }) { 16 | const [currentPlaceholder, setCurrentPlaceholder] = useState(0); 17 | 18 | useEffect(() => { 19 | let interval: any; 20 | const startAnimation = () => { 21 | interval = setInterval(() => { 22 | setCurrentPlaceholder((prev) => (prev + 1) % placeholders.length); 23 | }, 1500); 24 | }; 25 | startAnimation(); 26 | return () => clearInterval(interval); 27 | }, [placeholders.length]); 28 | 29 | const canvasRef = useRef(null); 30 | const newDataRef = useRef([]); 31 | const textareaRef = useRef(null); 32 | const [value, setValue] = useState(""); 33 | const [animating, setAnimating] = useState(false); 34 | 35 | const draw = useCallback(() => { 36 | if (!textareaRef.current) return; 37 | const canvas = canvasRef.current; 38 | if (!canvas) return; 39 | const ctx = canvas.getContext("2d"); 40 | if (!ctx) return; 41 | 42 | canvas.width = 800; 43 | canvas.height = 800; 44 | ctx.clearRect(0, 0, 800, 800); 45 | const computedStyles = getComputedStyle(textareaRef.current); 46 | 47 | const fontSize = parseFloat(computedStyles.getPropertyValue("font-size")); 48 | ctx.font = `${fontSize * 2}px ${computedStyles.fontFamily}`; 49 | ctx.fillStyle = "#FFF"; 50 | ctx.fillText(value, 16, 40); 51 | 52 | const imageData = ctx.getImageData(0, 0, 800, 800); 53 | const pixelData = imageData.data; 54 | const newData: any[] = []; 55 | 56 | for (let t = 0; t < 800; t++) { 57 | let i = 4 * t * 800; 58 | for (let n = 0; n < 800; n++) { 59 | let e = i + 4 * n; 60 | if ( 61 | pixelData[e] !== 0 && 62 | pixelData[e + 1] !== 0 && 63 | pixelData[e + 2] !== 0 64 | ) { 65 | newData.push({ 66 | x: n, 67 | y: t, 68 | color: [ 69 | pixelData[e], 70 | pixelData[e + 1], 71 | pixelData[e + 2], 72 | pixelData[e + 3], 73 | ], 74 | }); 75 | } 76 | } 77 | } 78 | 79 | newDataRef.current = newData.map(({ x, y, color }) => ({ 80 | x, 81 | y, 82 | r: 1, 83 | color: `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`, 84 | })); 85 | }, [value]); 86 | 87 | useEffect(() => { 88 | draw(); 89 | }, [value, draw]); 90 | 91 | const animate = (start: number) => { 92 | const animateFrame = (pos: number = 0) => { 93 | requestAnimationFrame(() => { 94 | const newArr = []; 95 | for (let i = 0; i < newDataRef.current.length; i++) { 96 | const current = newDataRef.current[i]; 97 | if (current.x < pos) { 98 | newArr.push(current); 99 | } else { 100 | if (current.r <= 0) { 101 | current.r = 0; 102 | continue; 103 | } 104 | current.x += Math.random() > 0.5 ? 1 : -1; 105 | current.y += Math.random() > 0.5 ? 1 : -1; 106 | current.r -= 0.05 * Math.random(); 107 | newArr.push(current); 108 | } 109 | } 110 | newDataRef.current = newArr; 111 | const ctx = canvasRef.current?.getContext("2d"); 112 | if (ctx) { 113 | ctx.clearRect(pos, 0, 800, 800); 114 | newDataRef.current.forEach((t) => { 115 | const { x: n, y: i, r: s, color: color } = t; 116 | if (n > pos) { 117 | ctx.beginPath(); 118 | ctx.rect(n, i, s, s); 119 | ctx.fillStyle = color; 120 | ctx.strokeStyle = color; 121 | ctx.stroke(); 122 | } 123 | }); 124 | } 125 | if (newDataRef.current.length > 0) { 126 | animateFrame(pos - 8); 127 | } else { 128 | setValue(""); 129 | setAnimating(false); 130 | } 131 | }); 132 | }; 133 | animateFrame(start); 134 | }; 135 | 136 | const handleKeyDown = (e: React.KeyboardEvent) => { 137 | if (e.key === "Enter" && !animating) { 138 | vanishAndSubmit(); 139 | } 140 | }; 141 | 142 | const vanishAndSubmit = () => { 143 | setAnimating(true); 144 | draw(); 145 | 146 | const value = textareaRef.current?.value || ""; 147 | if (value && textareaRef.current) { 148 | const maxX = newDataRef.current.reduce( 149 | (prev, current) => (current.x > prev ? current.x : prev), 150 | 0 151 | ); 152 | animate(maxX); 153 | } 154 | }; 155 | 156 | const handleSubmit = (e: React.FormEvent) => { 157 | e.preventDefault(); 158 | vanishAndSubmit(); 159 | onSubmit && onSubmit(e); 160 | }; 161 | return ( 162 |
169 | 176 |