{"version":3,"file":"BpNaK5rK.js","sources":["../../../../../../src/lib/utils/liquidityUtils.ts","../../../../../../src/lib/stores/liquidityStore.ts","../../../../../../src/lib/components/liquidity/modals/ConfirmLiquidityModal.svelte","../../../../../../src/lib/utils/formUtils.ts"],"sourcesContent":["import { BigNumber } from 'bignumber.js';\nimport { get } from 'svelte/store';\nimport { currentUserBalancesStore } from '$lib/stores/tokenStore';\nimport { calculateLiquidityAmounts } from \"$lib/api/pools\";\n\n/**\n * Formats a display value based on token decimals\n */\nexport function formatDisplayValue(value: string, tokenDecimals: number): string {\n if (!value || value === \"0\") return \"0\";\n \n const bn = new BigNumber(value).dividedBy(Math.pow(10, tokenDecimals));\n return bn.toFormat(tokenDecimals, BigNumber.ROUND_DOWN, {\n groupSeparator: ',',\n decimalSeparator: '.'\n });\n}\n\n/**\n * Validates if a string is a valid number input\n */\nexport function isValidNumber(value: string): boolean {\n if (!value) return true;\n // Remove commas and underscores first\n const cleanValue = value.replace(/[,_]/g, '');\n // Allow numbers with optional decimal point and optional scientific notation\n const regex = /^[0-9]*\\.?[0-9]*(?:[eE][+-]?[0-9]+)?$/;\n return regex.test(cleanValue) && !isNaN(Number(cleanValue));\n}\n\n/**\n * Calculates the maximum amount considering transfer fees.\n */\nexport async function calculateMaxAmount(\n token: FE.Token, \n rawBalance: string, \n feeMultiplier: number = 1\n): Promise {\n try {\n if (!token) return \"0\";\n if (!token.fee && !token.fee_fixed) { throw new Error(\"Could not determine token fee\"); }\n\n const balanceMicro = new BigNumber(rawBalance.replace(/_/g, ''));\n const tokenFeeMicro = new BigNumber(token.fee_fixed?.toString().replace(/_/g, '') || '0');\n const totalFeesMicro = tokenFeeMicro.multipliedBy(feeMultiplier);\n\n // Subtract fee from the balance\n const maxAmountMicro = balanceMicro.minus(totalFeesMicro);\n\n if (maxAmountMicro.isLessThan(0) || maxAmountMicro.isNaN()) {\n throw new Error(\"Insufficient balance to cover fees\");\n }\n\n if (maxAmountMicro.isZero()) {\n return \"0\";\n }\n\n // Convert microtokens → tokens\n const decimals = token.decimals ?? 6;\n const maxAmountTokens = maxAmountMicro.dividedBy(Math.pow(10, decimals));\n\n return maxAmountTokens.toFormat(decimals, BigNumber.ROUND_DOWN, {\n groupSeparator: ',',\n decimalSeparator: '.'\n });\n } catch (err) {\n throw new Error(err instanceof Error ? err.message : \"Failed to calculate max amount\");\n }\n}\n\n/**\n * Formats a number with commas while preserving decimal places\n */\nexport function formatWithCommas(value: string): string {\n if (!value) return \"0\";\n const parts = value.split('.');\n parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n return parts.join('.');\n}\n\n/**\n * Checks if a user has insufficient balance for a liquidity operation\n */\nexport function hasInsufficientBalance(\n amount0: string,\n amount1: string,\n token0: FE.Token,\n token1: FE.Token\n): boolean {\n if (!token0 || !token1) return false;\n try {\n const deposit0 = new BigNumber(amount0.replace(/[,_]/g, ''));\n const deposit1 = new BigNumber(amount1.replace(/[,_]/g, ''));\n\n // Grab balances from our token store\n const storeValue = get(currentUserBalancesStore);\n const balance0 = storeValue[token0.canister_id];\n const balance1 = storeValue[token1.canister_id];\n\n if (!balance0 || !balance1) return true;\n\n // Convert balances to tokens\n const balance0Tokens = new BigNumber(balance0.in_tokens.toString() ?? '0')\n .dividedBy(new BigNumber(10).pow(token0.decimals));\n const balance1Tokens = new BigNumber(balance1.in_tokens.toString() ?? '0')\n .dividedBy(new BigNumber(10).pow(token1.decimals));\n\n // Convert fee_fixed (microtokens) to tokens\n const fee0Tokens = new BigNumber(token0.fee_fixed ?? '0')\n .dividedBy(new BigNumber(10).pow(token0.decimals));\n const fee1Tokens = new BigNumber(token1.fee_fixed ?? '0')\n .dividedBy(new BigNumber(10).pow(token1.decimals));\n\n // Compare deposit + fee to the user's token balances\n const insufficientToken0 = deposit0.plus(fee0Tokens).isGreaterThan(balance0Tokens);\n const insufficientToken1 = deposit1.plus(fee1Tokens).isGreaterThan(balance1Tokens);\n\n return insufficientToken0 || insufficientToken1;\n } catch (err) {\n console.error(\"Error in hasInsufficientBalance:\", err);\n return true;\n }\n}\n\n/**\n * Gets the appropriate button text based on form state\n */\nexport function getButtonText(\n token0: FE.Token | null,\n token1: FE.Token | null,\n poolExists: boolean,\n hasInsufficientBalance: boolean,\n amount0: string,\n amount1: string,\n loading: boolean,\n loadingState: string\n): string {\n if (!token0 || !token1) return \"Select Tokens\";\n if (hasInsufficientBalance) return \"Insufficient Balance\";\n if (!amount0 || !amount1) return \"Enter Amounts\";\n if (loading) return loadingState || \"Loading...\";\n return \"Review Transaction\";\n}\n\n/**\n * Calculates and formats the pool ratio between two tokens\n */\nexport function calculatePoolRatio(\n token0: FE.Token | null,\n token1: FE.Token | null,\n amount0: string,\n amount1: string\n): string {\n if (token0 && token1 && amount0 && amount1) {\n const amt0 = new BigNumber(amount0.replace(/[,_]/g, ''));\n const amt1 = new BigNumber(amount1.replace(/[,_]/g, ''));\n if (amt0.isGreaterThan(0) && amt1.isGreaterThan(0)) {\n return `1 ${token0.symbol} = ${amt1.dividedBy(amt0).toFixed(6)} ${token1.symbol}`;\n }\n }\n return '';\n}\n\n/**\n * Calculates and formats the USD ratio between two tokens\n */\nexport function calculateUsdRatio(\n token0: FE.Token | null,\n token1: FE.Token | null\n): string {\n if (token0?.metrics.price && token1?.metrics.price) {\n const ratio = new BigNumber(token0.metrics.price).dividedBy(token1.metrics.price);\n return `1 ${token0.symbol} ≈ $${ratio.times(token1.metrics.price).toFixed(2)}`;\n }\n return '';\n}\n\n/**\n * Formats a large number for display (e.g., TVL, volume)\n */\nexport function formatLargeNumber(value: string | number | bigint, decimals: number = 2): string {\n const cleanValue = value.toString().replace(/_/g, '');\n const num = Number(cleanValue) / 1e6; // Convert from microdollars (or micro-units) to 'whole' units\n return new Intl.NumberFormat('en-US', {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(num);\n}\n\n/**\n * Handles input validation and formatting for liquidity amounts\n */\nexport function processLiquidityInput(\n value: string, \n maxDecimals: number,\n defaultValue: string = \"0\"\n): string {\n // Remove commas and underscores\n let cleanValue = value.replace(/[,_]/g, '');\n \n if (!isValidNumber(cleanValue)) {\n return defaultValue;\n }\n\n // If the string has leading zeroes, remove them unless it's \"0.\" or just \"0\"\n if (cleanValue.length > 1 && cleanValue.startsWith('0') && cleanValue[1] !== '.') {\n cleanValue = cleanValue.replace(/^0+/, '');\n }\n // If it starts with '.', prefix '0'\n if (cleanValue.startsWith('.')) {\n cleanValue = '0' + cleanValue;\n }\n\n // Handle decimal part up to maxDecimals\n if (cleanValue.includes('.')) {\n const [whole, decimal] = cleanValue.split('.');\n cleanValue = `${whole || '0'}.${decimal.slice(0, maxDecimals)}`;\n }\n\n // If empty or just '.', fallback\n if (!cleanValue || cleanValue === '.') {\n cleanValue = defaultValue;\n }\n\n // If there's a trailing dot (like \"123.\"), remove it\n if (cleanValue.endsWith('.')) {\n cleanValue = cleanValue.slice(0, -1);\n }\n\n return cleanValue;\n}\n\n/**\n * Validates if a pool exists for the given token pair\n */\nexport function findPool(\n token0: FE.Token | null,\n token1: FE.Token | null,\n pools: BE.Pool[]\n): BE.Pool | null {\n if (!token0 || !token1) return null;\n \n return pools.find(p => \n (p.address_0 === token0.canister_id && p.address_1 === token1.canister_id) ||\n (p.address_0 === token1.canister_id && p.address_1 === token0.canister_id)\n ) || null;\n}\n\n/**\n * Validates if the form inputs are valid for submission\n */\nexport function validateLiquidityForm(\n token0: FE.Token | null,\n token1: FE.Token | null,\n amount0: string,\n amount1: string,\n error: string | null,\n hasInsufficientBalance: boolean,\n pool: BE.Pool | null\n): boolean {\n return !!(\n token0 && \n token1 && \n parseFloat(amount0.replace(/[,_]/g, '')) > 0 && \n parseFloat(amount1.replace(/[,_]/g, '')) > 0 && \n !error && \n !hasInsufficientBalance && \n pool !== null\n );\n}\n\nexport function getPoolForTokenPair(\n token0: FE.Token | null,\n token1: FE.Token | null,\n pools: BE.Pool[]\n): BE.Pool | null {\n if (!token0 || !token1) return null;\n \n return pools.find(p => \n (p.address_0 === token0.canister_id && p.address_1 === token1.canister_id) ||\n (p.address_0 === token1.canister_id && p.address_1 === token0.canister_id)\n ) || null;\n}\n\nexport interface TokenPairState {\n token0: FE.Token | null;\n token1: FE.Token | null;\n amount0: string;\n amount1: string;\n error: string | null;\n}\n\nexport function validateTokenSelect(\n selectedToken: FE.Token,\n otherToken: FE.Token | null,\n allowedTokens: string[],\n defaultToken: string,\n tokens: FE.Token[]\n): { \n isValid: boolean;\n newToken: FE.Token | null;\n error?: string;\n} {\n if (!otherToken) { return { isValid: true, newToken: selectedToken };}\n const hasAllowedToken = allowedTokens.includes(selectedToken.symbol) || allowedTokens.includes(otherToken.symbol);\n const isDifferentTokens = selectedToken.symbol !== otherToken.symbol;\n const isValid = hasAllowedToken && isDifferentTokens\n\n if (!isValid) {\n const defaultTokenObj = tokens.find(t => t.symbol === defaultToken);\n return {\n isValid: false,\n newToken: defaultTokenObj || null,\n error: 'One token must be ICP or ckUSDT'\n };\n }\n\n return { isValid: true, newToken: selectedToken };\n}\n\n/**\n * Calculates token1 amount based on token0 amount and pool ratio\n * @param amount0 Amount of token0\n * @param token0 Token0 details\n * @param token1 Token1 details\n * @param pool Pool details\n * @returns Calculated amount of token1\n */\nexport async function calculateToken1FromPoolRatio(\n amount0: string,\n token0: FE.Token,\n token1: FE.Token,\n pool: BE.Pool\n): Promise {\n try {\n // Remove any commas from the input\n const sanitizedAmount0 = amount0.replace(/,/g, '');\n \n if (!sanitizedAmount0) {\n return \"0\";\n }\n\n const amount0BN = new BigNumber(sanitizedAmount0);\n if (amount0BN.isNaN() || amount0BN.lte(0)) {\n return \"0\";\n }\n\n // If pool is empty, we can't use the backend calculation\n const balance0BN = new BigNumber(pool.balance_0.toString());\n const balance1BN = new BigNumber(pool.balance_1.toString());\n \n if (balance0BN.lte(0) || balance1BN.lte(0)) {\n return \"0\";\n }\n \n // Convert to raw token amount (with decimals)\n const amount0Raw = BigInt(\n amount0BN.times(new BigNumber(10).pow(token0.decimals)).toFixed(0)\n );\n \n // Use backend service to calculate the corresponding token1 amount\n const result = await calculateLiquidityAmounts(\n token0.symbol,\n amount0Raw,\n token1.symbol\n );\n \n if (!result.Ok) {\n throw new Error(\"Failed to calculate liquidity amounts\");\n }\n \n // Convert the result back to a human-readable format\n const amount1BN = new BigNumber(result.Ok.amount_1.toString())\n .dividedBy(new BigNumber(10).pow(token1.decimals));\n \n // Format to 6 decimal places for display\n return amount1BN.toFixed(6);\n } catch (error) {\n console.error(\"Error calculating token1 from pool ratio:\", error);\n return \"0\";\n }\n}\n\n/**\n * Calculates token0 amount based on token1 amount and pool ratio\n * @param amount1 Amount of token1\n * @param token0 Token0 details\n * @param token1 Token1 details\n * @param pool Pool details\n * @returns Calculated amount of token0\n */\nexport async function calculateToken0FromPoolRatio(\n amount1: string,\n token0: FE.Token,\n token1: FE.Token,\n pool: BE.Pool\n): Promise {\n try {\n // Remove any commas from the input\n const sanitizedAmount1 = amount1.replace(/,/g, '');\n \n if (!sanitizedAmount1) {\n return \"0\";\n }\n\n const amount1BN = new BigNumber(sanitizedAmount1);\n if (amount1BN.isNaN() || amount1BN.lte(0)) {\n return \"0\";\n }\n\n // If pool is empty, we can't use the backend calculation\n const balance0BN = new BigNumber(pool.balance_0.toString());\n const balance1BN = new BigNumber(pool.balance_1.toString());\n \n if (balance0BN.lte(0) || balance1BN.lte(0)) {\n return \"0\";\n }\n \n // Since calculateLiquidityAmounts expects token0 amount as input,\n // we first need to estimate token0 amount based on pool ratio\n // Get raw balances from the pool\n const amount1Raw = amount1BN.times(new BigNumber(10).pow(token1.decimals));\n \n // Calculate the initial estimate using the pool ratio\n // amount0 = amount1 * (balance0 / balance1)\n const estimatedAmount0Raw = amount1Raw.times(balance0BN).dividedBy(balance1BN);\n \n // Convert to raw token amount (with decimals)\n const estimatedAmount0 = BigInt(estimatedAmount0Raw.toFixed(0));\n \n // Use backend service to calculate the corresponding token1 amount\n const result = await calculateLiquidityAmounts(\n token0.symbol,\n estimatedAmount0,\n token1.symbol\n );\n \n if (!result.Ok) {\n throw new Error(\"Failed to calculate liquidity amounts\");\n }\n \n // The backend might give us a different token1 amount than requested.\n // We need to scale our token0 amount to match the requested token1 amount.\n const resultAmount1BN = new BigNumber(result.Ok.amount_1.toString());\n const requestedAmount1Raw = amount1BN.times(new BigNumber(10).pow(token1.decimals));\n \n // Scale factor = requested amount1 / result amount1\n const scaleFactor = requestedAmount1Raw.dividedBy(resultAmount1BN);\n \n // Scale the token0 amount\n const scaledAmount0BN = new BigNumber(result.Ok.amount_0.toString())\n .times(scaleFactor)\n .dividedBy(new BigNumber(10).pow(token0.decimals));\n \n // Format to 6 decimal places for display\n return scaledAmount0BN.toFixed(6);\n } catch (error) {\n console.error(\"Error calculating token0 from pool ratio:\", error);\n return \"0\";\n }\n}\n\n/**\n * Calculates token1 amount based on token0 amount and price\n * @param amount0 Amount of token0\n * @param price Price of token0 in terms of token1\n * @returns Calculated amount of token1\n */\nexport function calculateToken1FromPrice(\n amount0: string,\n price: string\n): string {\n try {\n // Remove any commas from inputs\n const sanitizedAmount0 = amount0.replace(/,/g, '');\n const sanitizedPrice = price.replace(/,/g, '');\n \n if (!sanitizedAmount0 || !sanitizedPrice) {\n return \"0\";\n }\n \n const amount0BN = new BigNumber(sanitizedAmount0);\n const priceBN = new BigNumber(sanitizedPrice);\n \n if (amount0BN.isNaN() || priceBN.isNaN() || amount0BN.lte(0) || priceBN.lte(0)) {\n return \"0\";\n }\n \n // Calculate amount1 = amount0 * price using BigNumber\n const amount1BN = amount0BN.times(priceBN);\n return amount1BN.toString();\n } catch (error) {\n console.error(\"Error calculating token1 from price:\", error);\n return \"0\";\n }\n}\n\n/**\n * Calculates token0 amount based on token1 amount and price\n * @param amount1 Amount of token1\n * @param price Price of token0 in terms of token1\n * @returns Calculated amount of token0\n */\nexport function calculateToken0FromPrice(\n amount1: string,\n price: string\n): string {\n try {\n // Remove any commas from inputs\n const sanitizedAmount1 = amount1.replace(/,/g, '');\n const sanitizedPrice = price.replace(/,/g, '');\n \n if (!sanitizedAmount1 || !sanitizedPrice) {\n return \"0\";\n }\n \n const amount1BN = new BigNumber(sanitizedAmount1);\n const priceBN = new BigNumber(sanitizedPrice);\n \n if (amount1BN.isNaN() || priceBN.isNaN() || amount1BN.lte(0) || priceBN.lte(0)) {\n return \"0\";\n }\n \n // Calculate amount0 = amount1 / price using BigNumber\n const amount0BN = amount1BN.dividedBy(priceBN);\n return amount0BN.toString();\n } catch (error) {\n console.error(\"Error calculating token0 from price:\", error);\n return \"0\";\n }\n}\n\n/**\n * Calculates token amount based on percentage of balance\n * @param token Token details\n * @param balance Token balance in raw format\n * @param percentage Percentage to calculate (0-100)\n * @returns Calculated token amount\n */\nexport function calculateAmountFromPercentage(\n token: FE.Token,\n balance: string,\n percentage: number\n): string {\n try {\n if (!token || !balance) return \"0\";\n \n const balanceValue = new BigNumber(balance).div(\n new BigNumber(10).pow(token.decimals)\n );\n \n if (!balanceValue.isFinite() || balanceValue.isLessThanOrEqualTo(0)) return \"0\";\n \n // If it's 100% (MAX), subtract both the token fee and transaction fee\n const adjustedBalance =\n percentage === 100\n ? balanceValue.minus(new BigNumber(token.fee * 2))\n : balanceValue.times(percentage).div(100);\n \n // Return the raw value without formatting (no commas)\n return adjustedBalance.gt(0)\n ? adjustedBalance.toFixed(token.decimals)\n : \"0\";\n } catch (error) {\n console.error(\"Error calculating percentage amount:\", error);\n return \"0\";\n }\n}\n\n/**\n * Formats a number to non-zero decimal places\n * @param value Number to format\n * @returns Formatted string\n */\nexport function formatToNonZeroDecimal(value: number): string {\n if (isNaN(value) || value === 0) return \"0\";\n \n if (value < 0.01) {\n return value.toFixed(6);\n } else if (value < 1) {\n return value.toFixed(4);\n } else if (value < 10000) {\n return value.toFixed(2);\n } else {\n return value.toLocaleString(undefined, {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n });\n }\n}\n\n// Optimized calculation function with minimized parameters using BigNumber\nexport function calculateUserPoolPercentage(\n poolBalance0: bigint | undefined,\n poolBalance1: bigint | undefined,\n userAmount0: string | number | undefined,\n userAmount1: string | number | undefined,\n): string {\n if (!poolBalance0 || !poolBalance1 || !userAmount0 || !userAmount1) {\n return \"0\";\n }\n \n try {\n // Convert values to BigNumber with proper scaling\n const userAmount0BN = new BigNumber(userAmount0);\n const userAmount1BN = new BigNumber(userAmount1);\n \n const poolBalance0BN = new BigNumber(poolBalance0.toString());\n const poolBalance1BN = new BigNumber(poolBalance1.toString());\n \n if (poolBalance0BN.isZero() || poolBalance1BN.isZero()) return \"0\";\n \n // Calculate percentages based on both tokens\n const percentage0 = userAmount0BN.div(poolBalance0BN).times(100);\n const percentage1 = userAmount1BN.div(poolBalance1BN).times(100);\n \n // Use the average of both percentages (they should be very close)\n const averagePercentage = percentage0.plus(percentage1).div(2);\n \n // Format to 2 decimal places\n return formatToNonZeroDecimal(averagePercentage.toNumber());\n } catch (error) {\n console.error(\"Error calculating pool percentage:\", error);\n return \"0\";\n }\n}","import { get } from 'svelte/store';\nimport { livePools } from '$lib/stores/poolStore';\nimport { writable } from 'svelte/store';\n\ninterface LiquidityState {\n token0: FE.Token | null;\n token1: FE.Token | null;\n amount0: string;\n amount1: string;\n initialPrice: string;\n loading: boolean;\n error: string | null;\n showToken0Selector: boolean;\n showToken1Selector: boolean;\n tokenSelectorPosition: {\n x: number;\n y: number;\n height: number;\n windowHeight: number;\n windowWidth: number;\n } | null;\n pool: BE.Pool | null;\n}\n\nfunction createLiquidityStore() {\n const { subscribe, set, update } = writable({\n token0: null,\n token1: null,\n amount0: \"\",\n amount1: \"\",\n initialPrice: \"\",\n loading: false,\n error: null,\n showToken0Selector: false,\n showToken1Selector: false,\n tokenSelectorPosition: null,\n pool: null\n });\n\n return {\n subscribe,\n setToken: (index: 0 | 1, token: FE.Token) => {\n const pools = get(livePools);\n\n update(s => {\n let pool;\n if(index === 0) {\n pool = pools.find(pool => pool.address_0 === token.address && pool.address_1 === s.token1?.address) || null;\n } else {\n pool = pools.find(pool => pool.address_1 === token.address && pool.address_0 === s.token0?.address) || null;\n }\n\n return {\n ...s,\n [index === 0 ? 'token0' : 'token1']: token,\n pool: pool,\n showToken0Selector: false,\n showToken1Selector: false\n };\n });\n },\n setAmount: (index: 0 | 1, amount: string) => {\n update(s => ({\n ...s,\n [index === 0 ? 'amount0' : 'amount1']: amount\n }));\n },\n toggleTokenSelector: (index: 0 | 1, position: any) => {\n update(s => ({\n ...s,\n showToken0Selector: index === 0,\n showToken1Selector: index === 1,\n tokenSelectorPosition: position\n }));\n },\n closeTokenSelector: () => {\n update(s => ({\n ...s,\n showToken0Selector: false,\n showToken1Selector: false,\n tokenSelectorPosition: null\n }));\n },\n setError: (error: string | null) => {\n update(s => ({ ...s, error }));\n },\n setLoading: (loading: boolean) => {\n update(s => ({ ...s, loading }));\n },\n setInitialPrice: (price: string) => {\n update(s => ({\n ...s,\n initialPrice: price\n }));\n },\n resetAmounts: () => {\n update(s => ({\n ...s,\n amount0: \"\",\n amount1: \"\"\n }));\n },\n reset: () => {\n set({\n token0: null,\n token1: null,\n amount0: \"\",\n amount1: \"\",\n initialPrice: \"\",\n loading: false,\n error: null,\n showToken0Selector: false,\n showToken1Selector: false,\n tokenSelectorPosition: null,\n pool: null\n });\n },\n };\n}\n\nexport const liquidityStore = createLiquidityStore(); \n","\n\n\n
\n {#if error}\n
\n {error}\n
\n {/if}\n\n
\n
You will provide
\n\n
\n
\n
\n \n
\n {token0?.symbol}\n
\n
\n
\n {amount0}\n ${token0Value}\n
\n
\n\n
+
\n\n
\n
\n \n
\n {token1?.symbol}\n
\n
\n
\n {amount1}\n ${token1Value}\n
\n
\n
\n\n
\n
\n Total Value:\n ${totalValue}\n
\n
\n Exchange Rate:\n 1 {token0?.symbol} = {exchangeRate} {token1?.symbol}\n
\n
\n\n {#if isCreatingPool}\n \n
\n You are the first liquidity provider.\n
\n The ratio of tokens you add will set the price of this pool.\n
\n
\n {/if}\n
\n\n
\n \n Cancel\n \n \n
\n {isLoading ? \"Confirming...\" : \"Confirm\"}\n {#if isLoading}\n \n {/if}\n
\n \n
\n \n\n\n\n","export function handleFormattedNumberInput(\n value: string,\n cursorPosition: number,\n previousValue: string\n): {\n rawValue: string;\n formattedValue: string;\n newCursorPosition: number;\n} {\n // Remove existing commas\n let rawValue = value.replace(/,/g, '');\n \n if (rawValue === '' || !isValidNumber(rawValue)) {\n return {\n rawValue: '0',\n formattedValue: '',\n newCursorPosition: cursorPosition\n };\n }\n\n // Handle empty values or just decimal point\n if (!rawValue || rawValue === '.') {\n return {\n rawValue: '0',\n formattedValue: '',\n newCursorPosition: 0\n };\n }\n\n const formattedValue = formatWithCommas(rawValue);\n \n // Calculate new cursor position\n const beforeCursor = formattedValue.slice(0, cursorPosition);\n const commasBeforeCursor = (beforeCursor.match(/,/g) || []).length;\n const commasInOriginal = (previousValue.slice(0, cursorPosition).match(/,/g) || []).length;\n const commaDiff = commasBeforeCursor - commasInOriginal;\n \n return {\n rawValue,\n formattedValue,\n newCursorPosition: cursorPosition + commaDiff\n };\n}\n\nexport function isValidNumber(value: string): boolean {\n return /^\\d*\\.?\\d*$/.test(value);\n}\n\nexport function formatWithCommas(value: string): string {\n const parts = value.split('.');\n parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n return parts.join('.');\n}\n\nexport interface InputState {\n element: HTMLInputElement | null;\n focused: boolean;\n displayValue: string;\n}\n\nexport function createInputState(): InputState {\n return {\n element: null,\n focused: false,\n displayValue: \"\"\n };\n}\n\nexport function handleInputBlur(state: InputState) {\n state.focused = false;\n}\n\nexport function validateAndCleanInput(\n value: string,\n maxDecimals: number,\n defaultValue: string = \"0\"\n): string {\n // Remove commas and underscores\n let cleanValue = value.replace(/[,_]/g, \"\");\n\n if (!isValidNumber(cleanValue)) {\n return defaultValue;\n }\n\n // Handle decimal point\n if (cleanValue.includes(\".\")) {\n const [whole, decimal] = cleanValue.split(\".\");\n cleanValue = `${whole}.${decimal.slice(0, maxDecimals)}`;\n }\n\n // Remove leading zeros unless it's \"0.\" or just \"0\"\n if (cleanValue.length > 1 && cleanValue.startsWith(\"0\") && cleanValue[1] !== \".\") {\n cleanValue = cleanValue.replace(/^0+/, \"\");\n }\n\n // If empty or invalid after processing, set to default\n if (!cleanValue || cleanValue === \".\") {\n cleanValue = defaultValue;\n }\n\n return cleanValue;\n} "],"names":["async","calculateMaxAmount","token","rawBalance","feeMultiplier","fee","fee_fixed","Error","balanceMicro","BigNumber","replace","totalFeesMicro","_a","toString","multipliedBy","maxAmountMicro","minus","isLessThan","isNaN","isZero","decimals","dividedBy","Math","pow","toFormat","ROUND_DOWN","groupSeparator","decimalSeparator","err","message","calculateToken1FromPoolRatio","amount0","token0","token1","pool","sanitizedAmount0","amount0BN","lte","balance0BN","balance_0","balance1BN","balance_1","amount0Raw","BigInt","times","toFixed","result","calculateLiquidityAmounts","symbol","Ok","amount_1","error","console","calculateToken0FromPoolRatio","amount1","sanitizedAmount1","amount1BN","estimatedAmount0Raw","estimatedAmount0","resultAmount1BN","scaleFactor","amount_0","calculateToken1FromPrice","price","sanitizedPrice","priceBN","calculateToken0FromPrice","calculateAmountFromPercentage","balance","percentage","balanceValue","div","isFinite","isLessThanOrEqualTo","adjustedBalance","gt","calculateUserPoolPercentage","poolBalance0","poolBalance1","userAmount0","userAmount1","userAmount0BN","userAmount1BN","poolBalance0BN","poolBalance1BN","percentage0","percentage1","averagePercentage","plus","value","toNumber","toLocaleString","minimumFractionDigits","maximumFractionDigits","liquidityStore","subscribe","set","update","writable","initialPrice","loading","showToken0Selector","showToken1Selector","tokenSelectorPosition","setToken","index","pools","get","livePools","s","find","address_0","address","address_1","setAmount","amount","toggleTokenSelector","position","closeTokenSelector","setError","setLoading","setInitialPrice","resetAmounts","reset","createLiquidityStore","isCreatingPool","show","$.prop","$$props","onClose","modalKey","Date","now","target","isLoading","mounted","handleConfirm","$.get","$.set","parseTokenAmount","$liquidityStore","params","token_0","token_1","initial_price","parseFloat","toastStore","info","createPool","success","Promise","all","loadBalance","canister_id","currentUserPoolsStore","initialize","addLiquidityResult","addLiquidity","pollRequestStatus","handleCancel","onDestroy","token0Value","metrics","Number","token1Value","totalValue","exchangeRate","formatToNonZeroDecimal","$$render","consequent","consequent_1","consequent_2","$.set_attribute","img","logo_url","$.set_text","text_1","img_1","text_4","set_text","text_8","get$1","_g","_h","handleFormattedNumberInput","cursorPosition","previousValue","rawValue","test","isValidNumber","formattedValue","newCursorPosition","parts","split","join","formatWithCommas","beforeCursor","slice","match","length"],"mappings":"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"}