Skip to content

Commit

Permalink
Merge pull request #78 from alchemix-finance/feat/token-adapter-adjus…
Browse files Browse the repository at this point in the history
…ted-withdraw

FEAT: Dynamic withdraw amount of Aave yield bearing tokens, Connect Wallet Cta Button, Refactors
  • Loading branch information
n4n0GH authored Sep 2, 2024
2 parents 23bac52 + 731676d commit 2a6505e
Show file tree
Hide file tree
Showing 22 changed files with 926 additions and 298 deletions.
426 changes: 426 additions & 0 deletions src/abi/staticTokenAdapter.ts

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions src/components/common/CtaButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useAccount } from "wagmi";
import { Button } from "../ui/button";
import { useConnectModal } from "@rainbow-me/rainbowkit";
import { ComponentPropsWithRef } from "react";

/**
* Button that opens connect modal if the user is not connected.
*/
export const CtaButton = (props: ComponentPropsWithRef<typeof Button>) => {
const { address } = useAccount();
const { openConnectModal } = useConnectModal();
return (
<Button
{...props}
onClick={!address ? openConnectModal : props.onClick}
disabled={!address ? false : props.disabled}
>
{!address ? "Connect Wallet" : props.children}
</Button>
);
};
80 changes: 51 additions & 29 deletions src/components/common/input/VaultWithdrawTokenInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Vault } from "@/lib/types";
import { alchemistV2Abi } from "@/abi/alchemistV2";
import { formatEther, formatUnits, parseUnits, zeroAddress } from "viem";
import { useWatchQuery } from "@/hooks/useWatchQuery";
import { useMemo } from "react";
import { TokenInput } from "./TokenInput";
import { useStaticTokenAdapterWithdraw } from "@/hooks/useStaticTokenAdapterWithdraw";

export const VaultWithdrawTokenInput = ({
amount,
Expand Down Expand Up @@ -74,16 +74,7 @@ export const VaultWithdrawTokenInput = ({
},
});

useWatchQuery({
queryKeys: [sharesBalanceQueryKey, totalCollateralInDebtTokenQueryKey],
});

const otherCoverInDebt =
totalCollateralInDebtToken !== undefined &&
collateralInDebtToken !== undefined
? totalCollateralInDebtToken - collateralInDebtToken
: 0n;
const balanceInDebt = useMemo(() => {
const balanceInDebt = (() => {
if (collateralInDebtToken === undefined) {
return 0n;
}
Expand All @@ -93,12 +84,18 @@ export const VaultWithdrawTokenInput = ({

const maxWithdrawAmount = collateralInDebtToken - requiredCoverInDebt;

const otherCoverInDebt =
totalCollateralInDebtToken !== undefined &&
collateralInDebtToken !== undefined
? totalCollateralInDebtToken - collateralInDebtToken
: 0n;

if (otherCoverInDebt >= requiredCoverInDebt) {
return collateralInDebtToken;
} else {
return maxWithdrawAmount;
}
}, [collateralInDebtToken, otherCoverInDebt, vault]);
})();

const { data: balanceForUnderlying } = useReadContract({
address: vault.alchemist.address,
Expand All @@ -112,27 +109,52 @@ export const VaultWithdrawTokenInput = ({
},
});

const { data: balanceForYieldToken } = useReadContract({
address: vault.alchemist.address,
chainId: chain.id,
abi: alchemistV2Abi,
functionName: "convertUnderlyingTokensToYield",
args: [
vault.yieldToken,
parseUnits(
balanceForUnderlying ?? "0",
vault.underlyingTokensParams.decimals,
),
const { data: balanceForYieldToken, queryKey: balanceForYieldTokenQueryKey } =
useReadContract({
address: vault.alchemist.address,
chainId: chain.id,
abi: alchemistV2Abi,
functionName: "convertUnderlyingTokensToYield",
args: [
vault.yieldToken,
parseUnits(
balanceForUnderlying ?? "0",
vault.underlyingTokensParams.decimals,
),
],
query: {
enabled: balanceForUnderlying !== undefined,
select: (balance) =>
formatUnits(balance, vault.yieldTokenParams.decimals),
},
});

const { balanceForYieldTokenAdapter } = useStaticTokenAdapterWithdraw({
typeGuard: "withdrawInput",
balanceForYieldToken,
isSelectedTokenYieldToken,
vault,
});

/**
* NOTE: Watch queries for changes in sharesBalance, totalCollateral, and balanceForYieldToken.
* sharesBalanceQueryKey - if user deposited or withdrawed from vault for yield token;
* totalCollateralInDebtTokenQueryKey - if user deposited or withdrawed from vault for yield token;
* balanceForYieldTokenQueryKey - because shares to yield token uses price which changes each block.
*/
useWatchQuery({
queryKeys: [
sharesBalanceQueryKey,
totalCollateralInDebtTokenQueryKey,
balanceForYieldTokenQueryKey,
],
query: {
enabled: balanceForUnderlying !== undefined,
select: (balance) =>
formatUnits(balance, vault.yieldTokenParams.decimals),
},
});

const balance = isSelectedTokenYieldToken
? balanceForYieldToken
? vault.metadata.api.provider === "aave" &&
vault.metadata.yieldTokenOverride
? balanceForYieldTokenAdapter
: balanceForYieldToken
: balanceForUnderlying;

return (
Expand Down
10 changes: 5 additions & 5 deletions src/components/farms/GAlcxWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { Switch } from "../ui/switch";
import { Button } from "../ui/button";
import {
Expand Down Expand Up @@ -122,7 +122,7 @@ export const GAlcsWrapper = () => {
setAmount("");
};

const onWrap = useCallback(() => {
const onWrap = () => {
if (isApprovalNeeded) {
approveConfig && approve(approveConfig.request);
return;
Expand All @@ -145,9 +145,9 @@ export const GAlcsWrapper = () => {
description: "Unexpected error. Please contact Alchemix team.",
});
}
}, [approve, approveConfig, isApprovalNeeded, wrap, wrapConfig, wrapError]);
};

const onUnwrap = useCallback(() => {
const onUnwrap = () => {
if (unwrapError) {
toast.error("Error unwrapping ALCX", {
description:
Expand All @@ -165,7 +165,7 @@ export const GAlcsWrapper = () => {
description: "Unexpected error. Please contact Alchemix team.",
});
}
}, [unwrap, unwrapConfig, unwrapError]);
};

const handleOpen = () => {
setOpen((prev) => !prev);
Expand Down
25 changes: 11 additions & 14 deletions src/components/governance/row/ProposalAccordionRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getAddress } from "viem";
import { Fragment, useMemo, useState } from "react";
import { Fragment, useState } from "react";
import { useAccount, useWalletClient } from "wagmi";
import { toast } from "sonner";
import {
Expand Down Expand Up @@ -68,24 +68,21 @@ export const ProposalsAccordionRow = ({ proposal }: { proposal: Proposal }) => {

const isSupported = supportedTypes.indexOf(proposal.type) !== -1;

const message = useMemo(() => {
const choice = proposal.choices.indexOf(selectedChoice) + 1;
return {
choice,
proposal: proposal.id,
app: "alchemix",
space: "alchemixstakers.eth",
type: proposal.type,
metadata: "{}",
reason: "",
};
}, [proposal, selectedChoice]);

const { mutate: writeVote, isPending } = useMutation({
mutationFn: async () => {
if (!address) throw new Error("Not connected.");
if (!walletClient) throw new Error("No wallet.");

const message = {
choice: proposal.choices.indexOf(selectedChoice) + 1,
proposal: proposal.id,
app: "alchemix",
space: "alchemixstakers.eth",
type: proposal.type,
metadata: "{}",
reason: "",
};

const type2 = message.proposal.startsWith("0x");
const types = type2
? {
Expand Down
16 changes: 8 additions & 8 deletions src/components/transmuters/row/Claim.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { transmuterV2Abi } from "@/abi/transmuterV2";
import { CtaButton } from "@/components/common/CtaButton";
import { TransmuterInput } from "@/components/common/input/TransmuterInput";
import { Button } from "@/components/ui/button";
import { useChain } from "@/hooks/useChain";
import { useWriteContractMutationCallback } from "@/hooks/useWriteContractMutationCallback";
import { QueryKeys } from "@/lib/queries/queriesSchema";
import { Token, Transmuter } from "@/lib/types";
import { isInputZero } from "@/utils/inputNotZero";
import { useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { parseUnits } from "viem";
import {
Expand All @@ -34,7 +34,7 @@ export const Claim = ({

const {
data: claimConfig,
isFetching,
isPending,
error: claimConfigError,
} = useSimulateContract({
address: transmuter.address,
Expand Down Expand Up @@ -64,7 +64,7 @@ export const Claim = ({
}
}, [claimReceipt, queryClient]);

const onCtaClick = useCallback(() => {
const onCtaClick = () => {
if (claimConfigError) {
toast.error("Claim failed", {
description:
Expand All @@ -81,7 +81,7 @@ export const Claim = ({
description: "Unknown error occurred. Please contact Alchemix team.",
});
}
}, [claim, claimConfig, claimConfigError]);
};

return (
<>
Expand All @@ -95,13 +95,13 @@ export const Claim = ({
tokenDecimals={underlyingToken.decimals}
/>

<Button
<CtaButton
variant="outline"
onClick={onCtaClick}
disabled={isFetching || isInputZero(amount)}
disabled={isPending || isInputZero(amount)}
>
Claim
</Button>
</CtaButton>
</>
);
};
37 changes: 21 additions & 16 deletions src/components/transmuters/row/Deposit.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { transmuterV2Abi } from "@/abi/transmuterV2";
import { CtaButton } from "@/components/common/CtaButton";
import { TransmuterInput } from "@/components/common/input/TransmuterInput";
import { Button } from "@/components/ui/button";
import { useAllowance } from "@/hooks/useAllowance";
import { useChain } from "@/hooks/useChain";
import { useWriteContractMutationCallback } from "@/hooks/useWriteContractMutationCallback";
import { QueryKeys } from "@/lib/queries/queriesSchema";
import { Token, Transmuter } from "@/lib/types";
import { isInputZero } from "@/utils/inputNotZero";
import { useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { parseEther } from "viem";
import {
Expand All @@ -33,7 +33,13 @@ export const Deposit = ({

const [depositAmount, setDepositAmount] = useState("");

const { isApprovalNeeded, approve, approveConfig } = useAllowance({
const {
isApprovalNeeded,
approve,
approveConfig,
isPending: isPendingAllowance,
isFetching: isFetchingAllowance,
} = useAllowance({
tokenAddress: syntheticToken.address,
spender: transmuter.address,
amount: depositAmount,
Expand All @@ -42,7 +48,7 @@ export const Deposit = ({

const {
data: depositConfig,
isFetching,
isPending: isPendingDepositConfig,
error: depositConfigError,
} = useSimulateContract({
address: transmuter.address,
Expand Down Expand Up @@ -73,7 +79,7 @@ export const Deposit = ({
}
}, [depositReceipt, queryClient]);

const onCtaClick = useCallback(() => {
const onCtaClick = () => {
if (isApprovalNeeded === true) {
approveConfig && approve(approveConfig.request);
return;
Expand All @@ -96,14 +102,12 @@ export const Deposit = ({
description: "Unkown error. Please contact Alchemix team.",
});
}
}, [
isApprovalNeeded,
depositConfigError,
depositConfig,
approveConfig,
approve,
deposit,
]);
};

const isPending =
isApprovalNeeded === false
? isPendingDepositConfig
: isPendingAllowance || isFetchingAllowance;

return (
<>
Expand All @@ -116,13 +120,14 @@ export const Deposit = ({
type="Balance"
transmuterAddress={transmuter.address}
/>
<Button

<CtaButton
variant="outline"
onClick={onCtaClick}
disabled={isFetching || isInputZero(depositAmount)}
disabled={isPending || isInputZero(depositAmount)}
>
{isApprovalNeeded ? "Approve" : "Deposit"}
</Button>
</CtaButton>
</>
);
};
Loading

0 comments on commit 2a6505e

Please sign in to comment.