Skip to content

Commit

Permalink
add reset feature and prevent modal from closing during pool creation…
Browse files Browse the repository at this point in the history
… process
  • Loading branch information
MattPereira committed Oct 21, 2024
1 parent f173b4e commit 4d8862d
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 39 deletions.
10 changes: 7 additions & 3 deletions packages/nextjs/app/cow/_components/PoolResetModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ export const PoolResetModal = ({ setIsModalOpen, clearState, etherscanURL }: Poo
</li>
{etherscanURL && (
<li>
Viewing the pool on{" "}
Viewing the pool on a{" "}
<Link target="_blank" rel="noreferrer" href={etherscanURL}>
<span className="link">etherscan</span> <ArrowTopRightOnSquareIcon className="w-4 h-4 inline-block" />
<span className="link">block explorer</span>{" "}
<ArrowTopRightOnSquareIcon className="w-4 h-4 inline-block" />
</Link>
</li>
)}
Expand All @@ -47,7 +48,10 @@ export const PoolResetModal = ({ setIsModalOpen, clearState, etherscanURL }: Poo
Cancel
</button>
<button
onClick={clearState}
onClick={() => {
clearState();
setIsModalOpen(false);
}}
className="bg-error text-neutral-800 px-5 py-3 border border-error rounded-xl w-24"
>
Reset
Expand Down
6 changes: 3 additions & 3 deletions packages/nextjs/app/v3/_components/ChooseType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export function ChooseType() {
} hover:scale-105 p-7 w-full rounded-xl text-lg text shadow-inner`}
onClick={() => updatePool({ poolType: type })}
>
<div className="flex flex-col items-start">
<div className="font-bold text-xl text-center w-full mb-2">{type}</div>
<div className="text-start">{POOL_TYPE_DESCRIPTIONS[type]}</div>
<div className="flex flex-col text-center">
<div className="font-bold text-xl mb-2 w-full">{type}</div>
<div>{POOL_TYPE_DESCRIPTIONS[type]}</div>
</div>
</button>
))}
Expand Down
15 changes: 6 additions & 9 deletions packages/nextjs/app/v3/_components/PoolConfiguration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@ import { ChooseInfo, ChooseParameters, ChooseTokens, ChooseType } from "./";
import { PoolCreationModal } from "./PoolCreationModal";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline";
import { TransactionButton } from "~~/components/common";
import { useValidatePoolCreationInput } from "~~/hooks/v3";
import { TABS, type TabType, usePoolCreationStore, useValidatePoolCreationInput } from "~~/hooks/v3";
import { bgBeigeGradient } from "~~/utils";

const TABS = ["Type", "Tokens", "Parameters", "Information"] as const;
type TabType = (typeof TABS)[number];

export function PoolConfiguration() {
const { selectedTab, updatePool } = usePoolCreationStore();
const [isPoolCreationModalOpen, setIsPoolCreationModalOpen] = useState(false);
const [selectedTab, setSelectedTab] = useState<TabType>("Type");
const { prev, next } = getAdjacentTabs(selectedTab);

const { isParametersValid, isTypeValid, isInfoValid, isTokensValid, isPoolCreationInputValid } =
useValidatePoolCreationInput();

Expand All @@ -35,8 +31,8 @@ export function PoolConfiguration() {
}

function handleTabChange(direction: "prev" | "next") {
if (direction === "prev" && prev) setSelectedTab(prev);
if (direction === "next" && next) setSelectedTab(next);
if (direction === "prev" && prev) updatePool({ selectedTab: prev });
if (direction === "next" && next) updatePool({ selectedTab: next });
}

function getAdjacentTabs(currentTab: TabType): { prev: TabType | null; next: TabType | null } {
Expand All @@ -49,7 +45,7 @@ export function PoolConfiguration() {

return (
<>
<div className="w-full max-w-[700px]">
<div className="w-full max-w-[700px] flex flex-col gap-5">
<div className="bg-base-200 rounded-xl p-7 shadow-lg">
<div className="font-bold text-2xl mb-7">Pool Configuration</div>
<div className="relative grid grid-cols-4 text-center text-xl rounded-xl">
Expand Down Expand Up @@ -101,6 +97,7 @@ export function PoolConfiguration() {
</div>
</div>
</div>

{isPoolCreationModalOpen && <PoolCreationModal setIsModalOpen={setIsPoolCreationModalOpen} />}
</>
);
Expand Down
36 changes: 25 additions & 11 deletions packages/nextjs/app/v3/_components/PoolCreationModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useState } from "react";
import { ApproveButtonManager, PermitButtonManager } from "./";
import { sepolia } from "viem/chains";
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
import { StepsDisplay } from "~~/app/cow/_components";
import { PoolResetModal } from "~~/app/cow/_components";
import { PoolDetails } from "~~/app/v3/_components";
import { Alert, TransactionButton } from "~~/components/common";
import { useCreatePool, useInitializePool, usePoolCreationStore } from "~~/hooks/v3/";
Expand All @@ -12,18 +14,15 @@ interface PoolCreationModalProps {
setIsModalOpen: (isOpen: boolean) => void;
}

/**
* TODO: replace sepolia hardcoded chainId by adding chainId to zustand store
* TODO: Figure out forcing pool creation on chain that tokens are selected?
*/
export function PoolCreationModal({ setIsModalOpen }: PoolCreationModalProps) {
const [isResetModalOpen, setIsResetModalOpen] = useState(false);
const { mutate: createPool, isPending: isCreatePoolPending, error: createPoolError } = useCreatePool();
const {
mutate: initializePool,
isPending: isInitializePoolPending,
error: initializePoolError,
} = useInitializePool();
const { step, tokenConfigs, createPoolTxHash, initPoolTxHash } = usePoolCreationStore();
const { step, tokenConfigs, createPoolTxHash, initPoolTxHash, clearPoolStore, updatePool } = usePoolCreationStore();

const poolDeploymentUrl = createPoolTxHash ? getBlockExplorerTxLink(sepolia.id, createPoolTxHash) : undefined;
const poolInitializationUrl = initPoolTxHash ? getBlockExplorerTxLink(sepolia.id, initPoolTxHash) : undefined;
Expand Down Expand Up @@ -52,20 +51,24 @@ export function PoolCreationModal({ setIsModalOpen }: PoolCreationModalProps) {
blockExplorerUrl: poolInitializationUrl,
};

const poolCreationSteps = [firstStep, ...approveOnTokenSteps, ...approveOnPermit2Steps, lastStep];
const isPoolCreationInProgress =
isCreatePoolPending || isInitializePoolPending || (step > 1 && step < poolCreationSteps.length);

const poolCreationError = createPoolError || initializePoolError;

return (
<div className="fixed inset-0 bg-black bg-opacity-75 flex gap-7 justify-center items-center z-50">
<div
className="absolute w-full h-full"
onClick={() => {
if (isCreatePoolPending || isInitializePoolPending || step > 1) return;
if (isPoolCreationInProgress) return;
setIsModalOpen(false);
}}
/>
<div className="flex flex-col gap-5 relative z-10">
<div className="bg-base-300 rounded-lg min-w-[500px] p-5 flex flex-col gap-5">
<div className="font-bold text-2xl">Pool Creation</div>
<div className="font-bold text-2xl text-center">Pool Creation</div>
<PoolDetails />
{step === 1 ? (
<TransactionButton
Expand Down Expand Up @@ -110,14 +113,25 @@ export function PoolCreationModal({ setIsModalOpen }: PoolCreationModalProps) {
</div>
</Alert>
)}
<div onClick={() => setIsResetModalOpen(true)} className="text-center underline cursor-pointer text-lg mt-2">
Reset progress
</div>
</div>

<div className="relative z-10">
<StepsDisplay
currentStepNumber={step}
steps={[firstStep, ...approveOnTokenSteps, ...approveOnPermit2Steps, lastStep]}
/>
<StepsDisplay currentStepNumber={step} steps={poolCreationSteps} />
</div>
{isResetModalOpen && (
<PoolResetModal
etherscanURL={poolDeploymentUrl}
clearState={() => {
clearPoolStore();
setIsModalOpen(false);
updatePool({ selectedTab: "Type" });
}}
setIsModalOpen={setIsResetModalOpen}
/>
)}
</div>
);
}
6 changes: 3 additions & 3 deletions packages/nextjs/app/v3/_components/PoolDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export function PoolDetails({ isPreview }: { isPreview?: boolean }) {
isEmpty={!name && !symbol}
content={
<div>
<div>{name.length > 40 ? `${name.slice(0, 40)}...` : name}</div>
<div>{name.length > 38 ? `${name.slice(0, 38)}...` : name}</div>
<div>{symbol}</div>
</div>
}
Expand All @@ -128,9 +128,9 @@ interface DetailSectionProps {
function DetailSection({ title, isValid, isEmpty, isPreview, content }: DetailSectionProps) {
return (
<>
<div className="bg-base-100 p-4 rounded-xl">
<div className="bg-base-100 p-4 rounded-xl text-lg">
<div className="flex justify-between mb-2">
<div className="font-bold">{title}: </div>
<div className="font-bold text-xl">{title}: </div>
{isPreview && (
<div className="h-7 w-7 rounded-full">
{isValid ? (
Expand Down
32 changes: 24 additions & 8 deletions packages/nextjs/app/v3/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import { PoolConfiguration, PoolDetails } from "./_components";
import type { NextPage } from "next";
import { ArrowUpRightIcon } from "@heroicons/react/24/outline";
import { BalancerLogo } from "~~/components/assets/BalancerLogo";
import { usePoolStoreDebug } from "~~/hooks/v3";
import { Alert } from "~~/components/common";
import { usePoolStoreDebug, useValidateNetwork } from "~~/hooks/v3";

const BalancerV3: NextPage = () => {
usePoolStoreDebug();

const { isWrongNetwork } = useValidateNetwork();
return (
<div className="flex justify-center">
<div className="flex justify-center py-10 px-5 lg:px-10 w-full max-w-screen-2xl">
Expand All @@ -16,13 +18,27 @@ const BalancerV3: NextPage = () => {
<BalancerLogo width="55px" />
<h1 className="text-5xl font-bold text-center mb-0">Balancer v3</h1>
</div>
<div className="flex gap-5 w-full justify-center">
<PoolConfiguration />
<div className="bg-base-200 w-full max-w-[420px] rounded-xl shadow-lg p-5 h-fit">
<div className="font-bold text-2xl mb-3">Pool Preview</div>
<PoolDetails isPreview={true} />

{isWrongNetwork ? (
<div className="flex justify-center w-full">
<div>
<Alert type="warning">
<div className="flex items-center gap-2">
You are connected to an unsupported network. To continue, please switch to Sepolia
<ArrowUpRightIcon className="w-4 h-4" />
</div>
</Alert>
</div>
</div>
</div>
) : (
<div className="flex gap-5 w-full justify-center">
<PoolConfiguration />
<div className="bg-base-200 w-full max-w-[420px] rounded-xl shadow-lg p-5 h-fit">
<div className="font-bold text-2xl mb-3">Pool Preview</div>
<PoolDetails isPreview={true} />
</div>
</div>
)}
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/hooks/v3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./usePoolCreationStore";
export * from "./useCreatePool";
export * from "./useInitializePool";
export * from "./useValidatePoolCreationInput";
export * from "./useValidateNetwork";
5 changes: 5 additions & 0 deletions packages/nextjs/hooks/v3/usePoolCreationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { Address, zeroAddress } from "viem";
import { create } from "zustand";
import { type Token } from "~~/hooks/token";

export const TABS = ["Type", "Tokens", "Parameters", "Information"] as const;
export type TabType = (typeof TABS)[number];

export type AllowedPoolTypes = PoolType.Stable | PoolType.Weighted;

export type TokenConfig = {
Expand All @@ -19,6 +22,7 @@ export type TokenConfig = {

export interface PoolCreationStore {
step: number;
selectedTab: TabType;
isDelegatingManagement: boolean;
isUsingHooks: boolean;
poolAddress: Address | undefined;
Expand Down Expand Up @@ -55,6 +59,7 @@ export const initialPoolCreationState = {
isDelegatingManagement: true,
isUsingHooks: false,
poolAddress: undefined, // set after pool deployment
selectedTab: TABS[0],
name: "",
symbol: "",
poolType: undefined,
Expand Down
12 changes: 12 additions & 0 deletions packages/nextjs/hooks/v3/useValidateNetwork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { sepolia } from "viem/chains";
import { useWalletClient } from "wagmi";

const ALLOWED_NETWORKS = [sepolia.id] as const;

export function useValidateNetwork() {
const { data: walletClient } = useWalletClient();
const chainId = walletClient?.chain.id;
const isWrongNetwork = chainId !== undefined && !ALLOWED_NETWORKS.includes(chainId as typeof sepolia.id);

return { isWrongNetwork, chainId };
}
6 changes: 4 additions & 2 deletions packages/nextjs/hooks/v3/useValidatePoolCreationInput.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { PoolType, TokenType } from "@balancer/sdk";
import { isAddress } from "viem";
import { usePoolCreationStore } from "~~/hooks/v3";
import { usePoolCreationStore, useValidateNetwork } from "~~/hooks/v3";

export function useValidatePoolCreationInput() {
const { isWrongNetwork } = useValidateNetwork();

const {
poolType,
tokenConfigs,
Expand All @@ -17,7 +19,7 @@ export function useValidatePoolCreationInput() {
poolHooksContract,
} = usePoolCreationStore();

const isTypeValid = poolType !== undefined;
const isTypeValid = poolType !== undefined && !isWrongNetwork;

const isTokensValid = tokenConfigs.every(token => {
if (!token.address || !token.amount) return false;
Expand Down

0 comments on commit 4d8862d

Please sign in to comment.