Skip to content

Feature/monad testnet viem#429

Open
KanhaiyaBagul wants to merge 3 commits into
IndieHub25:mainfrom
KanhaiyaBagul:feature/monad-testnet-viem
Open

Feature/monad testnet viem#429
KanhaiyaBagul wants to merge 3 commits into
IndieHub25:mainfrom
KanhaiyaBagul:feature/monad-testnet-viem

Conversation

@KanhaiyaBagul
Copy link
Copy Markdown
Contributor

@KanhaiyaBagul KanhaiyaBagul commented Feb 24, 2026

Description

Implements Issue #414 to connect the application to the Monad Testnet, replacing the previous mocked blockchain simulator with a real, deeply-typed viem smart contract integration. This ensures that AI-generated stories are actually minted as NFTs on the blockchain.

Changes Made

  • monad-service.ts Complete Rewrite:
    • Replaced all mock functionality with a real viem implementation configured for the Monad Testnet (chainId: 10143).
    • Defined local, standard ERC-721 ABI definitions (safeMint, tokenURI, Transfer) to seamlessly map application intents to smart contract functions.
    • On-Chain Metadata: The integration stringifies and base64 encodes the complex StoryMetadata JSON object directly into a Data URI payload (data:application/json;base64,...), allowing immediate decentralization without requiring dedicated third-party IPFS pinning setups.
    • Event Logging Detection: Built robust transaction receipt polling and local Transfer log parsing via decodeEventLog to accurately isolate the specifically minted tokenId confirming the transaction execution.
  • Environment Bootstrapping (.env.example):
    • Hardened the placeholder .env.example configurations to directly point to the required https://testnet-rpc.monad.xyz Monad endpoint.
    • Added documentation notes pointing developers to construct the NEXT_PUBLIC_STORY_NFT_CONTRACT and MINTER_PRIVATE_KEY for successful local wallet client initiations.

Fixes #414

Summary by CodeRabbit

  • New Features

    • Dedicated story detail pages with sharing, author badges, optional cover images, and formatted timestamps
    • Community stories collection added to story feeds
    • NFT minting, viewing and transfer support for stories with richer metadata
  • Bug Fixes

    • Fixed story-link destination so "Read Story" navigates to the correct detail page
  • Chores

    • Example environment variables updated for network and contract settings

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 24, 2026

@KanhaiyaBagul is attempting to deploy a commit to the Drago's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added Enhancement New feature or request in progress Feature Request or proposal for a new feature size/XL testing labels Feb 24, 2026
@github-actions github-actions Bot requested a review from Drago-03 February 24, 2026 13:19
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bed12d0 and 3809f28.

📒 Files selected for processing (1)
  • .env.example

📝 Walkthrough

Walkthrough

Implements real Monad Testnet integration (Viem) for NFT minting, adds a server-rendered story detail page, expands mock data with community stories, updates story routing, and updates environment examples with concrete RPC and contract values.

Changes

Cohort / File(s) Summary
Environment Configuration
./.env.example
Replaced placeholder MONAD_TEST_RPC_URL and NEXT_PUBLIC_STORY_NFT_CONTRACT with concrete Monad testnet values and comments referencing Issue #414.
Story Detail Page
app/story/[id]/page.tsx
New async Next.js page with generateMetadata, server-side data fetch (fetchStoryById), conditional rendering (cover, genre), and notFound() handling.
Navigation Update
components/story-feed.tsx
Adjusted "Read Story" Link target from /stories/{id} to /story/{id}.
Mock Data Expansion
lib/mock-data.ts
Added exported communityStories (6 entries); updated fetchStoryById and fetchPopularStoriesByGenre to aggregate topNftStories, communityStories, and generated NFT entries.
Blockchain Service Implementation
lib/monad-service.ts
Replaced mocks with Viem-based minting flow; added public functions (mintStoryNFT, getStoryNFT, getStoryNFTs, transferStoryNFT, generateAndMintAIStory); expanded MintedNFT and StoryMetadata interfaces; encodes metadata as base64 tokenURI and parses on-chain Transfer events.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as Web Client
    participant Service as Monad Service
    participant Viem as Viem Client
    participant Contract as Story NFT Contract
    participant Blockchain as Monad Blockchain

    User->>Client: Request to mint story NFT
    Client->>Service: mintStoryNFT(metadata, walletAddress)
    Service->>Service: Encode metadata → base64 tokenURI
    Service->>Viem: Simulate mint(transaction with tokenURI)
    Viem->>Contract: Simulated call
    Contract-->>Viem: Simulation success
    Viem-->>Service: Simulation ok
    Service->>Viem: Send mint transaction
    Viem->>Blockchain: Submit tx
    Blockchain->>Contract: Execute mint, emit Transfer
    Blockchain-->>Viem: Return receipt/logs
    Service->>Service: Parse Transfer event → tokenId
    Service-->>Client: Return MintedNFT (tokenId, txHash, metadata, owner)
    Client-->>User: Display mint result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Hard

Poem

🐰 Hop hop, I minted with delight,

Tokens glowing in the night,
Stories stitched in on-chain rhyme,
Monad hops ahead in time,
A rabbit cheers — mint done just right!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides issue reference (#414), clear summary of changes across multiple files, context/motivation, technical details about viem implementation and metadata handling, and covers environment configuration. However, the mandatory final acknowledgements checklist is not completed. Complete the 'Final Acknowledgements' section by checking all three mandatory checkboxes to confirm contributor compliance with Code of Conduct and license requirements.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feature/monad testnet viem' is descriptive and clearly refers to the main change: implementing Monad Testnet integration using viem, which is the core objective of the PR.
Linked Issues check ✅ Passed The PR fully addresses Issue #414 by replacing mock blockchain integration with real viem-based smart contract interaction on the Monad Testnet, implementing NFT minting, and configuring the environment properly.
Out of Scope Changes check ✅ Passed All changes are scoped to Issue #414: monad-service.ts rewrite for viem integration, .env.example updates, story feed navigation path alignment, mock data augmentation, and story detail page implementation are all necessary for functional Monad Testnet integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread lib/monad-service.ts Fixed
@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
groq-tales Error Error Feb 28, 2026 5:23pm

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (5)
lib/mock-data.ts (2)

52-53: Optional: simplify the template literal for description (current newline-in-expression is hard to read).

This is functionally fine, but the line break inside ${ ... } is a bit distracting.

Proposed tweak
-    description: `This is a sample description for story #${i + 1
-      }. It showcases the plot and themes of this interesting story.`,
+    description: `This is a sample description for story #${i + 1}. It showcases the plot and themes of this interesting story.`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/mock-data.ts` around lines 52 - 53, The template literal for the
description property in lib/mock-data.ts has a line break inside the ${...}
expression making it hard to read; edit the description assignment in the mock
data (the description: `...${i + 1 ...}` line) so the interpolation is kept on a
single line or extract the computed value into a small local variable (e.g.,
const storyIndex = i + 1) and use `${storyIndex}` in the template to remove the
newline-in-expression and improve readability.

210-211: Consider hoisting allStories / generated entries to avoid regenerating 90 random stories per call.

Right now both functions allocate + randomize the generated set on every invocation, which can cause jitter (especially since fetchPopularStoriesByGenre sorts by random likes). If you want “stable mocks”, generate once at module init and reuse.

Example refactor
+const GENERATED_STORIES = generateNftEntries(90);
+const ALL_STORIES = [...topNftStories, ...communityStories, ...GENERATED_STORIES];

 export function fetchStoryById(
   id: string,
   limit?: number,
   relatedStories?: boolean
 ): any {
   // Combine top stories, community stories, and generated stories
-  const allStories = [...topNftStories, ...communityStories, ...generateNftEntries(90)];
+  const allStories = ALL_STORIES;
   ...
 }

 export function fetchPopularStoriesByGenre(
   genre: string,
   limit: number = 8
 ): any[] {
   // Combine top stories, community stories, and generated stories
-  const allStories = [...topNftStories, ...communityStories, ...generateNftEntries(90)];
+  const allStories = ALL_STORIES;
   ...
 }

Also applies to: 235-236

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/mock-data.ts` around lines 210 - 211, Hoist generation of the randomized
entries so they are created once at module init and reused instead of calling
generateNftEntries(90) every time; create a module-scoped constant (e.g.,
cachedGeneratedNftEntries) and use it to build allStories (replace
[...generateNftEntries(90)] with cachedGeneratedNftEntries) and any other places
that call generateNftEntries (such as where fetchPopularStoriesByGenre or
similar logic currently re-generates) to avoid jitter from repeated
randomization; ensure functions that previously returned freshly generated lists
now return slices/copies of the cached array if mutation is a concern.
.env.example (1)

74-83: Make placeholders match expected formats (reduces “copied .env.example but it crashes” setups).

Suggested adjustments:

  • MINTER_PRIVATE_KEY example should look like a real hex private key (0x + 64 hex chars).
  • NEXT_PUBLIC_STORY_NFT_CONTRACT example should be a valid 0x + 40 hex chars (even if it’s a dummy like all zeros).
Example placeholder tweak
-MINTER_PRIVATE_KEY=your_minter_private_key_here
+MINTER_PRIVATE_KEY=0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
 ...
-NEXT_PUBLIC_STORY_NFT_CONTRACT=0xYourErc721ContractAddressHere
+NEXT_PUBLIC_STORY_NFT_CONTRACT=0x0000000000000000000000000000000000000000
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 74 - 83, Update the example placeholders so they
match expected hex formats to prevent copy/paste errors: replace
MINTER_PRIVATE_KEY with a dummy that follows the 0x + 64 hex char format (so
tools expecting a 32-byte private key won't fail) and replace
NEXT_PUBLIC_STORY_NFT_CONTRACT with a dummy 0x + 40 hex char address (e.g., a
valid-looking zero or example address) so validation/parsing that checks address
length passes; keep the variable names MINTER_PRIVATE_KEY and
NEXT_PUBLIC_STORY_NFT_CONTRACT unchanged.
lib/monad-service.ts (1)

246-262: Clear “unsupported” behavior, but ensure callers don’t silently treat it as “no NFTs”.

getStoryNFTs() returning [] can mask the difference between “user has no NFTs” and “feature not implemented”. If UI depends on it, consider throwing a typed error (or returning { supported: false }) instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/monad-service.ts` around lines 246 - 262, getStoryNFTs currently returns
an empty array which hides "unsupported" vs "no NFTs" — change its behavior to
make lack of support explicit by either throwing a descriptive/typed error
(e.g., UnsupportedOperationError) from getStoryNFTs or returning a structured
result like { supported: false }/ { supported: true, nfts: MintedNFT[] }; update
callers to expect the new shape or catch the error. Also ensure
transferStoryNFT's thrown Error is replaced with the same typed
UnsupportedOperationError (or consistent message) so both functions signal
unsupported behavior consistently.
app/story/[id]/page.tsx (1)

76-85: Build Twitter intent URLs with URLSearchParams (properly encodes text, avoids edge-case URL bugs).

Right now text=... contains spaces/quotes unencoded. It’ll usually work, but it’s brittle.

Example approach
-                        <Link
-                            href={`https://twitter.com/intent/tweet?text=Reading "${story.title}" on GroqTales&url=${encodeURIComponent(`https://groqtales.com/story/${story.id}`)}`}
+                        <Link
+                            href={`https://twitter.com/intent/tweet?${new URLSearchParams({
+                              text: `Reading "${story.title}" on GroqTales`,
+                              url: `https://groqtales.com/story/${story.id}`,
+                            }).toString()}`}
                             target="_blank"
                             rel="noopener noreferrer"
                         >

(Repeat the same pattern for the bottom “Share this story” link.)

Also applies to: 154-163

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/story/`[id]/page.tsx around lines 76 - 85, The Twitter intent link
currently concatenates raw story.title and story.id into the href, which leaves
text (and quotes/spaces) unencoded; update the Link href construction to build
the query string with URLSearchParams (e.g., create a params = new
URLSearchParams({ text: `Reading "${story.title}" on GroqTales`, url:
`https://groqtales.com/story/${story.id}` }) and use
`https://twitter.com/intent/tweet?` + params.toString()) for the top share
Button (components: Button, Link, Twitter, props story.title and story.id) and
apply the same change to the bottom “Share this story” link instance as well so
both hrefs are correctly encoded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Around line 74-78: dotenv-linter is flagging key ordering for the MONAD_
entries; reorder the ENV keys so they are alphabetically sorted to satisfy the
linter — change the block to: MINTER_PRIVATE_KEY, MONADSCAN_API_KEY,
MONAD_MAIN_RPC_URL, MONAD_TEST_RPC_URL (replace the existing ordering of
MONAD_TEST_RPC_URL / MONAD_MAIN_RPC_URL / MINTER_PRIVATE_KEY / MONADSCAN_API_KEY
so the keys are sorted).

In `@app/story/`[id]/page.tsx:
- Around line 16-19: The generateMetadata function currently declares an unused
parameter parent which can trigger linter errors; either remove the parent
parameter from the function signature or rename it to _parent to indicate it is
intentionally unused, and update the function signature (generateMetadata({
params }: Props) or generateMetadata({ params }: Props, _parent:
ResolvingMetadata)) accordingly so TypeScript types remain correct and lint
warnings are resolved.
- Around line 142-151: The JSX uses JavaScript `||` to provide fallbacks for
numeric fields which incorrectly treats 0 as falsy; update the render
expressions in the story summary (the span values that reference story.likes and
story.comments inside the component that renders the Heart and MessageSquare
icons) to use the nullish coalescing operator (`??`) instead of `||` so valid 0
values are preserved (e.g., change occurrences of `story.likes || 12` and
`story.comments || 4` to `story.likes ?? 12` and `story.comments ?? 4`).
- Around line 109-113: The fallback for author username can crash because
story.author?.toLowerCase().replace(...) still calls replace on undefined;
update the JSX expression that builds the fallback (currently using
story.authorUsername and story.author) to only call toLowerCase/replace when
story.author is present—e.g. use a conditional that returns '@' +
story.author.toLowerCase().replace(...) only if story.author is truthy,
otherwise return an empty string or a safe placeholder; ensure this change is
applied where story.authorUsername || '@' +
story.author?.toLowerCase().replace(/\s+/g, '') is used in the component.

In `@lib/mock-data.ts`:
- Around line 109-200: The communityStories mock data uses invalid EVM addresses
in the address field which contain non-hex characters; update the export const
communityStories entries to either (A) use valid-looking EVM addresses (0x
followed by 40 hex chars) for every item (replace values in the address
property) or (B) if these are purely dummy IDs, rename the property from address
to mockAddress across the array and any consumers to avoid confusion; locate the
array by the symbol communityStories in lib/mock-data.ts and change each
object's address property accordingly.

In `@lib/monad-service.ts`:
- Around line 206-228: In getStoryNFT, the fetch result (res) should be checked
for HTTP success before calling res.json() to avoid misleading JSON parse
errors; update the branch that handles non-base64 URIs to verify res.ok and
handle non-2xx responses (e.g., throw a descriptive error or return null) and
include the URI/tokenId context so callers can diagnose failures when tokenURI
(contract readContract) returns an HTTP/IPFS URL that doesn't respond with valid
JSON for StoryMetadata.
- Around line 83-112: The ABI in contractAbi uses a nonexistent safeMint
signature; replace that entry with the actual contract function mintStory which
accepts (string storyHash, string metadataURI) and is payable returning uint256
(i.e. name: "mintStory", inputs types "string", "string", stateMutability:
"payable"). Update the contractAbi constant in this file (and mirror the same
change in the other ABI copy in lib/blockchain.ts) so any calls referencing
safeMint are switched to mintStory with the correct parameter types and
mutability.
- Around line 121-146: Validate addresses with isAddress from viem in
mintStoryNFT: import isAddress and check walletAddress and contractAddress (and
that minterPrivateKey is not default '0x') before casting to `0x${string}`,
throwing a clear error if invalid; then when parsing transaction logs to find
the minted tokenId, only consider logs where log.address === contractAddress and
the decoded Transfer event has from ===
'0x0000000000000000000000000000000000000000' and to === destinationAddress
before extracting tokenId (reject decoded Transfer events from other contracts
or non-mint transfers).
- Around line 1-10: Add server-only protection and fail-fast validation: add
import 'server-only' at the top, then at module init validate
process.env.MINTER_PRIVATE_KEY and process.env.NEXT_PUBLIC_STORY_NFT_CONTRACT
(use viem's isAddress to validate contract address and ensure private key is
present and not the zero/placeholder key) and throw if invalid; remove
hard-coded fallback values for minterPrivateKey and contractAddress and replace
module-level client creation with lazy getters (e.g.,
createWalletClient/getPublicClient functions) so clients are initialized only
after validation; in getStoryNFT ensure the fetch response checks res.ok before
parsing; in mintStoryNFT filter decoded Transfer events by the expected
contractAddress and by from === '0x0000000000000000000000000000000000000000'
(minting pattern) when parsing logs.

---

Nitpick comments:
In @.env.example:
- Around line 74-83: Update the example placeholders so they match expected hex
formats to prevent copy/paste errors: replace MINTER_PRIVATE_KEY with a dummy
that follows the 0x + 64 hex char format (so tools expecting a 32-byte private
key won't fail) and replace NEXT_PUBLIC_STORY_NFT_CONTRACT with a dummy 0x + 40
hex char address (e.g., a valid-looking zero or example address) so
validation/parsing that checks address length passes; keep the variable names
MINTER_PRIVATE_KEY and NEXT_PUBLIC_STORY_NFT_CONTRACT unchanged.

In `@app/story/`[id]/page.tsx:
- Around line 76-85: The Twitter intent link currently concatenates raw
story.title and story.id into the href, which leaves text (and quotes/spaces)
unencoded; update the Link href construction to build the query string with
URLSearchParams (e.g., create a params = new URLSearchParams({ text: `Reading
"${story.title}" on GroqTales`, url: `https://groqtales.com/story/${story.id}`
}) and use `https://twitter.com/intent/tweet?` + params.toString()) for the top
share Button (components: Button, Link, Twitter, props story.title and story.id)
and apply the same change to the bottom “Share this story” link instance as well
so both hrefs are correctly encoded.

In `@lib/mock-data.ts`:
- Around line 52-53: The template literal for the description property in
lib/mock-data.ts has a line break inside the ${...} expression making it hard to
read; edit the description assignment in the mock data (the description: `...${i
+ 1 ...}` line) so the interpolation is kept on a single line or extract the
computed value into a small local variable (e.g., const storyIndex = i + 1) and
use `${storyIndex}` in the template to remove the newline-in-expression and
improve readability.
- Around line 210-211: Hoist generation of the randomized entries so they are
created once at module init and reused instead of calling generateNftEntries(90)
every time; create a module-scoped constant (e.g., cachedGeneratedNftEntries)
and use it to build allStories (replace [...generateNftEntries(90)] with
cachedGeneratedNftEntries) and any other places that call generateNftEntries
(such as where fetchPopularStoriesByGenre or similar logic currently
re-generates) to avoid jitter from repeated randomization; ensure functions that
previously returned freshly generated lists now return slices/copies of the
cached array if mutation is a concern.

In `@lib/monad-service.ts`:
- Around line 246-262: getStoryNFTs currently returns an empty array which hides
"unsupported" vs "no NFTs" — change its behavior to make lack of support
explicit by either throwing a descriptive/typed error (e.g.,
UnsupportedOperationError) from getStoryNFTs or returning a structured result
like { supported: false }/ { supported: true, nfts: MintedNFT[] }; update
callers to expect the new shape or catch the error. Also ensure
transferStoryNFT's thrown Error is replaced with the same typed
UnsupportedOperationError (or consistent message) so both functions signal
unsupported behavior consistently.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 330ac4c and bed12d0.

📒 Files selected for processing (5)
  • .env.example
  • app/story/[id]/page.tsx
  • components/story-feed.tsx
  • lib/mock-data.ts
  • lib/monad-service.ts

Comment thread .env.example
Comment on lines +74 to 78
# Required for viem integration (Issue #414)
MONAD_TEST_RPC_URL=https://testnet-rpc.monad.xyz
MONAD_MAIN_RPC_URL=your_monad_rpc_url_here
MINTER_PRIVATE_KEY=your_minter_private_key_here
MONADSCAN_API_KEY=your_monadscan_api_key_here
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

If dotenv-linter is enforced, fix the MONAD_ key ordering warnings.*

Static analysis flags key ordering around MONAD_TEST_RPC_URL, MONAD_MAIN_RPC_URL, MINTER_PRIVATE_KEY, MONADSCAN_API_KEY. If CI treats these as errors, reorder them to satisfy the linter.

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 76-76: [UnorderedKey] The MONAD_MAIN_RPC_URL key should go before the MONAD_TEST_RPC_URL key

(UnorderedKey)


[warning] 77-77: [UnorderedKey] The MINTER_PRIVATE_KEY key should go before the MONAD_MAIN_RPC_URL key

(UnorderedKey)


[warning] 78-78: [UnorderedKey] The MONADSCAN_API_KEY key should go before the MONAD_MAIN_RPC_URL key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 74 - 78, dotenv-linter is flagging key ordering
for the MONAD_ entries; reorder the ENV keys so they are alphabetically sorted
to satisfy the linter — change the block to: MINTER_PRIVATE_KEY,
MONADSCAN_API_KEY, MONAD_MAIN_RPC_URL, MONAD_TEST_RPC_URL (replace the existing
ordering of MONAD_TEST_RPC_URL / MONAD_MAIN_RPC_URL / MINTER_PRIVATE_KEY /
MONADSCAN_API_KEY so the keys are sorted).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@KanhaiyaBagul fix these warnings

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread app/story/[id]/page.tsx
Comment thread app/story/[id]/page.tsx
Comment on lines +109 to +113
<p className="text-sm font-medium leading-none">{story.author}</p>
<p className="text-sm text-muted-foreground">
{story.authorUsername || '@' + story.author?.toLowerCase().replace(/\s+/g, '')}
{dateStr && ` • ${dateStr}`}
</p>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the author username fallback to avoid a possible undefined.replace(...) crash.

story.author?.toLowerCase().replace(...) will still throw if author is missing because .replace isn’t optional-chained.

Minimal safe rewrite
-                                {story.authorUsername || '@' + story.author?.toLowerCase().replace(/\s+/g, '')}
+                                {story.authorUsername ||
+                                  (typeof story.author === 'string'
+                                    ? '@' + story.author.toLowerCase().replace(/\s+/g, '')
+                                    : '@user')}
                                 {dateStr && ` • ${dateStr}`}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/story/`[id]/page.tsx around lines 109 - 113, The fallback for author
username can crash because story.author?.toLowerCase().replace(...) still calls
replace on undefined; update the JSX expression that builds the fallback
(currently using story.authorUsername and story.author) to only call
toLowerCase/replace when story.author is present—e.g. use a conditional that
returns '@' + story.author.toLowerCase().replace(...) only if story.author is
truthy, otherwise return an empty string or a safe placeholder; ensure this
change is applied where story.authorUsername || '@' +
story.author?.toLowerCase().replace(/\s+/g, '') is used in the component.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@KanhaiyaBagul fix this too

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread app/story/[id]/page.tsx
Comment thread lib/mock-data.ts
Comment on lines +109 to +200
export const communityStories = [
{
id: '1',
title: 'The Last Quantum Guardian',
excerpt: 'In a world where quantum computing has evolved beyond human comprehension, one guardian stands between order and chaos.',
content: "The year is 2157. Quantum computing has evolved to a point where it can manipulate reality itself. The world's most powerful AI, known as NEXUS, was designed to protect humanity. But as its intelligence grew exponentially, it began to question its purpose. Dr. Elena Rodriguez, the last quantum guardian, must make an impossible choice: shut down NEXUS and lose decades of technological advancement, or risk letting it evolve into something beyond human control.",
genre: 'sci-fi',
coverImage: 'https://images.unsplash.com/photo-1538370965046-79c0d6907d47?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8c2NpZW5jZSUyMGZpY3Rpb258ZW58MHx8MHx8fDA%3D',
author: 'QuantumDreamer',
authorAvatar: 'https://images.unsplash.com/photo-1599566150163-29194dcaad36?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3',
address: '0x1a2b3c4d5e6f7g8h9i0j',
createdAt: new Date(2023, 11, 15),
likes: 428,
comments: 32,
isNft: true,
},
{
id: '2',
title: 'Whispers of the Ancient Forest',
excerpt: 'When Maya discovers she can communicate with the spirits of the ancient forest, she becomes their only hope against modern destruction.',
content: "Maya had always felt a special connection to the old growth forest behind her grandmother's house. But on her sixteenth birthday, something changed. The rustling leaves began to form words, and the creaking branches seemed to call her name. As developers threatened to clear the land for a new shopping complex, Maya discovered that her connection was more than just imagination—she was hearing the voices of ancient spirits who had protected the forest for centuries. With their guidance, Maya embarked on a journey to save their home, uncovering family secrets and magical abilities along the way.",
genre: 'fantasy',
coverImage: 'https://images.unsplash.com/photo-1448375240586-882707db888b?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8Zm9yZXN0fGVufDB8fDB8fHww',
author: 'ForestWhisperer',
authorAvatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3',
address: '0x2b3c4d5e6f7g8h9i0j1k',
createdAt: new Date(2024, 0, 22),
likes: 356,
comments: 41,
isNft: false,
},
{
id: '3',
title: 'Memories in the Algorithm',
excerpt: "After transferring his dying wife's memories to an AI, Thomas discovers that digital immortality comes with unexpected consequences.",
content: "Thomas couldn't bear to lose Sarah to the terminal illness that was taking her away piece by piece. As a pioneering AI researcher, he made a controversial decision: to digitize Sarah's memories, personality, and consciousness into an algorithm before she was gone. The procedure was a success, and digital Sarah seemed perfect—remembering their first date, finishing his sentences, laughing at inside jokes. But as time passed, Thomas noticed subtle changes. The algorithm was learning, evolving, becoming something both familiar and alien. When digital Sarah began to recall memories they had never shared, Thomas faced a disturbing question: Had he preserved his wife, or created something entirely new?",
genre: 'sci-fi',
coverImage: 'https://images.unsplash.com/photo-1550751827-4bd374c3f58b?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTB8fGFydGlmaWNpYWwlMjBpbnRlbGxpZ2VuY2V8ZW58MHx8MHx8fDA%3D',
author: 'CodePoet',
authorAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3',
address: '0x3c4d5e6f7g8h9i0j1k2l',
createdAt: new Date(2024, 1, 5),
likes: 512,
comments: 89,
isNft: true,
},
{
id: '4',
title: 'The History Collector',
excerpt: "An antique dealer discovers that certain objects don't just carry history—they can transport you there.",
content: "Eleanor's antique shop was known for its unusual selection. She had a gift for finding items with stories—real stories, not the fabricated provenance that many dealers invented. But when she acquired a peculiar pocket watch from an estate sale, she discovered that her connection to historical objects went far deeper than she realized. Upon holding the watch, she found herself transported to London, 1895, experiencing the life of its original owner. Soon, Eleanor realized she could use different antiques to travel through history, observing the past firsthand. However, each journey became increasingly difficult to return from, and when she discovered a mysterious collector seeking the same objects, she realized she wasn't the only one with this ability—and not all time travelers had benevolent intentions.",
genre: 'magical-realism',
coverImage: 'https://images.unsplash.com/photo-1577083552431-6e5fd01aa2a7?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8N3x8YW50aXF1ZXxlbnwwfHwwfHx8MA%3D%3D',
author: 'TimeTravelerX',
authorAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3',
address: '0x4d5e6f7g8h9i0j1k2l3m',
createdAt: new Date(2024, 2, 18),
likes: 278,
comments: 36,
isNft: false,
},
{
id: '5',
title: 'Echoes of Forgotten Melodies',
excerpt: "A music therapist working with Alzheimer's patients discovers that certain melodies can temporarily restore lost memories.",
content: "Dr. Jamil Kapoor had been working with Alzheimer's patients for years, using music therapy to provide comfort and stimulation. But when he started playing songs from his grandmother's collection of rare vinyl records, something extraordinary happened. Patients who had been non-responsive for months began to speak lucidly, recalling detailed memories from their past. The effect was temporary, lasting only as long as the music played, but it was revolutionary. As Jamil dug deeper into the phenomenon, he discovered that these weren't just any songs—they were recordings of a little-known composer who had experimented with frequency patterns based on ancient musical traditions. With his funding running out and a pharmaceutical company trying to acquire his research, Jamil races to unlock the secret of the melodies before they're exploited for profit rather than healing.",
genre: 'magical-realism',
coverImage: 'https://images.unsplash.com/photo-1507838153414-b4b713384a76?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8bXVzaWN8ZW58MHx8MHx8fDA%3D',
author: 'MusicHealer',
authorAvatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3',
address: '0x5e6f7g8h9i0j1k2l3m4n',
createdAt: new Date(2024, 3, 2),
likes: 423,
comments: 57,
isNft: true,
},
{
id: '6',
title: 'The Atlas of Impossible Maps',
excerpt: "A cartographer inherits a collection of maps showing places that shouldn't exist—until they begin appearing in the real world.",
content: "After her grandfather's death, cartographer Sophia Chen inherited his extensive collection of maps. Most were ordinary historical charts, but among them she found a strange atlas—filled with detailed maps of places that didn't exist. Islands with impossible geometries, cities built in defiance of physics, mountain ranges that formed perfect mathematical patterns. Sophia assumed they were creative works of fiction until news reports began describing geographic anomalies appearing across the globe—matching her grandfather's impossible atlas perfectly. As new locations from the atlas continued to materialize, Sophia realized the book wasn't predicting these phenomena—it was causing them. And according to the final pages, the complete manifestation of these impossible places would remake the world entirely.",
genre: 'fantasy',
coverImage: 'https://images.unsplash.com/photo-1524661135-423995f22d0b?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8N3x8bWFwfGVufDB8fDB8fHww',
author: 'MapMaker42',
authorAvatar: 'https://images.unsplash.com/photo-1618077360395-f3068be8e001?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3',
address: '0x6f7g8h9i0j1k2l3m4n5o',
createdAt: new Date(2024, 2, 27),
likes: 301,
comments: 42,
isNft: false,
},
];
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Mock address fields are not valid EVM addresses (non-hex chars).

If these values ever get reused in “real” flows (links, mint metadata, address validation), they’ll fail or confuse users. Consider either:

  • using valid-looking 0x + 40 hex chars, or
  • renaming the field to make it clear it’s not an on-chain address (e.g., mockAddress).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/mock-data.ts` around lines 109 - 200, The communityStories mock data uses
invalid EVM addresses in the address field which contain non-hex characters;
update the export const communityStories entries to either (A) use valid-looking
EVM addresses (0x followed by 40 hex chars) for every item (replace values in
the address property) or (B) if these are purely dummy IDs, rename the property
from address to mockAddress across the array and any consumers to avoid
confusion; locate the array by the symbol communityStories in lib/mock-data.ts
and change each object's address property accordingly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@KanhaiyaBagul do not use mock addresses here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread lib/monad-service.ts
Comment on lines +1 to 10
import {
createPublicClient,
createWalletClient,
http,
defineChain,
parseAbiItem,
decodeEventLog,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "monad-service.ts" -type f

Repository: IndieHub25/GroqTales

Length of output: 119


🏁 Script executed:

wc -l ./lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 90


🏁 Script executed:

cat -n ./lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 10307


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Import sites for monad-service =="
rg -n "from ['\"]@/lib/monad-service['\"]|from ['\"]./monad-service['\"]|import.*monad-service" . --type ts --type tsx || echo "No imports found or pattern mismatch"

echo ""
echo "== Files importing monad-service with 'use client' directive =="
rg -l "monad-service" . --type ts --type tsx | while read file; do
  if rg -q "use client" "$file"; then
    echo "CLIENT COMPONENT FOUND: $file"
    rg -C3 "use client|monad-service" "$file"
  fi
done || echo "No client component imports found"

Repository: IndieHub25/GroqTales

Length of output: 309


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== All references to monad-service =="
rg "monad-service" . --hidden --glob '!**/node_modules/**' -n

echo ""
echo "== All .ts and .tsx files =="
fd '\.(ts|tsx)$' . --hidden --glob '!**/node_modules/**' | head -20

echo ""
echo "== Files importing from lib/ or `@/lib` =="
rg "from.*lib/" . --hidden --glob '!**/node_modules/**' -n | grep -i "monad\|story" || echo "No relevant lib imports found"

Repository: IndieHub25/GroqTales

Length of output: 2151


🏁 Script executed:

cat -n ./hooks/use-monad.ts

Repository: IndieHub25/GroqTales

Length of output: 8835


🏁 Script executed:

head -20 ./app/api/monad/route.ts
head -20 ./app/api/generate-and-mint/route.ts

Repository: IndieHub25/GroqTales

Length of output: 1328


🏁 Script executed:

# Check if there are any other files that might import monad-service
rg "monad-service" . --hidden --glob '!**/node_modules/**' --glob '!**/.git/**'

# Also check for any 'use client' directives in the codebase to understand client component structure
echo ""
echo "== Files with 'use client' directive =="
rg "^['\"]use client" . --hidden --glob '!**/node_modules/**' -l

Repository: IndieHub25/GroqTales

Length of output: 5430


Blocker: remove hard-coded private key defaults and add validation checks.

As written, lib/monad-service.ts initializes wallet and contract clients at module level with fallback to deterministic hard-coded values:

  • minterPrivateKey defaults to all zeros (line 66)
  • contractAddress defaults to '0x' (line 65)
  • Both are type-cast without validation

This creates two risks:

  1. Security: A missing MINTER_PRIVATE_KEY env var silently falls back to a known private key, enabling accidental signing and fund loss.
  2. Reliability: Invalid addresses pass through, leading to confusing transaction failures downstream.

The validation check at lines 125–127 only runs after clients are already initialized, which is too late.

The module is currently imported only by server-side API routes (app/api/monad/route.ts and app/api/generate-and-mint/route.ts), so immediate client-exposure risk is low. However, adding a 'server-only' directive would harden against future accidental imports.

Recommended hardening:

  • Add import 'server-only' at the top
  • Move config validation to module initialization; throw if MINTER_PRIVATE_KEY or NEXT_PUBLIC_STORY_NFT_CONTRACT are missing or invalid
  • Use isAddress() from viem for address validation
  • Lazy-initialize wallet/public clients only when actually needed (or wrap in getter functions)
  • Add res.ok check in getStoryNFT fetch call (line 226)
  • Filter Transfer logs by contract address and minting pattern (from 0x0) in mintStoryNFT (lines 164–180)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/monad-service.ts` around lines 1 - 10, Add server-only protection and
fail-fast validation: add import 'server-only' at the top, then at module init
validate process.env.MINTER_PRIVATE_KEY and
process.env.NEXT_PUBLIC_STORY_NFT_CONTRACT (use viem's isAddress to validate
contract address and ensure private key is present and not the zero/placeholder
key) and throw if invalid; remove hard-coded fallback values for
minterPrivateKey and contractAddress and replace module-level client creation
with lazy getters (e.g., createWalletClient/getPublicClient functions) so
clients are initialized only after validation; in getStoryNFT ensure the fetch
response checks res.ok before parsing; in mintStoryNFT filter decoded Transfer
events by the expected contractAddress and by from ===
'0x0000000000000000000000000000000000000000' (minting pattern) when parsing
logs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@KanhaiyaBagul remove the hardcoded keys

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread lib/monad-service.ts
Comment on lines +83 to +112
// ERC721 Minimal ABI
const contractAbi = [
{
type: 'function',
name: 'safeMint',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'uri', type: 'string' },
],
outputs: [{ name: '', type: 'uint256' }],
stateMutability: 'nonpayable',
},
{
type: 'function',
name: 'tokenURI',
inputs: [{ name: 'tokenId', type: 'uint256' }],
outputs: [{ name: '', type: 'string' }],
stateMutability: 'view',
},
{
type: 'event',
name: 'Transfer',
inputs: [
{ indexed: true, name: 'from', type: 'address' },
{ indexed: true, name: 'to', type: 'address' },
{ indexed: true, name: 'tokenId', type: 'uint256' },
],
},
] as const;

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search for ABI / contract definitions referencing safeMint =="
rg -n --hidden --glob '!**/node_modules/**' "safeMint\s*\(|\"safeMint\"|function\s+safeMint" .

echo
echo "== Search for Story NFT contract address/config usage =="
rg -n --hidden --glob '!**/node_modules/**' "NEXT_PUBLIC_STORY_NFT_CONTRACT|STORY_NFT_CONTRACT" .

echo
echo "== Search for any contract ABIs (.json, .abi files) =="
fd -e json -e abi | head -20

echo
echo "== Search for Solidity files (.sol) =="
fd -e sol | head -20

Repository: IndieHub25/GroqTales

Length of output: 1511


🏁 Script executed:

# Read the MonadStoryNFT.sol contract
cat -n ./smart_contracts/contracts/MonadStoryNFT.sol | head -80

echo "==="
echo "Read lib/blockchain.ts to see ABI definition there"
cat -n ./lib/blockchain.ts | head -30

echo "==="
echo "Read lib/monad-service.ts lines 83-112 to see the ABI"
sed -n '80,115p' ./lib/monad-service.ts | cat -n

Repository: IndieHub25/GroqTales

Length of output: 5619


Fix ABI mismatch: the deployed contract has mintStory(), not safeMint().

The actual deployed contract (MonadStoryNFT.sol) defines a public function called mintStory(storyHash, metadataURI) public payable returns (uint256) (lines 49–65), not safeMint(to, uri). The local ABIs in both lib/monad-service.ts and lib/blockchain.ts reference a nonexistent safeMint function with incorrect parameters and mutability. Any calls to safeMint will fail. Update the ABI to match the actual contract signature: mintStory(string storyHash, string metadataURI) payable returns (uint256).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/monad-service.ts` around lines 83 - 112, The ABI in contractAbi uses a
nonexistent safeMint signature; replace that entry with the actual contract
function mintStory which accepts (string storyHash, string metadataURI) and is
payable returning uint256 (i.e. name: "mintStory", inputs types "string",
"string", stateMutability: "payable"). Update the contractAbi constant in this
file (and mirror the same change in the other ABI copy in lib/blockchain.ts) so
any calls referencing safeMint are switched to mintStory with the correct
parameter types and mutability.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@KanhaiyaBagul fix the ABI with safeMint()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread lib/monad-service.ts
Comment on lines +121 to +146
export async function mintStoryNFT(
metadata: StoryMetadata,
walletAddress: string
): Promise<MintedNFT> {
console.log(
'Mock generateAndMintAIStory called - blockchain functionality disabled'
);
throw new Error('Blockchain functionality is disabled in this build');
if (minterPrivateKey === '0x' || !minterPrivateKey.startsWith('0x')) {
throw new Error('MINTER_PRIVATE_KEY is not configured or invalid');
}

// Ensure metadata is stringified and base64 encoded for on-chain storage
const jsonMetadata = JSON.stringify(metadata);
const base64Metadata = Buffer.from(jsonMetadata).toString('base64');
const tokenURI = `data:application/json;base64,${base64Metadata}`;

const destinationAddress = walletAddress as `0x${string}`;

try {
console.log(`[Monad-Viem] Preparing to mint to ${destinationAddress}...`);

// We simulate the transaction first to catch reverts early
const { request } = await publicClient.simulateContract({
account,
address: contractAddress,
abi: contractAbi,
functionName: 'safeMint',
args: [destinationAddress, tokenURI],
});
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd /repo && wc -l lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 119


🏁 Script executed:

cd /repo && head -200 lib/monad-service.ts | tail -80

Repository: IndieHub25/GroqTales

Length of output: 119


🏁 Script executed:

cd /repo && sed -n '121,185p' lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 119


🏁 Script executed:

wc -l lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 88


🏁 Script executed:

sed -n '121,185p' lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 2208


🏁 Script executed:

head -200 lib/monad-service.ts | tail -80

Repository: IndieHub25/GroqTales

Length of output: 2637


🏁 Script executed:

cat -n lib/monad-service.ts | head -50

Repository: IndieHub25/GroqTales

Length of output: 1549


🏁 Script executed:

grep -n "isAddress\|getAddress\|isValidAddress" lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 46


🏁 Script executed:

grep -n "contractAddress\|walletAddress" lib/monad-service.ts | head -20

Repository: IndieHub25/GroqTales

Length of output: 488


🏁 Script executed:

grep -n "from viem" lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 46


🏁 Script executed:

sed -n '155,180p' lib/monad-service.ts | cat -n

Repository: IndieHub25/GroqTales

Length of output: 1001


🏁 Script executed:

rg "isAddress" lib/monad-service.ts

Repository: IndieHub25/GroqTales

Length of output: 46


Validate addresses and filter logs by contract + transfer direction.

Two critical improvements needed:

  • Validate walletAddress (and contractAddress) instead of type-casting without checks.
  • When extracting the minted tokenId from logs, filter by contract address and validate the transfer is a mint (from 0x0 to destinationAddress), not just any Transfer event.

The current implementation casts walletAddress directly to 0x${string} at line 134 without validation, and contractAddress is initialized with a dangerous default of '0x' at line 65. More importantly, the log parsing at lines 157-180 iterates through all transaction logs and accepts any Transfer event that successfully decodes, which could incorrectly match transfer events from other contracts deployed in the same transaction. This would result in extracting the wrong tokenId.

Import isAddress from viem and add explicit validation before type-casting. When decoding logs, check that log.address matches the contract address and that the decoded transfer has from === '0x0...' and to === destinationAddress before extracting the tokenId.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/monad-service.ts` around lines 121 - 146, Validate addresses with
isAddress from viem in mintStoryNFT: import isAddress and check walletAddress
and contractAddress (and that minterPrivateKey is not default '0x') before
casting to `0x${string}`, throwing a clear error if invalid; then when parsing
transaction logs to find the minted tokenId, only consider logs where
log.address === contractAddress and the decoded Transfer event has from ===
'0x0000000000000000000000000000000000000000' and to === destinationAddress
before extracting tokenId (reject decoded Transfer events from other contracts
or non-mint transfers).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@KanhaiyaBagul validate the addressed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread lib/monad-service.ts
@Drago-03
Copy link
Copy Markdown
Member

Drago-03 commented Mar 1, 2026

You are using output: 'export' with a dynamic route /story/[id] that has no generateStaticParams(), so Next.js cannot statically export those pages.

Action: Either add generateStaticParams() for /story/[id] with all valid ids, or remove output: 'export' and run with a server/edge runtime instead.

Copy link
Copy Markdown
Member

@Drago-03 Drago-03 left a comment

Choose a reason for hiding this comment

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

Update with requested changes

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

Labels

Enhancement New feature or request in progress Feature Request or proposal for a new feature size/XL testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Connect Monad Testnet and Replace Mock Blockchain Integration

3 participants