feat: deposit wizard, OAuth recovery, PnL normalization, fragmentation edge cases (#503 #817 #818 #820)#876
Merged
edehvictor merged 1 commit intoJun 26, 2026
Conversation
…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>
|
@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. |
|
@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! 🚀 |
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) { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR delivers four related features across the advisor UI, Google Sheets integration, fragmentation analytics, and PnL charting:
/api/google-sheets/*backend routes.Closes #503
Closes #817
Closes #818
Closes #820
#503 — Guided deposit recommendation wizard
Problem:
AIAdvisor.tsxwas a placeholder with no wizard or real vault ranking.Solution:
DepositRecommendationWizard.tsx— 4-step UI integrated into the AI Advisor page.depositRecommendationService.ts— reusesrankStrategies/computeRiskAdjustedYieldwith profile mapping (aggressive→tolerantdrawdown model).POST /api/recommendwith real scoring; each recommendation includes rank, RAY, APY, risk score, TVL, and a human-readable explanation.RecommendationInputSnapshotwithtimeHorizonandliquidityNeeds.Acceptance criteria:
#817 — Fragmentation calculator degenerate inputs
Problem: Zero-liquidity pools, extreme skew, and repeated recomputation could produce unstable or misleading fragmentation metrics.
Solution:
normalizeProtocols()inFragmentationCalculatorto filter zero-TVL entries, deduplicate protocol names, and reject non-finite TVL (NaN/Infinity).estimateMultiProtocolRouting()now counts only active (positive-TVL) protocols.Acceptance criteria:
#818 — Google Sheets OAuth recovery
Problem: Expired access tokens returned
nullfromgetSession()with no refresh path; backend routes were not mounted; unsupported scopes had no user-facing message.Solution:
server/src/routes/googleSheets.tswithPOST /token,POST /refresh,POST /verify,POST /append. Google errors mapped toREAUTH_REQUIRED,INSUFFICIENT_SCOPE, andTOKEN_EXPIRED.refreshAccessToken(),ensureValidSession(),GoogleAuthError, scope detection viagetAuthStatus().GoogleSheetsPanelshows reconnect banners for expired sessions and missing Sheets scope./api/google-sheets/*is no longer a dead integration.Acceptance criteria:
#820 — PnL charts sparse/missing data
Problem:
PnLChartpassed API data directly to Recharts with no normalization; out-of-order or malformed snapshots could misrender.Solution:
pnlChartUtils.tswithnormalizePnLData(),normalizeDailySnapshots(), andnormalizeSnapshotPoint()(mirrorsSharePriceChartpattern).PnLChartnormalizes on fetch; shows a sparse-data warning when fewer than 3 valid points exist.pnlCalculatortests extended for empty price history, sparse gaps, and out-of-order price inputs.Acceptance criteria:
Files changed (23)
DepositRecommendationWizard.tsx,depositRecommendationService.ts,AIAdvisor.tsx,app.tsFragmentationCalculator.ts,FragmentationCalculator.test.tsgoogleSheets.ts(route),googleSheetsService.ts,errors.ts,GoogleSheetsPanel.tsxpnlChartUtils.ts,PnLChart.tsx,pnlCalculator.test.tsTest plan
cd server && npm test -- --testPathPattern="googleSheets|depositRecommendation|pnlCalculator|riskAdjustedYield|routeAudit"cd client && npm test -- --run googleSheetsService pnlChartUtils PnLChart DepositRecommendationWizardcd backend/keepers && npm test -- --testPathPattern="FragmentationCalculator"/ai-advisor, complete wizard with each risk profile, verify ranked vaults and explanations/pnlwith wallet connected — confirm chart handles sparse history without errorsMade with Cursor