Positions, quotes & limits
Four read functions cover everything a UI needs, and they share the same
parameter shape as the write flows:
{ connection, owner, collateralMint, apiBaseUrl }. None of them require a
signer - they’re plain reads keyed by wallet address.
| Function | Use it for |
|---|---|
getPosition | The raw position: balances, lender, vault |
getPositionMetrics | A ready-to-render dashboard - the exact numbers app.hobba.io shows |
getQuote | ”What happens if…” projections for calculators and confirm modals |
getLimits | Input validation: max borrow / repay / withdraw, min deposit |
getPosition - the raw position
Section titled “getPosition - the raw position”import { getPosition, CBBTC_MINT } from "@hobba-io/core";
const pos = await getPosition({ connection, owner: wallet.publicKey, collateralMint: CBBTC_MINT, apiBaseUrl: "https://app.hobba.io",});Returns a HobbaPosition - amounts as decimal strings in base units, so
nothing loses precision crossing a JSON boundary:
interface HobbaPosition { exists: boolean; // false → wallet has no position for this collateral lender: "juplend" | "kamino"; collateral: string; // deposited collateral, base units debtUsdc: string; // TOTAL debt: user + Sonnar's working borrow depositedToVault: string; // Sonnar's working capital (cost basis) userDebtUsdc: string; // what the user actually owes - show THIS as "loan" currentVault: string; // "allez" | "prime" | "rockawayRWA" | "perena" | "none" obligation?: string; // lender-side account address}The one field rule to get right: display userDebtUsdc as the user’s
loan, never debtUsdc. Total debt includes Sonnar’s working
borrow, which is the protocol’s, not the user’s.
getPositionMetrics - the rendered dashboard
Section titled “getPositionMetrics - the rendered dashboard”Everything on the app’s dashboard, computed with the app’s exact logic - LTV with a health status, liquidation price, drop-to-liquidation buffer, Supply/Loan APY figures, self-repay date, borrow-limit usage, lender and vault names, and per-day cost/earnings breakdowns for tooltips:
const m = await getPositionMetrics({ connection, owner, collateralMint, apiBaseUrl });
m.currentLtvPct; // e.g. 34.2m.ltvStatus; // "HEALTHY" | "MODERATE" | "NEEDS_ATTENTION"m.liquidationPriceUsd; // null when effectively debt-freem.loanApyPct; // negative → self-repaying; null renders as "-"m.selfRepayDate; // "14/03/2027" when loan APY is negative, else nullm.isEarning; // true → "Smart Earning", false → "Reducing Interest"m.lendingSource; // "JupLend" | "Kamino Main" | …m.yieldVault; // "Perena USD*" | "Allez USDC" | …Render these directly - the point is that partner UIs never re-implement Hobba’s rate, operator or dust-threshold rules.
getQuote - projections before an action
Section titled “getQuote - projections before an action”Feed it the deltas a user has typed and get back the current position, the projected position, and calculator figures - the same math behind the app’s calculator and confirm modals:
import { getQuote, SOL_MINT } from "@hobba-io/core";
const quote = await getQuote({ connection, collateralMint: SOL_MINT, owner: wallet.publicKey, // omit for a fresh, position-less calculator depositAmount: "2000000000", // deltas: base-unit decimal strings borrowAmount: "150000000", // any of deposit/borrow/repay/withdraw apiBaseUrl: "https://app.hobba.io",});
quote.current.ltvPct; // beforequote.projected.ltvPct; // afterquote.projected.liquidationPriceUsd; // price at which the position liquidatesquote.projected.dropToLiquidationPct; // % fall the position can absorbquote.projected.riskLabel; // "Very Safe" … "Extremely Risky"quote.projected.loanApyPct; // projected effective APYquote.calculator.selfRepayDate; // projected self-repay date, or nullquote.calculator.earningPerYearUsd; // projected yield subsidy, USD/yrFor synchronous per-keystroke math, computeQuote is the pure-function
variant - fetch the inputs once (rates, prices, position) and re-run it
locally on every input change.
getLimits - input bounds
Section titled “getLimits - input bounds”One call returns every bound your inputs need, so integrator math can’t drift from protocol rules:
const limits = await getLimits({ connection, owner, collateralMint, apiBaseUrl });
limits.maxBorrowUsdc; // micro-USDC left under the 50% LTV caplimits.maxRepayUsdc; // wallet vs debt, with the JupLend dust shavelimits.maxWithdraw; // base units, drift- and residual-adjustedlimits.minDeposit; // first-deposit vs top-up awarelimits.solReserve; // lamports to hold back on a "Max" SOL depositlimits.maxUserLtvPct; // 50limits.liquidationLtvPct; // live per lender + collateral (75-90)The values encode lender-specific edge cases (accrual drift margins, minimum debt floors, unwind residuals) that are easy to get wrong by hand - see the notes on the Repay and Withdraw pages.
A note on apiBaseUrl
Section titled “A note on apiBaseUrl”The reads fetch rates, prices and Jupiter Lend position data from the Hobba
backend, so they need apiBaseUrl just like the write flows. Browser calls
are subject to the origin allowlist - see Backend & CORS.