diff --git a/packages/api/.env.sample b/packages/api/.env.sample index 36efe8364..ef731e7ce 100644 --- a/packages/api/.env.sample +++ b/packages/api/.env.sample @@ -6,4 +6,5 @@ DISCORD_TOKEN= DISCORD_PRIVATE_CHANNEL_ID= DISCORD_PUBLIC_CHANNEL_ID= SENTRY_AUTH_TOKEN= -IPINFO_TOKEN= \ No newline at end of file +IPINFO_TOKEN= +API_ACCESS_TOKEN= \ No newline at end of file diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts index 9b61a1648..37ed889c3 100644 --- a/packages/api/src/app.ts +++ b/packages/api/src/app.ts @@ -1,18 +1,40 @@ -import express from 'express'; +import express, { Request } from 'express'; import cors from 'cors'; import { router } from './routes'; const corsOptions: cors.CorsOptions = { origin: ( origin: string | undefined, - callback: (error: Error | null, allow?: boolean) => void + callback: (error: Error | null, allow?: boolean) => void, + request?: Request ) => { - if (process.env.NODE_ENV !== 'production') { + // Allow all requests unless in production or staging + if ( + process.env.NODE_ENV !== 'production' && + process.env.NODE_ENV !== 'staging' + ) { + callback(null, true); + return; + } + + // Check for API token in production/staging + const authHeader = request?.headers?.authorization; + const apiToken = process.env.API_ACCESS_TOKEN; + + // If API token is provided and matches, allow the request regardless of origin + if ( + apiToken && + authHeader?.startsWith('Bearer ') && + authHeader.slice(7) === apiToken + ) { callback(null, true); - } else if ( + return; + } + + // Otherwise, only allow specific domains + if ( !origin || // Allow same-origin requests /^https?:\/\/([a-zA-Z0-9-]+\.)*foil\.xyz$/.test(origin) || - /^https?:\/\/localhost(:\d+)?$/.test(origin) || // local testing /^https?:\/\/([a-zA-Z0-9-]+\.)*vercel\.app$/.test(origin) //staging sites ) { callback(null, true); @@ -21,6 +43,10 @@ const corsOptions: cors.CorsOptions = { } }, optionsSuccessStatus: 200, + // Allow the Authorization header to be exposed to the client + exposedHeaders: ['Authorization'], + // Allow the Authorization header to be sent + allowedHeaders: ['Authorization', 'Content-Type'], }; const app = express(); diff --git a/packages/app/.env.sample b/packages/app/.env.sample index 0a591711d..a03d10d9c 100644 --- a/packages/app/.env.sample +++ b/packages/app/.env.sample @@ -1,2 +1,3 @@ NEXT_PUBLIC_FOIL_API_URL=http://localhost:3001 +NEXT_PUBLIC_FOIL_API_TOKEN= IPINFO_TOKEN= \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index ed1460f71..52e1c0b60 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -46,7 +46,6 @@ "@uniswap/sdk-core": "^5.3.1", "@uniswap/v3-core": "^1.0.1", "@uniswap/v3-sdk": "^3.13.1", - "axios": "^1.7.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", diff --git a/packages/app/src/app/admin/page.tsx b/packages/app/src/app/admin/page.tsx index 712652495..16fef60ef 100644 --- a/packages/app/src/app/admin/page.tsx +++ b/packages/app/src/app/admin/page.tsx @@ -2,7 +2,6 @@ 'use client'; -import axios from 'axios'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; @@ -14,8 +13,8 @@ import { } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import AdminTable from '~/components/admin/AdminTable'; -import { API_BASE_URL } from '~/lib/constants/constants'; import type { RenderJob } from '~/lib/interfaces/interfaces'; +import { foilApi } from '~/lib/utils/util'; const Admin = () => { const [job, setJob] = useState(); @@ -33,12 +32,12 @@ const Admin = () => { if (!serviceId || !jobId) return; setLoadingAction((prev) => ({ ...prev, getStatus: true })); - const response = await axios.get( - `${API_BASE_URL}/reindexStatus?jobId=${jobId}&serviceId=${serviceId}` + const response = await foilApi.get( + `/reindexStatus?jobId=${jobId}&serviceId=${serviceId}` ); - if (response.data.success && response.data.job) { - setJob(response.data.job); + if (response.success && response.job) { + setJob(response.job); } setLoadingAction((prev) => ({ ...prev, getStatus: false })); }; diff --git a/packages/app/src/app/positions/[id]/[position]/page.tsx b/packages/app/src/app/positions/[id]/[position]/page.tsx index 720e25672..0dee83199 100644 --- a/packages/app/src/app/positions/[id]/[position]/page.tsx +++ b/packages/app/src/app/positions/[id]/[position]/page.tsx @@ -6,11 +6,10 @@ import { Loader2, ExternalLink } from 'lucide-react'; import EpochTiming from '~/components/EpochTiming'; import NumberDisplay from '~/components/numberDisplay'; import { badgeVariants } from '~/components/ui/badge'; -import { API_BASE_URL } from '~/lib/constants/constants'; import { PeriodProvider } from '~/lib/context/PeriodProvider'; import { useResources } from '~/lib/hooks/useResources'; import { cn } from '~/lib/utils'; -import { tickToPrice } from '~/lib/utils/util'; +import { tickToPrice, foilApi } from '~/lib/utils/util'; const POLLING_INTERVAL = 10000; // Refetch every 10 seconds @@ -29,13 +28,7 @@ const usePosition = (contractId: string, positionId: string) => { return useQuery({ queryKey: ['position', contractId, positionId], queryFn: async () => { - const response = await fetch( - `${API_BASE_URL}/positions/${positionId}?contractId=${contractId}` - ); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); + return foilApi.get(`/positions/${positionId}?contractId=${contractId}`); }, refetchInterval: POLLING_INTERVAL, }); diff --git a/packages/app/src/app/subscribe/page.tsx b/packages/app/src/app/subscribe/page.tsx index 1954e16cb..d15947103 100644 --- a/packages/app/src/app/subscribe/page.tsx +++ b/packages/app/src/app/subscribe/page.tsx @@ -30,7 +30,7 @@ import { import { useFoil } from '~/lib/context/FoilProvider'; import { PeriodContext, PeriodProvider } from '~/lib/context/PeriodProvider'; import { useResources } from '~/lib/hooks/useResources'; -import { convertWstEthToGwei } from '~/lib/utils/util'; +import { convertWstEthToGwei, foilApi } from '~/lib/utils/util'; const SUBSCRIPTIONS_QUERY = gql` query GetSubscriptions($owner: String!) { @@ -139,23 +139,13 @@ const useSubscriptions = (address?: string) => { } // First fetch positions - const positionsResponse = await fetch( - `${process.env.NEXT_PUBLIC_FOIL_API_URL}/graphql`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: print(SUBSCRIPTIONS_QUERY), - variables: { - owner: address, - }, - }), - } - ); + const { data: positionsData, errors } = await foilApi.post('/graphql', { + query: print(SUBSCRIPTIONS_QUERY), + variables: { + owner: address, + }, + }); - const { data: positionsData, errors } = await positionsResponse.json(); if (errors) { throw new Error(errors[0].message); } @@ -172,10 +162,9 @@ const useSubscriptions = (address?: string) => { return Promise.all( activePositions.map(async (position: any) => { const contractId = `${position.epoch.market.chainId}:${position.epoch.market.address}`; - const transactionsResponse = await fetch( - `${process.env.NEXT_PUBLIC_FOIL_API_URL}/transactions?contractId=${contractId}&positionId=${position.positionId}` + const transactions = await foilApi.get( + `/transactions?contractId=${contractId}&positionId=${position.positionId}` ); - const transactions = await transactionsResponse.json(); return { ...position, diff --git a/packages/app/src/components/ConnectWalletModal.tsx b/packages/app/src/components/ConnectWalletModal.tsx index d4aac5aab..679db977d 100644 --- a/packages/app/src/components/ConnectWalletModal.tsx +++ b/packages/app/src/components/ConnectWalletModal.tsx @@ -15,8 +15,8 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; -import { API_BASE_URL } from '~/lib/constants/constants'; import { cn } from '~/lib/utils'; +import { foilApi } from '~/lib/utils/util'; interface ConnectWalletModalProps { open: boolean; @@ -62,8 +62,8 @@ export default function ConnectWalletModal({ return; } - fetch(`${API_BASE_URL}/permit`) - .then((res) => res.json()) + foilApi + .get('/permit') .then((data) => setPermittedByApi(data.permitted)) .catch((err) => console.error('Error fetching permit status:', err)); } diff --git a/packages/app/src/components/VolumeChart/index.tsx b/packages/app/src/components/VolumeChart/index.tsx index 3bf304dfc..910c2da3e 100644 --- a/packages/app/src/components/VolumeChart/index.tsx +++ b/packages/app/src/components/VolumeChart/index.tsx @@ -22,13 +22,11 @@ import { } from 'recharts'; import NumberDisplay from '~/components/numberDisplay'; -import { API_BASE_URL } from '~/lib/constants/constants'; import type { VolumeChartData, TimeWindow } from '~/lib/interfaces/interfaces'; import { formatXAxisTick, getXTicksToShow } from '~/lib/utils/chartUtil'; -import { getDisplayTextForVolumeWindow } from '~/lib/utils/util'; +import { getDisplayTextForVolumeWindow, foilApi } from '~/lib/utils/util'; const barColor = '#58585A'; -const NETWORK_ERROR_STRING = 'Network response was not ok'; dayjs.extend(utc); @@ -117,13 +115,9 @@ const VolumeChart = ({ } = useQuery({ queryKey: ['volume', contractId, epochId, activeWindow], queryFn: async () => { - const response = await fetch( - `${API_BASE_URL}/volume?contractId=${contractId}&epochId=${epochId}&timeWindow=${activeWindow}` + return foilApi.get( + `/volume?contractId=${contractId}&epochId=${epochId}&timeWindow=${activeWindow}` ); - if (!response.ok) { - throw new Error(NETWORK_ERROR_STRING); - } - return response.json(); }, enabled: !!contractId && !!epochId && !!activeWindow, retry: 3, diff --git a/packages/app/src/components/admin/AdminTable.tsx b/packages/app/src/components/admin/AdminTable.tsx index 46f002203..398de39b4 100644 --- a/packages/app/src/components/admin/AdminTable.tsx +++ b/packages/app/src/components/admin/AdminTable.tsx @@ -5,7 +5,6 @@ import { getSortedRowModel, type SortingState, } from '@tanstack/react-table'; -import axios from 'axios'; import { Loader2, ChevronDown, ChevronUp, ArrowUpDown } from 'lucide-react'; import React, { useState, useMemo } from 'react'; import { useSignMessage } from 'wagmi'; @@ -19,12 +18,10 @@ import { TableCell, } from '@/components/ui/table'; import { useToast } from '~/hooks/use-toast'; -import { - ADMIN_AUTHENTICATE_MSG, - API_BASE_URL, -} from '~/lib/constants/constants'; +import { ADMIN_AUTHENTICATE_MSG } from '~/lib/constants/constants'; import { useFoil } from '~/lib/context/FoilProvider'; import type { Market } from '~/lib/context/FoilProvider'; +import { foilApi } from '~/lib/utils/util'; import getColumns from './columns'; import type { MissingBlocks } from './types'; @@ -66,14 +63,14 @@ const AdminTable: React.FC = () => { const fetchMissingBlocks = async (market: Market, epochId: number) => { try { - const response = await axios.get( - `${API_BASE_URL}/missing-blocks?chainId=${market.chainId}&address=${market.address}&epochId=${epochId}` + const data = await foilApi.get( + `/missing-blocks?chainId=${market.chainId}&address=${market.address}&epochId=${epochId}` ); setMissingBlocks((prev) => ({ ...prev, [`${market.address}-${epochId}`]: { - resourcePrice: response.data.missingBlockNumbers, + resourcePrice: data.missingBlockNumbers, }, })); } catch (error) { @@ -108,22 +105,19 @@ const AdminTable: React.FC = () => { message: ADMIN_AUTHENTICATE_MSG, }); - const response = await axios.post( - `${API_BASE_URL}/reindexMissingBlocks`, - { - chainId, - address: marketAddress, - epochId, - model: reindexType === 'price' ? 'ResourcePrice' : 'Event', - signature, - timestamp, - } - ); + const response = await foilApi.post('/reindexMissingBlocks', { + chainId, + address: marketAddress, + epochId, + model: reindexType === 'price' ? 'ResourcePrice' : 'Event', + signature, + timestamp, + }); - if (response.data.success) { + if (response.success) { toast({ title: 'Reindexing started', - description: response.data.message, + description: response.message, variant: 'default', }); const market = markets.find((m) => m.address === marketAddress); @@ -133,7 +127,7 @@ const AdminTable: React.FC = () => { } else { toast({ title: 'Reindexing failed', - description: response.data.error, + description: response.error, variant: 'destructive', }); } @@ -160,13 +154,13 @@ const AdminTable: React.FC = () => { const signature = await signMessageAsync({ message: ADMIN_AUTHENTICATE_MSG, }); - const response = await axios.post(`${API_BASE_URL}/updateMarketPrivacy`, { + const response = await foilApi.post('/updateMarketPrivacy', { address: market.address, chainId: market.chainId, signature, timestamp, }); - if (response.data.success) { + if (response.success) { await refetchMarkets(); } setLoadingAction((prev) => ({ ...prev, [market.address]: false })); diff --git a/packages/app/src/components/leaderboard.tsx b/packages/app/src/components/leaderboard.tsx index 79206911a..c4d12ef1d 100644 --- a/packages/app/src/components/leaderboard.tsx +++ b/packages/app/src/components/leaderboard.tsx @@ -27,7 +27,7 @@ import { TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; -import { API_BASE_URL } from '~/lib/constants/constants'; +import { foilApi } from '~/lib/utils/util'; import NumberDisplay from './numberDisplay'; @@ -67,15 +67,9 @@ const useLeaderboard = (marketId: string, epochId: string) => { queryKey: ['epochLeaderboard', marketId, epochId], queryFn: async () => { // Get leaderboard and positions - const leaderboardResponse = await fetch( - `${API_BASE_URL}/leaderboard?contractId=${marketId}` + const leaderboard = await foilApi.get( + `/leaderboard?contractId=${marketId}` ); - if (!leaderboardResponse.ok) { - throw new Error('Failed to fetch leaderboard positions'); - } - - const [leaderboard] = await Promise.all([leaderboardResponse.json()]); - return [...leaderboard]; }, }); diff --git a/packages/app/src/components/liquidityPositionsTable.tsx b/packages/app/src/components/liquidityPositionsTable.tsx index 38dd2e93e..afd0728ed 100644 --- a/packages/app/src/components/liquidityPositionsTable.tsx +++ b/packages/app/src/components/liquidityPositionsTable.tsx @@ -37,10 +37,9 @@ import { TooltipTrigger, } from '@/components/ui/tooltip'; import { toast } from '~/hooks/use-toast'; -import { API_BASE_URL } from '~/lib/constants/constants'; import type { PeriodContextType } from '~/lib/context/PeriodProvider'; import { useResources } from '~/lib/hooks/useResources'; -import { tickToPrice } from '~/lib/utils/util'; +import { tickToPrice, foilApi } from '~/lib/utils/util'; import MarketCell from './MarketCell'; import NumberDisplay from './numberDisplay'; @@ -116,26 +115,15 @@ const useLPPositions = ( return useQuery({ queryKey: ['lpPositions', walletAddress, chainId, marketAddress], queryFn: async () => { - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data, errors } = await foilApi.post('/graphql', { + query: LP_POSITIONS_QUERY, + variables: { + owner: walletAddress || undefined, + chainId: walletAddress ? undefined : Number(chainId), + marketAddress: walletAddress ? undefined : marketAddress, }, - body: JSON.stringify({ - query: LP_POSITIONS_QUERY, - variables: { - owner: walletAddress || undefined, - chainId: walletAddress ? undefined : Number(chainId), - marketAddress: walletAddress ? undefined : marketAddress, - }, - }), }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const { data, errors } = await response.json(); if (errors) { throw new Error(errors[0].message); } diff --git a/packages/app/src/components/subscribe.tsx b/packages/app/src/components/subscribe.tsx index 8434709f5..8aec19c6f 100644 --- a/packages/app/src/components/subscribe.tsx +++ b/packages/app/src/components/subscribe.tsx @@ -41,7 +41,7 @@ import { import { useToast } from '~/hooks/use-toast'; import { useFoil } from '~/lib/context/FoilProvider'; import { PeriodContext } from '~/lib/context/PeriodProvider'; -import { mainnetClient } from '~/lib/utils/util'; +import { mainnetClient, foilApi } from '~/lib/utils/util'; import NumberDisplay from './numberDisplay'; import SimpleBarChart from './SimpleBarChart'; @@ -495,30 +495,15 @@ const Subscribe: FC = ({ } } - const response = await fetch( - `${process.env.NEXT_PUBLIC_FOIL_API_URL}/estimate/estimate`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - walletAddress: resolvedAddress, - chainId: finalChainId, - marketAddress: finalMarketAddress, - epochId: finalEpoch, - }), - } - ); - - if (!response.ok) { - throw new Error('Failed to fetch estimate'); - } - - const data = await response.json(); + const estimateData = await foilApi.post('/estimate/estimate', { + walletAddress: resolvedAddress, + chainId: finalChainId, + marketAddress: finalMarketAddress, + epochId: finalEpoch, + }); // Add check for no gas usage - if (!data.totalGasUsed || data.totalGasUsed === 0) { + if (!estimateData.totalGasUsed || estimateData.totalGasUsed === 0) { toast({ title: 'Recent Data Unavailable', description: `This address hasn't used gas in the last ${formattedDuration}.`, @@ -529,11 +514,11 @@ const Subscribe: FC = ({ // Store the results if there is gas usage setEstimationResults({ - totalGasUsed: data.totalGasUsed, - ethPaid: data.ethPaid || 0, - avgGasPerTx: data.avgGasPerTx || 0, - avgGasPrice: data.avgGasPrice || 0, - chartData: data.chartData || [], + totalGasUsed: estimateData.totalGasUsed, + ethPaid: estimateData.ethPaid || 0, + avgGasPerTx: estimateData.avgGasPerTx || 0, + avgGasPrice: estimateData.avgGasPrice || 0, + chartData: estimateData.chartData || [], }); } catch (error) { toast({ diff --git a/packages/app/src/components/traderPositionsTable.tsx b/packages/app/src/components/traderPositionsTable.tsx index 583b37584..ef70c4ea0 100644 --- a/packages/app/src/components/traderPositionsTable.tsx +++ b/packages/app/src/components/traderPositionsTable.tsx @@ -37,10 +37,10 @@ import { TooltipTrigger, } from '@/components/ui/tooltip'; import { toast } from '~/hooks/use-toast'; -import { API_BASE_URL } from '~/lib/constants/constants'; import type { PeriodContextType } from '~/lib/context/PeriodProvider'; // import { PeriodContext } from '~/lib/context/PeriodProvider'; import { useResources } from '~/lib/hooks/useResources'; +import { foilApi } from '~/lib/utils/util'; // import { convertWstEthToGwei } from '~/lib/util/util'; import MarketCell from './MarketCell'; @@ -111,26 +111,15 @@ const usePositions = ( return useQuery({ queryKey: ['positions', walletAddress, chainId, marketAddress], queryFn: async () => { - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data, errors } = await foilApi.post('/graphql', { + query: POSITIONS_QUERY, + variables: { + owner: walletAddress || undefined, + chainId: walletAddress ? undefined : Number(chainId), + marketAddress: walletAddress ? undefined : marketAddress, }, - body: JSON.stringify({ - query: POSITIONS_QUERY, - variables: { - owner: walletAddress || undefined, - chainId: walletAddress ? undefined : Number(chainId), - marketAddress: walletAddress ? undefined : marketAddress, - }, - }), }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const { data, errors } = await response.json(); if (errors) { throw new Error(errors[0].message); } diff --git a/packages/app/src/components/transactionTable.tsx b/packages/app/src/components/transactionTable.tsx index 992f15fdf..0408502fa 100644 --- a/packages/app/src/components/transactionTable.tsx +++ b/packages/app/src/components/transactionTable.tsx @@ -29,10 +29,9 @@ import { TableHeader, TableRow, } from '@/components/ui/table'; -import { API_BASE_URL } from '~/lib/constants/constants'; import type { PeriodContextType } from '~/lib/context/PeriodProvider'; import { useResources } from '~/lib/hooks/useResources'; -import { convertWstEthToGwei } from '~/lib/utils/util'; +import { convertWstEthToGwei, foilApi } from '~/lib/utils/util'; import MarketCell from './MarketCell'; import NumberDisplay from './numberDisplay'; @@ -114,28 +113,17 @@ function useTransactions( return useQuery({ queryKey: ['transactions', walletAddress, chainId, marketAddress], queryFn: async () => { - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data, errors } = await foilApi.post('/graphql', { + query: TRANSACTIONS_QUERY, + variables: { + // If we have a walletAddress, query all positions for that owner + // If no walletAddress, query the specific market/chain for all owners + owner: walletAddress || undefined, + chainId: walletAddress ? undefined : Number(chainId), + marketAddress: walletAddress ? undefined : marketAddress, }, - body: JSON.stringify({ - query: TRANSACTIONS_QUERY, - variables: { - // If we have a walletAddress, query all positions for that owner - // If no walletAddress, query the specific market/chain for all owners - owner: walletAddress || undefined, - chainId: walletAddress ? undefined : Number(chainId), - marketAddress: walletAddress ? undefined : marketAddress, - }, - }), }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const { data, errors } = await response.json(); if (errors) { throw new Error(errors[0].message); } diff --git a/packages/app/src/lib/constants/constants.ts b/packages/app/src/lib/constants/constants.ts index 80225c012..2d90e9d31 100644 --- a/packages/app/src/lib/constants/constants.ts +++ b/packages/app/src/lib/constants/constants.ts @@ -10,8 +10,6 @@ export const MIN_BIG_INT_SIZE = BigInt(10); export const TICK_SPACING_DEFAULT = 200; // i.e. 1% - Hardcoded for now, should be retrieved with pool.tickSpacing() -export const API_BASE_URL = process.env.NEXT_PUBLIC_FOIL_API_URL; - export const ADMIN_AUTHENTICATE_MSG = 'Please sign this message to authenticate yourself as an admin.'; diff --git a/packages/app/src/lib/context/FoilProvider.tsx b/packages/app/src/lib/context/FoilProvider.tsx index 414ca9344..acb2b5d66 100644 --- a/packages/app/src/lib/context/FoilProvider.tsx +++ b/packages/app/src/lib/context/FoilProvider.tsx @@ -7,8 +7,7 @@ import type React from 'react'; import { createContext, useContext, useEffect, useState } from 'react'; import { useToast } from '../../hooks/use-toast'; -import { API_BASE_URL } from '../constants/constants'; -import { gweiToEther, mainnetClient } from '../utils/util'; +import { gweiToEther, mainnetClient, foilApi } from '../utils/util'; export interface Market { id: number; @@ -108,17 +107,10 @@ export const FoilProvider: React.FC<{ children: React.ReactNode }> = ({ } = useQuery({ queryKey: ['markets'], queryFn: async () => { - const response = await fetch(`${API_BASE_URL}/markets`); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - const fetchedMarkets: Market[] = await response.json(); - - console.log('fetchedMarkets', fetchedMarkets, API_BASE_URL); - + const data = await foilApi.get('/markets'); const currentTimestamp = Math.floor(Date.now() / 1000); - return fetchedMarkets.map((market) => { + return data.map((market: Market) => { const sortedEpochs = [...market.epochs].sort( (a, b) => a.startTimestamp - b.startTimestamp ); diff --git a/packages/app/src/lib/context/PeriodProvider.tsx b/packages/app/src/lib/context/PeriodProvider.tsx index d8420a47d..7081f6bf7 100644 --- a/packages/app/src/lib/context/PeriodProvider.tsx +++ b/packages/app/src/lib/context/PeriodProvider.tsx @@ -8,11 +8,12 @@ import type { Chain } from 'viem/chains'; import { useReadContract } from 'wagmi'; import useFoilDeployment from '../../components/useFoilDeployment'; -import { API_BASE_URL, BLANK_MARKET } from '../constants/constants'; +import { BLANK_MARKET } from '../constants/constants'; import erc20ABI from '../erc20abi.json'; import { useUniswapPool } from '../hooks/useUniswapPool'; import type { EpochData, MarketParams } from '../interfaces/interfaces'; import { useToast } from '~/hooks/use-toast'; +import { foilApi } from '~/lib/utils/util'; // Types and Interfaces export interface PeriodContextType { @@ -87,18 +88,9 @@ export const PeriodProvider: React.FC = ({ queryKey: ['latestPrice', `${state.chainId}:${state.address}`, state.epoch], queryFn: async () => { try { - const response = await fetch( - `${API_BASE_URL}/prices/index/latest?contractId=${state.chainId}:${state.address}&epochId=${state.epoch}` + const data = await foilApi.get( + `/prices/index/latest?contractId=${state.chainId}:${state.address}&epochId=${state.epoch}` ); - if (!response.ok) { - // Return null instead of throwing for 404s - if (response.status === 404) { - console.warn('Price data not available yet'); - return null; - } - throw new Error('Network response was not ok'); - } - const data = await response.json(); return data.price / 1e9; } catch (error) { console.error('Error fetching latest price:', error); diff --git a/packages/app/src/lib/hooks/useChart.ts b/packages/app/src/lib/hooks/useChart.ts index 2f9222a78..9efa27e3c 100644 --- a/packages/app/src/lib/hooks/useChart.ts +++ b/packages/app/src/lib/hooks/useChart.ts @@ -8,8 +8,7 @@ import { useEffect, useRef, useState, useMemo } from 'react'; import { formatUnits } from 'viem'; import { useFoil } from '../context/FoilProvider'; -import { convertWstEthToGwei } from '../utils/util'; -import { API_BASE_URL } from '~/lib/constants/constants'; +import { convertWstEthToGwei, foilApi } from '../utils/util'; import type { PriceChartData } from '~/lib/interfaces/interfaces'; import { TimeWindow, TimeInterval } from '~/lib/interfaces/interfaces'; import { timeToLocal } from '~/lib/utils'; @@ -177,7 +176,6 @@ export const useChart = ({ const now = Math.floor(Date.now() / 1000); const isBeforeStart = startTime > now; - // Modify the query functions to use selectedInterval directly const { data: marketPrices } = useQuery({ queryKey: [ 'market-prices', @@ -193,25 +191,18 @@ export const useChart = ({ const from = now - timeRange; const interval = getIntervalSeconds(selectedInterval); - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data } = await foilApi.post('/graphql', { + query: print(MARKET_CANDLES_QUERY), + variables: { + address: market?.address, + chainId: market?.chainId, + epochId: market?.epochId?.toString(), + from, + to: now, + interval, }, - body: JSON.stringify({ - query: print(MARKET_CANDLES_QUERY), - variables: { - address: market?.address, - chainId: market?.chainId, - epochId: market?.epochId?.toString(), - from, - to: now, - interval, - }, - }), }); - const { data } = await response.json(); return data.marketCandles.map((candle: any) => ({ startTimestamp: timeToLocal(candle.timestamp * 1000), endTimestamp: timeToLocal((candle.timestamp + interval) * 1000), @@ -255,25 +246,18 @@ export const useChart = ({ const from = now - timeRange; const interval = getIntervalSeconds(selectedInterval); - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data } = await foilApi.post('/graphql', { + query: print(INDEX_CANDLES_QUERY), + variables: { + address: market?.address, + chainId: market?.chainId, + epochId: market?.epochId?.toString(), + from, + to: now, + interval, }, - body: JSON.stringify({ - query: print(INDEX_CANDLES_QUERY), - variables: { - address: market?.address, - chainId: market?.chainId, - epochId: market?.epochId?.toString(), - from, - to: now, - interval, - }, - }), }); - const { data } = await response.json(); return data.indexCandles.map((candle: any) => ({ price: Number(formatUnits(BigInt(candle.close), 9)), timestamp: timeToLocal(candle.timestamp * 1000), @@ -299,23 +283,16 @@ export const useChart = ({ const from = now - 28 * 24 * 60 * 60 * 2; // Two periods ago const interval = getIntervalSeconds(selectedInterval); - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data } = await foilApi.post('/graphql', { + query: print(RESOURCE_CANDLES_QUERY), + variables: { + slug: resourceSlug, + from, + to: now, + interval, }, - body: JSON.stringify({ - query: print(RESOURCE_CANDLES_QUERY), - variables: { - slug: resourceSlug, - from, - to: now, - interval, - }, - }), }); - const { data } = await response.json(); return data.resourceCandles.map((candle: any) => ({ timestamp: timeToLocal(candle.timestamp * 1000), price: Number(formatUnits(BigInt(candle.close), 9)), @@ -343,26 +320,17 @@ export const useChart = ({ const from = now - 28 * 24 * 60 * 60 * 2; // Two periods ago const interval = getIntervalSeconds(selectedInterval); - // TODO Adjust `interval`, or `from` to limit the amount of data fetched to some reasonable amount (i.e. 2000 candles) - - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data } = await foilApi.post('/graphql', { + query: print(TRAILING_RESOURCE_CANDLES_QUERY), + variables: { + slug: resourceSlug, + from, + to: now, + interval, + trailingTime: 28 * 24 * 60 * 60, // 28 days in seconds }, - body: JSON.stringify({ - query: print(TRAILING_RESOURCE_CANDLES_QUERY), - variables: { - slug: resourceSlug, - from, - to: now, - interval, - trailingTime: 28 * 24 * 60 * 60, // 28 days in seconds - }, - }), }); - const { data } = await response.json(); return data.resourceTrailingAverageCandles.map((candle: any) => ({ timestamp: timeToLocal(candle.timestamp * 1000), price: Number(formatUnits(BigInt(candle.close), 9)), diff --git a/packages/app/src/lib/hooks/useResources.ts b/packages/app/src/lib/hooks/useResources.ts index e4cd868b7..6e04c93e0 100644 --- a/packages/app/src/lib/hooks/useResources.ts +++ b/packages/app/src/lib/hooks/useResources.ts @@ -2,7 +2,7 @@ import { gql } from '@apollo/client'; import { useQuery } from '@tanstack/react-query'; import { print } from 'graphql'; -import { API_BASE_URL } from '~/lib/constants/constants'; +import { foilApi } from '~/lib/utils/util'; export interface Epoch { id: number; @@ -73,8 +73,7 @@ export const useResources = () => { return useQuery({ queryKey: ['resources'], queryFn: async () => { - const response = await fetch(`${API_BASE_URL}/resources`); - const data = await response.json(); + const data = await foilApi.get('/resources'); return data.map((resource: Omit) => ({ ...resource, iconPath: mapResourceToIconPath(resource.name), @@ -87,27 +86,16 @@ export const useLatestResourcePrice = (slug: string) => { return useQuery({ queryKey: ['resourcePrice', slug], queryFn: async () => { - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data } = await foilApi.post('/graphql', { + query: print(LATEST_RESOURCE_PRICE_QUERY), + variables: { + slug, + from: Math.floor(Date.now() / 1000) - 300, // Last 5 minutes + to: Math.floor(Date.now() / 1000), + interval: 60, // 1 minute intervals }, - body: JSON.stringify({ - query: print(LATEST_RESOURCE_PRICE_QUERY), - variables: { - slug, - from: Math.floor(Date.now() / 1000) - 300, // Last 5 minutes - to: Math.floor(Date.now() / 1000), - interval: 60, // 1 minute intervals - }, - }), }); - if (!response.ok) { - throw new Error('Failed to fetch latest price'); - } - - const { data } = await response.json(); const candles = data.resourceCandles; if (!candles || candles.length === 0) { throw new Error('No price data found'); @@ -150,26 +138,15 @@ export const useLatestIndexPrice = (market: { return null; } - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const { data } = await foilApi.post('/graphql', { + query: print(LATEST_INDEX_PRICE_QUERY), + variables: { + address: market.address, + chainId: market.chainId, + epochId: market.epochId.toString(), }, - body: JSON.stringify({ - query: print(LATEST_INDEX_PRICE_QUERY), - variables: { - address: market.address, - chainId: market.chainId, - epochId: market.epochId.toString(), - }, - }), }); - if (!response.ok) { - throw new Error('Failed to fetch latest index price'); - } - - const { data } = await response.json(); const candles = data.indexCandles; if (!candles || candles.length === 0) { throw new Error('No index price data found'); diff --git a/packages/app/src/lib/hooks/useSettlementPrice.ts b/packages/app/src/lib/hooks/useSettlementPrice.ts index 8d7dc19a2..68ad86d1c 100644 --- a/packages/app/src/lib/hooks/useSettlementPrice.ts +++ b/packages/app/src/lib/hooks/useSettlementPrice.ts @@ -3,9 +3,8 @@ import { useQuery } from '@tanstack/react-query'; import { print } from 'graphql'; import { formatEther } from 'viem'; -import { API_BASE_URL } from '~/lib/constants/constants'; import type { Market, Epoch } from '~/lib/types'; -import { gweiToEther, convertToSqrtPriceX96 } from '~/lib/utils/util'; +import { gweiToEther, convertToSqrtPriceX96, foilApi } from '~/lib/utils/util'; const INDEX_PRICE_QUERY = gql` query GetIndexPrice( @@ -30,13 +29,9 @@ export function useSettlementPrice(market: Market, epoch: Epoch) { const { data: stEthPerToken = 0, isLoading: isStEthLoading } = useQuery({ queryKey: ['stEthPerToken', market.chainId, epoch.endTimestamp], queryFn: async () => { - const response = await fetch( - `${API_BASE_URL}/getStEthPerTokenAtTimestamp?chainId=${market.chainId}&endTime=${epoch.endTimestamp}` + const data = await foilApi.get( + `/getStEthPerTokenAtTimestamp?chainId=${market.chainId}&endTime=${epoch.endTimestamp}` ); - if (!response.ok) { - throw new Error('Failed to fetch stEthPerToken'); - } - const data = await response.json(); return Number(formatEther(BigInt(data.stEthPerToken))); }, enabled: !!market.chainId && !!epoch.endTimestamp, @@ -50,29 +45,17 @@ export function useSettlementPrice(market: Market, epoch: Epoch) { epoch.endTimestamp, ], queryFn: async () => { - const response = await fetch(`${API_BASE_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', + const data = await foilApi.post('/graphql', { + query: print(INDEX_PRICE_QUERY), + variables: { + address: market.address, + chainId: market.chainId, + epochId: epoch.epochId.toString(), + timestamp: epoch.endTimestamp, }, - body: JSON.stringify({ - query: print(INDEX_PRICE_QUERY), - variables: { - address: market.address, - chainId: market.chainId, - epochId: epoch.epochId.toString(), - timestamp: epoch.endTimestamp, - }, - }), }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const { data } = await response.json(); const price = data.indexPriceAtTime; - if (!price) { throw new Error('No index price data found'); } diff --git a/packages/app/src/lib/utils/util.ts b/packages/app/src/lib/utils/util.ts index 860cc08fd..0ba16f418 100644 --- a/packages/app/src/lib/utils/util.ts +++ b/packages/app/src/lib/utils/util.ts @@ -7,6 +7,49 @@ import { mainnet } from 'viem/chains'; import type { FoilPosition } from '../interfaces/interfaces'; import { TimeWindow } from '../interfaces/interfaces'; +export const foilApi = { + baseUrl: process.env.NEXT_PUBLIC_FOIL_API_URL || '', + token: process.env.NEXT_PUBLIC_FOIL_API_TOKEN, + + getHeaders() { + const headers: HeadersInit = { + 'Content-Type': 'application/json', + }; + + if (this.token) { + headers.Authorization = `Bearer ${this.token}`; + } + + return headers; + }, + + async post(path: string, body: any) { + const response = await fetch(`${this.baseUrl}${path}`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error('Request failed'); + } + + return response.json(); + }, + + async get(path: string) { + const response = await fetch(`${this.baseUrl}${path}`, { + headers: this.getHeaders(), + }); + + if (!response.ok) { + throw new Error('Request failed'); + } + + return response.json(); + }, +}; + // Mainnet client for ENS resolution and stEthPerToken query export const mainnetClient = createPublicClient({ chain: mainnet, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 855c8f857..6d3ae370f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -177,9 +177,6 @@ importers: '@uniswap/v3-sdk': specifier: ^3.13.1 version: 3.14.0(hardhat@2.22.10(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) - axios: - specifier: ^1.7.7 - version: 1.7.7(debug@4.4.0) class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -324,7 +321,7 @@ importers: version: 19.0.0(react@19.0.0) vocs: specifier: latest - version: 1.0.0-alpha.62(@types/node@22.9.1)(@types/react-dom@18.3.0)(@types/react@18.3.7)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.21.3)(terser@5.37.0)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(typescript@5.6.2) + version: 1.0.0-alpha.62(@types/node@22.9.1)(@types/react-dom@18.3.0)(@types/react@18.3.7)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.21.3)(terser@5.38.2)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(typescript@5.6.2) devDependencies: '@types/react': specifier: ^18.3.5 @@ -5260,9 +5257,6 @@ packages: '@types/prop-types@15.7.13': resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} - '@types/prop-types@15.7.14': - resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - '@types/qs@6.9.16': resolution: {integrity: sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==} @@ -5275,9 +5269,6 @@ packages: '@types/react@18.3.12': resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} - '@types/react@18.3.18': - resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==} - '@types/react@18.3.7': resolution: {integrity: sha512-KUnDCJF5+AiZd8owLIeVHqmW9yM4sqmDVf2JRJiBMFkGvkoZ4/WyV2lL4zVsoinmRS/W3FeEdZLEWFRofnT2FQ==} @@ -13861,11 +13852,6 @@ packages: engines: {node: '>=10'} hasBin: true - terser@5.37.0: - resolution: {integrity: sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==} - engines: {node: '>=10'} - hasBin: true - terser@5.38.2: resolution: {integrity: sha512-w8CXxxbFA5zfNsR/i8HZq5bvn18AK0O9jj7hyo1YqkovLxEFa0uP0LCVGZRqiRaKRFxXhELBp8SteeAjEnfeJg==} engines: {node: '>=10'} @@ -18366,14 +18352,14 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.1 + '@types/node': 20.17.17 jest-mock: 29.7.0 '@jest/fake-timers@29.7.0': dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.9.1 + '@types/node': 20.17.17 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -18386,7 +18372,7 @@ snapshots: dependencies: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.9.1 + '@types/node': 20.17.17 '@types/yargs': 15.0.19 chalk: 4.1.2 @@ -18395,7 +18381,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.9.1 + '@types/node': 20.17.17 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -18453,7 +18439,7 @@ snapshots: dependencies: '@ledgerhq/cryptoassets': 5.53.0 '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.26.0 + '@ledgerhq/hw-transport': 5.51.1 bignumber.js: 9.1.2 rlp: 2.2.7 @@ -18470,7 +18456,7 @@ snapshots: dependencies: '@ledgerhq/devices': 5.51.1 '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.26.0 + '@ledgerhq/hw-transport': 5.51.1 '@ledgerhq/hw-transport-node-hid-noevents': 5.51.1 '@ledgerhq/logs': 5.50.0 lodash: 4.17.21 @@ -18481,7 +18467,7 @@ snapshots: '@ledgerhq/hw-transport-u2f@5.26.0': dependencies: '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.26.0 + '@ledgerhq/hw-transport': 5.51.1 '@ledgerhq/logs': 5.50.0 u2f-api: 0.2.7 @@ -18496,7 +18482,6 @@ snapshots: '@ledgerhq/devices': 5.51.1 '@ledgerhq/errors': 5.50.0 events: 3.3.0 - optional: true '@ledgerhq/logs@5.50.0': {} @@ -21546,7 +21531,7 @@ snapshots: cbor: 5.2.0 debug: 4.4.0(supports-color@8.1.1) lodash: 4.17.21 - semver: 7.6.3 + semver: 7.7.1 utf8: 3.0.0 web3-utils: 1.10.0 transitivePeerDependencies: @@ -21762,7 +21747,7 @@ snapshots: '@types/bn.js@4.11.6': dependencies: - '@types/node': 20.16.5 + '@types/node': 20.17.17 '@types/bn.js@5.1.1': dependencies: @@ -21952,7 +21937,7 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.9.1 + '@types/node': 20.17.17 '@types/node@10.17.60': {} @@ -22003,16 +21988,13 @@ snapshots: '@types/prop-types@15.7.13': {} - '@types/prop-types@15.7.14': - optional: true - '@types/qs@6.9.16': {} '@types/range-parser@1.2.7': {} '@types/react-dom@18.3.0': dependencies: - '@types/react': 18.3.18 + '@types/react': 18.3.12 optional: true '@types/react@18.3.12': @@ -22020,12 +22002,6 @@ snapshots: '@types/prop-types': 15.7.13 csstype: 3.1.3 - '@types/react@18.3.18': - dependencies: - '@types/prop-types': 15.7.14 - csstype: 3.1.3 - optional: true - '@types/react@18.3.7': dependencies: '@types/prop-types': 15.7.13 @@ -22582,7 +22558,7 @@ snapshots: '@usecannon/web-solc': 0.5.1 acorn: 8.14.0 axios: 1.7.7(debug@4.4.0) - axios-retry: 4.5.0(axios@1.7.7) + axios-retry: 4.5.0(axios@1.7.7(debug@4.4.0)) buffer: 6.0.3 chalk: 4.1.2 debug: 4.4.0(supports-color@8.1.1) @@ -22688,7 +22664,7 @@ snapshots: dependencies: '@vanilla-extract/private': 1.0.6 - '@vanilla-extract/integration@6.5.0(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.37.0)': + '@vanilla-extract/integration@6.5.0(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.38.2)': dependencies: '@babel/core': 7.25.2 '@babel/plugin-syntax-typescript': 7.25.4(@babel/core@7.25.2) @@ -22701,8 +22677,8 @@ snapshots: lodash: 4.17.21 mlly: 1.7.1 outdent: 0.8.0 - vite: 5.4.14(@types/node@22.9.1)(terser@5.37.0) - vite-node: 1.6.0(@types/node@22.9.1)(terser@5.37.0) + vite: 5.4.14(@types/node@22.9.1)(terser@5.38.2) + vite-node: 1.6.0(@types/node@22.9.1)(terser@5.38.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -22721,13 +22697,13 @@ snapshots: dependencies: '@vanilla-extract/css': 1.15.5(babel-plugin-macros@3.1.0) - '@vanilla-extract/vite-plugin@3.9.5(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.37.0)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(vite@5.4.14(@types/node@22.9.1)(terser@5.37.0))': + '@vanilla-extract/vite-plugin@3.9.5(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.38.2)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(vite@5.4.14(@types/node@22.9.1)(terser@5.38.2))': dependencies: - '@vanilla-extract/integration': 6.5.0(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.37.0) + '@vanilla-extract/integration': 6.5.0(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.38.2) outdent: 0.8.0 postcss: 8.4.47 postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2)) - vite: 5.4.14(@types/node@22.9.1)(terser@5.37.0) + vite: 5.4.14(@types/node@22.9.1)(terser@5.38.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -22746,14 +22722,14 @@ snapshots: next: 15.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@vitejs/plugin-react@4.3.1(vite@5.4.14(@types/node@22.9.1)(terser@5.37.0))': + '@vitejs/plugin-react@4.3.1(vite@5.4.14(@types/node@22.9.1)(terser@5.38.2))': dependencies: '@babel/core': 7.25.2 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.14(@types/node@22.9.1)(terser@5.37.0) + vite: 5.4.14(@types/node@22.9.1)(terser@5.38.2) transitivePeerDependencies: - supports-color @@ -23635,7 +23611,7 @@ snapshots: axe-core@4.10.0: {} - axios-retry@4.5.0(axios@1.7.7): + axios-retry@4.5.0(axios@1.7.7(debug@4.4.0)): dependencies: axios: 1.7.7(debug@4.4.0) is-retry-allowed: 2.2.0 @@ -24231,7 +24207,7 @@ snapshots: chrome-launcher@0.15.2: dependencies: - '@types/node': 22.9.1 + '@types/node': 20.17.17 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -24242,7 +24218,7 @@ snapshots: chromium-edge-launcher@0.2.0: dependencies: - '@types/node': 22.9.1 + '@types/node': 20.17.17 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -28964,7 +28940,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.9.1 + '@types/node': 20.17.17 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -28985,13 +28961,13 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.9.1 + '@types/node': 20.17.17 jest-util: 29.7.0 jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.9.1 + '@types/node': 20.17.17 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -29020,7 +28996,7 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.9.1 + '@types/node': 20.17.17 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -31939,7 +31915,7 @@ snapshots: jsdoc: 4.0.3 minimist: 1.2.8 protobufjs: 7.2.4 - semver: 7.6.3 + semver: 7.7.1 tmp: 0.2.3 uglify-js: 3.19.3 @@ -33329,7 +33305,7 @@ snapshots: pify: 4.0.1 recursive-readdir: 2.2.3 sc-istanbul: 0.4.6 - semver: 7.6.3 + semver: 7.7.1 shelljs: 0.8.5 web3-utils: 1.10.4 transitivePeerDependencies: @@ -33951,14 +33927,6 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - terser@5.37.0: - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.14.0 - commander: 2.20.3 - source-map-support: 0.5.21 - optional: true - terser@5.38.2: dependencies: '@jridgewell/source-map': 0.3.6 @@ -34894,13 +34862,13 @@ snapshots: - utf-8-validate - zod - vite-node@1.6.0(@types/node@22.9.1)(terser@5.37.0): + vite-node@1.6.0(@types/node@22.9.1)(terser@5.38.2): dependencies: cac: 6.7.14 debug: 4.4.0(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.1.1 - vite: 5.4.14(@types/node@22.9.1)(terser@5.37.0) + vite: 5.4.14(@types/node@22.9.1)(terser@5.38.2) transitivePeerDependencies: - '@types/node' - less @@ -34912,7 +34880,7 @@ snapshots: - supports-color - terser - vite@5.4.14(@types/node@22.9.1)(terser@5.37.0): + vite@5.4.14(@types/node@22.9.1)(terser@5.38.2): dependencies: esbuild: 0.21.5 postcss: 8.4.47 @@ -34920,11 +34888,11 @@ snapshots: optionalDependencies: '@types/node': 22.9.1 fsevents: 2.3.3 - terser: 5.37.0 + terser: 5.38.2 vlq@1.0.1: {} - vocs@1.0.0-alpha.62(@types/node@22.9.1)(@types/react-dom@18.3.0)(@types/react@18.3.7)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.21.3)(terser@5.37.0)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(typescript@5.6.2): + vocs@1.0.0-alpha.62(@types/node@22.9.1)(@types/react-dom@18.3.0)(@types/react@18.3.7)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.21.3)(terser@5.38.2)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(typescript@5.6.2): dependencies: '@floating-ui/react': 0.26.24(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@hono/node-server': 1.13.8(hono@3.12.12) @@ -34944,8 +34912,8 @@ snapshots: '@shikijs/twoslash': 1.22.0(typescript@5.6.2) '@vanilla-extract/css': 1.17.1(babel-plugin-macros@3.1.0) '@vanilla-extract/dynamic': 2.1.2 - '@vanilla-extract/vite-plugin': 3.9.5(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.37.0)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(vite@5.4.14(@types/node@22.9.1)(terser@5.37.0)) - '@vitejs/plugin-react': 4.3.1(vite@5.4.14(@types/node@22.9.1)(terser@5.37.0)) + '@vanilla-extract/vite-plugin': 3.9.5(@types/node@22.9.1)(babel-plugin-macros@3.1.0)(terser@5.38.2)(ts-node@10.9.2(@types/node@22.9.1)(typescript@5.6.2))(vite@5.4.14(@types/node@22.9.1)(terser@5.38.2)) + '@vitejs/plugin-react': 4.3.1(vite@5.4.14(@types/node@22.9.1)(terser@5.38.2)) autoprefixer: 10.4.20(postcss@8.4.47) cac: 6.7.14 chroma-js: 2.6.0 @@ -34988,7 +34956,7 @@ snapshots: ua-parser-js: 1.0.39 unified: 11.0.5 unist-util-visit: 5.0.0 - vite: 5.4.14(@types/node@22.9.1)(terser@5.37.0) + vite: 5.4.14(@types/node@22.9.1)(terser@5.38.2) transitivePeerDependencies: - '@types/node' - '@types/react'