Borrow
Borrow against a coin
Lock a coin's tokens in its pool and borrow SOL from the same pool. One fee up front, no interest, no deadline. quoteBorrow mirrors every check the program makes, so a loan you quote is a loan that lands.
How loans work
- Lending opens on a coin once its pool holds 0.1 SOL (
MIN_POOL_SOL_FOR_LENDING) and 150 slots after the pool was created. - You can borrow up to 40% of your tokens' value (
MAX_LTV_BPS). The value is what the tokens would sell for against a pool of the same depth, at the lower of the spot and the safe price, so a pumped price can't be borrowed against. - A pool lends out at most 15% of its SOL (
BORROW_CAP_BPS); the minimum loan is 0.01 SOL (MIN_DEBT_LAMPORTS). - The only cost is a one-time fee taken from the loan: 1% when the pool's cap is empty, rising to 3% at 80% use and 10% when full.
- The pool sets aside the matching share of its tokens with the SOL it lends, so taking or repaying a loan doesn't move the price.
- One position per wallet per coin. It stays open until you repay it, or until it is liquidated.
Quote a loan
import { quoteBorrow } from "@proglet/sdk";
const pool = await client.fetchPool(mint);
const slot = BigInt(await connection.getSlot());
const max = quoteBorrow(pool!, collateral, slot); // the most you can borrow
const q = quoteBorrow(pool!, collateral, slot, 20_000_000n); // or a chosen amount (capped at the max)
if (q.blocked) throw new Error(q.blocked); // "lending-closed" | "cooldown" | "no-price" | "too-small"| value | bigint | Collateral value in lamports at min(spot, safe price), the price borrow uses. |
| maxDebt | bigint | The largest loan the program accepts right now: 40% LTV, the pool's cap, the pool's SOL. |
| capped | boolean | True when the pool's cap, not the 40% LTV, sets maxDebt. |
| debt | bigint | The loan this quote is for. |
| feeBps, fee | bigint | The one-time fee rate and amount. |
| receive | bigint | What lands in the wallet: debt minus fee. |
| blocked | string? | Why borrow would fail now, if it would. |
Borrow
import { borrowInstruction } from "@proglet/sdk";
const ix = borrowInstruction({
mint,
owner: wallet.publicKey,
collateral,
debt: (max.maxDebt * 9_950n) / 10_000n, // a hair under the max, in case the price ticks down
maxFeeBps: q.feeBps + 25n, // the fee can rise if someone borrows first
});maxFeeBps protects you: if the fee has risen above it by the time the transaction lands, it fails with FeeAboveMax. Opening a loan also pays rent for the position account (about 0.0016 SOL), refunded when you repay in full.
Borrow more or add collateral
borrow also adds to an open position. The 40% check runs on the position's totals, and the fee is charged on the new debt only. Pass the open position to quoteBorrow to quote it:
const position = await client.fetchPosition(mint, wallet.publicKey);
// Borrow more against what is already locked:
const more = quoteBorrow(pool!, 0n, slot, undefined, position!);
borrowInstruction({ mint, owner, collateral: 0n, debt: more.maxDebt, maxFeeBps: more.feeBps + 25n });
// Only add collateral (no fee, lowers the risk; works even while new loans are paused):
borrowInstruction({ mint, owner, collateral: extraTokens, debt: 0n, maxFeeBps: 0n });Repay
repayInstruction pays the whole debt, returns all the collateral and closes the position. repayPartialInstruction pays part of it and returns the same share of the collateral, so the loan's LTV doesn't change.
import { quoteRepayPartial, repayInstruction, repayPartialInstruction } from "@proglet/sdk";
const part = quoteRepayPartial(position!, 5_000_000n);
// part.ok, part.collateralOut, part.remainingDebt, part.maxPartial
const ix = part.ok ? repayPartialInstruction(mint, wallet.publicKey, 5_000_000n) : repayInstruction(mint, wallet.publicKey);MIN_LOAN_SLOTS), which rules out flash loans. Repaying is never paused.Health and liquidation
import { positionHealth, liquidateInstruction, absorbInstruction } from "@proglet/sdk";
const h = positionHealth(pool!, position!, slot); // valued at the safe price, like the program
h?.ltvBps; // debt / value
h?.liquidatable; // true past 1/1.5 = 66.7%- A loan can be liquidated by anyone once its collateral, at the safe price, is worth less than 1.5 times the debt. The liquidator pays the debt and receives tokens worth the debt plus a bonus of 2.5% to 15% (deeper underwater, bigger bonus); the rest of the collateral stays in the pool.
- If the collateral is worth less than the debt itself, anyone can call
absorbinstead: the position closes, its tokens go into the pool, and the pool takes the SOL loss. - Both are valued at the safe price, never the live spot, so a one-slot dump can't trigger them.
