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

FIX: jUSDC vault cap and add "get jUSDC" link. #232

Merged
merged 2 commits into from
Oct 22, 2024
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
109 changes: 8 additions & 101 deletions src/components/common/input/LiquidateInput.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { formatUnits, zeroAddress } from "viem";
import { useAccount, useReadContract } from "wagmi";
import { useChain } from "@/hooks/useChain";
import { Vault } from "@/lib/types";
import { alchemistV2Abi } from "@/abi/alchemistV2";
import { useWatchQuery } from "@/hooks/useWatchQuery";
import { zeroAddress } from "viem";

import { TokenInput } from "./TokenInput";
import { ScopeKeys } from "@/lib/queries/queriesSchema";

import { Vault } from "@/lib/types";
import { useVaultsLiquidateAvailableBalance } from "@/lib/queries/vaults/useVaultsLiquidateAvailableBalance";

export const LiquidateTokenInput = ({
amount,
Expand All @@ -20,100 +18,9 @@ export const LiquidateTokenInput = ({
tokenSymbol: string;
tokenDecimals: number;
}) => {
const chain = useChain();
const { address } = useAccount();

const { data: unrealizedDebt } = useReadContract({
address: vault.alchemist.address,
abi: alchemistV2Abi,
chainId: chain.id,
functionName: "accounts",
args: [address ?? zeroAddress],
query: {
enabled: !!address,
select: ([debt]) => (debt < 0n ? 0n : debt),
},
});

const { data: normalizedDebtToUnderlying } = useReadContract({
address: vault.alchemist.address,
abi: alchemistV2Abi,
chainId: chain.id,
functionName: "normalizeDebtTokensToUnderlying",
args: [vault.underlyingToken, unrealizedDebt ?? 0n],
query: {
enabled: unrealizedDebt !== undefined,
},
});

const { data: maximumShares } = useReadContract({
address: vault.alchemist.address,
abi: alchemistV2Abi,
chainId: chain.id,
functionName: "convertUnderlyingTokensToShares",
args: [vault.yieldToken, normalizedDebtToUnderlying ?? 0n],
scopeKey: ScopeKeys.LiquidateInput,
query: {
enabled: normalizedDebtToUnderlying !== undefined,
},
});

const { data: debtInYield } = useReadContract({
address: vault.alchemist.address,
chainId: chain.id,
abi: alchemistV2Abi,
functionName: "convertSharesToYieldTokens",
args: [vault.yieldToken, maximumShares ?? 0n],
query: {
enabled: maximumShares !== undefined,
select: (balance) =>
formatUnits(balance, vault.yieldTokenParams.decimals),
},
});

const { data: sharesBalance } = useReadContract({
address: vault.alchemist.address,
chainId: chain.id,
abi: alchemistV2Abi,
functionName: "positions",
args: [address ?? zeroAddress, vault.yieldToken],
scopeKey: ScopeKeys.LiquidateInput,
query: {
enabled: !!address,
select: ([shares]) => shares,
},
});

const { data: balance } = useReadContract({
address: vault.alchemist.address,
chainId: chain.id,
abi: alchemistV2Abi,
functionName: "convertSharesToYieldTokens",
args: [vault.yieldToken, sharesBalance ?? 0n],
scopeKey: ScopeKeys.LiquidateInput,
query: {
enabled: sharesBalance !== undefined,
select: (balance) =>
formatUnits(balance, vault.yieldTokenParams.decimals),
},
});

/**
* NOTE: Watch queries for changes in maximumShares, sharesBalance, and balance.
* maximumShares - because underlying tokens to shares uses price which changes each block;
* sharesBalance - if user deposited or withdrawed from vault for yield token;
* balance - because shares to yield token uses price which changes each block.
*/
useWatchQuery({
scopeKey: ScopeKeys.LiquidateInput,
});

const externalMaximumAmount =
debtInYield !== undefined &&
balance !== undefined &&
+debtInYield < +balance
? debtInYield
: undefined;
const { balance, externalMaximumAmount } = useVaultsLiquidateAvailableBalance(
{ vault },
);

return (
<TokenInput
Expand Down
44 changes: 41 additions & 3 deletions src/components/vaults/common_actions/Liquidate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "@/components/ui/select";
import { useTokensQuery } from "@/lib/queries/useTokensQuery";
import {
useAccount,
useReadContract,
useSimulateContract,
useWaitForTransactionReceipt,
Expand All @@ -33,12 +34,15 @@ import { MAX_UINT256_BN } from "@/lib/constants";
import { invalidateWagmiUseQueryPredicate } from "@/utils/helpers/invalidateWagmiUseQueryPredicate";
import { CtaButton } from "@/components/common/CtaButton";
import { getTokenLogoUrl } from "@/utils/getTokenLogoUrl";
import { useVaultsLiquidateAvailableBalance } from "@/lib/queries/vaults/useVaultsLiquidateAvailableBalance";

export const Liquidate = () => {
const queryClient = useQueryClient();
const chain = useChain();
const mutationCallback = useWriteContractMutationCallback();

const { address } = useAccount();

const [amount, setAmount] = useState("");
const [slippage, setSlippage] = useState("0.5");
const [confirmedLiquidation, setConfirmedLiquidation] = useState(false);
Expand Down Expand Up @@ -90,7 +94,7 @@ export const Liquidate = () => {
v.yieldToken.toLowerCase() === liquidationTokenAddress?.toLowerCase(),
);

const { data: shares } = useReadContract({
let { data: shares } = useReadContract({
address: vault?.alchemist.address,
abi: alchemistV2Abi,
chainId: chain.id,
Expand All @@ -104,6 +108,33 @@ export const Liquidate = () => {
},
});

const { data: depositedSharesBalance } = useReadContract({
address: vault?.alchemist.address,
abi: alchemistV2Abi,
chainId: chain.id,
functionName: "positions",
args: [address!, vault?.yieldToken ?? zeroAddress],
query: {
enabled: !!address,
select: ([shares]) => shares,
},
});

const { balance } = useVaultsLiquidateAvailableBalance({ vault });

/**
* Guard against dynamic yield token price changes.
* When initially calculated max yield tokens amount from static shares,
* converts back to shares higher than deposited shares balance
*/
if (
depositedSharesBalance !== undefined &&
shares !== undefined &&
depositedSharesBalance < shares
) {
shares = depositedSharesBalance - 1n;
}

const { data: minimumOut } = useReadContract({
address: vault?.alchemist.address,
abi: alchemistV2Abi,
Expand Down Expand Up @@ -215,6 +246,13 @@ export const Liquidate = () => {
}
};

const isInsufficientBalance = balance !== undefined && +amount > +balance;
const isDisabledCta =
isPending ||
isInputZero(amount) ||
!confirmedLiquidation ||
isInsufficientBalance;

return (
<div className="space-y-4 bg-grey15inverse p-4 dark:bg-grey15">
<DebtSelection
Expand Down Expand Up @@ -283,9 +321,9 @@ export const Liquidate = () => {
variant="outline"
width="full"
onClick={onCtaClick}
disabled={isPending || isInputZero(amount) || !confirmedLiquidation}
disabled={isDisabledCta}
>
Liquidate
{isInsufficientBalance ? "Insufficient Balance" : "Liquidate"}
</CtaButton>
</>
)}
Expand Down
16 changes: 11 additions & 5 deletions src/components/vaults/row/VaultAccordionRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ export const VaultAccordionRow = ({ vault }: { vault: Vault }) => {
<p className="text-center text-sm text-lightgrey10">TVL / Cap</p>
<VaultCapacityCell
vault={vault}
tokenDecimals={vaultUnderlyingTokenData?.decimals}
yieldTokenDecimals={vaultYieldTokenData?.decimals}
underlyingTokenDecimals={vaultUnderlyingTokenData?.decimals}
tokenSymbol={vaultUnderlyingTokenData?.symbol}
/>
</div>
Expand Down Expand Up @@ -314,18 +315,20 @@ export const CurrencyCell = ({

const VaultCapacityCell = ({
vault,
tokenDecimals = 18,
yieldTokenDecimals = 18,
underlyingTokenDecimals = 18,
tokenSymbol,
}: {
vault: Vault;
tokenDecimals: number | undefined;
yieldTokenDecimals: number | undefined;
underlyingTokenDecimals: number | undefined;
tokenSymbol: string | undefined;
}) => {
const chain = useChain();

const limitValue = formatUnits(
vault.yieldTokenParams.maximumExpectedValue,
tokenDecimals,
yieldTokenDecimals,
);

const { data: capacity, isPending } = useReadContract({
Expand All @@ -336,7 +339,10 @@ const VaultCapacityCell = ({
args: [vault.yieldToken, vault.yieldTokenParams.totalShares],
query: {
select: (currentValueBn) => {
const currentValue = formatUnits(currentValueBn, tokenDecimals);
const currentValue = formatUnits(
currentValueBn,
underlyingTokenDecimals,
);
const isFull =
(parseFloat(currentValue) / parseFloat(limitValue)) * 100 >= 99;
return { currentValue, isFull };
Expand Down
10 changes: 5 additions & 5 deletions src/components/vaults/row/VaultMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ const messageConfig = {
} as const;

export const VaultMessage = (props: { message: VaultMessageType }) => {
const { message, type, learnMoreUrl } = props.message;
const { message, type, linkHref, linkLabel } = props.message;
return (
<div
className={cn(
"text-l flex w-full flex-row items-center space-x-4 rounded border p-2 pl-4 text-grey15",
type === "info" && "border-blue2 bg-blue1",
type === "info" && "border-blue1 bg-blue1/50",
type === "warning" && "border-orange2 bg-orange1",
type === "error" && "border-red3 bg-red1",
)}
Expand All @@ -36,16 +36,16 @@ export const VaultMessage = (props: { message: VaultMessageType }) => {
</svg>
<p>
{message}
{learnMoreUrl && (
{linkHref && (
<>
<span> </span>
<a
href={learnMoreUrl}
href={linkHref}
target="_blank"
rel="noreferrer"
className="underline hover:no-underline"
>
Learn more.
{linkLabel ?? "Learn more."}
</a>
</>
)}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/config/metadataTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export type MessageType = "info" | "warning" | "error";
export type VaultMessage = {
message: string;
type: MessageType;
learnMoreUrl?: string;
linkHref?: string;
linkLabel?: string;
};

type ApiProvider =
Expand Down
16 changes: 12 additions & 4 deletions src/lib/config/vaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ export const VAULTS: VaultsConfig = {
message:
"Yearn yvUSDC is currently disabled for underlying token deposit and withdraw.",
type: "warning",
learnMoreUrl: "https://discord.com/invite/yearn",
linkHref: "https://discord.com/invite/yearn",
},
],
gateway: "0xC02670867efac6D988F40878a5559a8D96002A56",
Expand All @@ -471,7 +471,7 @@ export const VAULTS: VaultsConfig = {
message:
"Yearn yvDAI is currently disabled for underlying token deposit and withdraw.",
type: "warning",
learnMoreUrl: "https://discord.com/invite/yearn",
linkHref: "https://discord.com/invite/yearn",
},
],
gateway: "0xC02670867efac6D988F40878a5559a8D96002A56",
Expand Down Expand Up @@ -533,7 +533,7 @@ export const VAULTS: VaultsConfig = {
message:
"Yearn yvWETH is currently disabled for underlying token deposit and withdraw.",
type: "warning",
learnMoreUrl: "https://discord.com/invite/yearn",
linkHref: "https://discord.com/invite/yearn",
},
],
gateway: "0xedE36d3F423EF198abE82D2463E0a18bcF2d9397",
Expand Down Expand Up @@ -574,7 +574,15 @@ export const VAULTS: VaultsConfig = {
underlyingSymbol: "USDC",
yieldSymbol: "jUSDC",
image: "jUSDC.webp",
messages: [],
messages: [
{
type: "info",
message:
"Only jUSDC deposit and withdraw are available. Get jUSDC from Jones.",
linkHref: "https://app.jonesdao.io/vaults/leveraged/usdc",
linkLabel: "Get jUSDC.",
},
],
api: {
apr: getJonesApy,
yieldType: "APY",
Expand Down
Loading