@cfxdevkit/defi-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +720 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +377 -0
- package/dist/index.d.ts +377 -0
- package/dist/index.js +684 -0
- package/dist/index.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contracts.ts","../src/usePoolTokens.ts"],"sourcesContent":["/**\n * AutomationManager contract ABI (subset) and helpers.\n *\n * The full artifact lives at:\n * contracts/artifacts/contracts/AutomationManager.sol/AutomationManager.json\n *\n * We only include the fragments needed by the frontend:\n * - createLimitOrder\n * - createDCAJob\n * - JobCreated event\n */\n\nimport type { Address } from 'viem';\n\nexport const AUTOMATION_MANAGER_ADDRESS: Address =\n (process.env.NEXT_PUBLIC_AUTOMATION_MANAGER_ADDRESS as Address | undefined) ??\n '0x0000000000000000000000000000000000000000';\n\n// ─── Minimal ERC-20 ABI (allowance + approve) ────────────────────────────────\n\nexport const ERC20_ABI = [\n {\n type: 'function',\n name: 'allowance',\n stateMutability: 'view',\n inputs: [\n { name: 'owner', type: 'address' },\n { name: 'spender', type: 'address' },\n ],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n type: 'function',\n name: 'approve',\n stateMutability: 'nonpayable',\n inputs: [\n { name: 'spender', type: 'address' },\n { name: 'amount', type: 'uint256' },\n ],\n outputs: [{ name: '', type: 'bool' }],\n },\n] as const;\n\n// ─── WCFX (Wrapped CFX) ABI ──────────────────────────────────────────────────\n// Follows the WETH pattern: deposit() wraps native CFX, withdraw() unwraps it.\n\nexport const WCFX_ABI = [\n {\n type: 'function',\n name: 'balanceOf',\n stateMutability: 'view',\n inputs: [{ name: 'account', type: 'address' }],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n type: 'function',\n name: 'allowance',\n stateMutability: 'view',\n inputs: [\n { name: 'owner', type: 'address' },\n { name: 'spender', type: 'address' },\n ],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n type: 'function',\n name: 'approve',\n stateMutability: 'nonpayable',\n inputs: [\n { name: 'spender', type: 'address' },\n { name: 'amount', type: 'uint256' },\n ],\n outputs: [{ name: '', type: 'bool' }],\n },\n {\n type: 'function',\n name: 'deposit',\n stateMutability: 'payable',\n inputs: [],\n outputs: [],\n },\n {\n type: 'function',\n name: 'withdraw',\n stateMutability: 'nonpayable',\n inputs: [{ name: 'wad', type: 'uint256' }],\n outputs: [],\n },\n] as const;\n\n/** Max uint256 — used as an unlimited approval amount for DCA jobs. */\nexport const MAX_UINT256 = 2n ** 256n - 1n;\n\nexport const AUTOMATION_MANAGER_ABI = [\n // ─── Custom Errors ─────────────────────────────────────────────────────────\n {\n type: 'error',\n name: 'DCACompleted',\n inputs: [{ name: 'jobId', type: 'bytes32' }],\n },\n {\n type: 'error',\n name: 'DCAIntervalNotReached',\n inputs: [{ name: 'nextExecution', type: 'uint256' }],\n },\n { type: 'error', name: 'EnforcedPause', inputs: [] },\n { type: 'error', name: 'ExpectedPause', inputs: [] },\n {\n type: 'error',\n name: 'InvalidParams',\n inputs: [{ name: 'reason', type: 'string' }],\n },\n {\n type: 'error',\n name: 'JobExpiredError',\n inputs: [{ name: 'jobId', type: 'bytes32' }],\n },\n {\n type: 'error',\n name: 'JobNotActive',\n inputs: [{ name: 'jobId', type: 'bytes32' }],\n },\n {\n type: 'error',\n name: 'JobNotFound',\n inputs: [{ name: 'jobId', type: 'bytes32' }],\n },\n {\n type: 'error',\n name: 'OwnableInvalidOwner',\n inputs: [{ name: 'owner', type: 'address' }],\n },\n {\n type: 'error',\n name: 'OwnableUnauthorizedAccount',\n inputs: [{ name: 'account', type: 'address' }],\n },\n {\n type: 'error',\n name: 'PriceConditionNotMet',\n inputs: [{ name: 'jobId', type: 'bytes32' }],\n },\n { type: 'error', name: 'ReentrancyGuardReentrantCall', inputs: [] },\n {\n type: 'error',\n name: 'SafeERC20FailedOperation',\n inputs: [{ name: 'token', type: 'address' }],\n },\n {\n type: 'error',\n name: 'SlippageTooHigh',\n inputs: [\n { name: 'requested', type: 'uint256' },\n { name: 'maxAllowed', type: 'uint256' },\n ],\n },\n {\n type: 'error',\n name: 'TooManyJobs',\n inputs: [{ name: 'user', type: 'address' }],\n },\n { type: 'error', name: 'Unauthorized', inputs: [] },\n { type: 'error', name: 'ZeroAddress', inputs: [] },\n\n // ─── Events ────────────────────────────────────────────────────────────────\n {\n type: 'event',\n name: 'JobCreated',\n anonymous: false,\n inputs: [\n { indexed: true, name: 'jobId', type: 'bytes32' },\n { indexed: true, name: 'owner', type: 'address' },\n { indexed: false, name: 'jobType', type: 'uint8' },\n ],\n },\n\n // ─── createLimitOrder ──────────────────────────────────────────────────────\n {\n type: 'function',\n name: 'createLimitOrder',\n stateMutability: 'nonpayable',\n inputs: [\n {\n name: 'params',\n type: 'tuple',\n components: [\n { name: 'tokenIn', type: 'address' },\n { name: 'tokenOut', type: 'address' },\n { name: 'amountIn', type: 'uint256' },\n { name: 'minAmountOut', type: 'uint256' },\n { name: 'targetPrice', type: 'uint256' },\n { name: 'triggerAbove', type: 'bool' },\n ],\n },\n { name: 'slippageBps', type: 'uint256' },\n { name: 'expiresAt', type: 'uint256' },\n ],\n outputs: [{ name: 'jobId', type: 'bytes32' }],\n },\n\n // ─── createDCAJob ──────────────────────────────────────────────────────────\n {\n type: 'function',\n name: 'createDCAJob',\n stateMutability: 'nonpayable',\n inputs: [\n {\n name: 'params',\n type: 'tuple',\n components: [\n { name: 'tokenIn', type: 'address' },\n { name: 'tokenOut', type: 'address' },\n { name: 'amountPerSwap', type: 'uint256' },\n { name: 'intervalSeconds', type: 'uint256' },\n { name: 'totalSwaps', type: 'uint256' },\n { name: 'swapsCompleted', type: 'uint256' },\n { name: 'nextExecution', type: 'uint256' },\n ],\n },\n { name: 'slippageBps', type: 'uint256' },\n { name: 'expiresAt', type: 'uint256' },\n ],\n outputs: [{ name: 'jobId', type: 'bytes32' }],\n },\n\n // ─── cancelJob ─────────────────────────────────────────────────────────────\n {\n type: 'function',\n name: 'cancelJob',\n stateMutability: 'nonpayable',\n inputs: [{ name: 'jobId', type: 'bytes32' }],\n outputs: [],\n },\n\n // ─── JobCancelled event ────────────────────────────────────────────────────\n {\n type: 'event',\n name: 'JobCancelled',\n anonymous: false,\n inputs: [\n { indexed: true, name: 'jobId', type: 'bytes32' },\n { indexed: true, name: 'canceller', type: 'address' },\n ],\n },\n] as const;\n","'use client';\n\n/**\n * usePoolTokens – resolves Swappi pools from the backend and enriches each\n * token with the connected user's on-chain balance.\n *\n * Resilience strategy – tokens and pairs only grow, never shrink:\n * - Two cumulative Maps are kept in refs (knownTokensRef, knownPairsRef).\n * - Every successful backend fetch MERGES into these maps; no entry is ever\n * removed. A flaky RPC response that temporarily drops some tokens cannot\n * make them disappear from the UI.\n * - The same merged set is written back to localStorage so the next mount\n * starts from the full union, not just the last partial response.\n * - Balance enrichment always works off knownTokensRef so balance updates\n * never reduce the visible token count.\n *\n * Caching strategy:\n * - Token metadata + pair topology → localStorage (key: cas_pool_meta_v1,\n * TTL 10 min). Read synchronously on mount so the dropdown renders at once.\n * - Backend /api/pools always runs in the background (stale-while-revalidate).\n * - All balanceOf calls are batched into a single Multicall3 request (1 RPC call).\n *\n * CFX (native) handling:\n * - Synthetic \"CFX (native)\" entry at CFX_NATIVE_ADDRESS (EIP-7528).\n * - WCFX ERC-20 is renamed to \"wCFX\".\n * - resolveTokenInAddress() maps the sentinel → WCFX before contract calls.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { type Address, createPublicClient, formatUnits, http } from 'viem';\nimport { confluxESpace, confluxESpaceTestnet } from 'viem/chains';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface TokenWithBalance {\n address: string;\n symbol: string;\n name: string;\n /**\n * Token decimals — `null` when GeckoTerminal did not publish them.\n * Consumers that need an accurate value for `parseUnits` / `formatUnits`\n * should fall back to an on-chain `decimals()` read when this is null.\n * Display-only callers can safely fall back to 18.\n */\n decimals: number | null;\n /** Token logo image URL (from GeckoTerminal), if available */\n logoURI?: string;\n /** Raw wei balance as string, '0' if no wallet connected */\n balanceWei: string;\n /** Human-readable balance, e.g. '123.45' */\n balanceFormatted: string;\n}\n\nexport interface PairInfo {\n address: string;\n token0: string;\n token1: string;\n}\n\nexport interface UsePoolTokensResult {\n tokens: TokenWithBalance[];\n pairs: PairInfo[];\n /** True only on first-ever load when there is no cached metadata yet. */\n loading: boolean;\n /** True while on-chain balances are being fetched (background enrichment). */\n balancesLoading: boolean;\n error: string | null;\n /** Non-null when balance RPC calls partially or fully failed (quota/node issue). */\n rpcWarning: string | null;\n refresh: () => void;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\nexport const CFX_NATIVE_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';\n\nconst WCFX_ADDRESSES: Record<string, string> = {\n testnet: '0x2ED3dddae5B2F321AF0806181FBFA6D049Be47d8',\n mainnet: '0x14b2D3bC65e74DAE1030EAFd8ac30c533c976A9b', // EIP-55 checksum: last char is lowercase b\n};\n\nexport function wcfxAddress(\n network = process.env.NEXT_PUBLIC_NETWORK ?? 'testnet'\n): string {\n return WCFX_ADDRESSES[network] ?? WCFX_ADDRESSES.testnet;\n}\n\nexport function resolveTokenInAddress(address: string): string {\n if (address.toLowerCase() === CFX_NATIVE_ADDRESS.toLowerCase()) {\n return wcfxAddress();\n }\n return address;\n}\n\n// ─── Cache ───────────────────────────────────────────────────────────────────\n\nconst CACHE_KEY = 'cas_pool_meta_v2';\nconst CACHE_TTL = 10 * 60 * 1000; // 10 minutes\n\ninterface PoolMeta {\n address: string; // always lower-cased\n symbol: string;\n name: string;\n decimals: number | null;\n logoURI?: string;\n}\n\ninterface PoolCache {\n tokens: PoolMeta[];\n pairs: PairInfo[];\n cachedAt: number;\n}\n\nfunction readCache(): PoolCache | null {\n if (typeof window === 'undefined') return null;\n try {\n const raw = localStorage.getItem(CACHE_KEY);\n if (!raw) return null;\n const c = JSON.parse(raw) as PoolCache;\n // Normalise addresses in old cache entries that might not be lowercased\n c.tokens = c.tokens.map((t) => ({\n ...t,\n address: t.address.toLowerCase(),\n }));\n c.pairs = c.pairs.map((p) => ({\n ...p,\n address: p.address.toLowerCase(),\n token0: p.token0.toLowerCase(),\n token1: p.token1.toLowerCase(),\n }));\n if (Date.now() - c.cachedAt > CACHE_TTL) return null;\n return c;\n } catch {\n return null;\n }\n}\n\n/** Read cache without TTL check – used as merge base even when stale. */\nfunction readCacheIgnoreTTL(): PoolCache | null {\n if (typeof window === 'undefined') return null;\n try {\n const raw = localStorage.getItem(CACHE_KEY);\n if (!raw) return null;\n const c = JSON.parse(raw) as PoolCache;\n c.tokens = c.tokens.map((t) => ({\n ...t,\n address: t.address.toLowerCase(),\n }));\n c.pairs = c.pairs.map((p) => ({\n ...p,\n address: p.address.toLowerCase(),\n token0: p.token0.toLowerCase(),\n token1: p.token1.toLowerCase(),\n }));\n return c;\n } catch {\n return null;\n }\n}\n\nfunction writeCache(tokens: PoolMeta[], pairs: PairInfo[]): void {\n try {\n localStorage.setItem(\n CACHE_KEY,\n JSON.stringify({\n tokens,\n pairs,\n cachedAt: Date.now(),\n } satisfies PoolCache)\n );\n } catch {\n // Storage quota exceeded or private-browsing mode — silently skip\n }\n}\n\n// ─── Union helpers ────────────────────────────────────────────────────────────\n\n/**\n * Merge fresh token metadata into the cumulative known-tokens map.\n * Existing entries are only overwritten if the fresh data has the same address\n * (metadata update e.g. decimals correction), but entries are never removed.\n */\nfunction mergeTokens(known: Map<string, PoolMeta>, fresh: PoolMeta[]): void {\n for (const t of fresh) {\n known.set(t.address.toLowerCase(), {\n ...t,\n address: t.address.toLowerCase(),\n });\n }\n}\n\n/**\n * Merge fresh pair info into the cumulative known-pairs map.\n * Key is sorted(token0, token1) so pair direction doesn't matter.\n */\nfunction mergePairs(known: Map<string, PairInfo>, fresh: PairInfo[]): void {\n for (const p of fresh) {\n const t0 = p.token0.toLowerCase();\n const t1 = p.token1.toLowerCase();\n const key = [t0, t1].sort().join('|');\n known.set(key, {\n address: p.address.toLowerCase(),\n token0: t0,\n token1: t1,\n });\n }\n}\n\n// ─── ABI ─────────────────────────────────────────────────────────────────────\n\nconst BALANCE_OF_ABI = [\n {\n name: 'balanceOf',\n type: 'function',\n stateMutability: 'view',\n inputs: [{ name: 'account', type: 'address' }],\n outputs: [{ name: '', type: 'uint256' }],\n },\n] as const;\n\n// ─── Helper functions ─────────────────────────────────────────────────────────\n\n/**\n * Returns tokens that are paired with tokenInAddress.\n * All address comparisons are case-insensitive.\n */\nexport function getPairedTokens(\n pairs: PairInfo[],\n allTokens: TokenWithBalance[],\n tokenInAddress: string\n): TokenWithBalance[] {\n if (!tokenInAddress) return allTokens;\n\n const incoming = tokenInAddress.toLowerCase();\n const wcfx = wcfxAddress().toLowerCase();\n const cfxNative = CFX_NATIVE_ADDRESS.toLowerCase();\n\n // Resolve CFX native → wCFX for pair lookups\n const resolved = incoming === cfxNative ? wcfx : incoming;\n\n // Build counterpart set (lowercased)\n const counterparts = new Set<string>();\n for (const p of pairs) {\n const t0 = p.token0.toLowerCase();\n const t1 = p.token1.toLowerCase();\n if (t0 === resolved) counterparts.add(t1);\n if (t1 === resolved) counterparts.add(t0);\n }\n\n // Replace wCFX with CFX native so users always see \"CFX\" not \"wCFX\",\n // unless the user IS selling CFX (then don't show CFX again on the other side).\n const wCfxPaired = counterparts.has(wcfx);\n counterparts.delete(wcfx);\n counterparts.delete(cfxNative); // always work from deduplicated ERC-20 set\n\n const results = allTokens.filter(\n (t) =>\n counterparts.has(t.address.toLowerCase()) &&\n t.address.toLowerCase() !== cfxNative &&\n t.address.toLowerCase() !== wcfx\n );\n\n // Inject CFX native at the top when wCFX was a counterpart and tokenIn is NOT CFX\n if (wCfxPaired && incoming !== cfxNative) {\n const cfxEntry = allTokens.find(\n (t) => t.address.toLowerCase() === cfxNative\n );\n if (cfxEntry) return [cfxEntry, ...results];\n }\n\n return results;\n}\n\n/** Build a zero-balance token list from raw metadata. */\nfunction metaToTokens(rawTokens: PoolMeta[]): TokenWithBalance[] {\n const wcfx = wcfxAddress().toLowerCase();\n return rawTokens.map((t) => ({\n ...t,\n address: t.address.toLowerCase(),\n symbol: t.address.toLowerCase() === wcfx ? 'wCFX' : t.symbol,\n name: t.address.toLowerCase() === wcfx ? 'Wrapped CFX' : t.name,\n logoURI: t.logoURI,\n balanceWei: '0',\n balanceFormatted: '0',\n }));\n}\n\nconst CFX_ENTRY_ZERO: TokenWithBalance = {\n address: CFX_NATIVE_ADDRESS,\n symbol: 'CFX',\n name: 'Conflux (native)',\n decimals: 18,\n balanceWei: '0',\n balanceFormatted: '0',\n};\n\n/**\n * Build the CFX (native) token entry, borrowing wCFX's logoURI so the icon\n * shows in the selector even though CFX is a synthetic (EIP-7528) entry.\n */\nfunction cfxEntryFrom(knownTokens: Map<string, PoolMeta>): TokenWithBalance {\n const wcfx = wcfxAddress().toLowerCase();\n const logo = knownTokens.get(wcfx)?.logoURI;\n return logo ? { ...CFX_ENTRY_ZERO, logoURI: logo } : CFX_ENTRY_ZERO;\n}\n\n/** Wrap a promise with a timeout.\n * - Resolves to `null` on timeout/network error (distinguishable from a real 0).\n * - `silent` skips the console.warn — use only for expected failures (e.g.\n * multicall balanceOf on non-ERC20 contracts, handled via allowFailure). */\nfunction withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n silent = false\n): Promise<T | null> {\n return new Promise((resolve) => {\n const t = setTimeout(() => {\n if (!silent)\n console.warn('[usePoolTokens] RPC call timed out after', ms, 'ms');\n resolve(null);\n }, ms);\n promise.then(\n (v) => {\n clearTimeout(t);\n resolve(v);\n },\n (err) => {\n clearTimeout(t);\n if (!silent) {\n const msg: string = err?.message ?? String(err);\n console.warn('[usePoolTokens] RPC call rejected:', msg);\n }\n resolve(null);\n }\n );\n });\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────────────────\n\nexport function usePoolTokens(\n userAddress: string | undefined\n): UsePoolTokensResult {\n const [tokens, setTokens] = useState<TokenWithBalance[]>([]);\n const [pairs, setPairs] = useState<PairInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [balancesLoading, setBalancesLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [rpcWarning, setRpcWarning] = useState<string | null>(null);\n const [_tick, setTick] = useState(0);\n\n const refresh = useCallback(() => setTick((t) => t + 1), []);\n\n // Auto-refresh balances every 30 s while a wallet is connected\n useEffect(() => {\n if (!userAddress) return;\n const id = setInterval(() => setTick((t) => t + 1), 30_000);\n return () => clearInterval(id);\n }, [userAddress]);\n\n // ── Cumulative union state ─────────────────────────────────────────────────\n // These maps only grow; entries are never removed.\n const knownTokensRef = useRef<Map<string, PoolMeta>>(new Map());\n const knownPairsRef = useRef<Map<string, PairInfo>>(new Map());\n\n /** Snapshot the current union maps into React state. */\n const applyKnown = useCallback(() => {\n const tokenArr = Array.from(knownTokensRef.current.values());\n const pairArr = Array.from(knownPairsRef.current.values());\n setPairs(pairArr);\n return { tokenArr, pairArr };\n }, []);\n\n // ── Phase 2: enrich with on-chain balances ─────────────────────────────────\n const fetchBalances = useCallback(\n async (owner: string, signal: AbortSignal) => {\n // Always work off the FULL known set, not just whatever the last fetch returned.\n // Exclude pair LP addresses — they live in knownPairs, not tokens, but\n // occasionally leak in; they have no balanceOf and would spam RPC errors.\n const pairAddrs = new Set(\n Array.from(knownPairsRef.current.values()).map((p) =>\n p.address.toLowerCase()\n )\n );\n const rawTokens = Array.from(knownTokensRef.current.values()).filter(\n (t) => !pairAddrs.has(t.address.toLowerCase())\n );\n\n setBalancesLoading(true);\n try {\n const network = (process.env.NEXT_PUBLIC_NETWORK ?? 'testnet') as\n | 'testnet'\n | 'mainnet';\n const rpcUrl =\n network === 'testnet'\n ? 'https://evmtestnet.confluxrpc.com'\n : 'https://evm.confluxrpc.com';\n const chain =\n network === 'testnet' ? confluxESpaceTestnet : confluxESpace;\n const client = createPublicClient({ chain, transport: http(rpcUrl) });\n\n const CALL_TIMEOUT = 30_000;\n\n console.log('[usePoolTokens] fetchBalances start', {\n owner,\n network,\n rpcUrl,\n ercTokenCount: rawTokens.length,\n });\n\n // ── Fetch native CFX balance (always, even if no ERC-20 tokens) ──\n // Not run through multicall — it's a plain eth_getBalance request.\n const nativeResult = await withTimeout(\n client.getBalance({ address: owner as Address }),\n CALL_TIMEOUT\n );\n if (signal.aborted) return;\n console.log(\n '[usePoolTokens] fetchBalances nativeResult:',\n nativeResult?.toString() ?? 'null (failed)'\n );\n\n // ── Fetch ERC-20 balances via Multicall3 (skip if no tokens yet) ─\n const MULTICALL3 =\n '0xcA11bde05977b3631167028862bE2a173976CA11' as const;\n let ercResults: (bigint | null)[] = [];\n let multicallResult: Awaited<\n ReturnType<typeof client.multicall>\n > | null = null;\n\n if (rawTokens.length > 0) {\n // Pass `silent: true` — per-token failures inside multicall are expected\n // (allowFailure:true handles them), so we don't want noise per token.\n multicallResult = await withTimeout(\n client.multicall({\n contracts: rawTokens.map((t) => ({\n address: t.address as Address,\n abi: BALANCE_OF_ABI,\n functionName: 'balanceOf' as const,\n args: [owner as Address],\n })),\n allowFailure: true,\n multicallAddress: MULTICALL3,\n }),\n CALL_TIMEOUT,\n /* silent */ true\n );\n ercResults = multicallResult\n ? (multicallResult as { status: string; result: unknown }[]).map(\n (r) => (r.status === 'success' ? (r.result as bigint) : null)\n )\n : rawTokens.map(() => null);\n const successCount = ercResults.filter((r) => r !== null).length;\n console.log(\n `[usePoolTokens] fetchBalances multicall: ${successCount}/${rawTokens.length} succeeded, full timeout: ${multicallResult === null}`\n );\n }\n if (signal.aborted) return;\n\n // Show warning banner only for hard failures (full timeout), not per-token\n // allowFailure rejections which are expected for non-ERC20 addresses.\n if (nativeResult === null) {\n const msg = 'CFX balance fetch failed — RPC may be unavailable.';\n console.warn('[usePoolTokens] fetchBalances:', msg);\n setRpcWarning(msg);\n } else if (rawTokens.length > 0 && multicallResult === null) {\n const msg =\n 'Token balance fetch timed out — RPC may be overloaded. Showing last known balances.';\n console.warn('[usePoolTokens] fetchBalances:', msg);\n setRpcWarning(msg);\n } else {\n setRpcWarning(null);\n }\n\n const wcfx = wcfxAddress().toLowerCase();\n const enriched: TokenWithBalance[] = rawTokens.map((t, i) => {\n const raw = ercResults[i];\n return {\n ...t,\n symbol: t.address.toLowerCase() === wcfx ? 'wCFX' : t.symbol,\n name: t.address.toLowerCase() === wcfx ? 'Wrapped CFX' : t.name,\n // null means the call timed out — balanceWei stays '0' for now;\n // the merge step below reinstates any previously-known balance.\n balanceWei: raw !== null ? raw.toString() : '0',\n balanceFormatted:\n raw !== null ? formatUnits(raw, t.decimals ?? 18) : '0',\n };\n });\n\n enriched.sort((a, b) => {\n const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);\n return diff > 0n\n ? 1\n : diff < 0n\n ? -1\n : a.symbol.localeCompare(b.symbol);\n });\n\n const nativeBal = nativeResult ?? 0n;\n const cfxWithBalance: TokenWithBalance = {\n ...cfxEntryFrom(knownTokensRef.current),\n balanceWei: nativeBal.toString(),\n balanceFormatted: formatUnits(nativeBal, 18),\n };\n\n if (!signal.aborted) {\n // Merge: if a call timed out (null) and we had a previous non-zero\n // balance for that token, keep it — prevents the wallet-filtered\n // Token In list from shrinking due to intermittent RPC failures.\n setTokens((prev) => {\n const prevMap = new Map(\n prev.map((t) => [t.address.toLowerCase(), t.balanceWei])\n );\n const timedOut = ercResults.map((r) => r === null);\n const merged = [\n // CFX: keep previous balance if native call timed out\n nativeResult !== null\n ? cfxWithBalance\n : (prev.find((t) => t.address === CFX_NATIVE_ADDRESS) ??\n cfxWithBalance),\n ...enriched.map((t, i) => {\n if (timedOut[i]) {\n const prevBal = prevMap.get(t.address.toLowerCase()) ?? '0';\n if (prevBal !== '0') {\n return {\n ...t,\n balanceWei: prevBal,\n balanceFormatted: formatUnits(\n BigInt(prevBal),\n t.decimals ?? 18\n ),\n };\n }\n }\n return t;\n }),\n ];\n const [cfxEntry, ...rest] = merged;\n rest.sort((a, b) => {\n const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);\n return diff > 0n\n ? 1\n : diff < 0n\n ? -1\n : a.symbol.localeCompare(b.symbol);\n });\n return [cfxEntry, ...rest];\n });\n }\n } catch (err: unknown) {\n console.error(\n '[usePoolTokens] fetchBalances threw:',\n (err as Error)?.message ?? err\n );\n setRpcWarning('Balance fetch failed — see browser console for details');\n // balance fetch errors are non-fatal; existing list stays visible\n } finally {\n if (!signal.aborted) setBalancesLoading(false);\n }\n },\n []\n );\n\n // ── Tick-watcher: re-fetch on-chain balances on every refresh() / 30s tick ─\n // The main Phase-1 effect already fetches balances on first mount via\n // fetchBalances(userAddress, signal). This separate effect runs on every\n // subsequent tick so manual refresh calls and the 30 s interval actually work.\n const tickMountedRef = useRef(false);\n // biome-ignore lint/correctness/useExhaustiveDependencies: _tick is intentionally a trigger-only dep, not referenced inside the effect body\n useEffect(() => {\n // Skip tick=0 (initial mount) — Phase 1 handles the first fetch.\n if (!tickMountedRef.current) {\n tickMountedRef.current = true;\n return;\n }\n if (!userAddress) return;\n const ctrl = new AbortController();\n void fetchBalances(userAddress, ctrl.signal);\n return () => ctrl.abort();\n }, [_tick, userAddress, fetchBalances]);\n\n // ── Phase 1: metadata (cache-first → background refresh) ──────────────────\n useEffect(() => {\n const abortCtrl = new AbortController();\n const { signal } = abortCtrl;\n\n // ── Seed known maps from localStorage (TTL-ignored so we always have a base)\n const persisted = readCacheIgnoreTTL();\n if (persisted) {\n mergeTokens(knownTokensRef.current, persisted.tokens);\n mergePairs(knownPairsRef.current, persisted.pairs);\n }\n\n // Step A: paint from valid cache immediately\n const cached = readCache(); // TTL-checked\n if (cached) {\n const { tokenArr } = applyKnown();\n // Use a functional update so we never clobber real balances that are\n // already in state (e.g. on a background tick refresh). If prev is\n // already populated we merge — same pattern as Step B — so the balance\n // column stays stable and doesn't flash to 0 while Step B's\n // fetchBalances is in flight.\n const cfxBase = cfxEntryFrom(knownTokensRef.current);\n const freshMeta = metaToTokens(tokenArr);\n setTokens((prev) => {\n if (prev.length === 0) {\n return [cfxBase, ...freshMeta];\n }\n const existing = new Map(prev.map((t) => [t.address.toLowerCase(), t]));\n const prevCfx = existing.get(CFX_NATIVE_ADDRESS.toLowerCase());\n const cfxEntry = prevCfx\n ? {\n ...cfxBase,\n balanceWei: prevCfx.balanceWei,\n balanceFormatted: prevCfx.balanceFormatted,\n }\n : cfxBase;\n const rest = freshMeta.map(\n (t) => existing.get(t.address.toLowerCase()) ?? t\n );\n rest.sort((a, b) => {\n const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);\n return diff > 0n\n ? 1\n : diff < 0n\n ? -1\n : a.symbol.localeCompare(b.symbol);\n });\n return [cfxEntry, ...rest];\n });\n setLoading(false);\n if (userAddress) {\n fetchBalances(userAddress, signal);\n }\n }\n // Step B: always re-fetch from backend (stale-while-revalidate)\n (async () => {\n try {\n const res = await fetch('/api/pools', { signal });\n if (!res.ok) throw new Error(`Backend returned ${res.status}`);\n const data = (await res.json()) as {\n tokens: PoolMeta[];\n pairs: PairInfo[];\n };\n\n if (signal.aborted) return;\n\n // Merge fresh data into the cumulative union — never shrink\n mergeTokens(knownTokensRef.current, data.tokens);\n mergePairs(knownPairsRef.current, data.pairs);\n\n const mergedTokens = Array.from(knownTokensRef.current.values());\n const mergedPairs = Array.from(knownPairsRef.current.values());\n\n // Persist the merged union (not just the fresh slice) to localStorage\n writeCache(mergedTokens, mergedPairs);\n\n // Update pairs in state\n setPairs(mergedPairs);\n\n // Show tokens right after the backend responds. Use a functional\n // update so we don't clobber balances that Step-A's fetchBalances may\n // have already filled in (warm-start case).\n // - Cold start (prev=[]): initialise with zero-balance metadata so\n // the token selectors appear immediately.\n // - Warm start (prev has enriched balances): union-in any newly-seen\n // tokens but preserve existing balances — avoids flash-to-zero.\n const freshMeta = metaToTokens(mergedTokens);\n // Capture logo-aware CFX entry now (knownTokensRef has wCFX logo after merge)\n const cfxBase = cfxEntryFrom(knownTokensRef.current);\n setTokens((prev) => {\n if (prev.length === 0) {\n return [cfxBase, ...freshMeta];\n }\n const existing = new Map(\n prev.map((t) => [t.address.toLowerCase(), t])\n );\n // Preserve existing balance but ensure logo is populated\n const prevCfx = existing.get(CFX_NATIVE_ADDRESS.toLowerCase());\n const cfxEntry = prevCfx\n ? {\n ...cfxBase,\n balanceWei: prevCfx.balanceWei,\n balanceFormatted: prevCfx.balanceFormatted,\n }\n : cfxBase;\n const rest = freshMeta.map(\n (t) => existing.get(t.address.toLowerCase()) ?? t\n );\n rest.sort((a, b) => {\n const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);\n return diff > 0n\n ? 1\n : diff < 0n\n ? -1\n : a.symbol.localeCompare(b.symbol);\n });\n return [cfxEntry, ...rest];\n });\n setLoading(false);\n\n if (!userAddress) return;\n\n // Re-run balance enrichment over the full merged set. On warm start\n // this is a refresh; on cold start this is the first enrichment.\n await fetchBalances(userAddress, signal);\n } catch (err: unknown) {\n if (signal.aborted) return;\n const msg = (err as Error)?.message ?? String(err);\n console.warn('[usePoolTokens] /api/pools fetch failed:', msg);\n if (cached || persisted) {\n // Degraded mode: we already have data on screen, just clear the spinner\n setLoading(false);\n } else {\n setError(msg);\n setLoading(false);\n }\n }\n })();\n\n return () => abortCtrl.abort();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [userAddress, applyKnown, fetchBalances]);\n\n return {\n tokens,\n pairs,\n loading,\n balancesLoading,\n error,\n rpcWarning,\n refresh,\n };\n}\n"],"mappings":";AAcO,IAAM,6BACV,QAAQ,IAAI,0CACb;AAIK,IAAM,YAAY;AAAA,EACvB;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,IACrC;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,UAAU,CAAC;AAAA,EACzC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,MACnC,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IACpC;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,OAAO,CAAC;AAAA,EACtC;AACF;AAKO,IAAM,WAAW;AAAA,EACtB;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ,CAAC,EAAE,MAAM,WAAW,MAAM,UAAU,CAAC;AAAA,IAC7C,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,UAAU,CAAC;AAAA,EACzC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,IACrC;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,UAAU,CAAC;AAAA,EACzC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,MACnC,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IACpC;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,OAAO,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ,CAAC;AAAA,IACT,SAAS,CAAC;AAAA,EACZ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ,CAAC,EAAE,MAAM,OAAO,MAAM,UAAU,CAAC;AAAA,IACzC,SAAS,CAAC;AAAA,EACZ;AACF;AAGO,IAAM,cAAc,MAAM,OAAO;AAEjC,IAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,iBAAiB,MAAM,UAAU,CAAC;AAAA,EACrD;AAAA,EACA,EAAE,MAAM,SAAS,MAAM,iBAAiB,QAAQ,CAAC,EAAE;AAAA,EACnD,EAAE,MAAM,SAAS,MAAM,iBAAiB,QAAQ,CAAC,EAAE;AAAA,EACnD;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,UAAU,MAAM,SAAS,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,WAAW,MAAM,UAAU,CAAC;AAAA,EAC/C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC7C;AAAA,EACA,EAAE,MAAM,SAAS,MAAM,gCAAgC,QAAQ,CAAC,EAAE;AAAA,EAClE;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,MAAM,aAAa,MAAM,UAAU;AAAA,MACrC,EAAE,MAAM,cAAc,MAAM,UAAU;AAAA,IACxC;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,CAAC;AAAA,EAC5C;AAAA,EACA,EAAE,MAAM,SAAS,MAAM,gBAAgB,QAAQ,CAAC,EAAE;AAAA,EAClD,EAAE,MAAM,SAAS,MAAM,eAAe,QAAQ,CAAC,EAAE;AAAA;AAAA,EAGjD;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,QAAQ;AAAA,MACN,EAAE,SAAS,MAAM,MAAM,SAAS,MAAM,UAAU;AAAA,MAChD,EAAE,SAAS,MAAM,MAAM,SAAS,MAAM,UAAU;AAAA,MAChD,EAAE,SAAS,OAAO,MAAM,WAAW,MAAM,QAAQ;AAAA,IACnD;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,UACV,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,UACnC,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,UACpC,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,UACpC,EAAE,MAAM,gBAAgB,MAAM,UAAU;AAAA,UACxC,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,UACvC,EAAE,MAAM,gBAAgB,MAAM,OAAO;AAAA,QACvC;AAAA,MACF;AAAA,MACA,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,MACvC,EAAE,MAAM,aAAa,MAAM,UAAU;AAAA,IACvC;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC9C;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,UACV,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,UACnC,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,UACpC,EAAE,MAAM,iBAAiB,MAAM,UAAU;AAAA,UACzC,EAAE,MAAM,mBAAmB,MAAM,UAAU;AAAA,UAC3C,EAAE,MAAM,cAAc,MAAM,UAAU;AAAA,UACtC,EAAE,MAAM,kBAAkB,MAAM,UAAU;AAAA,UAC1C,EAAE,MAAM,iBAAiB,MAAM,UAAU;AAAA,QAC3C;AAAA,MACF;AAAA,MACA,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,MACvC,EAAE,MAAM,aAAa,MAAM,UAAU;AAAA,IACvC;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,EAC9C;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ,CAAC,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAAA,IAC3C,SAAS,CAAC;AAAA,EACZ;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,QAAQ;AAAA,MACN,EAAE,SAAS,MAAM,MAAM,SAAS,MAAM,UAAU;AAAA,MAChD,EAAE,SAAS,MAAM,MAAM,aAAa,MAAM,UAAU;AAAA,IACtD;AAAA,EACF;AACF;;;ACzNA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AACzD,SAAuB,oBAAoB,aAAa,YAAY;AACpE,SAAS,eAAe,4BAA4B;AA4C7C,IAAM,qBAAqB;AAElC,IAAM,iBAAyC;AAAA,EAC7C,SAAS;AAAA,EACT,SAAS;AAAA;AACX;AAEO,SAAS,YACd,UAAU,QAAQ,IAAI,uBAAuB,WACrC;AACR,SAAO,eAAe,OAAO,KAAK,eAAe;AACnD;AAEO,SAAS,sBAAsB,SAAyB;AAC7D,MAAI,QAAQ,YAAY,MAAM,mBAAmB,YAAY,GAAG;AAC9D,WAAO,YAAY;AAAA,EACrB;AACA,SAAO;AACT;AAIA,IAAM,YAAY;AAClB,IAAM,YAAY,KAAK,KAAK;AAgB5B,SAAS,YAA8B;AACrC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,SAAS;AAC1C,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,IAAI,KAAK,MAAM,GAAG;AAExB,MAAE,SAAS,EAAE,OAAO,IAAI,CAAC,OAAO;AAAA,MAC9B,GAAG;AAAA,MACH,SAAS,EAAE,QAAQ,YAAY;AAAA,IACjC,EAAE;AACF,MAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,OAAO;AAAA,MAC5B,GAAG;AAAA,MACH,SAAS,EAAE,QAAQ,YAAY;AAAA,MAC/B,QAAQ,EAAE,OAAO,YAAY;AAAA,MAC7B,QAAQ,EAAE,OAAO,YAAY;AAAA,IAC/B,EAAE;AACF,QAAI,KAAK,IAAI,IAAI,EAAE,WAAW,UAAW,QAAO;AAChD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,qBAAuC;AAC9C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,SAAS;AAC1C,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,IAAI,KAAK,MAAM,GAAG;AACxB,MAAE,SAAS,EAAE,OAAO,IAAI,CAAC,OAAO;AAAA,MAC9B,GAAG;AAAA,MACH,SAAS,EAAE,QAAQ,YAAY;AAAA,IACjC,EAAE;AACF,MAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,OAAO;AAAA,MAC5B,GAAG;AAAA,MACH,SAAS,EAAE,QAAQ,YAAY;AAAA,MAC/B,QAAQ,EAAE,OAAO,YAAY;AAAA,MAC7B,QAAQ,EAAE,OAAO,YAAY;AAAA,IAC/B,EAAE;AACF,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,QAAoB,OAAyB;AAC/D,MAAI;AACF,iBAAa;AAAA,MACX;AAAA,MACA,KAAK,UAAU;AAAA,QACb;AAAA,QACA;AAAA,QACA,UAAU,KAAK,IAAI;AAAA,MACrB,CAAqB;AAAA,IACvB;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AASA,SAAS,YAAY,OAA8B,OAAyB;AAC1E,aAAW,KAAK,OAAO;AACrB,UAAM,IAAI,EAAE,QAAQ,YAAY,GAAG;AAAA,MACjC,GAAG;AAAA,MACH,SAAS,EAAE,QAAQ,YAAY;AAAA,IACjC,CAAC;AAAA,EACH;AACF;AAMA,SAAS,WAAW,OAA8B,OAAyB;AACzE,aAAW,KAAK,OAAO;AACrB,UAAM,KAAK,EAAE,OAAO,YAAY;AAChC,UAAM,KAAK,EAAE,OAAO,YAAY;AAChC,UAAM,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG;AACpC,UAAM,IAAI,KAAK;AAAA,MACb,SAAS,EAAE,QAAQ,YAAY;AAAA,MAC/B,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAIA,IAAM,iBAAiB;AAAA,EACrB;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,QAAQ,CAAC,EAAE,MAAM,WAAW,MAAM,UAAU,CAAC;AAAA,IAC7C,SAAS,CAAC,EAAE,MAAM,IAAI,MAAM,UAAU,CAAC;AAAA,EACzC;AACF;AAQO,SAAS,gBACd,OACA,WACA,gBACoB;AACpB,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,WAAW,eAAe,YAAY;AAC5C,QAAM,OAAO,YAAY,EAAE,YAAY;AACvC,QAAM,YAAY,mBAAmB,YAAY;AAGjD,QAAM,WAAW,aAAa,YAAY,OAAO;AAGjD,QAAM,eAAe,oBAAI,IAAY;AACrC,aAAW,KAAK,OAAO;AACrB,UAAM,KAAK,EAAE,OAAO,YAAY;AAChC,UAAM,KAAK,EAAE,OAAO,YAAY;AAChC,QAAI,OAAO,SAAU,cAAa,IAAI,EAAE;AACxC,QAAI,OAAO,SAAU,cAAa,IAAI,EAAE;AAAA,EAC1C;AAIA,QAAM,aAAa,aAAa,IAAI,IAAI;AACxC,eAAa,OAAO,IAAI;AACxB,eAAa,OAAO,SAAS;AAE7B,QAAM,UAAU,UAAU;AAAA,IACxB,CAAC,MACC,aAAa,IAAI,EAAE,QAAQ,YAAY,CAAC,KACxC,EAAE,QAAQ,YAAY,MAAM,aAC5B,EAAE,QAAQ,YAAY,MAAM;AAAA,EAChC;AAGA,MAAI,cAAc,aAAa,WAAW;AACxC,UAAM,WAAW,UAAU;AAAA,MACzB,CAAC,MAAM,EAAE,QAAQ,YAAY,MAAM;AAAA,IACrC;AACA,QAAI,SAAU,QAAO,CAAC,UAAU,GAAG,OAAO;AAAA,EAC5C;AAEA,SAAO;AACT;AAGA,SAAS,aAAa,WAA2C;AAC/D,QAAM,OAAO,YAAY,EAAE,YAAY;AACvC,SAAO,UAAU,IAAI,CAAC,OAAO;AAAA,IAC3B,GAAG;AAAA,IACH,SAAS,EAAE,QAAQ,YAAY;AAAA,IAC/B,QAAQ,EAAE,QAAQ,YAAY,MAAM,OAAO,SAAS,EAAE;AAAA,IACtD,MAAM,EAAE,QAAQ,YAAY,MAAM,OAAO,gBAAgB,EAAE;AAAA,IAC3D,SAAS,EAAE;AAAA,IACX,YAAY;AAAA,IACZ,kBAAkB;AAAA,EACpB,EAAE;AACJ;AAEA,IAAM,iBAAmC;AAAA,EACvC,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,kBAAkB;AACpB;AAMA,SAAS,aAAa,aAAsD;AAC1E,QAAM,OAAO,YAAY,EAAE,YAAY;AACvC,QAAM,OAAO,YAAY,IAAI,IAAI,GAAG;AACpC,SAAO,OAAO,EAAE,GAAG,gBAAgB,SAAS,KAAK,IAAI;AACvD;AAMA,SAAS,YACP,SACA,IACA,SAAS,OACU;AACnB,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,IAAI,WAAW,MAAM;AACzB,UAAI,CAAC;AACH,gBAAQ,KAAK,4CAA4C,IAAI,IAAI;AACnE,cAAQ,IAAI;AAAA,IACd,GAAG,EAAE;AACL,YAAQ;AAAA,MACN,CAAC,MAAM;AACL,qBAAa,CAAC;AACd,gBAAQ,CAAC;AAAA,MACX;AAAA,MACA,CAAC,QAAQ;AACP,qBAAa,CAAC;AACd,YAAI,CAAC,QAAQ;AACX,gBAAM,MAAc,KAAK,WAAW,OAAO,GAAG;AAC9C,kBAAQ,KAAK,sCAAsC,GAAG;AAAA,QACxD;AACA,gBAAQ,IAAI;AAAA,MACd;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAIO,SAAS,cACd,aACqB;AACrB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA6B,CAAC,CAAC;AAC3D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAqB,CAAC,CAAC;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,KAAK;AAC5D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAChE,QAAM,CAAC,OAAO,OAAO,IAAI,SAAS,CAAC;AAEnC,QAAM,UAAU,YAAY,MAAM,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;AAG3D,YAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,UAAM,KAAK,YAAY,MAAM,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,GAAM;AAC1D,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,WAAW,CAAC;AAIhB,QAAM,iBAAiB,OAA8B,oBAAI,IAAI,CAAC;AAC9D,QAAM,gBAAgB,OAA8B,oBAAI,IAAI,CAAC;AAG7D,QAAM,aAAa,YAAY,MAAM;AACnC,UAAM,WAAW,MAAM,KAAK,eAAe,QAAQ,OAAO,CAAC;AAC3D,UAAM,UAAU,MAAM,KAAK,cAAc,QAAQ,OAAO,CAAC;AACzD,aAAS,OAAO;AAChB,WAAO,EAAE,UAAU,QAAQ;AAAA,EAC7B,GAAG,CAAC,CAAC;AAGL,QAAM,gBAAgB;AAAA,IACpB,OAAO,OAAe,WAAwB;AAI5C,YAAM,YAAY,IAAI;AAAA,QACpB,MAAM,KAAK,cAAc,QAAQ,OAAO,CAAC,EAAE;AAAA,UAAI,CAAC,MAC9C,EAAE,QAAQ,YAAY;AAAA,QACxB;AAAA,MACF;AACA,YAAM,YAAY,MAAM,KAAK,eAAe,QAAQ,OAAO,CAAC,EAAE;AAAA,QAC5D,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,QAAQ,YAAY,CAAC;AAAA,MAC/C;AAEA,yBAAmB,IAAI;AACvB,UAAI;AACF,cAAM,UAAW,QAAQ,IAAI,uBAAuB;AAGpD,cAAM,SACJ,YAAY,YACR,sCACA;AACN,cAAM,QACJ,YAAY,YAAY,uBAAuB;AACjD,cAAM,SAAS,mBAAmB,EAAE,OAAO,WAAW,KAAK,MAAM,EAAE,CAAC;AAEpE,cAAM,eAAe;AAErB,gBAAQ,IAAI,uCAAuC;AAAA,UACjD;AAAA,UACA;AAAA,UACA;AAAA,UACA,eAAe,UAAU;AAAA,QAC3B,CAAC;AAID,cAAM,eAAe,MAAM;AAAA,UACzB,OAAO,WAAW,EAAE,SAAS,MAAiB,CAAC;AAAA,UAC/C;AAAA,QACF;AACA,YAAI,OAAO,QAAS;AACpB,gBAAQ;AAAA,UACN;AAAA,UACA,cAAc,SAAS,KAAK;AAAA,QAC9B;AAGA,cAAM,aACJ;AACF,YAAI,aAAgC,CAAC;AACrC,YAAI,kBAEO;AAEX,YAAI,UAAU,SAAS,GAAG;AAGxB,4BAAkB,MAAM;AAAA,YACtB,OAAO,UAAU;AAAA,cACf,WAAW,UAAU,IAAI,CAAC,OAAO;AAAA,gBAC/B,SAAS,EAAE;AAAA,gBACX,KAAK;AAAA,gBACL,cAAc;AAAA,gBACd,MAAM,CAAC,KAAgB;AAAA,cACzB,EAAE;AAAA,cACF,cAAc;AAAA,cACd,kBAAkB;AAAA,YACpB,CAAC;AAAA,YACD;AAAA;AAAA,YACa;AAAA,UACf;AACA,uBAAa,kBACR,gBAA0D;AAAA,YACzD,CAAC,MAAO,EAAE,WAAW,YAAa,EAAE,SAAoB;AAAA,UAC1D,IACA,UAAU,IAAI,MAAM,IAAI;AAC5B,gBAAM,eAAe,WAAW,OAAO,CAAC,MAAM,MAAM,IAAI,EAAE;AAC1D,kBAAQ;AAAA,YACN,4CAA4C,YAAY,IAAI,UAAU,MAAM,6BAA6B,oBAAoB,IAAI;AAAA,UACnI;AAAA,QACF;AACA,YAAI,OAAO,QAAS;AAIpB,YAAI,iBAAiB,MAAM;AACzB,gBAAM,MAAM;AACZ,kBAAQ,KAAK,kCAAkC,GAAG;AAClD,wBAAc,GAAG;AAAA,QACnB,WAAW,UAAU,SAAS,KAAK,oBAAoB,MAAM;AAC3D,gBAAM,MACJ;AACF,kBAAQ,KAAK,kCAAkC,GAAG;AAClD,wBAAc,GAAG;AAAA,QACnB,OAAO;AACL,wBAAc,IAAI;AAAA,QACpB;AAEA,cAAM,OAAO,YAAY,EAAE,YAAY;AACvC,cAAM,WAA+B,UAAU,IAAI,CAAC,GAAG,MAAM;AAC3D,gBAAM,MAAM,WAAW,CAAC;AACxB,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,QAAQ,EAAE,QAAQ,YAAY,MAAM,OAAO,SAAS,EAAE;AAAA,YACtD,MAAM,EAAE,QAAQ,YAAY,MAAM,OAAO,gBAAgB,EAAE;AAAA;AAAA;AAAA,YAG3D,YAAY,QAAQ,OAAO,IAAI,SAAS,IAAI;AAAA,YAC5C,kBACE,QAAQ,OAAO,YAAY,KAAK,EAAE,YAAY,EAAE,IAAI;AAAA,UACxD;AAAA,QACF,CAAC;AAED,iBAAS,KAAK,CAAC,GAAG,MAAM;AACtB,gBAAM,OAAO,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,UAAU;AACvD,iBAAO,OAAO,KACV,IACA,OAAO,KACL,KACA,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,QACvC,CAAC;AAED,cAAM,YAAY,gBAAgB;AAClC,cAAM,iBAAmC;AAAA,UACvC,GAAG,aAAa,eAAe,OAAO;AAAA,UACtC,YAAY,UAAU,SAAS;AAAA,UAC/B,kBAAkB,YAAY,WAAW,EAAE;AAAA,QAC7C;AAEA,YAAI,CAAC,OAAO,SAAS;AAInB,oBAAU,CAAC,SAAS;AAClB,kBAAM,UAAU,IAAI;AAAA,cAClB,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,QAAQ,YAAY,GAAG,EAAE,UAAU,CAAC;AAAA,YACzD;AACA,kBAAM,WAAW,WAAW,IAAI,CAAC,MAAM,MAAM,IAAI;AACjD,kBAAM,SAAS;AAAA;AAAA,cAEb,iBAAiB,OACb,iBACC,KAAK,KAAK,CAAC,MAAM,EAAE,YAAY,kBAAkB,KAClD;AAAA,cACJ,GAAG,SAAS,IAAI,CAAC,GAAG,MAAM;AACxB,oBAAI,SAAS,CAAC,GAAG;AACf,wBAAM,UAAU,QAAQ,IAAI,EAAE,QAAQ,YAAY,CAAC,KAAK;AACxD,sBAAI,YAAY,KAAK;AACnB,2BAAO;AAAA,sBACL,GAAG;AAAA,sBACH,YAAY;AAAA,sBACZ,kBAAkB;AAAA,wBAChB,OAAO,OAAO;AAAA,wBACd,EAAE,YAAY;AAAA,sBAChB;AAAA,oBACF;AAAA,kBACF;AAAA,gBACF;AACA,uBAAO;AAAA,cACT,CAAC;AAAA,YACH;AACA,kBAAM,CAAC,UAAU,GAAG,IAAI,IAAI;AAC5B,iBAAK,KAAK,CAAC,GAAG,MAAM;AAClB,oBAAM,OAAO,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,UAAU;AACvD,qBAAO,OAAO,KACV,IACA,OAAO,KACL,KACA,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,YACvC,CAAC;AACD,mBAAO,CAAC,UAAU,GAAG,IAAI;AAAA,UAC3B,CAAC;AAAA,QACH;AAAA,MACF,SAAS,KAAc;AACrB,gBAAQ;AAAA,UACN;AAAA,UACC,KAAe,WAAW;AAAA,QAC7B;AACA,sBAAc,6DAAwD;AAAA,MAExE,UAAE;AACA,YAAI,CAAC,OAAO,QAAS,oBAAmB,KAAK;AAAA,MAC/C;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAMA,QAAM,iBAAiB,OAAO,KAAK;AAEnC,YAAU,MAAM;AAEd,QAAI,CAAC,eAAe,SAAS;AAC3B,qBAAe,UAAU;AACzB;AAAA,IACF;AACA,QAAI,CAAC,YAAa;AAClB,UAAM,OAAO,IAAI,gBAAgB;AACjC,SAAK,cAAc,aAAa,KAAK,MAAM;AAC3C,WAAO,MAAM,KAAK,MAAM;AAAA,EAC1B,GAAG,CAAC,OAAO,aAAa,aAAa,CAAC;AAGtC,YAAU,MAAM;AACd,UAAM,YAAY,IAAI,gBAAgB;AACtC,UAAM,EAAE,OAAO,IAAI;AAGnB,UAAM,YAAY,mBAAmB;AACrC,QAAI,WAAW;AACb,kBAAY,eAAe,SAAS,UAAU,MAAM;AACpD,iBAAW,cAAc,SAAS,UAAU,KAAK;AAAA,IACnD;AAGA,UAAM,SAAS,UAAU;AACzB,QAAI,QAAQ;AACV,YAAM,EAAE,SAAS,IAAI,WAAW;AAMhC,YAAM,UAAU,aAAa,eAAe,OAAO;AACnD,YAAM,YAAY,aAAa,QAAQ;AACvC,gBAAU,CAAC,SAAS;AAClB,YAAI,KAAK,WAAW,GAAG;AACrB,iBAAO,CAAC,SAAS,GAAG,SAAS;AAAA,QAC/B;AACA,cAAM,WAAW,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,QAAQ,YAAY,GAAG,CAAC,CAAC,CAAC;AACtE,cAAM,UAAU,SAAS,IAAI,mBAAmB,YAAY,CAAC;AAC7D,cAAM,WAAW,UACb;AAAA,UACE,GAAG;AAAA,UACH,YAAY,QAAQ;AAAA,UACpB,kBAAkB,QAAQ;AAAA,QAC5B,IACA;AACJ,cAAM,OAAO,UAAU;AAAA,UACrB,CAAC,MAAM,SAAS,IAAI,EAAE,QAAQ,YAAY,CAAC,KAAK;AAAA,QAClD;AACA,aAAK,KAAK,CAAC,GAAG,MAAM;AAClB,gBAAM,OAAO,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,UAAU;AACvD,iBAAO,OAAO,KACV,IACA,OAAO,KACL,KACA,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,QACvC,CAAC;AACD,eAAO,CAAC,UAAU,GAAG,IAAI;AAAA,MAC3B,CAAC;AACD,iBAAW,KAAK;AAChB,UAAI,aAAa;AACf,sBAAc,aAAa,MAAM;AAAA,MACnC;AAAA,IACF;AAEA,KAAC,YAAY;AACX,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,cAAc,EAAE,OAAO,CAAC;AAChD,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,oBAAoB,IAAI,MAAM,EAAE;AAC7D,cAAM,OAAQ,MAAM,IAAI,KAAK;AAK7B,YAAI,OAAO,QAAS;AAGpB,oBAAY,eAAe,SAAS,KAAK,MAAM;AAC/C,mBAAW,cAAc,SAAS,KAAK,KAAK;AAE5C,cAAM,eAAe,MAAM,KAAK,eAAe,QAAQ,OAAO,CAAC;AAC/D,cAAM,cAAc,MAAM,KAAK,cAAc,QAAQ,OAAO,CAAC;AAG7D,mBAAW,cAAc,WAAW;AAGpC,iBAAS,WAAW;AASpB,cAAM,YAAY,aAAa,YAAY;AAE3C,cAAM,UAAU,aAAa,eAAe,OAAO;AACnD,kBAAU,CAAC,SAAS;AAClB,cAAI,KAAK,WAAW,GAAG;AACrB,mBAAO,CAAC,SAAS,GAAG,SAAS;AAAA,UAC/B;AACA,gBAAM,WAAW,IAAI;AAAA,YACnB,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,QAAQ,YAAY,GAAG,CAAC,CAAC;AAAA,UAC9C;AAEA,gBAAM,UAAU,SAAS,IAAI,mBAAmB,YAAY,CAAC;AAC7D,gBAAM,WAAW,UACb;AAAA,YACE,GAAG;AAAA,YACH,YAAY,QAAQ;AAAA,YACpB,kBAAkB,QAAQ;AAAA,UAC5B,IACA;AACJ,gBAAM,OAAO,UAAU;AAAA,YACrB,CAAC,MAAM,SAAS,IAAI,EAAE,QAAQ,YAAY,CAAC,KAAK;AAAA,UAClD;AACA,eAAK,KAAK,CAAC,GAAG,MAAM;AAClB,kBAAM,OAAO,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,UAAU;AACvD,mBAAO,OAAO,KACV,IACA,OAAO,KACL,KACA,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,UACvC,CAAC;AACD,iBAAO,CAAC,UAAU,GAAG,IAAI;AAAA,QAC3B,CAAC;AACD,mBAAW,KAAK;AAEhB,YAAI,CAAC,YAAa;AAIlB,cAAM,cAAc,aAAa,MAAM;AAAA,MACzC,SAAS,KAAc;AACrB,YAAI,OAAO,QAAS;AACpB,cAAM,MAAO,KAAe,WAAW,OAAO,GAAG;AACjD,gBAAQ,KAAK,4CAA4C,GAAG;AAC5D,YAAI,UAAU,WAAW;AAEvB,qBAAW,KAAK;AAAA,QAClB,OAAO;AACL,mBAAS,GAAG;AACZ,qBAAW,KAAK;AAAA,QAClB;AAAA,MACF;AAAA,IACF,GAAG;AAEH,WAAO,MAAM,UAAU,MAAM;AAAA,EAE/B,GAAG,CAAC,aAAa,YAAY,aAAa,CAAC;AAE3C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cfxdevkit/defi-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Conflux DevKit – DeFi React hooks for Swappi pool tokens and on-chain balances",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/**/*",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"require": "./dist/index.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": "^18.0.0",
|
|
28
|
+
"react-dom": "^18.0.0",
|
|
29
|
+
"viem": ">=2.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@biomejs/biome": "^2.3.10",
|
|
33
|
+
"@types/node": "^25.0.3",
|
|
34
|
+
"@types/react": "^18.3.18",
|
|
35
|
+
"@types/react-dom": "^18.3.5",
|
|
36
|
+
"@testing-library/react": "^14.1.1",
|
|
37
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
38
|
+
"@vitest/ui": "^4.0.16",
|
|
39
|
+
"react": "^18.3.1",
|
|
40
|
+
"react-dom": "^18.3.1",
|
|
41
|
+
"tsup": "^8.5.1",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"vitest": "^4.0.16"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"conflux",
|
|
47
|
+
"react",
|
|
48
|
+
"defi",
|
|
49
|
+
"swappi",
|
|
50
|
+
"hooks",
|
|
51
|
+
"blockchain",
|
|
52
|
+
"web3"
|
|
53
|
+
],
|
|
54
|
+
"author": "Conflux DevKit Team",
|
|
55
|
+
"license": "Apache-2.0",
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "git+https://github.com/cfxdevkit/conflux-devkit.git",
|
|
59
|
+
"directory": "packages/defi-react"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/cfxdevkit/conflux-devkit#readme",
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/cfxdevkit/conflux-devkit/issues"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsup",
|
|
67
|
+
"build:watch": "tsup --watch",
|
|
68
|
+
"type-check": "tsc --noEmit",
|
|
69
|
+
"dev": "tsup --watch",
|
|
70
|
+
"test": "vitest run --pass-with-no-tests",
|
|
71
|
+
"test:run": "vitest --run",
|
|
72
|
+
"test:coverage": "vitest --run --coverage",
|
|
73
|
+
"clean": "rm -rf dist",
|
|
74
|
+
"lint": "biome lint src/",
|
|
75
|
+
"lint:fix": "biome lint --write src/",
|
|
76
|
+
"format": "biome format src/",
|
|
77
|
+
"format:fix": "biome format --write src/",
|
|
78
|
+
"check": "biome check src/",
|
|
79
|
+
"check:fix": "biome check --write src/"
|
|
80
|
+
}
|
|
81
|
+
}
|