From 68c3d62525ab74af0cb17370fc3f4fb122552a3d Mon Sep 17 00:00:00 2001 From: FordPinto Date: Fri, 30 Oct 2026 13:17:39 -0600 Subject: [PATCH 1/2] Fix Silo deposited USD values to use current token prices --- src/pages/SiloToken.tsx | 11 +++---- src/pages/silo/SiloTable.tsx | 10 +++---- src/utils/__tests/siloValue.test.ts | 45 +++++++++++++++++++++++++++++ src/utils/siloValue.ts | 10 +++++++ 4 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 src/utils/__tests/siloValue.test.ts create mode 100644 src/utils/siloValue.ts diff --git a/src/pages/SiloToken.tsx b/src/pages/SiloToken.tsx index 3df4c142d..916971af2 100644 --- a/src/pages/SiloToken.tsx +++ b/src/pages/SiloToken.tsx @@ -38,6 +38,7 @@ import { useSiloData } from "@/state/useSiloData"; import { useSeason } from "@/state/useSunData"; import { useChainConstant } from "@/utils/chain"; import { formatter } from "@/utils/format"; +import { getTotalDepositedUsd } from "@/utils/siloValue"; import { stringEq } from "@/utils/string"; import { getTokenIndex } from "@/utils/token"; import { SiloTokenData, Token, TokenDepositData } from "@/utils/types"; @@ -135,7 +136,7 @@ function SiloTokenInner({ siloToken }: { siloToken: Token }) { {(!isMobile || (!currentAction && isMobile)) && ( )} @@ -341,7 +342,7 @@ const UndepositedSiloTokenInfoHeader = ({ siloToken, farmerBalanceSiloTokenAmoun }; interface ISiloTokenAbout extends IBaseSiloToken { - price: TokenValue; + tokenUsdPrice: TokenValue | undefined; siloData: ReturnType; } @@ -350,7 +351,7 @@ type SiloBDVs = { liquidityTVD: TokenValue; }; -const SiloTokenAbout = ({ siloToken, price, siloData }: ISiloTokenAbout) => { +const SiloTokenAbout = ({ siloToken, tokenUsdPrice, siloData }: ISiloTokenAbout) => { const seedGauge = useSeedGauge(); const siloTokenData = siloData.tokenData.get(siloToken); const denomination = useDenomination(); @@ -378,7 +379,7 @@ const SiloTokenAbout = ({ siloToken, price, siloData }: ISiloTokenAbout) => { const liquidityRatio = depositedBDV?.div(liquidityTVD.gt(0) ? liquidityTVD : TokenValue.ONE).mul(100); const optimalDepositedRatio = gaugeData?.optimalPctDepositedBdv; - const tvdUSD = depositedBDV?.mul(price); + const tvdUSD = getTotalDepositedUsd(siloTokenData, tokenUsdPrice); const depositedRatio = depositedBDV?.div(siloTVD.gt(0) ? siloTVD : TokenValue.ONE).mul(100); return ( @@ -390,7 +391,7 @@ const SiloTokenAbout = ({ siloToken, price, siloData }: ISiloTokenAbout) => {
Total Value Deposited
- {denomination === "USD" ? formatter.usd(tvdUSD) : formatter.pdv(depositedBDV)} + {denomination === "USD" ? (tvdUSD ? formatter.usd(tvdUSD) : "-") : formatter.pdv(depositedBDV)}
diff --git a/src/pages/silo/SiloTable.tsx b/src/pages/silo/SiloTable.tsx index 3840d0319..8e4bfd115 100644 --- a/src/pages/silo/SiloTable.tsx +++ b/src/pages/silo/SiloTable.tsx @@ -24,6 +24,7 @@ import { useSeason } from "@/state/useSunData"; import useTokenData from "@/state/useTokenData"; import { trackSimpleEvent } from "@/utils/analytics"; import { formatter } from "@/utils/format"; +import { getTotalDepositedUsd } from "@/utils/siloValue"; import { stringEq } from "@/utils/string"; import { getTokenIndex, sortTokensForDeposits } from "@/utils/token"; import { Token } from "@/utils/types"; @@ -133,10 +134,6 @@ function SiloTable({ hovering }: { hovering: boolean }) { const userData = farmerDeposits.get(token); const apy = apys?.[getTokenIndex(token)] as ExtendSiloTokenYield | undefined; - const depositedBDV = data?.depositedBDV; - const germinatingBDV = data?.germinatingBDV; - const totalBDV = depositedBDV?.add(germinatingBDV ?? TokenValue.ZERO); - // non whitelisted wells have 0 currentBDV const effectiveBDV = !token.isWhitelisted ? userData?.depositBDV ?? TokenValue.ZERO @@ -148,6 +145,7 @@ function SiloTable({ hovering }: { hovering: boolean }) { const _pool = priceData.pools.find((poolData) => stringEq(poolData.pool.address, token.address)); const _poolPrice = _pool?.price ?? TokenValue.ZERO; + const depositedUsd = getTotalDepositedUsd(data, token.isMain ? priceData.price : _pool?.lpUsd); const hasGerminatingDeposits = farmerSilo.deposits .get(token) @@ -212,8 +210,8 @@ function SiloTable({ hovering }: { hovering: boolean }) { {farmerSilo.isLoading ? ( - ) : _pool || token.isMain ? ( - formatter.usd(totalBDV?.mul(_pool ? _pool.price : priceData.price)) + ) : depositedUsd ? ( + formatter.usd(depositedUsd) ) : ( "-" )} diff --git a/src/utils/__tests/siloValue.test.ts b/src/utils/__tests/siloValue.test.ts new file mode 100644 index 000000000..59bf189c5 --- /dev/null +++ b/src/utils/__tests/siloValue.test.ts @@ -0,0 +1,45 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { describe, expect, it } from "vitest"; +import { getTotalDepositedUsd } from "../siloValue"; + +describe("getTotalDepositedUsd", () => { + it("values LP deposits at the LP price instead of recorded PDV times the Pinto price", () => { + // PINTO/cbETH at Base block 53298716: recorded PDV implied only $10,207.89. + const data = { + totalDeposited: TokenValue.fromHuman("2121.863992417706", 18), + germinatingAmount: TokenValue.ZERO, + depositedBDV: TokenValue.fromHuman("307318.477018", 6), + }; + const usd = getTotalDepositedUsd(data, TokenValue.fromHuman("18.393463", 6)); + + expect(usd?.toNumber()).toBeCloseTo(39028.43, 2); + }); + + it("includes germinating LP tokens and values only the deposited share of a pool", () => { + // A $200 pool with 100 LP tokens; 30 mature + 10 germinating LP are in the Silo. + const data = { + totalDeposited: TokenValue.fromHuman(30, 18), + germinatingAmount: TokenValue.fromHuman(10, 18), + }; + + expect(getTotalDepositedUsd(data, TokenValue.fromHuman(2, 6))?.toNumber()).toBe(80); + }); + + it("values Pinto deposits using Pinto's USD price", () => { + const data = { + totalDeposited: TokenValue.fromHuman(100, 6), + germinatingAmount: TokenValue.fromHuman(20, 6), + }; + + expect(getTotalDepositedUsd(data, TokenValue.fromHuman("0.033", 6))?.toNumber()).toBe(3.96); + }); + + it("distinguishes unavailable data from zero deposited value", () => { + const empty = { totalDeposited: TokenValue.ZERO, germinatingAmount: TokenValue.ZERO }; + + expect(getTotalDepositedUsd(undefined, TokenValue.ONE)).toBeUndefined(); + expect(getTotalDepositedUsd(empty, undefined)).toBeUndefined(); + expect(getTotalDepositedUsd(empty, TokenValue.ONE)?.toNumber()).toBe(0); + expect(getTotalDepositedUsd({ ...empty, totalDeposited: TokenValue.ONE }, TokenValue.ZERO)?.toNumber()).toBe(0); + }); +}); diff --git a/src/utils/siloValue.ts b/src/utils/siloValue.ts new file mode 100644 index 000000000..496473d98 --- /dev/null +++ b/src/utils/siloValue.ts @@ -0,0 +1,10 @@ +import type { TokenValue } from "@/classes/TokenValue"; +import type { SiloTokenData } from "@/utils/types"; + +export function getTotalDepositedUsd( + data: Pick | undefined, + tokenUsdPrice: TokenValue | undefined, +): TokenValue | undefined { + // Recorded PDV determines rewards; current token prices determine market value. + return data && tokenUsdPrice ? data.totalDeposited.add(data.germinatingAmount).mul(tokenUsdPrice) : undefined; +} From eaa785b270449854737c776a23cd03ac8791da84 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 30 Oct 2026 22:10:18 -0600 Subject: [PATCH 2/2] Regenerate GraphQL types from current subgraph schemas --- src/generated/gql/exchange/graphql.ts | 78 +++++++++++++++++++++++++ src/generated/gql/pinto/graphql.ts | 78 +++++++++++++++++++++++++ src/generated/gql/pintostalk/graphql.ts | 78 +++++++++++++++++++++++++ 3 files changed, 234 insertions(+) diff --git a/src/generated/gql/exchange/graphql.ts b/src/generated/gql/exchange/graphql.ts index 8fd480623..6f99de256 100644 --- a/src/generated/gql/exchange/graphql.ts +++ b/src/generated/gql/exchange/graphql.ts @@ -1312,6 +1312,23 @@ export enum ImplementationOrderBy { wells = 'wells' } +/** + * The severity level of a log entry. + * Log levels are ordered from most to least severe: CRITICAL > ERROR > WARNING > INFO > DEBUG + */ +export enum LogLevel { + /** Critical errors that require immediate attention */ + CRITICAL = 'CRITICAL', + /** Detailed diagnostic information for debugging */ + DEBUG = 'DEBUG', + /** Error conditions that indicate a failure */ + ERROR = 'ERROR', + /** Informational messages about normal operations */ + INFO = 'INFO', + /** Warning conditions that may require attention */ + WARNING = 'WARNING' +} + /** Defines the order direction, either ascending or descending */ export enum OrderDirection { asc = 'asc', @@ -1360,6 +1377,8 @@ export enum PumpOrderBy { export type Query = { __typename?: 'Query'; + /** Query execution logs emitted by the subgraph during indexing. Results are sorted by timestamp in descending order (newest first). */ + _logs: Array; /** Access to subgraph metadata */ _meta?: Maybe; aquifer?: Maybe; @@ -1397,6 +1416,17 @@ export type Query = { }; +export type QueryLogsArgs = { + first?: InputMaybe; + from?: InputMaybe; + level?: InputMaybe; + orderDirection?: InputMaybe; + search?: InputMaybe; + skip?: InputMaybe; + to?: InputMaybe; +}; + + export type QueryMetaArgs = { block?: InputMaybe; }; @@ -3946,6 +3976,54 @@ export type Block = { timestamp?: Maybe; }; +/** + * A key-value pair of additional data associated with a log entry. + * These correspond to arguments passed to the log function in the subgraph code. + */ +export type LogArgument = { + __typename?: '_LogArgument_'; + /** The parameter name */ + key: Scalars['String']['output']; + /** The parameter value, serialized as a string */ + value: Scalars['String']['output']; +}; + +/** + * Source code location metadata for a log entry. + * Indicates where in the subgraph's AssemblyScript code the log statement was executed. + */ +export type LogMeta = { + __typename?: '_LogMeta_'; + /** The column number in the source file */ + column: Scalars['Int']['output']; + /** The line number in the source file */ + line: Scalars['Int']['output']; + /** The module or file path where the log was emitted */ + module: Scalars['String']['output']; +}; + +/** + * A log entry emitted by a subgraph during indexing. + * Logs can be generated by the subgraph's AssemblyScript code using the `log.*` functions. + */ +export type Log = { + __typename?: '_Log_'; + /** Additional structured data passed to the log function as key-value pairs */ + arguments: Array; + /** Unique identifier for this log entry */ + id: Scalars['String']['output']; + /** The severity level of the log entry */ + level: LogLevel; + /** Metadata about the source location in the subgraph code where the log was emitted */ + meta: LogMeta; + /** The deployment hash of the subgraph that emitted this log */ + subgraphId: Scalars['String']['output']; + /** The log message text */ + text: Scalars['String']['output']; + /** The timestamp when the log was emitted, in RFC3339 format (e.g., '2024-01-15T10:30:00Z') */ + timestamp: Scalars['String']['output']; +}; + /** The type for the top-level _meta field */ export type Meta = { __typename?: '_Meta_'; diff --git a/src/generated/gql/pinto/graphql.ts b/src/generated/gql/pinto/graphql.ts index 77483d132..a6e41cd2f 100644 --- a/src/generated/gql/pinto/graphql.ts +++ b/src/generated/gql/pinto/graphql.ts @@ -1793,6 +1793,23 @@ export enum FarmerBalanceOrderBy { walletBalance = 'walletBalance' } +/** + * The severity level of a log entry. + * Log levels are ordered from most to least severe: CRITICAL > ERROR > WARNING > INFO > DEBUG + */ +export enum LogLevel { + /** Critical errors that require immediate attention */ + CRITICAL = 'CRITICAL', + /** Detailed diagnostic information for debugging */ + DEBUG = 'DEBUG', + /** Error conditions that indicate a failure */ + ERROR = 'ERROR', + /** Informational messages about normal operations */ + INFO = 'INFO', + /** Warning conditions that may require attention */ + WARNING = 'WARNING' +} + /** Defines the order direction, either ascending or descending */ export enum OrderDirection { asc = 'asc', @@ -2968,6 +2985,8 @@ export enum PoolOrderBy { export type Query = { __typename?: 'Query'; + /** Query execution logs emitted by the subgraph during indexing. Results are sorted by timestamp in descending order (newest first). */ + _logs: Array; /** Access to subgraph metadata */ _meta?: Maybe; bean?: Maybe; @@ -3007,6 +3026,17 @@ export type Query = { }; +export type QueryLogsArgs = { + first?: InputMaybe; + from?: InputMaybe; + level?: InputMaybe; + orderDirection?: InputMaybe; + search?: InputMaybe; + skip?: InputMaybe; + to?: InputMaybe; +}; + + export type QueryMetaArgs = { block?: InputMaybe; }; @@ -4432,6 +4462,54 @@ export type Block = { timestamp?: Maybe; }; +/** + * A key-value pair of additional data associated with a log entry. + * These correspond to arguments passed to the log function in the subgraph code. + */ +export type LogArgument = { + __typename?: '_LogArgument_'; + /** The parameter name */ + key: Scalars['String']['output']; + /** The parameter value, serialized as a string */ + value: Scalars['String']['output']; +}; + +/** + * Source code location metadata for a log entry. + * Indicates where in the subgraph's AssemblyScript code the log statement was executed. + */ +export type LogMeta = { + __typename?: '_LogMeta_'; + /** The column number in the source file */ + column: Scalars['Int']['output']; + /** The line number in the source file */ + line: Scalars['Int']['output']; + /** The module or file path where the log was emitted */ + module: Scalars['String']['output']; +}; + +/** + * A log entry emitted by a subgraph during indexing. + * Logs can be generated by the subgraph's AssemblyScript code using the `log.*` functions. + */ +export type Log = { + __typename?: '_Log_'; + /** Additional structured data passed to the log function as key-value pairs */ + arguments: Array; + /** Unique identifier for this log entry */ + id: Scalars['String']['output']; + /** The severity level of the log entry */ + level: LogLevel; + /** Metadata about the source location in the subgraph code where the log was emitted */ + meta: LogMeta; + /** The deployment hash of the subgraph that emitted this log */ + subgraphId: Scalars['String']['output']; + /** The log message text */ + text: Scalars['String']['output']; + /** The timestamp when the log was emitted, in RFC3339 format (e.g., '2024-01-15T10:30:00Z') */ + timestamp: Scalars['String']['output']; +}; + /** The type for the top-level _meta field */ export type Meta = { __typename?: '_Meta_'; diff --git a/src/generated/gql/pintostalk/graphql.ts b/src/generated/gql/pintostalk/graphql.ts index d995c1bb4..6a1e2e965 100644 --- a/src/generated/gql/pintostalk/graphql.ts +++ b/src/generated/gql/pintostalk/graphql.ts @@ -3074,6 +3074,23 @@ export enum GerminatingOrderBy { type = 'type' } +/** + * The severity level of a log entry. + * Log levels are ordered from most to least severe: CRITICAL > ERROR > WARNING > INFO > DEBUG + */ +export enum LogLevel { + /** Critical errors that require immediate attention */ + CRITICAL = 'CRITICAL', + /** Detailed diagnostic information for debugging */ + DEBUG = 'DEBUG', + /** Error conditions that indicate a failure */ + ERROR = 'ERROR', + /** Informational messages about normal operations */ + INFO = 'INFO', + /** Warning conditions that may require attention */ + WARNING = 'WARNING' +} + export type MarketPerformanceSeasonal = { __typename?: 'MarketPerformanceSeasonal'; /** Cumulative percentage change in value of each deposited non-bean token. It is possible for this value to appear out of sync with cumulativeUsdChange; it does not account for the number of these tokens deposited at the time of the value changing (only their ratios). Ordering according to silo.whitelistedTokens */ @@ -7023,6 +7040,8 @@ export enum PrevFarmerGerminatingEventOrderBy { export type Query = { __typename?: 'Query'; + /** Query execution logs emitted by the subgraph during indexing. Results are sorted by timestamp in descending order (newest first). */ + _logs: Array; /** Access to subgraph metadata */ _meta?: Maybe; beanstalk?: Maybe; @@ -7140,6 +7159,17 @@ export type Query = { }; +export type QueryLogsArgs = { + first?: InputMaybe; + from?: InputMaybe; + level?: InputMaybe; + orderDirection?: InputMaybe; + search?: InputMaybe; + skip?: InputMaybe; + to?: InputMaybe; +}; + + export type QueryMetaArgs = { block?: InputMaybe; }; @@ -13954,6 +13984,54 @@ export type Block = { timestamp?: Maybe; }; +/** + * A key-value pair of additional data associated with a log entry. + * These correspond to arguments passed to the log function in the subgraph code. + */ +export type LogArgument = { + __typename?: '_LogArgument_'; + /** The parameter name */ + key: Scalars['String']['output']; + /** The parameter value, serialized as a string */ + value: Scalars['String']['output']; +}; + +/** + * Source code location metadata for a log entry. + * Indicates where in the subgraph's AssemblyScript code the log statement was executed. + */ +export type LogMeta = { + __typename?: '_LogMeta_'; + /** The column number in the source file */ + column: Scalars['Int']['output']; + /** The line number in the source file */ + line: Scalars['Int']['output']; + /** The module or file path where the log was emitted */ + module: Scalars['String']['output']; +}; + +/** + * A log entry emitted by a subgraph during indexing. + * Logs can be generated by the subgraph's AssemblyScript code using the `log.*` functions. + */ +export type Log = { + __typename?: '_Log_'; + /** Additional structured data passed to the log function as key-value pairs */ + arguments: Array; + /** Unique identifier for this log entry */ + id: Scalars['String']['output']; + /** The severity level of the log entry */ + level: LogLevel; + /** Metadata about the source location in the subgraph code where the log was emitted */ + meta: LogMeta; + /** The deployment hash of the subgraph that emitted this log */ + subgraphId: Scalars['String']['output']; + /** The log message text */ + text: Scalars['String']['output']; + /** The timestamp when the log was emitted, in RFC3339 format (e.g., '2024-01-15T10:30:00Z') */ + timestamp: Scalars['String']['output']; +}; + /** The type for the top-level _meta field */ export type Meta = { __typename?: '_Meta_';