Skip to content

Update#1

Open
cryptoxbr1 wants to merge 90 commits intodxbr:mainfrom
cryptoxbr1:main
Open

Update#1
cryptoxbr1 wants to merge 90 commits intodxbr:mainfrom
cryptoxbr1:main

Conversation

@cryptoxbr1
Copy link

@cryptoxbr1 cryptoxbr1 commented Sep 16, 2025

Summary by CodeRabbit

  • New Features

    • On-chain integration for feed: fetch, create, like/unlike posts; basic comments and follow/unfollow supported.
    • Automatic wallet network switch to Base Mainnet on connect.
    • Simplified “Connect Wallet” flow with preferred wallets and optional WalletConnect support via environment config.
    • Profiles now load and update from chain data (including avatar support).
  • Bug Fixes

    • More reliable post submission and refresh states, with improved error handling.
  • Style

    • Updated header icon and removed static network status block.
  • Chores

    • Added on-chain QA script and modernized Tailwind plugin loading.

cgen-547940b70bbb499793a6d6b939b68077
… RPC from env

cgen-4a3699e76f1d44f7bda96039bf5e5b31
cgen-ed50c588e75e47a6af12b5277ff9fba9
cgen-5830c760159840beac87ca90dc367b10
cgen-eae9fcb6237843d2998bbf6a78e30ccd
cgen-0ccec6807dcc452d99c4d7a88adf32eb
cgen-c46ecd2eb6774ec1b57478a633b06a1d
cgen-0ecb608db6bb4e2eae3df7005c6e1d70
cgen-4a3cadc167b44cf99ce35ef7183e4834
cgen-614c1ab4cae44d7d899373b8c5b56fff
cgen-5c79ee083b5346e1936d348af18db176
cgen-0bc8330773f445e29522cb00018f6273
cgen-81a73decf4224bf79a62fdc115040402
cgen-4c9ccba811c047d498523299564c8eb4
cgen-3b1dc6ad33b44a06aabd42af5b19ba71
cgen-cca5671bb647454887b39f4d688900cc
cgen-4a173e1b03554a6fbccff3cf2729bf52
cgen-527d683a04944755a51631a01f7d6af9
cgen-896fd5366ef04df4a47223c3766d4489
cgen-84845ed243854fc5a28b44cade1c2c6b
cgen-8992742fcd904ea5bf63b6841edf1ab9
cgen-644bc1443b2442d9bfac49c30ccc76bc
cgen-0ca2efd369d04ce5addb9e3cd8e76778
cgen-1b487b8ffff444a08632e948257ce901
cgen-864a89a2f4e4422b94c085f0db70048f
cgen-97c127b961e440db8d9f80fa0b380d7d
cgen-7c67a50ecb314d1599d53c5e4fe3c839
builderio-bot and others added 28 commits September 16, 2025 13:43
cgen-549faa7bfbfb4964a4f0157c58fb5f14
cgen-d50c1f87063b48bc90de3f2e746e26d3
cgen-41dce36669d74a06ad11ec6926b9fbb9
cgen-8acf2da6385b43db8aae9c7a4b9c6613
cgen-d60fbf3fc51841ecb53e5a6f6272df99
cgen-275f686a633843dea0d61de15091917e
cgen-6f113e649ba441df92a681dca766d3e3
cgen-f493927c9ebf43398420dfc584814323
cgen-cd4e46f177764fcc91ef767d90d4d81e
cgen-3241011bf94a40cd9f20cdc9e0a8c2f9
cgen-5f1dc6f7e2714ba4a3f205d6859f1b92
cgen-7b19c5ef19b044ee97d68a82e217db37
cgen-ab97154a621e4ef98c9af4048eaf2934
cgen-378e0f6982614a5fae8cdb6614808f6c
cgen-2d3fa22aad014da5aaefe1b4d0c0c202
cgen-7982503302c84dfdb5575d9680b89c23
cgen-57291a3ceae74861ab16c4b3ed0a999b
cgen-a7a5c4784b4340dbaa0ea0933a3174ba
cgen-6975825988614f139296a32e41acc444
cgen-fccfa6dab6814a58847443cd38013fb2
cgen-4c850f313b4648bf918224ee7846d5c5
cgen-68ae67fd2c814fad84e5b1b873563ff0
cgen-62c60808c389434da735160126575e11
cgen-825920b6e2504a538ab6160a0f02cb7e
Fix smart contract integration and remove dummy data
@coderabbitai
Copy link

coderabbitai bot commented Sep 16, 2025

Walkthrough

Introduces on-chain integrations and utilities: new hooks for posts, profiles, comments, and follow; updates ConnectWallet to auto-switch to Base and streamline connection; integrates feed with on-chain posts; replaces placeholder contract ABIs/addresses with env-driven JSON ABIs; adds a QA script; adjusts wagmi config, minor React imports, and Tailwind plugin loading.

Changes

Cohort / File(s) Summary
On-chain hooks (new)
src/hooks/usePosts.ts, src/hooks/useComments.ts, src/hooks/useFollow.ts, src/hooks/useProfile.ts
Added hooks to fetch/mutate on-chain data (posts, comments, follow, profiles). Implement wallet interactions, reads/writes, toasts, error handling, and mock fallbacks where applicable. Public types and method signatures introduced/updated.
Contracts and wagmi config
src/lib/contracts.ts, src/lib/wagmi.ts
Switched to env-driven contract addresses; replaced string ABIs with JSON ABIs (as const). Wagmi connectors now dynamic (MetaMask, Coinbase, optional WalletConnect); Base transport uses env RPC.
Feed integration
src/components/Feed.tsx
Wired Feed to usePosts hook: loads, creates, likes/unlikes on-chain posts with refetching and optimistic updates. Adjusted Post type (optional fields, timestamp union). Simplified mock data and added early returns for non-feed tabs.
Wallet connect & network switch
src/components/ConnectWallet.tsx
Reworked to single “Connect Wallet” CTA with preferred connector selection; auto-adds/switches network to Base Mainnet on connect; added toasts and error handling; removed static network info UI.
Create post flow
src/components/CreatePost.tsx
Refactored submission to handle sync/async onPost uniformly with try/finally, simulated delay for sync, and error logging.
UI type refinements
src/components/ui/command.tsx, src/components/ui/textarea.tsx
Converted interface prop types to type aliases; runtime unchanged.
Minor React import cleanups
src/components/Dashboard.tsx, src/components/PostCard.tsx, src/pages/Index.tsx, src/pages/NotFound.tsx
Added default React import to TSX files and minor formatting/newline fixes; no logic changes.
QA script
scripts/qa-onchain-check.js
New Node.js script to validate Posts contract data via ethers.js: reads env RPC/address, fetches IDs, samples first post, logs results, handles errors and exit codes.
Tailwind plugin
tailwind.config.ts
Switched tailwindcss-animate from require() to static import; duplicated type import retained; config output unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as ConnectWallet
  participant Wagmi as wagmi/connect
  participant Wallet as EIP-1193 Wallet
  participant Chain as Base RPC

  User->>App: Click "Connect Wallet"
  App->>Wagmi: connect({ preferred connector })
  Wagmi->>Wallet: request accounts
  Wallet-->>Wagmi: accounts, chainId
  Wagmi-->>App: isConnected, connector
  App->>Wallet: eth_chainId
  Wallet-->>App: current chainId
  alt Not Base
    App->>Wallet: wallet_addEthereumChain(Base params)
    Wallet-->>App: result/err
    App->>Wallet: wallet_switchEthereumChain(8453)
    Wallet-->>App: switched/err
    App-->>User: toast(success/error)
  else Already on Base
    App-->>User: toast(connected)
  end
  App->>Chain: subsequent dapp calls (via hooks)
  Chain-->>App: responses
Loading
sequenceDiagram
  autonumber
  participant Feed as Feed.tsx
  participant Posts as usePosts hook
  participant RPC as publicClient (Base)
  participant Wallet as writeContract

  Note over Feed,Posts: Initial load
  Feed->>Posts: usePosts()
  Posts->>RPC: readContract(getAllPosts) or fallback reads
  RPC-->>Posts: post IDs / tuples
  Posts->>Posts: normalize, sort, setState
  Posts-->>Feed: posts, isLoading=false

  Note over Feed,Posts: Create post
  Feed->>Posts: createPost(content)
  alt Not connected
    Posts->>Wallet: connect (preferred)
  end
  alt Contract configured
    Posts->>Wallet: writeContract(createPost)
    Wallet-->>Posts: tx hash / error
    Posts->>Posts: set isCreating, toasts
    Posts->>RPC: refetch after delay
    RPC-->>Posts: updated posts
    Posts-->>Feed: updated state
  else Mock mode
    Posts->>Posts: append local post, toast
    Posts-->>Feed: updated state
  end

  Note over Feed,Posts: Like/Unlike similar flow with writeContract and refetch/optimistic updates
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

Thump-thump! I hop through chains tonight,
ABIs bloom, JSON bright.
Switch to Base, a quick little leap—
posts and follows burrow deep.
Toasts pop up like clover green,
mock or mainnet—swift and clean.
Carrots signed; on-chain we preen. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The provided title "Update" is a single-word, generic label that does not summarize the primary changes in this large changeset; the PR introduces on-chain hooks and scripts, updates contract ABIs, and modifies wallet connection and UI behavior, none of which are indicated by the title. Because the title is vague and non-descriptive, it does not meet the guideline for a concise, meaningful summary of the main change. Under the check criteria this makes the title inconclusive. Please rename the PR to a concise, specific one-line summary that highlights the primary change (for example: "Integrate on‑chain posts/comments/follows, update ABIs, and add wallet auto‑switch"). If multiple major areas are modified, choose the dominant theme or use a scoped title like "feat(onchain): ...", and expand details in the PR description listing the key files/areas changed.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/PostCard.tsx (1)

16-28: Fix Post type drift and timestamp handling to avoid TS mismatch.

Feed passes timestamp: number | Date and username?; PostCard requires Date and username (required). This can break type-checking and formatting.

Apply:

 interface Post {
   id: string;
   author: string;
-  username: string;
+  username?: string;
   content: string;
-  timestamp: Date;
+  timestamp: number | Date;
   likes: number;
   comments: number;
   reposts: number;
-  isLiked: boolean;
+  isLiked?: boolean;
   avatarUrl?: string;
   txHash?: string;
 }
 
-const formatTimestamp = (date: Date) => {
-  const now = new Date();
-  const diffMs = now.getTime() - date.getTime();
+const formatTimestamp = (ts: number | Date) => {
+  const date = typeof ts === 'number' ? new Date(ts) : ts;
+  const now = new Date();
+  const diffMs = now.getTime() - date.getTime();
   const diffMins = Math.floor(diffMs / 60000);
   const diffHours = Math.floor(diffMins / 60);
   const diffDays = Math.floor(diffHours / 24);
 
   if (diffMins < 1) return "now";
   if (diffMins < 60) return `${diffMins}m`;
   if (diffHours < 24) return `${diffHours}h`;
   return `${diffDays}d`;
 };
 
-<div className="text-sm text-muted-foreground">@{post.username}</div>
+<div className="text-sm text-muted-foreground">@{post.username ?? post.author.slice(0, 8)}</div>

Also applies to: 47-58, 75-80

🧹 Nitpick comments (24)
src/components/Dashboard.tsx (1)

14-17: Prevent state/DOM drift in dark-mode toggle

Drive the DOM class from state via an effect (and avoid manual toggle).

-import React, { useState } from "react";
+import React, { useState, useEffect } from "react";
@@
-  const toggleDarkMode = () => {
-    setDarkMode(!darkMode);
-    document.documentElement.classList.toggle("dark");
-  };
+  const toggleDarkMode = () => {
+    setDarkMode((v) => !v);
+  };
+  useEffect(() => {
+    document.documentElement.classList.toggle("dark", darkMode);
+  }, [darkMode]);
src/pages/NotFound.tsx (1)

16-18: Use client-side navigation

Prefer Link to avoid full page reload.

-import { useLocation } from "react-router-dom";
+import { useLocation, Link } from "react-router-dom";
@@
-        <a href="/" className="text-blue-500 underline hover:text-blue-700">
+        <Link to="/" className="text-blue-500 underline hover:text-blue-700">
           Return to Home
-        </a>
+        </Link>
tailwind.config.ts (1)

3-3: Duplicate type import triggers noRedeclare

Remove the second import of Config.

-import type { Config } from "tailwindcss";
 import tailwindcssAnimate from "tailwindcss-animate";
src/lib/wagmi.ts (2)

9-28: Avoid double assertion; use a type guard for connectors

Replace as unknown as Connector[] with a proper predicate.

-const connectors = [
-  metaMask(),
-  coinbaseWallet({
-    appName: 'BaseLine',
-    appLogoUrl: '/baseline-logo.png',
-  }),
-  ...(walletConnectProjectId
-    ? [
-        walletConnect({
-          projectId: walletConnectProjectId,
-          metadata: {
-            name: 'BaseLine',
-            description: 'A Web3 microblogging platform on Base',
-            url: 'https://baseline.app',
-            icons: ['/baseline-logo.png'],
-          },
-        }),
-      ]
-    : []),
-].filter(Boolean) as unknown as Connector[];
+const connectors = [
+  metaMask(),
+  coinbaseWallet({
+    appName: 'BaseLine',
+    appLogoUrl: '/baseline-logo.png',
+  }),
+  walletConnectProjectId
+    ? walletConnect({
+        projectId: walletConnectProjectId,
+        metadata: {
+          name: 'BaseLine',
+          description: 'A Web3 microblogging platform on Base',
+          url: 'https://baseline.app',
+          icons: ['/baseline-logo.png'],
+        },
+      })
+    : undefined,
+].filter((c): c is Connector => Boolean(c));

20-24: WalletConnect icon: use absolute HTTPS URL

WalletConnect requires absolute HTTPS icon URLs — change icons: ['/baseline-logo.png'] to an absolute URL (e.g. 'https://baseline.app/baseline-logo.png') or another HTTPS-hosted URL. File: src/lib/wagmi.ts (lines 20–24). Asset located at src/assets/baseline-logo.png.

src/components/CreatePost.tsx (2)

16-18: Type onPost to allow sync or async

Reflect actual usage and drop nullable handling.

-interface CreatePostProps {
-  onPost: (content: string) => void;
-}
+interface CreatePostProps {
+  onPost: (content: string) => void | Promise<unknown>;
+}

34-53: Simplify handlePost; remove promise duck-typing and fake delay

Await both sync/async uniformly via Promise.resolve; no simulated delay.

   const handlePost = async () => {
     if (!content.trim() || isPosting) return;
 
     setIsPosting(true);
     try {
-      const res = onPost ? onPost(content) : null;
-      if (res && typeof (res as { then?: unknown }).then === 'function') {
-        await res as Promise<unknown>;
-      } else {
-        // fallback simulated tx
-        await new Promise(resolve => setTimeout(resolve, 1000));
-      }
+      await Promise.resolve(onPost(content));
       setContent("");
       setCharCount(0);
     } catch (err) {
       console.error('Post failed', err);
     } finally {
       setIsPosting(false);
     }
   };
scripts/qa-onchain-check.js (1)

12-13: Clarify env error message to include both supported variables.

Message references only VITE_BASELINE_POSTS though you also check BASELINE_POSTS.

-    console.error('No Posts contract address provided in env VITE_BASELINE_POSTS');
+    console.error('No Posts contract address provided (set VITE_BASELINE_POSTS or BASELINE_POSTS)');
src/lib/contracts.ts (2)

46-48: Event param name typo: "followingEvent" should be "following".

Names don’t affect the event signature but do affect consumer ergonomics and generated typings.

-  { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "follower", "type": "address" }, { "indexed": true, "internalType": "address", "name": "followingEvent", "type": "address" } ], "name": "Unfollowed", "type": "event" },
+  { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "follower", "type": "address" }, { "indexed": true, "internalType": "address", "name": "following", "type": "address" } ], "name": "Unfollowed", "type": "event" },

1-7: Consider tightening address types to 0x${string} and validating env at startup.

This improves DX and catches misconfig early.

-export const CONTRACT_ADDRESSES = {
-  Profiles: import.meta.env.VITE_BASELINE_PROFILES as string | undefined,
-  Posts: import.meta.env.VITE_BASELINE_POSTS as string | undefined,
-  Comments: import.meta.env.VITE_BASELINE_COMMENTS as string | undefined,
-  Follow: import.meta.env.VITE_BASELINE_FOLLOW as string | undefined,
-} as const;
+export const CONTRACT_ADDRESSES = {
+  Profiles: import.meta.env.VITE_BASELINE_PROFILES as `0x${string}` | undefined,
+  Posts: import.meta.env.VITE_BASELINE_POSTS as `0x${string}` | undefined,
+  Comments: import.meta.env.VITE_BASELINE_COMMENTS as `0x${string}` | undefined,
+  Follow: import.meta.env.VITE_BASELINE_FOLLOW as `0x${string}` | undefined,
+} as const;
src/hooks/useComments.ts (3)

44-70: Batch reads with multicall to cut RPC round‑trips.

Reading each comment individually is N+1 and slow. Use publicClient.multicall.

-      const items = await Promise.all(
-        (ids as unknown[]).map(async (id: unknown) => {
-          const raw: unknown = await publicClient.readContract({
-            address: commentsAddr as `0x${string}`,
-            abi: COMMENTS_ABI as unknown as import('abitype').Abi,
-            functionName: 'comments',
-            args: [id as unknown],
-          });
-          const arr = raw as unknown as [
-            bigint | number | string,
-            bigint | number | string,
-            string,
-            string,
-            bigint | number
-          ];
-          return {
-            id: arr[0]?.toString(),
-            postId: arr[1]?.toString(),
-            author: arr[2],
-            content: arr[3],
-            timestamp: Number(arr[4] ?? Date.now()),
-            likes: 0,
-          };
-        })
-      );
+      const calls = (ids as readonly unknown[]).map((id) => ({
+        address: commentsAddr as `0x${string}`,
+        abi: COMMENTS_ABI as unknown as import('abitype').Abi,
+        functionName: 'comments' as const,
+        args: [id as unknown],
+      }));
+      const results = await publicClient.multicall({ contracts: calls });
+      const items = results.map((r) => {
+        const arr = r.result as unknown as [bigint, bigint, string, string, bigint];
+        return {
+          id: arr[0]?.toString(),
+          postId: arr[1]?.toString(),
+          author: arr[2],
+          content: arr[3],
+          timestamp: Number(arr[4] ?? 0), // seconds
+          likes: 0,
+        };
+      });

31-43: Type the read result to avoid unknown and brittle guards.

Let viem infer readonly bigint[] to skip Array.isArray checks.

-      const ids: unknown = await publicClient.readContract({
+      const ids = await publicClient.readContract({
         address: commentsAddr as `0x${string}`,
         abi: COMMENTS_ABI as unknown as import('abitype').Abi,
         functionName: 'getCommentsForPost',
         args: [BigInt(postId)],
       });
-
-      if (!Array.isArray(ids)) return [];

10-15: Validate numeric postId before BigInt coercion.

Prevent runtime throws on malformed ids.

-  const addComment = useCallback(async (postId: string, content: string) => {
+  const addComment = useCallback(async (postId: string, content: string) => {
+    if (!/^\d+$/.test(postId)) { toast.error('Invalid post id'); return; }
src/hooks/useFollow.ts (2)

1-2: Duplicate import of useCallback.

Remove the second import to fix the linter error.

-import { useCallback } from 'react';
-import { useCallback } from 'react';
+import { useCallback } from 'react';

12-31: Type cast target addresses to 0x${string} to satisfy ABI and catch bad input early.

Improves type-safety on writes.

-        args: [target],
+        args: [target as `0x${string}`],

Apply similarly in unfollowUser/get* calls.

src/hooks/useProfile.ts (2)

36-45: Avoid precision loss for avatarTokenId; keep bigint in state.

On-chain ids can exceed Number.MAX_SAFE_INTEGER.

-    avatarTokenId: number;
+    avatarTokenId: bigint;
@@
-        avatarTokenId: Number(tup[4] ?? 0),
+        avatarTokenId: BigInt(tup[4] ?? 0n),

And update references accordingly.


86-87: hasProfile should reflect on-chain existence, not just presence of an address.

Use the contract’s exists flag.

-    hasProfile: Boolean(targetAddress),
+    hasProfile: Boolean(profile?.exists),
src/components/PostCard.tsx (2)

81-90: Make the explorer button actually open the tx.

Currently it renders a button with no action/href.

-{post.txHash && (
-  <Button
-    variant="ghost"
-    size="sm"
-    className="ml-auto h-6 w-6 p-0 hover:bg-accent"
-    title="View on Base Scanner"
-  >
-    <ExternalLink className="w-3 h-3" />
-  </Button>
-)}
+{post.txHash && (
+  <Button
+    variant="ghost"
+    size="sm"
+    className="ml-auto h-6 w-6 p-0 hover:bg-accent"
+    asChild
+  >
+    <a
+      href={`https://base.blockscout.com/tx/${post.txHash}`}
+      target="_blank"
+      rel="noopener noreferrer"
+      title="View on Base Explorer"
+    >
+      <ExternalLink className="w-3 h-3" />
+    </a>
+  </Button>
+)}

128-145: Optional: keep the “liking” spinner in sync with actual like/unlike async.

handleLike stops the spinner before the parent async finishes; also onLike can be Promise<void>. Consider awaiting it.

-const handleLike = async () => {
+const handleLike = async () => {
   setIsLiking(true);
-  await new Promise(resolve => setTimeout(resolve, 500)); // Simulate blockchain tx
-  onLike(post.id);
-  setIsLiking(false);
+  try {
+    await onLike(post.id);
+  } finally {
+    setIsLiking(false);
+  }
 };

Also applies to: 40-45

src/components/Feed.tsx (2)

10-22: Converge on a single Post type to prevent drift.

You duplicate interface Post here while src/hooks/usePosts.ts exports one. Prefer importing the shared type and only widening the timestamp locally if needed.

-import React, { useEffect, useState } from "react";
+import React, { useEffect, useState } from "react";
+import type { Post as ChainPost } from "@/hooks/usePosts";
 
-interface Post {
-  id: string;
-  author: string;
-  username?: string;
-  content: string;
-  timestamp: number | Date;
-  likes: number;
-  comments: number;
-  reposts: number;
-  isLiked?: boolean;
-  avatarUrl?: string;
-  txHash?: string;
-}
+type Post = Omit<ChainPost, "timestamp"> & { timestamp: number | Date };

Also applies to: 24-37, 43-53


69-70: Nit: avoid deprecated substr.

substr is deprecated; use slice.

-        txHash: `0x${Math.random().toString(16).substr(2, 8)}...`
+        txHash: `0x${Math.random().toString(16).slice(2, 10)}...`
src/components/ConnectWallet.tsx (2)

40-55: Prefer switch-first, add-on-4902 flow for chain handling.

Some wallets error if you always call wallet_addEthereumChain. Try switch; on error 4902, then add and switch.

-        await ethereum.request({
-          method: 'wallet_addEthereumChain',
-          params: [{
-            chainId: '0x' + base.id.toString(16),
-            chainName: 'Base Mainnet',
-            rpcUrls: [baseRpc],
-            nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
-            blockExplorerUrls: ['https://base.blockscout.com/'],
-          }],
-        });
-
-        await ethereum.request({
-          method: 'wallet_switchEthereumChain',
-          params: [{ chainId: '0x' + base.id.toString(16) }],
-        });
+        try {
+          await ethereum.request({
+            method: 'wallet_switchEthereumChain',
+            params: [{ chainId: '0x' + base.id.toString(16) }],
+          });
+        } catch (e: any) {
+          if (e?.code === 4902) {
+            await ethereum.request({
+              method: 'wallet_addEthereumChain',
+              params: [{
+                chainId: '0x' + base.id.toString(16),
+                chainName: 'Base Mainnet',
+                rpcUrls: [baseRpc],
+                nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
+                blockExplorerUrls: ['https://base.blockscout.com/'],
+              }],
+            });
+            await ethereum.request({
+              method: 'wallet_switchEthereumChain',
+              params: [{ chainId: '0x' + base.id.toString(16) }],
+            });
+          } else {
+            throw e;
+          }
+        }

66-67: Nit: remove unused wallets.

Dead variable.

-  const wallets = [];
+  // no-op
src/hooks/usePosts.ts (1)

275-303: Gate like/unlike on connection or provide a connect prompt.

Writes will fail when not connected; mirror the connect flow used in createPost or short-circuit to mock with a toast.

-  const likePost = useCallback(async (postId: string) => {
+  const likePost = useCallback(async (postId: string) => {
+    if (!connected && !useMock) {
+      toast.error('Connect your wallet to like posts');
+      return;
+    }
-  const unlikePost = useCallback(async (postId: string) => {
+  const unlikePost = useCallback(async (postId: string) => {
+    if (!connected && !useMock) {
+      toast.error('Connect your wallet to unlike posts');
+      return;
+    }

Also applies to: 304-331

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24198ab and 5cba610.

⛔ Files ignored due to path filters (1)
  • bun.lockb is excluded by !**/bun.lockb
📒 Files selected for processing (17)
  • scripts/qa-onchain-check.js (1 hunks)
  • src/components/ConnectWallet.tsx (5 hunks)
  • src/components/CreatePost.tsx (3 hunks)
  • src/components/Dashboard.tsx (2 hunks)
  • src/components/Feed.tsx (5 hunks)
  • src/components/PostCard.tsx (2 hunks)
  • src/components/ui/command.tsx (1 hunks)
  • src/components/ui/textarea.tsx (1 hunks)
  • src/hooks/useComments.ts (1 hunks)
  • src/hooks/useFollow.ts (1 hunks)
  • src/hooks/usePosts.ts (1 hunks)
  • src/hooks/useProfile.ts (1 hunks)
  • src/lib/contracts.ts (1 hunks)
  • src/lib/wagmi.ts (1 hunks)
  • src/pages/Index.tsx (1 hunks)
  • src/pages/NotFound.tsx (1 hunks)
  • tailwind.config.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
src/hooks/usePosts.ts (2)
scripts/qa-onchain-check.js (3)
  • postsAddr (6-6)
  • ids (26-26)
  • post (31-31)
src/lib/contracts.ts (1)
  • CONTRACT_ADDRESSES (2-7)
src/hooks/useComments.ts (1)
src/lib/contracts.ts (2)
  • CONTRACT_ADDRESSES (2-7)
  • COMMENTS_ABI (37-43)
src/hooks/useFollow.ts (1)
src/lib/contracts.ts (2)
  • CONTRACT_ADDRESSES (2-7)
  • FOLLOW_ABI (45-54)
src/components/Feed.tsx (1)
src/hooks/usePosts.ts (2)
  • Post (7-19)
  • usePosts (42-345)
src/hooks/useProfile.ts (1)
src/lib/contracts.ts (2)
  • CONTRACT_ADDRESSES (2-7)
  • PROFILES_ABI (31-35)
🪛 Biome (2.1.2)
src/hooks/useFollow.ts

[error] 2-2: Shouldn't redeclare 'useCallback'. Consider to delete it or rename it.

'useCallback' is defined here:

(lint/suspicious/noRedeclare)

tailwind.config.ts

[error] 3-3: Shouldn't redeclare 'Config'. Consider to delete it or rename it.

'Config' is defined here:

(lint/suspicious/noRedeclare)

🔇 Additional comments (10)
src/components/Dashboard.tsx (1)

1-1: React default import: OK

Consistent with the PR; no issues.

src/pages/NotFound.tsx (1)

2-2: React default import: OK

Aligns with the project-wide import pattern.

tailwind.config.ts (1)

120-121: ESM plugin import: OK

Static import of tailwindcss-animate is correct.

src/components/ui/textarea.tsx (1)

5-5: Type alias migration: OK

No runtime impact; keeps public typing clear.

src/lib/wagmi.ts (1)

6-8: Env-driven RPC and project ID: OK

Sane defaults; no issues.

src/components/CreatePost.tsx (1)

1-1: React default import: OK

Matches the project pattern; fine.

src/components/ui/command.tsx (1)

24-24: No export required — CommandDialogProps is internal

Repository search shows CommandDialogProps is defined and used only in src/components/ui/command.tsx (lines 24, 26). No external usages found; no export needed.

scripts/qa-onchain-check.js (1)

2-2: Ensure this script runs in Node (ESM vs CJS).

This file uses ESM import syntax with a shebang. It will fail unless package.json has "type": "module" (or the file is .mjs). Otherwise, switch to require().

Would you like a CJS-compatible variant?

src/hooks/useComments.ts (1)

66-66: Confirm timestamp units.

Contracts typically return seconds; if UI expects ms, multiply by 1000.

src/pages/Index.tsx (1)

1-1: LGTM on React import.

Consistent with the PR-wide pattern; no issues.

Comment on lines +18 to +21
const POSTS_ABI = [
'function getAllPosts() view returns (uint256[])',
'function getPost(uint256) view returns (uint256 id, address author, string content, uint256 timestamp, uint256 likesCount, uint256 commentsCount, bool exists)'
];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

ABI mismatch with deployed contract schema (getPost shape) and missing getAllPosts definition.

Your local ABI expects getPost to return 7 fields (incl. likes/comments/existence), but src/lib/contracts.ts defines getPost as (Post tuple, uint256) and does not expose commentsCount/exists. Also, POSTS_ABI there does not include getAllPosts, yet the script calls it. This will revert at runtime. Align the ABI and stop calling non-existent functions.

Apply this minimal ABI fix (align to tuple + likeCount) and switch to events-based discovery (PostCreated) rather than getAllPosts:

-  const POSTS_ABI = [
-    'function getAllPosts() view returns (uint256[])',
-    'function getPost(uint256) view returns (uint256 id, address author, string content, uint256 timestamp, uint256 likesCount, uint256 commentsCount, bool exists)'
-  ];
+  const POSTS_ABI = [
+    'event PostCreated(uint256 indexed id, address indexed author, string content, uint256 timestamp)',
+    'function getPost(uint256) view returns ((uint256 id, address author, string content, uint256 timestamp), uint256)',
+    'function likeCounts(uint256) view returns (uint256)'
+  ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const POSTS_ABI = [
'function getAllPosts() view returns (uint256[])',
'function getPost(uint256) view returns (uint256 id, address author, string content, uint256 timestamp, uint256 likesCount, uint256 commentsCount, bool exists)'
];
const POSTS_ABI = [
'event PostCreated(uint256 indexed id, address indexed author, string content, uint256 timestamp)',
'function getPost(uint256) view returns ((uint256 id, address author, string content, uint256 timestamp), uint256)',
'function likeCounts(uint256) view returns (uint256)'
];
🤖 Prompt for AI Agents
In scripts/qa-onchain-check.js around lines 18 to 21, the hardcoded POSTS_ABI is
out of sync with the deployed contract: it declares getPost returning 7 fields
and includes getAllPosts (which the deployed contract/ src/lib/contracts.ts does
not expose). Replace the ABI entries so getPost matches the contract tuple
signature used in src/lib/contracts.ts (Post tuple plus uint256 likeCount) —
remove commentsCount and exists — and remove/stop calling getAllPosts; instead
implement discovery by filtering the PostCreated event from the contract
(queryFilter or provider.getLogs) to enumerate posts and then call getPost for
each returned id. Ensure the ABI only contains the actual functions/events used
(getPost and the PostCreated event) to avoid runtime reverts.

Comment on lines +23 to +41
try {
const contract = new Contract(postsAddr, POSTS_ABI, provider);
console.log('Calling getAllPosts...');
const ids = await contract.getAllPosts();
console.log('Received ids length:', ids.length);
if (ids.length > 0) {
const first = ids[0];
console.log('Fetching first post id:', first.toString());
const post = await contract.getPost(first);
console.log('Post:', {
id: post.id.toString(),
author: post.author,
content: (post.content || '').slice(0, 120),
timestamp: post.timestamp.toString(),
likes: (post.likesCount?.toString?.() || post[4]?.toString?.())
});
} else {
console.log('No posts found on-chain (getAllPosts returned empty array).');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace getAllPosts call with event scan to enumerate post ids.

Since the ABI doesn’t include getAllPosts, query PostCreated logs to grab recent post IDs and then read a post via getPost.

-    const contract = new Contract(postsAddr, POSTS_ABI, provider);
-    console.log('Calling getAllPosts...');
-    const ids = await contract.getAllPosts();
-    console.log('Received ids length:', ids.length);
-    if (ids.length > 0) {
-      const first = ids[0];
-      console.log('Fetching first post id:', first.toString());
-      const post = await contract.getPost(first);
-      console.log('Post:', {
-        id: post.id.toString(),
-        author: post.author,
-        content: (post.content || '').slice(0, 120),
-        timestamp: post.timestamp.toString(),
-        likes: (post.likesCount?.toString?.() || post[4]?.toString?.())
-      });
-    } else {
-      console.log('No posts found on-chain (getAllPosts returned empty array).');
-    }
+    const contract = new Contract(postsAddr, POSTS_ABI, provider);
+    console.log('Scanning PostCreated logs...');
+    const topic0 = (await import('ethers')).id('PostCreated(uint256,address,string,uint256)');
+    const logs = await provider.getLogs({ address: postsAddr, topics: [topic0], fromBlock: 0, toBlock: 'latest' });
+    console.log('Found PostCreated logs:', logs.length);
+    if (logs.length > 0) {
+      const last = logs[logs.length - 1];
+      const idHex = last.topics[1]; // indexed id
+      const postId = BigInt(idHex);
+      console.log('Fetching latest post id:', postId.toString());
+      const [post, likeCount] = await contract.getPost(postId);
+      console.log('Post:', {
+        id: post.id.toString(),
+        author: post.author,
+        content: (post.content || '').slice(0, 120),
+        timestamp: post.timestamp.toString(),
+        likes: likeCount?.toString?.()
+      });
+    } else {
+      console.log('No posts found on-chain (no PostCreated logs).');
+    }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In scripts/qa-onchain-check.js around lines 23 to 41, the code calls a
non-existent getAllPosts ABI method; replace that with a log scan for
PostCreated events and decode those logs to enumerate post IDs. Use the provider
to getLogs with a filter for the posts contract address and the PostCreated
event signature (via the POSTS_ABI) for a recent block range, decode each log
with an ethers Interface to extract the post ID list, then if any IDs exist call
contract.getPost(firstId) as before; ensure you handle the case of no logs found
and surface clear console messages and any decode errors.

Comment on lines 15 to +17
const { connectors, connect } = useConnect();
const { isConnected } = useAccount();
const baseRpc = (import.meta.env.VITE_BASE_RPC as string | undefined) ?? 'https://mainnet.base.org';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Await connection properly; use connectAsync for error handling.

connect isn’t awaited; errors won’t be caught. Use connectAsync and await it.

-  const { connectors, connect } = useConnect();
+  const { connectors, connectAsync } = useConnect();
-                  try {
-                    connect({ connector: target });
-                  } catch (err) {
+                  try {
+                    await connectAsync({ connector: target });
+                  } catch (err) {
                     console.error('Connect failed', err);
                     try { (await import('sonner')).toast.error('Failed to connect wallet'); } catch { console.warn('No sonner available'); }
                   }

Also applies to: 110-132

🤖 Prompt for AI Agents
In src/components/ConnectWallet.tsx around lines 15-17 (and also apply same
change to lines 110-132), the component uses the non-async connect function so
connection attempts aren’t awaited and thrown errors can’t be handled; replace
usages of connect with connectAsync and await the call inside an async handler,
wrap the await in a try/catch to handle and log/display errors, and ensure any
subsequent logic that depends on the connection awaits the connectAsync result
before proceeding.

Comment on lines +69 to +72
txHash: `0x${Math.random().toString(16).substr(2, 8)}...`
};
setPosts([newPost, ...posts]);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use functional state updates to avoid stale closures after awaits.

Multiple setPosts(posts => ...) sites currently reference stale posts, risking lost updates.

-      setPosts([newPost, ...posts]);
+      setPosts((prev) => [newPost, ...prev]);
-    setPosts(posts.map(post =>
+    setPosts((prev) => prev.map(post =>
       post.id === postId
         ? {
             ...post,
             isLiked: !post.isLiked,
             likes: post.isLiked ? post.likes - 1 : post.likes + 1
           }
         : post
     ));
-  setPosts(posts.map(post =>
+  setPosts((prev) => prev.map(post =>
     post.id === postId
       ? { ...post, reposts: post.reposts + 1 }
       : post
   ));

Also applies to: 86-96, 102-107, 54-58

🤖 Prompt for AI Agents
In src/components/Feed.tsx around lines 69-72 (and similarly at 86-96, 102-107,
and 54-58), the setPosts calls capture the outer posts variable and can lose
updates after awaits; change all setPosts usages to the functional updater
pattern (e.g., setPosts(prev => [newPost, ...prev]) or setPosts(prev =>
prev.map(...)) or setPosts(prev => prev.filter(...))) so updates use the latest
state and avoid stale closures.

Comment on lines +54 to +55
const { connectors, connect } = useConnect();

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t return early after connecting; proceed to create the post. Also await connectAsync.

Currently createPost connects then returns, forcing users to retry. Use connectAsync and continue execution.

-  const { connectors, connect } = useConnect();
+  const { connectors, connectAsync } = useConnect();
   const createPost = useCallback(async (content: string) => {
     // If user not connected, trigger wallet popup
     if (!connected) {
       try {
         const preferred = ["MetaMask","Coinbase Wallet"];
         const findPreferred = connectors.find((c) => preferred.some((p) => c.name.toLowerCase().includes(p.toLowerCase())));
         const fallback = connectors.find((c) => c.ready) || connectors[0];
         const target = findPreferred || fallback;
-        if (target && connect) {
-          await connect({ connector: target });
-          try { toast('Please approve the connection in your wallet'); } catch { /* ignore */ }
-          return;
-        }
+        if (target) {
+          await connectAsync({ connector: target });
+          try { toast.success('Wallet connected'); } catch { /* ignore */ }
+        } else {
+          try { toast.error('No wallet connectors available'); } catch { /* ignore */ }
+          return;
+        }
       } catch (err) {
         console.error('connect trigger failed', err);
       }
 
       try { toast('Please connect your wallet to post'); } catch { /* ignore */ }
-      return;
+      // continue; connected state should now be set, or write will fail and be handled below
     }

Also applies to: 201-220

🤖 Prompt for AI Agents
In src/hooks/usePosts.ts around lines 54-55 (and similarly at 201-220), the
createPost flow currently calls connect and returns immediately which prevents
post creation; replace that with awaiting connectAsync on the connector(s) and
then continue execution to create the post (do not early return). Specifically,
call await connectAsync(...) on the appropriate connector instance(s), handle
any errors, and then proceed to run the post-creation logic so users don't have
to retry; remove the early return and ensure proper async/await flow.

Comment on lines +22 to 23
const { isLoading: isUpdating } = useWaitForTransactionReceipt({ hash: undefined });

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

useWaitForTransactionReceipt wired with undefined hash.

This never tracks your tx. Either capture the hash from writeContract and wait explicitly, or plumb the hash into the hook.

-  const { isLoading: isUpdating } = useWaitForTransactionReceipt({ hash: undefined });
+  // Option A: drop the hook and wait programmatically after writes
+  // const { isLoading: isUpdating } = { isLoading: false as const };

And in handleUpdateProfile:

-        await writeUpdateProfileWithAvatar({
+        const hash = await writeUpdateProfileWithAvatar({
           address: profilesAddr as `0x${string}`,
           abi: PROFILES_ABI as unknown as import('abitype').Abi,
           functionName: 'updateProfile',
           args: [username, bio, avatarContract, BigInt(avatarTokenId ?? 0)],
         });
+        await publicClient.waitForTransactionReceipt({ hash });
@@
-        await writeUpdateProfile({
+        const hash = await writeUpdateProfile({
           address: profilesAddr as `0x${string}`,
           abi: PROFILES_ABI as unknown as import('abitype').Abi,
           functionName: 'updateProfile',
           args: [username, bio, '0x0000000000000000000000000000000000000000', BigInt(0)],
         });
+        await publicClient.waitForTransactionReceipt({ hash });
@@
-      toast.success('Profile update submitted');
-      // refetch
-      setTimeout(() => fetchProfile(), 1200);
+      toast.success('Profile updated');
+      await fetchProfile();

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/hooks/useProfile.ts around lines 22-23, useWaitForTransactionReceipt is
being called with a hardcoded undefined hash so it never tracks the transaction;
update the flow to capture the transaction hash returned by writeContract and
pass that hash into useWaitForTransactionReceipt (or alternatively call
waitForTransactionReceipt directly inside handleUpdateProfile with the returned
hash). Concretely: change the hook invocation to accept a reactive/hash variable
that gets set when writeContract resolves, and in handleUpdateProfile assign the
returned tx.hash to that variable (or await the tx receipt there) so the receipt
hook actually monitors the real transaction.

Comment on lines +24 to 35
const fetchProfile = useCallback(async () => {
const profilesAddr = CONTRACT_ADDRESSES.Profiles;
if (!profilesAddr || !targetAddress) return;

const handleCreateProfile = (username: string, bio: string) => {
if (!targetAddress) return;

// Mock implementation for now
toast.success(`Profile created for ${username}`);

// TODO: Uncomment when contracts are deployed
/*
try {
createProfile({
address: CONTRACT_ADDRESSES.Profiles as `0x${string}`,
abi: PROFILES_ABI,
functionName: 'createProfile',
args: [username, bio],
const res: unknown = await (await import('wagmi')).publicClient.readContract({
address: profilesAddr as `0x${string}`,
abi: PROFILES_ABI as unknown as import('abitype').Abi,
functionName: 'getProfile',
args: [targetAddress as `0x${string}`],
});
} catch (error) {
toast.error('Failed to create profile');
console.error(error);
}
*/
};

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Broken read path: using (await import('wagmi')).publicClient is invalid.

use wagmi’s usePublicClient() like other hooks; current code will throw at runtime.

-import { useAccount, useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
+import { useAccount, useWriteContract, useWaitForTransactionReceipt, usePublicClient } from 'wagmi';
@@
-  const fetchProfile = useCallback(async () => {
+  const publicClient = usePublicClient();
+  const fetchProfile = useCallback(async () => {
@@
-      const res: unknown = await (await import('wagmi')).publicClient.readContract({
+      const res = await publicClient.readContract({
         address: profilesAddr as `0x${string}`,
         abi: PROFILES_ABI as unknown as import('abitype').Abi,
         functionName: 'getProfile',
         args: [targetAddress as `0x${string}`],
       });
@@
-  }, [targetAddress]);
+  }, [publicClient, targetAddress]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fetchProfile = useCallback(async () => {
const profilesAddr = CONTRACT_ADDRESSES.Profiles;
if (!profilesAddr || !targetAddress) return;
const handleCreateProfile = (username: string, bio: string) => {
if (!targetAddress) return;
// Mock implementation for now
toast.success(`Profile created for ${username}`);
// TODO: Uncomment when contracts are deployed
/*
try {
createProfile({
address: CONTRACT_ADDRESSES.Profiles as `0x${string}`,
abi: PROFILES_ABI,
functionName: 'createProfile',
args: [username, bio],
const res: unknown = await (await import('wagmi')).publicClient.readContract({
address: profilesAddr as `0x${string}`,
abi: PROFILES_ABI as unknown as import('abitype').Abi,
functionName: 'getProfile',
args: [targetAddress as `0x${string}`],
});
} catch (error) {
toast.error('Failed to create profile');
console.error(error);
}
*/
};
const publicClient = usePublicClient();
const fetchProfile = useCallback(async () => {
const profilesAddr = CONTRACT_ADDRESSES.Profiles;
if (!profilesAddr || !targetAddress) return;
try {
const res = await publicClient.readContract({
address: profilesAddr as `0x${string}`,
abi: PROFILES_ABI as unknown as import('abitype').Abi,
functionName: 'getProfile',
args: [targetAddress as `0x${string}`],
});
🤖 Prompt for AI Agents
In src/hooks/useProfile.ts around lines 24-35, the code incorrectly attempts to
access (await import('wagmi')).publicClient.readContract which will throw at
runtime; replace this dynamic import usage with wagmi's usePublicClient hook
(call usePublicClient() at the top of the hook/component), then call
publicClient.readContract(...) instead, keeping the same
address/abi/functionName/args; ensure usePublicClient is imported from 'wagmi'
and add publicClient and any other needed values to the useCallback dependency
array.

Comment on lines +56 to +75
const handleUpdateProfile = useCallback(async (username: string, bio: string, avatarContract?: string, avatarTokenId?: number) => {
const profilesAddr = CONTRACT_ADDRESSES.Profiles;
if (!profilesAddr || !targetAddress) return;

const handleSetAvatar = (nftContract: string, tokenId: string) => {
if (!targetAddress) return;

// Mock implementation for now
toast.success('Avatar updated');

// TODO: Uncomment when contracts are deployed
/*
try {
setAvatar({
address: CONTRACT_ADDRESSES.Profiles as `0x${string}`,
abi: PROFILES_ABI,
functionName: 'setAvatar',
args: [nftContract as `0x${string}`, BigInt(tokenId)],
});
} catch (error) {
toast.error('Failed to set avatar');
console.error(error);
if (avatarContract) {
await writeUpdateProfileWithAvatar({
address: profilesAddr as `0x${string}`,
abi: PROFILES_ABI as unknown as import('abitype').Abi,
functionName: 'updateProfile',
args: [username, bio, avatarContract, BigInt(avatarTokenId ?? 0)],
});
} else {
await writeUpdateProfile({
address: profilesAddr as `0x${string}`,
abi: PROFILES_ABI as unknown as import('abitype').Abi,
functionName: 'updateProfile',
args: [username, bio, '0x0000000000000000000000000000000000000000', BigInt(0)],
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against wiping username/bio when setting avatar.

setAvatar currently passes empty strings if profile isn’t loaded, potentially overwriting on-chain data.

-    setAvatar: async (nftContract: string, tokenId: string) => {
-      await handleUpdateProfile(profile?.username ?? '', profile?.bio ?? '', nftContract, Number(tokenId));
-    },
+    setAvatar: async (nftContract: string, tokenId: string) => {
+      if (!profile) { toast.error('Load profile before setting avatar'); return; }
+      await handleUpdateProfile(profile.username, profile.bio, nftContract, Number(tokenId));
+    },

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/hooks/useProfile.ts around lines 56 to 75, the handler can call
updateProfile with empty username/bio (wiping on-chain data) when the in-memory
profile isn't loaded; change the logic to first ensure the current profile is
present (or return early) and use the existing profile.username and profile.bio
when avatarContract is provided, only falling back to allowed defaults if you
explicitly intend to clear fields; validate avatarTokenId before converting to
BigInt and pass the preserved username/bio into
writeUpdateProfileWithAvatar/writeUpdateProfile so the avatar update doesn't
overwrite other fields.

Comment on lines +22 to 29
{ "anonymous": false, "inputs": [ { "indexed": true, "internalType": "uint256", "name": "id", "type": "uint256" }, { "indexed": true, "internalType": "address", "name": "liker", "type": "address" } ], "name": "PostLiked", "type": "event" },
{ "inputs": [ { "internalType": "string", "name": "content", "type": "string" } ], "name": "createPost", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "nonpayable", "type": "function" },
{ "inputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" } ], "name": "getPost", "outputs": [ { "components": [ { "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "address", "name": "author", "type": "address" }, { "internalType": "string", "name": "content", "type": "string" }, { "internalType": "uint256", "name": "timestamp", "type": "uint256" } ], "internalType": "struct Posts.Post", "name": "", "type": "tuple" }, { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" },
{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "likeCounts", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" },
{ "inputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" } ], "name": "likePost", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "address", "name": "", "type": "address" } ], "name": "liked", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" },
{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "posts", "outputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "address", "name": "author", "type": "address" }, { "internalType": "string", "name": "content", "type": "string" }, { "internalType": "uint256", "name": "timestamp", "type": "uint256" } ], "stateMutability": "view", "type": "function" }
] as const;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

POSTS ABI lacks getAllPosts that the QA script expects.

Either add the view function here if the contract truly supports it, or update consumers (scripts/qa-onchain-check.js) to use events-driven enumeration. Keep a single source of truth for ABI to avoid drift.

Run to check usages expecting getAllPosts:


🏁 Script executed:

#!/bin/bash
rg -nP --type=ts --type=js -C2 '\bgetAllPosts\b'

Length of output: 1941


POSTS ABI missing getAllPosts — add it or update consumers

scripts/qa-onchain-check.js and src/hooks/usePosts.ts call getAllPosts, but the POSTS ABI in src/lib/contracts.ts does not include that function. Add the signature "function getAllPosts() view returns (uint256[])" to the ABI (preferred — single source of truth) or update the callers to rely on the existing fallback/event-driven enumeration.

Affected locations:

  • src/lib/contracts.ts:22-29 — current POSTS ABI (missing getAllPosts)
  • scripts/qa-onchain-check.js:19-26 — defines POSTS_ABI and calls contract.getAllPosts()
  • src/hooks/usePosts.ts:75-83 — attempts readContract({ functionName: 'getAllPosts' }); fallback at 129-131
🤖 Prompt for AI Agents
src/lib/contracts.ts lines 22-29: the POSTS ABI defined here is missing the
getAllPosts() view function used by scripts/qa-onchain-check.js and
src/hooks/usePosts.ts; add the ABI entry for getAllPosts — i.e. include the
function signature "function getAllPosts() view returns (uint256[])" in the
exported POSTS ABI array (maintain the as const typing), or alternatively update
the callers to stop invoking getAllPosts and use the existing events/fallback
enumeration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants