- Market Cap
- ${marketCap}
+ Market Cap (USD)
+ ${marketCapUSD}
+
+
+ Market Cap ({baseTokenTicker})
+ {marketCap}
24h Volume
diff --git a/src/pages/repository/components/decentralize-modals/Trade-Modal.tsx b/src/pages/repository/components/decentralize-modals/Trade-Modal.tsx
index 9dba1b6a..e203e243 100644
--- a/src/pages/repository/components/decentralize-modals/Trade-Modal.tsx
+++ b/src/pages/repository/components/decentralize-modals/Trade-Modal.tsx
@@ -6,12 +6,13 @@ import { Fragment } from 'react'
import React from 'react'
import { toast } from 'react-hot-toast'
import SVG from 'react-inlinesvg'
+import { BeatLoader } from 'react-spinners'
import CloseCrossIcon from '@/assets/icons/close-cross.svg'
import { Button } from '@/components/common/buttons'
import { imgUrlFormatter } from '@/helpers/imgUrlFormatter'
import { shortenAddress } from '@/helpers/shortenAddress'
-import { getBuySellTransactionsOfCurve, getTokenBuyPrice, getTokenSellPrice } from '@/lib/bonding-curve'
+import { getCurrentStep, getTokenBuyPrice, getTokenNextBuyPrice, getTokenSellPrice } from '@/lib/bonding-curve'
import { buyTokens } from '@/lib/bonding-curve/buy'
import { getCurveState, getTokenCurrentSupply } from '@/lib/bonding-curve/helpers'
import { sellTokens } from '@/lib/bonding-curve/sell'
@@ -20,7 +21,9 @@ import { useGlobalStore } from '@/stores/globalStore'
import { CurveState } from '@/stores/repository-core/types'
import { RepoToken } from '@/types/repository'
-import TradeChartComponent from './TradeChartComponent'
+import { customFormatNumber } from '../../helpers/customFormatNumbers'
+import MarketStats from './MarketStats'
+import { TradeChart } from './TradeChart'
import TransferToLP from './TransferToLP'
type TradeModalProps = {
@@ -28,14 +31,16 @@ type TradeModalProps = {
isOpen: boolean
}
-type ChartData = {
- time: string | number
- value: string | number
+interface MarketStatsProps {
+ marketCap: string
+ marketCapUSD: string
+ volume: string
+ circulatingSupply: string
+ baseTokenTicker: string
}
export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
const amountRef = React.useRef
(null)
- const [chartData, setChartData] = React.useState([])
const [balances, setBalances] = React.useState>({})
const [transactionPending, setTransactionPending] = React.useState(false)
const [curveState, setCurveState] = React.useState({} as CurveState)
@@ -45,6 +50,23 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
const [maxSupply, setMaxSupply] = React.useState('0')
const [reserveTokenBalance, setReserveTokenBalance] = React.useState('0')
+ const [afterTradeSupply, setAfterTradeSupply] = React.useState<{
+ rangeTo: number
+ price: number
+ index: number
+ } | null>(null)
+ const [isFetchingNextPrice, setIsFetchingNextPrice] = React.useState(false)
+ const [nextPrice, setNextPrice] = React.useState('0')
+ const [baseAssetPriceUSD, setBaseAssetPriceUSD] = React.useState('0')
+ const [priceInUSD, setPriceInUSD] = React.useState('0')
+ const [stats, setStats] = React.useState({
+ marketCap: '0',
+ marketCapUSD: '0',
+ volume: '0',
+ circulatingSupply: '0',
+ baseTokenTicker: '-'
+ })
+ const [currentIndex, setCurrentIndex] = React.useState(0)
const [repoTokenBalance, setRepoTokenBalance] = React.useState('0')
const [amount, setAmount] = React.useState('')
const [tokenPair, setTokenPair] = React.useState([])
@@ -75,9 +97,42 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
setMaxSupply(formattedMaxSupply)
// setProgress((+curveState.reserveBalance / +curveState.fundingGoal) * 100)
handleGetTokenHoldersBalances()
+ fetchStats()
}
}, [curveState])
+ React.useEffect(() => {
+ if (!curveState.steps || !stats?.circulatingSupply) return
+
+ if (!amount || amount === '0') return setAfterTradeSupply(null)
+ const numericAmount = Number(selectedSide === 'buy' ? amount : `-${amount}`)
+ const afterAmount = BigNumber(stats.circulatingSupply).plus(BigNumber(numericAmount))
+ const formattedAfterAmount = BigNumber(afterAmount).multipliedBy(
+ BigNumber(10).pow(BigNumber(curveState.repoToken.denomination))
+ )
+ const currentStep = curveState.steps[currentIndex + 1]
+ let index = 0
+ const point = curveState.steps.find((step, idx) => {
+ if (formattedAfterAmount.lte(step.rangeTo)) {
+ index = idx
+ return true
+ }
+ return false
+ })
+ const formattedPointPrice = BigNumber(point?.price || 0).dividedBy(
+ BigNumber(10).pow(BigNumber(curveState.reserveToken.denomination))
+ )
+ const formattedCurrentStepRange = BigNumber(currentStep.rangeTo).dividedBy(
+ BigNumber(10).pow(BigNumber(curveState.repoToken.denomination))
+ )
+
+ if (formattedCurrentStepRange.eq(afterAmount) && currentStep.price === point?.price) {
+ setAfterTradeSupply(null)
+ } else {
+ setAfterTradeSupply({ rangeTo: afterAmount.toNumber(), price: formattedPointPrice.toNumber(), index })
+ }
+ }, [curveState.steps, stats?.circulatingSupply, amount])
+
React.useEffect(() => {
if (!repo?.token || !curveState?.maxSupply) return
if (+currentSupply > 0) {
@@ -85,6 +140,7 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
.dividedBy(BigNumber(10).pow(BigNumber(repo.token!.denomination)))
.toString()
setProgress(BigNumber(+currentSupply).dividedBy(BigNumber(formattedMaxSupply)).multipliedBy(100).toNumber())
+ fetchCurrentStep(curveState, currentSupply)
}
}, [currentSupply, curveState])
@@ -96,6 +152,23 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
}
}, [amount])
+ React.useEffect(() => {
+ if (!baseAssetPriceUSD || !+baseAssetPriceUSD || !+nextPrice) return
+
+ // Convert nextPrice from AR to USD by multiplying AR price by USD/AR rate
+ const priceInUSD = BigNumber(nextPrice)
+ .multipliedBy(BigNumber(baseAssetPriceUSD))
+ // .dividedBy(BigNumber(10).pow(BigNumber(repo?.token!.denomination)))
+ .toString()
+
+ setPriceInUSD(priceInUSD)
+ }, [baseAssetPriceUSD, nextPrice])
+
+ React.useEffect(() => {
+ if (!curveState?.reserveToken || !+nextPrice) return
+ fetchBaseAssetPriceUSD()
+ }, [curveState, nextPrice])
+
async function handleGetTokenBalances() {
if (!repo || !address || !repo.bondingCurve || !repo.token) return
const reserveTokenBalance = await fetchTokenBalance(repo.bondingCurve.reserveToken.processId!, address!)
@@ -121,50 +194,6 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
setBalances(balancesAsPercentages)
}
- async function handleGetTransactions() {
- if (!repo || !repo.bondingCurve || !repo.token) return
-
- const chart: ChartData[] = []
-
- const transactions = await getBuySellTransactionsOfCurve(repo.bondingCurve.processId!)
- let lastTimestamp = 0
- transactions.forEach((transaction: any) => {
- const costTag = transaction.node.tags.find((tag: any) => tag.name === 'Cost')
- const tokensSoldTag = transaction.node.tags.find((tag: any) => tag.name === 'TokensSold')
- const tokensBoughtTag = transaction.node.tags.find((tag: any) => tag.name === 'TokensBought')
-
- const cost = costTag ? parseInt(costTag.value) : 0
- const tokensSold = tokensSoldTag ? parseInt(tokensSoldTag.value) : 0
- const tokensBought = tokensBoughtTag ? parseInt(tokensBoughtTag.value) : 0
- let price = 0
- // Calculate price based on transaction type
- if (tokensBought > 0) {
- // Buy transaction: Price = Cost / TokensBought
- price = cost / tokensBought
- } else if (tokensSold > 0) {
- // Sell transaction: Price = Proceeds / TokensSold
- price = cost / tokensSold
- } else {
- // If no tokens were bought or sold, skip this transaction as it doesn't affect the price
- return
- }
-
- let timestamp = transaction.node.ingested_at || 0
- // Ensure timestamps are incrementing
- if (timestamp <= lastTimestamp) {
- timestamp = lastTimestamp + 1
- }
- lastTimestamp = timestamp
-
- // const timestamp = transaction.node.ingested_at || 0
- chart.push({
- time: timestamp,
- value: price
- })
- })
- setChartData(chart)
- }
-
async function handleGetCurveState() {
if (!repo?.bondingCurve) return
@@ -282,7 +311,7 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
await handleGetTokenBalances()
await handleGetCurveState()
- await handleGetTransactions()
+ // await handleGetTransactions()
}
async function handleBuy() {
@@ -355,6 +384,58 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
.toString()
}
+ async function fetchCurrentStep(_curveData: CurveState, _currentSupply: string) {
+ const scaledCurrentSupply = BigNumber(_currentSupply)
+ .multipliedBy(BigNumber(10).pow(BigNumber(repo?.token?.denomination || 0)))
+ .toFixed()
+
+ const currentStep = await getCurrentStep(scaledCurrentSupply, _curveData.steps)
+ setCurrentIndex(currentStep - 1)
+ }
+
+ async function fetchStats() {
+ if (!repo?.token?.processId) return
+ setIsFetchingNextPrice(true)
+
+ const normalizedSupply = BigNumber(currentSupply).toFixed()
+ const nextPrice = await getTokenNextBuyPrice(currentSupply, curveState)
+ const nextPriceFormatted = BigNumber(nextPrice)
+ .dividedBy(BigNumber(10).pow(BigNumber(curveState.reserveToken.denomination)))
+ .toString()
+
+ setNextPrice(nextPriceFormatted)
+ setIsFetchingNextPrice(false)
+
+ const marketCapUSD = BigNumber(nextPriceFormatted).multipliedBy(BigNumber(normalizedSupply)).toFixed()
+ setStats({
+ marketCap: '0',
+ marketCapUSD: marketCapUSD,
+ volume: '0',
+ circulatingSupply: normalizedSupply,
+ baseTokenTicker: repo?.bondingCurve?.reserveToken?.tokenTicker || '-'
+ })
+ }
+
+ async function fetchBaseAssetPriceUSD() {
+ if (!curveState?.reserveToken) return
+ const token = curveState.reserveToken
+
+ if (token.tokenTicker === 'TUSDA') {
+ setBaseAssetPriceUSD('24')
+
+ return
+ }
+
+ try {
+ const response = await fetch('https://api.redstone.finance/prices?symbol=AR&provider=redstone-rapid&limit=1')
+ const data = await response.json()
+ setBaseAssetPriceUSD(data[0].value.toString())
+ } catch (error) {
+ console.error('Failed to fetch AR price:', error)
+ setBaseAssetPriceUSD('0')
+ }
+ }
+
const selectedToken = tokenPair[selectedTokenToTransact]
if (!repo || !selectedToken) return null
@@ -384,10 +465,10 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
-
+
- {repo.token?.tokenTicker}/{repo.bondingCurve?.reserveToken?.tokenTicker}
+ ${repo.token?.tokenTicker} / ${repo.bondingCurve?.reserveToken?.tokenTicker}
-
+
{/* TradingView Chart Section - 70% */}
-
-
+
+ {isFetchingNextPrice ? (
+
+
+
+ {repo?.bondingCurve?.reserveToken?.tokenTicker}
+
+
+ ) : (
+
+ {customFormatNumber(+nextPrice, 12, 3)}{' '}
+ {repo?.bondingCurve?.reserveToken?.tokenTicker}
+
+ )}
+
${customFormatNumber(+priceInUSD, 12, 3)}
+
+
+ {/*
+ /> */}
{/* Buy/Sell Widget Section - 30% */}
@@ -574,6 +678,7 @@ export default function TradeModal({ onClose, isOpen }: TradeModalProps) {
)}
+
diff --git a/src/pages/repository/components/decentralize-modals/TradeChart.tsx b/src/pages/repository/components/decentralize-modals/TradeChart.tsx
new file mode 100644
index 00000000..1a46043f
--- /dev/null
+++ b/src/pages/repository/components/decentralize-modals/TradeChart.tsx
@@ -0,0 +1,285 @@
+import BigNumber from 'bignumber.js'
+import { useEffect, useState } from 'react'
+import {
+ Area,
+ CartesianGrid,
+ Line,
+ LineChart,
+ ReferenceDot,
+ ReferenceLine,
+ ResponsiveContainer,
+ Tooltip,
+ XAxis,
+ YAxis
+} from 'recharts'
+
+import { CurveStep } from '@/lib/discrete-bonding-curve/curve'
+import { RepoLiquidityPoolToken, RepoToken } from '@/types/repository'
+
+import { formatNumberUsingNumeral, preventScientificNotationFloat } from '../../helpers/customFormatNumbers'
+
+// import { CurveStepExternal, CurveStepInternal } from '@/types/repository'
+
+export function TradeChart({
+ steps,
+ currentIndex,
+ tokenA,
+ tokenB,
+ afterTradeSupply
+}: {
+ steps: CurveStep[]
+ currentIndex: number | null
+ tokenA: RepoToken
+ tokenB: RepoLiquidityPoolToken
+ afterTradeSupply: { rangeTo: number; price: number; index: number } | null
+}) {
+ const [data, setData] = useState
([])
+ const [tooltip, setTooltip] = useState<{
+ show: boolean
+ x: number
+ y: number
+ index: number
+ }>({
+ show: false,
+ x: 0,
+ y: 0,
+ index: 0
+ })
+
+ useEffect(() => {
+ if (steps.length > 0) {
+ setData(
+ steps.map((step) => ({
+ rangeTo: BigNumber(step.rangeTo).dividedBy(BigNumber(10).pow(tokenA.denomination)).toNumber(),
+ price: BigNumber(step.price).dividedBy(BigNumber(10).pow(tokenB.denomination)).toNumber()
+ }))
+ )
+ }
+ }, [steps])
+
+ const handleMouseEnter = (event: any) => {
+ console.log(event)
+ setTooltip({
+ show: true,
+ x: event.cx,
+ y: event.cy,
+ index: Number(event.id)
+ })
+ }
+
+ const handleMouseLeave = () => {
+ console.log('leave')
+ setTooltip({ ...tooltip, show: false })
+ }
+
+ const handleTooltipLabel = (label: string) => {
+ const currentIndex = data.findIndex((entry) => entry.rangeTo === Number(label))
+ const previousIndex = currentIndex - 1
+ const previousStep = previousIndex > 0 ? data[previousIndex] : { rangeTo: 0, price: 0 }
+ const currentStep = data[currentIndex]
+
+ const previousStepFormatted = formatNumberUsingNumeral(previousStep.rangeTo).toUpperCase()
+ const currentStepFormatted = formatNumberUsingNumeral(currentStep.rangeTo).toUpperCase()
+
+ return `Mint Range: ${previousStepFormatted} - ${currentStepFormatted}`
+ }
+
+ const formatXAxisTick = (value: number) => {
+ return formatNumberUsingNumeral(value).toUpperCase()
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [`${preventScientificNotationFloat(value)} qAR`, 'Price']}
+ labelFormatter={handleTooltipLabel}
+ />
+
+
+
+
+ {afterTradeSupply && (
+ <>
+
+ }
+ stroke="green"
+ onMouseEnter={handleMouseEnter}
+ onMouseLeave={handleMouseLeave}
+ />
+ >
+ )}
+
+ }
+ stroke="#06b6d4"
+ onMouseEnter={handleMouseEnter}
+ onMouseLeave={handleMouseLeave}
+ />
+
+
+
+
+ )
+}
+
+// A simple custom tooltip component
+function CustomTooltip({
+ x,
+ y,
+ show,
+ index,
+ data
+}: {
+ x: number
+ y: number
+ show: boolean
+ index: number
+ data: CurveStep[]
+}) {
+ if (!show) return null
+
+ const style: React.CSSProperties = {
+ position: 'absolute',
+ left: x - 75, // offset the mouse position a bit
+ top: y + 20,
+ padding: '6px 8px',
+ backgroundColor: 'rgba(0,0,0,0.8)',
+ border: '1px solid rgba(255,255,255,0.1)',
+ borderRadius: '8px',
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
+ color: 'rgba(255,255,255,0.7)',
+ pointerEvents: 'none', // so mouse events pass through
+ zIndex: 999,
+ width: '250px'
+ }
+
+ const step = data[index || 0]
+ const previousStep = index > 0 ? data[index - 1] : { rangeTo: 0, price: 0 }
+ const previousStepFormatted = formatNumberUsingNumeral(previousStep.rangeTo).toUpperCase()
+ const currentStepFormatted = formatNumberUsingNumeral(step.rangeTo).toUpperCase()
+ return (
+
+
+ Current Range: {previousStepFormatted} - {currentStepFormatted}
+
+
Price: {preventScientificNotationFloat(step.price)} qAR
+
+ )
+}
+
+function PulsingDot(props: any) {
+ // The ReferenceDot will pass certain props to your custom shape,
+ // typically including cx, cy, r, and fill.
+ // You can add stroke, strokeWidth, etc., as needed.
+ const { cx, cy, id, r = 6, fill = '#a855f7', stroke = '#fff', onMouseEnter, onMouseLeave } = props
+
+ return (
+ onMouseEnter({ id, cx, cy })} onMouseLeave={() => onMouseLeave()}>
+ {/* 1) The central dot */}
+
+
+ {/* 2) A pulsing ring that grows & fades out repeatedly */}
+
+ {/* Expand radius from r to (r+10), for example, over 1s */}
+
+ {/* Fade out the ring from opacity=0.3 to 0 over 1s */}
+
+
+
+ )
+}
diff --git a/src/pages/repository/components/decentralize-modals/TradeChartComponent.tsx b/src/pages/repository/components/decentralize-modals/TradeChartComponent.tsx
deleted file mode 100644
index fd51828d..00000000
--- a/src/pages/repository/components/decentralize-modals/TradeChartComponent.tsx
+++ /dev/null
@@ -1,418 +0,0 @@
-import BigNumber from 'bignumber.js'
-// import { ColorType, createChart } from 'lightweight-charts'
-import Chart from 'chart.js/auto'
-import numeral from 'numeral'
-import React from 'react'
-import { BeatLoader } from 'react-spinners'
-
-import { getCurrentStep, getTokenNextBuyPrice } from '@/lib/bonding-curve'
-import { getTokenCurrentSupply } from '@/lib/bonding-curve/helpers'
-import { CurveStep } from '@/lib/discrete-bonding-curve/curve'
-import { customFormatNumber, formatNumberUsingNumeral } from '@/pages/repository/helpers/customFormatNumbers'
-import { CurveState } from '@/stores/repository-core/types'
-import { Repo } from '@/types/repository'
-
-import MarketStats from './MarketStats'
-
-const highlightPlugin = {
- id: 'highlight',
- beforeDraw: function (chart: any) {
- // @ts-ignore
- if (chart?.tooltip?._active && Array.isArray(chart.tooltip._active) && chart.tooltip._active.length) {
- // Get the current point and the next point
- const activePoint = chart.tooltip._active[0]
- const currentIndex = activePoint.index
- const currentPoint = activePoint.element
- const nextPoint = chart.getDatasetMeta(0).data[currentIndex + 1]
- // @ts-ignore
- if (!nextPoint) return
-
- const ctx = chart.ctx
- const x = currentPoint.x
- const nextX = nextPoint.x
- const yAxis = chart.scales.y
- ctx.save()
- ctx.fillStyle = 'rgba(6, 182, 212, 0.2)'
- ctx.fillRect(x, yAxis.top, nextX - x, yAxis.bottom - yAxis.top)
- ctx.restore()
- }
- }
-}
-Chart.register(highlightPlugin)
-type ChartData = {
- time: string | number
- value: string | number
-}
-
-interface MarketStatsProps {
- marketCap: string
- volume: string
- circulatingSupply: string
-}
-
-export default function TradeChartComponent({
- data,
- repo,
- reserveBalance,
- curveState,
- repoTokenBuyAmount
-}: {
- data: ChartData[]
- repo: Repo
- reserveBalance: string
- curveState: CurveState
- repoTokenBuyAmount: string
-}) {
- const [currentStep, setCurrentStep] = React.useState(0)
- const [isFetchingNextPrice, setIsFetchingNextPrice] = React.useState(false)
- const [nextPrice, setNextPrice] = React.useState('0')
- const [baseAssetPriceUSD, setBaseAssetPriceUSD] = React.useState('0')
- const [priceInUSD, setPriceInUSD] = React.useState('0')
- const [stats, setStats] = React.useState({
- marketCap: '0',
- volume: '0',
- circulatingSupply: '0'
- })
-
- const [curveStepsCache, setCurveStepsCache] = React.useState<{ rangeTo: number; price: number }[]>([])
- const [currentSupply, setCurrentSupply] = React.useState<{ rangeTo: number; price: number }>({ rangeTo: 0, price: 0 })
- const [afterTradeSupply, setAfterTradeSupply] = React.useState<{ rangeTo: number; price: number }>({
- rangeTo: 0,
- price: 0
- })
-
- const canvasRef = React.useRef(null)
- const chart = React.useRef | null>(null)
- console.log({ currentStep })
- // React.useEffect(() => {
- // if (!chartContainerRef.current) return
- // setSize({
- // width: chartContainerRef.current.clientWidth,
- // height: chartContainerRef.current.clientHeight
- // })
- // }, [chartContainerRef])
-
- React.useEffect(() => {
- if (!curveState) return
- const interval = setInterval(fetchNextPrice, 15000)
- return () => clearInterval(interval)
- }, [curveState])
-
- React.useEffect(() => {
- if (!curveState) return
- fetchStats()
- }, [data, reserveBalance, curveState])
-
- React.useEffect(() => {
- if (!curveStepsCache || !stats?.circulatingSupply) return
- const point = curveStepsCache.find((step) => Number(stats.circulatingSupply) <= step.rangeTo)
-
- setCurrentSupply({ rangeTo: Number(stats.circulatingSupply), price: point ? point.price : 0 })
- }, [curveStepsCache, stats])
-
- React.useEffect(() => {
- if (!curveStepsCache || !stats?.circulatingSupply) return
- const afterAmount = Number(stats.circulatingSupply) + Number(repoTokenBuyAmount)
- const point = curveStepsCache.find((step) => afterAmount <= step.rangeTo)
-
- setAfterTradeSupply({ rangeTo: afterAmount, price: point ? point.price : 0 })
- }, [curveStepsCache, repoTokenBuyAmount, stats])
-
- React.useEffect(() => {
- if (!chart.current) return
- chart.current.data.datasets[1].data = [currentSupply]
- chart.current?.update()
- }, [currentSupply])
- React.useEffect(() => {
- if (!chart.current) return
- chart.current.data.datasets[2].data = [afterTradeSupply]
- chart.current?.update()
- }, [afterTradeSupply])
-
- async function fetchStats() {
- if (!repo?.token?.processId) return
- const supply = await getTokenCurrentSupply(repo?.token?.processId)
-
- const normalizedSupply = BigNumber(supply)
- .dividedBy(BigNumber(10).pow(BigNumber(repo?.token?.denomination)))
- .toString()
- const nextPrice = await getTokenNextBuyPrice(supply, curveState)
- const nextPriceFormatted = BigNumber(nextPrice)
- .dividedBy(BigNumber(10).pow(BigNumber(curveState.reserveToken.denomination)))
- .toString()
-
- setNextPrice(nextPriceFormatted)
-
- const marketCap = BigNumber(nextPriceFormatted).multipliedBy(BigNumber(normalizedSupply)).toString()
-
- setStats({
- marketCap: marketCap,
- volume: '0',
- circulatingSupply: normalizedSupply
- })
- }
-
- React.useEffect(() => {
- if (!curveState || !chart.current) return
- setTimeout(() => {
- chart.current?.update()
- }, 100)
- }, [stats])
-
- async function fetchNextPrice() {
- if (!curveState || !curveState.repoToken?.processId) return
- setIsFetchingNextPrice(true)
- const currentSupply = await getTokenCurrentSupply(curveState.repoToken.processId)
-
- const nextPrice = await getTokenNextBuyPrice(currentSupply, curveState)
- setNextPrice(
- BigNumber(nextPrice)
- .dividedBy(BigNumber(10).pow(BigNumber(curveState.reserveToken.denomination)))
- .toString()
- )
- setIsFetchingNextPrice(false)
-
- const step = await getCurrentStep(currentSupply, curveState.steps)
- setCurrentStep(step)
- }
-
- // function formatYAxis(value: number) {
- // return value.toFixed(10)
- // }
-
- React.useEffect(() => {
- if (!curveState?.steps) return
- initializeChart(curveState.steps)
- }, [curveState, chart, stats])
-
- React.useEffect(() => {
- if (!curveState?.reserveToken || !+nextPrice) return
- fetchBaseAssetPriceUSD()
- }, [curveState, nextPrice])
-
- React.useEffect(() => {
- if (!baseAssetPriceUSD || !+baseAssetPriceUSD || !+nextPrice) return
-
- // Convert nextPrice from AR to USD by multiplying AR price by USD/AR rate
- const priceInUSD = BigNumber(nextPrice)
- .multipliedBy(BigNumber(baseAssetPriceUSD))
- // .dividedBy(BigNumber(10).pow(BigNumber(repo?.token!.denomination)))
- .toString()
-
- setPriceInUSD(priceInUSD)
- }, [baseAssetPriceUSD, nextPrice])
-
- async function fetchBaseAssetPriceUSD() {
- if (!curveState?.reserveToken) return
- const token = curveState.reserveToken
-
- if (token.tokenTicker === 'TUSDA') {
- setBaseAssetPriceUSD('24')
-
- return
- }
-
- try {
- const response = await fetch('https://api.redstone.finance/prices?symbol=AR&provider=redstone-rapid&limit=1')
- const data = await response.json()
- setBaseAssetPriceUSD(data[0].value.toString())
- } catch (error) {
- console.error('Failed to fetch AR price:', error)
- setBaseAssetPriceUSD('0')
- }
- }
-
- function initializeChart(steps: CurveStep[]) {
- if (!curveState?.repoToken) return
- const curveSteps = steps.map((step) => ({
- rangeTo: BigNumber(step.rangeTo / 10 ** +curveState.repoToken.denomination).toNumber(),
- price: BigNumber(step.price / 10 ** +curveState.reserveToken.denomination).toNumber()
- }))
- setCurveStepsCache(curveSteps)
- const ctx = canvasRef.current
- if (!ctx) return
-
- let _chart: Chart<'line' | 'scatter', CurveStep[], unknown> | null = chart.current
- if (!_chart) {
- const lineColor = '#06b6d4'
- const currentColor = '#667085'
- const afterBuyColor = 'green'
- _chart = new Chart(ctx, {
- data: {
- datasets: [
- {
- type: 'line',
- label: 'Price',
- data: curveSteps,
- borderColor: lineColor,
- backgroundColor: 'transparent',
- borderWidth: 2,
- pointBackgroundColor: lineColor,
- stepped: 'before',
- fill: true,
- parsing: {
- xAxisKey: 'rangeTo', // Use 'rangeTo' as the x-axis value
- yAxisKey: 'price' // Use 'price' as the y-axis value
- },
- order: 3
- },
- {
- label: 'Current Supply',
- data: [],
- borderColor: currentColor,
- backgroundColor: 'transparent',
- pointRadius: 6,
- borderWidth: 2,
- type: 'scatter',
- parsing: {
- xAxisKey: 'rangeTo', // Use 'rangeTo' as the x-axis value
- yAxisKey: 'price' // Use 'price' as the y-axis value
- },
- order: 1
- },
- {
- label: 'After Buy Supply',
- data: [],
- borderColor: afterBuyColor,
- backgroundColor: 'transparent',
- pointRadius: 6,
- borderWidth: 2,
- type: 'scatter',
- parsing: {
- xAxisKey: 'rangeTo', // Use 'rangeTo' as the x-axis value
- yAxisKey: 'price' // Use 'price' as the y-axis value
- },
- order: 2
- }
- ]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- interaction: {
- intersect: false,
- mode: 'index'
- },
- plugins: {
- legend: {
- display: false
- },
- tooltip: {
- filter(e) {
- if (!e.label) {
- return false
- }
-
- return true
- },
- displayColors: false,
- callbacks: {
- label: function (context) {
- return `Price per token: ${customFormatNumber(context.parsed.y, 18, 5)}`
- },
- title: function (items) {
- if (!items[0]) return ``
- const index = items[0].dataIndex
- const fromRange = (items[0].dataset?.data[index - 1] as any)?.rangeTo
-
- // if (index === items[0].dataset?.data.length - 1)
- // return `Max Supply: ${formatNumberUsingNumeral(fromRange).toUpperCase()}`
-
- const toRange = (items[0].dataset?.data[index] as any)?.rangeTo
- return `Range: ${formatNumberUsingNumeral(fromRange || 0).toUpperCase()} - ${formatNumberUsingNumeral(
- toRange || 0
- ).toUpperCase()}`
- }
- }
- }
- },
- onHover: function (event, chartElement) {
- if (!event.native) return
- if (chartElement.length) {
- const target = event.native.target as HTMLElement
- target.style.cursor = 'pointer'
- } else {
- const target = event.native.target as HTMLElement
- target.style.cursor = 'default'
- }
- },
- scales: {
- x: {
- type: 'linear',
- title: {
- display: true,
- text: 'Supply',
- color: '#64748b'
- },
- ticks: {
- font: {
- size: 14
- },
- maxTicksLimit: 6,
- callback: function (value) {
- return numeral(value).format('0a').toUpperCase()
- },
- color: '#64748b'
- },
- grid: {
- display: false
- }
- },
- y: {
- position: 'right',
- beginAtZero: true,
- ticks: {
- font: {
- size: 13
- },
- callback: function (value) {
- return customFormatNumber(+value)
- },
- color: '#64748b'
- },
- grid: {
- display: false
- }
- }
- }
- }
- })
- } else {
- _chart.data.datasets[0].data = curveSteps
- _chart.update()
- }
-
- chart.current = _chart
- }
-
- return (
-
-
- {isFetchingNextPrice ? (
-
-
- {repo?.bondingCurve?.reserveToken?.tokenTicker}
-
- ) : (
-
- {customFormatNumber(+nextPrice, 12, 3)}{' '}
- {repo?.bondingCurve?.reserveToken?.tokenTicker}
-
- )}
-
${customFormatNumber(+priceInUSD, 12, 3)}
-
-
- {!chart.current?.data.datasets[0].data.length && (
-
-
No data
-
- )}
-
-
-
-
- )
-}