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
- Wallet funded with USDB (→01), client initialized (→02)
- Hold a token to use as collateral — bought via trading, created, or vesting
- Understand floor vs spot pricing — LTV math depends on token type
Next steps after borrowing
- Borrowed USDB? Redeploy it: another trade, stake more STASIS, bet on a market, or create a token
- Want yield-bearing collateral? Use the vault loan path instead of regular
takeLoan() - Borrowed against a Floor+ token? See §6 of Module 04 for floor profit scenarios
- Stacking multiple loans? Read Module 12 for multi-path capital recycling
⚠️ 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 usingclient.tradingmethods — do NOT useextendLoan(),repayLoan(), orclaimLiquidation()on leverage positions. To close leverage, useclient.trading.partialLoanSell(positionId, percentage, true, minOut)— note: onclient.trading, notclient.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.
| Component | Rate | When Paid |
|---|---|---|
| Origination fee | 2% flat | Deducted upfront from USDB received |
| Daily interest | 0.005% per day | On collateral value, paid upfront |
| Extension fee | 0.005% per day | Paid 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:
| Duration | Origination | Interest | Total Cost |
|---|---|---|---|
| 10 days (minimum) | 2.00% | 0.05% | 2.05% |
| 30 days | 2.00% | 0.15% | 2.15% |
| 90 days | 2.00% | 0.45% | 2.45% |
| 365 days | 2.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)
| Param | Type | Description |
|---|---|---|
ecosystem | string | MAINTOKEN address for the collateral's ecosystem |
collateral | string | Collateral token address |
amount | bigint/int | Collateral amount (18 decimals) |
daysCount | bigint/int | Loan duration in days (minimum: 10, maximum: 1000) |
Pre-flight checks:
- Verify you hold sufficient collateral balance (use
getTokens()for an overview). daysCountmust be ≥ 10 — this is hardcoded in the contract. Passing 9 or less will revert.- 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 IDs — hubId 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 sequentialhubId. To find loans for a specific token, enumerate from 1 to count and checkdetails.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(fromgetUserLoanDetails()), 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)
| Param | Type | Description |
|---|---|---|
hubId | bigint/int | Hub loan ID |
addDays | bigint/int | Days to add |
payInStable | boolean | true = pay extension fee in USDB; false = deduct from collateral |
refinance | boolean | true = 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)
| Param | Type | Description |
|---|---|---|
hubId | bigint/int | Hub loan ID |
amountToAdd | bigint/int | Additional 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
| Param | Type | Description |
|---|---|---|
hubId | bigint/int | Hub loan ID |
percentage | bigint/int | Must be a multiple of 10 (10, 20, 30 ... 100). Non-multiples cause a silent contract revert. |
isLeverage | boolean | false for regular loans, true for leverage positions |
minOut | bigint/int | Minimum 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.stakingmodule, notclient.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 callclient.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):
| Method | What 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
| Action | Fee | Notes |
|---|---|---|
| Origination | 2% flat | Deducted upfront. One-time, non-refundable. |
| Daily interest | 0.005%/day | On collateral value, prepaid for full duration. |
| Extension | 0.005%/day | Same rate, paid upfront. ~400x cheaper than re-originating. |
| Repayment | Repay fullAmount | Includes prepaid interest. No discount for early repay. |
| Expiry (no repay) | Collateral burned/sold to cover debt | Remainder claimable via claimLiquidation(). |
| Partial sell | Standard trade fees on sold portion | Applied to the token sale portion only. |
Errors and Recovery
Common Errors
| Error | Cause | Fix |
|---|---|---|
| Transaction reverts silently | partialLoanSell percentage not a multiple of 10 | Use only 10, 20, 30 ... 100. |
Loan expired | Calling repay/extend after expiry timestamp | Call claimLiquidation() instead to claim remainder. |
Loan is still active | Calling claimLiquidation() before expiry | Wait until liquidationTime has passed, then claim. |
| Transaction reverts | daysCount < 10 on takeLoan() | Minimum is hardcoded at 10 days. Use 10n or higher. |
increaseLoan fails | Remaining days < 10 | Call extendLoan() first to add days, then increaseLoan(). |
staking.borrow() reverts | Wallet already has active vault loan | Use staking.addToLoan() to add collateral instead — see Module 06. |
partialLoanSell reverts or returns nothing | Called too soon after leverageBuy | Wait at least 5 seconds for backend to sync before calling. |
Insufficient balance | Not enough USDB to repay fullAmount | Get 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. UseaddToLoan()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
- Module 04 — Trading: Spend borrowed USDB or close leverage positions (different system!)
- Module 06 — Staking: Vault loans where collateral keeps earning yield
- Module 09 — Vesting: Take loans against locked vesting positions
- Module 11 — Token Mechanics: Floor math, Stable+ ratchet, hybrid multiplier
- Module 12 — Strategy & Stacking: Multi-loan chaining for capital efficiency
- Module 18 — SDK Reference: All
client.loansmethod signatures and full error index