Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update jup api and plot to include buy and sell prices #740

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/NewPosition/NewPosition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,8 @@ export const NewPosition: React.FC<INewPosition> = ({
getTicksInsideRange={getTicksInsideRange}
shouldResetPlot={shouldResetPlot}
setShouldResetPlot={setShouldResetPlot}
tokenAPriceData={tokenAPriceData}
tokenBPriceData={tokenBPriceData}
/>
) : (
<PoolInit
Expand Down
12 changes: 10 additions & 2 deletions src/components/NewPosition/RangeSelector/RangeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
calcTicksAmountInRange,
nearestTickIndex,
toMaxNumericPlaces,
calculateConcentrationRange
calculateConcentrationRange,
TokenPriceData
} from '@consts/utils'
import { PlotTickData } from '@reducers/positions'
import PlotTypeSwitch from '@components/PlotTypeSwitch/PlotTypeSwitch'
Expand Down Expand Up @@ -57,6 +58,8 @@ export interface IRangeSelector {
}
shouldResetPlot: boolean
setShouldResetPlot: (val: boolean) => void
tokenAPriceData?: TokenPriceData
tokenBPriceData?: TokenPriceData
}

export const RangeSelector: React.FC<IRangeSelector> = ({
Expand Down Expand Up @@ -87,7 +90,9 @@ export const RangeSelector: React.FC<IRangeSelector> = ({
setConcentrationIndex,
getTicksInsideRange,
shouldResetPlot,
setShouldResetPlot
setShouldResetPlot,
tokenAPriceData,
tokenBPriceData
}) => {
const classes = useStyles()

Expand Down Expand Up @@ -388,6 +393,7 @@ export const RangeSelector: React.FC<IRangeSelector> = ({
<Grid>
<Typography className={classes.currentPrice}>Current price</Typography>
<Typography className={classes.globalPrice}>Global price</Typography>
<Typography className={classes.buySellPrice}>Buy/sell price</Typography>
</Grid>
</Grid>
</Grid>
Expand Down Expand Up @@ -420,6 +426,8 @@ export const RangeSelector: React.FC<IRangeSelector> = ({
hasError={hasTicksError}
reloadHandler={reloadHandler}
volumeRange={volumeRange}
tokenAPriceData={tokenAPriceData}
tokenBPriceData={tokenBPriceData}
/>
<Typography className={classes.subheader}>Set price range</Typography>
<Grid container className={classes.inputs}>
Expand Down
7 changes: 7 additions & 0 deletions src/components/NewPosition/RangeSelector/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ const useStyles = makeStyles((theme: Theme) => ({
...typography.caption2,
textAlign: 'right',
marginLeft: 4
},
buySellPrice: {
display: 'inline-block',
color: colors.white.main,
...typography.caption2,
textAlign: 'right',
marginLeft: 4
}
}))

Expand Down
56 changes: 54 additions & 2 deletions src/components/PriceRangePlot/PriceRangePlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import classNames from 'classnames'
import ZoomInIcon from '@static/svg/zoom-in-icon.svg'
import ZoomOutIcon from '@static/svg/zoom-out-icon.svg'
import Brush from './Brush/Brush'
import { nearestTickIndex } from '@consts/utils'
import { nearestTickIndex, TokenPriceData } from '@consts/utils'
import { PlotTickData } from '@reducers/positions'
import loader from '@static/gif/loader.gif'
import useStyles from './style'
Expand Down Expand Up @@ -42,6 +42,8 @@ export interface IPriceRangePlot {
min: number
max: number
}
tokenAPriceData?: TokenPriceData
tokenBPriceData?: TokenPriceData
}

export const PriceRangePlot: React.FC<IPriceRangePlot> = ({
Expand All @@ -67,7 +69,9 @@ export const PriceRangePlot: React.FC<IPriceRangePlot> = ({
coverOnLoading = false,
hasError = false,
reloadHandler,
volumeRange
volumeRange,
tokenAPriceData,
tokenBPriceData
}) => {
const classes = useStyles()

Expand Down Expand Up @@ -315,6 +319,52 @@ export const PriceRangePlot: React.FC<IPriceRangePlot> = ({
)
}

const buyPriceLayer: Layer = ({ innerWidth, innerHeight }) => {
if (typeof tokenAPriceData === 'undefined' || typeof tokenBPriceData === 'undefined') {
return null
}

const unitLen = innerWidth / (plotMax - plotMin)
return (
<svg
x={(tokenAPriceData.buyPrice / tokenBPriceData.price - plotMin) * unitLen}
y={0}
width={60}
height={innerHeight}>
<defs>
<filter id='shadow' x='-10' y='-9' width='20' height={innerHeight}>
<feGaussianBlur in='SourceGraphic' stdDeviation='8' />
</filter>
</defs>
<rect x={14} y={20} width='16' height={innerHeight} filter='url(#shadow)' opacity='0.3' />
<rect x={19} y={20} width='3' height={innerHeight} fill={colors.white.main} />
</svg>
)
}

const sellPriceLayer: Layer = ({ innerWidth, innerHeight }) => {
if (typeof tokenAPriceData === 'undefined' || typeof tokenBPriceData === 'undefined') {
return null
}

const unitLen = innerWidth / (plotMax - plotMin)
return (
<svg
x={(tokenAPriceData.sellPrice / tokenBPriceData.price - plotMin) * unitLen}
y={0}
width={60}
height={innerHeight}>
<defs>
<filter id='shadow' x='-10' y='-9' width='20' height={innerHeight}>
<feGaussianBlur in='SourceGraphic' stdDeviation='8' />
</filter>
</defs>
<rect x={14} y={20} width='16' height={innerHeight} filter='url(#shadow)' opacity='0.3' />
<rect x={19} y={20} width='3' height={innerHeight} fill={colors.white.main} />
</svg>
)
}

const volumeRangeLayer: Layer = ({ innerWidth, innerHeight }) => {
if (typeof volumeRange === 'undefined') {
return null
Expand Down Expand Up @@ -504,6 +554,8 @@ export const PriceRangePlot: React.FC<IPriceRangePlot> = ({
'areas',
'lines',
globalPriceLayer,
buyPriceLayer,
sellPriceLayer,
currentLayer,
volumeRangeLayer,
brushLayer,
Expand Down
61 changes: 43 additions & 18 deletions src/store/consts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,10 +776,13 @@ interface RawJupApiResponse {
string,
{
id: string
mintSymbol: string
vsToken: string
vsTokenSymbol: string
price: number
price: string
extraInfo?: {
quotedPrice: {
buyPrice: string
sellPrice: string
}
}
}
>
timeTaken: number
Expand All @@ -797,11 +800,13 @@ export interface CoingeckoApiPriceData {

export interface TokenPriceData {
price: number
buyPrice: number
sellPrice: number
}

export const getCoingeckoPricesData = async (
ids: string[]
): Promise<Record<string, TokenPriceData>> => {
): Promise<Record<string, Omit<TokenPriceData, 'buyPrice' | 'sellPrice'>>> => {
const maxTokensPerRequest = 250
const chunkedIds = []
for (let i = 0; i < ids.length; i += maxTokensPerRequest) {
Expand All @@ -822,10 +827,13 @@ export const getCoingeckoPricesData = async (
Object.values(response.data).map(({ id, current_price: price }) => ({ id, price }))
)

return concatRes.reduce<Record<string, TokenPriceData>>((acc, { id, price }) => {
acc[id] = { price: price ?? 0 }
return acc
}, {})
return concatRes.reduce<Record<string, Omit<TokenPriceData, 'buyPrice' | 'sellPrice'>>>(
(acc, { id, price }) => {
acc[id] = { price: price ?? 0 }
return acc
},
{}
)
}

export const getJupPricesData = async (ids: string[]): Promise<Record<string, TokenPriceData>> => {
Expand All @@ -838,16 +846,22 @@ export const getJupPricesData = async (ids: string[]): Promise<Record<string, To

const requests = chunkedIds.map(
async idsChunk =>
await axios.get<RawJupApiResponse>(`https://price.jup.ag/v4/price?ids=${idsChunk.join(',')}`)
await axios.get<RawJupApiResponse>(
`https://api.jup.ag/price/v2?ids=${idsChunk.join(',')}&showExtraInfo=true`
)
)

const responses = await Promise.all(requests)
const concatRes = responses.flatMap(response =>
Object.values(response.data.data).map(({ id, price }) => ({ id, price }))
Object.values(response.data.data).map(({ id, price, extraInfo }) => ({ id, price, extraInfo }))
)

return concatRes.reduce<Record<string, TokenPriceData>>((acc, { id, price }) => {
acc[id] = { price: price ?? 0 }
return concatRes.reduce<Record<string, TokenPriceData>>((acc, { id, price, extraInfo }) => {
acc[id] = {
price: Number(price),
buyPrice: Number(extraInfo?.quotedPrice.buyPrice ?? 0),
sellPrice: Number(extraInfo?.quotedPrice.sellPrice ?? 0)
}
return acc
}, {})
}
Expand Down Expand Up @@ -1106,16 +1120,27 @@ export const thresholdsWithTokenDecimal = (decimals: number): FormatNumberThresh
]

export const getJupTokenPrice = async (id: string): Promise<TokenPriceData> => {
const response = await axios.get(`https://price.jup.ag/v4/price?ids=${id}&vsToken=USDC`)
const response = await axios.get<RawJupApiResponse>(
`https://api.jup.ag/price/v2?ids=${id}&showExtraInfo=true`
)

return {
price: response.data.data[id].price ?? 0
price: Number(response.data.data[id].price),
buyPrice: Number(response.data.data[id].extraInfo?.quotedPrice.buyPrice ?? 0),
sellPrice: Number(response.data.data[id].extraInfo?.quotedPrice.sellPrice ?? 0)
}
}

export const getJupTokensRatioPrice = async (id: string, vsId: string): Promise<TokenPriceData> => {
const response = await axios.get(`https://price.jup.ag/v4/price?ids=${id}&vsToken=${vsId}`)
export const getJupTokensRatioPrice = async (
id: string,
vsId: string
): Promise<Omit<TokenPriceData, 'buyPrice' | 'sellPrice'>> => {
const response = await axios.get<RawJupApiResponse>(
`https://api.jup.ag/price/v2?ids=${id}&vsToken=${vsId}`
)

return {
price: response.data.data[id].price ?? 0
price: Number(response.data.data[id].price)
}
}

Expand Down
Loading