merge main
This commit is contained in:
commit
ad83c20215
|
@ -0,0 +1,200 @@
|
||||||
|
import Decimal from 'decimal.js'
|
||||||
|
import { BirdeyePriceResponse } from 'types'
|
||||||
|
import { DAILY_SECONDS } from 'utils/constants'
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
export const NEXT_PUBLIC_BIRDEYE_API_KEY =
|
||||||
|
process.env.NEXT_PUBLIC_BIRDEYE_API_KEY ||
|
||||||
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NzM0NTE4MDF9.KTEqB1hrmZTMzk19rZNx9aesh2bIHj98Cb8sg5Ikz-Y'
|
||||||
|
|
||||||
|
export const API_URL = 'https://public-api.birdeye.so/'
|
||||||
|
|
||||||
|
export const socketUrl = `wss://public-api.birdeye.so/socket?x-api-key=${NEXT_PUBLIC_BIRDEYE_API_KEY}`
|
||||||
|
|
||||||
|
// Make requests to Birdeye API
|
||||||
|
export async function makeApiRequest(path: string) {
|
||||||
|
const response = await fetch(`${API_URL}${path}`, {
|
||||||
|
headers: {
|
||||||
|
'X-API-KEY': NEXT_PUBLIC_BIRDEYE_API_KEY,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
const RESOLUTION_MAPPING: Record<string, string> = {
|
||||||
|
'1': '1m',
|
||||||
|
'3': '3m',
|
||||||
|
'5': '5m',
|
||||||
|
'15': '15m',
|
||||||
|
'30': '30m',
|
||||||
|
'60': '1H',
|
||||||
|
'120': '2H',
|
||||||
|
'240': '4H',
|
||||||
|
'1D': '1D',
|
||||||
|
'1W': '1W',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseResolution(resolution: string) {
|
||||||
|
if (!resolution || !RESOLUTION_MAPPING[resolution])
|
||||||
|
return RESOLUTION_MAPPING[0]
|
||||||
|
|
||||||
|
return RESOLUTION_MAPPING[resolution]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNextBarTime(lastBar: any, resolution = '1D') {
|
||||||
|
if (!lastBar) return
|
||||||
|
|
||||||
|
const lastCharacter = resolution.slice(-1)
|
||||||
|
let nextBarTime
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case lastCharacter === 'W':
|
||||||
|
nextBarTime = 7 * 24 * 60 * 60 * 1000 + lastBar.time
|
||||||
|
break
|
||||||
|
|
||||||
|
case lastCharacter === 'D':
|
||||||
|
nextBarTime = 1 * 24 * 60 * 60 * 1000 + lastBar.time
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
nextBarTime = 1 * 60 * 1000 + lastBar.time
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextBarTime
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SUBSCRIPT_NUMBER_MAP: Record<number, string> = {
|
||||||
|
4: '₄',
|
||||||
|
5: '₅',
|
||||||
|
6: '₆',
|
||||||
|
7: '₇',
|
||||||
|
8: '₈',
|
||||||
|
9: '₉',
|
||||||
|
10: '₁₀',
|
||||||
|
11: '₁₁',
|
||||||
|
12: '₁₂',
|
||||||
|
13: '₁₃',
|
||||||
|
14: '₁₄',
|
||||||
|
15: '₁₅',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calcPricePrecision = (num: number | string) => {
|
||||||
|
if (!num) return 8
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case Math.abs(+num) < 0.00000000001:
|
||||||
|
return 16
|
||||||
|
|
||||||
|
case Math.abs(+num) < 0.000000001:
|
||||||
|
return 14
|
||||||
|
|
||||||
|
case Math.abs(+num) < 0.0000001:
|
||||||
|
return 12
|
||||||
|
|
||||||
|
case Math.abs(+num) < 0.00001:
|
||||||
|
return 10
|
||||||
|
|
||||||
|
case Math.abs(+num) < 0.05:
|
||||||
|
return 6
|
||||||
|
|
||||||
|
case Math.abs(+num) < 1:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
case Math.abs(+num) < 20:
|
||||||
|
return 3
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatPrice = (
|
||||||
|
num: number,
|
||||||
|
precision?: number,
|
||||||
|
gr0 = true,
|
||||||
|
): string => {
|
||||||
|
if (!num) {
|
||||||
|
return num.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!precision) {
|
||||||
|
precision = calcPricePrecision(+num)
|
||||||
|
}
|
||||||
|
|
||||||
|
let formated: string = new Decimal(num).toFixed(precision)
|
||||||
|
|
||||||
|
if (formated.match(/^0\.[0]+$/g)) {
|
||||||
|
formated = formated.replace(/\.[0]+$/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gr0 && formated.match(/\.0{4,15}[1-9]+/g)) {
|
||||||
|
const match = formated.match(/\.0{4,15}/g)
|
||||||
|
if (!match) return ''
|
||||||
|
const matchString: string = match[0].slice(1)
|
||||||
|
formated = formated.replace(
|
||||||
|
/\.0{4,15}/g,
|
||||||
|
`.0${SUBSCRIPT_NUMBER_MAP[matchString.length]}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return formated
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SwapChartDataItem = {
|
||||||
|
time: number
|
||||||
|
price: number
|
||||||
|
inputTokenPrice: number
|
||||||
|
outputTokenPrice: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchSwapChartPrices = async (
|
||||||
|
inputMint: string | undefined,
|
||||||
|
outputMint: string | undefined,
|
||||||
|
daysToShow: string,
|
||||||
|
) => {
|
||||||
|
if (!inputMint || !outputMint) return []
|
||||||
|
const interval = daysToShow === '1' ? '30m' : daysToShow === '7' ? '1H' : '4H'
|
||||||
|
const queryEnd = Math.floor(Date.now() / 1000)
|
||||||
|
const queryStart = queryEnd - parseInt(daysToShow) * DAILY_SECONDS
|
||||||
|
const inputQuery = `defi/history_price?address=${inputMint}&address_type=token&type=${interval}&time_from=${queryStart}&time_to=${queryEnd}`
|
||||||
|
const outputQuery = `defi/history_price?address=${outputMint}&address_type=token&type=${interval}&time_from=${queryStart}&time_to=${queryEnd}`
|
||||||
|
try {
|
||||||
|
const [inputResponse, outputResponse] = await Promise.all([
|
||||||
|
makeApiRequest(inputQuery),
|
||||||
|
makeApiRequest(outputQuery),
|
||||||
|
])
|
||||||
|
|
||||||
|
if (
|
||||||
|
inputResponse.success &&
|
||||||
|
inputResponse?.data?.items?.length &&
|
||||||
|
outputResponse.success &&
|
||||||
|
outputResponse?.data?.items?.length
|
||||||
|
) {
|
||||||
|
const parsedData: SwapChartDataItem[] = []
|
||||||
|
const inputData = inputResponse.data.items
|
||||||
|
const outputData = outputResponse.data.items
|
||||||
|
|
||||||
|
for (const item of inputData) {
|
||||||
|
const outputDataItem = outputData.find(
|
||||||
|
(data: BirdeyePriceResponse) => data.unixTime === item.unixTime,
|
||||||
|
)
|
||||||
|
|
||||||
|
const curentTimestamp = Date.now() / 1000
|
||||||
|
|
||||||
|
if (outputDataItem && item.unixTime <= curentTimestamp) {
|
||||||
|
parsedData.push({
|
||||||
|
time: Math.floor(item.unixTime * 1000),
|
||||||
|
price: item.value / outputDataItem.value,
|
||||||
|
inputTokenPrice: item.value,
|
||||||
|
outputTokenPrice: outputDataItem.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parsedData
|
||||||
|
} else return []
|
||||||
|
} catch (e) {
|
||||||
|
console.log('failed to fetch swap chart data from birdeye', e)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { parseResolution, getNextBarTime, socketUrl } from './helpers'
|
||||||
|
|
||||||
|
let subscriptionItem: any = {}
|
||||||
|
|
||||||
|
// Create WebSocket connection.
|
||||||
|
const socket = new WebSocket(socketUrl, 'echo-protocol')
|
||||||
|
|
||||||
|
// Connection opened
|
||||||
|
socket.addEventListener('open', (_event) => {
|
||||||
|
console.log('[socket] Connected birdeye')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for messages
|
||||||
|
socket.addEventListener('message', (msg) => {
|
||||||
|
const data = JSON.parse(msg.data)
|
||||||
|
|
||||||
|
if (data.type !== 'BASE_QUOTE_PRICE_DATA') return console.warn(data)
|
||||||
|
|
||||||
|
const currTime = data.data.unixTime * 1000
|
||||||
|
const lastBar = subscriptionItem.lastBar
|
||||||
|
|
||||||
|
if (
|
||||||
|
data.data.baseAddress !== subscriptionItem.baseAddress ||
|
||||||
|
data.data.quoteAddress !== subscriptionItem.quoteAddress
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
const resolution = subscriptionItem.resolution
|
||||||
|
const nextBarTime = getNextBarTime(lastBar, resolution)
|
||||||
|
|
||||||
|
let bar
|
||||||
|
if (currTime >= nextBarTime) {
|
||||||
|
bar = {
|
||||||
|
time: nextBarTime,
|
||||||
|
open: data.data.o,
|
||||||
|
high: data.data.h,
|
||||||
|
low: data.data.l,
|
||||||
|
close: data.data.c,
|
||||||
|
volume: data.data.v,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bar = {
|
||||||
|
...lastBar,
|
||||||
|
high: Math.max(lastBar.high, data.data.h),
|
||||||
|
low: Math.min(lastBar.low, data.data.l),
|
||||||
|
close: data.data.c,
|
||||||
|
volume: data.data.v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptionItem.lastBar = bar
|
||||||
|
subscriptionItem.callback(bar)
|
||||||
|
})
|
||||||
|
|
||||||
|
export function subscribeOnStream(
|
||||||
|
symbolInfo: any,
|
||||||
|
resolution: any,
|
||||||
|
onRealtimeCallback: any,
|
||||||
|
subscriberUID: any,
|
||||||
|
onResetCacheNeededCallback: any,
|
||||||
|
lastBar: any,
|
||||||
|
) {
|
||||||
|
subscriptionItem = {
|
||||||
|
resolution,
|
||||||
|
lastBar,
|
||||||
|
callback: onRealtimeCallback,
|
||||||
|
baseAddress: symbolInfo.base_token,
|
||||||
|
quoteAddress: symbolInfo.quote_token,
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = {
|
||||||
|
type: 'SUBSCRIBE_BASE_QUOTE_PRICE',
|
||||||
|
data: {
|
||||||
|
chartType: parseResolution(resolution),
|
||||||
|
baseAddress: symbolInfo.base_token,
|
||||||
|
quoteAddress: symbolInfo.quote_token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed')
|
||||||
|
socket.addEventListener('open', (_event) => {
|
||||||
|
if (!msg.data.baseAddress || msg.data.quoteAddress) return
|
||||||
|
socket.send(JSON.stringify(msg))
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[subscribeBars birdeye]')
|
||||||
|
if (msg.data.baseAddress && msg.data.quoteAddress) {
|
||||||
|
socket.send(JSON.stringify(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unsubscribeFromStream() {
|
||||||
|
const msg = {
|
||||||
|
type: 'UNSUBSCRIBE_BASE_QUOTE_PRICE',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[unsubscribeBars birdeye]')
|
||||||
|
socket.send(JSON.stringify(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeSocket() {
|
||||||
|
if (!isOpen(socket)) {
|
||||||
|
console.warn('Socket Closed birdeye')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn('[closeSocket birdeye]')
|
||||||
|
socket.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isOpen(ws?: WebSocket) {
|
||||||
|
const sock = ws || socket
|
||||||
|
return sock.readyState === sock.OPEN
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import useBankRates from 'hooks/useBankRates'
|
||||||
import usePositions from 'hooks/usePositions'
|
import usePositions from 'hooks/usePositions'
|
||||||
import { AdjustmentsHorizontalIcon } from '@heroicons/react/20/solid'
|
import { AdjustmentsHorizontalIcon } from '@heroicons/react/20/solid'
|
||||||
import EditLeverageModal from './modals/EditLeverageModal'
|
import EditLeverageModal from './modals/EditLeverageModal'
|
||||||
|
import Tooltip from './shared/Tooltip'
|
||||||
|
|
||||||
const set = mangoStore.getState().set
|
const set = mangoStore.getState().set
|
||||||
|
|
||||||
|
@ -139,10 +140,8 @@ const PositionItem = ({
|
||||||
return [liqRatio, liqPriceChangePercentage.toFixed(2)]
|
return [liqRatio, liqPriceChangePercentage.toFixed(2)]
|
||||||
}, [bank, borrowBalance, borrowBank, stakeBalance])
|
}, [bank, borrowBalance, borrowBank, stakeBalance])
|
||||||
|
|
||||||
const { financialMetrics, stakeBankDepositRate } = useBankRates(
|
const { financialMetrics, stakeBankDepositRate, borrowBankBorrowRate } =
|
||||||
bank.name,
|
useBankRates(bank.name, leverage)
|
||||||
leverage,
|
|
||||||
)
|
|
||||||
|
|
||||||
const APY_Daily_Compound =
|
const APY_Daily_Compound =
|
||||||
Math.pow(1 + Number(stakeBankDepositRate) / 365, 365) - 1
|
Math.pow(1 + Number(stakeBankDepositRate) / 365, 365) - 1
|
||||||
|
@ -192,9 +191,83 @@ const PositionItem = ({
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-1 text-th-fgd-4">Est. APY</p>
|
<p className="mb-1 text-th-fgd-4">Est. APY</p>
|
||||||
<span className="text-xl font-bold text-th-fgd-1">
|
{bank.name !== 'USDC' ? (
|
||||||
<FormatNumericValue value={Number(uiRate)} decimals={2} />%
|
<div className="w-max">
|
||||||
</span>
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<div className="space-y-2 md:px-3">
|
||||||
|
<div className="flex justify-between gap-6">
|
||||||
|
<p className="text-th-fgd-4">
|
||||||
|
{formatTokenSymbol(bank.name)} Yield APY
|
||||||
|
</p>
|
||||||
|
<span className="font-bold text-th-success">
|
||||||
|
{financialMetrics.collectedReturnsAPY > 0.01
|
||||||
|
? '+'
|
||||||
|
: ''}
|
||||||
|
<FormatNumericValue
|
||||||
|
value={financialMetrics.collectedReturnsAPY}
|
||||||
|
decimals={2}
|
||||||
|
/>
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between gap-6">
|
||||||
|
<p className="text-th-fgd-4">
|
||||||
|
{formatTokenSymbol(bank.name)} Collateral Fee APY
|
||||||
|
</p>
|
||||||
|
<span
|
||||||
|
className={`font-bold ${
|
||||||
|
financialMetrics?.collateralFeeAPY > 0.01
|
||||||
|
? 'text-th-error'
|
||||||
|
: 'text-th-bkg-4'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{financialMetrics?.collateralFeeAPY > 0.01 ? '-' : ''}
|
||||||
|
<FormatNumericValue
|
||||||
|
value={financialMetrics?.collateralFeeAPY?.toString()}
|
||||||
|
decimals={2}
|
||||||
|
/>
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{borrowBank ? (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-between gap-6">
|
||||||
|
<p className="text-th-fgd-4">{`${borrowBank?.name} Borrow APY`}</p>
|
||||||
|
<span
|
||||||
|
className={`font-bold ${
|
||||||
|
borrowBankBorrowRate > 0.01
|
||||||
|
? 'text-th-error'
|
||||||
|
: 'text-th-bkg-4'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
-
|
||||||
|
<FormatNumericValue
|
||||||
|
value={financialMetrics.borrowsAPY}
|
||||||
|
decimals={2}
|
||||||
|
/>
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="tooltip-underline text-xl font-bold text-th-fgd-1">
|
||||||
|
<FormatNumericValue value={Number(uiRate)} decimals={2} />%
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span className="text-xl font-bold text-th-fgd-1">
|
||||||
|
<FormatNumericValue value={Number(uiRate)} decimals={2} />%
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-1 text-th-fgd-4">Total Earned</p>
|
<p className="mb-1 text-th-fgd-4">Total Earned</p>
|
||||||
|
|
|
@ -104,16 +104,20 @@ function UnstakeForm({ token: selectedToken }: UnstakeFormProps) {
|
||||||
const stakeBankAmount =
|
const stakeBankAmount =
|
||||||
mangoAccount && stakeBank && mangoAccount.getTokenBalance(stakeBank)
|
mangoAccount && stakeBank && mangoAccount.getTokenBalance(stakeBank)
|
||||||
|
|
||||||
const borrowAmount =
|
const borrowBankAmount =
|
||||||
mangoAccount && borrowBank && mangoAccount.getTokenBalance(borrowBank)
|
mangoAccount && borrowBank && mangoAccount.getTokenBalance(borrowBank)
|
||||||
|
|
||||||
const leverage = useMemo(() => {
|
const leverage = useMemo(() => {
|
||||||
try {
|
try {
|
||||||
if (stakeBankAmount && borrowAmount) {
|
if (
|
||||||
|
stakeBankAmount &&
|
||||||
|
borrowBankAmount &&
|
||||||
|
borrowBankAmount.toNumber() < 0
|
||||||
|
) {
|
||||||
const lev = stakeBankAmount
|
const lev = stakeBankAmount
|
||||||
.div(
|
.div(
|
||||||
stakeBankAmount.sub(
|
stakeBankAmount.sub(
|
||||||
borrowAmount.abs().div(stakeBank.getAssetPrice()),
|
borrowBankAmount.abs().div(stakeBank.getAssetPrice()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toNumber()
|
.toNumber()
|
||||||
|
@ -125,7 +129,7 @@ function UnstakeForm({ token: selectedToken }: UnstakeFormProps) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}, [stakeBankAmount, borrowAmount, stakeBank])
|
}, [stakeBankAmount, borrowBankAmount, stakeBank])
|
||||||
|
|
||||||
const tokenMax = useMemo(() => {
|
const tokenMax = useMemo(() => {
|
||||||
if (!stakeBank || !mangoAccount) return { maxAmount: 0.0, maxDecimals: 6 }
|
if (!stakeBank || !mangoAccount) return { maxAmount: 0.0, maxDecimals: 6 }
|
||||||
|
|
|
@ -35,8 +35,8 @@ const Tooltip = ({
|
||||||
content={
|
content={
|
||||||
content ? (
|
content ? (
|
||||||
<div
|
<div
|
||||||
className={`${themeData.fonts.body.variable} ${themeData.fonts.display.variable} font-sans font-sans rounded-md bg-th-bkg-2 p-3 font-body text-xs leading-4 text-th-fgd-3 outline-none focus:outline-none ${className}`}
|
className={`${themeData.fonts.body.variable} ${themeData.fonts.display.variable} font-sans rounded-xl border-2 border-th-fgd-1 bg-th-bkg-1 p-4 font-body text-xs leading-4 text-th-fgd-3 outline-none focus:outline-none ${className}`}
|
||||||
style={{ boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.25)' }}
|
style={{ boxShadow: '0px 0px 0px 0px rgba(0,0,0,0)' }}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,7 +59,7 @@ const Content = ({
|
||||||
} & HTMLAttributes<HTMLDivElement>) => {
|
} & HTMLAttributes<HTMLDivElement>) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`inline-block cursor-help border-b border-dashed border-th-fgd-3 border-opacity-20 hover:border-th-bkg-2 ${className}`}
|
className={`inline-block cursor-help border-b border-dashed border-th-fgd-3 hover:border-th-bkg-2 ${className}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,40 +1,31 @@
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import {
|
import { fetchSwapChartPrices } from 'apis/birdeye/helpers'
|
||||||
fetchAndParsePricesCsv,
|
import { STAKEABLE_TOKENS_DATA } from 'utils/constants'
|
||||||
getPriceRangeFromPeriod,
|
|
||||||
calcYield,
|
|
||||||
DATA_SOURCE,
|
|
||||||
PERIOD,
|
|
||||||
} from '@glitchful-dev/sol-apy-sdk'
|
|
||||||
|
|
||||||
const fetchRates = async () => {
|
const fetchRates = async () => {
|
||||||
try {
|
try {
|
||||||
const [msolPrices, jitoPrices, bsolPrices, lidoPrices] = await Promise.all([
|
const [jlpPrices] = await Promise.all([
|
||||||
fetchAndParsePricesCsv(DATA_SOURCE.MARINADE_CSV),
|
fetchSwapChartPrices(STAKEABLE_TOKENS_DATA[0]?.mint_address, STAKEABLE_TOKENS_DATA[1]?.mint_address, '30')
|
||||||
fetchAndParsePricesCsv(DATA_SOURCE.JITO_CSV),
|
|
||||||
fetchAndParsePricesCsv(DATA_SOURCE.SOLBLAZE_CSV),
|
|
||||||
fetchAndParsePricesCsv(DATA_SOURCE.LIDO_CSV),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const resp = await fetch(
|
|
||||||
`https://api.coingecko.com/api/v3/coins/jupiter-perpetuals-liquidity-provider-token/market_chart?vs_currency=usd&days=30&interval=daily`,
|
|
||||||
)
|
|
||||||
const jlpPricesData = await resp.json()
|
|
||||||
const jlpPricesPrice = jlpPricesData.prices.map(
|
|
||||||
(priceAndTime: Array<number>) => priceAndTime[1],
|
|
||||||
)
|
|
||||||
|
|
||||||
// may be null if the price range cannot be calculated
|
// may be null if the price range cannot be calculated
|
||||||
|
/*
|
||||||
|
|
||||||
const msolRange = getPriceRangeFromPeriod(msolPrices, PERIOD.DAYS_30)
|
const msolRange = getPriceRangeFromPeriod(msolPrices, PERIOD.DAYS_30)
|
||||||
const jitoRange = getPriceRangeFromPeriod(jitoPrices, PERIOD.DAYS_30)
|
const jitoRange = getPriceRangeFromPeriod(jitoPrices, PERIOD.DAYS_30)
|
||||||
const bsolRange = getPriceRangeFromPeriod(bsolPrices, PERIOD.DAYS_30)
|
const bsolRange = getPriceRangeFromPeriod(bsolPrices, PERIOD.DAYS_30)
|
||||||
const lidoRange = getPriceRangeFromPeriod(lidoPrices, PERIOD.DAYS_30)
|
const lidoRange = getPriceRangeFromPeriod(lidoPrices, PERIOD.DAYS_30)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
const rateData: Record<string, number> = {}
|
const rateData: Record<string, number> = {}
|
||||||
rateData.jlp =
|
rateData.jlp =
|
||||||
(12 * (jlpPricesPrice[jlpPricesPrice.length - 2] - jlpPricesPrice[1])) /
|
(12 * (jlpPrices[jlpPrices.length - 2].price - jlpPrices[0].price)) /
|
||||||
jlpPricesPrice[1]
|
jlpPrices[0].price
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
if (msolRange) {
|
if (msolRange) {
|
||||||
rateData.msol = calcYield(msolRange)?.apy
|
rateData.msol = calcYield(msolRange)?.apy
|
||||||
}
|
}
|
||||||
|
@ -47,6 +38,9 @@ const fetchRates = async () => {
|
||||||
if (lidoRange) {
|
if (lidoRange) {
|
||||||
rateData.stsol = calcYield(lidoRange)?.apy
|
rateData.stsol = calcYield(lidoRange)?.apy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
return rateData
|
return rateData
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {}
|
return {}
|
||||||
|
|
|
@ -10,6 +10,12 @@ import { Modify } from '@blockworks-foundation/mango-v4'
|
||||||
import { Event } from '@project-serum/serum/lib/queue'
|
import { Event } from '@project-serum/serum/lib/queue'
|
||||||
import { PublicKey } from '@solana/web3.js'
|
import { PublicKey } from '@solana/web3.js'
|
||||||
|
|
||||||
|
export interface BirdeyePriceResponse {
|
||||||
|
address: string
|
||||||
|
unixTime: number
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
export type EmptyObject = { [K in keyof never]?: never }
|
export type EmptyObject = { [K in keyof never]?: never }
|
||||||
export interface OrderbookL2 {
|
export interface OrderbookL2 {
|
||||||
bids: number[][]
|
bids: number[][]
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
export const BORROW_TOKEN = 'USDC'
|
export const BORROW_TOKEN = 'USDC'
|
||||||
|
|
||||||
export const STAKEABLE_TOKENS_DATA = [
|
export const STAKEABLE_TOKENS_DATA = [
|
||||||
{ name: 'JLP', id: 1, active: true },
|
{ name: 'JLP', id: 1, active: true, mint_address: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4' },
|
||||||
{ name: 'USDC', id: 0, active: true },
|
{ name: 'USDC', id: 0, active: true, mint_address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' },
|
||||||
]
|
]
|
||||||
export const STAKEABLE_TOKENS = STAKEABLE_TOKENS_DATA.filter(
|
export const STAKEABLE_TOKENS = STAKEABLE_TOKENS_DATA.filter(
|
||||||
(d) => d.active,
|
(d) => d.active,
|
||||||
|
|
Loading…
Reference in New Issue