Skip to content

feat: add SELL quote from velora to support bigger quotes#501

Merged
antoncoding merged 7 commits intomasterfrom
feat/quote-sell
Apr 22, 2026
Merged

feat: add SELL quote from velora to support bigger quotes#501
antoncoding merged 7 commits intomasterfrom
feat/quote-sell

Conversation

@antoncoding
Copy link
Copy Markdown
Owner

@antoncoding antoncoding commented Apr 22, 2026

integrate paraswap sell quote for better liquidity

Summary by CodeRabbit

  • Bug Fixes

    • More reliable collateral-input leverage quoting: iterative quote attempts now find an executable sell route or surface an explicit error instead of returning empty/zero results.
    • Improved fallback behavior to reduce silent failures and improve quote accuracy.
  • New Features

    • Swap/quote errors are converted into clear, user-facing messages so leverage/deleverage failures are more actionable.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
monarch Ready Ready Preview, Comment Apr 22, 2026 6:04am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Warning

Rate limit exceeded

@starksama has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 59 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 55 minutes and 59 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b916ea89-adb9-4dcd-97ec-f2c8514b92f4

📥 Commits

Reviewing files that changed from the base of the PR and between 6e1322e and b01b369.

📒 Files selected for processing (1)
  • src/hooks/leverage/velora-quote-errors.ts
📝 Walkthrough
📝 Walkthrough
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding SELL quote support from Velora to handle larger quotes, which is the core objective of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/quote-sell

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

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

Inline comments:
In `@src/hooks/useLeverageQuote.ts`:
- Around line 278-303: Treat zero or insufficient SELL quotes as failures: when
computing borrowAssets (from buyRoute.srcAmount) if borrowAssets <= 0n return
the empty/failure shape, and after fetching sellRoute via fetchVeloraPriceRoute
verify that withSlippageFloor(BigInt(sellRoute.destAmount), slippageBps) meets
or exceeds the required target (targetFlashCollateralTokenAmount); if it does
not, return the same empty/failure response (flashLoanAssetAmount: 0n,
flashLegCollateralTokenAmount: 0n, priceRoute: null) so callers will fallback to
iterative sizing rather than treating an undersized BUY-derived SELL quote as
success.
- Around line 130-147: Validate the Velora route side and exact-amount before
using preview amounts: after calling fetchVeloraPriceRoute and before assigning
latestQuote, assert that sellRoute.side === 'SELL' and that
BigInt(sellRoute.srcAmount) === sellAmount (i.e., the route's source amount
matches the requested sellAmount); if either check fails, log/throw and skip
building latestQuote. Do the analogous checks where you use buy routes (ensure
route.side === 'BUY' and the route's dest/src amount matches the requested exact
amount) before reading sellRoute.destAmount or sellRoute.srcAmount, then only
compute quotedCollateralTokenAmount and call withSlippageFloor(slippageBps) and
set latestQuote when validations pass.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6b999d7c-b61c-40c5-b760-2183ebf41144

📥 Commits

Reviewing files that changed from the base of the PR and between 479e41a and 4a40874.

📒 Files selected for processing (1)
  • src/hooks/useLeverageQuote.ts

Comment thread src/hooks/useLeverageQuote.ts
Comment thread src/hooks/useLeverageQuote.ts Outdated
@coderabbitai coderabbitai Bot added the feature request Specific feature ready to be implemented label Apr 22, 2026
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.

♻️ Duplicate comments (2)
src/hooks/useLeverageQuote.ts (2)

199-221: ⚠️ Potential issue | 🟠 Major

Check the slippage-floored amount before accepting the SELL quote.

Line 206 stops once the gross quote reaches targetCollateralTokenAmount, but line 201 returns the slippage-floored amount. A quote that barely reaches the target can still return a below-target flashLegCollateralTokenAmount.

Proposed fix
-    if (quotedCollateralTokenAmount >= targetCollateralTokenAmount) {
+    if (latestQuote.flashLegCollateralTokenAmount >= targetCollateralTokenAmount) {
       break;
     }
@@
-  if (latestQuote.quotedCollateralTokenAmount < targetCollateralTokenAmount) {
+  if (latestQuote.flashLegCollateralTokenAmount < targetCollateralTokenAmount) {
     throw new Error('Failed to size Velora sell route for target leverage. Try a lower multiplier or refresh the quote.');
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useLeverageQuote.ts` around lines 199 - 221, The loop currently
accepts a quote when quotedCollateralTokenAmount >= targetCollateralTokenAmount
but the code uses the slippage-floored value (withSlippageFloor) as
flashLegCollateralTokenAmount; update the acceptance and final checks to use the
slippage-floored amount instead of the raw quotedCollateralTokenAmount: when
deciding to break and set latestQuote, compare
withSlippageFloor(quotedCollateralTokenAmount, slippageBps) (or
latestQuote.flashLegCollateralTokenAmount) against targetCollateralTokenAmount,
and in the final validation replace the quotedCollateralTokenAmount comparison
with a check on latestQuote.flashLegCollateralTokenAmount (or the floored value)
to ensure the post-slippage amount meets the target before accepting or
throwing.

117-130: ⚠️ Potential issue | 🟠 Major

Validate Velora side and exact amounts before using route amounts.

These tx-critical previews use buyRoute.srcAmount and sellRoute.destAmount without visible checks that the response matches the requested BUY/SELL side and exact amount. If this is not enforced inside fetchVeloraPriceRoute, add local guards before assigning quote values. Based on learnings, “For Velora/Paraswap routes, validate execution-authoritative fields only (trusted target, exact sell amount, min-out/close floor, token identities); do not block on echoed metadata”.

Proposed guard
     const buyRoute = await fetchVeloraPriceRoute({
       srcToken: loanTokenAddress,
       srcDecimals: loanTokenDecimals,
       destToken: collateralTokenAddress,
       destDecimals: collateralTokenDecimals,
       amount: targetCollateralTokenAmount,
       network: chainId,
       userAddress: swapExecutionAddress,
       side: 'BUY',
     });
 
+    if (buyRoute.side !== 'BUY') {
+      throw new Error('Velora returned a non-BUY route for BUY bootstrap.');
+    }
+
     const initialSellAmount = BigInt(buyRoute.srcAmount);
-    if (initialSellAmount > 0n && BigInt(buyRoute.destAmount) >= targetCollateralTokenAmount) {
+    if (initialSellAmount > 0n && BigInt(buyRoute.destAmount) === targetCollateralTokenAmount) {
       return initialSellAmount;
     }
@@
     const sellRoute = await fetchVeloraPriceRoute({
       srcToken: loanTokenAddress,
       srcDecimals: loanTokenDecimals,
       destToken: collateralTokenAddress,
       destDecimals: collateralTokenDecimals,
       amount: sellAmount,
       network: chainId,
       userAddress: swapExecutionAddress,
       side: 'SELL',
     });
+    if (sellRoute.side !== 'SELL' || BigInt(sellRoute.srcAmount) !== sellAmount) {
+      throw new Error('Velora returned a SELL route for a different exact input amount.');
+    }
     const quotedCollateralTokenAmount = BigInt(sellRoute.destAmount);

To verify whether this is already centralized:

#!/bin/bash
# Expect to find side and exact requested-amount validation either in fetchVeloraPriceRoute
# or at each tx-critical call site before route amounts are consumed.
rg -n -C6 --type=ts 'fetchVeloraPriceRoute|side|srcAmount|destAmount' src/features/swap/api/velora.ts src/hooks/useLeverageQuote.ts

Also applies to: 187-201

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

In `@src/hooks/useLeverageQuote.ts` around lines 117 - 130, Ensure the Velora
route is execution-authoritative before using its amounts: after calling
fetchVeloraPriceRoute (referencing buyRoute/sellRoute, srcAmount, destAmount),
assert route.side === the requested side ('BUY' or 'SELL'), verify
route.srcToken and route.destToken match the expected loanTokenAddress and
collateralTokenAddress, and validate the authoritative amount (for BUY ensure
BigInt(buyRoute.destAmount) >= targetCollateralTokenAmount and for SELL ensure
BigInt(sellRoute.srcAmount) equals the exact requested sell amount or meets the
required exact/min-out rules). If any guard fails, throw or reject the quote
instead of consuming buyRoute.srcAmount/sellRoute.destAmount; apply the same
checks for the other route usage at lines ~187-201.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/hooks/useLeverageQuote.ts`:
- Around line 199-221: The loop currently accepts a quote when
quotedCollateralTokenAmount >= targetCollateralTokenAmount but the code uses the
slippage-floored value (withSlippageFloor) as flashLegCollateralTokenAmount;
update the acceptance and final checks to use the slippage-floored amount
instead of the raw quotedCollateralTokenAmount: when deciding to break and set
latestQuote, compare withSlippageFloor(quotedCollateralTokenAmount, slippageBps)
(or latestQuote.flashLegCollateralTokenAmount) against
targetCollateralTokenAmount, and in the final validation replace the
quotedCollateralTokenAmount comparison with a check on
latestQuote.flashLegCollateralTokenAmount (or the floored value) to ensure the
post-slippage amount meets the target before accepting or throwing.
- Around line 117-130: Ensure the Velora route is execution-authoritative before
using its amounts: after calling fetchVeloraPriceRoute (referencing
buyRoute/sellRoute, srcAmount, destAmount), assert route.side === the requested
side ('BUY' or 'SELL'), verify route.srcToken and route.destToken match the
expected loanTokenAddress and collateralTokenAddress, and validate the
authoritative amount (for BUY ensure BigInt(buyRoute.destAmount) >=
targetCollateralTokenAmount and for SELL ensure BigInt(sellRoute.srcAmount)
equals the exact requested sell amount or meets the required exact/min-out
rules). If any guard fails, throw or reject the quote instead of consuming
buyRoute.srcAmount/sellRoute.destAmount; apply the same checks for the other
route usage at lines ~187-201.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d0978ac4-103a-4cf1-ac33-47f5aeee50bb

📥 Commits

Reviewing files that changed from the base of the PR and between 4a40874 and fe40549.

📒 Files selected for processing (2)
  • src/hooks/useDeleverageQuote.ts
  • src/hooks/useLeverageQuote.ts
✅ Files skipped from review due to trivial changes (1)
  • src/hooks/useDeleverageQuote.ts

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.

♻️ Duplicate comments (1)
src/hooks/useLeverageQuote.ts (1)

187-223: ⚠️ Potential issue | 🟠 Major

Validate the executable SELL amount and min-out before accepting the route.

Line 207 and Line 221 check the raw quote, but execution uses the slippage floor. This can accept a route whose minimum output is below the leverage target. Also confirm sellRoute.srcAmount matches sellAmount before storing the route.

Proposed fix
     const sellRoute = await fetchVeloraPriceRoute({
       srcToken: loanTokenAddress,
       srcDecimals: loanTokenDecimals,
       destToken: collateralTokenAddress,
       destDecimals: collateralTokenDecimals,
       amount: sellAmount,
       network: chainId,
       userAddress: swapExecutionAddress,
       side: 'SELL',
     });
+    const routeSellAmount = BigInt(sellRoute.srcAmount);
+    if (routeSellAmount !== sellAmount) {
+      throw new Error('Velora returned a SELL route with a different source amount than requested.');
+    }
+
     const quotedCollateralTokenAmount = BigInt(sellRoute.destAmount);
+    const minCollateralTokenAmount = withSlippageFloor(quotedCollateralTokenAmount, slippageBps);
 
     latestQuote = {
-      flashLoanAssetAmount: sellAmount,
-      flashLegCollateralTokenAmount: withSlippageFloor(quotedCollateralTokenAmount, slippageBps),
+      flashLoanAssetAmount: routeSellAmount,
+      flashLegCollateralTokenAmount: minCollateralTokenAmount,
       quotedCollateralTokenAmount,
       priceRoute: sellRoute,
     };
 
-    if (quotedCollateralTokenAmount >= targetCollateralTokenAmount) {
+    if (minCollateralTokenAmount >= targetCollateralTokenAmount) {
       break;
     }
 
     sellAmount = getNextSellAmountForTargetCollateral({
-      currentSellAmount: sellAmount,
-      quotedCollateralAmount: quotedCollateralTokenAmount,
+      currentSellAmount: routeSellAmount,
+      quotedCollateralAmount: minCollateralTokenAmount,
       targetCollateralAmount: targetCollateralTokenAmount,
     });
   }
@@
-  if (latestQuote.quotedCollateralTokenAmount < targetCollateralTokenAmount) {
+  if (latestQuote.flashLegCollateralTokenAmount < targetCollateralTokenAmount) {
     throw new Error('Failed to size Velora sell route for target leverage. Try a lower multiplier or refresh the quote.');
   }

Based on learnings, Applies to **/{swap,paraswap}.{ts,tsx} : For Velora/Paraswap routes, validate execution-authoritative fields only (trusted target, exact sell amount, min-out/close floor, token identities); do not block on echoed metadata.

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

In `@src/hooks/useLeverageQuote.ts` around lines 187 - 223, Ensure the accepted
Velora route is executable by validating execution-authoritative fields: inside
the attempt loop (where sellRoute, sellAmount, quotedCollateralTokenAmount and
latestQuote are set) verify that sellRoute.srcAmount equals the current
sellAmount and that the execution min-out
(withSlippageFloor(quotedCollateralTokenAmount, slippageBps)) is >=
targetCollateralTokenAmount; also confirm sellRoute.srcToken/destToken match
loanTokenAddress/collateralTokenAddress before assigning latestQuote and
breaking; if any check fails, treat it as a non-viable quote and continue the
loop (so getNextSellAmountForTargetCollateral can run) and only throw after the
loop if no route passes these execution checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/hooks/useLeverageQuote.ts`:
- Around line 187-223: Ensure the accepted Velora route is executable by
validating execution-authoritative fields: inside the attempt loop (where
sellRoute, sellAmount, quotedCollateralTokenAmount and latestQuote are set)
verify that sellRoute.srcAmount equals the current sellAmount and that the
execution min-out (withSlippageFloor(quotedCollateralTokenAmount, slippageBps))
is >= targetCollateralTokenAmount; also confirm sellRoute.srcToken/destToken
match loanTokenAddress/collateralTokenAddress before assigning latestQuote and
breaking; if any check fails, treat it as a non-viable quote and continue the
loop (so getNextSellAmountForTargetCollateral can run) and only throw after the
loop if no route passes these execution checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c7ba07f9-422b-4096-9946-5989673631f4

📥 Commits

Reviewing files that changed from the base of the PR and between fe40549 and 6e1322e.

📒 Files selected for processing (3)
  • src/hooks/leverage/velora-quote-errors.ts
  • src/hooks/useDeleverageQuote.ts
  • src/hooks/useLeverageQuote.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/hooks/useDeleverageQuote.ts

Copy link
Copy Markdown
Collaborator

@starksama starksama left a comment

Choose a reason for hiding this comment

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

Reviewed end-to-end after the follow-up fixes.

Why I'm approving:

  • collateral-input leverage quotes now use a price-aware BUY bootstrap when available, and fall back to iterative SELL sizing when BUY fails at size
  • this fixes the original regression risk on pairs like USDC -> cbBTC while still handling larger routes like PYUSD -> sUSDe
  • user-facing quote failures are now much clearer, including reported impact when Velora returns it
  • I re-checked the main CodeRabbit concerns; the earlier blocking ones are addressed

What I validated:

  • live quote behavior on PYUSD -> sUSDe, USDC -> cbBTC, and WETH -> wstETH
  • top-market scan to separate algorithm issues from plain upstream liquidity / impact failures
  • execution safety assumptions remain fail-closed downstream in the leverage/deleverage execution path

Remaining caveat, but not blocking for this PR:

  • collateral-input preview still accepts success on raw quoted output rather than slippage-floor >= target. That is worth a follow-up if we want stricter target semantics, but I do not see it as a merge blocker for this fix.

Given the fixes and live verification, this looks good to merge.

@antoncoding antoncoding merged commit 499b527 into master Apr 22, 2026
4 checks passed
@antoncoding antoncoding deleted the feat/quote-sell branch April 22, 2026 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request Specific feature ready to be implemented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants