Skip to content

Commit 2f433e3

Browse files
authored
Merge pull request #70 from silostack/fix/supply-apy-calculation
copied apy calculation code from solend-lite to sdk (calculation was broken)
2 parents 60d4abb + 4460b23 commit 2f433e3

File tree

6 files changed

+35
-81
lines changed

6 files changed

+35
-81
lines changed

.github/workflows/liquidator.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ jobs:
1818
working-directory: ./liquidator
1919
steps:
2020
- uses: actions/checkout@v3
21-
- name: Use Node.js 16
21+
- name: Use Node.js 18
2222
uses: actions/setup-node@v3
2323
with:
24-
node-version: 16
24+
node-version: 18
2525
- name: Install dependencies
2626
run: yarn install --immutable --immutable-cache --check-cache
2727
- name: Lint

.github/workflows/solend-sdk.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ jobs:
1818
working-directory: ./solend-sdk
1919
steps:
2020
- uses: actions/checkout@v3
21-
- name: Use Node.js 16
21+
- name: Use Node.js 18
2222
uses: actions/setup-node@v3
2323
with:
24-
node-version: 16
24+
node-version: 18
2525
- name: Install dependencies
2626
run: yarn install --immutable --immutable-cache --check-cache
2727
- name: Lint

solend-sdk/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@solendprotocol/solend-sdk",
3-
"version": "0.6.52",
3+
"version": "0.6.53",
44
"private": true,
55
"main": "src/index.ts",
66
"module": "src/index.ts",

solend-sdk/src/classes/reserve.ts

+20-68
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { parseReserve } from "../state/reserve";
55
import BN from "bn.js";
66
import { WAD, WANG } from "./constants";
77
import { ReserveConfigType, RewardsDataType, ReserveDataType } from "./shared";
8-
import { SLOTS_PER_YEAR } from "../core/constants";
8+
import { calculateBorrowInterest, calculateSupplyInterest } from "../core";
99

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

@@ -29,69 +29,19 @@ export class SolendReserve {
2929
}
3030

3131
private calculateSupplyAPY = (reserve: ParsedReserve) => {
32-
const apr = this.calculateSupplyAPR(reserve);
33-
const apy =
34-
new BigNumber(1)
35-
.plus(new BigNumber(apr).dividedBy(SLOTS_PER_YEAR))
36-
.toNumber() **
37-
SLOTS_PER_YEAR -
38-
1;
39-
return apy;
32+
return calculateSupplyInterest(reserve, true).toNumber();
4033
};
4134

4235
private calculateBorrowAPY = (reserve: ParsedReserve) => {
43-
const apr = this.calculateBorrowAPR(reserve);
44-
const apy =
45-
new BigNumber(1)
46-
.plus(new BigNumber(apr).dividedBy(SLOTS_PER_YEAR))
47-
.toNumber() **
48-
SLOTS_PER_YEAR -
49-
1;
50-
return apy;
36+
return calculateBorrowInterest(reserve, true).toNumber();
5137
};
5238

5339
private calculateSupplyAPR(reserve: ParsedReserve) {
54-
const currentUtilization = this.calculateUtilizationRatio(reserve);
55-
56-
const borrowAPY = this.calculateBorrowAPR(reserve);
57-
return currentUtilization * borrowAPY;
58-
}
59-
60-
private calculateUtilizationRatio(reserve: ParsedReserve) {
61-
const totalBorrowsWads = new BigNumber(
62-
reserve.liquidity.borrowedAmountWads.toString()
63-
).div(WAD);
64-
const currentUtilization = totalBorrowsWads
65-
.dividedBy(
66-
totalBorrowsWads.plus(reserve.liquidity.availableAmount.toString())
67-
)
68-
.toNumber();
69-
70-
return currentUtilization;
40+
return calculateSupplyInterest(reserve, false).toNumber();
7141
}
7242

7343
private calculateBorrowAPR(reserve: ParsedReserve) {
74-
const currentUtilization = this.calculateUtilizationRatio(reserve);
75-
const optimalUtilization = reserve.config.optimalUtilizationRate / 100;
76-
77-
let borrowAPR;
78-
if (optimalUtilization === 1.0 || currentUtilization < optimalUtilization) {
79-
const normalizedFactor = currentUtilization / optimalUtilization;
80-
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
81-
const minBorrowRate = reserve.config.minBorrowRate / 100;
82-
borrowAPR =
83-
normalizedFactor * (optimalBorrowRate - minBorrowRate) + minBorrowRate;
84-
} else {
85-
const normalizedFactor =
86-
(currentUtilization - optimalUtilization) / (1 - optimalUtilization);
87-
const optimalBorrowRate = reserve.config.optimalBorrowRate / 100;
88-
const maxBorrowRate = reserve.config.maxBorrowRate / 100;
89-
borrowAPR =
90-
normalizedFactor * (maxBorrowRate - optimalBorrowRate) +
91-
optimalBorrowRate;
92-
}
93-
94-
return borrowAPR;
44+
return calculateBorrowInterest(reserve, false).toNumber();
9545
}
9646

9747
setBuffer(buffer: AccountInfo<Buffer> | null) {
@@ -105,23 +55,23 @@ export class SolendReserve {
10555
if (!this.buffer) {
10656
this.buffer = await this.connection.getAccountInfo(
10757
new PublicKey(this.config.address),
108-
"processed"
58+
"processed",
10959
);
11060
}
11161

11262
if (!this.buffer) {
11363
throw Error(
114-
`Error requesting account info for ${this.config.liquidityToken.name}`
64+
`Error requesting account info for ${this.config.liquidityToken.name}`,
11565
);
11666
}
11767

11868
const parsedData = parseReserve(
11969
new PublicKey(this.config.address),
120-
this.buffer
70+
this.buffer,
12171
)?.info;
12272
if (!parsedData) {
12373
throw Error(
124-
`Unable to parse data of reserve ${this.config.liquidityToken.name}`
74+
`Unable to parse data of reserve ${this.config.liquidityToken.name}`,
12575
);
12676
}
12777

@@ -133,7 +83,7 @@ export class SolendReserve {
13383
poolSize: string,
13484
rewardPrice: number,
13585
tokenPrice: number,
136-
decimals: number
86+
decimals: number,
13787
) {
13888
const poolValueUSD = new BigNumber(poolSize)
13989
.times(tokenPrice)
@@ -162,14 +112,14 @@ export class SolendReserve {
162112
stats.totalDepositsWads.toString(),
163113
reward.price,
164114
stats.assetPriceUSD,
165-
this.config.liquidityToken.decimals
115+
this.config.liquidityToken.decimals,
166116
).toNumber(),
167117
price: reward.price,
168118
}));
169119

170120
const totalAPY = new BigNumber(stats.supplyInterestAPY)
171121
.plus(
172-
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0))
122+
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0)),
173123
)
174124
.toNumber();
175125

@@ -196,14 +146,14 @@ export class SolendReserve {
196146
stats.totalBorrowsWads.toString(),
197147
reward.price,
198148
stats.assetPriceUSD,
199-
this.config.liquidityToken.decimals
149+
this.config.liquidityToken.decimals,
200150
).toNumber(),
201151
price: reward.price,
202152
}));
203153

204154
const totalAPY = new BigNumber(stats.borrowInterestAPY)
205155
.minus(
206-
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0))
156+
rewards.reduce((acc, reward) => acc.plus(reward.apy), new BigNumber(0)),
207157
)
208158
.toNumber();
209159

@@ -215,11 +165,11 @@ export class SolendReserve {
215165
}
216166

217167
private formatReserveData(
218-
parsedData: NonNullable<ReturnType<typeof parseReserve>>["info"]
168+
parsedData: NonNullable<ReturnType<typeof parseReserve>>["info"],
219169
): ReserveDataType {
220170
const totalBorrowsWads = parsedData.liquidity.borrowedAmountWads;
221171
const totalLiquidityWads = parsedData.liquidity.availableAmount.mul(
222-
new BN(WAD)
172+
new BN(WAD),
223173
);
224174
const totalDepositsWads = totalBorrowsWads.add(totalLiquidityWads);
225175
const cTokenExchangeRate = new BigNumber(totalDepositsWads.toString())
@@ -238,13 +188,13 @@ export class SolendReserve {
238188
maxBorrowRate: parsedData.config.maxBorrowRate / 100,
239189
protocolTakeRate: parsedData.config.protocolTakeRate / 100,
240190
borrowFeePercentage: new BigNumber(
241-
parsedData.config.fees.borrowFeeWad.toString()
191+
parsedData.config.fees.borrowFeeWad.toString(),
242192
)
243193
.dividedBy(WAD)
244194
.toNumber(),
245195
hostFeePercentage: parsedData.config.fees.hostFeePercentage / 100,
246196
flashLoanFeePercentage: new BigNumber(
247-
parsedData.config.fees.flashLoanFeeWad.toString()
197+
parsedData.config.fees.flashLoanFeeWad.toString(),
248198
)
249199
.dividedBy(WAD)
250200
.toNumber(),
@@ -262,6 +212,8 @@ export class SolendReserve {
262212
totalLiquidityWads,
263213
supplyInterestAPY: this.calculateSupplyAPY(parsedData),
264214
borrowInterestAPY: this.calculateBorrowAPY(parsedData),
215+
supplyInterestAPR: this.calculateSupplyAPR(parsedData),
216+
borrowInterestAPR: this.calculateBorrowAPR(parsedData),
265217
assetPriceUSD: new BigNumber(parsedData.liquidity.marketPrice.toString())
266218
.div(WAD)
267219
.toNumber(),

solend-sdk/src/classes/shared.ts

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ export type ReserveDataType = {
9393
totalLiquidityWads: BN;
9494
supplyInterestAPY: number;
9595
borrowInterestAPY: number;
96+
supplyInterestAPR: number;
97+
borrowInterestAPR: number;
9698
assetPriceUSD: number;
9799
protocolTakeRate: number;
98100
userDepositLimit?: number;

solend-sdk/src/core/utils/rates.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ const calculateSupplyAPR = (reserve: Reserve) => {
77

88
const borrowAPR = calculateBorrowAPR(reserve);
99
const protocolTakePercentage = BigNumber(1).minus(
10-
reserve.config.protocolTakeRate / 100
10+
reserve.config.protocolTakeRate / 100,
1111
);
1212

1313
return currentUtilization.times(borrowAPR).times(protocolTakePercentage);
1414
};
1515

1616
const calculateUtilizationRatio = (reserve: Reserve) => {
1717
const borrowedAmount = new BigNumber(
18-
reserve.liquidity.borrowedAmountWads.toString()
18+
reserve.liquidity.borrowedAmountWads.toString(),
1919
).shiftedBy(-18);
2020
const totalSupply = borrowedAmount.plus(
21-
reserve.liquidity.availableAmount.toString()
21+
reserve.liquidity.availableAmount.toString(),
2222
);
2323
const currentUtilization = borrowedAmount.dividedBy(totalSupply);
2424

@@ -28,10 +28,10 @@ const calculateUtilizationRatio = (reserve: Reserve) => {
2828
const calculateBorrowAPR = (reserve: Reserve) => {
2929
const currentUtilization = calculateUtilizationRatio(reserve);
3030
const optimalUtilization = new BigNumber(
31-
reserve.config.optimalUtilizationRate / 100
31+
reserve.config.optimalUtilizationRate / 100,
3232
);
3333
const maxUtilizationRate = new BigNumber(
34-
reserve.config.maxUtilizationRate / 100
34+
reserve.config.maxUtilizationRate / 100,
3535
);
3636
let borrowAPR;
3737
if (currentUtilization.isLessThanOrEqualTo(optimalUtilization)) {
@@ -42,7 +42,7 @@ const calculateBorrowAPR = (reserve: Reserve) => {
4242
const normalizedFactor = currentUtilization.dividedBy(optimalUtilization);
4343

4444
const optimalBorrowRate = new BigNumber(
45-
reserve.config.optimalBorrowRate / 100
45+
reserve.config.optimalBorrowRate / 100,
4646
);
4747

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

5656
const optimalBorrowRate = new BigNumber(
57-
reserve.config.optimalBorrowRate / 100
57+
reserve.config.optimalBorrowRate / 100,
5858
);
5959
const maxBorrowRate = new BigNumber(reserve.config.maxBorrowRate / 100);
6060

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

6969
const maxBorrowRate = new BigNumber(reserve.config.maxBorrowRate / 100);
7070
const superMaxBorrowRate = new BigNumber(
71-
reserve.config.superMaxBorrowRate.toNumber() / 100
71+
reserve.config.superMaxBorrowRate.toNumber() / 100,
7272
);
7373

7474
borrowAPR = weight

0 commit comments

Comments
 (0)