diff --git a/apps/hub/src/app/pools/create/CreatePageContent.tsx b/apps/hub/src/app/pools/create/CreatePageContent.tsx index ff60e793d..8977a2e77 100755 --- a/apps/hub/src/app/pools/create/CreatePageContent.tsx +++ b/apps/hub/src/app/pools/create/CreatePageContent.tsx @@ -7,11 +7,9 @@ import { ADDRESS_ZERO, TransactionActionType, balancerPoolCreationHelperAbi, - balancerVaultAbi, useBeraJs, type Token, } from "@bera/berajs"; -import { getAllowances, getWalletBalances } from "@bera/berajs/actions"; import { balancerPoolCreationHelper, balancerVaultAddress } from "@bera/config"; import { ActionButton, @@ -25,87 +23,53 @@ import { Button } from "@bera/ui/button"; import { Card } from "@bera/ui/card"; import { Icons } from "@bera/ui/icons"; import { PoolType } from "@berachain-foundation/berancer-sdk"; -import { - Address, - Log, - TransactionReceipt, - decodeEventLog, - keccak256, - parseUnits, -} from "viem"; +import { keccak256, parseUnits } from "viem"; import { usePublicClient } from "wagmi"; import CreatePoolInitialLiquidityInput from "~/components/create-pool/create-pool-initial-liquidity-input"; import CreatePoolInput from "~/components/create-pool/create-pool-input"; import { usePools } from "~/b-sdk/usePools"; import useMultipleTokenApprovalsWithSlippage from "~/hooks/useMultipleTokenApprovalsWithSlippage"; +import { TokenInput } from "~/hooks/useMultipleTokenInput"; import { usePoolCreationRelayerApproval } from "~/hooks/usePoolCreationRelayerApproval"; -export const findEventInReceiptLogs = ({ - receipt, - to, - abi, - eventName, -}: { - // NOTE: this source is https://github.com/balancer/b-sdk/blob/main/test/lib/utils/findEventInReceiptLogs.ts#L3 - receipt: TransactionReceipt; - to: Address; - abi: readonly unknown[]; - eventName: string; -}): { eventName: string; args: any } => { - const event = receipt.logs - .filter((log: Log) => { - return log.address.toLowerCase() === to.toLowerCase(); - }) - .map((log) => { - return decodeEventLog({ abi, ...log }); - }) - .find((decodedLog) => decodedLog?.eventName === eventName); - if (!event) { - throw new Error("Event not found in logs"); - } - return event; -}; - export default function CreatePageContent() { const router = useRouter(); const { captureException, track } = useAnalytics(); // FIXME: analytics - const [tokens, setTokens] = useState([]); // NOTE: functionally max is 3 tokens + const [tokens, setTokens] = useState([]); // NOTE: functionally max is 3 tokens FIXME: use TokenInput!!! const [poolType, setPoolType] = useState(PoolType.ComposableStable); - const [baseAmount, setBaseAmount] = useState(""); - const [quoteAmounts, setQuoteAmounts] = useState([]); + const [tokenAmounts, setTokenAmounts] = useState([]); const [isDupePool, setIsDupePool] = useState(false); const [dupePool, setDupePool] = useState(null); const [errorMessage, setErrorMessage] = useState(null); const [enableLiquidityInput, setEnableLiquidityInput] = useState(false); - const { account, config: beraConfig } = useBeraJs(); + const { account } = useBeraJs(); const publicClient = usePublicClient(); - const [baseToken, quoteTokens] = useMemo(() => { - // FIXME this is an awkward way to store these, and we should really be storing BigInt amounts as well - return [tokens[0], tokens.slice(1)]; - }, [tokens]); - - // check for token approvals + // check for token approvals FIXME: we should not be including slippage in this calculation, its messing up the approval amount check const { needsApproval: tokensNeedApproval, refresh: refreshAllowances } = useMultipleTokenApprovalsWithSlippage( - tokens.map((token, index) => ({ - address: token.address, - amount: - token === baseToken ? baseAmount : quoteAmounts[index - 1] ?? "0", - decimals: token.decimals, - exceeding: false, - })), + tokens.map( + (token, index) => + ({ + symbol: token.symbol, + name: token.name, + address: token.address, + amount: tokenAmounts[index] ?? "0", + decimals: token.decimals, + exceeding: false, + } as TokenInput), + ), balancerVaultAddress, ); // Check relayer approval const { ModalPortal: ModalPortalRelayerApproval, - data: isRelayerApproved, + swr: { data: isRelayerApproved, isLoading: isLoadingRelayerStatus }, writeApproval: approveRelayer, isLoading: isRelayerApprovalLoading, isError: isRelayerApprovalError, @@ -116,7 +80,7 @@ export default function CreatePageContent() { useEffect(() => { if (isRelayerApprovalError) { setErrorMessage( - `Error approving pool creation helper on vault: ${isRelayerApprovalError?.message}`, + `Error approving pool creation helper on vault: ${isRelayerApprovalError}`, ); } }, [isRelayerApprovalError]); @@ -133,12 +97,8 @@ export default function CreatePageContent() { }); }; - const handleBaseAssetAmountChange = (amount: string) => { - setBaseAmount(amount); - }; - - const handleQuoteAssetAmountChange = (amount: string, index: number) => { - setQuoteAmounts((prev) => { + const handleTokenAmountChange = (amount: string, index: number) => { + setTokenAmounts((prev) => { const newAmounts = [...prev]; newAmounts[index] = amount; return newAmounts; @@ -192,17 +152,16 @@ export default function CreatePageContent() { actionType: TransactionActionType.CREATE_POOL, }); - // check for duplicates + // Check for duplicate pools useEffect(() => { - if (!baseToken || !quoteTokens.length) return; + if (tokens.length === 0) return; if (isLoadingPools) return; const isDupe = pools?.find((pool) => { - const hasAllTokens = [baseToken, ...quoteTokens].every((token) => - pool?.tokenAddresses?.includes(token?.address.toLowerCase()), + const hasAllTokens = tokens.every((token) => + pool?.tokenAddresses?.includes(token.address.toLowerCase()), ); if (hasAllTokens && pool?.poolType.toString() === poolType.toString()) { - // FIXME: switch to v3 query for this reason setDupePool(pool); return true; } @@ -211,34 +170,24 @@ export default function CreatePageContent() { }); setIsDupePool(!!isDupe); - }, [baseToken, quoteTokens, pools, isLoadingPools]); - - // FIXME: we need to raise if the amount exceeds balance as an error message + }, [tokens, pools, isLoadingPools]); // update the form state if the user changes the pool type (i.e. let them input liquidity again) useEffect(() => { - const requiredQuoteTokensLength = poolType === PoolType.Weighted ? 3 : 2; + // FIXME this max/min doesnt follow the rules + const requiredTokensLength = poolType === PoolType.Weighted ? 3 : 2; setTokens((prevTokens) => { - if (prevTokens.length > requiredQuoteTokensLength) { - return prevTokens.slice(0, requiredQuoteTokensLength); + if (prevTokens.length > requiredTokensLength) { + return prevTokens.slice(0, requiredTokensLength); } return prevTokens; }); - setQuoteAmounts((prevAmounts) => { - const requiredQuoteAmountsLength = requiredQuoteTokensLength - 1; - if ( - prevAmounts.length !== requiredQuoteAmountsLength || - prevAmounts.some( - (amount, index) => - index >= requiredQuoteAmountsLength && amount !== "", - ) - ) { - const updatedAmounts = [ - ...prevAmounts.slice(0, requiredQuoteAmountsLength), - ]; - while (updatedAmounts.length < requiredQuoteAmountsLength) { + setTokenAmounts((prevAmounts) => { + if (prevAmounts.length !== requiredTokensLength) { + const updatedAmounts = prevAmounts.slice(0, requiredTokensLength); + while (updatedAmounts.length < requiredTokensLength) { updatedAmounts.push(""); } return updatedAmounts; @@ -248,8 +197,8 @@ export default function CreatePageContent() { // Determine if liquidity input should be enabled if ( - baseToken && - tokens.length === requiredQuoteTokensLength && + tokens && + tokens.length === requiredTokensLength && tokens.every((token) => token) && !isLoadingPools && !isDupePool && @@ -260,9 +209,8 @@ export default function CreatePageContent() { setEnableLiquidityInput(false); } }, [ - baseToken, tokens, - quoteAmounts, + tokenAmounts, poolType, isLoadingPools, isDupePool, @@ -407,21 +355,14 @@ export default function CreatePageContent() {
    - - {quoteTokens.map((token, index) => ( + {tokens.map((token, index) => ( - handleQuoteAssetAmountChange(amount, index) + handleTokenAmountChange(amount, index) } /> ))} @@ -453,7 +394,7 @@ export default function CreatePageContent() { {/* FIXME: pool name and pool symbol input */} -
    +

    Pool Name

    @@ -478,32 +419,32 @@ export default function CreatePageContent() { onClick={approveRelayer} className="mt-4 w-full" > - {isRelayerApprovalLoading + {isRelayerApprovalLoading || isLoadingRelayerStatus ? "Approving..." : "Approve Pool Creation Helper"} )} - {tokensNeedApproval.length > 0 && ( - // FIXME this has the WORST logic going on thanks to this base quote crap. it will never resolve quote above 1 right - t.address === tokensNeedApproval.at(0)?.address, - ) as Token - } - spender={balancerVaultAddress} - onApproval={() => refreshAllowances()} - /> - )} + {tokensNeedApproval.length > 0 && + (() => { + const approvalTokenIndex = tokens.findIndex( + (t) => t.address === tokensNeedApproval[0]?.address, + ); + const approvalToken = tokens[approvalTokenIndex] as Token; + const approvalAmount = parseUnits( + tokenAmounts[approvalTokenIndex] || "0", + approvalToken?.decimals ?? 18, + ); + + return ( + refreshAllowances()} + /> + ); + })()} {/* Pool creation button itself */} @@ -531,12 +472,9 @@ export default function CreatePageContent() { const tokenRateCacheDurations = tokens.map(() => BigInt(100)); const exemptFromYieldProtocolFeeFlag = tokens.map(() => false); const amplificationParameter = BigInt(62); - const amountsIn = [ - parseUnits(baseAmount, baseToken?.decimals ?? 18), - ...quoteAmounts.map((amt, idx) => - parseUnits(amt, quoteTokens[idx]?.decimals ?? 18), - ), - ]; + const amountsIn = tokens.map((token, index) => + parseUnits(tokenAmounts[index] || "0", token.decimals ?? 18), + ); const owner = account; const isStablePool = poolType === PoolType.ComposableStable || diff --git a/apps/hub/src/hooks/usePoolCreationRelayerApproval.ts b/apps/hub/src/hooks/usePoolCreationRelayerApproval.ts index f216db4f2..e8fcae13d 100644 --- a/apps/hub/src/hooks/usePoolCreationRelayerApproval.ts +++ b/apps/hub/src/hooks/usePoolCreationRelayerApproval.ts @@ -27,7 +27,16 @@ export type UseApproveRelayerArgs = { export const usePoolCreationRelayerApproval = ( { relayerAddress, poolCreationHelper }: UseApproveRelayerArgs, options?: DefaultHookOptions, -): DefaultHookReturnType => { +): { + writeApproval: () => Promise; + ModalPortal: any; + isLoading: boolean; + isError: boolean; + isSuccess: boolean; + refresh: () => void; + swr: DefaultHookReturnType; +} => { + // TODO: this ought to be split into two things: a usePoll... and a useApproval... const { account } = useBeraJs(); const publicClient = usePublicClient(); @@ -69,12 +78,17 @@ export const usePoolCreationRelayerApproval = ( }); }; - const swrResponse = useSWR(QUERY_KEY, checkApprovalStatus, { - ...options?.opts, - }); + // @ts-ignore typing hell FIXME + const swrResponse: DefaultHookReturnType = useSWR( + QUERY_KEY, + checkApprovalStatus, + { + ...options?.opts, + }, + ); return { - ...swrResponse, + swr: swrResponse, writeApproval, ModalPortal, isLoading,