Skip to content

Commit

Permalink
Handle amount greater than wallet balance
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Aug 6, 2024
1 parent 19c7eed commit 8a2814f
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 17 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <RPC_URL>
```

3. Start the frontend
4. Start the frontend

```
yarn start
Expand Down
19 changes: 15 additions & 4 deletions packages/nextjs/app/cow/_components/PoolConfiguration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

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/";
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();
Expand All @@ -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();

Expand All @@ -52,14 +60,17 @@ export const PoolConfiguration = () => {
}
}, [token1, token2]);

const sufficientBalances = balance1 > token1RawAmount && balance2 > token2RawAmount;

const canProceedToCreate =
token1 !== null &&
token2 !== null &&
token1Amount !== "" &&
token2Amount !== "" &&
hasAgreedToWarning &&
poolName !== "" &&
poolSymbol !== "";
poolSymbol !== "" &&
sufficientBalances;

return (
<>
Expand All @@ -80,7 +91,7 @@ export const PoolConfiguration = () => {

setToken1(selectedToken);
}}
tokenOptions={tokenList || []}
tokenOptions={filteredTokenList || []}
handleAmountChange={e => setToken1Amount(e.target.value)}
/>
<TokenField
Expand All @@ -93,7 +104,7 @@ export const PoolConfiguration = () => {

setToken2(selectedToken);
}}
tokenOptions={tokenList || []}
tokenOptions={filteredTokenList || []}
handleAmountChange={e => setToken2Amount(e.target.value)}
/>
</div>
Expand Down
20 changes: 11 additions & 9 deletions packages/nextjs/components/common/TokenField.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -23,22 +25,21 @@ export const TokenField = ({
handleAmountChange: (e: React.ChangeEvent<HTMLInputElement>) => 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);
} else {
price = 0;
}

const amountGreaterThanBalance = parseUnits(value, selectedToken?.decimals || 0) > balance;

return (
<>
<div className="relative w-full">
Expand All @@ -49,23 +50,24 @@ 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" : ""}`}
/>
<div className="absolute top-0 left-0 ">
<div className="p-2.5">
<button
disabled={isDisabled}
onClick={() => setIsModalOpen(true)}
className="px-3 py-2 bg-secondary shadow-md disabled:text-base-content text-lg font-bold disabled:bg-base-100 rounded-lg flex justify-between items-center gap-2 mb-[1.5px]"
className="px-3 py-1.5 bg-secondary shadow-md disabled:text-base-content text-lg font-bold disabled:bg-base-100 rounded-lg flex justify-between items-center gap-2 mb-[1.5px]"
>
{selectedToken?.symbol && selectedToken.logoURI !== "" && <TokenImage token={selectedToken} />}
{selectedToken?.symbol ? selectedToken.symbol : "Select Token"}{" "}
{!isDisabled && <ChevronDownIcon className="w-4 h-4 mt-0.5" />}
</button>

{selectedToken && (
<div className="ml-1 text-neutral-400 text-sm flex gap-5 ">
<div>Balance: {formatToHuman(balance, selectedToken?.decimals || 0)}</div>
<div className={`text-neutral-400 text-sm flex gap-1 ${amountGreaterThanBalance ? "text-red-400" : ""}`}>
<WalletIcon className="h-4 w-4 mt-0.5" /> {formatToHuman(balance, selectedToken?.decimals || 0)}
<div>{amountGreaterThanBalance && "Insufficient balance"}</div>
</div>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/scaffold.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 8a2814f

Please sign in to comment.