biuauthui
Version:
Ui modal for biuAuth
125 lines (113 loc) • 4.25 kB
text/typescript
/* eslint-disable consistent-return */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable no-console */
/* eslint-disable camelcase */
import { MaxUint256 } from "@ethersproject/constants";
import { AddressZero } from "@ethersproject/constants/src.ts/addresses";
import { TransactionResponse } from "@ethersproject/providers";
import BigNumber from "bignumber.js";
import { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAccount, usePublicClient, useWaitForTransaction, useWalletClient } from "wagmi";
import { Erc20__factory } from "../payTypes/Erc20__factory";
import { useERC20 } from "./useContract";
import useNotification from "./useNotification";
import useTokenAllowance from "./useTokenAllowance";
export enum ApprovalState {
UNKNOWN,
NOT_APPROVED,
PENDING,
APPROVED,
}
export function useApproveCallback(
amount: BigNumber | null,
tokenAddress: string,
spender?: string
): [ApprovalState, () => Promise<TransactionResponse | undefined>, boolean, boolean] {
const { address } = useAccount();
const { t } = useTranslation();
const [approveHash, setApproveHash] = useState("");
const [approveLoading, setApproveLoading] = useState(false);
const { failedTip, successTip } = useNotification();
const { data: walletClient } = useWalletClient();
const publicClient = usePublicClient();
const { isLoading } = useWaitForTransaction({
hash: approveHash as `0x${string}`,
onSuccess() {
if (approveHash) {
successTip("Approve Success!");
setApproveHash("");
setApproveLoading(false);
}
},
onError() {
if (approveHash) {
failedTip("Approve Failed!");
setApproveHash("");
setApproveLoading(false);
}
},
});
const currentAllowance = useTokenAllowance({ amount, address: tokenAddress }, address ?? undefined, spender, approveHash);
// check the current approval status
const approvalState: ApprovalState = useMemo(() => {
if (!spender) return ApprovalState.UNKNOWN;
if (tokenAddress === AddressZero) return ApprovalState.APPROVED;
if (!currentAllowance) return ApprovalState.UNKNOWN;
return currentAllowance?.isLessThan(amount || new BigNumber(0)) ? ApprovalState.NOT_APPROVED : ApprovalState.APPROVED;
}, [amount, tokenAddress, currentAllowance, spender, address, approveHash]);
const tokenContract = useERC20(tokenAddress);
const approve = useCallback(async (): Promise<any> => {
setApproveLoading(true);
if (approvalState !== ApprovalState.NOT_APPROVED) {
failedTip(t("Approve was called unnecessarily"));
console.error("approve was called unnecessarily");
setApproveLoading(false);
return undefined;
}
if (!tokenAddress) {
failedTip(t("No token"));
console.error("no token");
setApproveLoading(false);
return undefined;
}
if (!tokenContract) {
failedTip(t("Cannot find contract of the token %tokenAddress%", { tokenAddress }));
console.error("tokenContract is null");
setApproveLoading(false);
return undefined;
}
if (!amount) {
failedTip(t("Missing amount to approve"));
console.error("missing amount to approve");
setApproveLoading(false);
return undefined;
}
if (!spender) {
failedTip(t("No spender"));
console.error("no spender");
setApproveLoading(false);
return undefined;
}
const useExact = false;
try {
const { request } = await publicClient.simulateContract({
abi: Erc20__factory.abi,
account: address as `0x${string}`,
address: tokenContract.address as `0x${string}`,
functionName: "approve",
args: [spender, useExact ? amount.toString() : MaxUint256],
});
// @ts-ignore
const hash = await walletClient?.writeContract(request);
if (hash) {
setApproveHash(hash);
return hash;
}
setApproveLoading(false);
} catch (error) {
setApproveLoading(false);
}
}, [approvalState, tokenAddress, tokenContract, amount, spender, publicClient, walletClient]);
return [approvalState, approve, approveLoading, isLoading];
}