import { useEffect, useRef, useState } from "react"; import TooltipInfo from "../TooltipInfo"; import { formatEther } from "viem"; import { useScaffoldReadContract } from "~~/hooks/scaffold-eth"; const getHighlightColor = (oldPrice: bigint | undefined, newPrice: bigint | undefined): string => { if (oldPrice === undefined || newPrice === undefined) return ""; const change = Math.abs(parseFloat(formatEther(newPrice)) - parseFloat(formatEther(oldPrice))); if (change < 50) return "bg-success"; if (change < 100) return "bg-warning"; return "bg-error"; }; interface PriceWidgetProps { contractName: "StakingOracle" | "WhitelistOracle"; } export const PriceWidget = ({ contractName }: PriceWidgetProps) => { const [highlight, setHighlight] = useState(false); const [highlightColor, setHighlightColor] = useState(""); const prevPrice = useRef(undefined); const prevBucket = useRef(null); const [showBucketLoading, setShowBucketLoading] = useState(false); // Poll getCurrentBucketNumber to detect bucket changes const { data: contractBucketNum } = useScaffoldReadContract({ contractName: "StakingOracle", functionName: "getCurrentBucketNumber", watch: true, }) as { data: bigint | undefined }; useEffect(() => { if (contractBucketNum !== undefined) { // Check if bucket changed if (prevBucket.current !== null && contractBucketNum !== prevBucket.current) { setShowBucketLoading(true); setTimeout(() => setShowBucketLoading(false), 2000); // Show loading for 2 seconds after bucket change } prevBucket.current = contractBucketNum; } }, [contractBucketNum]); const isStaking = contractName === "StakingOracle"; // For WhitelistOracle, check if there are any active oracles (reported within staleness window) const { data: activeOracles } = useScaffoldReadContract({ contractName: "WhitelistOracle", functionName: "getActiveOracleNodes", watch: true, }) as { data: readonly `0x${string}`[] | undefined }; const { data: currentPrice, isError } = useScaffoldReadContract({ contractName, functionName: isStaking ? ("getLatestPrice" as any) : ("getPrice" as any), watch: true, }) as { data: bigint | undefined; isError: boolean; isLoading: boolean }; // For WhitelistOracle: no active oracles means no fresh price // For StakingOracle: rely on error state const noActiveOracles = !isStaking && activeOracles !== undefined && activeOracles.length === 0; const hasValidPrice = !isError && !noActiveOracles && currentPrice !== undefined && currentPrice !== 0n; useEffect(() => { if (currentPrice !== undefined && prevPrice.current !== undefined && currentPrice !== prevPrice.current) { setHighlightColor(getHighlightColor(prevPrice.current, currentPrice)); setHighlight(true); setTimeout(() => { setHighlight(false); setHighlightColor(""); }, 650); } prevPrice.current = currentPrice; }, [currentPrice]); return (

Current Price

{showBucketLoading ? (
) : hasValidPrice ? ( {`$${parseFloat(formatEther(currentPrice)).toFixed(2)}`} ) : (
No fresh price
)}
); };