diff --git a/src/assets/tokens/PINTO_wstETH.png b/src/assets/tokens/PINTO_wstETH.png new file mode 100644 index 000000000..2e4402765 Binary files /dev/null and b/src/assets/tokens/PINTO_wstETH.png differ diff --git a/src/assets/tokens/wstETH.png b/src/assets/tokens/wstETH.png new file mode 100644 index 000000000..f7772f6dc Binary files /dev/null and b/src/assets/tokens/wstETH.png differ diff --git a/src/components/DevPage.tsx b/src/components/DevPage.tsx index 0e5b637e9..a722981e0 100644 --- a/src/components/DevPage.tsx +++ b/src/components/DevPage.tsx @@ -1180,8 +1180,8 @@ const ViewFunctionCaller = () => { className="h-10 px-3 py-2 text-sm border rounded-md bg-white" > - {viewFunctions.map((fn) => ( - @@ -1204,7 +1204,10 @@ const ViewFunctionCaller = () => {
Function Parameters:
{selectedFunctionObj.inputs.map((input, idx) => ( -
+
diff --git a/src/components/charts/AdvancedChart.tsx b/src/components/charts/AdvancedChart.tsx index 4bce088df..8cd184b9d 100644 --- a/src/components/charts/AdvancedChart.tsx +++ b/src/components/charts/AdvancedChart.tsx @@ -153,10 +153,10 @@ export const AdvancedChart = () => { const filtered = useMemo(() => { const output: TVChartFormattedData[][] = []; if (selectedCharts.length === 0) return output; - selectedCharts.forEach((selection, selectedIndex) => { + selectedCharts.forEach((selection) => { const selectedChart = chartSetupData[selection]; const _output: TVChartFormattedData[] = []; - seasonsData.data.forEach((seasonData, index) => { + seasonsData.data.forEach((seasonData) => { // Verify a datapoint is available for this season (some data, like tractor, is not since season 1) if (selectedChart.priceScaleKey in seasonData) { const formatValue = selectedChart.valueFormatter; diff --git a/src/components/charts/LineChart.tsx b/src/components/charts/LineChart.tsx index 75430e6a8..3dc32f676 100644 --- a/src/components/charts/LineChart.tsx +++ b/src/components/charts/LineChart.tsx @@ -18,7 +18,7 @@ import { LineChartHorizontalReferenceLine, plugins } from "./chartHelpers"; Chart.register(LineController, LineElement, LinearScale, LogarithmicScale, CategoryScale, PointElement, Filler); export type LineChartData = { - values: number[]; + values: Array; } & Record; export type MakeGradientFunction = ( @@ -96,8 +96,14 @@ const LineChart = React.memo( const [yTickMin, yTickMax] = useMemo(() => { // Otherwise calculate based on data - const maxData = data.reduce((acc, next) => Math.max(acc, ...next.values), Number.MIN_SAFE_INTEGER); - const minData = data.reduce((acc, next) => Math.min(acc, ...next.values), Number.MAX_SAFE_INTEGER); + const maxData = data.reduce( + (acc, next) => Math.max(acc, ...next.values.filter((v): v is number => v !== null)), + Number.MIN_SAFE_INTEGER, + ); + const minData = data.reduce( + (acc, next) => Math.min(acc, ...next.values.filter((v): v is number => v !== null)), + Number.MAX_SAFE_INTEGER, + ); const maxTick = maxData === minData && maxData === 0 ? 1 : maxData; let minTick = minData - (maxData - minData) * 0.1; diff --git a/src/components/charts/MarketPerformanceChart.tsx b/src/components/charts/MarketPerformanceChart.tsx index 75c2c8abe..c3ba8ada8 100644 --- a/src/components/charts/MarketPerformanceChart.tsx +++ b/src/components/charts/MarketPerformanceChart.tsx @@ -165,20 +165,32 @@ const MarketPerformanceChart = ({ season, size, className }: MarketPerformanceCh const chartData: LineChartData[] = []; const tokens: (Token | undefined)[] = []; const chartStrokeGradients: StrokeGradientFunction[] = []; - for (const token of ["NET", "WETH", "cbETH", "cbBTC", "WSOL"]) { + const allTokens = ["NET", "WETH", "cbETH", "wstETH", "cbBTC", "WSOL"]; + const tokensPresent: string[] = []; + for (const token of allTokens) { + if (allData[token]?.length > 0) { + tokensPresent.push(token); + } + } + for (const token of tokensPresent) { + const missingDatapoints = allData.NET.length - allData[token].length; for (let i = 0; i < allData[token].length; i++) { - chartData[i] ??= { + chartData[i + missingDatapoints] ??= { timestamp: allData[token][i].timestamp, values: [], }; if (dataType !== DataType.PRICE) { - chartData[i].values.push(allData[token][i].value); + chartData[i + missingDatapoints].values.push(allData[token][i].value); } else { - chartData[i].values.push( + chartData[i + missingDatapoints].values.push( transformValue(allData[token][i].value, minValues[token], maxValues[token], priceTransformRanges[token]), ); } } + for (let i = 0; i < missingDatapoints; i++) { + // Datapoint was missing for this token but present for other tokens + chartData[i].values.push(null); + } const tokenObj = tokenConfig.find((t) => t.symbol === token); tokens.push(tokenObj); chartStrokeGradients.push(gradientFunctions.solid(tokenObj?.color ?? "green")); @@ -298,6 +310,8 @@ const MarketPerformanceChart = ({ season, size, className }: MarketPerformanceCh {chartDataset.tokens.map((token, idx) => { const tokenSymbol = token?.symbol ?? "NET"; const isNetToken = tokenSymbol === "NET"; + const missingDatapoints = allData.NET.length - allData[tokenSymbol].length; + const value = allData[tokenSymbol][displayIndex - missingDatapoints]?.value; return (
{tokenSymbol === "NET" && "Total: "}

- {displayValueFormatter(allData[tokenSymbol][displayIndex].value)} + {typeof value === "number" ? displayValueFormatter(value) : "-"}

{idx < Object.keys(allData).length - 1 && ( diff --git a/src/components/charts/TVChart.tsx b/src/components/charts/TVChart.tsx index da839ad39..a75c131e1 100644 --- a/src/components/charts/TVChart.tsx +++ b/src/components/charts/TVChart.tsx @@ -16,9 +16,7 @@ import { createChart, } from "lightweight-charts"; import { MutableRefObject, useEffect, useMemo, useRef, useState } from "react"; -import { GearIcon } from "../Icons"; import TooltipSimple from "../TooltipSimple"; -import { ResponsivePopover } from "../ui/ResponsivePopover"; export type TVChartFormattedData = { time: Time; diff --git a/src/components/nav/ChartSelectPanel.tsx b/src/components/nav/ChartSelectPanel.tsx index c9dc7660a..6e468245a 100644 --- a/src/components/nav/ChartSelectPanel.tsx +++ b/src/components/nav/ChartSelectPanel.tsx @@ -6,10 +6,10 @@ import { useDebouncedEffect } from "@/utils/useDebounce"; import { cn } from "@/utils/utils"; import { useAtom } from "jotai"; import { isEqual } from "lodash"; -import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { memo, useCallback, useEffect, useMemo, useState } from "react"; import { renderAnnouncement } from "../AnnouncementBanner"; import { ChevronDownIcon, SearchIcon } from "../Icons"; -import { MIN_ADV_SEASON, chartSeasonInputsAtom, selectedChartsAtom } from "../charts/AdvancedChart"; +import { chartSeasonInputsAtom, selectedChartsAtom } from "../charts/AdvancedChart"; import { Input } from "../ui/Input"; import { ScrollArea } from "../ui/ScrollArea"; import { Separator } from "../ui/Separator"; @@ -105,10 +105,11 @@ const ChartSelectPanel = memo(() => { } else { // When selecting, initialize season input to min value const chartData = chartSetupData.find((chart) => chart.index === selection); - if (chartData && chartData.inputOptions === "SEASON") { + const opts = chartData?.inputOptions; + if (opts && opts.type === "SEASON") { setInternalSeasonInputs((prev) => ({ ...prev, - [chartData.id]: MIN_ADV_SEASON, + [chartData.id]: opts.minSeason, })); } selectedItems.push(selection); @@ -159,7 +160,15 @@ const ChartSelectPanel = memo(() => { () => { const clampedInputs = {}; for (const chartId in rawSeasonInputs) { - clampedInputs[chartId] = Math.max(MIN_ADV_SEASON, Math.min(currentSeason, rawSeasonInputs[chartId])); + const chartData = chartSetupData.find((chart) => chart.id === chartId); + if (!chartData || !chartData.inputOptions) { + // Should be unreachable (season input cannot change for chart that didn't configure seasons) + continue; + } + clampedInputs[chartId] = Math.max( + chartData.inputOptions.minSeason, + Math.min(currentSeason, rawSeasonInputs[chartId]), + ); } setInternalSeasonInputs(clampedInputs); }, @@ -230,7 +239,7 @@ const ChartSelectPanel = memo(() => {
{data.shortDescription}
- {isSelected && data.inputOptions === "SEASON" && ( + {isSelected && data.inputOptions?.type === "SEASON" && (
e.stopPropagation()}>