Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion packages/nextjs/app/v3/_components/PoolCreation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PoolDetails } from "../PoolDetails";
import { SupportAndResetModals } from "../SupportAndResetModals";
import { ApproveOnTokenManager } from "./ApproveOnTokenManager";
import { PoolCreatedView } from "./PoolCreatedView";
import { useStableSurgeStep } from "./useStableSurgeStep";
import { Alert, PoolStepsDisplay, TransactionButton } from "~~/components/common";
import { useIsHyperEvm, useIsUsingBigBlocks, useToggleBlockSize } from "~~/hooks/hyperliquid";
import {
Expand Down Expand Up @@ -142,6 +143,8 @@ export function PoolCreation({ setIsModalOpen }: { setIsModalOpen: (isOpen: bool
const showUseBigBlocksStep = isHyperEvm && !isUsingBigBlocks && step === 1;
const showUseSmallBlocksStep = isHyperEvm && isUsingBigBlocks && step > 1;

const { showSetMaxSurgeFeeStep, setMaxSurgeFeeStep, showWarnDaoMustUpdateFee } = useStableSurgeStep();

const poolCreationSteps = [
...(showUseBigBlocksStep ? [useToggleBlockSizeStep] : []),
deployStep,
Expand All @@ -150,6 +153,7 @@ export function PoolCreation({ setIsModalOpen }: { setIsModalOpen: (isOpen: bool
...swapToBoostedStep,
...approveOnBoostedVariantSteps,
initializeStep,
...(showSetMaxSurgeFeeStep ? [setMaxSurgeFeeStep] : []),
];

return (
Expand All @@ -172,6 +176,16 @@ export function PoolCreation({ setIsModalOpen }: { setIsModalOpen: (isOpen: bool
<PoolDetails />
</div>

{showWarnDaoMustUpdateFee && (
<Alert type="warning">
Since you have chosen the Balancer DAO as the swap fee manager, ask for help{" "}
<a href="https://discord.balancer.fi/" target="_blank" rel="noreferrer" className="link">
on our Discord
</a>{" "}
to update the max surge fee for better integration with aggregators.
</Alert>
)}

{step <= poolCreationSteps.length ? (
poolCreationSteps[step - 1].component
) : (
Expand All @@ -180,6 +194,7 @@ export function PoolCreation({ setIsModalOpen }: { setIsModalOpen: (isOpen: bool

<SupportAndResetModals callback={() => setIsModalOpen(false)} />
</div>

{step > poolCreationSteps.length && (
<Alert type="success">
Your pool has been successfully initialized and will be available to view in the Balancer app shortly!
Expand All @@ -203,20 +218,23 @@ interface TransactionButtonManagerProps {
onSubmit: () => void;
isPending: boolean;
error: Error | null;
infoMsg?: string;
}

function transactionButtonManager({
export function transactionButtonManager({
label,
blockExplorerUrl,
onSubmit,
isPending,
error,
infoMsg,
}: TransactionButtonManagerProps) {
return {
label,
blockExplorerUrl,
component: (
<div className="flex flex-col gap-3">
{infoMsg && <Alert type="info">{infoMsg}</Alert>}
<TransactionButton onClick={onSubmit} title={label} isDisabled={isPending} isPending={isPending} />
{error && (
<Alert type="error">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { transactionButtonManager } from "./index";
import { PoolType } from "@balancer/sdk";
import { zeroAddress } from "viem";
import { useAccount } from "wagmi";
import { useSetMaxSurgeFee } from "~~/hooks/v3/";
import { useSetMaxSurgeFeeTxHash } from "~~/hooks/v3/";
import { usePoolCreationStore } from "~~/hooks/v3/";
import { getBlockExplorerTxLink } from "~~/utils/scaffold-eth";

export function useStableSurgeStep() {
const { address: connectedWalletAddress } = useAccount();
const { chain, setMaxSurgeFeeTx, poolType, swapFeeManager } = usePoolCreationStore();
const {
mutate: setMaxSurgeFee,
isPending: isSetMaxSurgeFeePending,
error: setMaxSurgeFeeError,
} = useSetMaxSurgeFee();
const { isFetching: isSetMaxSurgeFeeTxHashPending, error: setMaxSurgeFeeTxHashError } = useSetMaxSurgeFeeTxHash();

const setMaxSurgeFeeUrl = setMaxSurgeFeeTx.wagmiHash && getBlockExplorerTxLink(chain?.id, setMaxSurgeFeeTx.wagmiHash);

const setMaxSurgeFeeStep = transactionButtonManager({
label: "Set Max Fee",
onSubmit: setMaxSurgeFee,
isPending: isSetMaxSurgeFeePending || isSetMaxSurgeFeeTxHashPending,
error: setMaxSurgeFeeError || setMaxSurgeFeeTxHashError,
blockExplorerUrl: setMaxSurgeFeeUrl,
infoMsg: "For better integration with aggregators, we recommend setting this pool's max surge fee to 10%",
});

const isStableSurge = poolType === PoolType.StableSurge;
const connectedWalletIsSwapFeeManager = swapFeeManager === connectedWalletAddress;
const showSetMaxSurgeFeeStep = isStableSurge && connectedWalletIsSwapFeeManager;
const isDaoSwapFeeManager = swapFeeManager === "" || swapFeeManager === zeroAddress;
const showWarnDaoMustUpdateFee = isStableSurge && isDaoSwapFeeManager;

return { setMaxSurgeFeeStep, showSetMaxSurgeFeeStep, showWarnDaoMustUpdateFee };
}
13 changes: 12 additions & 1 deletion packages/nextjs/app/v3/_components/PoolDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function PoolDetails({ isPreview }: { isPreview?: boolean }) {
eclpParams,
} = usePoolCreationStore();
const { poolHooksWhitelist } = usePoolHooksWhitelist(chain?.id);
const stableSurgeHookAddress = poolHooksWhitelist.find(hook => hook.label === "StableSurge")?.value;

const { isOnlyInitializingPool } = useUserDataStore();

Expand Down Expand Up @@ -203,7 +204,17 @@ export function PoolDetails({ isPreview }: { isPreview?: boolean }) {
<div className="flex justify-between">
<div className="">Pool hooks contract</div>
<div>
{poolHooksContract === zeroAddress ? (
{poolType === PoolType.StableSurge && stableSurgeHookAddress ? (
<a
className="link text-info no-underline flex gap-1 items-center"
target="_blank"
rel="noopener noreferrer"
href={getBlockExplorerAddressLink(targetNetwork, stableSurgeHookAddress)}
>
StableSurge
<ArrowTopRightOnSquareIcon className="w-4 h-4" />
</a>
) : poolHooksContract === zeroAddress ? (
"None"
) : !poolHooksContract ? (
"-"
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/hooks/v3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export * from "./useInitializePoolTxHash";
export * from "./useValidateInitializationInputs";
export * from "./useFetchTokenRate";
export * from "./usePoolHooksWhitelist";
export * from "./useSetMaxSurgeFee";
export * from "./useSetMaxSurgeFeeTxHash";
2 changes: 2 additions & 0 deletions packages/nextjs/hooks/v3/usePoolCreationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface PoolCreationStore {
createPoolTx: TransactionDetails;
initPoolTx: TransactionDetails;
swapToBoostedTx: TransactionDetails;
setMaxSurgeFeeTx: TransactionDetails;
eclpParams: EclpParams;
reClammParams: ReClammParams;
updatePool: (updates: Partial<PoolCreationStore>) => void;
Expand Down Expand Up @@ -137,6 +138,7 @@ export const initialPoolCreationState = {
createPoolTx: { safeHash: undefined, wagmiHash: undefined, isSuccess: false },
initPoolTx: { safeHash: undefined, wagmiHash: undefined, isSuccess: false },
swapToBoostedTx: { safeHash: undefined, wagmiHash: undefined, isSuccess: false },
setMaxSurgeFeeTx: { safeHash: undefined, wagmiHash: undefined, isSuccess: false },
// UX controls
isChooseTokenAmountsModalOpen: false,
};
Expand Down
50 changes: 50 additions & 0 deletions packages/nextjs/hooks/v3/useSetMaxSurgeFee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { usePoolCreationStore } from "./usePoolCreationStore";
import { usePoolHooksWhitelist } from "./usePoolHooksWhitelist";
import { useMutation } from "@tanstack/react-query";
import { parseAbi, parseUnits } from "viem";
import { usePublicClient, useWalletClient } from "wagmi";
import { useTransactor } from "~~/hooks/scaffold-eth";

export const useSetMaxSurgeFee = () => {
const { data: walletClient } = useWalletClient();
const publicClient = usePublicClient();
const writeTx = useTransactor(); // scaffold hook for tx status toast notifications
const { updatePool, setMaxSurgeFeeTx, poolAddress, chain } = usePoolCreationStore();
const { poolHooksWhitelist } = usePoolHooksWhitelist(chain?.id);

const stableSurgeHookAddress = poolHooksWhitelist.find(hook => hook.label === "StableSurge")?.value;

const setMaxSurgeFee = async () => {
if (!poolAddress) throw new Error("useSetMaxSurgeFee: poolAddress is undefined");
if (!walletClient) throw new Error("useApproveToken: wallet client is undefined");
if (!publicClient) throw new Error("useApproveToken: public client is undefined");
if (!stableSurgeHookAddress) throw new Error("useSetMaxSurgeFee: stableSurgeHookAddress is undefined");

console.log("stableSurgeHookAddress", stableSurgeHookAddress);

const { request: setMaxFee } = await publicClient.simulateContract({
address: stableSurgeHookAddress, // stable surge hook address
abi: parseAbi(["function setMaxSurgeFeePercentage(address pool, uint256 newMaxSurgeSurgeFeePercentage)"]),
functionName: "setMaxSurgeFeePercentage",
account: walletClient.account,
args: [poolAddress, parseUnits("10", 16)], // fixed to 10% ?
});

console.log("setMaxFee", setMaxFee);

const txHash = await writeTx(() => walletClient.writeContract(setMaxFee), {
// callbacks to save tx hash's to store
onSafeTxHash: safeHash => updatePool({ setMaxSurgeFeeTx: { ...setMaxSurgeFeeTx, safeHash } }),
onWagmiTxHash: wagmiHash => updatePool({ setMaxSurgeFeeTx: { ...setMaxSurgeFeeTx, wagmiHash } }),
});
console.log("Approved pool contract to spend token, txHash:", txHash);
return txHash;
};

return useMutation({
mutationFn: () => setMaxSurgeFee(),
onError: error => {
console.error(error);
},
});
};
44 changes: 44 additions & 0 deletions packages/nextjs/hooks/v3/useSetMaxSurgeFeeTxHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useSafeAppsSDK } from "@safe-global/safe-apps-react-sdk";
import { useQuery } from "@tanstack/react-query";
import { usePublicClient } from "wagmi";
import { useIsSafeWallet } from "~~/hooks/safe/useIsSafeWallet";
import { usePoolCreationStore } from "~~/hooks/v3/";
import { pollSafeTxStatus } from "~~/utils/safe";

export function useSetMaxSurgeFeeTxHash() {
const { setMaxSurgeFeeTx, updatePool, poolType, step } = usePoolCreationStore();
const { wagmiHash, safeHash } = setMaxSurgeFeeTx;

const publicClient = usePublicClient();
const isSafeWallet = useIsSafeWallet();
const { sdk } = useSafeAppsSDK();

return useQuery({
queryKey: ["setMaxSurgeFeeTx", wagmiHash, safeHash],
queryFn: async () => {
if (!publicClient) throw new Error("No public client for set max surge fee tx hash");
if (poolType === undefined) throw new Error("Pool type is undefined");

if (isSafeWallet && safeHash && !wagmiHash) {
const wagmiHash = await pollSafeTxStatus(sdk, safeHash);
updatePool({ initPoolTx: { safeHash, wagmiHash, isSuccess: false } });
return null; // Trigger a re-query with the new wagmiHash
}

if (!wagmiHash) return null;

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

if (txReceipt.status === "success") {
updatePool({ step: step + 1, setMaxSurgeFeeTx: { safeHash, wagmiHash, isSuccess: true } });
return { isSuccess: true };
} else if (txReceipt.status === "reverted") {
updatePool({ setMaxSurgeFeeTx: { safeHash: undefined, wagmiHash: undefined, isSuccess: false } });
// other option is tx reverts at which point we want to clear state to attempt new tx to be sent
updatePool({ setMaxSurgeFeeTx: { safeHash: undefined, wagmiHash: undefined, isSuccess: false } });
throw new Error("Set max surge fee transaction reverted");
}
},
enabled: Boolean(!setMaxSurgeFeeTx.isSuccess && (safeHash || wagmiHash)),
});
}