This commit is contained in:
tjs 2022-07-10 22:01:16 -04:00
parent 30ba236553
commit 771ee63ea5
14 changed files with 683 additions and 92 deletions

View File

@ -1,17 +1,27 @@
import { useWallet } from '@solana/wallet-adapter-react'
import MangoAccount from '../components/MangoAccount'
import TokenList from './TokenList'
import AccountActions from './AccountActions'
import Swap from './swap/Swap'
import SwapTokenChart from './swap/SwapTokenChart'
import mangoStore from '../store/state'
const Home = () => {
const { connected } = useWallet()
const inputTokenInfo = mangoStore((s) => s.inputTokenInfo)
const outputTokenInfo = mangoStore((s) => s.outputTokenInfo)
return (
<div className="mt-8">
<div className="flex-col space-y-4">
<div className="mx-auto flex max-w-7xl justify-center space-x-4">
<MangoAccount />
<div className="w-full space-y-6">
<SwapTokenChart
inputTokenId={inputTokenInfo?.extensions?.coingeckoId}
outputTokenId={outputTokenInfo?.extensions?.coingeckoId}
/>
<TokenList />
</div>
<div className="space-y-6">
<Swap />
{connected ? <AccountActions /> : null}

View File

@ -1,10 +1,10 @@
import Image from 'next/image'
import mangoStore from '../store/state'
import { formatDecimal } from '../utils/numbers'
import { formatDecimal, numberFormat } from '../utils/numbers'
import ContentBox from './shared/ContentBox'
const MangoAccount = () => {
const TokenList = () => {
const mangoAccount = mangoStore((s) => s.mangoAccount)
const group = mangoStore((s) => s.group)
@ -19,18 +19,20 @@ const MangoAccount = () => {
<table className="mt-4 min-w-full">
<thead>
<tr>
<th className="pr-12 text-sm">Asset</th>
<th className="pr-12 text-sm">Price</th>
<th className="pr-12 text-sm">Deposits/Lend</th>
<th className="pr-12 text-sm">Borrows</th>
<th className="pr-12 text-sm">Balance</th>
<th className="text-left text-sm">Asset</th>
<th className="text-right text-sm">Deposits</th>
<th className="text-right text-sm">APR</th>
<th className="text-right text-sm">Borrows</th>
<th className="text-right text-sm">APR</th>
<th className="text-right text-sm">Balance</th>
</tr>
</thead>
<tbody>
{banks.map((bank) => {
const oraclePrice = bank.value.price
return (
<tr key={bank.key}>
<td className="pr-12 pt-4">
<td className="pt-4">
<div className="flex items-center">
<div className="mr-4 flex min-w-[30px] items-center">
<Image
@ -40,15 +42,15 @@ const MangoAccount = () => {
src={`/icons/${bank.value.name.toLowerCase()}.svg`}
/>
</div>
<div>
<div className="flex flex-col">
<div>{bank.value.name}</div>
<div className="text-sm text-th-fgd-4">
${formatDecimal(oraclePrice.toNumber(), 2)}
</div>
</div>
</div>
</td>
<td className="pr-12 pt-4 text-right">
<div className="">${bank.value.price?.toFixed(2)}</div>
</td>
<td className="pr-12 pt-4">
<td className="pt-4">
<div className="flex flex-col text-right">
<div>
{formatDecimal(
@ -56,6 +58,10 @@ const MangoAccount = () => {
bank.value.mintDecimals
)}
</div>
</div>
</td>
<td className="pt-4">
<div className="flex flex-col text-right">
<div className="text-green-500">
{formatDecimal(
bank.value.getDepositRate().toNumber(),
@ -65,7 +71,7 @@ const MangoAccount = () => {
</div>
</div>
</td>
<td className="pr-12 pt-4">
<td className="pt-4">
<div className="flex flex-col text-right">
<div>
{formatDecimal(
@ -73,6 +79,10 @@ const MangoAccount = () => {
bank.value.mintDecimals
)}
</div>
</div>
</td>
<td className="pt-4">
<div className="flex flex-col text-right">
<div className="text-red-500">
{formatDecimal(
bank.value.getBorrowRate().toNumber(),
@ -82,20 +92,19 @@ const MangoAccount = () => {
</div>
</div>
</td>
<td className="pr-12 pt-4 text-right">
<td className="pt-4 text-right">
<div className="px-2">
{mangoAccount
? formatDecimal(
mangoAccount.deposits(bank.value),
bank.value.mintDecimals
)
? formatDecimal(mangoAccount.getUi(bank.value))
: '-'}
</div>
<div className="px-2">
<div className="px-2 text-sm text-th-fgd-4">
$
{mangoAccount
? formatDecimal(
mangoAccount.borrows(bank.value),
bank.value.mintDecimals
mangoAccount.getUi(bank.value) *
oraclePrice.toNumber(),
2
)
: '-'}
</div>
@ -111,4 +120,4 @@ const MangoAccount = () => {
)
}
export default MangoAccount
export default TokenList

View File

@ -0,0 +1,20 @@
const LineChartIcon = ({ className }: { className: string }) => {
return (
<svg
className={`${className}`}
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
fill="currentColor"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.766 24.073a1.5 1.5 0 0 1 .098-2.12l9.875-8.999a1.5 1.5 0 0 1 1.99-.028l3.493 3.008 8.892-8.105a1.5 1.5 0 1 1 2.021 2.217l-9.875 9a1.5 1.5 0 0 1-1.989.028l-3.493-3.007-8.893 8.104a1.5 1.5 0 0 1-2.119-.098Z"
/>
</svg>
)
}
export default LineChartIcon

View File

@ -21,11 +21,6 @@ type JupiterRoutesProps = {
setAmountOut: (x?: number) => void
}
type Routes = {
routesInfos: RouteInfo[]
cached: boolean
}
const parseJupiterRoute = async (
jupiter: Jupiter,
selectedRoute: RouteInfo,
@ -49,16 +44,7 @@ const parseJupiterRoute = async (
}
const getBestRoute = (routesInfos: RouteInfo[]) => {
const routesInfosWithoutRaydium = routesInfos.filter((r) => {
if (r.marketInfos.length > 1) {
for (const mkt of r.marketInfos) {
if (mkt.amm.label === 'Raydium' || mkt.amm.label === 'Serum')
return false
}
}
return true
})
return routesInfosWithoutRaydium[0]
return routesInfos[0]
}
const JupiterRoutes = ({
@ -71,7 +57,7 @@ const JupiterRoutes = ({
setAmountOut,
}: JupiterRoutesProps) => {
const [jupiter, setJupiter] = useState<Jupiter>()
const [routes, setRoutes] = useState<Routes>()
const [routes, setRoutes] = useState<RouteInfo[]>()
const [selectedRoute, setSelectedRoute] = useState<RouteInfo>()
const [showRoutesModal, setShowRoutesModal] = useState(false)
const [outputTokenInfo, setOutputTokenInfo] = useState<TokenInfo>()
@ -121,18 +107,28 @@ const JupiterRoutes = ({
inputAmount: amountIn * 10 ** inputBank.mintDecimals, // raw input amount of tokens
slippage, // The slippage in % terms
filterTopNResult: 10,
onlyDirectRoutes: true,
})
setRoutes(computedRoutes)
const bestRoute = getBestRoute(computedRoutes!.routesInfos)
setSelectedRoute(bestRoute)
const tokenOut = tokens.find(
// @ts-ignore
(t) => t.address === outputBank.mint.toString()
(t: any) => t.address === outputBank.mint.toString()
)
setOutputTokenInfo(tokenOut)
setAmountOut(toUiDecimals(bestRoute.outAmount, tokenOut.decimals))
const routesInfosWithoutRaydium = computedRoutes?.routesInfos.filter(
(r) => {
if (r.marketInfos.length > 1) {
for (const mkt of r.marketInfos) {
if (mkt.amm.label === 'Raydium') return false
}
}
return true
}
)
if (routesInfosWithoutRaydium?.length) {
setRoutes(routesInfosWithoutRaydium)
const bestRoute = getBestRoute(computedRoutes!.routesInfos)
setSelectedRoute(bestRoute)
setAmountOut(toUiDecimals(bestRoute.outAmount, tokenOut.decimals))
}
}
}
@ -150,7 +146,7 @@ const JupiterRoutes = ({
{submitting ? <Loading className="mr-2 h-5 w-5" /> : null} Swap
</Button>
</div>
{routes?.routesInfos.length && selectedRoute && outputTokenInfo ? (
{routes?.length && selectedRoute && outputTokenInfo ? (
<>
<div
role="button"
@ -160,7 +156,7 @@ const JupiterRoutes = ({
}}
>
<SelectedRoute
routes={routes.routesInfos}
routes={routes}
selectedRoute={selectedRoute}
inputTokenSymbol={inputToken}
/>
@ -180,8 +176,9 @@ const JupiterRoutes = ({
<RoutesModal
show={showRoutesModal}
onClose={() => setShowRoutesModal(false)}
setSelectedRoute={setSelectedRoute}
selectedRoute={selectedRoute}
routes={routes.routesInfos}
routes={routes}
inputTokenSymbol={inputToken}
outputTokenInfo={outputTokenInfo}
/>

View File

@ -1,11 +1,13 @@
import { RouteInfo } from '@jup-ag/core'
import { Dispatch, SetStateAction } from 'react'
import mangoStore from '../../store/state'
import { TokenInfo } from '../../types/jupiter'
import { formatDecimal } from '../../utils/numbers'
import Modal from '../shared/Modal'
type RoutesModalProps = {
onClose: (x: boolean) => void
onClose: () => void
setSelectedRoute: Dispatch<SetStateAction<RouteInfo | undefined>>
show: boolean
routes: RouteInfo[]
selectedRoute: RouteInfo
@ -18,12 +20,19 @@ const RoutesModal = ({
show,
routes,
selectedRoute,
setSelectedRoute,
inputTokenSymbol,
outputTokenInfo,
}: RoutesModalProps) => {
const tokens = mangoStore.getState().jupiterTokens
const handleSelectRoute = (route: RouteInfo) => {
setSelectedRoute(route)
onClose()
}
return (
<Modal isOpen={show} onClose={() => onClose(false)}>
<Modal isOpen={show} onClose={() => onClose()}>
<div className="mb-4 text-center text-lg font-bold text-th-fgd-1">
{routes?.length} routes found
</div>
@ -41,7 +50,7 @@ const RoutesModal = ({
>
<button
className="w-full p-4"
// onClick={() => handleSelectRoute(route)}
onClick={() => handleSelectRoute(route)}
>
<div className="flex items-center justify-between">
<div className="flex flex-col text-left">

View File

@ -1,5 +1,6 @@
import { useState, ChangeEvent, SelectHTMLAttributes } from 'react'
import { useState, ChangeEvent } from 'react'
import { TransactionInstruction } from '@solana/web3.js'
import { ArrowDownIcon, SwitchVerticalIcon } from '@heroicons/react/solid'
import mangoStore from '../../store/state'
import ContentBox from '../shared/ContentBox'
@ -7,10 +8,7 @@ import { notify } from '../../utils/notifications'
import JupiterRoutes from './JupiterRoutes'
import TokenSelect from '../TokenSelect'
import useDebounce from '../shared/useDebounce'
const numberFormat = new Intl.NumberFormat('en', {
maximumSignificantDigits: 7,
})
import { numberFormat } from '../../utils/numbers'
const Swap = () => {
const [amountIn, setAmountIn] = useState('')
@ -20,19 +18,34 @@ const Swap = () => {
const [submitting, setSubmitting] = useState(false)
const [slippage, setSlippage] = useState(0.1)
const debouncedAmountIn = useDebounce(amountIn, 300)
const set = mangoStore.getState().set
const tokens = mangoStore((s) => s.jupiterTokens)
const handleAmountInChange = (e: ChangeEvent<HTMLInputElement>) => {
setAmountIn(e.target.value)
}
const handleTokenInSelect = (e: ChangeEvent<HTMLSelectElement>) => {
const inputTokenInfo = tokens.find((t: any) => t.symbol === e.target.value)
set((s) => {
s.inputTokenInfo = inputTokenInfo
})
setInputToken(e.target.value)
}
const handleTokenOutSelect = (e: ChangeEvent<HTMLSelectElement>) => {
const outputTokenInfo = tokens.find((t: any) => t.symbol === e.target.value)
set((s) => {
s.outputTokenInfo = outputTokenInfo
})
setOutputToken(e.target.value)
}
const handleSwitchTokens = () => {
setInputToken(outputToken)
setOutputToken(inputToken)
}
const handleSwap = async (
userDefinedInstructions: TransactionInstruction[]
) => {
@ -49,20 +62,19 @@ const Swap = () => {
mangoAccount,
inputToken,
amountIn: parseFloat(amountIn),
outputToken: 'USDC',
outputToken,
userDefinedInstructions,
})
console.log('Success swapping:', tx)
// notify({
// title: 'Transaction confirmed',
// type: 'success',
// txid: tx,
// })
notify({
title: 'Transaction confirmed',
type: 'success',
txid: tx,
})
await actions.reloadAccount()
} catch (e: any) {
console.log('Error swapping:', e)
notify({
title: 'Transaction failed',
description: e.message,
@ -77,19 +89,38 @@ const Swap = () => {
return (
<ContentBox className="max-w-md">
<div className="">
<div className="mt-1 flex justify-between rounded-md bg-th-bkg-1 py-2 px-6">
<TokenSelect token={inputToken} onChange={handleTokenInSelect} />
<div>
<input
type="text"
name="amountIn"
id="amountIn"
className="tex-th-fgd-2 w-full rounded-lg border-none bg-transparent text-right text-2xl focus:ring-0"
placeholder="0.00"
value={amountIn}
onChange={handleAmountInChange}
/>
<div className="mt-1 flex-col rounded-md bg-th-bkg-1 py-2 px-6">
<div className="flex justify-between">
<TokenSelect token={inputToken} onChange={handleTokenInSelect} />
<div>
<input
type="text"
name="amountIn"
id="amountIn"
className="tex-th-fgd-2 w-full rounded-lg border-none bg-transparent text-right text-2xl focus:ring-0"
placeholder="0.00"
value={amountIn}
onChange={handleAmountInChange}
/>
</div>
</div>
<div>
<label
htmlFor="default-range"
className="block text-sm font-medium text-gray-900 dark:text-gray-300"
></label>
<input
id="default-range"
type="range"
className="mb-6 h-1 w-full cursor-pointer appearance-none rounded-lg bg-th-bkg-3"
></input>
</div>
</div>
<div className="-my-5 flex justify-center">
<button onClick={handleSwitchTokens}>
<ArrowDownIcon className="h-10 w-10 rounded-full border-4 border-th-bkg-1 bg-th-bkg-2 p-1.5 text-th-fgd-3 md:hover:text-th-primary" />
{/* <SwitchVerticalIcon className="default-transition h-10 w-10 rounded-full border-4 border-th-bkg-1 bg-th-bkg-2 p-1.5 text-th-fgd-3 md:hover:text-th-primary" /> */}
</button>
</div>
<div className="mt-4 flex items-center justify-between rounded-md py-2 px-6">
<TokenSelect token={outputToken} onChange={handleTokenOutSelect} />

View File

@ -0,0 +1,298 @@
import { FunctionComponent, useEffect, useMemo, useState } from 'react'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { AreaChart, Area, XAxis, YAxis, Tooltip } from 'recharts'
import useDimensions from 'react-cool-dimensions'
import LineChartIcon from '../icons/LineChartIcon'
import ContentBox from '../shared/ContentBox'
dayjs.extend(relativeTime)
interface SwapTokenChartProps {
inputTokenId?: string
outputTokenId?: string
}
export const numberFormatter = Intl.NumberFormat('en', {
minimumFractionDigits: 1,
maximumFractionDigits: 5,
})
export const numberCompacter = Intl.NumberFormat('en', {
notation: 'compact',
maximumFractionDigits: 2,
})
const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
inputTokenId,
outputTokenId,
}) => {
const [chartData, setChartData] = useState([])
const [hideChart, setHideChart] = useState(false)
const [baseTokenId, setBaseTokenId] = useState('')
const [quoteTokenId, setQuoteTokenId] = useState('')
const [inputTokenInfo, setInputTokenInfo] = useState<any>(null)
const [outputTokenInfo, setOutputTokenInfo] = useState<any>(null)
const [mouseData, setMouseData] = useState<any>(null)
const [daysToShow, setDaysToShow] = useState(1)
const { observe, width, height } = useDimensions()
const handleMouseMove = (coords: any) => {
if (coords.activePayload) {
setMouseData(coords.activePayload[0].payload)
}
}
const handleMouseLeave = () => {
setMouseData(null)
}
useEffect(() => {
if (!inputTokenId || !outputTokenId) {
return
}
if (['usd-coin', 'tether'].includes(inputTokenId)) {
setBaseTokenId(outputTokenId)
setQuoteTokenId(inputTokenId)
} else {
setBaseTokenId(inputTokenId)
setQuoteTokenId(outputTokenId)
}
}, [inputTokenId, outputTokenId])
// Use ohlc data
const getChartData = async () => {
const inputResponse = await fetch(
`https://api.coingecko.com/api/v3/coins/${baseTokenId}/ohlc?vs_currency=usd&days=${daysToShow}`
)
const outputResponse = await fetch(
`https://api.coingecko.com/api/v3/coins/${quoteTokenId}/ohlc?vs_currency=usd&days=${daysToShow}`
)
const inputData = await inputResponse.json()
const outputData = await outputResponse.json()
let data: any[] = []
if (Array.isArray(inputData)) {
data = data.concat(inputData)
}
if (Array.isArray(outputData)) {
data = data.concat(outputData)
}
const formattedData = data.reduce((a, c) => {
const found = a.find((price: any) => price.time === c[0])
if (found) {
if (['usd-coin', 'tether'].includes(quoteTokenId)) {
found.price = found.inputPrice / c[4]
} else {
found.price = c[4] / found.inputPrice
}
} else {
a.push({ time: c[0], inputPrice: c[4] })
}
return a
}, [])
formattedData[formattedData.length - 1].time = Date.now()
setChartData(formattedData.filter((d: any) => d.price))
}
// Alternative chart data. Needs a timestamp tolerance to get data points for each asset
// const getChartData = async () => {
// const now = Date.now() / 1000
// const inputResponse = await fetch(
// `https://api.coingecko.com/api/v3/coins/${inputTokenId}/market_chart/range?vs_currency=usd&from=${
// now - 1 * 86400
// }&to=${now}`
// )
// const outputResponse = await fetch(
// `https://api.coingecko.com/api/v3/coins/${outputTokenId}/market_chart/range?vs_currency=usd&from=${
// now - 1 * 86400
// }&to=${now}`
// )
// const inputData = await inputResponse.json()
// const outputData = await outputResponse.json()
// const data = inputData?.prices.concat(outputData?.prices)
// const formattedData = data.reduce((a, c) => {
// const found = a.find(
// (price) => c[0] >= price.time - 120000 && c[0] <= price.time + 120000
// )
// if (found) {
// found.price = found.inputPrice / c[1]
// } else {
// a.push({ time: c[0], inputPrice: c[1] })
// }
// return a
// }, [])
// setChartData(formattedData.filter((d) => d.price))
// }
const getInputTokenInfo = async () => {
const response = await fetch(
`https://api.coingecko.com/api/v3/coins/${inputTokenId}?localization=false&tickers=false&developer_data=false&sparkline=false
`
)
const data = await response.json()
setInputTokenInfo(data)
}
const getOutputTokenInfo = async () => {
const response = await fetch(
`https://api.coingecko.com/api/v3/coins/${outputTokenId}?localization=false&tickers=false&developer_data=false&sparkline=false
`
)
const data = await response.json()
setOutputTokenInfo(data)
}
useMemo(() => {
if (baseTokenId && quoteTokenId) {
getChartData()
}
}, [daysToShow, baseTokenId, quoteTokenId])
useMemo(() => {
if (baseTokenId) {
getInputTokenInfo()
}
if (quoteTokenId) {
getOutputTokenInfo()
}
}, [baseTokenId, quoteTokenId])
const chartChange = chartData.length
? ((chartData[chartData.length - 1]['price'] - chartData[0]['price']) /
chartData[0]['price']) *
100
: 0
return (
<ContentBox>
{chartData.length && baseTokenId && quoteTokenId ? (
<div className="">
<div className="flex items-start justify-between">
<div>
{inputTokenInfo && outputTokenInfo ? (
<div className="text-sm text-th-fgd-3">
{`${outputTokenInfo?.symbol?.toUpperCase()}/${inputTokenInfo?.symbol?.toUpperCase()}`}
</div>
) : null}
{mouseData ? (
<>
<div className="text-lg font-bold text-th-fgd-1">
{numberFormatter.format(mouseData['price'])}
<span
className={`ml-2 text-xs ${
chartChange >= 0 ? 'text-th-green' : 'text-th-red'
}`}
>
{chartChange.toFixed(2)}%
</span>
</div>
<div className="text-xs font-normal text-th-fgd-3">
{dayjs(mouseData['time']).format('DD MMM YY, h:mma')}
</div>
</>
) : (
<>
<div className="text-lg font-bold text-th-fgd-1">
{numberFormatter.format(
chartData[chartData.length - 1]['price']
)}
<span
className={`ml-2 text-xs ${
chartChange >= 0 ? 'text-th-green' : 'text-th-red'
}`}
>
{chartChange.toFixed(2)}%
</span>
</div>
<div className="text-xs font-normal text-th-fgd-3">
{dayjs(chartData[chartData.length - 1]['time']).format(
'DD MMM YY, h:mma'
)}
</div>
</>
)}
</div>
<div className="flex justify-end">
<button
className={`px-3 py-2 text-xs font-bold text-th-fgd-4 focus:outline-none md:hover:text-th-primary ${
daysToShow === 1 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(1)}
>
24H
</button>
<button
className={`px-3 py-2 text-xs font-bold text-th-fgd-4 focus:outline-none md:hover:text-th-primary ${
daysToShow === 7 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(7)}
>
7D
</button>
<button
className={`px-3 py-2 text-xs font-bold text-th-fgd-4 focus:outline-none md:hover:text-th-primary ${
daysToShow === 30 && 'text-th-primary'
}`}
onClick={() => setDaysToShow(30)}
>
30D
</button>
</div>
</div>
{!hideChart ? (
<div className="mt-6 h-36 w-full" ref={observe}>
<AreaChart
width={width}
height={height}
data={chartData}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
>
<Tooltip
cursor={{
strokeOpacity: 0,
}}
content={<></>}
/>
{/* <defs>
<linearGradient id="gradientArea" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#ffba24" stopOpacity={0.9} />
<stop offset="90%" stopColor="#ffba24" stopOpacity={0} />
</linearGradient>
</defs> */}
<Area
isAnimationActive={true}
type="monotone"
dataKey="price"
stroke="#ffba24"
fill="url(#gradientArea)"
/>
<XAxis dataKey="time" hide />
<YAxis
dataKey="price"
type="number"
domain={['dataMin', 'dataMax']}
hide
/>
</AreaChart>
</div>
) : null}
</div>
) : (
<div className="mt-4 rounded-md bg-th-bkg-3 p-4 text-center text-th-fgd-3 md:mt-0">
<LineChartIcon className="mx-auto h-6 w-6 text-th-primary" />
Chart not available
</div>
)}
</ContentBox>
)
}
export default SwapTokenChart

View File

@ -20,13 +20,16 @@
"@solana/wallet-adapter-react-ui": "^0.9.6",
"@solana/wallet-adapter-wallets": "^0.16.0",
"@tailwindcss/forms": "^0.5.0",
"dayjs": "^1.11.3",
"immer": "^9.0.12",
"lodash.debounce": "^4.0.8",
"next": "12.1.5",
"next-themes": "^0.1.1",
"process": "^0.11.10",
"react": "18.0.0",
"react-cool-dimensions": "^2.0.7",
"react-dom": "18.0.0",
"recharts": "^2.1.12",
"zustand": "^3.7.2"
},
"peerDependencies": {

View File

@ -35,6 +35,12 @@ const loadJupTokens = async () => {
set((s) => {
s.jupiterTokens = result
})
const inputTokenInfo = result.find((t: any) => t.symbol === 'SOL')
const outputTokenInfo = result.find((t: any) => t.symbol === 'USDC')
set((s) => {
s.inputTokenInfo = inputTokenInfo
s.outputTokenInfo = outputTokenInfo
})
})
}

View File

@ -47,6 +47,8 @@ export type MangoStore = {
notificationIdCounter: number
notifications: Array<Notification>
serumOrders: Order[] | undefined
inputTokenInfo: any
outputTokenInfo: any
set: (x: (x: MangoStore) => void) => void
wallet: {
tokens: TokenAccount[]
@ -67,11 +69,13 @@ const mangoStore = create<MangoStore>(
connection,
group: undefined,
client: MangoClient.connect(provider, CLUSTER, MANGO_V4_ID[CLUSTER]),
inputTokenInfo: undefined,
jupiterTokens: [],
mangoAccount: undefined,
markets: undefined,
notificationIdCounter: 0,
notifications: [],
outputTokenInfo: undefined,
serumOrders: undefined,
set: (fn) => set(produce(fn)),
wallet: {

View File

@ -69,4 +69,32 @@
body {
@apply font-body tracking-wider;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 16px;
width: 16px;
border-radius: 100%;
background: var(--primary);
cursor: pointer;
margin-top: -1px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */
}
/* All the same stuff for Firefox */
input[type=range]::-moz-range-thumb {
height: 16px;
width: 16px;
border-radius: 100%;
background: var(--primary);
cursor: pointer;
}
/* All the same stuff for IE */
input[type=range]::-ms-thumb {
height: 16px;
width: 16px;
border-radius: 100%;
background: var(--primary);
cursor: pointer;
}

View File

@ -1,4 +1,11 @@
import { RouteInfo } from '@jup-ag/core'
export type TokenInfo = {
decimals: number
symbol: string
}
export type Routes = {
routesInfos: RouteInfo[]
cached: boolean
}

View File

@ -13,3 +13,7 @@ export const formatDecimal = (
if (decimals === 6) return digits6.format(value)
if (decimals === 9) return digits9.format(value)
}
export const numberFormat = new Intl.NumberFormat('en', {
maximumSignificantDigits: 7,
})

191
yarn.lock
View File

@ -10,6 +10,13 @@
core-js-pure "^3.20.2"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580"
integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.10.2", "@babel/runtime@^7.16.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.6.2":
version "7.17.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
@ -17,16 +24,9 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580"
integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
dependencies:
regenerator-runtime "^0.13.4"
"@blockworks-foundation/mango-v4@git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-v4.git":
version "0.0.1"
resolved "git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-v4.git#0ff269c38c72229a33a79a7fc37f400456a35c73"
resolved "git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-v4.git#c829fdf59d4aaf75733278d5c209acc42b02fcb4"
dependencies:
"@project-serum/anchor" "^0.24.2"
"@project-serum/serum" "^0.13.65"
@ -1696,6 +1696,11 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
inherits "^2.0.1"
safe-buffer "^5.0.1"
classnames@^2.2.5:
version "2.3.1"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
@ -1767,6 +1772,11 @@ crypto-hash@^1.3.0:
resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247"
integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==
css-unit-converter@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21"
integrity sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@ -1777,11 +1787,77 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33"
integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==
d3-array@2, d3-array@^2.3.0:
version "2.12.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81"
integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==
dependencies:
internmap "^1.0.0"
"d3-color@1 - 2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e"
integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==
"d3-format@1 - 2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767"
integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==
"d3-interpolate@1.2.0 - 2", d3-interpolate@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163"
integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==
dependencies:
d3-color "1 - 2"
"d3-path@1 - 2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-2.0.0.tgz#55d86ac131a0548adae241eebfb56b4582dd09d8"
integrity sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==
d3-scale@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.3.0.tgz#28c600b29f47e5b9cd2df9749c206727966203f3"
integrity sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==
dependencies:
d3-array "^2.3.0"
d3-format "1 - 2"
d3-interpolate "1.2.0 - 2"
d3-time "^2.1.1"
d3-time-format "2 - 3"
d3-shape@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-2.1.0.tgz#3b6a82ccafbc45de55b57fcf956c584ded3b666f"
integrity sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==
dependencies:
d3-path "1 - 2"
"d3-time-format@2 - 3":
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6"
integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==
dependencies:
d3-time "1 - 2"
"d3-time@1 - 2", d3-time@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682"
integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==
dependencies:
d3-array "2"
damerau-levenshtein@^1.0.7:
version "1.0.8"
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
dayjs@^1.11.3:
version "1.11.3"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258"
integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==
debug@4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
@ -1810,7 +1886,7 @@ debug@^4.1.1, debug@^4.3.2:
dependencies:
ms "2.1.2"
decimal.js-light@^2.5.1:
decimal.js-light@^2.4.1, decimal.js-light@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934"
integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==
@ -1888,6 +1964,13 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
dom-helpers@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
dependencies:
"@babel/runtime" "^7.1.2"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
@ -2365,7 +2448,7 @@ event-stream@=3.3.4:
stream-combiner "~0.0.4"
through "~2.3.1"
eventemitter3@^4.0.0, eventemitter3@^4.0.4, eventemitter3@^4.0.7:
eventemitter3@^4.0.0, eventemitter3@^4.0.1, eventemitter3@^4.0.4, eventemitter3@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
@ -2408,6 +2491,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-equals@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.4.tgz#3add9410585e2d7364c2deeb6a707beadb24b927"
integrity sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==
fast-glob@^3.2.11, fast-glob@^3.2.9:
version "3.2.11"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
@ -2755,6 +2843,11 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
internmap@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95"
integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
is-arguments@^1.0.4:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
@ -3088,7 +3181,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.20, lodash@^4.17.21:
lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -3544,6 +3637,11 @@ postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss-value-parser@^3.3.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
@ -3595,7 +3693,7 @@ promise-retry@2.0.1, promise-retry@^2.0.1:
err-code "^2.0.2"
retry "^0.12.0"
prop-types@^15.8.1:
prop-types@^15.6.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -3641,6 +3739,11 @@ randombytes@^2.1.0:
dependencies:
safe-buffer "^5.1.0"
react-cool-dimensions@^2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/react-cool-dimensions/-/react-cool-dimensions-2.0.7.tgz#2fe6657608f034cd7c89f149ed14e79cf1cb2d50"
integrity sha512-z1VwkAAJ5d8QybDRuYIXTE41RxGr5GYsv1bQhbOBE8cMfoZQZpcF0odL64vdgrQVzat2jayedj1GoYi80FWcbA==
react-dom@18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023"
@ -3649,11 +3752,41 @@ react-dom@18.0.0:
loose-envify "^1.1.0"
scheduler "^0.21.0"
react-is@^16.13.1:
react-is@^16.10.2, react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-resize-detector@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-7.1.2.tgz#8ef975dd8c3d56f9a5160ac382ef7136dcd2d86c"
integrity sha512-zXnPJ2m8+6oq9Nn8zsep/orts9vQv3elrpA+R8XTcW7DVVUJ9vwDwMXaBtykAYjMnkCIaOoK9vObyR7ZgFNlOw==
dependencies:
lodash "^4.17.21"
react-smooth@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-2.0.1.tgz#74c7309916d6ccca182c4b30c8992f179e6c5a05"
integrity sha512-Own9TA0GPPf3as4vSwFhDouVfXP15ie/wIHklhyKBH5AN6NFtdk0UpHBnonV11BtqDkAWlt40MOUc+5srmW7NA==
dependencies:
fast-equals "^2.0.0"
react-transition-group "2.9.0"
react-transition-group@2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==
dependencies:
dom-helpers "^3.4.0"
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
react@18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96"
@ -3677,6 +3810,38 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
recharts-scale@^0.4.4:
version "0.4.5"
resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9"
integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==
dependencies:
decimal.js-light "^2.4.1"
recharts@^2.1.12:
version "2.1.12"
resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.1.12.tgz#2cbc87b0ed386a1328e9bab2b808a5fbce22e59f"
integrity sha512-dAzEuc9AjM+IF0A33QzEdBEUnyGKJcGUPa0MYm0vd38P3WouQjrj2egBrCNInE7ZcQwN+z3MoT7Rw03u8nP9HA==
dependencies:
classnames "^2.2.5"
d3-interpolate "^2.0.0"
d3-scale "^3.0.0"
d3-shape "^2.0.0"
eventemitter3 "^4.0.1"
lodash "^4.17.19"
react-is "^16.10.2"
react-resize-detector "^7.1.2"
react-smooth "^2.0.1"
recharts-scale "^0.4.4"
reduce-css-calc "^2.1.8"
reduce-css-calc@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz#7ef8761a28d614980dc0c982f772c93f7a99de03"
integrity sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==
dependencies:
css-unit-converter "^1.1.1"
postcss-value-parser "^3.3.0"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"