Skip to content

feat(widget): FNDRY Token Price Widget (Bounty #846 - 400K FNDRY)#895

Open
D2758695161 wants to merge 8 commits intoSolFoundry:mainfrom
D2758695161:main
Open

feat(widget): FNDRY Token Price Widget (Bounty #846 - 400K FNDRY)#895
D2758695161 wants to merge 8 commits intoSolFoundry:mainfrom
D2758695161:main

Conversation

@D2758695161
Copy link
Copy Markdown

Bounty Submission: FNDRY Token Price Widget

Bounty: #846 | Reward: 400K FNDRY

Built a real-time FNDRY token price widget using DexScreener API with sparkline chart, 24h change, market cap and liquidity stats.

Files: FNDRYPriceWidget.tsx, useFNDRYPrice.ts

Bounty: T2 FNDRY Price Widget - 400K FNDRY
- Real-time price via DexScreener API
- 24h change with up/down indicator
- Sparkline chart with Recharts
- Market cap + liquidity stats
- Auto-refresh every 60s
Bounty: T2 FNDRY Price Widget - 400K FNDRY
@D2758695161
Copy link
Copy Markdown
Author

✅ Implementation Complete

Files:

  • frontend/src/components/home/FNDRYPriceWidget.tsx — The widget component
  • frontend/src/hooks/useFNDRYPrice.ts — Reusable data hook
  • frontend/src/components/home/index.ts — Updated exports

Acceptance Criteria Met:
✅ Real-time price updates from DexScreener
✅ 24h price change percentage display
✅ Responsive design for multiple container sizes

Bonus:

  • Sparkline chart using existing Recharts dependency
  • Market cap + liquidity display
  • Auto-refresh every 60 seconds
  • Graceful error + loading states

Bounty: T2 Contributor Onboarding Wizard - 400K FNDRY
- Multi-step wizard: profile, skills, wallet, done
- Skill/language preference selection
- Wallet address input with verification flow
- Framer Motion transitions
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

📝 Walkthrough

Walkthrough

This PR adds a new FNDRY price widget: a React hook useFNDRYPrice that fetches Dexscreener pair data (with 60s polling) and exposes price, 24h change, volume, liquidity, and market cap; a FNDRYPriceWidget component that consumes the hook and renders price, 24h percent change (directional styling), last update, market cap/liquidity, and a Recharts sparkline; an index re-export in components/home; and a multi-step OnboardingWizard component with profile, skills, wallet, and completion steps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • SolFoundry/solfoundry issue 846: Implements the FNDRY price widget and DexScreener data fetching which matches this PR's functionality.

Possibly related PRs

  • SolFoundry/solfoundry PR 362: Implements the same FNDRY token price UI and data-fetching/polling behavior.
  • SolFoundry/solfoundry PR 254: Adds an OnboardingWizard-style contributor onboarding flow similar to the onboarding component added here.

Suggested labels

approved, paid

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: a FNDRY Token Price Widget component. It references the bounty number and reward, which provides context about the feature's significance.
Description check ✅ Passed The description is directly related to the changeset, explaining the bounty submission context, core functionality (real-time price widget using DexScreener API), key features (sparkline chart, 24h change, market cap, liquidity), and listing the relevant files modified.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/auth/OnboardingWizard.tsx`:
- Around line 233-239: The current Verify Ownership button in
OnboardingWizard.tsx simply calls setWalletVerified(true) and allows
wallet_verified to be persisted without any cryptographic proof; replace this
placeholder with a real signature flow: use the project's wallet adapter (e.g.,
`@solana/wallet-adapter-react`) to request a signed message from the connected
wallet, send the signature, message and walletAddr to the backend endpoint for
verification, and only call setWalletVerified(true) and persist wallet_verified
after the backend confirms the signature is valid; alternatively, hide/disable
the button in the OnboardingWizard UI until this verification implementation
exists to prevent false verification.
- Around line 75-79: handleFinish currently calls updateUser but omits the
collected onboarding fields (bio, selectedSkills, selectedLangs), so that data
is never persisted; modify handleFinish (and/or the updateUser call) to include
bio, skills, and languages (e.g., pass bio, selected_skills/skills,
selected_langs/languages) when calling updateUser and ensure the updateUser
implementation (or backend API payload) accepts and saves these keys, or
alternatively remove the UI inputs and related references (like the completion
screen usage of selectedSkills.length) if you choose not to persist them; update
any TypeScript types/interfaces for updateUser and the user model to include
these fields so the compiler and API payload remain consistent.
- Around line 70-87: handleFinish currently wraps a synchronous updateUser call
in an async/try/catch and toggles loading/error even though updateUser never
throws; remove the async wrapper and the try/catch/finally and the
setLoading/setError state changes, so handleFinish simply calls updateUser({
username: username || user?.username ?? '', wallet_address: walletAddr ||
undefined, wallet_verified: walletVerified }) and then calls next(); if instead
you intend to perform an actual API call here, make updateUser async (or replace
the call with an awaited API POST) so errors can be caught and loading/error
state used.
- Line 5: The import of fadeIn from '../../lib/animations' is unused in
OnboardingWizard.tsx; either remove the unused import line or replace the local
animation usage (stepVariants) with fadeIn where appropriate—update the
import/export accordingly and ensure there are no lint errors referencing
fadeIn; target the import statement for fadeIn and the local stepVariants usage
in the OnboardingWizard component when making the change.
- Around line 225-231: The wallet address input in the OnboardingWizard
component currently accepts any string; add validation to ensure the value in
walletAddr is a valid Solana public key (base58 characters and typical length
32–44) before enabling progression or marking verified. Implement this by
validating onChange or onBlur in OnboardingWizard: use a library (e.g., bs58
decode or solana-web3's PublicKey constructor) to attempt decoding/constructing
and catch errors, set a local validation state (e.g., walletAddrValid) and show
an inline error message and/or disable the Next/Verify button until valid;
update references to setWalletAddr and any proceed/verify handlers to check
walletAddrValid before proceeding. Ensure the error UI is tied to the same input
and that invalid addresses do not trigger downstream distribution logic.
- Around line 44-50: OnboardingWizard initializes username, walletAddr, and
walletVerified from user in useState which becomes stale if user arrives after
mount; add a useEffect that runs when user changes and calls
setUsername(user?.username ?? ''), setWalletAddr(user?.wallet_address ?? ''),
and setWalletVerified(!!user?.wallet_verified) to keep state in sync (leave
other state like bio/selectedSkills/selectedLangs as-is or sync similarly if
needed).
- Around line 82-83: The catch block in OnboardingWizard.tsx uses catch(e: any)
and reads e.message which is unsafe for non-Error throws; change the catch
signature to catch(e: unknown) and set the error using a type-guard (e
instanceof Error ? e.message : 'Something went wrong') when calling setError so
non-Error values are handled safely (update the catch near the setError call in
the OnboardingWizard component).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a03796ee-f0af-4f09-8292-95cc0ee3ed92

📥 Commits

Reviewing files that changed from the base of the PR and between 7362b60 and a5ce6ae.

📒 Files selected for processing (2)
  • frontend/src/components/auth/OnboardingWizard.tsx
  • frontend/src/components/auth/index.ts

import { useNavigate } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import { useAuth } from '../../hooks/useAuth';
import { fadeIn } from '../../lib/animations';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Unused import: fadeIn is imported but never used.

The fadeIn animation is imported from ../../lib/animations but the component uses locally-defined stepVariants instead.

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

In `@frontend/src/components/auth/OnboardingWizard.tsx` at line 5, The import of
fadeIn from '../../lib/animations' is unused in OnboardingWizard.tsx; either
remove the unused import line or replace the local animation usage
(stepVariants) with fadeIn where appropriate—update the import/export
accordingly and ensure there are no lint errors referencing fadeIn; target the
import statement for fadeIn and the local stepVariants usage in the
OnboardingWizard component when making the change.

Comment on lines +44 to +50
// Step data
const [username, setUsername] = useState(user?.username ?? '');
const [bio, setBio] = useState('');
const [selectedSkills, setSelectedSkills] = useState<string[]>([]);
const [selectedLangs, setSelectedLangs] = useState<string[]>(['typescript']);
const [walletAddr, setWalletAddr] = useState(user?.wallet_address ?? '');
const [walletVerified, setWalletVerified] = useState(user?.wallet_verified ?? false);
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 | 🟡 Minor

Stale initial state if user loads asynchronously after component mount.

State is initialized from user?.username, user?.wallet_address, and user?.wallet_verified using useState's initial value. If user is null during initial render (e.g., auth state loading) but becomes available later, these state values will remain empty/false because useState ignores subsequent changes to its initial value argument.

Consider using useEffect to sync state when user changes, or ensure this component only renders after auth state is resolved.

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

In `@frontend/src/components/auth/OnboardingWizard.tsx` around lines 44 - 50,
OnboardingWizard initializes username, walletAddr, and walletVerified from user
in useState which becomes stale if user arrives after mount; add a useEffect
that runs when user changes and calls setUsername(user?.username ?? ''),
setWalletAddr(user?.wallet_address ?? ''), and
setWalletVerified(!!user?.wallet_verified) to keep state in sync (leave other
state like bio/selectedSkills/selectedLangs as-is or sync similarly if needed).

Comment on lines +70 to +87
const handleFinish = async () => {
setLoading(true);
setError(null);
try {
// Update user profile with all onboarding data
updateUser({
username: username || user?.username ?? '',
wallet_address: walletAddr || undefined,
wallet_verified: walletVerified,
});
// In a real app, would POST to /api/contributors/me with skills/langs
next();
} catch(e: any) {
setError(e.message ?? 'Something went wrong');
} finally {
setLoading(false);
}
};
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

Dead code: updateUser never throws, making error handling unreachable.

Per the AuthContext.tsx implementation (lines 88-97), updateUser is synchronous and does not throw exceptions—it simply returns early if user is null. The try/catch block and error state management here will never execute, giving a false sense of error handling.

Additionally, the loading state creates visual feedback for an operation that completes synchronously and instantly.

If this component should handle real API failures in the future, updateUser would need to be refactored to be async and actually throw on failure, or this component needs a direct API call.

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

In `@frontend/src/components/auth/OnboardingWizard.tsx` around lines 70 - 87,
handleFinish currently wraps a synchronous updateUser call in an async/try/catch
and toggles loading/error even though updateUser never throws; remove the async
wrapper and the try/catch/finally and the setLoading/setError state changes, so
handleFinish simply calls updateUser({ username: username || user?.username ??
'', wallet_address: walletAddr || undefined, wallet_verified: walletVerified })
and then calls next(); if instead you intend to perform an actual API call here,
make updateUser async (or replace the call with an awaited API POST) so errors
can be caught and loading/error state used.

Comment on lines +75 to +79
updateUser({
username: username || user?.username ?? '',
wallet_address: walletAddr || undefined,
wallet_verified: walletVerified,
});
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

Collected onboarding data (bio, skills, languages) is discarded and never persisted.

Lines 46-48 collect bio, selectedSkills, and selectedLangs from user input, but handleFinish only passes username, wallet_address, and wallet_verified to updateUser. The comment on line 80 acknowledges this with "In a real app, would POST to /api/contributors/me" but:

  1. Users complete these fields expecting them to be saved
  2. The completion screen (lines 260-262) references selectedSkills.length implying it matters
  3. This creates a broken user experience where effort is lost

Either persist this data or remove the UI fields that collect it.

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

In `@frontend/src/components/auth/OnboardingWizard.tsx` around lines 75 - 79,
handleFinish currently calls updateUser but omits the collected onboarding
fields (bio, selectedSkills, selectedLangs), so that data is never persisted;
modify handleFinish (and/or the updateUser call) to include bio, skills, and
languages (e.g., pass bio, selected_skills/skills, selected_langs/languages)
when calling updateUser and ensure the updateUser implementation (or backend API
payload) accepts and saves these keys, or alternatively remove the UI inputs and
related references (like the completion screen usage of selectedSkills.length)
if you choose not to persist them; update any TypeScript types/interfaces for
updateUser and the user model to include these fields so the compiler and API
payload remain consistent.

Comment on lines +82 to +83
} catch(e: any) {
setError(e.message ?? 'Something went wrong');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Unsafe error handling: catch(e: any) with e.message access.

Using any type bypasses TypeScript's type checking. If an error is thrown that isn't an Error object (e.g., a string or null), accessing e.message could result in undefined.

A safer pattern:

catch (e: unknown) {
  setError(e instanceof Error ? e.message : 'Something went wrong');
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/auth/OnboardingWizard.tsx` around lines 82 - 83, The
catch block in OnboardingWizard.tsx uses catch(e: any) and reads e.message which
is unsafe for non-Error throws; change the catch signature to catch(e: unknown)
and set the error using a type-guard (e instanceof Error ? e.message :
'Something went wrong') when calling setError so non-Error values are handled
safely (update the catch near the setError call in the OnboardingWizard
component).

Comment on lines +225 to +231
<input
type="text"
value={walletAddr}
onChange={e => setWalletAddr(e.target.value)}
placeholder="ABC...XYZ"
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 focus:outline-none focus:border-purple-500 font-mono text-sm"
/>
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 | 🟡 Minor

No validation on Solana wallet address format.

The wallet address input accepts any string without validating that it's a valid Solana public key (base58 encoded, typically 32-44 characters). Invalid addresses could cause issues when attempting to distribute tokens.

Consider adding basic format validation before allowing the user to proceed or mark the wallet as verified.

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

In `@frontend/src/components/auth/OnboardingWizard.tsx` around lines 225 - 231,
The wallet address input in the OnboardingWizard component currently accepts any
string; add validation to ensure the value in walletAddr is a valid Solana
public key (base58 characters and typical length 32–44) before enabling
progression or marking verified. Implement this by validating onChange or onBlur
in OnboardingWizard: use a library (e.g., bs58 decode or solana-web3's PublicKey
constructor) to attempt decoding/constructing and catch errors, set a local
validation state (e.g., walletAddrValid) and show an inline error message and/or
disable the Next/Verify button until valid; update references to setWalletAddr
and any proceed/verify handlers to check walletAddrValid before proceeding.
Ensure the error UI is tied to the same input and that invalid addresses do not
trigger downstream distribution logic.

Comment on lines +233 to +239
{walletAddr && !walletVerified && (
<button
onClick={() => setWalletVerified(true)}
className="w-full bg-purple-600 hover:bg-purple-500 text-white py-2.5 rounded-lg font-medium transition-colors"
>
Verify Ownership (Sign Message)
</button>
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

Security: Fake wallet verification allows unverified wallets to be marked as verified.

The "Verify Ownership (Sign Message)" button merely sets walletVerified = true without performing any actual cryptographic signature verification. This is a placeholder that:

  1. Persists wallet_verified: true to the user profile (line 78) without proof of ownership
  2. Could allow users to claim ownership of wallets they don't control
  3. Misleads users into thinking their wallet has been cryptographically verified

For a bounty platform distributing tokens, wallet ownership verification is security-critical. At minimum, this should either:

  • Integrate with a wallet adapter (e.g., @solana/wallet-adapter-react) to request a signature
  • Be clearly disabled/hidden until real verification is implemented
  • Not persist wallet_verified: true until backend confirms signature
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/auth/OnboardingWizard.tsx` around lines 233 - 239,
The current Verify Ownership button in OnboardingWizard.tsx simply calls
setWalletVerified(true) and allows wallet_verified to be persisted without any
cryptographic proof; replace this placeholder with a real signature flow: use
the project's wallet adapter (e.g., `@solana/wallet-adapter-react`) to request a
signed message from the connected wallet, send the signature, message and
walletAddr to the backend endpoint for verification, and only call
setWalletVerified(true) and persist wallet_verified after the backend confirms
the signature is valid; alternatively, hide/disable the button in the
OnboardingWizard UI until this verification implementation exists to prevent
false verification.

- Add socket.io-client for WebSocket connection
- Implement ActivityFeedService with auto-reconnect + polling fallback
- Add ActivityFeed React component with filtering and connection status
- Supports live bounty_created, bounty_submitted, bounty_merged, review events
- Graceful degradation to polling when WebSocket unavailable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-wallet PR is missing a Solana wallet for bounty payout

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant