From 8c0c6de40d86a6d79b5c3a8da8bdca9cc695fe63 Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Thu, 8 Aug 2024 12:31:17 -0700 Subject: [PATCH 1/7] Persist pool creation state in local storage --- .../app/cow/_components/PoolConfiguration.tsx | 41 ++++++++++--------- .../app/cow/_components/PoolCreation.tsx | 39 +++++++++++++++--- packages/nextjs/app/cow/page.tsx | 29 ++++++++++++- packages/nextjs/hooks/common/index.ts | 2 +- .../nextjs/hooks/common/useLocalStorage.ts | 33 --------------- packages/nextjs/hooks/common/useStore.ts | 17 ++++++++ packages/nextjs/hooks/cow/types.ts | 6 +-- .../nextjs/hooks/cow/usePoolCreationState.ts | 27 ++++++++---- packages/nextjs/hooks/cow/useReadPool.ts | 4 +- packages/nextjs/scaffold.config.ts | 2 +- 10 files changed, 125 insertions(+), 75 deletions(-) delete mode 100644 packages/nextjs/hooks/common/useLocalStorage.ts create mode 100644 packages/nextjs/hooks/common/useStore.ts diff --git a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx index a4bb70b..6d1e7f8 100644 --- a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx +++ b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx @@ -6,6 +6,7 @@ import { parseUnits } from "viem"; import { useAccount } from "wagmi"; import { Alert, TransactionButton } from "~~/components/common"; import { TextField, TokenField } from "~~/components/common/"; +import { useStore } from "~~/hooks/common"; import { useCheckIfPoolExists } from "~~/hooks/cow"; import { getPoolUrl } from "~~/hooks/cow/getPoolUrl"; import { usePoolCreationPersistedState } from "~~/hooks/cow/usePoolCreationState"; @@ -23,7 +24,7 @@ export const PoolConfiguration = () => { const [hasAgreedToWarning, setAgreedToWarning] = useState(false); const [poolName, setPoolName] = useState(""); const [poolSymbol, setPoolSymbol] = useState(""); - const setPersistedState = usePoolCreationPersistedState(state => state.setPersistedState); + const setPersistedState = useStore(usePoolCreationPersistedState, state => state.setPersistedState); const { data } = useFetchTokenList(); const tokenList = data || []; @@ -167,24 +168,26 @@ export const PoolConfiguration = () => { )}
- { - setPersistedState({ - chainId: chain?.id || 0, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - token1: token1!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - token2: token2!, - token1Amount, - token2Amount, - poolName, - poolSymbol, - }); - }} - /> + {setPersistedState && ( + { + setPersistedState({ + chainId: chain?.id || 0, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + token1: token1!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + token2: token2!, + token1Amount, + token2Amount, + poolName, + poolSymbol, + }); + }} + /> + )}
); diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index ce67ab8..9138541 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -1,9 +1,16 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { StepsDisplay } from "./StepsDisplay"; import { Address, parseUnits } from "viem"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import { Alert, ExternalLinkButton, TextField, TokenField, TransactionButton } from "~~/components/common/"; -import { useBindPool, useCreatePool, useFinalizePool, useReadPool, useSetSwapFee } from "~~/hooks/cow/"; +import { + useBindPool, + useCreatePool, + useFinalizePool, + useNewPoolEvents, + useReadPool, + useSetSwapFee, +} from "~~/hooks/cow/"; import { getPoolUrl } from "~~/hooks/cow/getPoolUrl"; import { PoolCreationState } from "~~/hooks/cow/usePoolCreationState"; import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; @@ -21,6 +28,7 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => const [currentStep, setCurrentStep] = useState(1); const [userPoolAddress, setUserPoolAddress] = useState
(); + useNewPoolEvents(setUserPoolAddress); const { targetNetwork } = useTargetNetwork(); const isWrongNetwork = targetNetwork.id !== state.chainId; const { data: pool, refetch: refetchPool } = useReadPool(userPoolAddress); @@ -80,7 +88,7 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => const handleBindTokens = async () => { if (!pool) throw new Error("Required value is undefined in handleBindTokens"); - const poolTokens = pool.getCurrentTokens.map(token => token.toLowerCase()); + const poolTokens = pool.currentTokens.map(token => token.toLowerCase()); // If not already bound, bind the token const txs = []; if (!poolTokens.includes(state.token1.address.toLowerCase())) { @@ -117,6 +125,24 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => }); }; + useEffect(() => { + if (pool && pool.numTokens < 2n) { + if (allowance1 < token1RawAmount || allowance2 < token2RawAmount) { + setCurrentStep(2); + } else { + setCurrentStep(3); + } + } + if (pool && pool.numTokens === 2n && !pool.isFinalized) { + if (pool.swapFee !== pool.MAX_FEE) { + setCurrentStep(4); + } else { + setCurrentStep(5); + } + } + if (pool && pool.isFinalized) setCurrentStep(6); + }, [pool]); + return ( <>
@@ -137,9 +163,12 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => {pool && currentStep === 6 && ( <> -
- {pool.address} +
+
+ {pool.address} +
+ You CoW AMM pool was successfully created! Because of caching, it may take a few minutes for the pool to appear in the Balancer app diff --git a/packages/nextjs/app/cow/page.tsx b/packages/nextjs/app/cow/page.tsx index 68b2dec..c0a2d73 100644 --- a/packages/nextjs/app/cow/page.tsx +++ b/packages/nextjs/app/cow/page.tsx @@ -1,22 +1,34 @@ "use client"; +import { useEffect, useState } from "react"; import { PoolCreation } from "./_components"; import type { NextPage } from "next"; import { PoolConfiguration } from "~~/app/cow/_components/PoolConfiguration"; import { usePoolCreationPersistedState } from "~~/hooks/cow/usePoolCreationState"; const CowAmm: NextPage = () => { + const [isMounted, setIsMounted] = useState(false); + const persistedState = usePoolCreationPersistedState(state => state.state); const clearPersistedState = usePoolCreationPersistedState(state => state.clearPersistedState); + useEffect(() => { + setIsMounted(true); + }, []); + return (

Create a CoW AMM Pool

- {!persistedState && } - {persistedState && } + {!isMounted ? ( + + ) : !persistedState ? ( + + ) : ( + persistedState && + )}
@@ -25,3 +37,16 @@ const CowAmm: NextPage = () => { }; export default CowAmm; + +const CowLoadingSkeleton = () => { + return ( + <> +
+
+
+
+
+
+ + ); +}; diff --git a/packages/nextjs/hooks/common/index.ts b/packages/nextjs/hooks/common/index.ts index 709aadd..4f75998 100644 --- a/packages/nextjs/hooks/common/index.ts +++ b/packages/nextjs/hooks/common/index.ts @@ -1 +1 @@ -export * from "./useLocalStorage"; +export * from "./useStore"; diff --git a/packages/nextjs/hooks/common/useLocalStorage.ts b/packages/nextjs/hooks/common/useLocalStorage.ts deleted file mode 100644 index 77d3a29..0000000 --- a/packages/nextjs/hooks/common/useLocalStorage.ts +++ /dev/null @@ -1,33 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; - -export const useLocalStorage = (key: string, defaultValue: T) => { - const [state, setState] = useState(defaultValue); - const [hasMounted, setHasMounted] = useState(false); - - useEffect(() => { - setHasMounted(true); - }, []); - - useEffect(() => { - if (hasMounted) { - const savedValue = window.localStorage.getItem(key); - if (savedValue) { - try { - setState(JSON.parse(savedValue)); - }catch {} - } - } - }, [hasMounted, key]); - - useEffect(() => { - if (hasMounted) { - if (state) { - window.localStorage.setItem(key, JSON.stringify(state)); - } - } - }, [key, state, hasMounted]); - - return [state, setState] as const; -}; diff --git a/packages/nextjs/hooks/common/useStore.ts b/packages/nextjs/hooks/common/useStore.ts new file mode 100644 index 0000000..ceeeb9d --- /dev/null +++ b/packages/nextjs/hooks/common/useStore.ts @@ -0,0 +1,17 @@ +// useStore.ts +import { useEffect, useState } from "react"; + +/** + * A custom hook for using Zustand store in Next.js. + * https://docs.pmnd.rs/zustand/integrations/persisting-store-data#usage-in-next.js + */ +export const useStore = (store: (callback: (state: T) => unknown) => unknown, callback: (state: T) => F) => { + const result = store(callback) as F; + const [data, setData] = useState(); + + useEffect(() => { + setData(result); + }, [result]); + + return data; +}; diff --git a/packages/nextjs/hooks/cow/types.ts b/packages/nextjs/hooks/cow/types.ts index 8ec2822..a6f453c 100644 --- a/packages/nextjs/hooks/cow/types.ts +++ b/packages/nextjs/hooks/cow/types.ts @@ -3,9 +3,9 @@ import { type Address } from "viem"; export type BCowPool = { address: Address; isFinalized: boolean; - getNumTokens: bigint; - getCurrentTokens: Address[]; - getSwapFee: bigint; + numTokens: bigint; + currentTokens: Address[]; + swapFee: bigint; MAX_FEE: bigint; }; diff --git a/packages/nextjs/hooks/cow/usePoolCreationState.ts b/packages/nextjs/hooks/cow/usePoolCreationState.ts index c6165af..99adbcd 100644 --- a/packages/nextjs/hooks/cow/usePoolCreationState.ts +++ b/packages/nextjs/hooks/cow/usePoolCreationState.ts @@ -1,4 +1,5 @@ import { create } from "zustand"; +import { persist } from "zustand/middleware"; import { Token } from "~~/hooks/token"; export interface PoolCreationState { @@ -11,12 +12,20 @@ export interface PoolCreationState { poolSymbol: string; } -export const usePoolCreationPersistedState = create<{ - state: PoolCreationState | null; - setPersistedState: (state: PoolCreationState) => void; - clearPersistedState: () => void; -}>(set => ({ - state: null, - setPersistedState: (state: PoolCreationState) => set({ state }), - clearPersistedState: () => set({ state: null }), -})); +export const usePoolCreationPersistedState = create( + persist<{ + state: PoolCreationState | null; + setPersistedState: (state: PoolCreationState) => void; + clearPersistedState: () => void; + }>( + set => ({ + state: null, + setPersistedState: (state: PoolCreationState) => set({ state }), + clearPersistedState: () => set({ state: null }), + }), + { + name: "pool-creation-state", + getStorage: () => localStorage, + }, + ), +); diff --git a/packages/nextjs/hooks/cow/useReadPool.ts b/packages/nextjs/hooks/cow/useReadPool.ts index a82b724..62a6043 100644 --- a/packages/nextjs/hooks/cow/useReadPool.ts +++ b/packages/nextjs/hooks/cow/useReadPool.ts @@ -15,7 +15,7 @@ export const useReadPool = (address: Address | undefined) => { if (!client) throw new Error("Wagmi public client is undefined"); if (!address) throw new Error("Pool address is undefined"); - const [isFinalized, getNumTokens, getCurrentTokens, getSwapFee, MAX_FEE] = await Promise.all([ + const [isFinalized, numTokens, currentTokens, swapFee, MAX_FEE] = await Promise.all([ client.readContract({ abi, address, @@ -43,7 +43,7 @@ export const useReadPool = (address: Address | undefined) => { }), ]); - return { address, isFinalized, getNumTokens, getCurrentTokens, getSwapFee, MAX_FEE }; + return { address, isFinalized, numTokens, currentTokens, swapFee, MAX_FEE }; }, enabled: !!address, }); diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index b581f46..daad007 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.sepolia, chains.mainnet, chains.gnosis], + targetNetworks: [chains.foundry, chains.sepolia, chains.mainnet, chains.gnosis], // If using chains.foundry as your targetNetwork, you must specify a network to fork targetFork: chains.sepolia, From 7e3348aecbaa981a8e600e8334fe930b3ab23161 Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Thu, 8 Aug 2024 12:32:27 -0700 Subject: [PATCH 2/7] Fix linter --- packages/nextjs/app/cow/_components/PoolCreation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index 9138541..83efb9b 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -141,7 +141,7 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => } } if (pool && pool.isFinalized) setCurrentStep(6); - }, [pool]); + }, [pool, allowance1, allowance2, token1RawAmount, token2RawAmount]); return ( <> From 368728605c8c0b9545f1cbadb888b37e2dd4095c Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Thu, 8 Aug 2024 13:43:08 -0700 Subject: [PATCH 3/7] Track step number using zustand store --- .../app/cow/_components/PoolConfiguration.tsx | 42 ++++++------ .../app/cow/_components/PoolCreation.tsx | 66 ++++++++++--------- .../nextjs/components/common/TextField.tsx | 2 +- .../nextjs/components/common/TokenField.tsx | 2 +- packages/nextjs/hooks/common/index.ts | 1 - packages/nextjs/hooks/common/useStore.ts | 17 ----- packages/nextjs/hooks/cow/useBindPool.ts | 2 +- packages/nextjs/hooks/cow/useFinalizePool.ts | 5 +- .../nextjs/hooks/cow/usePoolCreationState.ts | 3 +- .../nextjs/hooks/token/useApproveToken.ts | 2 +- packages/nextjs/scaffold.config.ts | 2 +- 11 files changed, 65 insertions(+), 79 deletions(-) delete mode 100644 packages/nextjs/hooks/common/index.ts delete mode 100644 packages/nextjs/hooks/common/useStore.ts diff --git a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx index 6d1e7f8..4845020 100644 --- a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx +++ b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx @@ -6,7 +6,6 @@ import { parseUnits } from "viem"; import { useAccount } from "wagmi"; import { Alert, TransactionButton } from "~~/components/common"; import { TextField, TokenField } from "~~/components/common/"; -import { useStore } from "~~/hooks/common"; import { useCheckIfPoolExists } from "~~/hooks/cow"; import { getPoolUrl } from "~~/hooks/cow/getPoolUrl"; import { usePoolCreationPersistedState } from "~~/hooks/cow/usePoolCreationState"; @@ -24,7 +23,7 @@ export const PoolConfiguration = () => { const [hasAgreedToWarning, setAgreedToWarning] = useState(false); const [poolName, setPoolName] = useState(""); const [poolSymbol, setPoolSymbol] = useState(""); - const setPersistedState = useStore(usePoolCreationPersistedState, state => state.setPersistedState); + const setPersistedState = usePoolCreationPersistedState(state => state.setPersistedState); const { data } = useFetchTokenList(); const tokenList = data || []; @@ -168,26 +167,25 @@ export const PoolConfiguration = () => { )}
- {setPersistedState && ( - { - setPersistedState({ - chainId: chain?.id || 0, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - token1: token1!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - token2: token2!, - token1Amount, - token2Amount, - poolName, - poolSymbol, - }); - }} - /> - )} + { + setPersistedState({ + chainId: chain?.id || 0, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + token1: token1!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + token2: token2!, + token1Amount, + token2Amount, + poolName, + poolSymbol, + step: 1, + }); + }} + />
); diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index 83efb9b..a31336e 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -13,6 +13,7 @@ import { } from "~~/hooks/cow/"; import { getPoolUrl } from "~~/hooks/cow/getPoolUrl"; import { PoolCreationState } from "~~/hooks/cow/usePoolCreationState"; +import { usePoolCreationPersistedState } from "~~/hooks/cow/usePoolCreationState"; import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; import { useApproveToken, useReadToken } from "~~/hooks/token"; import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth"; @@ -25,7 +26,7 @@ interface ManagePoolCreationProps { export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => { const token1RawAmount = parseUnits(state.token1Amount, state.token1.decimals); const token2RawAmount = parseUnits(state.token2Amount, state.token2.decimals); - const [currentStep, setCurrentStep] = useState(1); + const [userPoolAddress, setUserPoolAddress] = useState
(); useNewPoolEvents(setUserPoolAddress); @@ -52,24 +53,27 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => const { mutate: finalizePool, isPending: isFinalizePending, error: finalizeError } = useFinalizePool(); const txError = createPoolError || approveError || bindError || setSwapFeeError || finalizeError; + const setPersistedState = usePoolCreationPersistedState(state => state.setPersistedState); + const handleCreatePool = () => { - const payload = { name: state.poolName, symbol: state.poolSymbol }; - createPool(payload, { - onSuccess: newPoolAddress => { - setUserPoolAddress(newPoolAddress); - setCurrentStep(2); + createPool( + { name: state.poolName, symbol: state.poolSymbol }, + { + onSuccess: newPoolAddress => { + setUserPoolAddress(newPoolAddress); + setPersistedState({ ...state, step: 2 }); + }, }, - }); + ); }; const handleApproveTokens = async () => { - if (!pool) throw new Error("Pool address is required to approve tokens"); const txs = []; if (token1RawAmount > allowance1) { txs.push( approve({ token: state.token1.address, - spender: pool.address, + spender: pool?.address, rawAmount: token1RawAmount, }), ); @@ -78,69 +82,69 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => txs.push( approve({ token: state.token2.address, - spender: pool.address, + spender: pool?.address, rawAmount: token2RawAmount, }), ); const results = await Promise.all(txs); - if (results.every(result => result === "success")) setCurrentStep(3); + if (results.every(result => result === "success")) setPersistedState({ ...state, step: 3 }); }; const handleBindTokens = async () => { - if (!pool) throw new Error("Required value is undefined in handleBindTokens"); - const poolTokens = pool.currentTokens.map(token => token.toLowerCase()); - // If not already bound, bind the token const txs = []; - if (!poolTokens.includes(state.token1.address.toLowerCase())) { + // If not already bound, bind the token + const poolTokens = pool?.currentTokens.map(token => token.toLowerCase()); + if (!poolTokens?.includes(state.token1.address.toLowerCase())) { txs.push( bind({ - pool: pool.address, + pool: pool?.address, token: state.token1.address, rawAmount: token1RawAmount, }), ); } - if (!poolTokens.includes(state.token2.address.toLowerCase())) { + if (!poolTokens?.includes(state.token2.address.toLowerCase())) { txs.push( bind({ - pool: pool.address, + pool: pool?.address, token: state.token2.address, rawAmount: token2RawAmount, }), ); } const results = await Promise.all(txs); - if (results.every(result => result === "success")) setCurrentStep(4); + if (results.every(result => result === "success")) setPersistedState({ ...state, step: 4 }); }; const handleSetSwapFee = async () => { if (!pool) throw new Error("Pool is undefined in handleSetSwapFee"); - setSwapFee({ pool: pool.address, rawAmount: pool.MAX_FEE }, { onSuccess: () => setCurrentStep(5) }); + setSwapFee( + { pool: pool.address, rawAmount: pool.MAX_FEE }, + { onSuccess: () => setPersistedState({ ...state, step: 5 }) }, + ); }; const handleFinalize = async () => { - if (!pool) throw new Error("Pool is undefined in handleFinalize"); - finalizePool(pool.address, { - onSuccess: () => setCurrentStep(6), + finalizePool(pool?.address, { + onSuccess: () => setPersistedState({ ...state, step: 6 }), }); }; useEffect(() => { if (pool && pool.numTokens < 2n) { if (allowance1 < token1RawAmount || allowance2 < token2RawAmount) { - setCurrentStep(2); + setPersistedState({ ...state, step: 2 }); } else { - setCurrentStep(3); + setPersistedState({ ...state, step: 3 }); } } if (pool && pool.numTokens === 2n && !pool.isFinalized) { if (pool.swapFee !== pool.MAX_FEE) { - setCurrentStep(4); + setPersistedState({ ...state, step: 4 }); } else { - setCurrentStep(5); + setPersistedState({ ...state, step: 5 }); } } - if (pool && pool.isFinalized) setCurrentStep(6); }, [pool, allowance1, allowance2, token1RawAmount, token2RawAmount]); return ( @@ -159,9 +163,9 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) =>
- {currentStep < 6 && } + {state.step < 6 && } - {pool && currentStep === 6 && ( + {pool && state.step === 6 && ( <>
@@ -187,7 +191,7 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => {isWrongNetwork && You're connected to the wrong network} {(() => { - switch (currentStep) { + switch (state.step) { case 1: return ( = ({ label, placeholder, value, value={value} onChange={onChange} disabled={isDisabled} - className="shadow-md rounded-xl w-full input bg-base-300 disabled:text-base-content disabled:bg-base-300 px-5 h-[55px] text-lg" + className="shadow-md border-0 rounded-xl w-full input bg-base-300 disabled:text-base-content disabled:bg-base-300 px-5 h-[55px] text-lg" />
); diff --git a/packages/nextjs/components/common/TokenField.tsx b/packages/nextjs/components/common/TokenField.tsx index 2a9fbcd..48fe329 100644 --- a/packages/nextjs/components/common/TokenField.tsx +++ b/packages/nextjs/components/common/TokenField.tsx @@ -51,7 +51,7 @@ export const TokenField: React.FC = ({ min="0" placeholder="0.0" value={value} - className={`${sufficientAmount !== undefined && (amountGreaterThanBalance || !sufficientAmount) && "ring-1 ring-red-400"} h-[77px] pb-5 text-right text-2xl w-full input rounded-xl bg-base-300 disabled:bg-base-300 disabled:text-base-content`} + className={`${sufficientAmount !== undefined && (amountGreaterThanBalance || !sufficientAmount) && "ring-1 ring-red-400"} border-0 h-[77px] pb-5 text-right text-2xl w-full input rounded-xl bg-base-300 disabled:bg-base-300 disabled:text-base-content`} />
diff --git a/packages/nextjs/hooks/common/index.ts b/packages/nextjs/hooks/common/index.ts deleted file mode 100644 index 4f75998..0000000 --- a/packages/nextjs/hooks/common/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./useStore"; diff --git a/packages/nextjs/hooks/common/useStore.ts b/packages/nextjs/hooks/common/useStore.ts deleted file mode 100644 index ceeeb9d..0000000 --- a/packages/nextjs/hooks/common/useStore.ts +++ /dev/null @@ -1,17 +0,0 @@ -// useStore.ts -import { useEffect, useState } from "react"; - -/** - * A custom hook for using Zustand store in Next.js. - * https://docs.pmnd.rs/zustand/integrations/persisting-store-data#usage-in-next.js - */ -export const useStore = (store: (callback: (state: T) => unknown) => unknown, callback: (state: T) => F) => { - const result = store(callback) as F; - const [data, setData] = useState(); - - useEffect(() => { - setData(result); - }, [result]); - - return data; -}; diff --git a/packages/nextjs/hooks/cow/useBindPool.ts b/packages/nextjs/hooks/cow/useBindPool.ts index 88489de..41f6089 100644 --- a/packages/nextjs/hooks/cow/useBindPool.ts +++ b/packages/nextjs/hooks/cow/useBindPool.ts @@ -5,7 +5,7 @@ import { abis } from "~~/contracts/abis"; import { useTransactor } from "~~/hooks/scaffold-eth"; type BindPayload = { - pool: Address; + pool: Address | undefined; token: Address; rawAmount: bigint; }; diff --git a/packages/nextjs/hooks/cow/useFinalizePool.ts b/packages/nextjs/hooks/cow/useFinalizePool.ts index 7f6e3b0..b2fa36c 100644 --- a/packages/nextjs/hooks/cow/useFinalizePool.ts +++ b/packages/nextjs/hooks/cow/useFinalizePool.ts @@ -9,9 +9,10 @@ export const useFinalizePool = () => { const publicClient = usePublicClient(); const writeTx = useTransactor(); // scaffold hook for tx status toast notifications - const finalize = async (pool: Address) => { + const finalize = async (pool: Address | undefined) => { if (!publicClient) throw new Error("No public client found!"); if (!walletClient) throw new Error("No wallet client found!"); + if (!pool) throw new Error("No pool address found!"); const { request: finalizePool } = await publicClient.simulateContract({ abi: abis.CoW.BCoWPool, @@ -28,5 +29,5 @@ export const useFinalizePool = () => { }); }; - return useMutation({ mutationFn: (pool: Address) => finalize(pool) }); + return useMutation({ mutationFn: (pool: Address | undefined) => finalize(pool) }); }; diff --git a/packages/nextjs/hooks/cow/usePoolCreationState.ts b/packages/nextjs/hooks/cow/usePoolCreationState.ts index 99adbcd..306a694 100644 --- a/packages/nextjs/hooks/cow/usePoolCreationState.ts +++ b/packages/nextjs/hooks/cow/usePoolCreationState.ts @@ -10,6 +10,7 @@ export interface PoolCreationState { token2Amount: string; poolName: string; poolSymbol: string; + step: number; } export const usePoolCreationPersistedState = create( @@ -24,7 +25,7 @@ export const usePoolCreationPersistedState = create( clearPersistedState: () => set({ state: null }), }), { - name: "pool-creation-state", + name: "cow-pool-creation-state", getStorage: () => localStorage, }, ), diff --git a/packages/nextjs/hooks/token/useApproveToken.ts b/packages/nextjs/hooks/token/useApproveToken.ts index 66e5fdf..0db62ad 100644 --- a/packages/nextjs/hooks/token/useApproveToken.ts +++ b/packages/nextjs/hooks/token/useApproveToken.ts @@ -17,7 +17,7 @@ export const useApproveToken = (refetchAllowances: () => void) => { const approve = async ({ token, spender, rawAmount }: ApprovePayload) => { if (!token) throw new Error("Cannot approve token without token address"); - if (!spender) throw new Error("Cannot approve token without spender address"); + if (!spender) throw new Error("Cannot approve token without spender address (the pool)"); if (!walletClient) throw new Error("No wallet client found"); if (!publicClient) throw new Error("No public client found"); 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, From 2a61084e5614a45885f44ba69f6939d8a0311e51 Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Thu, 8 Aug 2024 13:44:43 -0700 Subject: [PATCH 4/7] Fix linter --- packages/nextjs/app/cow/_components/PoolCreation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index a31336e..008ad07 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -145,7 +145,7 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => setPersistedState({ ...state, step: 5 }); } } - }, [pool, allowance1, allowance2, token1RawAmount, token2RawAmount]); + }, [pool, allowance1, allowance2, token1RawAmount, token2RawAmount, state, setPersistedState]); return ( <> From 10cad54a35e1bf08ea848f089fe6f0dd8d6067f7 Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Thu, 8 Aug 2024 14:07:33 -0700 Subject: [PATCH 5/7] Handle step 6 bug --- packages/nextjs/app/cow/_components/PoolCreation.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index 008ad07..9097146 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -145,7 +145,9 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => setPersistedState({ ...state, step: 5 }); } } - }, [pool, allowance1, allowance2, token1RawAmount, token2RawAmount, state, setPersistedState]); + if (pool && pool.isFinalized && state.step !== 1) setPersistedState({ ...state, step: 6 }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pool, allowance1, allowance2, token1RawAmount, token2RawAmount]); return ( <> From 5d6344a12a698bfe962b580d01d03aa8c3fd2d1b Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Thu, 8 Aug 2024 14:55:22 -0700 Subject: [PATCH 6/7] fix alert spelling --- packages/nextjs/app/cow/_components/PoolCreation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index 9097146..c07b998 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -176,7 +176,7 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) =>
- You CoW AMM pool was successfully created! Because of caching, it may take a few minutes for the pool to + Your CoW AMM pool was successfully created! Because of caching, it may take a few minutes for the pool to appear in the Balancer app From 0e39544ab6b3bf1d410ef7c2e2905ccd0af53383 Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Thu, 8 Aug 2024 15:52:41 -0700 Subject: [PATCH 7/7] Add back button to step 1 --- .../nextjs/app/cow/_components/PoolCreation.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index c07b998..6a40b40 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -196,12 +196,17 @@ export const PoolCreation = ({ state, clearState }: ManagePoolCreationProps) => switch (state.step) { case 1: return ( - + <> + +
+ Back to Configure +
+ ); case 2: return (