Skip to content

Commit

Permalink
Implement dynamic APY fetch for multiply tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
sablevsky committed Dec 12, 2024
1 parent 85f4b8b commit 079d9a2
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 111 deletions.
106 changes: 60 additions & 46 deletions src/pages/tokenLending/LeverageLanding/LeverageLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { Multiply, SOL, StarSecondary, USDC } from '@banx/icons'
import { PATHS } from '@banx/router'
import { buildUrlWithModeAndToken } from '@banx/store'
import { AssetMode, useTokenType } from '@banx/store/common'
import { LRTS_TOKEN_STATIC_META } from '@banx/transactions/leverage/lrtsSOL'
import { isBanxSolTokenType, isUsdcTokenType } from '@banx/utils'

import { MULTIPLY_PAIRS, MultiplyPair } from '../LeveragePage'
import { useCollateralYield } from '../LeveragePage/hooks'
import FLPImage from './assets/FLP.png'
import JUPImage from './assets/JUP.png'
import MeteoraImage from './assets/Meteora.png'
import ezSOLImage from './assets/ezSOL.png'

Expand Down Expand Up @@ -65,7 +65,14 @@ export const LeverageLanding = () => {
</div>

<div className={styles.tokensList}>
{TOKEN_ITEMS.map((tokenItem, idx) => (
{MULTIPLY_PAIRS.map((pair) => (
<TokenItemRealLink
key={pair.collateralMint.toBase58()}
pair={pair}
onClick={() => handleGoToLeveragePage(pair.collateralTicker)}
/>
))}
{MOCK_TOKEN_ITEMS.map((tokenItem, idx) => (
<TokenItemLink
key={idx}
{...tokenItem}
Expand All @@ -78,49 +85,6 @@ export const LeverageLanding = () => {
)
}

const TOKEN_ITEMS: TokenItem[] = [
{
logoUrl: LRTS_TOKEN_STATIC_META.logoUrl,
ticker: LRTS_TOKEN_STATIC_META.ticker,
maxApy: 9,
maxMultiply: 100,
//TODO: Fetch extraRewardsDescription from markets rewards json
extraRewardsDescription: 'This market earns additional rewards: Adrastea restaking',
disabled: false,
},
{
logoUrl: JUPImage,
ticker: 'JLP',
maxApy: 9,
maxMultiply: 100,
//TODO: Fetch extraRewardsDescription from markets rewards json
extraRewardsDescription: 'This market earns additional rewards: JLP Pool',
disabled: false,
},
{
logoUrl: FLPImage,
ticker: 'FLP.1',
maxMultiply: 100,
disabled: true,
},
{
logoUrl: MeteoraImage,
ticker: 'Meteora LP',
maxMultiply: 100,
disabled: true,
},
{
logoUrl: ezSOLImage,
ticker: 'ezSOL',
maxMultiply: 100,
disabled: true,
},
{
ticker: 'Any token',
disabled: true,
},
]

type TokenItem = {
logoUrl?: string
ticker: string
Expand All @@ -130,6 +94,31 @@ type TokenItem = {
extraRewardsDescription?: string
}

type TokenItemRealLinkProps = {
pair: MultiplyPair
onClick: () => void
maxMultiply?: number
}

const TokenItemRealLink: FC<TokenItemRealLinkProps> = ({ pair, onClick }) => {
const { collateralYield } = useCollateralYield(pair)

const { collateralLogoUrl, collateralTicker } = pair

const maxApy = collateralYield.toNumber() / 100

return (
<TokenItemLink
logoUrl={collateralLogoUrl}
ticker={collateralTicker}
extraRewardsDescription="This market earns additional rewards"
maxApy={maxApy}
maxMultiply={100}
onClick={onClick}
/>
)
}

const TokenItemLink: FC<TokenItem & { onClick: () => void }> = ({
ticker,
logoUrl,
Expand Down Expand Up @@ -173,3 +162,28 @@ const TokenItemLink: FC<TokenItem & { onClick: () => void }> = ({
</div>
)
}

const MOCK_TOKEN_ITEMS: TokenItem[] = [
{
logoUrl: FLPImage,
ticker: 'FLP.1',
maxMultiply: 100,
disabled: true,
},
{
logoUrl: MeteoraImage,
ticker: 'Meteora LP',
maxMultiply: 100,
disabled: true,
},
{
logoUrl: ezSOLImage,
ticker: 'ezSOL',
maxMultiply: 100,
disabled: true,
},
{
ticker: 'Any token',
disabled: true,
},
]
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const LeveragePositionsContent: FC<LeveragePositionsContentProps> = ({ pair }) =
{!noData && (
<div className={styles.cardsList}>
{filteredLoans.map((loan) => (
<LeveragePositionCard key={loan.publicKey} loan={loan} />
<LeveragePositionCard key={loan.publicKey} loan={loan} pair={pair} />
))}
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ import { TokenLoan } from '@banx/api/tokens'
import { useTokenLoansTransactions } from '@banx/pages/tokenLending/LoansTokenPage/TokenLoansContent'
import { caclulateBorrowTokenLoanValue, formatCompact, getTokenLoanSupply } from '@banx/utils'

import { useCollateralConversionRate } from '../../../hooks'
import { MultiplyPair } from '../../../constants'
import { useCollateralConversionRate, useCollateralYield } from '../../../hooks'
import { TOOLTIP_TEXTS } from '../../constants'
import { calculateNetAprByLoan } from '../../helpers'

import styles from './LeveragePositionCard.module.less'

type LeveragePositionCardProps = {
loan: TokenLoan
pair: MultiplyPair
}

const LeveragePositionCard: FC<LeveragePositionCardProps> = ({ loan }) => {
const LeveragePositionCard: FC<LeveragePositionCardProps> = ({ loan, pair }) => {
const { sellToRepay } = useTokenLoansTransactions()

return (
Expand All @@ -32,7 +34,7 @@ const LeveragePositionCard: FC<LeveragePositionCardProps> = ({ loan }) => {
<PositionMainInfo loan={loan} />

<div className={styles.additionalContentWrapper}>
<PositionAdditionalInfo loan={loan} />
<PositionAdditionalInfo loan={loan} pair={pair} />

<Button
onClick={() => sellToRepay(loan)}
Expand Down Expand Up @@ -77,18 +79,20 @@ const PositionMainInfo: FC<{ loan: TokenLoan }> = ({ loan }) => {
)
}

const PositionAdditionalInfo: FC<{ loan: TokenLoan }> = ({ loan }) => {
const PositionAdditionalInfo: FC<{ loan: TokenLoan; pair: MultiplyPair }> = ({ loan, pair }) => {
const { connection } = useConnection()
const { rate: conversionRate } = useCollateralConversionRate(
new web3.PublicKey(loan.collateral.mint),
loan.bondTradeTransaction.lendingToken,
connection,
)

const { collateralYield } = useCollateralYield(pair)

const loanPnl = loan.pnl ?? 0
const leverage = loan.fraktBond.leverageBasePoints / BASE_POINTS / 100
const totalDebt = caclulateBorrowTokenLoanValue(loan).toNumber()
const netApr = calculateNetAprByLoan(loan, conversionRate)
const netApr = calculateNetAprByLoan(loan, conversionRate, collateralYield)

const isNegativePnl = loanPnl < 0
const isNegativeNetApr = netApr < 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ import { calculateCurrentInterestSolPure } from 'fbonds-core/lib/fbond-protocol/
import moment from 'moment'

import { TokenLoan } from '@banx/api/tokens'
import { lrtsSOL } from '@banx/transactions/leverage'
import {
ZERO_BN,
bnToNumberSafe,
caclulateBorrowTokenLoanValue,
calcTokenLoanAprWithRepayFee,
} from '@banx/utils'

export const calculateNetAprByLoan = (loan: TokenLoan, conversionRate: number) => {
export const calculateNetAprByLoan = (
loan: TokenLoan,
conversionRate: number,
collateralYield: BN,
) => {
const totalDebt = caclulateBorrowTokenLoanValue(loan)

const aprRate = calcTokenLoanAprWithRepayFee(loan)
Expand All @@ -25,7 +28,10 @@ export const calculateNetAprByLoan = (loan: TokenLoan, conversionRate: number) =
})

//? Wrap if multiple tokens supported
const collateralYieldPerYear = calcCollateralYieldPerYear(new BN(loan.fraktBond.fbondTokenSupply))
const collateralYieldPerYear = calcCollateralYieldPerYear(
new BN(loan.fraktBond.fbondTokenSupply),
collateralYield,
)
const collateralYieldPerYearInToken = convertYieldToTokenValue(
collateralYieldPerYear,
conversionRate,
Expand All @@ -36,8 +42,8 @@ export const calculateNetAprByLoan = (loan: TokenLoan, conversionRate: number) =
return calcNetAprFromYieldAndDebtDiff(loan, yieldAndDebtDiff)
}

const calcCollateralYieldPerYear = (collateralTokenAmount: BN): BN => {
return collateralTokenAmount.mul(new BN(lrtsSOL.LRTS_YIELD)).div(new BN(BASE_POINTS))
const calcCollateralYieldPerYear = (collateralTokenAmount: BN, collateralYield: BN): BN => {
return collateralTokenAmount.mul(collateralYield).div(new BN(BASE_POINTS))
}

const convertYieldToTokenValue = (collateralYield: BN, conversionRate: number): BN => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ZERO_BN, bnToFixed, bnToNumberSafe } from '@banx/utils'

import { MultiplyPair } from '../../constants'
import { calculateTokenLoanBorrowAmount } from '../../helpers'
import { useCollateralYield } from '../../hooks'
import { LeverageSimpleOffer } from '../../types'

import styles from './Summary.module.less'
Expand All @@ -35,6 +36,8 @@ export const Summary: FC<SummaryProps> = ({
collateral,
pair,
}) => {
const { collateralYield } = useCollateralYield(pair)

const borrowAmount = selectedOffer
? calculateTokenLoanBorrowAmount(
totalCollateralAmount.sub(userEnteredCollateralAmount),
Expand All @@ -57,7 +60,7 @@ export const Summary: FC<SummaryProps> = ({
conversionRate,
offer: selectedOffer,
collateral,
pair,
collateralYield,
})
: 0

Expand Down Expand Up @@ -120,15 +123,15 @@ type CalculateNetAprParams = {
userEnteredCollateralAmount: BN
conversionRate: number
collateral: CollateralToken
pair: MultiplyPair
collateralYield: BN
}
const calculateNetApr = ({
totalCollateralAmount,
userEnteredCollateralAmount,
conversionRate,
offer,
collateral,
pair,
collateralYield,
}: CalculateNetAprParams) => {
const totalBorrowAmount = calculateTokenLoanBorrowAmount(
totalCollateralAmount.sub(userEnteredCollateralAmount),
Expand All @@ -145,9 +148,7 @@ const calculateNetApr = ({
})

//? Wrap if multiple tokens supported
const collateralYieldPerYear = totalCollateralAmount
.mul(new BN(pair.collateralYield))
.div(new BN(BASE_POINTS))
const collateralYieldPerYear = totalCollateralAmount.mul(collateralYield).div(new BN(BASE_POINTS))
const collateralYieldPerYearInToken = collateralYieldPerYear
.mul(new BN(BASE_POINTS))
.div(new BN(conversionRate * BASE_POINTS))
Expand Down
22 changes: 14 additions & 8 deletions src/pages/tokenLending/LeveragePage/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BN, web3 } from 'fbonds-core'
import { BASE_POINTS } from 'fbonds-core/lib/fbond-protocol/constants'
import { LendingTokenType } from 'fbonds-core/lib/fbond-protocol/types'

import {
Expand All @@ -13,8 +14,10 @@ export const MIN_MULTIPLIER_VALUE = 2

export type MultiplyPair = {
collateralTicker: string
collateralLogoUrl: string

collateralMint: web3.PublicKey
collateralYield: BN //TODO value can be dynamic
getCollateralYield: () => Promise<BN>
marketTokenType: LendingTokenType
marketPublicKey: web3.PublicKey
loanValueLimit?: BN
Expand All @@ -23,22 +26,25 @@ export type MultiplyPair = {
}
export const MULTIPLY_PAIRS: MultiplyPair[] = [
{
collateralTicker: lrtsSOL.LRTS_TOKEN_TICKER,
collateralTicker: 'lrtsSOL',
collateralLogoUrl:
'https://i.degencdn.com/ipfs/bafkreigoloautosntlteqccwusdjpxvtm3nm3kqvkavwxwickrk5irfety',
collateralMint: lrtsSOL.LRTS_MINT,
collateralYield: lrtsSOL.LRTS_YIELD,
marketTokenType: lrtsSOL.LRTS_MARKET_TOKEN_TYPE,
marketPublicKey: lrtsSOL.LRTS_MARKET,
loanValueLimit: new BN(120 * getTokenDecimals(lrtsSOL.LRTS_MARKET_TOKEN_TYPE)),
getCollateralYield: () => Promise.resolve(new BN(0.082 * BASE_POINTS)),
marketTokenType: LendingTokenType.BanxSol,
marketPublicKey: new web3.PublicKey('7EuPa26AjGdnQ7JcqM3kFhwFR4U2NQTU9guHcmaDF2G'),
loanValueLimit: new BN(120 * getTokenDecimals(LendingTokenType.BanxSol)),
createLeverageTxnHandler: lrtsSOL.createLrtsLeverageTxnData,
createSellToRepayTxnHandler: lrtsSOL.createLrtsSellToRepayTokenLoanTxnData,
},
{
collateralTicker: 'JLP',
collateralLogoUrl: 'https://static.jup.ag/jlp/icon.png',
collateralMint: new web3.PublicKey('27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4'),
collateralYield: new BN(1500), //TODO get from API
getCollateralYield: () => Promise.resolve(new BN(0.89 * BASE_POINTS)),
marketTokenType: LendingTokenType.Usdc,
marketPublicKey: new web3.PublicKey('ECxo4ZF9zyTGVXq42wwnKboX4hFNmAyhyqnyCgxVAm4S'),
loanValueLimit: new BN(120 * getTokenDecimals(lrtsSOL.LRTS_MARKET_TOKEN_TYPE)),
loanValueLimit: undefined,
createLeverageTxnHandler: leverage.createLeverageTxnData,
createSellToRepayTxnHandler: leverage.createSellToRepayTokenLoanTxnData,
},
Expand Down
25 changes: 15 additions & 10 deletions src/pages/tokenLending/LeveragePage/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,20 @@ export const useLrtsLeverage = ({ pair, collateralToken }: UseLrtsLeverageParams
}
}

export const useCollateralYield = (pair: MultiplyPair) => {
const { data: collateralYield, isLoading } = useQuery(
['collateralYield', pair.collateralMint],
() => pair.getCollateralYield(),
{
enabled: !!pair,
staleTime: 30 * 60 * 1000,
refetchOnWindowFocus: false,
},
)

return { collateralYield: collateralYield || ZERO_BN, isLoading }
}

export const useCollateralConversionRate = (
mint: web3.PublicKey,
tokenType: LendingTokenType,
Expand Down Expand Up @@ -305,20 +319,11 @@ export const useMultiplyPair = (ticker: string) => {
return { pair, collateralToken, isLoading }
}

type SimpleOffer = {
id: string
publicKey: web3.PublicKey
maxTokenToGet: BN
collateralsPerToken: BN
apr: BN //? apr in base points
lender: web3.PublicKey
}

//TODO Move to SDK
/**
* Assume that offer doesn't have a curve!
*/
export function convertToSimpleOffer(offer: BondOfferV3): SimpleOffer | null {
export function convertToSimpleOffer(offer: BondOfferV3): tokenOfferUtils.SimpleOffer | null {
const {
publicKey,
currentSpotPrice,
Expand Down
Loading

0 comments on commit 079d9a2

Please sign in to comment.