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), )