diff --git a/.env.development b/.env.development index f0a0b4f66f..cfb8e24b2a 100644 --- a/.env.development +++ b/.env.development @@ -9,5 +9,3 @@ NEXT_PUBLIC_ENABLE_STAKING=true NEXT_PUBLIC_ENABLE_GOVERNANCE=true NEXT_PUBLIC_API_BASEURL=https://aave-api-v2.aave.com NEXT_PUBLIC_SUBGRAPH_API_KEY= -FAMILY_API_KEY= -FAMILY_API_URL= diff --git a/.env.example b/.env.example index dff3a73a25..5f0dcb804a 100644 --- a/.env.example +++ b/.env.example @@ -49,8 +49,6 @@ LINEA_RPC_API_KEY= SONIC_RPC_API_KEY= CELO_RPC_API_KEY= MONAD_RPC_API_KEY= -FAMILY_API_KEY= -FAMILY_API_URL= COINGECKO_API_KEY= # TokenLogic REST API (api.tokenlogic.xyz), used by /api/protocol-totals for whole-protocol figures TL_API_KEY= diff --git a/pages/api/prices-proxy.md b/pages/api/prices-proxy.md deleted file mode 100644 index 64c14b8dc9..0000000000 --- a/pages/api/prices-proxy.md +++ /dev/null @@ -1,80 +0,0 @@ -# Family Prices Proxy API - -This API route provides a server-side proxy for the Family API prices endpoint, allowing client-side code to fetch token prices without exposing the API key. - -## Environment Variables - -The following environment variable must be set: - -``` -FAMILY_API_KEY=your_family_api_key_here -``` - -## Endpoint - -`POST /api/family-prices-proxy` - -## Request Format - -```json -{ - "tokenIds": ["1:0x0000000000000000000000000000000000000000", "137:0x2791bca1f2de4661ed88a30c99a7a9449aa84174"] -} -``` - -Where `tokenIds` is an array of strings in the format `{chainId}:{tokenAddress}`. - -## Response Format - -The proxy returns the same response format as the Family API: - -```json -{ - "prices": { - "1:0x0000000000000000000000000000000000000000": { - "usd": 2807.04, - "usd_24h_change": null, - "usd_24h_vol": null - }, - "137:0x2791bca1f2de4661ed88a30c99a7a9449aa84174": { - "usd": 1.00, - "usd_24h_change": -0.01, - "usd_24h_vol": 1000000 - } - } -} -``` - -## CORS Policy - -The proxy implements the same CORS policy as other API routes: -- Allows requests from `https://app.aave.com` and `https://aave.com` -- Allows requests from Vercel deployment URLs matching `*.avaraxyz.vercel.app` -- Can be configured to allow all origins by setting `CORS_DOMAINS_ALLOWED=true` - -## Usage - -The proxy is used by the `FamilyPricesService` class: - -```typescript -const familyService = new FamilyPricesService(); - -// Get single token price -const price = await familyService.getTokenUsdPrice(1, '0x...'); - -// Get multiple token prices -const prices = await familyService.getMultipleTokenUsdPrices([ - { chainId: 1, tokenAddress: '0x...' }, - { chainId: 137, tokenAddress: '0x...' } -]); -``` - -## Error Handling - -The proxy handles various error scenarios: -- Missing or invalid API key -- Invalid request format -- Family API errors -- Network errors - -All errors are logged server-side and return appropriate HTTP status codes with error messages. \ No newline at end of file diff --git a/pages/api/prices-proxy.ts b/pages/api/prices-proxy.ts deleted file mode 100644 index 3fb4d4ffc1..0000000000 --- a/pages/api/prices-proxy.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; - -const FAMILY_API_URL = process.env.FAMILY_API_URL; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const allowedOrigins = ['https://app.aave.com', 'https://aave.com']; - const origin = req.headers.origin; - - const isOriginAllowed = (origin: string | undefined): boolean => { - if (!origin) return false; - - if (allowedOrigins.includes(origin)) return true; - - // Match any subdomain ending with avaraxyz.vercel.app for deployment urls - const allowedPatterns = [/^https:\/\/.*avaraxyz\.vercel\.app$/]; - - return allowedPatterns.some((pattern) => pattern.test(origin)); - }; - - if (process.env.CORS_DOMAINS_ALLOWED === 'true') { - res.setHeader('Access-Control-Allow-Origin', '*'); - } else if (origin && isOriginAllowed(origin)) { - res.setHeader('Access-Control-Allow-Origin', origin); - } - - res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - - if (req.method === 'OPTIONS') { - return res.status(200).end(); - } - - if (req.method !== 'POST') { - return res.status(405).json({ error: 'Method not allowed' }); - } - - try { - const { tokenIds } = req.body; - - if (!tokenIds || !Array.isArray(tokenIds)) { - return res.status(400).json({ error: 'tokenIds array is required' }); - } - - const familyApiKey = process.env.FAMILY_API_KEY; - if (!familyApiKey || !FAMILY_API_URL) { - console.error('FAMILY_API_KEY or FAMILY_API_URL environment variable is not set'); - return res.status(500).json({ error: 'Internal server error' }); - } - - const requestBody = { - tokenIds, - }; - - const response = await fetch(FAMILY_API_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': familyApiKey, - Origin: origin || 'https://app.aave.com', - Referer: 'https://app.aave.com/', - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - console.error('Family API error:', response.status, response.statusText); - return res.status(response.status).json({ - error: 'Failed to fetch prices from Family API', - details: response.statusText, - }); - } - - const data = await response.json(); - return res.status(200).json(data); - } catch (error) { - console.error('Family prices proxy error:', error); - return res.status(500).json({ error: 'Internal server error', details: String(error) }); - } -} diff --git a/src/components/transactions/Swap/helpers/cow/rates.helpers.ts b/src/components/transactions/Swap/helpers/cow/rates.helpers.ts index fa112e35d0..379c7358f8 100644 --- a/src/components/transactions/Swap/helpers/cow/rates.helpers.ts +++ b/src/components/transactions/Swap/helpers/cow/rates.helpers.ts @@ -1,12 +1,9 @@ -import { ChainId } from '@aave/contract-helpers'; import { OrderKind, QuoteAndPost, WRAPPED_NATIVE_CURRENCIES } from '@cowprotocol/cow-sdk'; import { BigNumber } from 'bignumber.js'; import { getEthersProvider } from 'src/libs/web3-data-provider/adapters/EthersAdapter'; import { CoWProtocolPricesService } from 'src/services/CoWProtocolPricesService'; -import { FamilyPricesService } from 'src/services/FamilyPricesService'; import { getErrorTextFromError, TxAction } from 'src/ui-config/errorMapping'; import { wagmiConfig } from 'src/ui-config/wagmiConfig'; -import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig'; import { COW_PARTNER_FEE, isChainIdSupportedByCoWProtocol } from '../../constants/cow.constants'; import { isNativeToken } from '../../helpers/cow'; @@ -15,33 +12,6 @@ import { getAppDataForQuote } from './adapters.helpers'; import { getCowTradingSdkByChainIdAndAppCode } from './env.helpers'; import { getSlippageSuggestion } from './orders.helpers'; -export const getTokenUsdPrice = async ( - chainId: number, - tokenAddress: string, - isTokenCustom: boolean, - isMainnet: boolean -) => { - const cowProtocolPricesService = new CoWProtocolPricesService(); - const familyPricesService = new FamilyPricesService(); - - try { - let price; - - if (!isTokenCustom && isMainnet) { - price = await familyPricesService.getTokenUsdPrice(chainId, tokenAddress); - } - - if (price) { - return price; - } - - return await cowProtocolPricesService.getTokenUsdPrice(chainId, tokenAddress); - } catch (familyError) { - console.error(familyError); - return undefined; - } -}; - export async function getCowProtocolSellRates({ chainId, amount, @@ -53,8 +23,6 @@ export async function getCowProtocolSellRates({ swapType, inputSymbol, outputSymbol, - isInputTokenCustom, - isOutputTokenCustom, appCode, market, setError, @@ -62,6 +30,7 @@ export async function getCowProtocolSellRates({ invertedQuoteRoute = false, }: ProviderRatesParams): Promise { const tradingSdk = await getCowTradingSdkByChainIdAndAppCode(chainId, appCode); + const cowProtocolPricesService = new CoWProtocolPricesService(); let orderBookQuote: QuoteAndPost | undefined; let srcTokenPriceUsd: string | undefined; let destTokenPriceUsd: string | undefined; @@ -83,9 +52,6 @@ export async function getCowProtocolSellRates({ const provider = await getEthersProvider(wagmiConfig, { chainId }); const signer = provider?.getSigner(); - const isMainnet = - !getNetworkConfig(chainId as unknown as ChainId).isTestnet && - !getNetworkConfig(chainId as unknown as ChainId).isFork; if (!inputSymbol || !outputSymbol) { throw new Error('No input or output symbol provided'); @@ -130,8 +96,8 @@ export async function getCowProtocolSellRates({ 'Failed to get quote from CoW Protocol'; throw new Error(errorMessage); }), - getTokenUsdPrice(chainId, srcTokenWrapped, isInputTokenCustom ?? false, isMainnet), - getTokenUsdPrice(chainId, destTokenWrapped, isOutputTokenCustom ?? false, isMainnet), + cowProtocolPricesService.getTokenUsdPrice(chainId, srcTokenWrapped), + cowProtocolPricesService.getTokenUsdPrice(chainId, destTokenWrapped), ]); if (!srcTokenPriceUsd || !destTokenPriceUsd) { diff --git a/src/components/transactions/Swap/hooks/useSwapQuote.ts b/src/components/transactions/Swap/hooks/useSwapQuote.ts index 414ac20386..1629d91f52 100644 --- a/src/components/transactions/Swap/hooks/useSwapQuote.ts +++ b/src/components/transactions/Swap/hooks/useSwapQuote.ts @@ -19,7 +19,6 @@ import { SwapQuoteType as SwapQuoteType, SwapState, SwapType, - TokenType, } from '../types'; interface TokenSelectionParams { @@ -29,8 +28,6 @@ interface TokenSelectionParams { destDecimals: number; inputSymbol: string; outputSymbol: string; - isInputTokenCustom: boolean; - isOutputTokenCustom: boolean; side: 'buy' | 'sell'; } @@ -68,12 +65,6 @@ const getTokenSelectionForQuote = ( const outputSymbol = invertedQuoteRoute ? state.sourceToken.symbol : state.destinationToken.symbol; - const isInputTokenCustom = invertedQuoteRoute - ? state.destinationToken.tokenType === TokenType.USER_CUSTOM - : state.sourceToken.tokenType === TokenType.USER_CUSTOM; - const isOutputTokenCustom = invertedQuoteRoute - ? state.sourceToken.tokenType === TokenType.USER_CUSTOM - : state.destinationToken.tokenType === TokenType.USER_CUSTOM; const side = invertedQuoteRoute ? (state.side === 'buy' ? 'sell' : 'buy') : state.side; return { @@ -83,8 +74,6 @@ const getTokenSelectionForQuote = ( destDecimals, inputSymbol, outputSymbol, - isInputTokenCustom, - isOutputTokenCustom, side, }; }; @@ -309,27 +298,18 @@ const useMultiProviderSwapQuoteQuery = ({ const appCode = APP_CODE_PER_SWAP_TYPE[params.swapType]; - const { - srcToken, - destToken, - srcDecimals, - destDecimals, - inputSymbol, - outputSymbol, - isInputTokenCustom, - isOutputTokenCustom, - side, - } = useMemo( - () => getTokenSelectionForQuote(requiresQuoteInverted, provider, state), - [ - provider, - state.sourceToken, - state.destinationToken, - state.side, - requiresQuoteInverted, - state.useFlashloan, - ] - ); + const { srcToken, destToken, srcDecimals, destDecimals, inputSymbol, outputSymbol, side } = + useMemo( + () => getTokenSelectionForQuote(requiresQuoteInverted, provider, state), + [ + provider, + state.sourceToken, + state.destinationToken, + state.side, + requiresQuoteInverted, + state.useFlashloan, + ] + ); return useQuery({ queryFn: async () => { @@ -378,8 +358,6 @@ const useMultiProviderSwapQuoteQuery = ({ destDecimals, inputSymbol, outputSymbol, - isInputTokenCustom, - isOutputTokenCustom, appCode, market: currentMarket, setError, diff --git a/src/components/transactions/Swap/types/quote.types.ts b/src/components/transactions/Swap/types/quote.types.ts index fb62eb37d6..728f59add1 100644 --- a/src/components/transactions/Swap/types/quote.types.ts +++ b/src/components/transactions/Swap/types/quote.types.ts @@ -27,8 +27,6 @@ export type ProviderRatesParams = { inputSymbol?: string; outputSymbol?: string; - isInputTokenCustom?: boolean; - isOutputTokenCustom?: boolean; appCode: string; market: CustomMarket; diff --git a/src/services/FamilyPricesService.ts b/src/services/FamilyPricesService.ts deleted file mode 100644 index 138f362f79..0000000000 --- a/src/services/FamilyPricesService.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { API_ETH_MOCK_ADDRESS } from '@aave/contract-helpers'; -import { zeroAddress } from 'viem'; - -type FamilyPricesResponse = { - prices: { - [tokenId: string]: { - usd: number; - }; - }; -}; -export class FamilyPricesService { - private proxyUrl = '/api/prices-proxy'; - - /** - * Fetches the USD price of a token from the Family API via proxy. - * @param chainId - The ID of the blockchain network. - * @param tokenAddress - The address of the token. - * @returns The USD price of the token. - */ - async getTokenUsdPrice(chainId: number, tokenAddress: string): Promise { - try { - const apiExpectedAddress = - tokenAddress.toLowerCase() === API_ETH_MOCK_ADDRESS.toLowerCase() - ? zeroAddress - : tokenAddress; - - const tokenId = `${chainId}:${apiExpectedAddress}`.toLowerCase(); - - const response = await fetch(this.proxyUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - tokenIds: [tokenId], - }), - }); - - if (!response.ok) { - throw new Error(`Failed to fetch USD price: ${response.statusText}`); - } - - const data = (await response.json()) as FamilyPricesResponse; - - if (data?.prices && data.prices[tokenId]?.usd !== undefined) { - return data.prices[tokenId].usd.toString(); - } - - return undefined; - } catch (error) { - console.error('Error fetching token USD price:', error); - return undefined; - } - } - - /** - * Fetches the USD prices of multiple tokens from the Family API via proxy. - * @param tokenRequests - Array of objects with chainId and tokenAddress. - * @returns Map of tokenId to USD price. - */ - async getMultipleTokenUsdPrices( - tokenRequests: Array<{ chainId: number; tokenAddress: string }> - ): Promise> { - const priceMap = new Map(); - - try { - if (tokenRequests.length === 0) { - return priceMap; - } - - const tokenIds = tokenRequests.map(({ chainId, tokenAddress }) => { - const apiExpectedAddress = - tokenAddress.toLowerCase() === API_ETH_MOCK_ADDRESS.toLowerCase() - ? zeroAddress - : tokenAddress; - return `${chainId}:${apiExpectedAddress}`; - }); - - const response = await fetch(this.proxyUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - tokenIds, - }), - }); - - if (!response.ok) { - throw new Error(`Failed to fetch USD prices: ${response.statusText}`); - } - - const data = (await response.json()) as FamilyPricesResponse; - - if (data?.prices) { - Object.entries(data.prices).forEach( - ([tokenId, priceData]: [string, FamilyPricesResponse['prices'][string]]) => { - if (priceData?.usd !== undefined) { - priceMap.set(tokenId, priceData.usd.toString()); - } - } - ); - } - - return priceMap; - } catch (error) { - console.error('Error fetching multiple token USD prices:', error); - return priceMap; - } - } -}