From a2e765d430a7563e98c4ed3aa0680316816efe80 Mon Sep 17 00:00:00 2001
From: 0xshiba1 <158560741+0xshiba1@users.noreply.github.com>
Date: Mon, 15 Jul 2024 15:21:11 +0800
Subject: [PATCH] prepare for borrow rewards
---
.../dashboard/AprWithRewardsBreakdown.tsx | 22 +--
.../src/components/dashboard/MarketTable.tsx | 2 +
.../dashboard/account/AccountPositionCard.tsx | 3 +
.../actions-modal/HistoricalAprLineChart.tsx | 40 +++--
frontend/src/lib/events.ts | 144 ++++++++++--------
frontend/src/lib/format.ts | 14 +-
frontend/src/lib/liquidityMining.ts | 4 +-
frontend/src/pages/index.tsx | 3 +
frontend/src/pages/swap/[[...slug]].tsx | 2 +
9 files changed, 142 insertions(+), 92 deletions(-)
diff --git a/frontend/src/components/dashboard/AprWithRewardsBreakdown.tsx b/frontend/src/components/dashboard/AprWithRewardsBreakdown.tsx
index d288d210..8c22ef7b 100644
--- a/frontend/src/components/dashboard/AprWithRewardsBreakdown.tsx
+++ b/frontend/src/components/dashboard/AprWithRewardsBreakdown.tsx
@@ -79,8 +79,8 @@ const formatAprPercent = (
showChange: boolean,
) =>
showChange && !newValue.eq(value)
- ? `${formatPercent(value)} → ${isAprModifierInvalid ? "N/A" : formatPercent(newValue)}`
- : formatPercent(value);
+ ? `${formatPercent(value, { useAccountingSign: true })} → ${isAprModifierInvalid ? "N/A" : formatPercent(newValue, { useAccountingSign: true })}`
+ : formatPercent(value, { useAccountingSign: true });
interface AprWithRewardsBreakdownProps {
side: Side;
@@ -159,13 +159,11 @@ export default function AprWithRewardsBreakdown({
}));
// Total APR
- const totalAprPercent = getTotalAprPercent(aprPercent, filteredRewards);
- const newTotalAprPercent = newAprRewards.reduce(
- (acc, reward) =>
- side === Side.DEPOSIT
- ? acc.plus(reward.stats.aprPercent)
- : acc.minus(reward.stats.aprPercent),
+ const totalAprPercent = getTotalAprPercent(side, aprPercent, filteredRewards);
+ const newTotalAprPercent = getTotalAprPercent(
+ side,
newAprPercent,
+ newAprRewards,
);
if (filteredRewards.length === 0)
@@ -263,8 +261,12 @@ export default function AprWithRewardsBreakdown({
key={index}
isLast={index === aprRewards.length - 1}
value={formatAprPercent(
- reward.stats.aprPercent,
- newAprRewards[index].stats.aprPercent,
+ reward.stats.aprPercent.times(
+ side === Side.DEPOSIT ? 1 : -1,
+ ),
+ newAprRewards[index].stats.aprPercent.times(
+ side === Side.DEPOSIT ? 1 : -1,
+ ),
isAprModifierInvalid,
showChange,
)}
diff --git a/frontend/src/components/dashboard/MarketTable.tsx b/frontend/src/components/dashboard/MarketTable.tsx
index b3761bab..d7470e70 100644
--- a/frontend/src/components/dashboard/MarketTable.tsx
+++ b/frontend/src/components/dashboard/MarketTable.tsx
@@ -143,11 +143,13 @@ export default function MarketTable() {
const borrowedAmountUsd = reserve.borrowedAmountUsd;
const depositAprPercent = reserve.depositAprPercent;
const totalDepositAprPercent = getTotalAprPercent(
+ Side.DEPOSIT,
reserve.depositAprPercent,
getFilteredRewards(data.rewardMap[coinType].deposit),
);
const borrowAprPercent = reserve.borrowAprPercent;
const totalBorrowAprPercent = getTotalAprPercent(
+ Side.BORROW,
reserve.borrowAprPercent,
getFilteredRewards(data.rewardMap[coinType].borrow),
);
diff --git a/frontend/src/components/dashboard/account/AccountPositionCard.tsx b/frontend/src/components/dashboard/account/AccountPositionCard.tsx
index dd5fb2b2..b1714451 100644
--- a/frontend/src/components/dashboard/account/AccountPositionCard.tsx
+++ b/frontend/src/components/dashboard/account/AccountPositionCard.tsx
@@ -4,6 +4,7 @@ import BigNumber from "bignumber.js";
import { AlertTriangle, FileClock, TrendingUp } from "lucide-react";
import { ParsedObligation } from "@suilend/sdk/parsers/obligation";
+import { Side } from "@suilend/sdk/types";
import AccountBreakdown from "@/components/dashboard/account/AccountBreakdown";
import BorrowLimitTitle from "@/components/dashboard/account/BorrowLimitTitle";
@@ -46,6 +47,7 @@ function AccountPositionCardContent() {
// APR
const aprWeightedDepositsUsd = obligation.deposits.reduce((acc, deposit) => {
const totalAprPercent = getTotalAprPercent(
+ Side.DEPOSIT,
deposit.reserve.depositAprPercent,
getFilteredRewards(data.rewardMap[deposit.reserve.coinType].deposit),
);
@@ -55,6 +57,7 @@ function AccountPositionCardContent() {
const aprWeightedBorrowsUsd = obligation.borrows.reduce((acc, borrow) => {
const totalAprPercent = getTotalAprPercent(
+ Side.BORROW,
borrow.reserve.borrowAprPercent,
getFilteredRewards(data.rewardMap[borrow.reserve.coinType].borrow),
);
diff --git a/frontend/src/components/dashboard/actions-modal/HistoricalAprLineChart.tsx b/frontend/src/components/dashboard/actions-modal/HistoricalAprLineChart.tsx
index 3f285d98..3691b22a 100644
--- a/frontend/src/components/dashboard/actions-modal/HistoricalAprLineChart.tsx
+++ b/frontend/src/components/dashboard/actions-modal/HistoricalAprLineChart.tsx
@@ -33,7 +33,7 @@ import {
DAY_S,
Days,
RESERVE_EVENT_SAMPLE_INTERVAL_S_MAP,
- calculateRewardDepositAprPercent,
+ calculateRewardAprPercent,
} from "@/lib/events";
import { formatPercent } from "@/lib/format";
import {
@@ -68,6 +68,19 @@ function TooltipContent({ side, fields, d, viewBox, x }: TooltipContentProps) {
if (fields.every((field) => d[field] === undefined)) return null;
if (viewBox === undefined || x === undefined) return null;
+
+ const interestField = `${side}InterestAprPercent`;
+ const rewardFields = fields.filter((field) => field !== interestField);
+
+ const aprPercent = new BigNumber(d[interestField] as number);
+ const totalAprPercent = rewardFields.reduce(
+ (acc, field) =>
+ acc.plus(
+ new BigNumber(d[field] as number).times(side === Side.DEPOSIT ? 1 : -1),
+ ),
+ new BigNumber(aprPercent),
+ );
+
return (
// Subset of TooltipContent className
{capitalize(side)} APR
- {formatPercent(
- new BigNumber(
- fields.reduce(
- (acc: number, field) => acc + (d[field] as number),
- 0,
- ),
- ),
- )}
+ {formatPercent(totalAprPercent, { useAccountingSign: true })}
@@ -103,7 +109,14 @@ function TooltipContent({ side, fields, d, viewBox, x }: TooltipContentProps) {
isLast={index === fields.length - 1}
value={
- {formatPercent(new BigNumber(d[field] as number))}
+ {formatPercent(
+ !coinType
+ ? aprPercent
+ : new BigNumber(d[field] as number).times(
+ side === Side.DEPOSIT ? 1 : -1,
+ ),
+ { useAccountingSign: true },
+ )}
}
>
@@ -401,8 +414,9 @@ export default function HistoricalAprLineChart({
const d = aprRewardReserves.reduce(
(acc, rewardReserve) => ({
...acc,
- [`depositInterestAprPercent_${rewardReserve.coinType}`]: event
- ? calculateRewardDepositAprPercent(
+ [`${side}InterestAprPercent_${rewardReserve.coinType}`]: event
+ ? calculateRewardAprPercent(
+ side,
event,
reserveAssetDataEventsMap?.[rewardReserve.id]?.[
days
@@ -423,7 +437,7 @@ export default function HistoricalAprLineChart({
});
return result as ChartData[];
- }, [reserveAssetDataEventsMap, reserve, days, aprRewardReserves]);
+ }, [reserveAssetDataEventsMap, reserve, days, aprRewardReserves, side]);
const isLoading = chartData === undefined;
return (
diff --git a/frontend/src/lib/events.ts b/frontend/src/lib/events.ts
index c8f0abb0..b4b2710b 100644
--- a/frontend/src/lib/events.ts
+++ b/frontend/src/lib/events.ts
@@ -2,6 +2,7 @@ import BigNumber from "bignumber.js";
import { ParsedDownsampledApiReserveAssetDataEvent } from "@suilend/sdk/parsers/apiReserveAssetDataEvent";
import { ParsedReserve } from "@suilend/sdk/parsers/reserve";
+import { Side } from "@suilend/sdk/types";
import {
NORMALIZED_SUI_COINTYPE,
@@ -72,7 +73,8 @@ type ReducedPoolReward = {
endTimeMs: number;
};
-export const calculateRewardDepositAprPercent = (
+export const calculateRewardAprPercent = (
+ side: Side,
event: ParsedDownsampledApiReserveAssetDataEvent,
rewardEvents: ParsedDownsampledApiReserveAssetDataEvent[],
reserve: ParsedReserve,
@@ -86,73 +88,81 @@ export const calculateRewardDepositAprPercent = (
const rewardCoinType = rewardEvent.coinType;
- const historicalRewardsMap: Record = {
- [NORMALIZED_SUI_COINTYPE]: [
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(93613.13),
- startTimeMs: 1713225600000,
- endTimeMs: 1713830400000,
- },
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(177579),
- startTimeMs: 1713830400000, // 2024-04-23 08:00:00
- endTimeMs: 1715040000000, // 2024-05-07 08:00:00
- },
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(162386.57),
- startTimeMs: 1715040000000, // 2024-05-07 08:00:00
- endTimeMs: 1716249600000, //2024-05-21 08:00:00
- },
- ],
- [NORMALIZED_USDC_COINTYPE]: [
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(75915.32),
- startTimeMs: 1713225600000,
- endTimeMs: 1713830400000,
- },
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(168534),
- startTimeMs: 1713830400000, // 2024-04-23 08:00:00
- endTimeMs: 1715040000000, // 2024-05-07 08:00:00
- },
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(176679.79),
- startTimeMs: 1715040000000, // 2024-05-07 08:00:00
- endTimeMs: 1716249600000, //2024-05-21 08:00:00
- },
- ],
- [NORMALIZED_USDT_COINTYPE]: [
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(64602.32),
- startTimeMs: 1713225600000,
- endTimeMs: 1713830400000,
- },
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(128939),
- startTimeMs: 1713830400000, // 2024-04-23 08:00:00
- endTimeMs: 1715040000000, // 2024-05-07 08:00:00
- },
- {
- coinType: NORMALIZED_SUI_COINTYPE,
- totalRewards: new BigNumber(116534.73),
- startTimeMs: 1715040000000, // 2024-05-07 08:00:00
- endTimeMs: 1716249600000, //2024-05-21 08:00:00
- },
- ],
+ const historicalRewardsMap: Record<
+ Side,
+ Record
+ > = {
+ [Side.DEPOSIT]: {
+ [NORMALIZED_SUI_COINTYPE]: [
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(93613.13),
+ startTimeMs: 1713225600000,
+ endTimeMs: 1713830400000,
+ },
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(177579),
+ startTimeMs: 1713830400000, // 2024-04-23 08:00:00
+ endTimeMs: 1715040000000, // 2024-05-07 08:00:00
+ },
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(162386.57),
+ startTimeMs: 1715040000000, // 2024-05-07 08:00:00
+ endTimeMs: 1716249600000, //2024-05-21 08:00:00
+ },
+ ],
+ [NORMALIZED_USDC_COINTYPE]: [
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(75915.32),
+ startTimeMs: 1713225600000,
+ endTimeMs: 1713830400000,
+ },
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(168534),
+ startTimeMs: 1713830400000, // 2024-04-23 08:00:00
+ endTimeMs: 1715040000000, // 2024-05-07 08:00:00
+ },
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(176679.79),
+ startTimeMs: 1715040000000, // 2024-05-07 08:00:00
+ endTimeMs: 1716249600000, //2024-05-21 08:00:00
+ },
+ ],
+ [NORMALIZED_USDT_COINTYPE]: [
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(64602.32),
+ startTimeMs: 1713225600000,
+ endTimeMs: 1713830400000,
+ },
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(128939),
+ startTimeMs: 1713830400000, // 2024-04-23 08:00:00
+ endTimeMs: 1715040000000, // 2024-05-07 08:00:00
+ },
+ {
+ coinType: NORMALIZED_SUI_COINTYPE,
+ totalRewards: new BigNumber(116534.73),
+ startTimeMs: 1715040000000, // 2024-05-07 08:00:00
+ endTimeMs: 1716249600000, //2024-05-21 08:00:00
+ },
+ ],
+ },
+ [Side.BORROW]: {},
};
const allPoolRewards: ReducedPoolReward[] = [
- ...reserve.depositsPoolRewardManager.poolRewards,
+ ...(side === Side.DEPOSIT
+ ? reserve.depositsPoolRewardManager.poolRewards
+ : reserve.borrowsPoolRewardManager.poolRewards),
];
- (historicalRewardsMap[event.coinType] ?? []).forEach((hr) => {
+ (historicalRewardsMap[side][event.coinType] ?? []).forEach((hr) => {
if (
allPoolRewards.find(
(pr) =>
@@ -185,7 +195,11 @@ export const calculateRewardDepositAprPercent = (
pr.totalRewards
.times(rewardEvent.price)
.times(new BigNumber(msPerYear).div(pr.endTimeMs - pr.startTimeMs))
- .div(event.depositedAmountUsd)
+ .div(
+ side === Side.DEPOSIT
+ ? event.depositedAmountUsd
+ : event.borrowedAmountUsd,
+ )
.times(100),
),
new BigNumber(0),
diff --git a/frontend/src/lib/format.ts b/frontend/src/lib/format.ts
index eab7423f..61744087 100644
--- a/frontend/src/lib/format.ts
+++ b/frontend/src/lib/format.ts
@@ -154,14 +154,22 @@ export const formatPoints = (value: BigNumber, options?: { dp?: number }) => {
});
};
-export const formatPercent = (value: BigNumber, options?: { dp?: number }) => {
+export const formatPercent = (
+ value: BigNumber,
+ options?: { dp?: number; useAccountingSign?: boolean },
+) => {
const dp = options?.dp ?? 2;
+ const useAccountingSign = options?.useAccountingSign ?? false;
- return Intl.NumberFormat(undefined, {
+ const formattedValue = Intl.NumberFormat(undefined, {
style: "percent",
minimumFractionDigits: dp,
maximumFractionDigits: dp,
- }).format(value.div(100).toNumber());
+ }).format(value.abs().div(100).toNumber());
+
+ return !useAccountingSign || value.gte(0)
+ ? formattedValue
+ : `(${formattedValue})`;
};
export const formatDuration = (seconds: BigNumber) => {
diff --git a/frontend/src/lib/liquidityMining.ts b/frontend/src/lib/liquidityMining.ts
index 2bccf4f6..8cc45379 100644
--- a/frontend/src/lib/liquidityMining.ts
+++ b/frontend/src/lib/liquidityMining.ts
@@ -222,10 +222,12 @@ export const getDedupedPerDayRewards = (
};
export const getTotalAprPercent = (
+ side: Side,
aprPercent: BigNumber,
filteredRewards: RewardSummary[],
) =>
getDedupedAprRewards(filteredRewards).reduce(
- (acc, reward) => acc.plus(reward.stats.aprPercent),
+ (acc, reward) =>
+ acc.plus(reward.stats.aprPercent.times(side === Side.DEPOSIT ? 1 : -1)),
aprPercent,
);
diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx
index a3b38c5f..02f25015 100644
--- a/frontend/src/pages/index.tsx
+++ b/frontend/src/pages/index.tsx
@@ -3,6 +3,8 @@ import NextLink from "next/link";
import { Droplet, Server } from "lucide-react";
+import { Side } from "@suilend/sdk/types";
+
import DiscordIcon from "@/components/assets/DiscordIcon";
import XIcon from "@/components/assets/XIcon";
import HeaderBase from "@/components/layout/HeaderBase";
@@ -162,6 +164,7 @@ export default function Home() {
)
.map((reserve) => {
const totalDepositAprPercent = getTotalAprPercent(
+ Side.DEPOSIT,
reserve.depositAprPercent,
getFilteredRewards(
data.rewardMap[reserve.coinType].deposit,
diff --git a/frontend/src/pages/swap/[[...slug]].tsx b/frontend/src/pages/swap/[[...slug]].tsx
index fc24f374..93e6cb7f 100644
--- a/frontend/src/pages/swap/[[...slug]].tsx
+++ b/frontend/src/pages/swap/[[...slug]].tsx
@@ -14,6 +14,7 @@ import { useLocalStorage } from "usehooks-ts";
import { v4 as uuidv4 } from "uuid";
import { SuilendClient } from "@suilend/sdk/client";
+import { Side } from "@suilend/sdk/types";
import Button from "@/components/shared/Button";
import Spinner from "@/components/shared/Spinner";
@@ -120,6 +121,7 @@ function Page() {
);
const tokenOutReserveDepositAprPercent = tokenOutReserve
? getTotalAprPercent(
+ Side.DEPOSIT,
tokenOutReserve.depositAprPercent,
getFilteredRewards(data.rewardMap[tokenOutReserve.coinType].deposit),
)