diff --git a/package.json b/package.json index 3667467..9605439 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ }, "packageManager": "yarn@3.2.3", "devDependencies": { + "@types/canvas-confetti": "^1.6.4", "husky": "^8.0.1", "lint-staged": "^13.0.3" }, @@ -44,6 +45,7 @@ "node": ">=18.17.0" }, "dependencies": { - "axios": "^1.7.7" + "axios": "^1.7.7", + "canvas-confetti": "^1.9.3" } } diff --git a/packages/chrome-extension/contentScript.js b/packages/chrome-extension/contentScript.js index 36f3eec..891fe8c 100644 --- a/packages/chrome-extension/contentScript.js +++ b/packages/chrome-extension/contentScript.js @@ -22,9 +22,9 @@ function injectAiAuditButton() { const aiAuditButton = document.createElement('button'); aiAuditButton.textContent = '✨ AI Audit'; aiAuditButton.style.cssText = ` - background-color: #6A0DAD; + background-color: #334155; border: none; - color: white; + color: #F8FAFC; padding: 5px 10px; font-size: 12px; margin-left: 10px; diff --git a/packages/chrome-extension/styles.css b/packages/chrome-extension/styles.css index 3a2f4c2..a1468d7 100644 --- a/packages/chrome-extension/styles.css +++ b/packages/chrome-extension/styles.css @@ -1,7 +1,7 @@ body { width: 400px; - background-color: #1e1e1e; - color: #e0e0e0; + background-color: #111827; + color: #F8FAFC; font-family: Arial, sans-serif; margin: 0; padding: 0; @@ -115,7 +115,7 @@ body { } .link { - color: #4caf50; + color: #009C59; text-decoration: none; } @@ -138,23 +138,27 @@ body { } .ai-review.low-risk { - background-color: #e6ffe6; - border: 1px solid #4CAF50; + background-color: #DCFCE7; + border: 1px solid #009C59; + color: #1F2937; } .ai-review.moderate-risk { - background-color: #ffffcc; - border: 1px solid #FFD700; + background-color: #FEFCE8; + border: 1px solid #FFB310; + color: #1F2937; } .ai-review.high-risk { - background-color: #ffe6e6; - border: 1px solid #FF0000; + background-color: #FEE2E2; + border: 1px solid #B91C1C; + color: #1F2937; } .ai-review.neutral { - background-color: #f0f0f0; - border: 1px solid #808080; + background-color: #E2E8F0; + border: 1px solid #334155; + color: #1F2937; } .hidden { diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index bb2f06f..d2bbc86 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -1,22 +1,62 @@ "use client"; -import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { Address } from "viem"; import { useAccount } from "wagmi"; +import ReviewPage from "~~/components/inspectorAiComponents/ReviewPage"; +import { AddressInput } from "~~/components/scaffold-eth"; +import { RainbowKitCustomConnectButton } from "~~/components/scaffold-eth"; + +// Expected URL structure: /?address=0x1234...5678&chain=ethereum +// Example: http://localhost:3000/?address=0x1234567890123456789012345678901234567890&chain=ethereum export default function Home() { - const router = useRouter(); const { isConnected } = useAccount(); + const router = useRouter(); + const searchParams = useSearchParams(); + + const [inputAddress, setInputAddress] = useState
(); + + console.log("page: Home component rendered, isConnected:", isConnected); + + const address = searchParams.get("address"); + const chain = searchParams.get("chain") || "ethereum"; - if (!isConnected) { - router.push("/wallet-connection"); - } else { - router.push("/world-id-verification"); - } + const handleAddressSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (inputAddress) { + router.push(`/?address=${inputAddress}&chain=${chain}`); + } + }; return (
-

Welcome to PumpInspector

-

Loading...

+ {isConnected ? ( + address ? ( + + ) : ( +
+

Review a Contract

+
+ setInputAddress(value as Address)} + placeholder="Enter contract address to review" + /> + + +
+ ) + ) : ( +
+

Welcome to Inspector AI

+

Please connect your wallet to continue.

+ +
+ )}
); } diff --git a/packages/nextjs/components/Footer.tsx b/packages/nextjs/components/Footer.tsx index 92b3c62..8b4b938 100644 --- a/packages/nextjs/components/Footer.tsx +++ b/packages/nextjs/components/Footer.tsx @@ -1,26 +1,13 @@ import React from "react"; -import Link from "next/link"; -import { hardhat } from "viem/chains"; -import { CurrencyDollarIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; -import { HeartIcon } from "@heroicons/react/24/outline"; -import { SwitchTheme } from "~~/components/SwitchTheme"; -import { BuidlGuidlLogo } from "~~/components/assets/BuidlGuidlLogo"; -import { Faucet } from "~~/components/scaffold-eth"; -import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; -import { useGlobalState } from "~~/services/store/store"; /** * Site footer */ export const Footer = () => { - const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrency.price); - const { targetNetwork } = useTargetNetwork(); - const isLocalNetwork = targetNetwork.id === hardhat.id; - return (
-
+ {/*
{nativeCurrencyPrice > 0 && (
@@ -41,9 +28,9 @@ export const Footer = () => { )}
-
+
*/}
-
+ {/*
    @@ -74,7 +61,7 @@ export const Footer = () => {
-
+
*/}
); }; diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx index f24a1de..0e6df28 100644 --- a/packages/nextjs/components/Header.tsx +++ b/packages/nextjs/components/Header.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useRef, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; -import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline"; +import { Bars3Icon } from "@heroicons/react/24/outline"; import { FaucetButton, RainbowKitCustomConnectButton } from "~~/components/scaffold-eth"; import { useOutsideClick } from "~~/hooks/scaffold-eth"; @@ -19,11 +19,11 @@ export const menuLinks: HeaderMenuLink[] = [ label: "Home", href: "/", }, - { - label: "Debug Contracts", - href: "/debug", - icon: , - }, + // { + // label: "Debug Contracts", + // href: "/debug", + // icon: , + // }, ]; export const HeaderMenuLinks = () => { @@ -64,7 +64,7 @@ export const Header = () => { ); return ( -
+
-
- SE2 logo -
-
- Scaffold-ETH - Ethereum dev stack +
+ Inspector AI logo
    diff --git a/packages/nextjs/components/inspectorAiComponents/ReviewPage.tsx b/packages/nextjs/components/inspectorAiComponents/ReviewPage.tsx new file mode 100644 index 0000000..f12f8df --- /dev/null +++ b/packages/nextjs/components/inspectorAiComponents/ReviewPage.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { IDKitWidget, ISuccessResult, VerificationLevel } from "@worldcoin/idkit"; +import { useInspectorAI } from "~~/hooks/scaffold-eth/useInspectorAI"; + +interface ReviewPageProps { + address: string; + chain: string; +} + +export default function ReviewPage({ address, chain }: ReviewPageProps) { + console.log("ReviewPage: Rendering with props:", { address, chain }); + + const [isVerified, setIsVerified] = useState(false); + const [rating, setRating] = useState(0); + const [comment, setComment] = useState(""); + const { addReview, loadReviews, reviews, isAddingReview } = useInspectorAI(address); + + console.log("ReviewPage: Hook values:", { reviews, isAddingReview }); + + useEffect(() => { + console.log("ReviewPage: useEffect - Loading reviews"); + loadReviews(); + }, [loadReviews]); + + const handleVerification = () => { + console.log("ReviewPage: Verification successful"); + setIsVerified(true); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + console.log("ReviewPage: Submitting review", { rating, comment }); + await addReview(rating, comment); + setRating(0); + setComment(""); + }; + + const verifyProof = async (proof: ISuccessResult) => { + console.log("ReviewPage: Proof received:", proof); + handleVerification(); + }; + + console.log("ReviewPage: Current state", { isVerified, rating, comment, reviews }); + + return ( +
    +
    +
    +

    Review Contract

    +

    Chain: {chain}

    +

    Address: {address}

    +
    +
    +
    + {[1, 2, 3, 4, 5].map(star => ( + setRating(star)} + /> + ))} +
    +
    +
    + +
    +
    + + {({ open }) => ( + + )} + +
    +
    + +
    +
    +
    +
    + +
    +

    Reviews

    + {reviews.length > 0 ? ( +
      + {reviews.map((review, index) => ( +
    • +
      +
      + {[1, 2, 3, 4, 5].map(star => ( + + ))} +
      + by {review.reviewer} +
      +

      {review.comment}

      +
    • + ))} +
    + ) : ( +

    No reviews yet.

    + )} +
    +
    + ); +} diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 13d75b4..594e280 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; const deployedContracts = { 31337: { InspectorAI: { - address: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + address: "0x5FbDB2315678afecb367f032d93F642f64180aa3", abi: [ { anonymous: false, diff --git a/packages/nextjs/hooks/scaffold-eth/useInspectorAI.ts b/packages/nextjs/hooks/scaffold-eth/useInspectorAI.ts new file mode 100644 index 0000000..fa70ea1 --- /dev/null +++ b/packages/nextjs/hooks/scaffold-eth/useInspectorAI.ts @@ -0,0 +1,68 @@ +import { useCallback, useState } from "react"; +import { useScaffoldReadContract } from "./useScaffoldReadContract"; +import { useScaffoldWriteContract } from "./useScaffoldWriteContract"; +import confetti from "canvas-confetti"; +import { notification } from "~~/utils/scaffold-eth"; + +export const useInspectorAI = (contractAddress: string) => { + const [reviews, setReviews] = useState([]); + + console.log("[useInspectorAI] Initializing hook with contractAddress:", contractAddress); + + const { data: fetchedReviews, refetch: refetchReviews } = useScaffoldReadContract({ + contractName: "InspectorAI", + functionName: "getReviews", + args: [contractAddress], + }); + + console.log("[useInspectorAI] fetchedReviews:", fetchedReviews); + + const { writeContract: addReviewAsync, isMining: isAddingReview } = useScaffoldWriteContract("InspectorAI"); + + const addReview = useCallback( + async (rating: number, comment: string) => { + console.log("[useInspectorAI] addReview called with rating:", rating, "comment:", comment); + try { + console.log("[useInspectorAI] Calling addReview function"); + await addReviewAsync({ + functionName: "addReview", + args: [contractAddress, BigInt(rating) as any, comment], + }); + + console.log("[useInspectorAI] Transaction sent"); + + // The transaction is already confirmed by the time we reach here + console.log("[useInspectorAI] Transaction confirmed"); + + notification.success("Review added successfully!"); + confetti({ + particleCount: 100, + spread: 70, + origin: { y: 0.6 }, + }); + await refetchReviews(); + } catch (error) { + console.error("[useInspectorAI] Error adding review:", error); + notification.error("Failed to add review"); + } + }, + [addReviewAsync, contractAddress, refetchReviews], + ); + + const loadReviews = useCallback(async () => { + console.log("[useInspectorAI] loadReviews called"); + try { + await refetchReviews(); + if (fetchedReviews) { + console.log("[useInspectorAI] Fetched reviews:", fetchedReviews); + setReviews(fetchedReviews as any[]); + } + } catch (error) { + console.error("[useInspectorAI] Error loading reviews:", error); + notification.error("Failed to load reviews"); + } + }, [refetchReviews, fetchedReviews]); + + console.log("[useInspectorAI] Returning hook functions"); + return { addReview, loadReviews, reviews, isAddingReview }; +}; diff --git a/packages/nextjs/public/logo_with_name.png b/packages/nextjs/public/logo_with_name.png new file mode 100644 index 0000000..e05ba4e Binary files /dev/null and b/packages/nextjs/public/logo_with_name.png differ diff --git a/packages/nextjs/styles/globals.css b/packages/nextjs/styles/globals.css index 3fb55ae..dcfc1d3 100644 --- a/packages/nextjs/styles/globals.css +++ b/packages/nextjs/styles/globals.css @@ -1,32 +1,31 @@ -@import "tailwindcss/base"; -@import "tailwindcss/components"; -@import "tailwindcss/utilities"; +@tailwind base; +@tailwind components; +@tailwind utilities; -:root, -[data-theme] { - background: oklch(var(--b2)); +body { + @apply bg-background text-textDark; } -body { - min-height: 100vh; +.btn-primary { + @apply bg-primary text-textDark; +} + +.btn-warning { + @apply bg-warningDark text-textLight; } -h1, -h2, -h3, -h4 { - margin-bottom: 0.5rem; - line-height: 1; +.btn-danger { + @apply bg-dangerDark text-textLight; } -p { - margin: 1rem 0; +.btn-success { + @apply bg-successDark text-textLight; } -.btn { - @apply shadow-md; +.card { + @apply bg-card text-textLight; } -.btn.btn-ghost { - @apply shadow-none; +.tag { + @apply bg-tagBg text-textLight; } diff --git a/packages/nextjs/tailwind.config.js b/packages/nextjs/tailwind.config.js index 9099dc5..1a8cfa1 100644 --- a/packages/nextjs/tailwind.config.js +++ b/packages/nextjs/tailwind.config.js @@ -82,6 +82,20 @@ module.exports = { animation: { "pulse-fast": "pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite", }, + colors: { + primary: '#334155', + background: '#111827', + textDark: '#F8FAFC', + textLight: '#1F2937', + warningDark: '#FFB310', + warningLight: '#FEFCE8', + dangerDark: '#B91C1C', + dangerLight: '#FEE2E2', + successDark: '#009C59', + successLight: '#DCFCE7', + tagBg: '#F8FAFC', + card: '#E2E8F0', + }, }, }, };