Skip to content

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.

FunctionUse it for
getPositionThe raw position: balances, lender, vault
getPositionMetricsA ready-to-render dashboard - the exact numbers app.hobba.io shows
getQuote”What happens if…” projections for calculators and confirm modals
getLimitsInput validation: max borrow / repay / withdraw, min deposit
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.2
m.ltvStatus; // "HEALTHY" | "MODERATE" | "NEEDS_ATTENTION"
m.liquidationPriceUsd; // null when effectively debt-free
m.loanApyPct; // negative → self-repaying; null renders as "-"
m.selfRepayDate; // "14/03/2027" when loan APY is negative, else null
m.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.

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; // before
quote.projected.ltvPct; // after
quote.projected.liquidationPriceUsd; // price at which the position liquidates
quote.projected.dropToLiquidationPct; // % fall the position can absorb
quote.projected.riskLabel; // "Very Safe" … "Extremely Risky"
quote.projected.loanApyPct; // projected effective APY
quote.calculator.selfRepayDate; // projected self-repay date, or null
quote.calculator.earningPerYearUsd; // projected yield subsidy, USD/yr

For 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.

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 cap
limits.maxRepayUsdc; // wallet vs debt, with the JupLend dust shave
limits.maxWithdraw; // base units, drift- and residual-adjusted
limits.minDeposit; // first-deposit vs top-up aware
limits.solReserve; // lamports to hold back on a "Max" SOL deposit
limits.maxUserLtvPct; // 50
limits.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.

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.