diff --git a/.env.example b/.env.example index 01c757b18..078addfb5 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,9 @@ -VITE_RPC_URI_FOR_1=https://rpc.yearn.fi/chain/1 -VITE_RPC_URI_FOR_10=https://rpc.yearn.fi/chain/10 -VITE_RPC_URI_FOR_100=https://rpc.yearn.fi/chain/100 -VITE_RPC_URI_FOR_137=https://rpc.yearn.fi/chain/137 -VITE_RPC_URI_FOR_250=https://rpc.yearn.fi/chain/250 -VITE_RPC_URI_FOR_8453=https://rpc.yearn.fi/chain/8453 -VITE_RPC_URI_FOR_42161=https://rpc.yearn.fi/chain/42161 -VITE_RPC_URI_FOR_747474=https://rpc.yearn.fi/chain/747474 +RPC_URI_FOR_1=https://rpc.yearn.fi/chain/1 +RPC_URI_FOR_10=https://rpc.yearn.fi/chain/10 +RPC_URI_FOR_100=https://rpc.yearn.fi/chain/100 +RPC_URI_FOR_137=https://rpc.yearn.fi/chain/137 +RPC_URI_FOR_250=https://rpc.yearn.fi/chain/250 +RPC_URI_FOR_8453=https://rpc.yearn.fi/chain/8453 +RPC_URI_FOR_42161=https://rpc.yearn.fi/chain/42161 +RPC_URI_FOR_747474=https://rpc.yearn.fi/chain/747474 +KONG_ENDPOINT=https://kong.yearn.fi/api/rest diff --git a/README.md b/README.md index d58d4ff1e..f081318e9 100644 --- a/README.md +++ b/README.md @@ -128,13 +128,15 @@ Artifact URLs default to `https://docs.yearn.fi`. To generate artifacts against ## Configure .env -The docs site pulls data from on-chain smart contracts and now resolves RPCs by chain. Copy `.env.example` to `.env` and set any `VITE_RPC_URI_FOR_` values you want to override. +The docs site pulls data from on-chain smart contracts and now resolves RPCs by chain. Copy `.env.example` to `.env` and set any `RPC_URI_FOR_` values you want to override. By default, the repo will use Yearn RPC endpoints in the form `https://rpc.yearn.fi/chain/`, so the example values can usually be used as-is. Runtime docs reads use the chain declared in each page's `rpcCalls` frontmatter. The address-check script is still mainnet-only today, so it uses chain `1`. -The legacy `ALCHEMY_API_KEY` mainnet fallback is still supported temporarily for compatibility, but `VITE_RPC_URI_FOR_1` is now the preferred configuration. +The site reads vault snapshots from Kong. Set `KONG_ENDPOINT` only when you need to point at a different Kong-compatible REST host. + +`ALCHEMY_API_KEY` is no longer read by the site. Use `RPC_URI_FOR_1` for mainnet RPC overrides. ## Deployment diff --git a/docusaurus.config.js b/docusaurus.config.js index 79ed809e2..00becbbbd 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -9,8 +9,8 @@ import { getRpcUriOverridesFromEnv } from './src/ethereum/publicRpc' const branchName = process.env.BRANCH_NAME || 'unknown' const isDev = process.env.IS_DEV === 'true' const rpcUris = getRpcUriOverridesFromEnv(process.env) -const yDaemon = process.env.YDAEMON_ENDPOINT || 'unknown' -const yPriceMagic = process.env.YPRICEMAGIC_ENDPOINT || 'unknown' +const kongEndpoint = + process.env.KONG_ENDPOINT || 'https://kong.yearn.fi/api/rest' export default { title: 'Yearn Docs', @@ -31,8 +31,7 @@ export default { branchName, isDev, rpcUris, - yDaemon, - yPriceMagic, + kongEndpoint, }, future: { experimental_faster: true, diff --git a/src/components/veYFI-calculator/fetch.ts b/src/components/veYFI-calculator/fetch.ts index 3ff634553..78aaec3a9 100644 --- a/src/components/veYFI-calculator/fetch.ts +++ b/src/components/veYFI-calculator/fetch.ts @@ -82,12 +82,65 @@ export async function fetchAllGaugeData(publicClient: any) { return Promise.all(gaugeDataPromises) } -export async function fetchTokenPrice(yDaemon: string, address: string) { - const response = await fetch(`${yDaemon}/1/vaults/${address}`) - if (!response.ok) { - console.error('Failed to fetch token price') - return null +type KongVaultSnapshot = { + decimals?: number | string + pricePerShare?: string + totalAssets?: string + tvl?: { + close?: number + } + asset?: { + decimals?: number | string + } +} + +const formatScaledAmount = ( + value: string | undefined, + decimals: number | string | undefined +) => { + if (!value || decimals === undefined) { + return undefined + } + + const amount = Number(formatUnits(BigInt(value), Number(decimals))) + return Number.isFinite(amount) ? amount : undefined +} + +export async function fetchVaultSharePrice( + kongEndpoint: string, + address: string +) { + try { + const endpoint = kongEndpoint.replace(/\/+$/, '') + const response = await fetch(`${endpoint}/snapshot/1/${address}`) + if (!response.ok) { + console.error('Failed to fetch vault snapshot') + return undefined + } + + const data = (await response.json()) as KongVaultSnapshot + const totalAssets = formatScaledAmount( + data.totalAssets, + data.asset?.decimals + ) + const tvl = data.tvl?.close + const pricePerShare = formatScaledAmount(data.pricePerShare, data.decimals) + + if ( + !totalAssets || + !tvl || + !pricePerShare || + totalAssets <= 0 || + tvl <= 0 || + pricePerShare <= 0 + ) { + return undefined + } + + const sharePrice = pricePerShare * (tvl / totalAssets) + return Number.isFinite(sharePrice) ? sharePrice : undefined + } catch (error) { + console.error('Failed to fetch vault snapshot', error) + return undefined } - const data = await response.json() - return data } diff --git a/src/components/veYFI-calculator/veYFI-calculator.tsx b/src/components/veYFI-calculator/veYFI-calculator.tsx index ed34ed664..9ef1a9f84 100644 --- a/src/components/veYFI-calculator/veYFI-calculator.tsx +++ b/src/components/veYFI-calculator/veYFI-calculator.tsx @@ -33,7 +33,7 @@ import { import { fetchVeYFISupply, fetchAllGaugeData, - fetchTokenPrice, + fetchVaultSharePrice, fetchLiquidLockerVeYFIBalance, } from './fetch' import BoostChart from './BoostChart' @@ -60,9 +60,8 @@ const VeYFICalculator: React.FC = () => { const { getPublicClient } = useContext(PublicClientContext) const publicClient = getPublicClient(DEFAULT_MAINNET_CHAIN_ID) const { siteConfig } = useDocusaurusContext() - const { yDaemon } = siteConfig.customFields as { - yDaemon: string - yPriceMagic: string + const { kongEndpoint } = siteConfig.customFields as { + kongEndpoint: string } const [veYfiTotalSupply, setVeYfiTotalSupply] = useState(0) @@ -73,6 +72,9 @@ const VeYFICalculator: React.FC = () => { const [selectedVaultSharePrice, setSelectedVaultSharePrice] = useState< number | undefined >(undefined) + const [vaultPriceError, setVaultPriceError] = useState( + undefined + ) const [depositAmount, setDepositAmount] = useState('') const [depositAmountInUSD, setDepositAmountInUSD] = useState( '' @@ -99,6 +101,10 @@ const VeYFICalculator: React.FC = () => { >({}) const [useLiquidLocker, setUseLiquidLocker] = useState(false) const [isDataFetched, setIsDataFetched] = useState(false) + const hasValidVaultSharePrice = + selectedVaultSharePrice !== undefined && + Number.isFinite(selectedVaultSharePrice) && + selectedVaultSharePrice > 0 useEffect(() => { const fetchData = async () => { @@ -139,11 +145,10 @@ const VeYFICalculator: React.FC = () => { return } if (!isNaN(Number(val))) { - const depositAmountInDollars = selectedVaultSharePrice - ? selectedVaultSharePrice * Number(val) - : 0 - setDepositAmountInUSD(depositAmountInDollars) setDepositAmount(Number(val)) + setDepositAmountInUSD( + hasValidVaultSharePrice ? selectedVaultSharePrice * Number(val) : '' + ) } setShowChart1(false) setShowChart2(false) @@ -159,10 +164,9 @@ const VeYFICalculator: React.FC = () => { } if (!isNaN(Number(val))) { setDepositAmountInUSD(Number(val)) - const depositAmountInShares = selectedVaultSharePrice - ? Number(val) / selectedVaultSharePrice - : 0 - setDepositAmount(depositAmountInShares) + setDepositAmount( + hasValidVaultSharePrice ? Number(val) / selectedVaultSharePrice : '' + ) } setShowChart1(false) setShowChart2(false) @@ -187,15 +191,21 @@ const VeYFICalculator: React.FC = () => { const handleVaultChange = async (vaultName: string) => { setSelectedVault(vaultName) + setSelectedVaultSharePrice(undefined) + setVaultPriceError(undefined) const selectedGauge = gaugeData.find((gauge) => gauge.name === vaultName) if (selectedGauge) { - const tokenPriceData = await fetchTokenPrice( - yDaemon, + const vaultSharePrice = await fetchVaultSharePrice( + kongEndpoint, selectedGauge.underlyingVaultAddress ) - const underlyingPrice = tokenPriceData?.tvl.price - const pricePerShare = tokenPriceData?.apr.pricePerShare.today - const vaultSharePrice = pricePerShare * underlyingPrice + if ( + vaultSharePrice === undefined || + !Number.isFinite(vaultSharePrice) || + vaultSharePrice <= 0 + ) { + setVaultPriceError('Vault price unavailable') + } setSelectedVaultSharePrice(vaultSharePrice) } setShowChart1(false) @@ -205,6 +215,11 @@ const VeYFICalculator: React.FC = () => { } const handleCalculateButton1Click = () => { + if (!hasValidVaultSharePrice) { + setVaultPriceError('Vault price unavailable') + return + } + const vaultSharePrice = selectedVaultSharePrice const totalDeposited = gaugeData.find((g) => g.name === selectedVault)?.totalAssets || 0 const depositVal = Number(depositAmount) || 0 @@ -229,19 +244,18 @@ const VeYFICalculator: React.FC = () => { ) const dataUSD = dataShares.map((entry) => ({ ...entry, - amountDepositedInGauge: - entry.amountDepositedInGauge * (selectedVaultSharePrice ?? 0), + amountDepositedInGauge: entry.amountDepositedInGauge * vaultSharePrice, })) // get the boost for the entered deposit amount const specificBoost = calculateBoost( veYFIVal, veYfiTotalSupply, totalDeposited, - Number(depositAmountInUSD) / (selectedVaultSharePrice ?? 0) + Number(depositAmountInUSD) / vaultSharePrice ) const specificBoostDataPoint = generateSinglePoint(depositVal, calcFunc) const specificBoostDataPointUSD = - (specificBoostDataPoint?.value ?? 0) * (selectedVaultSharePrice ?? 0) + (specificBoostDataPoint?.value ?? 0) * vaultSharePrice setCalculatedBoost({ veYFI: veYFIVal, value: specificBoostDataPoint?.value || 0, @@ -255,6 +269,10 @@ const VeYFICalculator: React.FC = () => { } const handleCalculateButton2Click = () => { + if (!hasValidVaultSharePrice) { + setVaultPriceError('Vault price unavailable') + return + } const totalDeposited = gaugeData.find((g) => g.name === selectedVault)?.totalAssets || 0 const depositVal = Number(depositAmount) || 0 @@ -316,6 +334,7 @@ const VeYFICalculator: React.FC = () => { ))} + {vaultPriceError &&

{vaultPriceError}

}
{/* */} @@ -382,6 +401,7 @@ const VeYFICalculator: React.FC = () => { onClick={handleCalculateButton1Click} disabled={ !selectedVault || + !hasValidVaultSharePrice || (useVeYfiCalculator ? isNaN(Number(veYFIFromLock)) || !veYFIFromLock : isNaN(Number(veYFIAmount)) || !veYFIAmount) @@ -482,6 +502,7 @@ const VeYFICalculator: React.FC = () => { ))} + {vaultPriceError &&

{vaultPriceError}

}
{ onClick={handleCalculateButton2Click} disabled={ !selectedVault || + !hasValidVaultSharePrice || isNaN(Number(depositAmount)) || !depositAmount } diff --git a/src/ethereum/publicRpc.ts b/src/ethereum/publicRpc.ts index d696f22a2..a5d610f4c 100644 --- a/src/ethereum/publicRpc.ts +++ b/src/ethereum/publicRpc.ts @@ -3,7 +3,7 @@ import * as viemChains from 'viem/chains' const DEFAULT_RPC_BASE_URL = 'https://rpc.yearn.fi/chain' const DEFAULT_MAINNET_CHAIN_ID = 1 -const RPC_ENV_PREFIXES = ['VITE_RPC_URI_FOR_', 'RPC_URI_FOR_'] +const RPC_ENV_PREFIX = 'RPC_URI_FOR_' const knownChains = { 1: viemChains.mainnet, @@ -58,24 +58,16 @@ export function getRpcUriOverridesFromEnv( continue } - for (const prefix of RPC_ENV_PREFIXES) { - if (!key.startsWith(prefix)) { - continue - } - - const chainId = key.slice(prefix.length) - if (!/^\d+$/.test(chainId)) { - continue - } + if (!key.startsWith(RPC_ENV_PREFIX)) { + continue + } - rpcUris[chainId] = trimmedValue + const chainId = key.slice(RPC_ENV_PREFIX.length) + if (!/^\d+$/.test(chainId)) { + continue } - } - const legacyAlchemyKey = env.ALCHEMY_API_KEY?.trim() - if (legacyAlchemyKey && !rpcUris[String(DEFAULT_MAINNET_CHAIN_ID)]) { - rpcUris[String(DEFAULT_MAINNET_CHAIN_ID)] = - `https://eth-mainnet.g.alchemy.com/v2/${legacyAlchemyKey}` + rpcUris[chainId] = trimmedValue } return rpcUris