Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
36c851f
feat: add wsteth + pintowsteth
burr-nim Nov 12, 2025
47fc67a
feat: add wsteth to silo page
burr-nim Nov 12, 2025
e415176
feat: add wsteth
burr-nim Nov 13, 2025
d8c37de
Merge branch 'main' into burr/wsteth-migration
burr-nim Nov 18, 2025
2c02676
feat: fix dev page keys + update pintowsteth address
burr-nim Nov 18, 2025
880ec07
feat: update to use multicall instead of advpipe on silo convert cache
burr-nim Nov 18, 2025
9f01c00
feat: add invalid well swap strategizer
burr-nim Nov 18, 2025
aeabe09
feat: update silo convert
burr-nim Nov 18, 2025
b9d3d0a
bug: fix swaps div by zero bug
burr-nim Nov 19, 2025
d3d4606
feat: remove unnecessary comment
burr-nim Nov 19, 2025
0557718
feat: add new token icons
burr-nim Nov 24, 2025
df925cd
feat: update silo convert quoteing
burr-nim Nov 24, 2025
649332e
Wsteth market performance wip
PintoPirate Nov 26, 2025
893d8a2
Merge branch 'burr/wsteth-migration' into pp/market-chart-wsteth
PintoPirate Nov 26, 2025
b79231d
Arranges newly appearing datapoints
PintoPirate Nov 27, 2025
a03c221
Fix incorrect selected value display
PintoPirate Nov 28, 2025
97423bb
Proper undefined check
PintoPirate Nov 28, 2025
9156f56
Wsteth added to big chart
PintoPirate Nov 28, 2025
75e61ae
Correct sync offset
PintoPirate Nov 28, 2025
91c107d
minimal allowed starting cumulative season to wsteth deployment
PintoPirate Nov 29, 2025
d243a5c
Apply appropriate offset to cumulative market data
PintoPirate Nov 29, 2025
cc41350
Remove unnecessary offset
PintoPirate Dec 2, 2025
4dfb481
Add missing wsteth mapping
PintoPirate Dec 2, 2025
6977f62
remove test wsteth data
PintoPirate Dec 2, 2025
5aa7e08
Remove console log
PintoPirate Dec 3, 2025
6e9e542
Refactor nonnull assertion
PintoPirate Dec 3, 2025
bc4832e
Merge pull request #316 from pinto-org/pp/market-chart-wsteth
PintoPirate Dec 6, 2025
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
Binary file added src/assets/tokens/PINTO_wstETH.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/tokens/wstETH.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions src/components/DevPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1180,8 +1180,8 @@ const ViewFunctionCaller = () => {
className="h-10 px-3 py-2 text-sm border rounded-md bg-white"
>
<option value="">-- Select a function --</option>
{viewFunctions.map((fn) => (
<option key={fn.name} value={fn.name}>
{viewFunctions.map((fn, i) => (
<option key={`${fn.name}-view-funciton-caller-${i.toString()}`} value={fn.name}>
{fn.name}
{fn.inputs && fn.inputs.length > 0 && ` (${fn.inputs.length} params)`}
</option>
Expand All @@ -1204,7 +1204,10 @@ const ViewFunctionCaller = () => {
<div className="text-sm font-medium">Function Parameters:</div>
<div className="flex flex-col gap-3 p-4 border rounded-lg bg-gray-50">
{selectedFunctionObj.inputs.map((input, idx) => (
<div key={idx} className="flex items-center gap-2">
<div
key={`${idx.toString()}-view-function-caller-input-${input.name ?? ""}`}
className="flex items-center gap-2"
>
<Label className="text-xs text-gray-600 whitespace-nowrap min-w-[120px]">
{input.name || `param${idx}`} ({input.type}):
</Label>
Expand Down
4 changes: 2 additions & 2 deletions src/components/charts/AdvancedChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 9 additions & 3 deletions src/components/charts/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<number | null>;
} & Record<string, any>;

export type MakeGradientFunction = (
Expand Down Expand Up @@ -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;
Expand Down
24 changes: 19 additions & 5 deletions src/components/charts/MarketPerformanceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down Expand Up @@ -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 (
<div
key={`${tokenSymbol}-value`}
Expand All @@ -324,7 +338,7 @@ const MarketPerformanceChart = ({ season, size, className }: MarketPerformanceCh
<div style={{ color: token?.color }} className={`${!token?.color && "text-pinto-green-3"}`}>
{tokenSymbol === "NET" && "Total: "}
<p className="inline-block w-[7.1ch] text-right">
{displayValueFormatter(allData[tokenSymbol][displayIndex].value)}
{typeof value === "number" ? displayValueFormatter(value) : "-"}
</p>
</div>
{idx < Object.keys(allData).length - 1 && (
Expand Down
2 changes: 0 additions & 2 deletions src/components/charts/TVChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 16 additions & 7 deletions src/components/nav/ChartSelectPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
},
Expand Down Expand Up @@ -230,7 +239,7 @@ const ChartSelectPanel = memo(() => {
<div className="pinto-sm-light text-pinto-gray-4">{data.shortDescription}</div>
</div>
</div>
{isSelected && data.inputOptions === "SEASON" && (
{isSelected && data.inputOptions?.type === "SEASON" && (
<div className="mt-2 flex items-center gap-2" onClick={(e) => e.stopPropagation()}>
<label
htmlFor={`season-input-${data.id}`}
Expand All @@ -241,7 +250,7 @@ const ChartSelectPanel = memo(() => {
<Input
id={`season-input-${data.id}`}
type="number"
min={MIN_ADV_SEASON}
min={data.inputOptions?.minSeason}
max={currentSeason}
value={rawSeasonInputs[data.id] || ""}
onChange={(e) => handleSeasonInputChange(data.id, e.target.value)}
Expand Down
25 changes: 19 additions & 6 deletions src/components/nav/PriceButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import { PoolData, usePriceData, useTwaDeltaBLPQuery, useTwaDeltaBQuery } from "
import useTokenData from "@/state/useTokenData";
import { withTracking } from "@/utils/analytics";
import { formatter } from "@/utils/format";
import { stringEq } from "@/utils/string";
import { getTokenIndex } from "@/utils/token";
import { Token } from "@/utils/types";
import { cn } from "@/utils/utils";
import { HTMLAttributes, memo, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { useChainId } from "wagmi";
import { renderAnnouncement } from "../AnnouncementBanner";
import { InlineCenterSpan, Row } from "../Container";
import { InlineCenterSpan } from "../Container";
import { ExternalLinkIcon, ForwardArrowIcon } from "../Icons";
import TooltipSimple from "../TooltipSimple";
import { Card, CardContent, CardFooter, CardHeader } from "../ui/Card";
Expand All @@ -38,7 +39,7 @@ function PriceButtonPanel() {
const { data: twaDeltaBMap } = useTwaDeltaBLPQuery();

const whitelistedPools = useMemo(() => {
return priceData.pools.filter((pool) => pool.pool.isWhitelisted);
return priceData.pools.filter((pool) => pool.pool && pool.pool.isWhitelisted);
}, [priceData.pools]);

const mainTokenBalances = useMemo(() => {
Expand Down Expand Up @@ -301,14 +302,18 @@ const PoolGroup = ({
if (props.showPrices) {
return (
<>
<PoolPriceDisplay key={`${pool.pool.address}_prices_${i}`} pool={pool} priceData={props.priceData} />
<PoolPriceDisplay
key={`${props.title}_${pool.pool.address}_prices_${i}`}
pool={pool}
priceData={props.priceData}
/>
</>
);
}

return (
<PoolCard
key={`${pool.pool.address}_card_${i}`}
key={`${props.title}_${pool.pool.address}_card_${i}`}
pool={pool}
chainId={props.chainId}
priceData={props.priceData}
Expand All @@ -329,8 +334,16 @@ interface PoolPriceDisplayProps {

const PoolPriceDisplay = ({ pool, priceData }: PoolPriceDisplayProps) => {
const backingTokenIndex = pool.tokens.findIndex((token: Token) => !token.isMain);

if (backingTokenIndex === -1) {
return null;
}

const tokenToShow = priceData.tokenPrices.get(pool.tokens[backingTokenIndex]);
if (!tokenToShow) return null;

if (!tokenToShow) {
return null;
}

return (
<div className="flex flex-row justify-between">
Expand Down Expand Up @@ -375,7 +388,7 @@ const PoolCard = ({ pool, chainId, priceData, expandAll, useTwa, twaDeltaBMap }:
const token0BalanceUsd = pool.balances[0].mul(token0Price ?? TokenValue.ZERO) || 0n;
const token1BalanceUsd = pool.balances[1].mul(token1Price ?? TokenValue.ZERO) || 0n;
const mainTokenIndex = pool.tokens.findIndex((token: Token) => token.isMain);
const deltaBar = pool.balances[mainTokenIndex].gt(0)
const deltaBar = pool.balances[mainTokenIndex]?.gt(0)
? Number(pool.deltaB.abs().div(pool.balances[mainTokenIndex]).mul(100).toHuman()).toFixed(2)
: "0";

Expand Down
2 changes: 2 additions & 0 deletions src/constants/slots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
USDC_TOKEN,
WETH_TOKEN,
WSOL_TOKEN,
WSTETH_TOKEN,
} from "@/constants/tokens";
import { AddressMap } from "@/utils/types";
import { ChainLookup } from "@/utils/types.generic";
Expand All @@ -15,6 +16,7 @@ export const addressAllowanceSlotMap: ChainLookup<AddressMap<number>> = {
[base.id]: {
[MAIN_TOKEN[base.id].address.toLowerCase()]: 1,
[WETH_TOKEN[base.id].address.toLowerCase()]: 4,
[WSTETH_TOKEN[base.id].address.toLowerCase()]: 2,
[CBETH_TOKEN[base.id].address.toLowerCase()]: 52,
[USDC_TOKEN[base.id].address.toLowerCase()]: 10,
[CBBTC_TOKEN[base.id].address.toLowerCase()]: 10,
Expand Down
Loading
Loading