This guide covers integration with the LOAN lending protocol on XPR Network.
LOAN is the native lending protocol on XPR Network enabling:
- Supply assets to earn interest - Deposit tokens, receive L-tokens (shares)
- Collateralized loans - Borrow against supplied collateral
- Variable interest rates - Supply/demand-based APY
- Liquidations - Automated health factor monitoring
- LOAN token rewards - Earn LOAN governance tokens on supply/borrow
- Governance - LOAN token holders govern protocol
| Contract | Description |
|---|---|
lending.loan |
Main lending/borrowing/redeem logic |
shares.loan |
L-token share receipts (LXPR, LUSDC, etc.) |
loan.token |
LOAN governance token |
oracles |
Price feeds for collateral |
⚠️ Important: The main contract islending.loan, NOTlending. Using the wrong contract will cause transactions to fail.
When you supply assets, you receive L-tokens representing your share of the pool:
| Supplied | L-Token Received | Precision | Token Contract |
|---|---|---|---|
| XPR | LXPR | 4 | shares.loan |
| XUSDC | LUSDC | 6 | shares.loan |
| XBTC | LBTC | 8 | shares.loan |
| XETH | LETH | 8 | shares.loan |
| XMD | LXMD | 6 | shares.loan |
| XUSDT | LUSDT | 6 | shares.loan |
| XMT (MTL — Metal DAO) | LXMT | 8 | shares.loan |
| XXRP | LXRP | 6 | shares.loan |
| XDOGE | LDOGE | 6 | shares.loan |
| XHBAR | LHBAR | 6 | shares.loan |
| XADA | LADA | 6 | shares.loan |
| XXLM | LXLM | 6 | shares.loan |
| XLTC | LLTC | 8 | shares.loan |
| XSOL | LSOL | 6 | shares.loan |
L-token values increase over time as interest accrues, so you get back more than you deposited.
Health factor measures loan safety:
Health Factor = (Collateral Value × Liquidation Threshold) / Debt Value
| Health Factor | Status |
|---|---|
| > 1.5 | Safe |
| 1.0 - 1.5 | Warning |
| < 1.0 | Liquidatable |
Maximum borrowing power as percentage of collateral:
| Asset | Collateral Factor |
|---|---|
| XPR | 40% |
| XUSDC | 80% |
| XBTC | 70% |
| XETH | 70% |
| XMD | 90% |
| XMT (MTL — Metal DAO) | 50% |
| XDOGE | 60% |
| XADA | 60% |
| XLTC | 60% |
| XSOL | 60% |
| XXLM | 60% |
| XXRP | 60% |
| XHBAR | 60% |
Before supplying, you must enter the markets for the assets you want to use:
# Enter XPR and XUSDC markets
proton action lending.loan entermarkets '{"payer":"myaccount","user":"myaccount","markets":["LXPR","LUSDC"]}' myaccountTransfer tokens to lending.loan with memo mint:
# Supply 40000 XPR
proton action eosio.token transfer '{"from":"myaccount","to":"lending.loan","quantity":"40000.0000 XPR","memo":"mint"}' myaccount
# Supply 100 XUSDC
proton action xtokens transfer '{"from":"myaccount","to":"lending.loan","quantity":"100.000000 XUSDC","memo":"mint"}' myaccountasync function supply(
session: any,
quantity: string,
tokenContract: string // 'eosio.token' for XPR, 'xtokens' for wrapped tokens
): Promise<any> {
return session.transact({
actions: [{
account: tokenContract,
name: 'transfer',
authorization: [session.auth],
data: {
from: session.auth.actor,
to: 'lending.loan',
quantity: quantity,
memo: 'mint'
}
}]
}, { broadcast: true });
}
// Example: Supply 1000 XUSDC
await supply(session, '1000.000000 XUSDC', 'xtokens');
// Example: Supply 10000 XPR
await supply(session, '10000.0000 XPR', 'eosio.token');Note: The
mintmemo triggers L-token minting. Your L-token shares appear in thelending.loansharestable scoped tolending.loan.
# Check your L-token shares
curl -s -X POST https://proton.eosusa.io/v1/chain/get_table_rows \
-H 'Content-Type: application/json' \
-d '{"code":"lending.loan","scope":"lending.loan","table":"shares","lower_bound":"myaccount","upper_bound":"myaccount","limit":1,"json":true}'Response format:
{
"rows": [{
"account": "myaccount",
"tokens": [
{
"key": {"sym": "4,LXPR", "contract": "shares.loan"},
"value": 390334875
},
{
"key": {"sym": "6,LUSDC", "contract": "shares.loan"},
"value": 71479562
}
]
}]
}The value field is the raw integer. Divide by 10^precision to get the human-readable amount:
- LXPR value
390334875with precision 4 =39033.4875 LXPR - LUSDC value
71479562with precision 6 =71.479562 LUSDC
To remove assets from your entered markets (e.g., when you no longer want them used as collateral):
# Exit XPR and XUSDC markets
proton action lending.loan exitmarkets '{"user":"myaccount","markets":["LXPR","LUSDC"]}' myaccountasync function exitMarkets(
session: any,
markets: string[] // e.g. ['LXPR', 'LUSDC']
): Promise<any> {
return session.transact({
actions: [{
account: 'lending.loan',
name: 'exitmarkets',
authorization: [session.auth],
data: {
user: session.auth.actor,
markets: markets
}
}]
}, { broadcast: true });
}Note: You cannot exit a market if you have outstanding borrows against that collateral.
The redeem action burns your L-tokens and returns the underlying asset plus accrued interest.
The action takes an extended_asset parameter:
# Redeem all LXPR shares back to XPR
proton action lending.loan redeem '{"redeemer":"myaccount","token":{"quantity":"39033.4875 LXPR","contract":"shares.loan"}}' myaccount
# Redeem all LUSDC shares back to XUSDC
proton action lending.loan redeem '{"redeemer":"myaccount","token":{"quantity":"71.479562 LUSDC","contract":"shares.loan"}}' myaccountasync function redeem(
session: any,
quantity: string, // e.g. "39033.4875 LXPR"
): Promise<any> {
return session.transact({
actions: [{
account: 'lending.loan',
name: 'redeem',
authorization: [session.auth],
data: {
redeemer: session.auth.actor,
token: {
quantity: quantity,
contract: 'shares.loan'
}
}
}]
}, { broadcast: true });
}
// Example: Redeem all LXPR
await redeem(session, '39033.4875 LXPR');
⚠️ Precision matters! LXPR uses precision 4 (e.g.,39033.4875), LUSDC uses precision 6 (e.g.,71.479562). Using wrong precision will fail.
The redeem action will:
- Burn your L-tokens via
shares.loanretireaction - Issue LOAN reward tokens
- Transfer the underlying asset back to you (e.g., XPR via
eosio.token)
async function borrow(
session: any,
quantity: string,
tokenContract: string // 'eosio.token' for XPR, 'xtokens' for wrapped tokens
): Promise<any> {
// There is no health_factor column on-chain. Compute it yourself from
// `shares` (L-token balances), `markets` (collateral_factor, exchange rate)
// and oracle prices: sum(collateral value × collateral_factor) / sum(debt value).
const healthFactor = await computeHealthFactor(session.auth.actor);
if (healthFactor < 1.2) {
throw new Error('Health factor too low to borrow');
}
return session.transact({
actions: [{
account: 'lending.loan',
name: 'borrow',
authorization: [session.auth],
data: {
borrower: session.auth.actor,
underlying: {
quantity: quantity,
contract: tokenContract
},
use_stable_rate: false
}
}]
}, { broadcast: true });
}# Borrow 500 XPR against collateral
proton action lending.loan borrow '{"borrower":"myaccount","underlying":{"quantity":"500.0000 XPR","contract":"eosio.token"},"use_stable_rate":false}' myaccountTransfer tokens back with repay memo:
# Repay 500 XUSDC
proton action xtokens transfer '{"from":"myaccount","to":"lending.loan","quantity":"500.000000 XUSDC","memo":"repay"}' myaccount
# Repay 1000 XPR
proton action eosio.token transfer '{"from":"myaccount","to":"lending.loan","quantity":"1000.0000 XPR","memo":"repay"}' myaccountLOAN tokens are distributed as rewards for supplying and borrowing:
proton action lending.loan claimrewards '{"claimer":"myaccount","markets":["LXPR","LUSDC"]}' myaccountNote: Use the L-token symbol_code format (e.g.,
LXPRnot4,LXPR) for the markets parameter. You can only claim for markets you have active positions in.
async function getUserShares(account: string): Promise<any> {
const response = await fetch('https://proton.eosusa.io/v1/chain/get_table_rows', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code: 'lending.loan',
scope: 'lending.loan',
table: 'shares',
lower_bound: account,
upper_bound: account,
limit: 1,
json: true
})
});
const { rows } = await response.json();
return rows[0] ?? null;
}async function getMarkets(): Promise<any[]> {
const response = await fetch('https://proton.eosusa.io/v1/chain/get_table_rows', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code: 'lending.loan',
scope: 'lending.loan',
table: 'markets',
limit: 100,
json: true
})
});
const { rows } = await response.json();
return rows;
}async function getLiquidatableBorrows(): Promise<any[]> {
const response = await fetch('https://proton.eosusa.io/v1/chain/get_table_rows', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code: 'lending.loan',
scope: 'lending.loan',
table: 'borrows',
limit: 1000,
json: true
})
});
const { rows } = await response.json();
// `borrows` rows hold principals per token only — there is no health_factor
// column. Compute HF per account (collateral × collateral_factor vs. debt,
// both at oracle prices) and keep the ones below 1.0.
const scored = await Promise.all(
rows.map(async (p: any) => ({ ...p, hf: await computeHealthFactor(p.account) }))
);
return scored.filter((p) => p.hf < 1.0);
}# Liquidate an underwater position: repay with the borrowed token, memo is
# comma-separated and names the L-TOKEN collateral to seize (verified from a
# live log.seize: memo "liquidate,<borrower>,LXRP")
proton action xtokens transfer '{"from":"myaccount","to":"lending.loan","quantity":"500.000000 XUSDC","memo":"liquidate,targetaccount,LXPR"}' myaccountOne global setting, not per-collateral: globals.cfg.liquidation_incentive = 0.10 (10%) and close_factor = 0.10 (at most 10% of the debt per liquidation). Observed seizures pay 1 / (1 − 0.10) ≈ 11.1% more collateral value than debt repaid. Read it live:
proton table lending.loan globals.cfg| Property | Value |
|---|---|
| Contract | loan.token |
| Symbol | LOAN |
| Precision | 4 |
| Max Supply | Unbounded (0.0000 LOAN on the token contract — i.e. no cap) |
| Circulating (September 2026) | ~114.2 billion LOAN (114153323234.0560 LOAN) |
Note: Earlier versions of this doc claimed a 100,000,000 LOAN max supply. That was wrong —
eosio.token::statshowsmax_supply: 0.0000 LOAN(unbounded) andsupplyalready exceeds 113 billion. Verify the current supply with:curl -s -X POST https://proton.eosusa.io/v1/chain/get_currency_stats \ -H 'Content-Type: application/json' \ -d '{"code":"loan.token","symbol":"LOAN"}'
Note: LOAN staking may not be active on mainnet. The
loan.stakingcontract does not appear to exist as of March 2026. Check on-chain before attempting staking operations.
# XPR price (feed index 3)
curl -s -X POST https://proton.eosusa.io/v1/chain/get_table_rows \
-H 'Content-Type: application/json' \
-d '{"code":"oracles","scope":"oracles","table":"data","lower_bound":3,"upper_bound":3,"limit":1,"json":true}'Oracle feed indices for LOAN collateral assets (excerpt — the canonical full table with liveness notes is in oracles-randomness.md):
| Index | Asset |
|---|---|
| 3 | XPR/USD |
| 4 | BTC/USD |
| 6 | MTL/USD (XMT) |
| 7 | ETH/USD |
| 8 | DOGE/USD |
| 9 | USDT/USD |
| 16 | LTC/USD |
| 18 | XRP/USD |
| 19 | SOL/USD |
| 21 | HBAR/USD |
| 22 | ADA/USD |
| 23 | XLM/USD |
Index 5 (USDC/USD) and 12 (XMD/USD) are dormant (no updates in 1+ years) — use 9 (USDT/USD) as the dollar reference.
The whitelist table (updatewl(account, hash, add)) lists contract accounts by code hash that are permitted to call LOAN from contract code. End-user operations are not whitelist-gated: mint, borrow, repay and liquidate all succeed for ordinary accounts that are not in the table (verified September 2026 on a live borrow and liquidation by non-whitelisted accounts).
To check the whitelist:
curl -s -X POST https://proton.eosusa.io/v1/chain/get_table_rows \
-H 'Content-Type: application/json' \
-d '{"code":"lending.loan","scope":"lending.loan","table":"whitelist","limit":100,"json":true}'# Enter markets (required before first supply)
proton action lending.loan entermarkets '{"payer":"alice","user":"alice","markets":["LXPR","LUSDC"]}' alice
# Exit markets (remove collateral eligibility)
proton action lending.loan exitmarkets '{"user":"alice","markets":["LXPR","LUSDC"]}' alice
# Supply XPR (mint L-tokens)
proton action eosio.token transfer '{"from":"alice","to":"lending.loan","quantity":"1000.0000 XPR","memo":"mint"}' alice
# Supply XUSDC (mint L-tokens)
proton action xtokens transfer '{"from":"alice","to":"lending.loan","quantity":"100.000000 XUSDC","memo":"mint"}' alice
# Redeem LXPR back to XPR
proton action lending.loan redeem '{"redeemer":"alice","token":{"quantity":"975.1234 LXPR","contract":"shares.loan"}}' alice
# Redeem LUSDC back to XUSDC
proton action lending.loan redeem '{"redeemer":"alice","token":{"quantity":"71.479562 LUSDC","contract":"shares.loan"}}' alice
# Borrow
proton action lending.loan borrow '{"borrower":"alice","underlying":{"quantity":"500.0000 XPR","contract":"eosio.token"},"use_stable_rate":false}' alice
# Repay
proton action xtokens transfer '{"from":"alice","to":"lending.loan","quantity":"500.000000 XUSDC","memo":"repay"}' alice
# Claim LOAN rewards
proton action lending.loan claimrewards '{"claimer":"alice","markets":["LXPR","LUSDC"]}' alice
# Check shares
# Use RPC: code=lending.loan, scope=lending.loan, table=shares, lower_bound=alice, upper_bound=alice| Contract | Table | Scope | Description |
|---|---|---|---|
lending.loan |
shares |
lending.loan |
User L-token share balances |
lending.loan |
markets |
lending.loan |
Market stats, APYs, reserve ratios |
lending.loan |
borrows |
lending.loan |
User borrow positions: {account, tokens:[{key:{sym,contract}, value:{variable_principal, variable_interest_index, stable_principal, ...}}]} — no health-factor column |
lending.loan |
whitelist |
lending.loan |
Contract accounts (by code hash) allowed to call LOAN from contract code |
loan.token |
(standard token tables) | LOAN governance token | |
oracles |
data |
oracles |
Price feeds |
Use these endpoints for querying tables (in order of reliability):
https://proton.eosusa.io(recommended)https://proton.protonuk.iohttps://proton.cryptolions.io
- Contract is
lending.loan— notlending - Share tokens live on
shares.loan— not in your token balance - Memo
mintfor deposits, memorepayfor loan repayment - Redeem uses
extended_assetformat:{"quantity":"X LXPR","contract":"shares.loan"} - Precision must match exactly — LXPR=4, LUSDC=6, LBTC=8, etc.
- Enter markets first before supplying or borrowing
- LOAN rewards are issued during redeem and can also be claimed separately