Skip to content

Commit

Permalink
ZapBasedPreview (#1745)
Browse files Browse the repository at this point in the history
* preview close long form updated

* Refactor testnet chain detection and token fiat price query

* fix preview

* fix preview

Co-authored-by: Danny Delott <[email protected]>

* update query key

* move toast inside query client provider

* uniqu by

---------

Co-authored-by: Danny Delott <[email protected]>
  • Loading branch information
jackburrus and DannyDelott authored Jan 29, 2025
1 parent 02ec97a commit ae72b83
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 14 deletions.
6 changes: 6 additions & 0 deletions apps/hyperdrive-trading/src/chains/isTestnetChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import { foundry, sepolia } from "viem/chains";
* Checks if the given chain ID corresponds to a testnet chain.
* @param chainId - The chain ID to check.
* @returns True if the chain ID corresponds to a testnet chain, false otherwise.
* @deprecated testnet chains should not include a check on fork chains, use isTestnetChain2 instead
*/
export function isTestnetChain(chainId: number): boolean {
const testnetChainIds = [sepolia.id, foundry.id] as number[];
return isForkChain(chainId) || testnetChainIds.includes(chainId);
}

export function isTestnetChain2(chainId: number): boolean {
const testnetChainIds = [sepolia.id, foundry.id] as number[];
return testnetChainIds.includes(chainId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,33 @@ import {
mainnetAppConfig,
testnetAppConfig,
} from "@delvtech/hyperdrive-appconfig";
import uniqBy from "lodash.uniqby";
import { useFeatureFlag } from "src/ui/base/featureFlags/featureFlags";
import { useTokenList } from "src/ui/tokenlist/useTokenList";
import { useChainId } from "wagmi";

export function useAppConfigForConnectedChain(): AppConfig {
const connectedChainId = useChainId();

return isMainnetChain(connectedChainId) ? mainnetAppConfig : testnetAppConfig;
const appConfig = isMainnetChain(connectedChainId)
? mainnetAppConfig
: testnetAppConfig;

// Add any zap tokens to the appConfig using uniswap's tokenlist
const { isFlagEnabled } = useFeatureFlag("zaps");
const { tokenList } = useTokenList({
chainId: connectedChainId,
enabled: isFlagEnabled,
});
if (tokenList) {
return {
...appConfig,
tokens: uniqBy(
[...appConfig.tokens, ...tokenList],
(token) => `${token.address}-${token.chainId}`,
),
};
}

return appConfig;
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export function CloseLongForm({
chainId: hyperdrive.chainId,
hyperdriveAddress: hyperdrive.address,
maturityTime: long.maturity,
tokenOutAddress: activeWithdrawToken.address,
bondAmountIn: bondAmountAsBigInt,
asBase: activeWithdrawToken.address === hyperdrive.poolConfig.baseToken,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { fixed } from "@delvtech/fixed-point-wasm";
import {
getBaseToken,
getHyperdriveConfig,
} from "@delvtech/hyperdrive-appconfig";
import { useQuery } from "@tanstack/react-query";
import { makeQueryKey } from "src/base/makeQueryKey";
import { makeQueryKey2 } from "src/base/makeQueryKey";
import { QueryStatusWithIdle, getStatus } from "src/base/queryStatus";
import { getDepositAssets } from "src/hyperdrive/getDepositAssets";
import { useAppConfigForConnectedChain } from "src/ui/appconfig/useAppConfigForConnectedChain";
import { prepareSharesOut } from "src/ui/hyperdrive/hooks/usePrepareSharesOut";
import { useReadHyperdrive } from "src/ui/hyperdrive/hooks/useReadHyperdrive";
import { useTokenFiatPrice } from "src/ui/token/hooks/useTokenFiatPrice";
import { Address } from "viem";
interface UsePreviewCloseLongOptions {
chainId: number;
Expand All @@ -13,6 +20,7 @@ interface UsePreviewCloseLongOptions {
*/
maturityTime: bigint | undefined;
bondAmountIn: bigint | undefined;
tokenOutAddress: Address | undefined;
asBase?: boolean;
enabled?: boolean;
}
Expand All @@ -27,6 +35,7 @@ interface UsePreviewCloseLongResult {
export function usePreviewCloseLong({
hyperdriveAddress,
chainId,
tokenOutAddress,
maturityTime,
bondAmountIn,
asBase = true,
Expand All @@ -39,29 +48,96 @@ export function usePreviewCloseLong({
const appConfig = useAppConfigForConnectedChain();
const queryEnabled =
!!hyperdriveAddress &&
!!appConfig &&
!!maturityTime &&
!!bondAmountIn &&
!!readHyperdrive &&
!!tokenOutAddress &&
enabled;

const baseToken = getBaseToken({
hyperdriveChainId: chainId,
hyperdriveAddress: hyperdriveAddress!,
appConfig,
});

const hyperdriveConfig = getHyperdriveConfig({
hyperdriveChainId: chainId,
hyperdriveAddress: hyperdriveAddress!,
appConfig,
});

const depositAssets = getDepositAssets(hyperdriveConfig, appConfig);
const isZapping = !depositAssets.some(
(asset) => asset.address === tokenOutAddress,
);

// If we're zapping, we'll need to convert the zap token to fiat and then into
// base so we can correctly preview the tx using the sdk. To do this, we need
// the fiat price of both the zap and base token
const { fiatPrice: zapTokenPrice } = useTokenFiatPrice({
tokenAddress: tokenOutAddress,
chainId,
enabled: isZapping,
});

const { fiatPrice: baseTokenPrice } = useTokenFiatPrice({
tokenAddress: baseToken.address,
chainId,
enabled: isZapping,
});

const { data, status, fetchStatus, error } = useQuery({
queryKey: makeQueryKey("previewCloseLong", {
chainId,
hyperdriveAddress,
maturityTime: maturityTime?.toString(),
bondAmountIn: bondAmountIn?.toString(),
asBase,
queryKey: makeQueryKey2({
namespace: "hyperdrive",
queryId: "previewCloseLong",
params: {
chainId,
hyperdriveAddress: hyperdriveAddress!,
maturityTime,
bondAmountIn,
asBase,
zapTokenPrice,
baseTokenPrice,
tokenOutAddress,
},
}),
enabled: queryEnabled,
queryFn: queryEnabled
? async () => {
if (isZapping) {
const resultInBase = await readHyperdrive.previewCloseLong({
maturityTime,
bondAmountIn,
asBase: true, // Safe to hardcode to true because previewCloseLong uses the Rust SDK, which doesn't perform checks on if the pool has a base or shares token
});

// Step 1: Calculate fiat value of the base amount
const fiatValueOfAmountOut = baseTokenPrice
? fixed(baseTokenPrice).mul(
resultInBase.amountOut,
hyperdriveConfig.decimals,
)
: 0n;

// Step 2: Convert that fiat value into the ZAP token amount
const zapAmountOut = zapTokenPrice
? fixed(fiatValueOfAmountOut).div(
zapTokenPrice,
hyperdriveConfig.decimals,
)
: 0n;

return {
...resultInBase,
amountOut: zapAmountOut ? zapAmountOut.bigint : 0n,
};
}

const result = await readHyperdrive.previewCloseLong({
maturityTime,
bondAmountIn,
asBase,
});

if (!asBase) {
return {
...result,
Expand Down
10 changes: 10 additions & 0 deletions apps/hyperdrive-trading/src/ui/hyperdrive/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ interface HyperdriveQueryKeys {
hyperdriveAddress: Address;
blockNumber: bigint | undefined;
};
previewCloseLong: {
chainId: number;
hyperdriveAddress: Address;
maturityTime: bigint | undefined;
bondAmountIn: bigint | undefined;
asBase: boolean;
zapTokenPrice: bigint | undefined;
baseTokenPrice: bigint | undefined;
tokenOutAddress: Address | undefined;
};
}
declare module "src/base/makeQueryKey" {
interface QueryKeys {
Expand Down
2 changes: 1 addition & 1 deletion apps/hyperdrive-trading/src/ui/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ logAppVersion();
root.render(
<WagmiProvider config={wagmiConfig}>
<Plausible />
<ToastProvider />
<QueryClientProvider client={queryClient}>
<ToastProvider />
<RainbowKitProvider
appInfo={{
appName: "Hyperdrive",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function CurrentValueCell({
hyperdriveAddress: hyperdrive.address,
maturityTime: row.maturity,
bondAmountIn: row.details?.bondAmount || 0n,
tokenOutAddress: baseToken.address,
});

const currentValueLabel = formatBalance({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { getPublicClient } from "@wagmi/core";
import { ZERO_ADDRESS } from "src/base/constants";
import { makeQueryKey2 } from "src/base/makeQueryKey";
import { isTestnetChain } from "src/chains/isTestnetChain";
import { isTestnetChain2 } from "src/chains/isTestnetChain";
import { wagmiConfig } from "src/network/wagmiClient";
import { useAppConfigForConnectedChain } from "src/ui/appconfig/useAppConfigForConnectedChain";
import { Address, PublicClient } from "viem";
Expand All @@ -20,10 +20,11 @@ export function useTokenFiatPrice({
} {
const appConfig = useAppConfigForConnectedChain();
const { data } = useQuery(
makeTokenFiatPriceQuery({ appConfig, chainId, tokenAddress, enabled }),
makeTokenFiatPriceQuery({ chainId, tokenAddress, enabled, appConfig }),
);
return { fiatPrice: data };
}

export function makeTokenFiatPriceQuery({
appConfig,
chainId,
Expand All @@ -36,7 +37,7 @@ export function makeTokenFiatPriceQuery({
enabled?: boolean;
}): UseQueryOptions<bigint> {
const queryEnabled =
!isTestnetChain(chainId) &&
!isTestnetChain2(chainId) &&
!!tokenAddress &&
tokenAddress !== ZERO_ADDRESS &&
enabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const defiLlamaChainNameIdentifier: Record<number, string> = {
[gnosis.id]: "gnosis",
[linea.id]: "linea",
[base.id]: "base",
[707]: "ethereum",
};

export const fetchDefiLlamaTokenPrice: PriceOracleFn = async ({
Expand Down

0 comments on commit ae72b83

Please sign in to comment.