From 243235b0dfeb1aac22692cf3444bf79e72a237fc Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Sat, 14 Dec 2024 11:00:53 -0800 Subject: [PATCH] refactor cow amm to store create pool tx hash in local storage --- .../app/cow/_components/PoolConfiguration.tsx | 2 +- .../app/cow/_components/PoolCreated.tsx | 10 +---- .../app/cow/_components/PoolCreation.tsx | 43 ++++++++++--------- packages/nextjs/hooks/cow/index.ts | 2 +- .../nextjs/hooks/cow/useCowFactoryEvents.ts | 28 ------------ packages/nextjs/hooks/cow/useCreatePool.ts | 22 +++++++--- .../nextjs/hooks/cow/usePoolCreationStore.ts | 2 +- .../hooks/cow/useWaitForTransactionReceipt.ts | 34 +++++++++++++++ 8 files changed, 78 insertions(+), 65 deletions(-) delete mode 100644 packages/nextjs/hooks/cow/useCowFactoryEvents.ts create mode 100644 packages/nextjs/hooks/cow/useWaitForTransactionReceipt.ts diff --git a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx index 8ea07e57..431378e5 100644 --- a/packages/nextjs/app/cow/_components/PoolConfiguration.tsx +++ b/packages/nextjs/app/cow/_components/PoolConfiguration.tsx @@ -170,7 +170,7 @@ export const PoolConfiguration = () => { address: undefined, step: 1, tokenWeights, - isInitialState: true, + createPoolTxHash: undefined, }); }} /> diff --git a/packages/nextjs/app/cow/_components/PoolCreated.tsx b/packages/nextjs/app/cow/_components/PoolCreated.tsx index a848c654..bb1f6b8b 100644 --- a/packages/nextjs/app/cow/_components/PoolCreated.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreated.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; -import { usePathname, useRouter } from "next/navigation"; import CopyToClipboard from "react-copy-to-clipboard"; import { getAddress } from "viem"; import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline"; @@ -16,8 +15,6 @@ interface PoolCreatedProps { export const PoolCreated = ({ etherscanURL, poolAddress, chainId, clearState }: PoolCreatedProps) => { const [addressCopied, setAddressCopied] = useState(false); - const router = useRouter(); - const pathname = usePathname(); if (!poolAddress) return null; @@ -26,11 +23,6 @@ export const PoolCreated = ({ etherscanURL, poolAddress, chainId, clearState }: const domainName = extractDomain(etherscanURL || ""); const blockExplorerName = domainName.split(".")[0]; - const handleCreateAnother = () => { - clearState(); - router.replace(pathname); - }; - return ( <>
@@ -77,7 +69,7 @@ export const PoolCreated = ({ etherscanURL, poolAddress, chainId, clearState }:
clearState()} isPending={false} isDisabled={false} /> diff --git a/packages/nextjs/app/cow/_components/PoolCreation.tsx b/packages/nextjs/app/cow/_components/PoolCreation.tsx index 87e337fc..37abeb17 100644 --- a/packages/nextjs/app/cow/_components/PoolCreation.tsx +++ b/packages/nextjs/app/cow/_components/PoolCreation.tsx @@ -1,5 +1,4 @@ import { useEffect } from "react"; -import { useSearchParams } from "next/navigation"; import { PoolCreated } from "./"; import { parseUnits } from "viem"; import { useSwitchChain } from "wagmi"; @@ -16,15 +15,16 @@ import { CHAIN_NAMES } from "~~/hooks/balancer/"; import { type PoolCreationState, useBindToken, - useCowFactoryEvents, useCreatePool, useFinalizePool, useReadPool, useSetSwapFee, + useWaitForTransactionReceipt, } from "~~/hooks/cow/"; import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; import { useApproveToken, useReadToken } from "~~/hooks/token"; import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth"; +import { getBlockExplorerTxLink } from "~~/utils/scaffold-eth"; import { getPerTokenWeights } from "~~/utils/token-weights"; interface ManagePoolCreationProps { @@ -34,17 +34,10 @@ interface ManagePoolCreationProps { } export const PoolCreation = ({ poolCreation, updatePoolCreation, clearPoolCreation }: ManagePoolCreationProps) => { - useCowFactoryEvents(); + const { address: poolAddress, createPoolTxHash } = poolCreation; - const searchParams = useSearchParams(); - const poolAddress = searchParams.get("address") || poolCreation.address; - - useEffect(() => { - const addressParam = searchParams.get("address"); - if (addressParam && addressParam !== poolCreation.address) { - updatePoolCreation({ address: addressParam }); - } - }, [searchParams, poolCreation.address, updatePoolCreation]); + const { error: poolCreationTxReceiptError, isPending: isPoolCreationTxReceiptPending } = + useWaitForTransactionReceipt(); const token1RawAmount = parseUnits(poolCreation.token1Amount, poolCreation.token1.decimals); const token2RawAmount = parseUnits(poolCreation.token2Amount, poolCreation.token2.decimals); @@ -74,11 +67,19 @@ export const PoolCreation = ({ poolCreation, updatePoolCreation, clearPoolCreati } = useBindToken(poolCreation.tokenWeights, false); const { mutate: setSwapFee, isPending: isSetSwapFeePending, error: setSwapFeeError } = useSetSwapFee(); const { mutate: finalizePool, isPending: isFinalizePending, error: finalizeError } = useFinalizePool(); + const txError = - createPoolError || approve1Error || approve2Error || bind1Error || bind2Error || setSwapFeeError || finalizeError; + createPoolError || + poolCreationTxReceiptError || + approve1Error || + approve2Error || + bind1Error || + bind2Error || + setSwapFeeError || + finalizeError; useEffect(() => { - if (!poolData || poolCreation.isInitialState) return; + if (!poolData || !poolAddress) return; if (poolData.isFinalized) { updatePoolCreation({ step: 8 }); @@ -144,12 +145,9 @@ export const PoolCreation = ({ poolCreation, updatePoolCreation, clearPoolCreati return ( { - // user has officially begun the pool creation process - updatePoolCreation({ isInitialState: false }); - createPool( { name: poolCreation.name, symbol: poolCreation.symbol }, { @@ -303,7 +301,12 @@ export const PoolCreation = ({ poolCreation, updatePoolCreation, clearPoolCreati { - const { address: connectedAddress } = useAccount(); - const { targetNetwork } = useTargetNetwork(); - const { updatePoolCreation } = usePoolCreationStore(); - - const { data: events, isLoading: isLoadingEvents } = useScaffoldEventHistory({ - contractName: "BCoWFactory", - eventName: "LOG_NEW_POOL", - fromBlock: externalContracts[targetNetwork.id as keyof typeof externalContracts].BCoWFactory.fromBlock, - watch: true, - filters: { caller: connectedAddress }, - }); - - useEffect(() => { - if (!isLoadingEvents && events) { - const pools = events.map(e => e.args.bPool).filter((pool): pool is string => pool !== undefined); - // zero index is the most recently created pool for the user - const mostRecentlyCreated = pools[0]; - updatePoolCreation({ address: mostRecentlyCreated }); - } - }, [isLoadingEvents, events, updatePoolCreation]); -}; diff --git a/packages/nextjs/hooks/cow/useCreatePool.ts b/packages/nextjs/hooks/cow/useCreatePool.ts index a1e2fd03..708597dd 100644 --- a/packages/nextjs/hooks/cow/useCreatePool.ts +++ b/packages/nextjs/hooks/cow/useCreatePool.ts @@ -1,8 +1,9 @@ import { useMutation } from "@tanstack/react-query"; import { Address, parseEventLogs } from "viem"; -import { usePublicClient } from "wagmi"; +import { usePublicClient, useWalletClient } from "wagmi"; import { abis } from "~~/contracts/abis"; -import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; +import { usePoolCreationStore } from "~~/hooks/cow/usePoolCreationStore"; +import { useScaffoldContract, useTransactor } from "~~/hooks/scaffold-eth"; type CreatePoolPayload = { name: string; @@ -10,15 +11,26 @@ type CreatePoolPayload = { }; export const useCreatePool = () => { - const { writeContractAsync: bCoWFactory } = useScaffoldWriteContract("BCoWFactory"); + const { data: bCoWFactory } = useScaffoldContract({ contractName: "BCoWFactory" }); const publicClient = usePublicClient(); + const { data: walletClient } = useWalletClient(); + const writeTx = useTransactor(); + const { updatePoolCreation } = usePoolCreationStore(); const createPool = async ({ name, symbol }: CreatePoolPayload): Promise
=> { - if (!publicClient) throw new Error("No public client"); - const hash = await bCoWFactory({ + if (!publicClient || !bCoWFactory || !walletClient) throw new Error("useCreatePool missing required setup"); + + const { request } = await publicClient.simulateContract({ + account: walletClient.account, + address: bCoWFactory.address, + abi: bCoWFactory.abi, functionName: "newBPool", args: [name, symbol], }); + + const hash = await writeTx(() => walletClient.writeContract(request), { + onTransactionHash: txHash => updatePoolCreation({ createPoolTxHash: txHash }), + }); if (!hash) throw new Error("No pool creation transaction hash"); const txReceipt = await publicClient.getTransactionReceipt({ hash }); const logs = parseEventLogs({ diff --git a/packages/nextjs/hooks/cow/usePoolCreationStore.ts b/packages/nextjs/hooks/cow/usePoolCreationStore.ts index 22eefffd..88d2b1f1 100644 --- a/packages/nextjs/hooks/cow/usePoolCreationStore.ts +++ b/packages/nextjs/hooks/cow/usePoolCreationStore.ts @@ -14,7 +14,7 @@ export interface PoolCreationState { address: Address | undefined; // updated by tx receipt from completion of step 1 step: number; tokenWeights: "5050" | "8020"; - isInitialState: boolean; + createPoolTxHash: `0x${string}` | undefined; } export const usePoolCreationStore = create( diff --git a/packages/nextjs/hooks/cow/useWaitForTransactionReceipt.ts b/packages/nextjs/hooks/cow/useWaitForTransactionReceipt.ts new file mode 100644 index 00000000..d26b63da --- /dev/null +++ b/packages/nextjs/hooks/cow/useWaitForTransactionReceipt.ts @@ -0,0 +1,34 @@ +import { useQuery } from "@tanstack/react-query"; +import { parseEventLogs } from "viem"; +import { usePublicClient } from "wagmi"; +import { abis } from "~~/contracts/abis"; +import { usePoolCreationStore } from "~~/hooks/cow/usePoolCreationStore"; + +export const useWaitForTransactionReceipt = () => { + const publicClient = usePublicClient(); + const { poolCreation, updatePoolCreation } = usePoolCreationStore(); + const createPoolTxHash = poolCreation?.createPoolTxHash; + const poolAddress = poolCreation?.address; + + return useQuery({ + queryKey: ["fetch-createPool-tx-receipt", createPoolTxHash], + queryFn: async () => { + if (!publicClient) throw new Error("Missing public client for fetching pool creation tx receipt"); + if (!createPoolTxHash) return "No need to fetch receipt yet"; + if (poolAddress) return poolAddress; + + const txReceipt = await publicClient.waitForTransactionReceipt({ hash: createPoolTxHash }); + const logs = parseEventLogs({ + abi: abis.CoW.BCoWFactory, + logs: txReceipt.logs, + }); + + const newPoolAddress = (logs[0].args as { caller: string; bPool: string }).bPool; + if (!newPoolAddress) throw new Error("No new pool address from pool creation tx receipt"); + + updatePoolCreation({ address: newPoolAddress, step: 2 }); + + return newPoolAddress; + }, + }); +};