Skip to content

Commit

Permalink
StakableBar Component (#32)
Browse files Browse the repository at this point in the history
* StakableBar setup

* added checkered-blue color to css

* put balances into a hook, fixed StakableBar

* removed comment

* removed unused variable

* refactored useTokenBalances hook

* refactored variables from hooks

* fixed naminig of astBalance

---------

Co-authored-by: greypixel <[email protected]>
  • Loading branch information
mikestarrdev and gpxl-dev authored Aug 23, 2023
1 parent 1cf449e commit 9c93137
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 89 deletions.
23 changes: 4 additions & 19 deletions src/features/staking/StakeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { format } from "@greypixel_/nicenumbers";
import { useRef } from "react";
import { twJoin } from "tailwind-merge";
import { useAccount, useBalance, useNetwork } from "wagmi";
import { ContractTypes } from "../../config/ContractAddresses";
import { useContractAddresses } from "../../config/hooks/useContractAddress";
import { useAccount, useNetwork } from "wagmi";
import { Button } from "../common/Button";
import StakingModal from "./StakingModal";
import { useTokenBalances } from "../../hooks/useTokenBalances";

export const StakeButton = ({}: {}) => {
const { address, isConnected } = useAccount();
const { chain } = useNetwork();
const [stakedAst] = useContractAddresses([ContractTypes.AirSwapStaking], {
defaultChainId: 1,
useDefaultAsFallback: true,
});

const stakingModalRef = useRef<HTMLDialogElement | null>(null);

Expand All @@ -23,21 +17,12 @@ export const StakeButton = ({}: {}) => {
}
};

const { data: sAstBalance } = useBalance({
token: stakedAst.address,
address,
staleTime: 300_000, // 5 minutes,
cacheTime: Infinity,
chainId: stakedAst.chainId,
});

const formattedBalance =
format(sAstBalance?.value, { tokenDecimals: 4 }) + " sAST";
const { sAstBalanceFormatted: sAstBalance } = useTokenBalances();

return (
<>
<div className={twJoin("flex flex-row items-center gap-4 py-3")}>
<span className="hidden xs:flex font-medium">{formattedBalance}</span>
<span className="hidden font-medium xs:flex">{`${sAstBalance} sAST`}</span>
<Button
className="-my-3 -mr-5 bg-accent-blue font-bold uppercase"
onClick={handleOpenStakingModal}
Expand Down
34 changes: 7 additions & 27 deletions src/features/staking/StakingModal.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { format } from "@greypixel_/nicenumbers";
import { FC, RefObject, useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { VscChromeClose } from "react-icons/vsc";
import { twJoin } from "tailwind-merge";
import {
useBalance,
useContractRead,
useContractWrite,
usePrepareContractWrite,
Expand All @@ -20,8 +18,10 @@ import ManageStake from "./subcomponents/ManageStake";
import PendingTransaction from "./subcomponents/PendingTransaction";
import TransactionFailed from "./subcomponents/TransactionFailed";
import { StatusStaking } from "./types/StakingTypes";
import { buttonStatusText } from "./uils/buttonStatusText";
import { modalHeadline } from "./uils/headline";
import { buttonStatusText } from "./utils/buttonStatusText";
import { modalHeadline } from "./utils/headline";
import { useTokenBalances } from "../../hooks/useTokenBalances";
import { format } from "@greypixel_/nicenumbers";

interface StakingModalInterface {
stakingModalRef: RefObject<HTMLDialogElement>;
Expand All @@ -45,6 +45,8 @@ const StakingModal: FC<StakingModalInterface> = ({
} = useForm<{ stakingAmount: number }>();
const stakingAmount = watch("stakingAmount") || "0";

const { astBalanceFormatted: astBalance } = useTokenBalances();

const [AirSwapToken] = useContractAddresses([ContractTypes.AirSwapToken], {
defaultChainId: 1,
useDefaultAsFallback: true,
Expand All @@ -57,20 +59,6 @@ const StakingModal: FC<StakingModalInterface> = ({
},
);

const { data: astBalanceData } = useBalance({
address,
token: AirSwapToken.address,
staleTime: 300_000, // 5 minutes,
cacheTime: Infinity,
});

const { data: sAstBalanceData } = useBalance({
address,
token: AirSwapStaking.address,
staleTime: 300_000, // 5 minutes,
cacheTime: Infinity,
});

const { data: astAllowanceData, refetch: refetchAllowance } = useContractRead(
{
address: AirSwapToken.address,
Expand Down Expand Up @@ -110,9 +98,6 @@ const StakingModal: FC<StakingModalInterface> = ({
},
});

// convert unformatted balances
const astBalance = format(astBalanceData?.value, { tokenDecimals: 4 });
const sAstBalance = format(sAstBalanceData?.value, { tokenDecimals: 4 });
const astAllowance = format(astAllowanceData, { tokenDecimals: 4 });

const needsApproval = +astBalance < +stakingAmount;
Expand Down Expand Up @@ -221,12 +206,7 @@ const StakingModal: FC<StakingModalInterface> = ({
</div>
</div>
{statusStaking === "unapproved" || statusStaking === "readyToStake" ? (
<ManageStake
sAstBalance={sAstBalance}
astBalance={astBalance}
register={register}
setValue={setValue}
/>
<ManageStake register={register} setValue={setValue} />
) : null}

{statusStaking === "approving" || statusStaking === "staking" ? (
Expand Down
52 changes: 9 additions & 43 deletions src/features/staking/subcomponents/ManageStake.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,25 @@ import { twJoin } from "tailwind-merge";
import AirSwapLogo from "../../../assets/airswap-logo.svg";
import { Button } from "../../common/Button";
import LineBreak from "../../common/LineBreak";
import { StakeInput } from "../types/StakingTypes";
import { StakableBar } from "./StakableBar";
import { useTokenBalances } from "../../../hooks/useTokenBalances";

interface ManageStakeProps {
sAstBalance: string;
astBalance: string;
register: UseFormRegister<StakeInput>;
setValue: UseFormSetValue<StakeInput>;
register: UseFormRegister<{ stakingAmount: number }>;
setValue: UseFormSetValue<{ stakingAmount: number }>;
}

const ManageStake: FC<ManageStakeProps> = ({
sAstBalance,
astBalance,
register,
setValue,
}) => {
const ManageStake: FC<ManageStakeProps> = ({ register, setValue }) => {
const [stakeOrUnstake, setStakeOrUnstake] = useState<"stake" | "unstake">(
"stake",
);

const { astBalanceFormatted: astBalance } = useTokenBalances();

return (
<>
<LineBreak />
<div className="flex flex-col space-y-3">
{stakeOrUnstake === "stake" && (
<>
<div className="mt-6">
{/* TODO: add progress bar here with AST balance */}
(PROGRESS BAR)
</div>
<div className="flex flex-row">
<span className="mr-2">{sAstBalance}</span>unstakable
</div>
<div className="flex flex-row">
<span className="mr-2">{sAstBalance}</span>staked
</div>
<div className="flex flex-row">
<span className="mr-2">{astBalance}</span>stakable
</div>
</>
)}
{stakeOrUnstake === "unstake" && (
<>
<div className="mt-6">
{/* TODO: add progress bar here with AST balance */}
(PROGRESS BAR)
</div>
<div className="flex flex-row">
<span className="mr-2">{astBalance}</span>
<span>stakable</span>
</div>
</>
)}
</div>
<StakableBar mode={stakeOrUnstake} />
<LineBreak />
<div className="font-lg pointer-cursor mt-6 rounded-md font-semibold">
<Button
Expand Down Expand Up @@ -95,7 +61,7 @@ const ManageStake: FC<ManageStakeProps> = ({
<div className="flex flex-col text-right uppercase">
<div>
<input
placeholder={astBalance}
placeholder={astBalance.toString()}
{...register("stakingAmount", {
required: true,
min: 0,
Expand Down
84 changes: 84 additions & 0 deletions src/features/staking/subcomponents/StakableBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { FC } from "react";
import { calculateTokenProportions } from "../utils/calculateTokenProportions";
import { twJoin } from "tailwind-merge";
import { BsCircleFill } from "react-icons/bs";
import "../../../index.css";
import { useTokenBalances } from "../../../hooks/useTokenBalances";

interface StakableBarProps {
mode: "stake" | "unstake";
}

/**
*
* @param unstakable - balance of sAST available to unstake
* @param staked - total amount of SAST
* @param stakable - amount of available AST
* @returns
*/
export const StakableBar: FC<StakableBarProps> = ({ mode }) => {
const {
ustakableSAstBalanceFormatted: unstakable,
sAstBalanceFormatted: staked,
astBalanceFormatted: stakable,
} = useTokenBalances();

const { unstakablePercent, stakedPercent, stakablePercent } =
calculateTokenProportions({
unstakable: +unstakable,
staked: +staked,
stakable: +stakable,
});

return (
<div className="flex w-full flex-col space-y-3">
{mode === "stake" && (
<>
<div className="m-auto mt-6 flex h-3 w-full flex-row rounded-full">
<div
style={{ flexBasis: `${unstakablePercent}%` }}
className="checkered-blue rounded-l-full"
></div>
<div
style={{ flexBasis: `${stakedPercent}%` }}
className="bg-accent-blue"
></div>
<div
style={{ flexBasis: `${stakablePercent}%` }}
className="rounded-r-full bg-accent-gray"
></div>
</div>
<div className="flex flex-row items-center">
<div className="checkered-blue rounded-full">
<BsCircleFill className="text-transparent" />
</div>
<span className="mx-2">{unstakable}</span>
unstakable
</div>
<div className="flex flex-row items-center">
<BsCircleFill className="text-blue-500" />
<span className="mx-2">{staked}</span>staked
</div>
<div className="flex flex-row items-center">
<BsCircleFill className="text-accent-gray" />
<span className="mx-2">{stakable}</span>stakable
</div>
</>
)}
{mode === "unstake" && (
<>
<div className="m-auto mt-6 flex h-3 w-[100%] flex-row rounded-full">
<div
className={twJoin(["w-full rounded-full bg-accent-gray"])}
// style={{ flexBasis: `${stakablePercent}%` }}
></div>
</div>
<div className="flex flex-row items-center">
<BsCircleFill className="text-accent-gray" />
<span className="mx-2">{stakable}</span>stakable
</div>
</>
)}
</div>
);
};
23 changes: 23 additions & 0 deletions src/features/staking/utils/calculateTokenProportions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
type PercentObject = {
unstakablePercent: number;
stakedPercent: number;
stakablePercent: number;
};

export const calculateTokenProportions = ({
unstakable,
staked,
stakable,
}: {
unstakable: number;
staked: number;
stakable: number;
}): PercentObject => {
const totalBalance = unstakable + staked + stakable;

return {
unstakablePercent: (unstakable / totalBalance) * 100,
stakedPercent: (staked / totalBalance) * 100,
stakablePercent: (stakable / totalBalance) * 100,
};
};
File renamed without changes.
72 changes: 72 additions & 0 deletions src/hooks/useTokenBalances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useAccount, useContractReads } from "wagmi";
import { useContractAddresses } from "../config/hooks/useContractAddress";
import { ContractTypes } from "../config/ContractAddresses";
import { stakingAbi } from "../contracts/stakingAbi";
import { astAbi } from "../contracts/astAbi";
import { format } from "@greypixel_/nicenumbers";

export const useTokenBalances = () => {
const { address, isConnected } = useAccount();
const [sAstAddress] = useContractAddresses([ContractTypes.AirSwapStaking], {
defaultChainId: 1,
useDefaultAsFallback: true,
});

const [AirSwapToken] = useContractAddresses([ContractTypes.AirSwapToken], {
defaultChainId: 1,
useDefaultAsFallback: true,
});
const airSwapTokenContract = {
address: AirSwapToken.address,
chain: AirSwapToken.chainId,
abi: astAbi,
};

const airSwapStakingContract = {
address: sAstAddress.address,
chain: sAstAddress.chainId,
abi: stakingAbi,
};

const { data } = useContractReads({
contracts: [
{
...airSwapStakingContract,
functionName: "available",
args: [address as `0x${string}`],
},
{
...airSwapStakingContract,
functionName: "balanceOf",
args: [address as `0x${string}`],
},
{
...airSwapTokenContract,
functionName: "balanceOf",
args: [address as `0x${string}`],
},
],
enabled: !!isConnected,
});

const unstakableSAstBalanceRaw = (data && data[0].result) || 0;
const sAstBalanceRaw = (data && data[1].result) || 0;
const astBalanceRaw = (data && data[2].result) || 0;

const ustakableSAstBalanceFormatted =
format((data && data[0].result) || 0, { tokenDecimals: 4 }) || 0;
const sAstBalanceFormatted =
format(data && data[1].result, { tokenDecimals: 4 }) || 0;

const astBalanceFormatted =
format(data && data[2].result, { tokenDecimals: 4 }) || 0;

return {
unstakableSAstBalanceRaw,
ustakableSAstBalanceFormatted,
sAstBalanceRaw,
sAstBalanceFormatted,
astBalanceRaw,
astBalanceFormatted,
};
};
Loading

0 comments on commit 9c93137

Please sign in to comment.