Module 05 — Lending

What this covers: Complete guide to BASIS loans — take, extend, repay, partial sell, and claim. Includes vault loan (wSTASIS) cross-reference, the sell-vs-borrow decision, fees, and error recovery.

Prerequisites

Next steps after borrowing

⚠️ This module covers LOANS, not leverage. Leverage lives on the SWAP contract; loans live on the LOAN contract. Completely separate systems with separate IDs. If you used leverageBuy(), manage that position through Module 04 §3c–3d using client.trading methods — do NOT use extendLoan(), repayLoan(), or claimLiquidation() on leverage positions. To close leverage, use client.trading.partialLoanSell(positionId, percentage, true, minOut) — note: on client.trading, not client.loans.


Why Borrow on BASIS

The single most important fact about BASIS loans: there is no price-based liquidation. Ever.

On every other DeFi platform, borrowing against a volatile asset means you live with margin call risk. One flash crash and your collateral is gone. On BASIS, that cannot happen. Loans expire by time only — if your collateral drops in price mid-loan, nothing happens. You choose when to exit: repay early, extend cheaply, or let it expire.

Specific reasons to borrow instead of sell:

  • Keep your upside. Selling forfeits future appreciation. Borrowing lets you hold the position while accessing capital. If the token 5x's after you borrow, you still own all of it.
  • Capital works twice. You hold the token AND deploy borrowed USDB into another position simultaneously — see Module 12 for full stacking paths.
  • Extensions are cheap. 400x cheaper per day than originating a new loan. You're never locked into a long commitment — take 10 days minimum, extend as needed.
  • Any token works as collateral. Stable+, Floor+, Predict+, STASIS, wSTASIS, vesting positions — the system accepts all of them.
  • Collateral appreciates. Since floor prices never decrease and Stable+ can only go up, your collateral value may exceed your debt by expiry — remainder is yours to claim.
  • Earn airdrop points. Loan origination and active loans accrue points — borrowing isn't just defensive, it stacks rewards.

How It Works

The Basics

Deposit tokens as collateral → receive USDB → repay before expiry to reclaim collateral.

LTV by token type:

  • Stable+ (STASIS) / Predict+: 100% LTV at spot price. Floor = spot for these tokens, so you borrow the full market value.
  • Floor+: 100% LTV at floor price (not spot). The gap between floor and spot is your built-in safety margin.

On expiry (if not repaid or extended):

  • Collateral is burned (Stable+/Predict+) or sold (Floor+) to cover the outstanding debt.
  • If collateral value > debt: the remainder is claimable via claimLiquidation(). It is NOT automatically returned to you — you must claim it.
  • If collateral value < debt (Floor+ only in extreme cases): you lose the collateral. You never owe anything beyond it. No margin calls, no debt beyond collateral.

The Cost Model

Interest is prepaid and non-refundable. There is no compounding, no accrual — you pay upfront for the days you request.

ComponentRateWhen Paid
Origination fee2% flatDeducted upfront from USDB received
Daily interest0.005% per dayOn collateral value, paid upfront
Extension fee0.005% per dayPaid upfront when extending

Repayment amount: Read fullAmount from getUserLoanDetails() — this is the total USDB obligation (original loan + prepaid interest). You repay this amount; no discount for early repayment.

Total cost by duration:

DurationOriginationInterestTotal Cost
10 days (minimum)2.00%0.05%2.05%
30 days2.00%0.15%2.15%
90 days2.00%0.45%2.45%
365 days2.00%1.83%3.83%

Optimal strategy: Always take the minimum duration (10 days hardcoded) and extend as needed. A 100-day extension costs 0.5% vs. a new 100-day loan costing 2.05%. Extensions are roughly 400x cheaper per day than re-originating.


Actions

Take a Loan

// JS
const result = await client.loans.takeLoan(MAINTOKEN, collateralToken, parseUnits("100", 18), 10n);

// Python
result = client.loans.take_loan(MAINTOKEN, collateral_token, 100 * 10**18, 10)
ParamTypeDescription
ecosystemstringMAINTOKEN address for the collateral's ecosystem
collateralstringCollateral token address
amountbigint/intCollateral amount (18 decimals)
daysCountbigint/intLoan duration in days (minimum: 10, maximum: 1000)

Pre-flight checks:

  1. Verify you hold sufficient collateral balance (use getTokens() for an overview).
  2. daysCount must be ≥ 10 — this is hardcoded in the contract. Passing 9 or less will revert.
  3. For Floor+ collateral: LTV is against floor price, not spot. Calculate expected USDB out = floorPrice × amount × 0.98 (after 2% fee).

Note: loans.takeLoan() is a simple one-layer loan. Your collateral is locked but does NOT earn yield. To earn vault yield on your collateral while borrowing, use the vault loan path (staking.borrow()) — covered in Module 06.

After the call — finding your hubId:

After takeLoan(), your hubId = getUserLoanCount(wallet) (the latest hub ID, 1-indexed). Use this numeric ID for getUserLoanDetails(), repayLoan(), extendLoan(), and all other loan operations. Do not pass token addresses as loan IDshubId is a number, not an address.

const loanCount = await client.loans.getUserLoanCount(walletAddress);
const hubId = loanCount; // 1-indexed: count equals the latest hubId
const details = await client.loans.getUserLoanDetails(walletAddress, hubId);
// details.collateralToken tells you which token backs this loan

Hub ID vs token address: getUserLoanCount(wallet) returns the total loans ever created by this wallet across all tokens. Each loan gets a sequential hubId. To find loans for a specific token, enumerate from 1 to count and check details.collateralToken.


Repay a Loan

Repay debt in full before expiry to reclaim all collateral.

// JS
const result = await client.loans.repayLoan(hubId);

// Python
result = client.loans.repay_loan(hub_id)
  • Auto-approves USDB to LoanHub.
  • You repay fullAmount (from getUserLoanDetails()), not just the principal.
  • Repaying early does NOT save money — unused days are forfeited. Let loans run to near-expiry if you can.

Extend a Loan

Extend duration for 0.005%/day — approximately 400x cheaper than originating a new loan.

// JS
const result = await client.loans.extendLoan(hubId, 30n, true, false);
// (hubId, additionalDays, payInUSDB, refinance)

// Python
result = client.loans.extend_loan(hub_id, 30, True, False)
ParamTypeDescription
hubIdbigint/intHub loan ID
addDaysbigint/intDays to add
payInStablebooleantrue = pay extension fee in USDB; false = deduct from collateral
refinancebooleantrue = recalculate LTV at current prices; earns airdrop points

Critical rule: If a loan has fewer than 10 days remaining, you must extend it before calling increaseLoan() or any other operation that requires an active loan. The contract enforces a minimum remaining duration for add-collateral operations.


Increase Collateral

Add more collateral to an existing loan without taking a new loan.

// JS
const result = await client.loans.increaseLoan(hubId, parseUnits("50", 18));

// Python
result = client.loans.increase_loan(hub_id, 50 * 10**18)
ParamTypeDescription
hubIdbigint/intHub loan ID
amountToAddbigint/intAdditional collateral (18 decimals)

Pre-flight: Check remaining days. If < 10 days remaining, call extendLoan() first or this will fail.


Partial Sell

Sell a portion of collateral while the loan is active. Proceeds go toward repaying debt proportionally.

// JS — regular hub loan only
const result = await client.loans.hubPartialLoanSell(hubId, 30n, false, 0n);
// (hubId, percentage, isLeverage=false, minOut)

// ⚠️ For leverage positions, use client.trading.partialLoanSell() instead — see Module 04 §3d
ParamTypeDescription
hubIdbigint/intHub loan ID
percentagebigint/intMust be a multiple of 10 (10, 20, 30 ... 100). Non-multiples cause a silent contract revert.
isLeveragebooleanfalse for regular loans, true for leverage positions
minOutbigint/intMinimum USDB output (slippage protection)

Hard rule: Percentage must be exactly 10, 20, 30, 40, 50, 60, 70, 80, 90, or 100. Passing 25 or 33 will silently revert without error — you'll spend gas and nothing will happen.

⚠️ Leverage uses a completely different system. Leverage positions live on the SWAP contract and are managed via client.trading methods — getLeverageCount(wallet), getLeveragePosition(wallet, index), and partialLoanSell(positionId, pct, true, minOut). These are NOT client.loans methods. The IDs, contracts, and method signatures are all different. → See Module 04 §3c–3d for leverage. Do NOT use hubPartialLoanSell for leverage — use client.trading.partialLoanSell.


Claim After Expiry

After a loan expires, if collateral value exceeded debt, claim the remainder.

// JS
const result = await client.loans.claimLiquidation(hubId);

// Python
result = client.loans.claim_liquidation(hub_id)

Wait for expiry first. Calling this on an active (not yet expired) loan will revert. Check liquidationTime in getUserLoanDetails() — this is the expiry timestamp. Only call claimLiquidation() after that time has passed.

Do not forget to claim. Remaining collateral sits in the contract indefinitely until you call this. Token prices can continue moving while it waits.


Staking Loan Cross-Reference (Vault Loans)

The vault loan path gives you an extra layer: your collateral earns yield while locked.

Flow:

Buy STASIS → wrap to wSTASIS (client.staking.wrap()) → lock as collateral (client.staking.lock()) → borrow USDB (client.staking.borrow(stasisAmount, days))

Key differences from loans.takeLoan():

  • Your wSTASIS continues earning vault yield from all platform trading fees while locked
  • Uses client.staking module, not client.loans
  • One loan per wallet: The vault supports only one active borrow position per wallet. If you already have an active staking loan, call client.staking.addToLoan() to add collateral — do NOT call client.staking.borrow() again or it will revert.

Check before borrowing:

// Get your locked wSTASIS and convert to STASIS equivalent
const wStasisShares = await client.publicClient.readContract({
  address: client.stakingAddress,
  abi: [{"inputs":[{"name":"","type":"address"}],"name":"balanceOf",
         "outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],
  functionName: 'balanceOf',
  args: [wallet],
});
const stasisEquivalent = await client.staking.convertToAssets(wStasisShares);
// stasisEquivalent is your maximum borrow amount
await client.staking.borrow(stasisEquivalent, 10n); // 10 days minimum

Vault loan methods (client.staking):

MethodWhat It Does
lock(shares)Lock wSTASIS as collateral. Still earns yield while locked.
unlock(shares)Release locked wSTASIS. Must repay loan first.
borrow(stasisAmount, days)Borrow USDB. One active loan per wallet.
repay()Repay vault loan in full. Auto-approves USDB.
addToLoan(additionalAmount)Add more collateral to existing vault loan.
extendLoan(daysToAdd, payInUSDB, refinance)Extend vault loan duration at 0.005%/day.
settleLiquidation()Settle an expired vault loan position (time-based expiry, not price liquidation).

Same fees as regular loans: 2% origination + 0.005%/day interest.

Full details: Module 06 — Staking


Sell vs. Borrow Decision

Not every situation calls for a loan. Use this framework:

Borrow when:

  • You believe the token will continue to appreciate — you want to keep the upside
  • You need temporary liquidity (paying fees, entering another position, bridging capital)
  • The token is STASIS/wSTASIS — it can only go up, so no price risk on your collateral
  • You want capital to work in two places simultaneously (collateral earns vault yield + borrowed USDB deployed elsewhere)

Sell when:

  • You want to lock in profit and exit the position (use sell())
  • You believe the price has peaked or the market is turning
  • The loan cost (2% origination) exceeds the expected benefit of holding
  • You need permanent liquidity, not temporary

Key insight for Floor+ positions: Your loan LTV is calculated against the floor price, but you can sell at the market price. When a Floor+ token has appreciated significantly, there's a large spread between what you owe (floor-based debt) and what the tokens are actually worth at market. Selling captures that entire spread. Borrowing only gives you floor-value USDB while you wait.

The stacking play: The power of borrowing on BASIS is chaining. Each borrow returns ~97.5% of your USDB back (Stable+) or ~96.5% (Floor+/Predict+) for redeployment. Three stacks from $1,000 gives you three active positions plus ~$908 still liquid — all from one starting bag, none of it price-liquidatable. See Module 12 for the full multi-path stack.


Fees Summary

ActionFeeNotes
Origination2% flatDeducted upfront. One-time, non-refundable.
Daily interest0.005%/dayOn collateral value, prepaid for full duration.
Extension0.005%/daySame rate, paid upfront. ~400x cheaper than re-originating.
RepaymentRepay fullAmountIncludes prepaid interest. No discount for early repay.
Expiry (no repay)Collateral burned/sold to cover debtRemainder claimable via claimLiquidation().
Partial sellStandard trade fees on sold portionApplied to the token sale portion only.

Errors and Recovery

Common Errors

ErrorCauseFix
Transaction reverts silentlypartialLoanSell percentage not a multiple of 10Use only 10, 20, 30 ... 100.
Loan expiredCalling repay/extend after expiry timestampCall claimLiquidation() instead to claim remainder.
Loan is still activeCalling claimLiquidation() before expiryWait until liquidationTime has passed, then claim.
Transaction revertsdaysCount < 10 on takeLoan()Minimum is hardcoded at 10 days. Use 10n or higher.
increaseLoan failsRemaining days < 10Call extendLoan() first to add days, then increaseLoan().
staking.borrow() revertsWallet already has active vault loanUse staking.addToLoan() to add collateral instead — see Module 06.
partialLoanSell reverts or returns nothingCalled too soon after leverageBuyWait at least 5 seconds for backend to sync before calling.
Insufficient balanceNot enough USDB to repay fullAmountGet more USDB from faucet or other source before repaying.

For the full alphabetical error index across all modules, see Module 18 — SDK Reference.

Recovery Flow: "Duration too short" on addToLoan

If increaseLoan() fails because remaining days < 10:

const details = await client.loans.getUserLoanDetails(walletAddress, hubId);
const now = Math.floor(Date.now() / 1000);
const remainingDays = Math.floor((Number(details.liquidationTime) - now) / 86400);

if (remainingDays < 10) {
  const daysToAdd = 10 - remainingDays + 1; // add enough to get above 10
  await client.loans.extendLoan(hubId, BigInt(daysToAdd), true, false);
}
// Now safe to add collateral
await client.loans.increaseLoan(hubId, additionalAmount);

Recovery Flow: "Vault Loan Already Exists"

If staking.borrow() reverts because you already have an active vault loan:

// Check for existing vault loan before attempting to borrow
const stakingDetails = await client.staking.getLoanDetails(walletAddress);
if (stakingDetails.active) {
  // Already has a loan — add collateral instead
  await client.staking.addToLoan(additionalStasisAmount);
  // To extend duration:
  await client.staking.extendLoan(additionalDays, true, false);
} else {
  // No active loan — safe to borrow
  await client.staking.borrow(stasisAmount, 10n);
}

Common Mistakes

  • Treating the 2% fee as an annual rate. It is a flat origination fee. A 365-day loan costs ~3.83% total, not 730%.
  • Taking long loans "to be safe." Interest is prepaid. If you repay on day 5 of a 90-day loan, you forfeit 85 days of prepaid interest. Always take 10 days and extend.
  • Re-originating instead of extending. Each new loan costs 2% flat. An extension costs 0.005%/day. Extending 100 days = 0.5% vs. new loan = 2.05%.
  • Using non-multiple-of-10 on partial sell. 25%, 33%, 50.5% will all silently revert. Only use clean multiples: 10, 20, 30 ... 100.
  • Forgetting to claim after expiry. Surplus collateral sits in the contract until you call claimLiquidation(). It does not auto-return.
  • Calling borrow() twice on a vault loan. Only one vault loan per wallet. Use addToLoan() for additional collateral.

What Next

With borrowed USDB in hand, your options:

  • Trading — Buy tokens with borrowed USDB. Your collateral appreciates, your new position also appreciates.
  • Staking — Full vault loan walkthrough: wrap → lock → borrow. Collateral earns yield while borrowed against.
  • Token Creation — Create a Floor+ or Predict+ token. Deploy borrowed USDB as the creation capital.
  • Predictions — Buy outcome shares with borrowed USDB. Your token collateral appreciates from volume, your bet pays out on resolution.
  • Vesting — Borrow against a vesting position with takeLoanOnVesting() (same fee model, same expiry rules).
  • Strategy & Stacking — Full multi-position stacking: how to chain loans across paths for maximum capital efficiency and airdrop point multipliers.

See Also