Skip to content

Commit

Permalink
Merge pull request #70 from silostack/fix/supply-apy-calculation
Browse files Browse the repository at this point in the history
copied apy calculation code from solend-lite to sdk (calculation was broken)
  • Loading branch information
nope-finance authored Mar 20, 2024
2 parents 47569cc + 7ad497f commit 892c3d7
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 81 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/liquidator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
working-directory: ./liquidator
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
run: yarn install --immutable --immutable-cache --check-cache
- name: Lint
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/solend-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
working-directory: ./solend-sdk
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
run: yarn install --immutable --immutable-cache --check-cache
- name: Lint
Expand Down
2 changes: 1 addition & 1 deletion solend-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@solendprotocol/solend-sdk",
"version": "0.6.52",
"version": "0.6.53",
"private": true,
"main": "src/index.ts",
"module": "src/index.ts",
Expand Down
88 changes: 20 additions & 68 deletions solend-sdk/src/classes/reserve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { parseReserve } from "../state/reserve";
import BN from "bn.js";
import { WAD, WANG } from "./constants";
import { ReserveConfigType, RewardsDataType, ReserveDataType } from "./shared";
import { SLOTS_PER_YEAR } from "../core/constants";
import { calculateBorrowInterest, calculateSupplyInterest } from "../core";

type ParsedReserve = NonNullable<ReturnType<typeof parseReserve>>["info"];

Expand All @@ -29,69 +29,19 @@ export class SolendReserve {
}

private calculateSupplyAPY = (reserve: ParsedReserve) => {
const apr = this.calculateSupplyAPR(reserve);
const apy =
new BigNumber(1)
.plus(new BigNumber(apr).dividedBy(SLOTS_PER_YEAR))
.toNumber() **
SLOTS_PER_YEAR -
1;
return apy;
return calculateSupplyInterest(reserve, true).toNumber();
};

private calculateBorrowAPY = (reserve: ParsedReserve) => {
const apr = this.calculateBorrowAPR(reserve);
const apy =
new BigNumber(1)
.plus(new BigNumber(apr).dividedBy(SLOTS_PER_YEAR))
.toNumber() **
SLOTS_PER_YEAR -
1;
return apy;
return calculateBorrowInterest(reserve, true).toNumber();
};

private calculateSupplyAPR(reserve: ParsedReserve) {
const currentUtilization = this.calculateUtilizationRatio(reserve);

const borrowAPY = this.calculateBorrowAPR(reserve);
return currentUtilization * borrowAPY;
}

private calculateUtilizationRatio(reserve: ParsedReserve) {
const totalBorrowsWads = new BigNumber(
reserve.liquidity.borrowedAmountWads.toString()
).div(WAD);
const currentUtilization = totalBorrowsWads
.dividedBy(
totalBorrowsWads.plus(reserve.liquidity.availableAmount.toString())
)
.toNumber();

return currentUtilization;
return calculateSupplyInterest(reserve, false).toNumber();
}

private calculateBorrowAPR(reserve: ParsedReserve) {
const currentUtilization = this.calculateUtilizationRatio(reserve);
const optimalUtilization = reserve.config.optimalUtilizationRate / 100;

let borrowAPR;
if (optimalUtilization === 1.0 || currentUtilization < optimalUtilization) {
const normalizedFactor = currentUtilization / optimalUtilization;
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
const minBorrowRate = reserve.config.minBorrowRate / 100;
borrowAPR =
normalizedFactor * (optimalBorrowRate - minBorrowRate) + minBorrowRate;
} else {
const normalizedFactor =
(currentUtilization - optimalUtilization) / (1 - optimalUtilization);
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
const maxBorrowRate = reserve.config.maxBorrowRate / 100;
borrowAPR =
normalizedFactor * (maxBorrowRate - optimalBorrowRate) +
optimalBorrowRate;
}

return borrowAPR;
return calculateBorrowInterest(reserve, false).toNumber();
}

setBuffer(buffer: AccountInfo<Buffer> | null) {
Expand All @@ -105,23 +55,23 @@ export class SolendReserve {
if (!this.buffer) {
this.buffer = await this.connection.getAccountInfo(
new PublicKey(this.config.address),
"processed"
"processed",
);
}

if (!this.buffer) {
throw Error(
`Error requesting account info for ${this.config.liquidityToken.name}`
`Error requesting account info for ${this.config.liquidityToken.name}`,
);
}

const parsedData = parseReserve(
new PublicKey(this.config.address),
this.buffer
this.buffer,
)?.info;
if (!parsedData) {
throw Error(
`Unable to parse data of reserve ${this.config.liquidityToken.name}`
`Unable to parse data of reserve ${this.config.liquidityToken.name}`,
);
}

Expand All @@ -133,7 +83,7 @@ export class SolendReserve {
poolSize: string,
rewardPrice: number,
tokenPrice: number,
decimals: number
decimals: number,
) {
const poolValueUSD = new BigNumber(poolSize)
.times(tokenPrice)
Expand Down Expand Up @@ -162,14 +112,14 @@ export class SolendReserve {
stats.totalDepositsWads.toString(),
reward.price,
stats.assetPriceUSD,
this.config.liquidityToken.decimals
this.config.liquidityToken.decimals,
).toNumber(),
price: reward.price,
}));

const totalAPY = new BigNumber(stats.supplyInterestAPY)
.plus(
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0))
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0)),
)
.toNumber();

Expand All @@ -196,14 +146,14 @@ export class SolendReserve {
stats.totalBorrowsWads.toString(),
reward.price,
stats.assetPriceUSD,
this.config.liquidityToken.decimals
this.config.liquidityToken.decimals,
).toNumber(),
price: reward.price,
}));

const totalAPY = new BigNumber(stats.borrowInterestAPY)
.minus(
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0))
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0)),
)
.toNumber();

Expand All @@ -215,11 +165,11 @@ export class SolendReserve {
}

private formatReserveData(
parsedData: NonNullable<ReturnType<typeof parseReserve>>["info"]
parsedData: NonNullable<ReturnType<typeof parseReserve>>["info"],
): ReserveDataType {
const totalBorrowsWads = parsedData.liquidity.borrowedAmountWads;
const totalLiquidityWads = parsedData.liquidity.availableAmount.mul(
new BN(WAD)
new BN(WAD),
);
const totalDepositsWads = totalBorrowsWads.add(totalLiquidityWads);
const cTokenExchangeRate = new BigNumber(totalDepositsWads.toString())
Expand All @@ -238,13 +188,13 @@ export class SolendReserve {
maxBorrowRate: parsedData.config.maxBorrowRate / 100,
protocolTakeRate: parsedData.config.protocolTakeRate / 100,
borrowFeePercentage: new BigNumber(
parsedData.config.fees.borrowFeeWad.toString()
parsedData.config.fees.borrowFeeWad.toString(),
)
.dividedBy(WAD)
.toNumber(),
hostFeePercentage: parsedData.config.fees.hostFeePercentage / 100,
flashLoanFeePercentage: new BigNumber(
parsedData.config.fees.flashLoanFeeWad.toString()
parsedData.config.fees.flashLoanFeeWad.toString(),
)
.dividedBy(WAD)
.toNumber(),
Expand All @@ -262,6 +212,8 @@ export class SolendReserve {
totalLiquidityWads,
supplyInterestAPY: this.calculateSupplyAPY(parsedData),
borrowInterestAPY: this.calculateBorrowAPY(parsedData),
supplyInterestAPR: this.calculateSupplyAPR(parsedData),
borrowInterestAPR: this.calculateBorrowAPR(parsedData),
assetPriceUSD: new BigNumber(parsedData.liquidity.marketPrice.toString())
.div(WAD)
.toNumber(),
Expand Down
2 changes: 2 additions & 0 deletions solend-sdk/src/classes/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export type ReserveDataType = {
totalLiquidityWads: BN;
supplyInterestAPY: number;
borrowInterestAPY: number;
supplyInterestAPR: number;
borrowInterestAPR: number;
assetPriceUSD: number;
protocolTakeRate: number;
userDepositLimit?: number;
Expand Down
16 changes: 8 additions & 8 deletions solend-sdk/src/core/utils/rates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ const calculateSupplyAPR = (reserve: Reserve) => {

const borrowAPR = calculateBorrowAPR(reserve);
const protocolTakePercentage = BigNumber(1).minus(
reserve.config.protocolTakeRate / 100
reserve.config.protocolTakeRate / 100,
);

return currentUtilization.times(borrowAPR).times(protocolTakePercentage);
};

const calculateUtilizationRatio = (reserve: Reserve) => {
const borrowedAmount = new BigNumber(
reserve.liquidity.borrowedAmountWads.toString()
reserve.liquidity.borrowedAmountWads.toString(),
).shiftedBy(-18);
const totalSupply = borrowedAmount.plus(
reserve.liquidity.availableAmount.toString()
reserve.liquidity.availableAmount.toString(),
);
const currentUtilization = borrowedAmount.dividedBy(totalSupply);

Expand All @@ -28,10 +28,10 @@ const calculateUtilizationRatio = (reserve: Reserve) => {
const calculateBorrowAPR = (reserve: Reserve) => {
const currentUtilization = calculateUtilizationRatio(reserve);
const optimalUtilization = new BigNumber(
reserve.config.optimalUtilizationRate / 100
reserve.config.optimalUtilizationRate / 100,
);
const maxUtilizationRate = new BigNumber(
reserve.config.maxUtilizationRate / 100
reserve.config.maxUtilizationRate / 100,
);
let borrowAPR;
if (currentUtilization.isLessThanOrEqualTo(optimalUtilization)) {
Expand All @@ -42,7 +42,7 @@ const calculateBorrowAPR = (reserve: Reserve) => {
const normalizedFactor = currentUtilization.dividedBy(optimalUtilization);

const optimalBorrowRate = new BigNumber(
reserve.config.optimalBorrowRate / 100
reserve.config.optimalBorrowRate / 100,
);

borrowAPR = normalizedFactor
Expand All @@ -54,7 +54,7 @@ const calculateBorrowAPR = (reserve: Reserve) => {
.dividedBy(maxUtilizationRate.minus(optimalUtilization));

const optimalBorrowRate = new BigNumber(
reserve.config.optimalBorrowRate / 100
reserve.config.optimalBorrowRate / 100,
);
const maxBorrowRate = new BigNumber(reserve.config.maxBorrowRate / 100);

Expand All @@ -68,7 +68,7 @@ const calculateBorrowAPR = (reserve: Reserve) => {

const maxBorrowRate = new BigNumber(reserve.config.maxBorrowRate / 100);
const superMaxBorrowRate = new BigNumber(
reserve.config.superMaxBorrowRate.toNumber() / 100
reserve.config.superMaxBorrowRate.toNumber() / 100,
);

borrowAPR = weight
Expand Down

0 comments on commit 892c3d7

Please sign in to comment.