Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZapBasedPreview #1745

Merged
merged 7 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading