Skip to content

Commit

Permalink
Price fetching strategy update (#636)
Browse files Browse the repository at this point in the history
This aims to unify the price fetching strategy with the rest of the data
fetching hooks, by relying on TanStack Query rather than using an
internal provider.

- Support setting a CoinGecko API key (demo or pro).
- Refactor usePrice() and its callers to return UseQueryResult.
- Remove the <Price> provider (relying on the TanStack Query cache instead).
- Remove the possibility to update prices in demo mode (UpdatePrices.tsx).
- Remove the random price variations in demo mode.
- Move PRICE_REFRESH_INTERVAL into constants.ts.
  • Loading branch information
bpierre authored Dec 5, 2024
1 parent 882277c commit ec15af8
Show file tree
Hide file tree
Showing 33 changed files with 308 additions and 434 deletions.
3 changes: 3 additions & 0 deletions frontend/app/.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ NEXT_PUBLIC_VERCEL_ANALYTICS=false
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=1
NEXT_PUBLIC_DEPLOYMENT_FLAVOR=

# use demo|API_KEY (demo API) or pro|API_KEY (pro API)
NEXT_PUBLIC_COINGECKO_API_KEY=

# Ethereum (mainnet)
# NEXT_PUBLIC_CHAIN_ID=1
# NEXT_PUBLIC_CHAIN_NAME=Ethereum
Expand Down
17 changes: 7 additions & 10 deletions frontend/app/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import content from "@/src/content";
import { DemoMode } from "@/src/demo-mode";
import { VERCEL_ANALYTICS } from "@/src/env";
import { Ethereum } from "@/src/services/Ethereum";
import { Prices } from "@/src/services/Prices";
import { StoredState } from "@/src/services/StoredState";
import { TransactionFlow } from "@/src/services/TransactionFlow";
import { UiKit } from "@liquity2/uikit";
Expand All @@ -34,15 +33,13 @@ export default function Layout({
<StoredState>
<DemoMode>
<Ethereum>
<Prices>
<TransactionFlow>
<AboutModal>
<AppLayout>
{children}
</AppLayout>
</AboutModal>
</TransactionFlow>
</Prices>
<TransactionFlow>
<AboutModal>
<AppLayout>
{children}
</AppLayout>
</AboutModal>
</TransactionFlow>
</Ethereum>
</DemoMode>
</StoredState>
Expand Down
2 changes: 0 additions & 2 deletions frontend/app/src/comps/AppLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { ReactNode } from "react";

import { UpdatePrices } from "@/src/comps/Debug/UpdatePrices";
import { ProtocolStats } from "@/src/comps/ProtocolStats/ProtocolStats";
import { TopBar } from "@/src/comps/TopBar/TopBar";
import { css } from "@/styled-system/css";
Expand Down Expand Up @@ -69,7 +68,6 @@ export function AppLayout({
<ProtocolStats />
</div>
</div>
<UpdatePrices />
</div>
);
}
86 changes: 0 additions & 86 deletions frontend/app/src/comps/Debug/UpdatePrices.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/app/src/comps/Positions/PositionCardBorrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function PositionCardBorrow({
const token = getCollToken(collIndex);
const collateralPriceUsd = usePrice(token?.symbol ?? null);

const ltv = collateralPriceUsd && getLtv(deposit, borrowed, collateralPriceUsd);
const ltv = collateralPriceUsd.data && getLtv(deposit, borrowed, collateralPriceUsd.data);
const redemptionRisk = getRedemptionRisk(interestRate);

const maxLtv = token && dn.from(1 / token.collateralRatio, 18);
Expand Down
23 changes: 11 additions & 12 deletions frontend/app/src/comps/Positions/PositionCardLeverage.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { PositionLoanCommitted } from "@/src/types";
import type { ReactNode } from "react";

import { getContracts } from "@/src/contracts";
import { formatRedemptionRisk } from "@/src/formatting";
import { getLiquidationRisk, getLtv, getRedemptionRisk } from "@/src/liquity-math";
import { getCollToken } from "@/src/liquity-utils";
import { usePrice } from "@/src/services/Prices";
import { riskLevelToStatusMode } from "@/src/uikit-utils";
import { css } from "@/styled-system/css";
import { HFlex, IconLeverage, StatusDot, TokenIcon, TOKENS_BY_SYMBOL } from "@liquity2/uikit";
import { HFlex, IconLeverage, StatusDot, TokenIcon } from "@liquity2/uikit";
import * as dn from "dnum";
import Link from "next/link";
import { PositionCard } from "./PositionCard";
Expand All @@ -33,21 +33,20 @@ export function PositionCardLeverage({
statusTag?: ReactNode;
})
{
const contracts = getContracts();
const { symbol } = contracts.collaterals[collIndex];
const token = TOKENS_BY_SYMBOL[symbol];

const collateralPriceUsd = usePrice(symbol);
const token = getCollToken(collIndex);
if (!token) {
throw new Error(`Collateral token not found for index ${collIndex}`);
}

if (!collateralPriceUsd) {
const collateralPriceUsd = usePrice(token.symbol);
if (!collateralPriceUsd.data) {
return null;
}

const ltv = getLtv(deposit, borrowed, collateralPriceUsd);
const redemptionRisk = getRedemptionRisk(interestRate);

const maxLtv = dn.from(1 / token.collateralRatio, 18);
const ltv = getLtv(deposit, borrowed, collateralPriceUsd.data);
const liquidationRisk = ltv && getLiquidationRisk(ltv, maxLtv);
const redemptionRisk = getRedemptionRisk(interestRate);

return (
<Link
Expand Down Expand Up @@ -83,7 +82,7 @@ export function PositionCardLeverage({
value: (
<HFlex gap={8} alignItems="center" justifyContent="flex-start">
{deposit ? dn.format(deposit, 2) : "−"}
<TokenIcon size={24} symbol={symbol} />
<TokenIcon size={24} symbol={token.symbol} />
</HFlex>
),
label: "Net value",
Expand Down
101 changes: 65 additions & 36 deletions frontend/app/src/comps/ProtocolStats/ProtocolStats.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"use client";

import type { TokenSymbol } from "@/src/types";

import { Amount } from "@/src/comps/Amount/Amount";
import { Logo } from "@/src/comps/Logo/Logo";
import { getContracts } from "@/src/contracts";
import { useAccount } from "@/src/services/Ethereum";
import { useAllPrices } from "@/src/services/Prices";
import { usePrice } from "@/src/services/Prices";
import { useTotalDeposited } from "@/src/subgraph-hooks";
import { css } from "@/styled-system/css";
import { AnchorTextButton, HFlex, shortenAddress, TokenIcon } from "@liquity2/uikit";
Expand All @@ -16,18 +18,7 @@ const DISPLAYED_PRICES = ["LQTY", "BOLD", "ETH"] as const;

export function ProtocolStats() {
const account = useAccount();
const prices = useAllPrices();
const totalDeposited = useTotalDeposited();

const tvl = getContracts()
.collaterals
.map((collateral, collIndex) => {
const price = prices[collateral.symbol];
const deposited = totalDeposited.data?.[collIndex].totalDeposited;
return price && deposited && dn.mul(price, deposited);
})
.reduce((a, b) => b ? dn.add(a ?? dn.from(0, 18), b) : a, null);

const tvl = useTvl();
return (
<div
className={css({
Expand Down Expand Up @@ -60,29 +51,12 @@ export function ProtocolStats() {
</span>
</HFlex>
<HFlex gap={16}>
{DISPLAYED_PRICES.map((symbol) => {
const price = prices[symbol];
return (
<HFlex
key={symbol}
gap={4}
>
<TokenIcon
size={16}
symbol={symbol}
/>
<HFlex gap={8}>
<span>{symbol}</span>
<Amount
prefix="$"
fallback="…"
value={price}
format="2z"
/>
</HFlex>
</HFlex>
);
})}
{DISPLAYED_PRICES.map((symbol) => (
<Price
key={symbol}
symbol={symbol}
/>
))}
{account.address && (
<AnchorTextButton
id="footer-account-button"
Expand Down Expand Up @@ -114,3 +88,58 @@ export function ProtocolStats() {
</div>
);
}

function Price({ symbol }: { symbol: TokenSymbol }) {
const price = usePrice(symbol);
return (
<HFlex
key={symbol}
gap={4}
>
<TokenIcon
size={16}
symbol={symbol}
/>
<HFlex gap={8}>
<span>{symbol}</span>
<Amount
prefix="$"
fallback="…"
value={price.data}
format="2z"
/>
</HFlex>
</HFlex>
);
}

function useTvl() {
const { collaterals } = getContracts();
const totalDeposited = useTotalDeposited();
const collPrices = Object.fromEntries(collaterals.map((collateral) => (
[collateral.symbol, usePrice(collateral.symbol)] as const
))) as Record<TokenSymbol, ReturnType<typeof usePrice>>;

// make sure all prices and the total deposited have loaded before calculating the TVL
if (
!Object.values(collPrices).every((cp) => cp.status === "success")
|| totalDeposited.status !== "success"
) {
return null;
}

const tvlByCollateral = collaterals.map((collateral, collIndex) => {
const price = collPrices[collateral.symbol].data;
return price && dn.mul(
price,
totalDeposited.data[collIndex].totalDeposited,
);
});

let tvl = dn.from(0, 18);
for (const value of tvlByCollateral ?? []) {
tvl = value ? dn.add(tvl, value) : tvl;
}

return tvl;
}
1 change: 1 addition & 0 deletions frontend/app/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const UPFRONT_INTEREST_PERIOD = 7n * ONE_DAY_IN_SECONDS;
export const SP_YIELD_SPLIT = 72n * 10n ** 16n; // 72%

export const DATA_REFRESH_INTERVAL = 30_000;
export const PRICE_REFRESH_INTERVAL = 60_000;

export const LEVERAGE_MAX_SLIPPAGE = 0.05; // 5%
export const CLOSE_FROM_COLLATERAL_SLIPPAGE = 0.05; // 5%
Expand Down
11 changes: 0 additions & 11 deletions frontend/app/src/demo-mode/demo-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,6 @@ import type { Dnum } from "dnum";
import { INTEREST_RATE_INCREMENT, INTEREST_RATE_MAX, INTEREST_RATE_MIN } from "@/src/constants";
import * as dn from "dnum";

export const PRICE_UPDATE_INTERVAL = 15_000;
export const PRICE_UPDATE_VARIATION = 0.003;
export const PRICE_UPDATE_MANUAL = false;

export const LQTY_PRICE = dn.from(0.64832, 18);
export const ETH_PRICE = dn.from(2_580.293872, 18);
export const RETH_PRICE = dn.from(2_884.72294, 18);
export const WSTETH_PRICE = dn.from(2_579.931, 18);
export const BOLD_PRICE = dn.from(1.0031, 18);
export const LUSD_PRICE = dn.from(1.012, 18);

export const STAKED_LQTY_TOTAL = [43_920_716_739_092_664_364_409_174n, 18] as const;

export const ACCOUNT_STAKED_LQTY = {
Expand Down
Loading

0 comments on commit ec15af8

Please sign in to comment.