Solend has 3 types of accounts: lending market, reserve, and obligation.
There's only one instance of a lending market for now. There's a one-to-one mapping from listed asset to reserve. There's a one-to-one mapping from user to obligations (which tracks user's collateral/borrows).
Calculating TVL from on-chain data example
Deriving obligation address
Copy const seed = LENDING_MARKET_ADDRESS.slice(0, 32);
const obligationAddress = await PublicKey.createWithSeed(
new PublicKey(address),
seed,
new PublicKey(SOLEND_PROGRAM_ID),
);
Copy export const calculateSupplyAPY = (reserve: Reserve) => {
const currentUtilization = calculateUtilizationRatio(reserve);
const borrowAPY = calculateBorrowAPY(reserve);
return currentUtilization * borrowAPY;
};
export const calculateUtilizationRatio = (reserve: Reserve) => {
const borrowedAmount = reserve.liquidity.borrowedAmountWads
.div(new BN(`1${''.padEnd(18, '0')}`))
.toNumber();
const availableAmount = reserve.liquidity.availableAmount.toNumber();
const currentUtilization =
borrowedAmount / (availableAmount + borrowedAmount);
return currentUtilization;
};
export const calculateBorrowAPY = (reserve: Reserve) => {
const currentUtilization = calculateUtilizationRatio(reserve);
const optimalUtilization = reserve.config.optimalUtilizationRate / 100;
let borrowAPY;
if (optimalUtilization === 1.0 || currentUtilization < optimalUtilization) {
const normalizedFactor = currentUtilization / optimalUtilization;
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
const minBorrowRate = reserve.config.minBorrowRate / 100;
borrowAPY =
normalizedFactor * (optimalBorrowRate - minBorrowRate) + minBorrowRate;
} else {
const normalizedFactor =
(currentUtilization - optimalUtilization) / (1 - optimalUtilization);
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
const maxBorrowRate = reserve.config.maxBorrowRate / 100;
borrowAPY =
normalizedFactor * (maxBorrowRate - optimalBorrowRate) +
optimalBorrowRate;
}
return borrowAPY;
};