Skip to content

hi#10

Open
naveendhaterwal wants to merge 4 commits into
mainfrom
naveen3
Open

hi#10
naveendhaterwal wants to merge 4 commits into
mainfrom
naveen3

Conversation

@naveendhaterwal
Copy link
Copy Markdown
Collaborator

@naveendhaterwal naveendhaterwal commented Oct 4, 2025

Summary by CodeRabbit

  • New Features

    • End-to-end Solana/EOS reward flow with treasury-backed transfers (SOL, EOS, SPL).
    • Organization Approval UI component and demo pages (EOS Rewards, Solana Demo, Test Inputs).
    • Client-only rendering helper and Phantom wallet integration.
  • API

    • New approve-reward endpoints for submitting and listing reward approvals.
  • Security

    • Built-in checks: rate limiting, amount validation, transaction verification, treasury checks, suspicious-activity detection.
  • Documentation

    • Comprehensive Solana-based reward flow guide.
  • Database

    • Expanded Issue schema and status support for reward lifecycle.
  • Chores

    • Theme/ hydration adjustments and dependency updates.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 4, 2025

Walkthrough

Adds a Solana/EOS reward system: new Solana and security utilities, server reward pipeline and API routes, multi-step organization approval UI/pages, Prisma schema migration for rewards, EOS token-balance refactor, theme/hydration tweaks, dependency updates, and supporting docs.

Changes

Cohort / File(s) Summary
Solana & Treasury Utilities
lib/solana-utils.js
New comprehensive Solana/EOS utilities: Devnet connection, treasury key handling, balance readers (SOL/SPL/EOS), transfer transaction builders, treasury send helpers, verification and transaction detail helpers, eosUtils and treasuryUtils exports.
Security & Validation
lib/security-utils.js
New security module: rate-limits, reward amount validation, transaction integrity checks, treasury funds checks, security event logging, aggregated performSecurityChecks and detectSuspiciousActivity.
Server Action & Reward Pipeline
actions/rewardContributorForIssue.js
Replaces stub with server-side reward flow: input validation, performSecurityChecks, suspicious-activity detection, token configuration, sendRewardFromTreasury (EOS/SOL/SPL), post-send verification, DB updates (transactional), and UI revalidation.
API Routes (Approve Reward)
app/api/organization/approve-reward/route.js, api/organization/approve-reward/route.js
New POST handlers to verify approval transactions and upsert/create issues; GET to list issues by wallet/status. Includes request validation, transaction verification attempts, Prisma upserts, audit logging, and JSON responses.
Organization Approval UI & Client Helpers
components/OrganizationApproval.jsx, components/ClientOnly.jsx, components/theme-provider.jsx
New OrganizationApproval multi-step client component (wallet flows, build/send transfer, POST approval), ClientOnly wrapper for mount-gated rendering, and ThemeProvider mount gating changes.
Demo & Pages
app/eos-rewards/page.jsx, app/solana-demo/page.jsx, app/test-inputs/page.jsx
New demo pages integrating wallet adapters, OrganizationApproval demos, EOS data loading, and static test-inputs UI.
EOS token helper
EOS/eostoken.jsx
Refactored getTokenBalance signature -> getTokenBalance(walletAddress, tokenMintAddress), per-call Connection creation, associated token account resolution, numeric balance return, and simplified error handling returning 0 on failure/account-missing.
Prisma Schema & Migration
prisma/schema.prisma, prisma/migrations/.../migration.sql
Database migration to support rewards: Issue.id -> String (cuid), added rewardAmount (was tokenReward), tokenType, tokenMintAddress, transactionSignature, treasury/organization wallets, timestamps, optional GitHub fields, Contribution.issueId -> String, new IssueStatus value FUNDED, FK updates.
Docs
SOLANA_REWARD_FLOW.md
New comprehensive guide documenting architecture, flows, security, implementation suggestions, and examples for the Solana reward flow.
Layout & Theming
app/layout.js
Adds suppressHydrationWarning on html/body and updates default theme config (defaultTheme: light, enableSystem: false).
Dependencies
package.json
Removes @clerk/nextjs and @prisma/client, adds next-themes and sonner, and consolidates duplicate dependency declarations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Org as Organization (Wallet)
  participant OA as OrganizationApproval (UI)
  participant API as /api/organization/approve-reward
  participant SA as rewardContributorForIssue (Server Action)
  participant SEC as security-utils
  participant SOL as solana-utils (treasury)
  participant CL as Solana Cluster
  participant DB as Prisma/Database
  participant UI as Revalidate UI

  Org->>OA: Connect wallet, create/fund approval (SOL/SPL/EOS)
  OA->>CL: Send on-chain transfer (user → treasury)
  CL-->>OA: Signature
  OA->>API: POST approval {issue, amount, tokenType, mint, signature, wallets}
  API->>DB: Upsert Issue & Approval record
  API-->>OA: {success, issueId, signature}

  note over SA,SEC: Server-side reward pipeline (alternate flow)
  OA->>SA: trigger rewardContributorForIssue(issueId, username)
  SA->>SEC: performSecurityChecks(...)
  SEC-->>SA: {passed | failures}
  SA->>SOL: send*FromTreasury(recipient, amount, mint?)
  SOL->>CL: Submit transaction
  CL-->>SOL: Signature/confirmation
  SA->>DB: Transactional update (Contribution, Issue status -> FUNDED/REWARDED)
  SA->>UI: revalidatePath(profile, leaderboard)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

A rabbit taps the ledger’s glow, ✨
Devnet breezes help rewards to flow,
Tokens hop from treasury burrow,
Signatures gleam—blocks line in a row.
Approvals squeak, contributions grow—🥕🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title “hi” is completely unrelated to the extensive changes in this pull request and fails to convey any meaningful information about the main modifications. Please provide a concise, descriptive title summarizing the primary change in this PR, for example “Add comprehensive Solana/EOS reward flow with security checks and UI components.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch naveen3

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.

Copy link
Copy Markdown

@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: 13

🧹 Nitpick comments (1)
SOLANA_REWARD_FLOW.md (1)

9-44: Add a language hint to the fenced block.

The flow diagram fence trips MD040 (“fenced-code-language”). Add a language (e.g., text) after the opening backticks to satisfy markdownlint and keep CI green.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d860ea and 95e7cf6.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • EOS/eostoken.jsx (1 hunks)
  • SOLANA_REWARD_FLOW.md (1 hunks)
  • actions/rewardContributorForIssue.js (3 hunks)
  • actions/userProfile.js (3 hunks)
  • api/organization/approve-reward/route.js (1 hunks)
  • app/api/organization/approve-reward/route.js (1 hunks)
  • app/eos-rewards/page.jsx (1 hunks)
  • app/layout.js (1 hunks)
  • app/solana-demo/page.jsx (1 hunks)
  • app/test-inputs/page.jsx (1 hunks)
  • components/ClientOnly.jsx (1 hunks)
  • components/OrganizationApproval.jsx (1 hunks)
  • components/theme-provider.jsx (1 hunks)
  • lib/security-utils.js (1 hunks)
  • lib/solana-utils.js (1 hunks)
  • package.json (1 hunks)
  • prisma/migrations/20251004214857_update_issue_schema_for_rewards/migration.sql (1 hunks)
  • prisma/schema.prisma (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
app/test-inputs/page.jsx (1)
components/OrganizationApproval.jsx (1)
  • OrganizationApproval (19-524)
app/layout.js (1)
components/theme-provider.jsx (1)
  • ThemeProvider (6-18)
lib/security-utils.js (2)
actions/rewardContributorForIssue.js (4)
  • tokenType (105-105)
  • signature (34-34)
  • tokenMintAddress (106-106)
  • user (71-73)
lib/solana-utils.js (10)
  • signature (217-221)
  • signature (285-289)
  • signature (422-426)
  • verifyTransaction (304-314)
  • getTransactionDetails (335-346)
  • balance (50-50)
  • balance (358-358)
  • balance (454-454)
  • treasuryUtils (437-460)
  • treasuryUtils (437-460)
app/api/organization/approve-reward/route.js (2)
lib/security-utils.js (1)
  • verifyTransactionIntegrity (105-156)
actions/userProfile.js (1)
  • issue (204-214)
app/solana-demo/page.jsx (1)
components/OrganizationApproval.jsx (1)
  • OrganizationApproval (19-524)
api/organization/approve-reward/route.js (2)
app/api/organization/approve-reward/route.js (6)
  • POST (7-105)
  • body (9-9)
  • body (10-20)
  • organizationWallet (111-111)
  • issue (39-69)
  • prisma (5-5)
lib/solana-utils.js (1)
  • verifyTransaction (304-314)
actions/rewardContributorForIssue.js (5)
lib/solana-utils.js (10)
  • eosUtils (463-475)
  • eosUtils (463-475)
  • isValidWalletAddress (321-328)
  • signature (217-221)
  • signature (285-289)
  • signature (422-426)
  • sendEosFromTreasury (376-434)
  • sendSolFromTreasury (203-229)
  • sendTokenFromTreasury (238-297)
  • verifyTransaction (304-314)
actions/userProfile.js (3)
  • issue (204-214)
  • user (15-28)
  • user (60-60)
api/organization/approve-reward/route.js (1)
  • issue (38-38)
app/api/organization/approve-reward/route.js (1)
  • issue (39-69)
lib/security-utils.js (3)
  • user (321-324)
  • performSecurityChecks (227-291)
  • detectSuspiciousActivity (298-341)
lib/solana-utils.js (3)
EOS/eostoken.jsx (6)
  • EOS_PRICE_USD (8-8)
  • connection (4-4)
  • connection (12-12)
  • getTokenBalance (10-42)
  • accountInfo (23-23)
  • account (30-30)
components/OrganizationApproval.jsx (1)
  • connection (25-25)
actions/rewardContributorForIssue.js (1)
  • tokenMintAddress (106-106)
app/eos-rewards/page.jsx (4)
EOS/eostoken.jsx (3)
  • metadata (47-47)
  • fetchTokenMetadata (44-53)
  • fetchTokenMetadata (44-53)
components/OrganizationApproval.jsx (2)
  • loading (21-21)
  • OrganizationApproval (19-524)
lib/solana-utils.js (4)
  • treasuryUtils (437-460)
  • treasuryUtils (437-460)
  • eosUtils (463-475)
  • eosUtils (463-475)
components/ClientOnly.jsx (1)
  • ClientOnly (5-17)
components/OrganizationApproval.jsx (2)
lib/solana-utils.js (26)
  • connection (30-30)
  • connection (30-30)
  • connection (118-118)
  • connection (186-186)
  • eosUtils (463-475)
  • eosUtils (463-475)
  • treasuryUtils (437-460)
  • treasuryUtils (437-460)
  • publicKey (49-49)
  • publicKey (66-66)
  • getEosBalance (353-368)
  • balance (50-50)
  • balance (358-358)
  • balance (454-454)
  • getSolBalance (47-56)
  • getTokenBalance (64-94)
  • isValidWalletAddress (321-328)
  • transaction (109-115)
  • transaction (158-158)
  • transaction (209-215)
  • transaction (258-258)
  • transaction (306-308)
  • transaction (337-340)
  • transaction (395-395)
  • createTokenTransferTransaction (137-195)
  • createSolTransferTransaction (103-127)
components/ClientOnly.jsx (1)
  • ClientOnly (5-17)
EOS/eostoken.jsx (1)
lib/solana-utils.js (8)
  • connection (30-30)
  • connection (30-30)
  • connection (118-118)
  • connection (186-186)
  • EOS_PRICE_USD (27-27)
  • getTokenBalance (64-94)
  • accountInfo (75-75)
  • account (81-81)
🪛 Biome (2.1.2)
actions/rewardContributorForIssue.js

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

'performSecurityChecks' is defined here:

(lint/suspicious/noRedeclare)


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

'detectSuspiciousActivity' is defined here:

(lint/suspicious/noRedeclare)

🪛 Gitleaks (8.28.0)
lib/solana-utils.js

[high] 25-25: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 markdownlint-cli2 (0.18.1)
SOLANA_REWARD_FLOW.md

9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment on lines +13 to +15
import { performSecurityChecks, detectSuspiciousActivity } from "@/lib/security-utils";
import { performSecurityChecks, detectSuspiciousActivity } from "@/lib/security-utils";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Remove duplicate import declaration

performSecurityChecks and detectSuspiciousActivity are imported twice, which redeclares the bindings and causes a build-time noRedeclare/syntax error. Drop the duplicate import.

-import { performSecurityChecks, detectSuspiciousActivity } from "@/lib/security-utils";
📝 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
import { performSecurityChecks, detectSuspiciousActivity } from "@/lib/security-utils";
import { performSecurityChecks, detectSuspiciousActivity } from "@/lib/security-utils";
// actions/rewardContributorForIssue.js
import { performSecurityChecks, detectSuspiciousActivity } from "@/lib/security-utils";
🧰 Tools
🪛 Biome (2.1.2)

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

'performSecurityChecks' is defined here:

(lint/suspicious/noRedeclare)


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

'detectSuspiciousActivity' is defined here:

(lint/suspicious/noRedeclare)

🤖 Prompt for AI Agents
In actions/rewardContributorForIssue.js around lines 13 to 15 there is a
duplicate import of performSecurityChecks and detectSuspiciousActivity from
"@/lib/security-utils"; remove the redundant import statement so each symbol is
imported only once, leaving a single import line that brings in both functions.

Comment on lines 29 to +84
if (!issue) return { error: `Issue #${githubIssueId} not found in our database.` };
if (!user) return { error: `User '${githubUsername}' is not registered in our app.` };
if (issue.status !== 'OPEN') return { error: 'Issue is not open for rewards.' };
if (!user.walletAddress) return { error: `User ${githubUsername} has no wallet address.` };
if (issue.tokenReward <= 0) return { error: 'Issue has no token reward set.' };
if (issue.rewardAmount <= 0) return { error: 'Issue has no token reward set.' };

// 3. Security checks
const securityResult = await performSecurityChecks({
userId: user.id,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Allow funded issues through the status guard

The approval flow marks issues as FUNDED (see app/api/organization/approve-reward/route.js), so this guard rejects every funded issue and the reward action never executes. Accept the funded status (or similar) before issuing the payout.

-      if (issue.status !== 'OPEN') return { error: 'Issue is not open for rewards.' };
+      const eligibleStatuses = ['OPEN', 'FUNDED'];
+      if (!eligibleStatuses.includes(issue.status)) {
+        return { error: `Issue status '${issue.status}' is not eligible for rewards.` };
+      }
📝 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
if (!issue) return { error: `Issue #${githubIssueId} not found in our database.` };
if (!user) return { error: `User '${githubUsername}' is not registered in our app.` };
if (issue.status !== 'OPEN') return { error: 'Issue is not open for rewards.' };
if (!user.walletAddress) return { error: `User ${githubUsername} has no wallet address.` };
if (issue.tokenReward <= 0) return { error: 'Issue has no token reward set.' };
if (issue.rewardAmount <= 0) return { error: 'Issue has no token reward set.' };
// 3. Security checks
const securityResult = await performSecurityChecks({
userId: user.id,
if (!issue) return { error: `Issue #${githubIssueId} not found in our database.` };
if (!user) return { error: `User '${githubUsername}' is not registered in our app.` };
const eligibleStatuses = ['OPEN', 'FUNDED'];
if (!eligibleStatuses.includes(issue.status)) {
return { error: `Issue status '${issue.status}' is not eligible for rewards.` };
}
if (!user.walletAddress) return { error: `User ${githubUsername} has no wallet address.` };
if (issue.rewardAmount <= 0) return { error: 'Issue has no token reward set.' };
// 3. Security checks
const securityResult = await performSecurityChecks({
🤖 Prompt for AI Agents
In actions/rewardContributorForIssue.js around lines 76 to 84, the status guard
currently rejects any non-'OPEN' issue which prevents issues marked 'FUNDED' by
the approval flow from proceeding; modify the check to allow 'FUNDED' (and any
equivalent accepted status) by changing the condition to only error when
issue.status is not one of the allowed statuses (e.g., ['OPEN', 'FUNDED']), and
update the error message to reflect that the issue must be in an allowed state
for rewards.

Comment on lines +28 to +35
// Verify the transaction on Solana blockchain
const isTransactionValid = await verifyTransaction(transactionSignature);
if (!isTransactionValid) {
return NextResponse.json(
{ error: "Transaction verification failed" },
{ status: 400 }
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Transaction verification is ineffective.

verifyTransaction (see lib/solana-utils.js) only checks that the signature exists; it never confirms the payer, recipient, or amount. An attacker can therefore submit any old signature and mark an issue as funded without moving funds to the treasury. Use the hardened integrity helper (or fetch the transaction details yourself) and assert the recipient and amount before accepting the approval.

🤖 Prompt for AI Agents
In api/organization/approve-reward/route.js around lines 28-35, the current
verifyTransaction call only checks signature existence and is insufficient;
replace it with a hardened verification that fetches the transaction details (or
use the provided hardened integrity helper) and assert that the transaction
signature matches the supplied signature, the payer and recipient public keys
match the expected payer/treasury addresses, and the transferred lamports/amount
equals the expected reward amount before returning success; if any of these
checks fail, return a 400 with a clear error.

Comment on lines +44 to +55
tokenReward: rewardAmount,
// Add additional fields to store reward metadata
},
});
} else {
// Create new issue (if coming from direct approval flow)
issue = await prisma.issue.create({
data: {
title: issueTitle,
githubIssueId: BigInt(Date.now()), // Temporary - should be actual GitHub issue ID
number: Math.floor(Math.random() * 10000), // Temporary - should be actual issue number
tokenReward: rewardAmount,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Updating tokenReward will crash with the new schema.

The Prisma model now exposes rewardAmount; tokenReward no longer exists. This upsert will throw Unknown arg 'tokenReward' in data.tokenReward, breaking the approval flow. Update both the update and create branches to write rewardAmount (and coerce to a number/Decimal as needed).

🤖 Prompt for AI Agents
In api/organization/approve-reward/route.js around lines 44 to 55, the upsert
uses the removed field tokenReward which will cause Prisma to throw Unknown arg
'tokenReward'; update both the update and create branches to write the new field
rewardAmount instead and ensure you coerce the incoming value to the model type
(e.g., Number or Prisma Decimal) before passing it into data.rewardAmount so the
upsert succeeds.

Comment on lines +57 to +62
repository: {
// This would need to be connected to actual repository
// For now, this is a placeholder
connect: { id: 1 } // You'll need to adjust this based on your repository logic
}
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hard-coded repository linkage corrupts data.

Creating an issue with connect: { id: 1 } will either fail (repo 1 missing) or attach every new issue to the same repository, which is incorrect. Pass the actual repository ID from the request (and validate it) instead of using a placeholder.

Comment on lines +107 to +151
if (formData.tokenType === "EOS") {
// For EOS token, check if token account exists first
const userBalance = await getEosBalance(publicKey.toString());
console.log('User EOS balance:', userBalance);

if (userBalance < amount) {
throw new Error(`Insufficient EOS balance. You have ${userBalance} EOS but trying to transfer ${amount} EOS`);
}

// Use SPL token transfer for EOS
transaction = await createTokenTransferTransaction(
publicKey.toString(),
treasuryAddress,
eosUtils.getMintAddress(),
Math.floor(amount * (10 ** eosUtils.getDecimals())) // Convert to token units
);
} else if (formData.tokenType === "SOL") {
// Check SOL balance
const userBalance = await getSolBalance(publicKey.toString());
console.log('User SOL balance:', userBalance);

if (userBalance < amount + 0.01) { // Include fee
throw new Error(`Insufficient SOL balance. You have ${userBalance} SOL but trying to transfer ${amount} SOL plus fees`);
}

transaction = await createSolTransferTransaction(
publicKey.toString(),
treasuryAddress,
amount
);
} else {
// For other SPL tokens
const userBalance = await getTokenBalance(publicKey.toString(), formData.tokenMintAddress);
console.log('User token balance:', userBalance);

if (userBalance < amount) {
throw new Error(`Insufficient token balance. You have ${userBalance} tokens but trying to transfer ${amount} tokens`);
}

transaction = await createTokenTransferTransaction(
publicKey.toString(),
treasuryAddress,
formData.tokenMintAddress,
amount
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Compare token balances in the correct units before sending

getEosBalance/getTokenBalance return raw token units (lamports). Comparing those lamports directly to the human-entered amount causes the insufficient-balance guard to pass even when the wallet can’t actually cover the transfer (e.g., 1 EOS = 1,000,000 lamports). Parse the on-chain balance (or convert the requested amount to raw units) before the comparison, and do the same for other SPL tokens. Based on lib/solana-utils.js, lines 352-367.

-        const userBalance = await getEosBalance(publicKey.toString());
-        console.log('User EOS balance:', userBalance);
-
-        if (userBalance < amount) {
+        const userBalanceLamports = await getEosBalance(publicKey.toString());
+        const userBalance = eosUtils.parseAmount(userBalanceLamports);
+        console.log('User EOS balance:', userBalance);
+
+        if (userBalance < amount) {
@@
-        const userBalance = await getTokenBalance(publicKey.toString(), formData.tokenMintAddress);
-        console.log('User token balance:', userBalance);
-
-        if (userBalance < amount) {
+        const userBalanceLamports = await getTokenBalance(publicKey.toString(), formData.tokenMintAddress);
+        console.log('User token balance (raw units):', userBalanceLamports);
+        const requiredLamports = amount * Math.pow(10, eosUtils.getDecimals());
+        if (userBalanceLamports < requiredLamports) {

(Adjust the SPL conversion to use the correct decimals for the selected mint.)

Comment on lines 6 to 17
export function ThemeProvider({ children, ...props }) {
const [mounted, setMounted] = React.useState(false);

React.useEffect(() => {
setMounted(true);
}, []);

if (!mounted) {
return children;
}

return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Keep NextThemesProvider mounted on first render.

By returning children before the component mounts, you remove the NextThemesProvider wrapper during the initial render. Any descendant calling useTheme() will now throw (“useTheme must be used within ”), breaking the app at runtime. Please keep the provider present on the first render (e.g., revert to always rendering NextThemesProviderand handle hydration viasuppressHydrationWarning` or a hidden placeholder) so the context is available immediately.

🤖 Prompt for AI Agents
In components/theme-provider.jsx around lines 6 to 17, the current early return
of children before mounted removes the NextThemesProvider on initial render and
breaks useTheme hooks; remove the conditional return so NextThemesProvider is
always rendered, and handle hydration instead by passing
suppressHydrationWarning to the provider or wrapping children in a placeholder
(e.g., render children with visibility hidden or aria-hidden until mounted) so
the provider/context exists immediately while avoiding flicker.

Comment thread EOS/eostoken.jsx
};

const metadataUri = "https://raw.githubusercontent.com/naveenkumar29052006/eos/main/metadata.json";
}const metadataUri = "https://raw.githubusercontent.com/naveenkumar29052006/eos/main/metadata.json";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix syntax error after getTokenBalance definition

metadataUri is declared immediately after the closing brace without a separator (}const ...), which is a syntax error and will prevent this module from compiling. Insert a newline or semicolon before defining metadataUri.

-}const metadataUri = "https://raw.githubusercontent.com/naveenkumar29052006/eos/main/metadata.json";
+}
+
+const metadataUri = "https://raw.githubusercontent.com/naveenkumar29052006/eos/main/metadata.json";
📝 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 metadataUri = "https://raw.githubusercontent.com/naveenkumar29052006/eos/main/metadata.json";
}
const metadataUri = "https://raw.githubusercontent.com/naveenkumar29052006/eos/main/metadata.json";
🤖 Prompt for AI Agents
In EOS/eostoken.jsx around line 42, there is a syntax error where the closing
brace of the previous function and the declaration of metadataUri are run
together ("}const metadataUri..."); separate them by inserting a newline or a
semicolon after the closing brace so metadataUri is declared on its own
statement (e.g., place a newline before const metadataUri or add a semicolon
after the brace).

Comment thread lib/solana-utils.js
Comment on lines +349 to +367
* Get EOS token balance of a wallet
* @param {string} walletAddress - Wallet address to check balance
* @returns {Promise<{balance: number, usdValue: number}>} EOS balance and USD value
*/
export async function getEosBalance(walletAddress) {
try {
if (!walletAddress) {
return 0;
}
const balance = await getTokenBalance(walletAddress, eosUtils.getMintAddress());
return balance || 0;
} catch (error) {
console.error('Error getting EOS balance:', error);
// If it's a token account not found error, return 0
if (error.name === 'TokenAccountNotFoundError' || error.message.includes('TokenAccountNotFoundError')) {
return 0;
}
return 0;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Correct EOS balance scaling and error handling

getTokenBalance returns lamport units, so getEosBalance currently reports balances that are 10⁶ too large and inflates the USD value downstream. The doc comment also promises an object it doesn’t return. Normalize the balance with eosUtils.parseAmount and guard the error handling.

- * @returns {Promise<{balance: number, usdValue: number}>} EOS balance and USD value
+ * @returns {Promise<number>} EOS balance in EOS units
@@
-    const balance = await getTokenBalance(walletAddress, eosUtils.getMintAddress());
-    return balance || 0;
+    const rawBalance = await getTokenBalance(walletAddress, eosUtils.getMintAddress());
+    return eosUtils.parseAmount(rawBalance || 0);
@@
-    if (error.name === 'TokenAccountNotFoundError' || error.message.includes('TokenAccountNotFoundError')) {
+    if (
+      error.name === "TokenAccountNotFoundError" ||
+      error.message?.includes("TokenAccountNotFoundError")
+    ) {
📝 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
* Get EOS token balance of a wallet
* @param {string} walletAddress - Wallet address to check balance
* @returns {Promise<{balance: number, usdValue: number}>} EOS balance and USD value
*/
export async function getEosBalance(walletAddress) {
try {
if (!walletAddress) {
return 0;
}
const balance = await getTokenBalance(walletAddress, eosUtils.getMintAddress());
return balance || 0;
} catch (error) {
console.error('Error getting EOS balance:', error);
// If it's a token account not found error, return 0
if (error.name === 'TokenAccountNotFoundError' || error.message.includes('TokenAccountNotFoundError')) {
return 0;
}
return 0;
}
* Get EOS token balance of a wallet
* @param {string} walletAddress - Wallet address to check balance
* @returns {Promise<number>} EOS balance in EOS units
*/
export async function getEosBalance(walletAddress) {
try {
if (!walletAddress) {
return 0;
}
const rawBalance = await getTokenBalance(walletAddress, eosUtils.getMintAddress());
return eosUtils.parseAmount(rawBalance || 0);
} catch (error) {
console.error('Error getting EOS balance:', error);
// If it's a token account not found error, return 0
if (
error.name === "TokenAccountNotFoundError" ||
error.message?.includes("TokenAccountNotFoundError")
) {
return 0;
}
return 0;
}
}
🤖 Prompt for AI Agents
In lib/solana-utils.js around lines 349 to 367, the function currently returns
raw lamports and a primitive number (or 0) while the docstring promises an
object and the balance must be scaled; update it to return an object { balance,
usdValue } and normalize the raw token amount using
eosUtils.parseAmount(rawBalance) (so lamports -> tokens), return { balance: 0,
usdValue: 0 } when walletAddress is falsy, and when catching errors guard
accesses (check error exists before reading name/message), keep the
TokenAccountNotFoundError branch to return { balance: 0, usdValue: 0 }, and
ensure the caught error is logged via console.error before returning.

Comment on lines +22 to +31
ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN "githubUrl" TEXT,
ADD COLUMN "organizationWallet" TEXT,
ADD COLUMN "rewardAmount" DECIMAL(18,9) NOT NULL,
ADD COLUMN "tokenMintAddress" TEXT,
ADD COLUMN "tokenType" TEXT,
ADD COLUMN "transactionSignature" TEXT,
ADD COLUMN "treasuryWallet" TEXT,
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL,
ALTER COLUMN "id" DROP DEFAULT,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Backfill or default new required columns before altering issues.

rewardAmount and updatedAt are added as NOT NULL with no default, so this migration will fail immediately on any existing data in issues. Please add a default/backfill (e.g., populate with UPDATE ... before enforcing NOT NULL, or add a default such as DEFAULT 0/DEFAULT CURRENT_TIMESTAMP and drop it afterwards) so the migration succeeds on production datasets.

🤖 Prompt for AI Agents
In
prisma/migrations/20251004214857_update_issue_schema_for_rewards/migration.sql
around lines 22 to 31, the migration adds rewardAmount and updatedAt as NOT NULL
with no default which will fail on existing rows; fix by adding the columns as
nullable or with a safe temporary default, backfill existing rows, then enforce
NOT NULL: 1) add rewardAmount with DEFAULT 0 (or nullable) and updatedAt with
DEFAULT CURRENT_TIMESTAMP (or nullable); 2) run an UPDATE to set sensible values
for all existing issues; 3) ALTER the columns to SET NOT NULL; 4) if you used
temporary defaults, ALTER to DROP DEFAULT afterward.

Copy link
Copy Markdown

@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: 2

Caution

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

⚠️ Outside diff range comments (3)
package.json (1)

15-21: Remove duplicate dependency.

@radix-ui/react-slot is declared twice (lines 15 and 21). Remove the duplicate entry to avoid confusion.

Apply this diff to remove the duplicate:

     "@radix-ui/react-avatar": "^1.1.10",
-    "@radix-ui/react-slot": "^1.2.3",

     "@radix-ui/react-dialog": "^1.1.15",
actions/userProfile.js (2)

88-94: Update field reference from tokenReward to rewardAmount.

The code still references issue.tokenReward which was renamed to issue.rewardAmount in the Prisma schema (schema.prisma line 92). This will cause runtime errors.

Apply this diff to fix the field references:

     const contributedRepos = contributions.map(contribution => ({
       repo: contribution.issue.repository.name,
       issue: contribution.issue.title,
-      reward: `${Number(contribution.issue.tokenReward)}x`,
+      reward: `${Number(contribution.issue.rewardAmount)}x`,
       date: new Date(contribution.completedAt).toLocaleDateString('en-GB'), // Formats date as DD/MM/YYYY
       status: 'Success', // Assuming all logged contributions were successful
     }));
 
     // 3. Calculate total earned tokens from the same data
-    const totalEarned = contributions.reduce((sum, c) => sum + Number(c.issue.tokenReward), 0);
+    const totalEarned = contributions.reduce((sum, c) => sum + Number(c.issue.rewardAmount), 0);

138-138: Update SQL query to use rewardAmount column.

The raw SQL query still references the tokenReward column which was renamed to rewardAmount in the database schema. This will cause a SQL error at runtime.

Apply this diff to fix the column reference:

       SELECT
         u."githubUsername" AS "name",
         u."githubAvatarUrl" AS "photo",
-        SUM(i."tokenReward") AS "points",
+        SUM(i."rewardAmount") AS "points",
         COUNT(c.id) AS "issuesSolved"
       FROM "users" u
       JOIN "contributions" c ON u.id = c."contributorId"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95e7cf6 and dd2fde7.

📒 Files selected for processing (3)
  • actions/userProfile.js (1 hunks)
  • package.json (1 hunks)
  • prisma/schema.prisma (3 hunks)
🔇 Additional comments (4)
package.json (1)

12-12: Inconsistent with AI-generated summary.

The summary indicates that @clerk/nextjs was removed, but it's still present in the dependencies. Verify whether this package should remain or be removed.

prisma/schema.prisma (2)

22-22: LGTM!

The addition of the FUNDED status to the IssueStatus enum aligns with the reward workflow changes introduced in this PR.


92-97: LGTM!

The new reward-related fields (rewardAmount, tokenType, tokenMintAddress, organizationWallet, treasuryWallet, transactionSignature) appropriately support the reward workflow. The Decimal(18, 9) precision for rewardAmount is suitable for token amounts.

actions/userProfile.js (1)

232-238: LGTM!

The field name change from tokenReward to rewardAmount in setIssueBounty correctly aligns with the Prisma schema updates.

Comment thread prisma/schema.prisma

repoId Int
repository Repository @relation(fields: [repoId], references: [id], onDelete: Cascade)
id String @id @default(cuid()) // Changed to String to support custom IDs
Copy link
Copy Markdown

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

Verify data migration for id type change.

Changing the Issue.id from Int @id @default(autoincrement()) to String @id @default(cuid()) is a breaking change that affects existing data and foreign key relationships.

Ensure that:

  1. A migration strategy exists to convert existing integer IDs to string IDs
  2. All foreign key references (e.g., Contribution.issueId at line 114) are updated consistently
  3. Application code that queries or manipulates Issue.id is updated to handle string IDs

Run the following script to verify all references to Issue.id in the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Find all references to Issue.id to verify they handle string IDs correctly

# Search for Issue.id references
rg -nP --type=js --type=ts -C3 '\bIssue\.id\b|\bissueId\b'

# Search for Prisma queries that filter by id
rg -nP --type=js --type=ts -C3 'issue.*where.*\bid\b'

# Search for numeric comparisons that might break with string IDs
ast-grep --pattern 'issue.id $_ $_'

Length of output: 7754


🏁 Script executed:

#!/bin/bash
if [ -d "prisma/migrations" ]; then
  echo "Migration directories:"
  find prisma/migrations -maxdepth 2 -type d
else
  echo "No prisma/migrations directory found"
fi

Length of output: 143


🏁 Script executed:

#!/bin/bash
cat prisma/migrations/20251004214857_update_issue_schema_for_rewards/migration.sql

Length of output: 1745


Fix migration and code references for string IDs and new fields.

  • Migration 20251004214857_update_issue_schema_for_rewards adds rewardAmount and updatedAt as NOT NULL without defaults or backfill—add DEFAULT clauses or a data-backfill step to avoid migration failures.
  • In api/organization/approve-reward/route.js (line 42), remove parseInt(issueId)—use the string ID directly—and replace the obsolete tokenReward reference with rewardAmount.
  • Audit all other Prisma calls/filtering on Issue.id to ensure they handle string IDs.
🤖 Prompt for AI Agents
In prisma/schema.prisma around line 86 (Issue.id changed to String via
@default(cuid())), update the migration
20251004214857_update_issue_schema_for_rewards to avoid NOT NULL failures by
either adding DEFAULT values for rewardAmount and updatedAt (e.g., DEFAULT 0 for
numeric and DEFAULT now() for timestamp) or include an explicit backfill step in
the migration that sets existing rows to a safe value before making the columns
NOT NULL; in api/organization/approve-reward/route.js at line ~42 remove
parseInt(issueId) and treat issueId as the string ID, replace any usage of
tokenReward with rewardAmount, and ensure the Prisma query uses the string ID;
finally audit all other Prisma queries/filters that reference Issue.id to accept
string IDs (remove parseInt/casts, update typings if necessary) and ensure any
clients or tests that assumed numeric IDs are updated accordingly.

Comment thread prisma/schema.prisma
Comment on lines +88 to +89
githubIssueId BigInt? @unique // Made optional
number Int? // Made optional
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify handling of newly optional fields.

Several fields have been made optional (githubIssueId, number, repoId) and the repository relation is now nullable. Ensure that application code properly handles these optional fields.

Run the following script to find code that might assume these fields are non-null:

Also applies to: 101-102


🏁 Script executed:

#!/bin/bash
# Description: Find code that accesses these fields without null checks

# Search for direct property access without null checks
rg -nP --type=js --type=ts -C3 'issue\.githubIssueId(?!\?)' 
rg -nP --type=js --type=ts -C3 'issue\.number(?!\?)'
rg -nP --type=js --type=ts -C3 'issue\.repository\.(?!\?)' 

# Search for Prisma queries that assume these fields exist
rg -nP --type=js --type=ts -C3 'where.*githubIssueId.*:'

Length of output: 643


🏁 Script executed:

#!/bin/bash
# Search for direct access to optional fields number and repository
rg -n --type js --type ts 'issue\.number' -C3
rg -n --type js --type ts 'issue\.repository' -C3

# Search for Prisma queries assuming non-null number or repoId
rg -n --type js --type ts 'where.*number.*:' -C3
rg -n --type js --type ts 'where.*repoId.*:' -C3

Length of output: 1012


Guard accesses to newly optional fields

  • prisma upsert in actions/userProfile.js (ln.231–234) uses githubIssueId and repoId without null checks
  • rewardContributorForIssue.js (ln.114) accesses issue.number unconditionally
  • userProfile.js (ln.86) reads contribution.issue.repository.name assuming non-null repository

Validate or default these fields before use.

🤖 Prompt for AI Agents
In prisma/schema.prisma around lines 88–89 the fields githubIssueId and number
were made optional; update the call sites: in actions/userProfile.js (around
lines 231–234) guard githubIssueId and repoId before including them in the
Prisma upsert—either omit those fields from the upsert data when null/undefined
or provide safe defaults; in rewardContributorForIssue.js (around line 114)
check that issue.number is not null/undefined before using it and handle the
missing-number case (early return or alternative flow); in userProfile.js
(around line 86) validate that contribution.issue.repository exists before
accessing .name (use optional chaining or an explicit null check and provide a
fallback). Ensure all changes avoid runtime null dereferences and only pass
defined values into Prisma operations.

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.

1 participant