diff --git a/.github/workflows/liquidator.yml b/.github/workflows/liquidator.yml index 35e06430..41fe6905 100644 --- a/.github/workflows/liquidator.yml +++ b/.github/workflows/liquidator.yml @@ -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 diff --git a/.github/workflows/solend-sdk.yml b/.github/workflows/solend-sdk.yml index 0ebb6d82..cbdac4c6 100644 --- a/.github/workflows/solend-sdk.yml +++ b/.github/workflows/solend-sdk.yml @@ -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 diff --git a/solend-sdk/package.json b/solend-sdk/package.json index 03eefecb..24b9ec93 100644 --- a/solend-sdk/package.json +++ b/solend-sdk/package.json @@ -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", diff --git a/solend-sdk/src/classes/reserve.ts b/solend-sdk/src/classes/reserve.ts index 3b1fb949..6ce97516 100644 --- a/solend-sdk/src/classes/reserve.ts +++ b/solend-sdk/src/classes/reserve.ts @@ -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>["info"]; @@ -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 | null) { @@ -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}`, ); } @@ -133,7 +83,7 @@ export class SolendReserve { poolSize: string, rewardPrice: number, tokenPrice: number, - decimals: number + decimals: number, ) { const poolValueUSD = new BigNumber(poolSize) .times(tokenPrice) @@ -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(); @@ -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(); @@ -215,11 +165,11 @@ export class SolendReserve { } private formatReserveData( - parsedData: NonNullable>["info"] + parsedData: NonNullable>["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()) @@ -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(), @@ -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(), diff --git a/solend-sdk/src/classes/shared.ts b/solend-sdk/src/classes/shared.ts index efb8d3ee..0a520d79 100644 --- a/solend-sdk/src/classes/shared.ts +++ b/solend-sdk/src/classes/shared.ts @@ -93,6 +93,8 @@ export type ReserveDataType = { totalLiquidityWads: BN; supplyInterestAPY: number; borrowInterestAPY: number; + supplyInterestAPR: number; + borrowInterestAPR: number; assetPriceUSD: number; protocolTakeRate: number; userDepositLimit?: number; diff --git a/solend-sdk/src/core/utils/rates.ts b/solend-sdk/src/core/utils/rates.ts index ae0dcd45..f91fedb2 100644 --- a/solend-sdk/src/core/utils/rates.ts +++ b/solend-sdk/src/core/utils/rates.ts @@ -7,7 +7,7 @@ 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); @@ -15,10 +15,10 @@ const calculateSupplyAPR = (reserve: Reserve) => { 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); @@ -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)) { @@ -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 @@ -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); @@ -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