Integrate Wallet Verification System#139
Conversation
📝 WalkthroughWalkthroughThis PR refactors the wallet verification system by removing local state management from the settings page, simplifying verification source to rely solely on userData, and substantially redesigning the WalletVerification component to implement a wallet connectivity-driven flow with OTP stages and updated API service types. Changes
Sequence DiagramsequenceDiagram
participant User
participant WalletExt as Wallet Extension
participant WalletVerification as WalletVerification Component
participant API as Backend API
User->>WalletVerification: Click Connect Wallet
WalletVerification->>WalletExt: Request Connection
WalletExt->>WalletExt: User Approves
WalletExt->>WalletVerification: Connected + Address
WalletVerification->>API: handleInitiate() - Request Verification Code
API->>WalletVerification: verificationToken + status
User->>WalletVerification: Enter OTP Code
WalletVerification->>API: handleVerify(token, code)
API->>WalletVerification: Verification Success
WalletVerification->>User: Display Confirmed Status
WalletVerification->>WalletVerification: Trigger onVerificationComplete()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In
`@paystell-frontend/src/components/wallet/walletconnect/WalletVerification.tsx`:
- Around line 247-252: The CONNECT-stage verify button currently calls
handleInitiate but does nothing if userId is undefined; update the UI and
handler to guard against this by disabling or preventing the click when userId
is missing and adding an explicit check inside handleInitiate (or the initiating
function) that returns early and surfaces an error/notification. Specifically,
in the block handling stage === STAGES.CONNECT around the Button, bind a
disabled prop (or hidden/tooltip) based on !userId and/or avoid calling
handleInitiate when userId is falsy, and add a userId presence check at the
start of handleInitiate to early-return and trigger an error toast/log so the
no-op is not silent.
- Around line 260-270: The OTP input currently accepts any characters; update
the Input onChange handler for the element with id="code" (where value={code}
and setCode is called) to strip non-digit characters and only allow up to 6
digits (e.g., filter e.target.value to digits before calling setCode), and
ensure the Verify button (onClick={handleVerify}) remains disabled unless
code.length === 6 and consists only of digits; this enforces client-side
numeric-only OTP input and prevents invalid submissions to the backend.
- Around line 272-274: In WalletVerification.tsx update the Button onClick that
calls setStage(STAGES.CONNECT) so it also clears any lingering verification
state before restarting (e.g. reset token/code/error and related flags in this
component) by invoking the appropriate setters (for example setToken(null),
setVerificationCode(''), setVerificationError(null) or whatever state variables
are used) so a new connect attempt starts with a clean state; keep the call to
setStage(STAGES.CONNECT) as well.
🧹 Nitpick comments (1)
paystell-frontend/src/components/wallet/walletconnect/WalletVerification.tsx (1)
136-139: Remove the no-op effect.
It currently does nothing and adds noise.♻️ Proposed cleanup
-useEffect(() => { - if (stage === STAGES.CONNECT && isConnected && publicKey) { - } -}, [isConnected, publicKey, stage]);
| if (stage === STAGES.CONNECT) { | ||
| return ( | ||
| <Button className="w-full" onClick={handleInitiate}> | ||
| <MdShield className="w-4 h-4 mr-2" /> Verify Wallet Ownership | ||
| </Button> | ||
| ); |
There was a problem hiding this comment.
Guard against a silent no-op when userId is missing.
Right now the button can be clicked and nothing happens if userId is undefined.
✅ Proposed guard
- if (stage === STAGES.CONNECT) {
- return (
- <Button className="w-full" onClick={handleInitiate}>
+ if (stage === STAGES.CONNECT) {
+ return (
+ <Button className="w-full" onClick={handleInitiate} disabled={!userId}>
<MdShield className="w-4 h-4 mr-2" /> Verify Wallet Ownership
</Button>
);
}📝 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.
| if (stage === STAGES.CONNECT) { | |
| return ( | |
| <Button className="w-full" onClick={handleInitiate}> | |
| <MdShield className="w-4 h-4 mr-2" /> Verify Wallet Ownership | |
| </Button> | |
| ); | |
| if (stage === STAGES.CONNECT) { | |
| return ( | |
| <Button className="w-full" onClick={handleInitiate} disabled={!userId}> | |
| <MdShield className="w-4 h-4 mr-2" /> Verify Wallet Ownership | |
| </Button> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In `@paystell-frontend/src/components/wallet/walletconnect/WalletVerification.tsx`
around lines 247 - 252, The CONNECT-stage verify button currently calls
handleInitiate but does nothing if userId is undefined; update the UI and
handler to guard against this by disabling or preventing the click when userId
is missing and adding an explicit check inside handleInitiate (or the initiating
function) that returns early and surfaces an error/notification. Specifically,
in the block handling stage === STAGES.CONNECT around the Button, bind a
disabled prop (or hidden/tooltip) based on !userId and/or avoid calling
handleInitiate when userId is falsy, and add a userId presence check at the
start of handleInitiate to early-return and trigger an error toast/log so the
no-op is not silent.
| <Input | ||
| id="code" | ||
| placeholder="Enter 6-digit code" | ||
| value={code} | ||
| onChange={(e) => setCode(e.target.value)} | ||
| className="text-center tracking-widest text-lg font-bold" | ||
| maxLength={6} | ||
| /> | ||
| </div> | ||
| <Button className="w-full" onClick={handleVerify} disabled={code.length < 6}> | ||
| <MdCheckCircle className="w-4 h-4 mr-2" /> Verify Code |
There was a problem hiding this comment.
OTP input should enforce digits.
Currently any characters are accepted; backend will reject, but client-side validation should prevent that.
✅ Proposed validation
<Input
id="code"
placeholder="Enter 6-digit code"
value={code}
- onChange={(e) => setCode(e.target.value)}
+ onChange={(e) => setCode(e.target.value.replace(/\D/g, ''))}
className="text-center tracking-widest text-lg font-bold"
maxLength={6}
+ inputMode="numeric"
+ pattern="\d*"
/>📝 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.
| <Input | |
| id="code" | |
| placeholder="Enter 6-digit code" | |
| value={code} | |
| onChange={(e) => setCode(e.target.value)} | |
| className="text-center tracking-widest text-lg font-bold" | |
| maxLength={6} | |
| /> | |
| </div> | |
| <Button className="w-full" onClick={handleVerify} disabled={code.length < 6}> | |
| <MdCheckCircle className="w-4 h-4 mr-2" /> Verify Code | |
| <Input | |
| id="code" | |
| placeholder="Enter 6-digit code" | |
| value={code} | |
| onChange={(e) => setCode(e.target.value.replace(/\D/g, ''))} | |
| className="text-center tracking-widest text-lg font-bold" | |
| maxLength={6} | |
| inputMode="numeric" | |
| pattern="\d*" | |
| /> | |
| </div> | |
| <Button className="w-full" onClick={handleVerify} disabled={code.length < 6}> | |
| <MdCheckCircle className="w-4 h-4 mr-2" /> Verify Code |
🤖 Prompt for AI Agents
In `@paystell-frontend/src/components/wallet/walletconnect/WalletVerification.tsx`
around lines 260 - 270, The OTP input currently accepts any characters; update
the Input onChange handler for the element with id="code" (where value={code}
and setCode is called) to strip non-digit characters and only allow up to 6
digits (e.g., filter e.target.value to digits before calling setCode), and
ensure the Verify button (onClick={handleVerify}) remains disabled unless
code.length === 6 and consists only of digits; this enforces client-side
numeric-only OTP input and prevents invalid submissions to the backend.
| <Button variant="ghost" className="w-full text-xs" onClick={() => setStage(STAGES.CONNECT)}> | ||
| Change Wallet or Restart | ||
| </Button> |
There was a problem hiding this comment.
Reset token/code/error when restarting.
Otherwise the prior code/token can linger and confuse the next attempt.
✅ Proposed reset
- <Button variant="ghost" className="w-full text-xs" onClick={() => setStage(STAGES.CONNECT)}>
+ <Button
+ variant="ghost"
+ className="w-full text-xs"
+ onClick={() => {
+ setStage(STAGES.CONNECT);
+ setCode('');
+ setVerificationToken(null);
+ setError(null);
+ localStorage.removeItem('wallet_verification_token');
+ }}
+ >
Change Wallet or Restart
</Button>📝 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.
| <Button variant="ghost" className="w-full text-xs" onClick={() => setStage(STAGES.CONNECT)}> | |
| Change Wallet or Restart | |
| </Button> | |
| <Button | |
| variant="ghost" | |
| className="w-full text-xs" | |
| onClick={() => { | |
| setStage(STAGES.CONNECT); | |
| setCode(''); | |
| setVerificationToken(null); | |
| setError(null); | |
| localStorage.removeItem('wallet_verification_token'); | |
| }} | |
| > | |
| Change Wallet or Restart | |
| </Button> |
🤖 Prompt for AI Agents
In `@paystell-frontend/src/components/wallet/walletconnect/WalletVerification.tsx`
around lines 272 - 274, In WalletVerification.tsx update the Button onClick that
calls setStage(STAGES.CONNECT) so it also clears any lingering verification
state before restarting (e.g. reset token/code/error and related flags in this
component) by invoking the appropriate setters (for example setToken(null),
setVerificationCode(''), setVerificationError(null) or whatever state variables
are used) so a new connect attempt starts with a clean state; keep the call to
setStage(STAGES.CONNECT) as well.
|
Hi @MPSxDev |
📌 Pull Request Description
📝 Summary
This PR integrates the backend Wallet Verification System into the frontend application. It enables users to securely connect their Stellar wallets and verify ownership through an email-based OTP (One-Time Password) flow, delivering a clear, guided, and production-ready verification experience.
🔗 Related Issues
Closes #114
🔄 Changes Made
This PR introduces a full end-to-end wallet verification flow aligned with the backend API and business logic.
Service Layer
wallet-verification.tsto fully match backend specifications.initiateVerificationandverifyWallet.Components
WalletVerification.tsxuseWallethook to detect wallet connection state and retrieve the Stellar public key.showCardprop to optionally disable the outer container for flexible embedding.WalletVerificationSection.tsxWalletVerificationcomponent withshowCard={false}for seamless integration into the settings dashboard.Pages
app/dashboard/settings/page.tsxuseWallethooks and local verification state.🖼️ Current Output
🧪 Testing
✅ Testing Checklist
Summary by CodeRabbit
Release Notes
New Features
Updates
✏️ Tip: You can customize this high-level summary in your review settings.