Skip to content

Commit

Permalink
Handle if user disconnects during pool init tx pending
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Dec 12, 2024
1 parent af1dfac commit bf29e2b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 40 deletions.
63 changes: 54 additions & 9 deletions packages/nextjs/app/v3/_components/PoolCreationManager.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { ApproveOnTokenManager } from ".";
import { parseEventLogs } from "viem";
import { usePublicClient } from "wagmi";
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
import { PoolDetails } from "~~/app/v3/_components";
Expand All @@ -11,6 +12,7 @@ import {
TransactionButton,
} from "~~/components/common";
import {
poolFactoryAbi,
useBoostableWhitelist,
useCreatePool,
useInitializePool,
Expand All @@ -25,8 +27,20 @@ import { getBlockExplorerTxLink } from "~~/utils/scaffold-eth/";
* Manages the pool creation process using a modal that cannot be closed after execution of the first step
*/
export function PoolCreationManager({ setIsModalOpen }: { setIsModalOpen: (isOpen: boolean) => void }) {
const { step, tokenConfigs, clearPoolStore, createPoolTxHash, swapTxHash, initPoolTxHash, chain, updatePool } =
usePoolCreationStore();
const [isCreatePoolReceiptLoading, setIsCreatePoolReceiptLoading] = useState(false);
const [isInitPoolReceiptLoading, setIsInitPoolReceiptLoading] = useState(false);

const {
step,
tokenConfigs,
clearPoolStore,
createPoolTxHash,
swapTxHash,
initPoolTxHash,
chain,
updatePool,
poolType,
} = usePoolCreationStore();
const { clearUserData } = useUserDataStore();
const { data: boostableWhitelist } = useBoostableWhitelist();

Expand All @@ -47,7 +61,7 @@ export function PoolCreationManager({ setIsModalOpen }: { setIsModalOpen: (isOpe
label: "Deploy Pool",
blockExplorerUrl: poolDeploymentUrl,
onSubmit: createPool,
isPending: isCreatePoolPending,
isPending: isCreatePoolPending || isCreatePoolReceiptLoading,
error: createPoolError,
});

Expand Down Expand Up @@ -104,7 +118,7 @@ export function PoolCreationManager({ setIsModalOpen }: { setIsModalOpen: (isOpe
const initializeStep = createTransactionStep({
label: "Initialize Pool",
onSubmit: initializePool,
isPending: isInitializePoolPending,
isPending: isInitializePoolPending || isInitPoolReceiptLoading,
error: initializePoolError,
blockExplorerUrl: poolInitializationUrl,
});
Expand All @@ -117,17 +131,48 @@ export function PoolCreationManager({ setIsModalOpen }: { setIsModalOpen: (isOpe
initializeStep,
];

// If user disconnects during pending tx state, this will look up tx receipt based on tx hash saved in local storage immediately after tx is sent (i.e. right after button is clicked)
useEffect(() => {
async function getTxReceipt() {
if (!publicClient || !createPoolTxHash || poolType === undefined || step !== 1) return;
setIsCreatePoolReceiptLoading(true);
try {
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: createPoolTxHash });
const logs = parseEventLogs({
abi: poolFactoryAbi[poolType],
logs: txReceipt.logs,
});
if (logs.length > 0 && "args" in logs[0] && "pool" in logs[0].args) {
const newPool = logs[0].args.pool;
updatePool({ poolAddress: newPool, step: 2 });
} else {
throw new Error("Expected pool address not found in event logs");
}
} catch (error) {
console.error("Error getting create pool transaction receipt:", error);
} finally {
setIsCreatePoolReceiptLoading(false);
}
}
getTxReceipt();
}, [createPoolTxHash, publicClient, poolType, updatePool, step]);

// Handle edge case where user disconnects while pool init tx is pending
useEffect(() => {
async function getTxReceipt() {
if (!publicClient || !initPoolTxHash || step < poolCreationSteps.length) return;
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: initPoolTxHash });
if (txReceipt.status === "success") {
updatePool({ step: poolCreationSteps.length + 1 });
try {
setIsInitPoolReceiptLoading(true);
const txReceipt = await publicClient.waitForTransactionReceipt({ hash: initPoolTxHash });
if (txReceipt.status === "success") updatePool({ step: poolCreationSteps.length + 1 });
} catch (error) {
console.error("Error getting init pool transaction receipt:", error);
} finally {
setIsInitPoolReceiptLoading(false);
}
}
getTxReceipt();
}, [initPoolTxHash, publicClient, updatePool]);
}, [initPoolTxHash, publicClient, updatePool, step]);

return (
<div className="fixed inset-0 bg-black bg-opacity-75 flex gap-7 justify-center items-center z-50">
Expand Down
27 changes: 1 addition & 26 deletions packages/nextjs/hooks/v3/useCreatePool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useEffect } from "react";
import {
CreatePool,
CreatePoolV3BaseInput,
Expand All @@ -14,7 +13,7 @@ import { usePublicClient, useWalletClient } from "wagmi";
// import { useTransactor } from "~~/hooks/scaffold-eth";
import { useBoostableWhitelist, usePoolCreationStore } from "~~/hooks/v3";

const poolFactoryAbi = {
export const poolFactoryAbi = {
[PoolType.Weighted]: weightedPoolFactoryAbi_V3,
[PoolType.Stable]: stablePoolFactoryAbi_V3,
};
Expand All @@ -40,34 +39,10 @@ export const useCreatePool = () => {
disableUnbalancedLiquidity,
amplificationParameter,
updatePool,
createPoolTxHash,
step,
} = usePoolCreationStore();

const { data: boostableWhitelist } = useBoostableWhitelist();

// If user disconnects during pending tx state, this will look up tx receipt based on tx hash saved in local storage immediately after tx is sent (i.e. right after button is clicked)
useEffect(() => {
async function getTxReceipt() {
if (!publicClient || !createPoolTxHash || poolType === undefined || step !== 1) return;

const txReceipt = await publicClient.waitForTransactionReceipt({ hash: createPoolTxHash });

const logs = parseEventLogs({
abi: poolFactoryAbi[poolType],
logs: txReceipt.logs,
});

if (logs.length > 0 && "args" in logs[0] && "pool" in logs[0].args) {
const newPool = logs[0].args.pool;
updatePool({ poolAddress: newPool, step: 2 });
} else {
throw new Error("Expected pool address not found in event logs");
}
}
getTxReceipt();
}, [createPoolTxHash, publicClient, poolType, updatePool, step]);

function createPoolInput(poolType: PoolType): CreatePoolV3StableInput | CreatePoolV3WeightedInput {
if (poolType === undefined) throw new Error("No pool type provided!");
if (!publicClient) throw new Error("Public client must be available!");
Expand Down
5 changes: 0 additions & 5 deletions packages/nextjs/hooks/v3/useInitializePool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useEffect } from "react";
import { BALANCER_ROUTER, InitPool, InitPoolDataProvider, InitPoolInput, balancerRouterAbi } from "@balancer/sdk";
import { useMutation } from "@tanstack/react-query";
import { getContract, parseUnits } from "viem";
Expand All @@ -17,10 +16,6 @@ export const useInitializePool = () => {
const { poolAddress, poolType, tokenConfigs, updatePool, step } = usePoolCreationStore();
const { data: boostableWhitelist } = useBoostableWhitelist();

useEffect(() => {
console.log("init pool useEffect");
}, []);

async function initializePool() {
if (!poolAddress) throw new Error("Pool address missing");
if (!rpcUrl) throw new Error("RPC URL missing");
Expand Down

0 comments on commit bf29e2b

Please sign in to comment.