Skip to content

feat: deposit wizard, OAuth recovery, PnL normalization, fragmentation edge cases (#503 #817 #818 #820)#876

Merged
edehvictor merged 1 commit into
edehvictor:mainfrom
Abidoyesimze:feat/503-817-818-820-deposit-wizard-oauth-pnl-fragmentation
Jun 26, 2026
Merged

feat: deposit wizard, OAuth recovery, PnL normalization, fragmentation edge cases (#503 #817 #818 #820)#876
edehvictor merged 1 commit into
edehvictor:mainfrom
Abidoyesimze:feat/503-817-818-820-deposit-wizard-oauth-pnl-fragmentation

Conversation

@Abidoyesimze

Copy link
Copy Markdown
Contributor

Summary

This PR delivers four related features across the advisor UI, Google Sheets integration, fragmentation analytics, and PnL charting:

Closes #503
Closes #817
Closes #818
Closes #820


#503 — Guided deposit recommendation wizard

Problem: AIAdvisor.tsx was a placeholder with no wizard or real vault ranking.

Solution:

  • Added DepositRecommendationWizard.tsx — 4-step UI integrated into the AI Advisor page.
  • Added depositRecommendationService.ts — reuses rankStrategies / computeRiskAdjustedYield with profile mapping (aggressivetolerant drawdown model).
  • Replaced stub POST /api/recommend with real scoring; each recommendation includes rank, RAY, APY, risk score, TVL, and a human-readable explanation.
  • Extended RecommendationInputSnapshot with timeHorizon and liquidityNeeds.

Acceptance criteria:

  • Multi-step wizard UI
  • Risk-adjusted yield scoring logic reused
  • Explanations for why each vault was selected
  • Tests for conservative, balanced, and aggressive profiles

#817 — Fragmentation calculator degenerate inputs

Problem: Zero-liquidity pools, extreme skew, and repeated recomputation could produce unstable or misleading fragmentation metrics.

Solution:

  • Added normalizeProtocols() in FragmentationCalculator to filter zero-TVL entries, deduplicate protocol names, and reject non-finite TVL (NaN / Infinity).
  • estimateMultiProtocolRouting() now counts only active (positive-TVL) protocols.
  • Throws a clear error when all pools have zero liquidity.

Acceptance criteria:

  • Zero-liquidity pools handled predictably
  • Highly skewed and dispersed inputs produce bounded output
  • Repeated recomputation is deterministic (tested)
  • Edge-case tests added

#818 — Google Sheets OAuth recovery

Problem: Expired access tokens returned null from getSession() with no refresh path; backend routes were not mounted; unsupported scopes had no user-facing message.

Solution:

  • Server: New server/src/routes/googleSheets.ts with POST /token, POST /refresh, POST /verify, POST /append. Google errors mapped to REAUTH_REQUIRED, INSUFFICIENT_SCOPE, and TOKEN_EXPIRED.
  • Client: refreshAccessToken(), ensureValidSession(), GoogleAuthError, scope detection via getAuthStatus().
  • UI: GoogleSheetsPanel shows reconnect banners for expired sessions and missing Sheets scope.
  • Route audit updated — /api/google-sheets/* is no longer a dead integration.

Acceptance criteria:

  • Expired credentials trigger a recoverable refresh path
  • Refresh-token expiry / authorization revocation clears session and prompts re-auth
  • Unsupported scope messaging validated
  • OAuth recovery covered by client and server tests

#820 — PnL charts sparse/missing data

Problem: PnLChart passed API data directly to Recharts with no normalization; out-of-order or malformed snapshots could misrender.

Solution:

  • Added pnlChartUtils.ts with normalizePnLData(), normalizeDailySnapshots(), and normalizeSnapshotPoint() (mirrors SharePriceChart pattern).
  • PnLChart normalizes on fetch; shows a sparse-data warning when fewer than 3 valid points exist.
  • Backend pnlCalculator tests extended for empty price history, sparse gaps, and out-of-order price inputs.

Acceptance criteria:

  • Empty and sparse datasets render safely
  • Out-of-order points normalized before charting
  • Data normalization regressions covered by tests

Files changed (23)

Area Key files
Wizard (#503) DepositRecommendationWizard.tsx, depositRecommendationService.ts, AIAdvisor.tsx, app.ts
Fragmentation (#817) FragmentationCalculator.ts, FragmentationCalculator.test.ts
Google Sheets (#818) googleSheets.ts (route), googleSheetsService.ts, errors.ts, GoogleSheetsPanel.tsx
PnL (#820) pnlChartUtils.ts, PnLChart.tsx, pnlCalculator.test.ts

Test plan

  • cd server && npm test -- --testPathPattern="googleSheets|depositRecommendation|pnlCalculator|riskAdjustedYield|routeAudit"
  • cd client && npm test -- --run googleSheetsService pnlChartUtils PnLChart DepositRecommendationWizard
  • cd backend/keepers && npm test -- --testPathPattern="FragmentationCalculator"
  • Manual: open /ai-advisor, complete wizard with each risk profile, verify ranked vaults and explanations
  • Manual: connect Google Sheets panel, verify reconnect banner on expired/missing-scope sessions
  • Manual: open /pnl with wallet connected — confirm chart handles sparse history without errors

Made with Cursor

…n edge cases

Implements four related issues: guided deposit recommendations (edehvictor#503),
fragmentation calculator stability (edehvictor#817), Google Sheets OAuth recovery
(edehvictor#818), and sparse PnL chart rendering (edehvictor#820).

Co-authored-by: Cursor <cursoragent@cursor.com>
@Abidoyesimze Abidoyesimze requested a review from edehvictor as a code owner June 26, 2026 15:28
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@devsimze is attempting to deploy a commit to the Edeh Victor's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Abidoyesimze Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Comment on lines +187 to +190
const response = await fetch(
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}`,
{ headers: { Authorization: `Bearer ${accessToken}` } },
);
Comment on lines +205 to +215
const response = await fetch(
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}:append?valueInputOption=USER_ENTERED`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ values: rows }),
},
);
Comment on lines +265 to +291
router.post("/verify", async (req: Request, res: Response) => {
try {
const { spreadsheetId, sheetName } = req.body as {
spreadsheetId?: string;
sheetName?: string;
};
const accessToken = req.headers.authorization?.replace("Bearer ", "");

if (!spreadsheetId || !sheetName || !accessToken) {
res.status(400).json({ error: "Missing required parameters" });
return;
}

const result = await verifySpreadsheetAccess(spreadsheetId, accessToken);
if (!result.ok) {
const mapped = mapGoogleAuthError(result.status, result.body);
res.status(mapped.status).json({ error: mapped.message, code: mapped.code });
return;
}

res.json({ success: true });
} catch (error) {
res.status(400).json({
error: error instanceof Error ? error.message : "Verification failed",
});
}
});
};
const accessToken = req.headers.authorization?.replace("Bearer ", "");

if (!spreadsheetId || !sheetName || !accessToken) {
};
const accessToken = req.headers.authorization?.replace("Bearer ", "");

if (!spreadsheetId || !sheetName || !accessToken) {
};
const accessToken = req.headers.authorization?.replace("Bearer ", "");

if (!spreadsheetId || !sheetName || !accessToken) {

@edehvictor edehvictor left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nice contribution!

@edehvictor edehvictor merged commit 8ff3826 into edehvictor:main Jun 26, 2026
4 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants