From 8a2814fd18c9eb15f1dacbb0d0232bc2bccfe0b1 Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Tue, 6 Aug 2024 15:08:19 -0700 Subject: [PATCH] Handle amount greater than wallet balance --- README.md | 12 ++++++++--- .../app/cow/_components/PoolConfiguration.tsx | 19 ++++++++++++++---- .../nextjs/components/common/TokenField.tsx | 20 ++++++++++--------- packages/nextjs/scaffold.config.ts | 2 +- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 705886a..2d58f8c 100644 --- a/README.md +++ b/README.md @@ -28,19 +28,25 @@ yarn start ## Run on Fork -1. Choose `targetFork` network in `scaffold.config.ts` +1. Add `chains.foundry` as the first item in `scaffoldConfig.targetNetworks` + +``` + targetNetworks: [chains.foundry, chains.sepolia, chains.mainnet, chains.gnosis], +``` + +2. Choose `targetFork` network in `scaffold.config.ts` ``` targetFork: chains.sepolia, ``` -2. Start the Fork using a matching RPC_URL +3. Start the fork using `RPC_URL` that matches chain chosed for `targetFork` ``` anvil --fork-url ``` -3. Start the frontend +4. Start the frontend ``` yarn start diff --git a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx index 65beac3..465606d 100644 --- a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx +++ b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import Link from "next/link"; +import { parseUnits } from "viem"; import { useAccount } from "wagmi"; import { Alert, TransactionButton } from "~~/components/common"; import { TextField, TokenField } from "~~/components/common/"; @@ -9,7 +10,7 @@ import { useCheckIfPoolExists } from "~~/hooks/cow"; import { getPoolUrl } from "~~/hooks/cow/getPoolUrl"; import { usePoolCreationPersistedState } from "~~/hooks/cow/usePoolCreationState"; import { useTargetNetwork } from "~~/hooks/scaffold-eth"; -import { type Token, useFetchTokenList } from "~~/hooks/token"; +import { type Token, useFetchTokenList, useReadToken } from "~~/hooks/token"; export const PoolConfiguration = () => { const { targetNetwork } = useTargetNetwork(); @@ -25,7 +26,14 @@ export const PoolConfiguration = () => { const { data } = useFetchTokenList(); const tokenList = data || []; + const filteredTokenList = tokenList.filter( + token => token.address !== token1?.address && token.address !== token2?.address, + ); const { existingPool } = useCheckIfPoolExists(token1?.address, token2?.address); + const { balance: balance1 } = useReadToken(token1?.address); + const { balance: balance2 } = useReadToken(token2?.address); + const token1RawAmount = parseUnits(token1Amount, token1?.decimals ?? 0); + const token2RawAmount = parseUnits(token2Amount, token2?.decimals ?? 0); const { chain } = useAccount(); @@ -52,6 +60,8 @@ export const PoolConfiguration = () => { } }, [token1, token2]); + const sufficientBalances = balance1 > token1RawAmount && balance2 > token2RawAmount; + const canProceedToCreate = token1 !== null && token2 !== null && @@ -59,7 +69,8 @@ export const PoolConfiguration = () => { token2Amount !== "" && hasAgreedToWarning && poolName !== "" && - poolSymbol !== ""; + poolSymbol !== "" && + sufficientBalances; return ( <> @@ -80,7 +91,7 @@ export const PoolConfiguration = () => { setToken1(selectedToken); }} - tokenOptions={tokenList || []} + tokenOptions={filteredTokenList || []} handleAmountChange={e => setToken1Amount(e.target.value)} /> { setToken2(selectedToken); }} - tokenOptions={tokenList || []} + tokenOptions={filteredTokenList || []} handleAmountChange={e => setToken2Amount(e.target.value)} /> diff --git a/packages/nextjs/components/common/TokenField.tsx b/packages/nextjs/components/common/TokenField.tsx index 0727585..ddbe75a 100644 --- a/packages/nextjs/components/common/TokenField.tsx +++ b/packages/nextjs/components/common/TokenField.tsx @@ -1,7 +1,9 @@ "use client"; import { useState } from "react"; +import { parseUnits } from "viem"; import { ChevronDownIcon } from "@heroicons/react/24/outline"; +import { WalletIcon } from "@heroicons/react/24/outline"; import { TokenImage, TokenSelectModal } from "~~/components/common"; import { type Token } from "~~/hooks/token"; import { useFetchTokenPrices, useReadToken } from "~~/hooks/token"; @@ -23,15 +25,12 @@ export const TokenField = ({ handleAmountChange: (e: React.ChangeEvent) => void; }) => { const [isModalOpen, setIsModalOpen] = useState(false); - - const { balance } = useReadToken(selectedToken?.address); const { data: tokenPrices, isLoading, isError } = useFetchTokenPrices(); + const { balance } = useReadToken(selectedToken?.address); let price; if (tokenPrices && selectedToken?.address) { - price = tokenPrices.find( - token => selectedToken.address && token?.address.toLowerCase() === selectedToken?.address.toLowerCase(), - )?.price; + price = tokenPrices.find(token => token.address.toLowerCase() === selectedToken?.address.toLowerCase())?.price; } if (price && price > 0) { price = (price * Number(value)).toFixed(2); @@ -39,6 +38,8 @@ export const TokenField = ({ price = 0; } + const amountGreaterThanBalance = parseUnits(value, selectedToken?.decimals || 0) > balance; + return ( <>
@@ -49,14 +50,14 @@ export const TokenField = ({ min="0" placeholder="0.0" value={value} - className={`pb-5 text-right text-2xl w-full input rounded-xl bg-base-300 disabled:bg-base-300 disabled:text-base-content h-[77px]`} + className={`pb-5 text-right text-2xl w-full input rounded-xl bg-base-300 disabled:bg-base-300 disabled:text-base-content h-[77px] ${amountGreaterThanBalance ? "ring-1 ring-red-400" : ""}`} />
{selectedToken && ( -
-
Balance: {formatToHuman(balance, selectedToken?.decimals || 0)}
+
+ {formatToHuman(balance, selectedToken?.decimals || 0)} +
{amountGreaterThanBalance && "Insufficient balance"}
)}
diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index daad007..b581f46 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -11,7 +11,7 @@ export type ScaffoldConfig = { const scaffoldConfig = { // The networks on which your DApp is live - targetNetworks: [chains.foundry, chains.sepolia, chains.mainnet, chains.gnosis], + targetNetworks: [chains.sepolia, chains.mainnet, chains.gnosis], // If using chains.foundry as your targetNetwork, you must specify a network to fork targetFork: chains.sepolia,